diff --git a/apps/.gitkeep b/apps/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/emqx_auth_http/.github/workflows/run_test_cases.yaml b/apps/emqx_auth_http/.github/workflows/run_test_cases.yaml new file mode 100644 index 000000000..7a17028e7 --- /dev/null +++ b/apps/emqx_auth_http/.github/workflows/run_test_cases.yaml @@ -0,0 +1,26 @@ +name: Run test cases + +on: [push, pull_request] + +jobs: + run_test_cases: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: run test cases + run: | + docker network create --driver bridge --ipv6 --subnet fd15:555::/64 tests_emqx_bridge + docker run -i \ + --network tests_emqx_bridge \ + -v $(pwd):/emqx_auth_http \ + erlang:22.3 \ + bash -c "make -C /emqx_auth_http xref + make -C /emqx_auth_http eunit + make -C /emqx_auth_http ct + make -C /emqx_auth_http cover" + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: logs + path: _build/test/logs diff --git a/apps/emqx_auth_http/.gitignore b/apps/emqx_auth_http/.gitignore new file mode 100644 index 000000000..557a3a337 --- /dev/null +++ b/apps/emqx_auth_http/.gitignore @@ -0,0 +1,25 @@ +.eunit +deps +*.o +*.beam +*.plt +erl_crash.dump +ebin +rel/example_project +.concrete/DEV_MODE +.rebar +.erlang.mk/ +emqx_auth_http.d +data +ct.cover.spec +cover/ +ct.coverdata +eunit.coverdata +logs/ +erlang.mk +_build/ +rebar.lock +rebar3.crashdump +etc/emqx_auth_http.conf.rendered +.rebar3/ +*.swp diff --git a/apps/emqx_auth_http/LICENSE b/apps/emqx_auth_http/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/apps/emqx_auth_http/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/apps/emqx_auth_http/README.md b/apps/emqx_auth_http/README.md new file mode 100644 index 000000000..ed743334a --- /dev/null +++ b/apps/emqx_auth_http/README.md @@ -0,0 +1,100 @@ +emqx_auth_http +============== + +EMQ X HTTP Auth/ACL Plugin + +Build +----- + +``` +make && make tests +``` + +Configure the Plugin +-------------------- + +File: etc/emqx_auth_http.conf + +``` +##-------------------------------------------------------------------- +## Authentication request. +## +## Variables: +## - %u: username +## - %c: clientid +## - %a: ipaddress +## - %r: protocol +## - %P: password +## - %C: common name of client TLS cert +## - %d: subject of client TLS cert +## +## Value: URL +auth.http.auth_req = http://127.0.0.1:8080/mqtt/auth +## Value: post | get | put +auth.http.auth_req.method = post +## Value: Params +auth.http.auth_req.params = clientid=%c,username=%u,password=%P + +##-------------------------------------------------------------------- +## Superuser request. +## +## Variables: +## - %u: username +## - %c: clientid +## - %a: ipaddress +## - %r: protocol +## - %P: password +## - %C: common name of client TLS cert +## - %d: subject of client TLS cert +## +## Value: URL +auth.http.super_req = http://127.0.0.1:8080/mqtt/superuser +## Value: post | get | put +auth.http.super_req.method = post +## Value: Params +auth.http.super_req.params = clientid=%c,username=%u + +##-------------------------------------------------------------------- +## ACL request. +## +## Variables: +## - %A: 1 | 2, 1 = sub, 2 = pub +## - %u: username +## - %c: clientid +## - %a: ipaddress +## - %r: protocol +## - %m: mountpoint +## - %t: topic +## +## Value: URL +auth.http.acl_req = http://127.0.0.1:8080/mqtt/acl +## Value: post | get | put +auth.http.acl_req.method = get +## Value: Params +auth.http.acl_req.params = access=%A,username=%u,clientid=%c,ipaddr=%a,topic=%t +``` + +Load the Plugin +--------------- + +``` +./bin/emqx_ctl plugins load emqx_auth_http +``` + +HTTP API +-------- + +200 if ok + +4xx if unauthorized + +License +------- + +Apache License Version 2.0 + +Author +------ + +EMQ X Team. + diff --git a/apps/emqx_auth_http/etc/emqx_auth_http.conf b/apps/emqx_auth_http/etc/emqx_auth_http.conf new file mode 100644 index 000000000..86c4ac002 --- /dev/null +++ b/apps/emqx_auth_http/etc/emqx_auth_http.conf @@ -0,0 +1,162 @@ +##-------------------------------------------------------------------- +## HTTP Auth/ACL Plugin +##-------------------------------------------------------------------- + +##-------------------------------------------------------------------- +## Authentication request. + +## HTTP URL API path for authentication request +## +## Value: URL +## +## Examples: http://127.0.0.1:8991/mqtt/auth, https://[::1]:8991/mqtt/auth +auth.http.auth_req = http://127.0.0.1:8991/mqtt/auth + +## Value: post | get +auth.http.auth_req.method = post + +## It only works when method=post +## Value: json | x-www-form-urlencoded +auth.http.auth_req.content_type = x-www-form-urlencoded + +## Variables: +## - %u: username +## - %c: clientid +## - %a: ipaddress +## - %r: protocol +## - %P: password +## - %p: sockport of server accepted +## - %C: common name of client TLS cert +## - %d: subject of client TLS cert +## +## Value: Params +auth.http.auth_req.params = clientid=%c,username=%u,password=%P + +##-------------------------------------------------------------------- +## Superuser request. + +## HTTP URL API path for Superuser request +## +## Value: URL +## +## Examples: http://127.0.0.1:8991/mqtt/superuser, https://[::1]:8991/mqtt/superuser +#auth.http.super_req = http://127.0.0.1:8991/mqtt/superuser + +## Value: post | get +#auth.http.super_req.method = post + +## It only works when method=pos +## Value: json | x-www-form-urlencoded +#auth.http.super_req.content_type = x-www-form-urlencoded + +## Variables: +## - %u: username +## - %c: clientid +## - %a: ipaddress +## - %r: protocol +## - %P: password +## - %p: sockport of server accepted +## - %C: common name of client TLS cert +## - %d: subject of client TLS cert +## +## Value: Params +#auth.http.super_req.params = clientid=%c,username=%u + +##-------------------------------------------------------------------- +## ACL request. + +## HTTP URL API path for ACL request +## +## Value: URL +## +## Examples: http://127.0.0.1:8991/mqtt/acl, https://[::1]:8991/mqtt/acl +auth.http.acl_req = http://127.0.0.1:8991/mqtt/acl + +## Value: post | get +auth.http.acl_req.method = get + +## It only works when method=post +## Value: json | x-www-form-urlencoded +auth.http.acl_req.content_type = x-www-form-urlencoded + +## Variables: +## - %A: 1 | 2, 1 = sub, 2 = pub +## - %u: username +## - %c: clientid +## - %a: ipaddress +## - %r: protocol +## - %m: mountpoint +## - %t: topic +## +## Value: Params +auth.http.acl_req.params = access=%A,username=%u,clientid=%c,ipaddr=%a,topic=%t,mountpoint=%m + +##------------------------------------------------------------------------------ +## Http Reqeust options + +## Time-out time for the http request, 0 is never timeout. +## +## Value: Duration +## -h: hour, e.g. '2h' for 2 hours +## -m: minute, e.g. '5m' for 5 minutes +## -s: second, e.g. '30s' for 30 seconds +## +## Default: 0 +## auth.http.request.timeout = 0 + +## Connection time-out time, used during the initial request +## when the client is connecting to the server +## +## Value: Duration +## +## Default is same with the timeout option +## auth.http.request.connect_timeout = 0 + +## Re-send http reuqest times +## +## Value: integer +## +## Default: 3 +auth.http.request.retry_times = 3 + +## The interval for re-sending the http request +## +## Value: Duration +## +## Default: 1s +auth.http.request.retry_interval = 1s + +## The 'Exponential Backoff' mechanism for re-sending request. The actually +## re-send time interval is `interval * backoff ^ times` +## +## Value: float +## +## Default: 2.0 +auth.http.request.retry_backoff = 2.0 + +##------------------------------------------------------------------------------ +## SSL options + +## Path to the file containing PEM-encoded CA certificates. The CA certificates +## are used during server authentication and when building the client certificate chain. +## +## Value: File +## auth.http.ssl.cacertfile = {{ platform_etc_dir }}/certs/ca.pem + +## The path to a file containing the client's certificate. +## +## Value: File +## auth.http.ssl.certfile = {{ platform_etc_dir }}/certs/client-cert.pem + +## Path to a file containing the client's private PEM-encoded key. +## +## Value: File +## auth.http.ssl.keyfile = {{ platform_etc_dir }}/certs/client-key.pem + +##-------------------------------------------------------------------- +## HTTP Request Headers +## +## Example: auth.http.header.Accept-Encoding = * +## +## Value: String +## auth.http.header.Accept = */* diff --git a/apps/emqx_auth_http/include/emqx_auth_http.hrl b/apps/emqx_auth_http/include/emqx_auth_http.hrl new file mode 100644 index 000000000..09e58e324 --- /dev/null +++ b/apps/emqx_auth_http/include/emqx_auth_http.hrl @@ -0,0 +1,25 @@ + +-define(APP, emqx_auth_http). + +-record(http_request, {method = post, content_type, url, params, options = []}). + +-record(auth_metrics, { + success = 'client.auth.success', + failure = 'client.auth.failure', + ignore = 'client.auth.ignore' + }). + +-record(acl_metrics, { + allow = 'client.acl.allow', + deny = 'client.acl.deny', + ignore = 'client.acl.ignore' + }). + +-define(METRICS(Type), tl(tuple_to_list(#Type{}))). +-define(METRICS(Type, K), #Type{}#Type.K). + +-define(AUTH_METRICS, ?METRICS(auth_metrics)). +-define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). + +-define(ACL_METRICS, ?METRICS(acl_metrics)). +-define(ACL_METRICS(K), ?METRICS(acl_metrics, K)). diff --git a/apps/emqx_auth_http/priv/emqx_auth_http.schema b/apps/emqx_auth_http/priv/emqx_auth_http.schema new file mode 100644 index 000000000..e6a986344 --- /dev/null +++ b/apps/emqx_auth_http/priv/emqx_auth_http.schema @@ -0,0 +1,165 @@ +%%-*- mode: erlang -*- +%% emqx_auth_http config mapping +{mapping, "auth.http.auth_req", "emqx_auth_http.auth_req", [ + {datatype, string} +]}. + +{mapping, "auth.http.auth_req.method", "emqx_auth_http.auth_req", [ + {default, post}, + {datatype, {enum, [post, get]}} +]}. + +{mapping, "auth.http.auth_req.content_type", "emqx_auth_http.auth_req", [ + {default, 'x-www-form-urlencoded'}, + {datatype, {enum, [json, 'x-www-form-urlencoded']}} +]}. + +{mapping, "auth.http.auth_req.params", "emqx_auth_http.auth_req", [ + {datatype, string} +]}. + +{translation, "emqx_auth_http.auth_req", fun(Conf) -> + case cuttlefish:conf_get("auth.http.auth_req", Conf) of + undefined -> cuttlefish:unset(); + Url -> + Params = cuttlefish:conf_get("auth.http.auth_req.params", Conf), + [{url, Url}, + {method, cuttlefish:conf_get("auth.http.auth_req.method", Conf)}, + {content_type, cuttlefish:conf_get("auth.http.auth_req.content_type", Conf)}, + {params, [list_to_tuple(string:tokens(S, "=")) || S <- string:tokens(Params, ",")]}] + end +end}. + +{mapping, "auth.http.super_req", "emqx_auth_http.super_req", [ + {datatype, string} +]}. + +{mapping, "auth.http.super_req.method", "emqx_auth_http.super_req", [ + {default, post}, + {datatype, {enum, [post, get]}} +]}. + +{mapping, "auth.http.super_req.content_type", "emqx_auth_http.super_req", [ + {default, 'x-www-form-urlencoded'}, + {datatype, {enum, [json, 'x-www-form-urlencoded']}} +]}. + +{mapping, "auth.http.super_req.params", "emqx_auth_http.super_req", [ + {datatype, string} +]}. + +{translation, "emqx_auth_http.super_req", fun(Conf) -> + case cuttlefish:conf_get("auth.http.super_req", Conf, undefined) of + undefined -> cuttlefish:unset(); + Url -> Params = cuttlefish:conf_get("auth.http.super_req.params", Conf), + [{url, Url}, {method, cuttlefish:conf_get("auth.http.super_req.method", Conf)}, + {content_type, cuttlefish:conf_get("auth.http.super_req.content_type", Conf)}, + {params, [list_to_tuple(string:tokens(S, "=")) || S <- string:tokens(Params, ",")]}] + end +end}. + +{mapping, "auth.http.acl_req", "emqx_auth_http.acl_req", [ + {default, undefined}, + {datatype, string} +]}. + +{mapping, "auth.http.acl_req.method", "emqx_auth_http.acl_req", [ + {default, post}, + {datatype, {enum, [post, get]}} +]}. + +{mapping, "auth.http.acl_req.content_type", "emqx_auth_http.acl_req", [ + {default, 'x-www-form-urlencoded'}, + {datatype, {enum, [json, 'x-www-form-urlencoded']}} +]}. + +{mapping, "auth.http.acl_req.params", "emqx_auth_http.acl_req", [ + {datatype, string} +]}. + +{translation, "emqx_auth_http.acl_req", fun(Conf) -> + case cuttlefish:conf_get("auth.http.acl_req", Conf, undefined) of + undefined -> cuttlefish:unset(); + Url -> Params = cuttlefish:conf_get("auth.http.acl_req.params", Conf), + [{url, Url}, {method, cuttlefish:conf_get("auth.http.acl_req.method", Conf)}, + {content_type, cuttlefish:conf_get("auth.http.acl_req.content_type", Conf)}, + {params, [list_to_tuple(string:tokens(S, "=")) || S <- string:tokens(Params, ",")]}] + end +end}. + +{mapping, "auth.http.request.timeout", "emqx_auth_http.http_opts", [ + {default, 0}, + {datatype, [integer, {duration, ms}]} +]}. + +{mapping, "auth.http.request.connect_timeout", "emqx_auth_http.http_opts", [ + {datatype, [integer, {duration, ms}]} +]}. + +{mapping, "auth.http.ssl.cacertfile", "emqx_auth_http.http_opts", [ + {datatype, string} +]}. + +{mapping, "auth.http.ssl.certfile", "emqx_auth_http.http_opts", [ + {datatype, string} +]}. + +{mapping, "auth.http.ssl.keyfile", "emqx_auth_http.http_opts", [ + {datatype, string} +]}. + +{translation, "emqx_auth_http.http_opts", fun(Conf) -> + Filter = fun(L) -> [{K, V} || {K, V} <- L, V =/= undefined] end, + InfinityFun = fun(0) -> infinity; + (Duration) -> Duration + end, + SslOpts = Filter([{cacertfile, cuttlefish:conf_get("auth.http.ssl.cacertfile", Conf, undefined)}, + {certfile, cuttlefish:conf_get("auth.http.ssl.certfile", Conf, undefined)}, + {keyfile, cuttlefish:conf_get("auth.http.ssl.keyfile", Conf, undefined)}]), + Opts = [{timeout, InfinityFun(cuttlefish:conf_get("auth.http.request.timeout", Conf))}, + {connect_timeout, InfinityFun(cuttlefish:conf_get("auth.http.request.connect_timeout", Conf, undefined))}], + case SslOpts of + [] -> Filter(Opts); + _ -> + TlsVers = ['tlsv1.2','tlsv1.1',tlsv1], + DefaultOpts = [{versions, TlsVers}, + {ciphers, lists:foldl( + fun(TlsVer, Ciphers) -> + Ciphers ++ ssl:cipher_suites(all, TlsVer) + end, [], TlsVers)}], + Filter([{ssl, DefaultOpts ++ SslOpts} | Opts]) + end +end}. + +{mapping, "auth.http.request.retry_times", "emqx_auth_http.retry_opts", [ + {default, 3}, + {datatype, integer} +]}. + +{mapping, "auth.http.request.retry_interval", "emqx_auth_http.retry_opts", [ + {default, "1s"}, + {datatype, {duration, ms}} +]}. + +{mapping, "auth.http.request.retry_backoff", "emqx_auth_http.retry_opts", [ + {default, 2.0}, + {datatype, float} +]}. + +{translation, "emqx_auth_http.retry_opts", fun(Conf) -> + [{times, cuttlefish:conf_get("auth.http.request.retry_times", Conf)}, + {interval, cuttlefish:conf_get("auth.http.request.retry_interval", Conf)}, + {backoff, cuttlefish:conf_get("auth.http.request.retry_backoff", Conf)}] +end}. + +{mapping, "auth.http.header.$field", "emqx_auth_http.headers", [ + {datatype, string} +]}. + +{translation, "emqx_auth_http.headers", fun(Conf) -> + lists:map( + fun({["auth", "http", "header", Field], Value}) -> + {Field, Value} + end, + cuttlefish_variable:filter_by_prefix("auth.http.header", Conf)) +end}. \ No newline at end of file diff --git a/apps/emqx_auth_http/rebar.config b/apps/emqx_auth_http/rebar.config new file mode 100644 index 000000000..026e6fc9b --- /dev/null +++ b/apps/emqx_auth_http/rebar.config @@ -0,0 +1,26 @@ +{deps, []}. + +{edoc_opts, [{preprocess, true}]}. +{erl_opts, [warn_unused_vars, + warn_shadow_vars, + warn_unused_import, + warn_obsolete_guard, + debug_info, + {parse_transform}]}. + +{xref_checks, [undefined_function_calls, undefined_functions, + locals_not_used, deprecated_function_calls, + warnings_as_errors, deprecated_functions]}. + +{cover_enabled, true}. +{cover_opts, [verbose]}. +{cover_export_enabled, true}. + +{profiles, + [{test, + [{deps, + [{emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}}, + {emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.2.0"}}} + ]} + ]} + ]}. diff --git a/apps/emqx_auth_http/src/emqx_acl_http.erl b/apps/emqx_auth_http/src/emqx_acl_http.erl new file mode 100644 index 000000000..ebe415937 --- /dev/null +++ b/apps/emqx_auth_http/src/emqx_acl_http.erl @@ -0,0 +1,92 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_acl_http). + +-include("emqx_auth_http.hrl"). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-logger_header("[ACL http]"). + +-import(emqx_auth_http_cli, + [ request/8 + , feedvar/2 + ]). + +%% ACL callbacks +-export([ register_metrics/0 + , check_acl/5 + , description/0 + ]). + +-spec(register_metrics() -> ok). +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS). + +%%-------------------------------------------------------------------- +%% ACL callbacks +%%-------------------------------------------------------------------- + +check_acl(ClientInfo, PubSub, Topic, AclResult, State) -> + return_with(fun inc_metrics/1, + do_check_acl(ClientInfo, PubSub, Topic, AclResult, State)). + +do_check_acl(#{username := <<$$, _/binary>>}, _PubSub, _Topic, _AclResult, _Config) -> + ok; +do_check_acl(ClientInfo, PubSub, Topic, _AclResult, #{acl_req := AclReq, + http_opts := HttpOpts, + retry_opts := RetryOpts, + headers := Headers}) -> + ClientInfo1 = ClientInfo#{access => access(PubSub), topic => Topic}, + case check_acl_request(AclReq, ClientInfo1, Headers, HttpOpts, RetryOpts) of + {ok, 200, "ignore"} -> ok; + {ok, 200, _Body} -> {stop, allow}; + {ok, _Code, _Body} -> {stop, deny}; + {error, Error} -> + ?LOG(error, "Request ACL url ~s, error: ~p", + [AclReq#http_request.url, Error]), + ok + end. + +description() -> "ACL with HTTP API". + +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- + +inc_metrics(ok) -> + emqx_metrics:inc(?ACL_METRICS(ignore)); +inc_metrics({stop, allow}) -> + emqx_metrics:inc(?ACL_METRICS(allow)); +inc_metrics({stop, deny}) -> + emqx_metrics:inc(?ACL_METRICS(deny)). + +return_with(Fun, Result) -> + Fun(Result), Result. + +check_acl_request(#http_request{url = Url, + method = Method, + content_type = ContentType, + params = Params, + options = Options}, + ClientInfo, Headers, HttpOpts, RetryOpts) -> + request(Method, ContentType, Url, feedvar(Params, ClientInfo), Headers, HttpOpts, Options, RetryOpts). + +access(subscribe) -> 1; +access(publish) -> 2. + diff --git a/apps/emqx_auth_http/src/emqx_auth_http.app.src b/apps/emqx_auth_http/src/emqx_auth_http.app.src new file mode 100644 index 000000000..eaabdb37e --- /dev/null +++ b/apps/emqx_auth_http/src/emqx_auth_http.app.src @@ -0,0 +1,14 @@ +{application, emqx_auth_http, + [{description, "EMQ X Authentication/ACL with HTTP API"}, + {vsn, "git"}, + {modules, []}, + {registered, [emqx_auth_http_sup]}, + {applications, [kernel,stdlib]}, + {mod, {emqx_auth_http_app, []}}, + {env, []}, + {licenses, ["Apache-2.0"]}, + {maintainers, ["EMQ X Team "]}, + {links, [{"Homepage", "https://emqx.io/"}, + {"Github", "https://github.com/emqx/emqx-auth-http"} + ]} + ]}. diff --git a/apps/emqx_auth_http/src/emqx_auth_http.app.src.script b/apps/emqx_auth_http/src/emqx_auth_http.app.src.script new file mode 100644 index 000000000..0e14ff23f --- /dev/null +++ b/apps/emqx_auth_http/src/emqx_auth_http.app.src.script @@ -0,0 +1,24 @@ +%%-*- mode: erlang -*- +%% .app.src.script + +RemoveLeadingV = + fun(Tag) -> + case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of + nomatch -> + re:replace(Tag, "/", "-", [{return ,list}]); + _ -> + %% if it is a version number prefixed by 'v' or 'e', then remove it + re:replace(Tag, "[v|e]", "", [{return ,list}]) + end + end, + +case os:getenv("EMQX_DEPS_DEFAULT_VSN") of + false -> CONFIG; % env var not defined + [] -> CONFIG; % env var set to empty string + Tag -> + [begin + AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}), + {application, App, AppConf0} + end || Conf = {application, App, AppConf} <- CONFIG] +end. + diff --git a/apps/emqx_auth_http/src/emqx_auth_http.erl b/apps/emqx_auth_http/src/emqx_auth_http.erl new file mode 100644 index 000000000..54e41c989 --- /dev/null +++ b/apps/emqx_auth_http/src/emqx_auth_http.erl @@ -0,0 +1,116 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_http). + +-include("emqx_auth_http.hrl"). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). +-include_lib("emqx/include/types.hrl"). + +-logger_header("[Auth http]"). + +-import(emqx_auth_http_cli, + [ request/8 + , feedvar/2 + ]). + +%% Callbacks +-export([ register_metrics/0 + , check/3 + , description/0 + ]). + +-spec(register_metrics() -> ok). +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). + +check(ClientInfo, AuthResult, #{auth_req := AuthReq, + super_req := SuperReq, + http_opts := HttpOpts, + retry_opts := RetryOpts, + headers := Headers}) -> + case authenticate(AuthReq, ClientInfo, Headers, HttpOpts, RetryOpts) of + {ok, 200, "ignore"} -> + emqx_metrics:inc(?AUTH_METRICS(ignore)), ok; + {ok, 200, Body} -> + emqx_metrics:inc(?AUTH_METRICS(success)), + IsSuperuser = is_superuser(SuperReq, ClientInfo, Headers, HttpOpts, RetryOpts), + {stop, AuthResult#{is_superuser => IsSuperuser, + auth_result => success, + anonymous => false, + mountpoint => mountpoint(Body, ClientInfo)}}; + {ok, Code, _Body} -> + ?LOG(error, "Deny connection from url: ~s, response http code: ~p", + [AuthReq#http_request.url, Code]), + emqx_metrics:inc(?AUTH_METRICS(failure)), + {stop, AuthResult#{auth_result => http_to_connack_error(Code), + anonymous => false}}; + {error, Error} -> + ?LOG(error, "Request auth url: ~s, error: ~p", + [AuthReq#http_request.url, Error]), + emqx_metrics:inc(?AUTH_METRICS(failure)), + %%FIXME later: server_unavailable is not right. + {stop, AuthResult#{auth_result => server_unavailable, + anonymous => false}} + end. + +description() -> "Authentication by HTTP API". + +%%-------------------------------------------------------------------- +%% Requests +%%-------------------------------------------------------------------- + +authenticate(#http_request{url = Url, + method = Method, + content_type = ContentType, + params = Params, + options = Options}, + ClientInfo, HttpHeaders, HttpOpts, RetryOpts) -> + request(Method, ContentType, Url, feedvar(Params, ClientInfo), HttpHeaders, HttpOpts, Options, RetryOpts). + +-spec(is_superuser(maybe(#http_request{}), emqx_types:client(), list(), list(), list()) -> boolean()). +is_superuser(undefined, _ClientInfo, _HttpHeaders, _HttpOpts, _RetryOpts) -> + false; +is_superuser(#http_request{url = Url, + method = Method, + content_type = ContentType, + params = Params, + options = Options}, + ClientInfo, HttpHeaders, HttpOpts, RetryOpts) -> + case request(Method, ContentType, Url, feedvar(Params, ClientInfo), HttpHeaders, HttpOpts, Options, RetryOpts) of + {ok, 200, _Body} -> true; + {ok, _Code, _Body} -> false; + {error, Error} -> ?LOG(error, "Request superuser url ~s, error: ~p", [Url, Error]), + false + end. + +mountpoint(Body, #{mountpoint := Mountpoint}) -> + case emqx_json:safe_decode(iolist_to_binary(Body), [return_maps]) of + {error, _} -> Mountpoint; + {ok, Json} when is_map(Json) -> + maps:get(<<"mountpoint">>, Json, Mountpoint); + {ok, _NotMap} -> Mountpoint + end. + +http_to_connack_error(400) -> bad_username_or_password; +http_to_connack_error(401) -> bad_username_or_password; +http_to_connack_error(403) -> not_authorized; +http_to_connack_error(429) -> banned; +http_to_connack_error(503) -> server_unavailable; +http_to_connack_error(504) -> server_busy; +http_to_connack_error(_) -> server_unavailable. diff --git a/apps/emqx_auth_http/src/emqx_auth_http_app.erl b/apps/emqx_auth_http/src/emqx_auth_http_app.erl new file mode 100644 index 000000000..51f6762ee --- /dev/null +++ b/apps/emqx_auth_http/src/emqx_auth_http_app.erl @@ -0,0 +1,103 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_http_app). + +-behaviour(application). +-behaviour(supervisor). + +-emqx_plugin(auth). + +-include("emqx_auth_http.hrl"). + +-export([ start/2 + , stop/1 + ]). +-export([init/1]). + +%%-------------------------------------------------------------------- +%% Application Callbacks +%%-------------------------------------------------------------------- + +start(_StartType, _StartArgs) -> + with_env(auth_req, fun load_auth_hook/1), + with_env(acl_req, fun load_acl_hook/1), + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +load_auth_hook(AuthReq) -> + ok = emqx_auth_http:register_metrics(), + SuperReq = r(application:get_env(?APP, super_req, undefined)), + HttpOpts = application:get_env(?APP, http_opts, []), + RetryOpts = application:get_env(?APP, retry_opts, []), + Headers = application:get_env(?APP, headers, []), + Params = #{auth_req => AuthReq, + super_req => SuperReq, + http_opts => HttpOpts, + retry_opts => maps:from_list(RetryOpts), + headers => Headers}, + emqx:hook('client.authenticate', {emqx_auth_http, check, [Params]}). + +load_acl_hook(AclReq) -> + ok = emqx_acl_http:register_metrics(), + HttpOpts = application:get_env(?APP, http_opts, []), + RetryOpts = application:get_env(?APP, retry_opts, []), + Headers = application:get_env(?APP, headers, []), + Params = #{acl_req => AclReq, + http_opts => HttpOpts, + retry_opts => maps:from_list(RetryOpts), + headers => Headers}, + emqx:hook('client.check_acl', {emqx_acl_http, check_acl, [Params]}). + +stop(_State) -> + emqx:unhook('client.authenticate', {emqx_auth_http, check}), + emqx:unhook('client.check_acl', {emqx_acl_http, check_acl}). + +%%-------------------------------------------------------------------- +%% Dummy supervisor +%%-------------------------------------------------------------------- + +init([]) -> + {ok, { {one_for_all, 10, 100}, []} }. + +%%-------------------------------------------------------------------- +%% Internel functions +%%-------------------------------------------------------------------- + +with_env(Par, Fun) -> + case application:get_env(?APP, Par) of + undefined -> ok; + {ok, Req} -> Fun(r(Req)) + end. + +r(undefined) -> + undefined; +r(Config) -> + Method = proplists:get_value(method, Config, post), + ContentType = proplists:get_value(content_type, Config, 'x-www-form-urlencoded'), + Url = proplists:get_value(url, Config), + Params = proplists:get_value(params, Config), + #http_request{method = Method, content_type = ContentType, url = Url, params = Params, options = inet(Url)}. + +inet(Url) -> + case uri_string:parse(Url) of + #{host := Host} -> + case inet:parse_address(Host) of + {ok, Ip} when tuple_size(Ip) =:= 8 -> + [{ipv6_host_with_brackets, true}, {socket_opts, [{ipfamily, inet6}]}]; + _ -> [] + end; + _ -> [] + end. diff --git a/apps/emqx_auth_http/src/emqx_auth_http_cli.erl b/apps/emqx_auth_http/src/emqx_auth_http_cli.erl new file mode 100644 index 000000000..35a20d0f0 --- /dev/null +++ b/apps/emqx_auth_http/src/emqx_auth_http_cli.erl @@ -0,0 +1,101 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_http_cli). + +-export([ request/8 + , feedvar/2 + , feedvar/3 + ]). + +%%-------------------------------------------------------------------- +%% HTTP Request +%%-------------------------------------------------------------------- + +request(get, _ContentType, Url, Params, HttpHeaders, HttpOpts, Options, RetryOpts) -> + Req = {Url ++ "?" ++ cow_qs:qs(bin_kw(Params)), HttpHeaders}, + reply(request_(get, Req, [{autoredirect, true} | HttpOpts], Options, RetryOpts)); + +request(post, 'x-www-form-urlencoded', Url, Params, HttpHeaders, HttpOpts, Options, RetryOpts) -> + Req = {Url, HttpHeaders, "application/x-www-form-urlencoded", cow_qs:qs(bin_kw(Params))}, + reply(request_(post, Req, [{autoredirect, true} | HttpOpts], Options, RetryOpts)); + +request(post, json, Url, Params, HttpHeaders, HttpOpts, Options, RetryOpts) -> + Req = {Url, HttpHeaders, "application/json", emqx_json:encode(bin_kw(Params))}, + reply(request_(post, Req, [{autoredirect, true} | HttpOpts], Options, RetryOpts)). + +request_(Method, Req, HTTPOpts, Opts, RetryOpts = #{times := Times, + interval := Interval, + backoff := BackOff}) -> + case httpc:request(Method, Req, HTTPOpts, Opts) of + {error, _Reason} when Times > 0 -> + timer:sleep(trunc(Interval)), + RetryOpts1 = RetryOpts#{times := Times - 1, + interval := Interval * BackOff}, + request_(Method, Req, HTTPOpts, Opts, RetryOpts1); + Other -> Other + end. + +reply({ok, {{_, Code, _}, _Headers, Body}}) -> + {ok, Code, Body}; +reply({ok, Code, Body}) -> + {ok, Code, Body}; +reply({error, Error}) -> + {error, Error}. + +%% TODO: move this conversion to cuttlefish config and schema +bin_kw(KeywordList) when is_list(KeywordList) -> + [{bin(K), bin(V)} || {K, V} <- KeywordList]. + +bin(Atom) when is_atom(Atom) -> + list_to_binary(atom_to_list(Atom)); +bin(Int) when is_integer(Int) -> + integer_to_binary(Int); +bin(Float) when is_float(Float) -> + float_to_binary(Float, [{decimals, 12}, compact]); +bin(List) when is_list(List)-> + list_to_binary(List); +bin(Binary) when is_binary(Binary) -> + Binary. + +%%-------------------------------------------------------------------- +%% Feed Variables +%%-------------------------------------------------------------------- + +feedvar(Params, ClientInfo = #{clientid := ClientId, + protocol := Protocol, + peerhost := Peerhost}) -> + lists:map(fun({Param, "%u"}) -> {Param, maps:get(username, ClientInfo, null)}; + ({Param, "%c"}) -> {Param, ClientId}; + ({Param, "%r"}) -> {Param, Protocol}; + ({Param, "%a"}) -> {Param, inet:ntoa(Peerhost)}; + ({Param, "%P"}) -> {Param, maps:get(password, ClientInfo, null)}; + ({Param, "%p"}) -> {Param, maps:get(sockport, ClientInfo, null)}; + ({Param, "%C"}) -> {Param, maps:get(cn, ClientInfo, null)}; + ({Param, "%d"}) -> {Param, maps:get(dn, ClientInfo, null)}; + ({Param, "%A"}) -> {Param, maps:get(access, ClientInfo, null)}; + ({Param, "%t"}) -> {Param, maps:get(topic, ClientInfo, null)}; + ({Param, "%m"}) -> {Param, maps:get(mountpoint, ClientInfo, null)}; + ({Param, Var}) -> {Param, Var} + end, Params). + +feedvar(Params, Var, Val) -> + lists:map(fun({Param, Var0}) when Var0 == Var -> + {Param, Val}; + ({Param, Var0}) -> + {Param, Var0} + end, Params). + diff --git a/apps/emqx_auth_http/test/emqx_auth_http_SUITE.erl b/apps/emqx_auth_http/test/emqx_auth_http_SUITE.erl new file mode 100644 index 000000000..25ff942c5 --- /dev/null +++ b/apps/emqx_auth_http/test/emqx_auth_http_SUITE.erl @@ -0,0 +1,167 @@ +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +-module(emqx_auth_http_SUITE). + +-compile(export_all). +-compile(nowarn_export_all). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +-define(APP, emqx_auth_http). + +-define(USER(ClientId, Username, Protocol, Peerhost, Zone), + #{clientid => ClientId, username => Username, protocol => Protocol, + peerhost => Peerhost, zone => Zone}). + +-define(USER(ClientId, Username, Protocol, Peerhost, Zone, Mountpoint), + #{clientid => ClientId, username => Username, protocol => Protocol, + peerhost => Peerhost, zone => Zone, mountpoint => Mountpoint}). + +%%-------------------------------------------------------------------- +%% Setups +%%-------------------------------------------------------------------- + +all() -> + [{group, http_inet}, + {group, http_inet6}, + {group, https_inet}, + {group, https_inet6}]. + +groups() -> + Cases = emqx_ct:all(?MODULE), + [{Name, Cases} || Name <- [http_inet, http_inet6, https_inet, https_inet6]]. + +init_per_group(GrpName, Cfg) -> + [Schema, Inet] = [list_to_atom(X) || X <- string:tokens(atom_to_list(GrpName), "_")], + http_auth_server:start(Schema, Inet), + Fun = fun(App) -> set_special_configs(App, Schema, Inet) end, + emqx_ct_helpers:start_apps([emqx_auth_http], Fun), + Cfg. + +end_per_group(_GrpName, _Cfg) -> + http_auth_server:stop(), + emqx_ct_helpers:stop_apps([emqx_auth_http, emqx]). + +set_special_configs(emqx, _Schmea, _Inet) -> + application:set_env(emqx, allow_anonymous, true), + application:set_env(emqx, enable_acl_cache, false), + LoadedPluginPath = filename:join(["test", "emqx_SUITE_data", "loaded_plugins"]), + application:set_env(emqx, plugins_loaded_file, + emqx_ct_helpers:deps_path(emqx, LoadedPluginPath)); + +set_special_configs(emqx_auth_http, Schema, Inet) -> + AuthReq = maps:from_list(application:get_env(emqx_auth_http, auth_req, [])), + SuprReq = maps:from_list(application:get_env(emqx_auth_http, super_req, [])), + AclReq = maps:from_list(application:get_env(emqx_auth_http, acl_req, [])), + SvrAddr = http_server_host(Schema, Inet), + + AuthReq1 = AuthReq#{method := get, url := SvrAddr ++ "/mqtt/auth"}, + SuprReq1 = SuprReq#{method := post, content_type := 'x-www-form-urlencoded', url := SvrAddr ++ "/mqtt/superuser"}, + AclReq1 = AclReq #{method := post, content_type := json, url := SvrAddr ++ "/mqtt/acl"}, + + Schema =:= https andalso set_https_client_opts(), + + application:set_env(emqx_auth_http, auth_req, maps:to_list(AuthReq1)), + application:set_env(emqx_auth_http, super_req, maps:to_list(SuprReq1)), + application:set_env(emqx_auth_http, acl_req, maps:to_list(AclReq1)). + +%% @private +set_https_client_opts() -> + HttpOpts = maps:from_list(application:get_env(emqx_auth_http, http_opts, [])), + HttpOpts1 = HttpOpts#{ssl => emqx_ct_helpers:client_ssl_twoway()}, + application:set_env(emqx_auth_http, http_opts, maps:to_list(HttpOpts1)). + +%% @private +http_server_host(http, inet) -> "http://127.0.0.1:8991"; +http_server_host(http, inet6) -> "http://[::1]:8991"; +http_server_host(https, inet) -> "https://127.0.0.1:8991"; +http_server_host(https, inet6) -> "https://[::1]:8991". + +%%------------------------------------------------------------------------------ +%% Testcases +%%------------------------------------------------------------------------------ + +t_check_acl(_) -> + SuperUser = ?USER(<<"superclient">>, <<"superuser">>, mqtt, {127,0,0,1}, external), + deny = emqx_access_control:check_acl(SuperUser, subscribe, <<"users/testuser/1">>), + deny = emqx_access_control:check_acl(SuperUser, publish, <<"anytopic">>), + + User1 = ?USER(<<"client1">>, <<"testuser">>, mqtt, {127,0,0,1}, external), + UnIpUser1 = ?USER(<<"client1">>, <<"testuser">>, mqtt, {192,168,0,4}, external), + UnClientIdUser1 = ?USER(<<"unkonwc">>, <<"testuser">>, mqtt, {127,0,0,1}, external), + UnnameUser1= ?USER(<<"client1">>, <<"unuser">>, mqtt, {127,0,0,1}, external), + allow = emqx_access_control:check_acl(User1, subscribe, <<"users/testuser/1">>), + deny = emqx_access_control:check_acl(User1, publish, <<"users/testuser/1">>), + deny = emqx_access_control:check_acl(UnIpUser1, subscribe, <<"users/testuser/1">>), + deny = emqx_access_control:check_acl(UnClientIdUser1, subscribe, <<"users/testuser/1">>), + deny = emqx_access_control:check_acl(UnnameUser1, subscribe, <<"$SYS/testuser/1">>), + + User2 = ?USER(<<"client2">>, <<"xyz">>, mqtt, {127,0,0,1}, external), + UserC = ?USER(<<"client2">>, <<"xyz">>, mqtt, {192,168,1,3}, external), + allow = emqx_access_control:check_acl(UserC, publish, <<"a/b/c">>), + deny = emqx_access_control:check_acl(User2, publish, <<"a/b/c">>), + deny = emqx_access_control:check_acl(User2, subscribe, <<"$SYS/testuser/1">>). + +t_check_auth(_) -> + User1 = ?USER(<<"client1">>, <<"testuser1">>, mqtt, {127,0,0,1}, external, undefined), + User2 = ?USER(<<"client2">>, <<"testuser2">>, mqtt, {127,0,0,1}, exteneral, undefined), + User3 = ?USER(<<"client3">>, undefined, mqtt, {127,0,0,1}, exteneral, undefined), + + {ok, #{auth_result := success, + anonymous := false, + is_superuser := false}} = emqx_access_control:authenticate(User1#{password => <<"pass1">>}), + {error, bad_username_or_password} = emqx_access_control:authenticate(User1#{password => <<"pass">>}), + {error, bad_username_or_password} = emqx_access_control:authenticate(User1#{password => <<>>}), + + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(User2#{password => <<"pass2">>}), + {error, bad_username_or_password} = emqx_access_control:authenticate(User2#{password => <<>>}), + {error, bad_username_or_password} = emqx_access_control:authenticate(User2#{password => <<"errorpwd">>}), + + {error, bad_username_or_password} = emqx_access_control:authenticate(User3#{password => <<"pwd">>}). + +t_sub_pub(_) -> + ct:pal("start client"), + {ok, T1} = emqtt:start_link([{host, "localhost"}, + {clientid, <<"client1">>}, + {username, <<"testuser1">>}, + {password, <<"pass1">>}]), + {ok, _} = emqtt:connect(T1), + emqtt:publish(T1, <<"topic">>, <<"body">>, [{qos, 0}, {retain, true}]), + timer:sleep(1000), + {ok, T2} = emqtt:start_link([{host, "localhost"}, + {clientid, <<"client2">>}, + {username, <<"testuser2">>}, + {password, <<"pass2">>}]), + {ok, _} = emqtt:connect(T2), + emqtt:subscribe(T2, <<"topic">>), + receive + {publish, _Topic, Payload} -> + ?assertEqual(<<"body">>, Payload) + after 1000 -> false end, + emqtt:disconnect(T1), + emqtt:disconnect(T2). + +t_comment_config(_) -> + AuthCount = length(emqx_hooks:lookup('client.authenticate')), + AclCount = length(emqx_hooks:lookup('client.check_acl')), + application:stop(?APP), + [application:unset_env(?APP, Par) || Par <- [acl_req, auth_req]], + application:start(?APP), + ?assertEqual([], emqx_hooks:lookup('client.authenticate')), + ?assertEqual(AuthCount - 1, length(emqx_hooks:lookup('client.authenticate'))), + ?assertEqual(AclCount - 1, length(emqx_hooks:lookup('client.check_acl'))). + diff --git a/apps/emqx_auth_http/test/http_auth_server.erl b/apps/emqx_auth_http/test/http_auth_server.erl new file mode 100644 index 000000000..54c4d38b3 --- /dev/null +++ b/apps/emqx_auth_http/test/http_auth_server.erl @@ -0,0 +1,152 @@ +-module(http_auth_server). + +-export([ start/2 + , stop/0 + ]). + +-define(SUPERUSER, [[{"username", "superuser"}, {"clientid", "superclient"}]]). + +-define(ACL, [[{<<"username">>, <<"testuser">>}, + {<<"clientid">>, <<"client1">>}, + {<<"access">>, <<"1">>}, + {<<"topic">>, <<"users/testuser/1">>}, + {<<"ipaddr">>, <<"127.0.0.1">>}, + {<<"mountpoint">>, <<"null">>}], + [{<<"username">>, <<"xyz">>}, + {<<"clientid">>, <<"client2">>}, + {<<"access">>, <<"2">>}, + {<<"topic">>, <<"a/b/c">>}, + {<<"ipaddr">>, <<"192.168.1.3">>}, + {<<"mountpoint">>, <<"null">>}], + [{<<"username">>, <<"testuser1">>}, + {<<"clientid">>, <<"client1">>}, + {<<"access">>, <<"2">>}, + {<<"topic">>, <<"topic">>}, + {<<"ipaddr">>, <<"127.0.0.1">>}, + {<<"mountpoint">>, <<"null">>}], + [{<<"username">>, <<"testuser2">>}, + {<<"clientid">>, <<"client2">>}, + {<<"access">>, <<"1">>}, + {<<"topic">>, <<"topic">>}, + {<<"ipaddr">>, <<"127.0.0.1">>}, + {<<"mountpoint">>, <<"null">>}]]). + +-define(AUTH, [[{<<"clientid">>, <<"client1">>}, + {<<"username">>, <<"testuser1">>}, + {<<"password">>, <<"pass1">>}], + [{<<"clientid">>, <<"client2">>}, + {<<"username">>, <<"testuser2">>}, + {<<"password">>, <<"pass2">>}]]). + +%%------------------------------------------------------------------------------ +%% REST Interface +%%------------------------------------------------------------------------------ + +-rest_api(#{ name => auth + , method => 'GET' + , path => "/mqtt/auth" + , func => authenticate + , descr => "Authenticate user access permission" + }). + +-rest_api(#{ name => is_superuser + , method => 'GET' + , path => "/mqtt/superuser" + , func => is_superuser + , descr => "Is super user" + }). + +-rest_api(#{ name => acl + , method => 'GET' + , path => "/mqtt/acl" + , func => check_acl + , descr => "Check acl" + }). + +-rest_api(#{ name => auth + , method => 'POST' + , path => "/mqtt/auth" + , func => authenticate + , descr => "Authenticate user access permission" + }). + +-rest_api(#{ name => is_superuser + , method => 'POST' + , path => "/mqtt/superuser" + , func => is_superuser + , descr => "Is super user" + }). + +-rest_api(#{ name => acl + , method => 'POST' + , path => "/mqtt/acl" + , func => check_acl + , descr => "Check acl" + }). + +-export([ authenticate/2 + , is_superuser/2 + , check_acl/2 + ]). + +authenticate(_Binding, Params) -> + return(check(Params, ?AUTH)). + +is_superuser(_Binding, Params) -> + return(check(Params, ?SUPERUSER)). + +check_acl(_Binding, Params) -> + return(check(Params, ?ACL)). + +return(allow) -> {200, <<"allow">>}; +return(deny) -> {400, <<"deny">>}. + +start(http, Inet) -> + application:ensure_all_started(minirest), + Handlers = [{"/", minirest:handler(#{modules => [?MODULE]})}], + Dispatch = [{"/[...]", minirest, Handlers}], + minirest:start_http(http_auth_server, #{socket_opts => [Inet, {port, 8991}]}, Dispatch); + +start(https, Inet) -> + application:ensure_all_started(minirest), + Handlers = [{"/", minirest:handler(#{modules => [?MODULE]})}], + Dispatch = [{"/[...]", minirest, Handlers}], + minirest:start_https(http_auth_server, #{socket_opts => [Inet, {port, 8991} | certopts()]}, Dispatch). + +%% @private +certopts() -> + Certfile = filename:join(["etc", "certs", "cert.pem"]), + Keyfile = filename:join(["etc", "certs", "key.pem"]), + CaCert = filename:join(["etc", "certs", "cacert.pem"]), + [{verify, verify_peer}, + {certfile, emqx_ct_helpers:deps_path(emqx, Certfile)}, + {keyfile, emqx_ct_helpers:deps_path(emqx, Keyfile)}, + {cacertfile, emqx_ct_helpers:deps_path(emqx, CaCert)}] ++ emqx_ct_helpers:client_ssl(). + +stop() -> + minirest:stop_http(http_auth_server). + +-spec check(HttpReqParams :: list(), DefinedConf :: list()) -> allow | deny. +check(_Params, []) -> + %ct:pal("check auth_result: deny~n"), + deny; +check(Params, [ConfRecord|T]) -> + % ct:pal("Params: ~p, ConfRecord:~p ~n", [Params, ConfRecord]), + case match_config(Params, ConfRecord) of + not_match -> + check(Params, T); + matched -> allow + end. + +match_config([], _ConfigColumn) -> + %ct:pal("match_config auth_result: matched~n"), + matched; + +match_config([Param|T], ConfigColumn) -> + %ct:pal("Param: ~p, ConfigColumn:~p ~n", [Param, ConfigColumn]), + case lists:member(Param, ConfigColumn) of + true -> + match_config(T, ConfigColumn); + false -> + not_match + end. diff --git a/apps/emqx_auth_jwt/.github/workflows/run_test_cases.yaml b/apps/emqx_auth_jwt/.github/workflows/run_test_cases.yaml new file mode 100644 index 000000000..b8e722570 --- /dev/null +++ b/apps/emqx_auth_jwt/.github/workflows/run_test_cases.yaml @@ -0,0 +1,29 @@ +name: Run test cases + +on: [push, pull_request] + +jobs: + run_test_cases: + runs-on: ubuntu-latest + + container: + image: erlang:22.1 + + steps: + - uses: actions/checkout@v1 + - name: run test cases + run: | + make xref + make eunit + make ct + make cover + - uses: actions/upload-artifact@v1 + if: always() + with: + name: logs + path: _build/test/logs + - uses: actions/upload-artifact@v1 + with: + name: cover + path: _build/test/cover + diff --git a/apps/emqx_auth_jwt/.gitignore b/apps/emqx_auth_jwt/.gitignore new file mode 100644 index 000000000..d038bde4d --- /dev/null +++ b/apps/emqx_auth_jwt/.gitignore @@ -0,0 +1,27 @@ +.eunit +deps +*.o +*.beam +*.plt +erl_crash.dump +ebin +rel/example_project +.concrete/DEV_MODE +.rebar +.erlang.mk/ +emqx_auth_jwt.d +data/ +.DS_Store +cover/ +ct.coverdata +eunit.coverdata +logs/ +test/ct.cover.spec +emq_auth_jwt.d +erlang.mk +_build/ +rebar.lock +rebar3.crashdump +etc/emqx_auth_jwt.conf.rendered +.rebar3/ +*.swp diff --git a/apps/emqx_auth_jwt/LICENSE b/apps/emqx_auth_jwt/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/apps/emqx_auth_jwt/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/apps/emqx_auth_jwt/README.md b/apps/emqx_auth_jwt/README.md new file mode 100644 index 000000000..9675ae87c --- /dev/null +++ b/apps/emqx_auth_jwt/README.md @@ -0,0 +1,90 @@ + +# emqx-auth-jwt + +EMQ X JWT Authentication Plugin + +Build +----- + +``` +make && make tests +``` + +Configure the Plugin +-------------------- + +File: etc/plugins/emqx_auth_jwt.conf + +``` +## HMAC Hash Secret. +## +## Value: String +auth.jwt.secret = emqxsecret + +## From where the JWT string can be got +## +## Value: username | password +## Default: password +auth.jwt.from = password + +## RSA or ECDSA public key file. +## +## Value: File +## auth.jwt.pubkey = etc/certs/jwt_public_key.pem + +## Enable to verify claims fields +## +## Value: on | off +auth.jwt.verify_claims = off + +## The checklist of claims to validate +## +## Value: String +## auth.jwt.verify_claims.$name = expected +## +## Variables: +## - %u: username +## - %c: clientid +# auth.jwt.verify_claims.username = %u +``` + +Load the Plugin +--------------- + +``` +./bin/emqx_ctl plugins load emqx_auth_jwt +``` + +Example +------- + +``` +mosquitto_pub -t 'pub' -m 'hello' -i test -u test -P eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiYm9iIiwiYWdlIjoyOX0.bIV_ZQ8D5nQi0LT8AVkpM4Pd6wmlbpR9S8nOLJAsA8o +``` + +Algorithms +---------- + +The JWT spec supports several algorithms for cryptographic signing. This plugin currently supports: + +* HS256 - HMAC using SHA-256 hash algorithm +* HS384 - HMAC using SHA-384 hash algorithm +* HS512 - HMAC using SHA-512 hash algorithm + +* RS256 - RSA with the SHA-256 hash algorithm +* RS384 - RSA with the SHA-384 hash algorithm +* RS512 - RSA with the SHA-512 hash algorithm + +* ES256 - ECDSA using the P-256 curve +* ES384 - ECDSA using the P-384 curve +* ES512 - ECDSA using the P-512 curve + +License +------- + +Apache License Version 2.0 + +Author +------ + +EMQ X Team. diff --git a/apps/emqx_auth_jwt/TODO.md b/apps/emqx_auth_jwt/TODO.md new file mode 100644 index 000000000..dfd730e0a --- /dev/null +++ b/apps/emqx_auth_jwt/TODO.md @@ -0,0 +1,2 @@ +1. Notice for the [Critical vulnerabilities in JSON Web Token](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/) + diff --git a/apps/emqx_auth_jwt/doc/hmac-vs-ecdsa-for-jwt.txt b/apps/emqx_auth_jwt/doc/hmac-vs-ecdsa-for-jwt.txt new file mode 100644 index 000000000..88fa5ebde --- /dev/null +++ b/apps/emqx_auth_jwt/doc/hmac-vs-ecdsa-for-jwt.txt @@ -0,0 +1,3 @@ + +https://crypto.stackexchange.com/questions/30657/hmac-vs-ecdsa-for-jwt + diff --git a/apps/emqx_auth_jwt/etc/emqx_auth_jwt.conf b/apps/emqx_auth_jwt/etc/emqx_auth_jwt.conf new file mode 100644 index 000000000..b9a1caa04 --- /dev/null +++ b/apps/emqx_auth_jwt/etc/emqx_auth_jwt.conf @@ -0,0 +1,39 @@ +##-------------------------------------------------------------------- +## JWT Auth Plugin +##-------------------------------------------------------------------- + +## HMAC Hash Secret. +## +## Value: String +auth.jwt.secret = emqxsecret + +## From where the JWT string can be got +## +## Value: username | password +## Default: password +auth.jwt.from = password + +## RSA or ECDSA public key file. +## +## Value: File +## auth.jwt.pubkey = etc/certs/jwt_public_key.pem + +## Enable to verify claims fields +## +## Value: on | off +auth.jwt.verify_claims = off + +## The checklist of claims to validate +## +## Value: String +## auth.jwt.verify_claims.$name = expected +## +## Variables: +## - %u: username +## - %c: clientid +# auth.jwt.verify_claims.username = %u + +## The Signature format +## - `der`: The erlang default format +## - `raw`: Compatible with others platform maybe +#auth.jwt.signature_format = der diff --git a/apps/emqx_auth_jwt/priv/emqx_auth_jwt.schema b/apps/emqx_auth_jwt/priv/emqx_auth_jwt.schema new file mode 100644 index 000000000..e8210a8cd --- /dev/null +++ b/apps/emqx_auth_jwt/priv/emqx_auth_jwt.schema @@ -0,0 +1,48 @@ +%%-*- mode: erlang -*- + +{mapping, "auth.jwt.secret", "emqx_auth_jwt.secret", [ + {datatype, string} +]}. + +{mapping, "auth.jwt.from", "emqx_auth_jwt.from", [ + {default, password}, + {datatype, atom} +]}. + +{mapping, "auth.jwt.pubkey", "emqx_auth_jwt.pubkey", [ + {datatype, string} +]}. + +{mapping, "auth.jwt.verify_claims", "emqx_auth_jwt.verify_claims", [ + {default, off}, + {datatype, flag} +]}. + +{mapping, "auth.jwt.verify_claims.$name", "emqx_auth_jwt.verify_claims", [ + {datatype, string} +]}. + +{translation, "emqx_auth_jwt.verify_claims", fun(Conf) -> + case cuttlefish:conf_get("auth.jwt.verify_claims", Conf) of + false -> cuttlefish:unset(); + true -> + lists:foldr( + fun({["auth","jwt","verify_claims", Name], Value}, Acc) -> + [{list_to_atom(Name), list_to_binary(Value)} | Acc]; + ({["auth","jwt","verify_claims"], _Value}, Acc) -> + Acc + end, [], cuttlefish_variable:filter_by_prefix("auth.jwt.verify_claims", Conf)) + end +end}. + +{mapping, "auth.jwt.signature_format", "emqx_auth_jwt.jwerl_opts", [ + {default, "der"}, + {datatype, {enum, [raw, der]}} +]}. + +{translation, "emqx_auth_jwt.jwerl_opts", fun(Conf) -> + Filter = fun(L) -> [I || I <- L, I /= undefined] end, + maps:from_list(Filter( + [{raw, cuttlefish:conf_get("auth.jwt.signature_format", Conf) == raw}] + )) +end}. diff --git a/apps/emqx_auth_jwt/rebar.config b/apps/emqx_auth_jwt/rebar.config new file mode 100644 index 000000000..f711075ba --- /dev/null +++ b/apps/emqx_auth_jwt/rebar.config @@ -0,0 +1,24 @@ +{deps, + [{jwerl, {git, "https://github.com/emqx/jwerl.git", {branch, "1.1.1"}}} + ]}. + +{edoc_opts, [{preprocess, true}]}. +{erl_opts, [warn_unused_vars, + warn_shadow_vars, + warn_unused_import, + warn_obsolete_guard, + debug_info, + {parse_transform}]}. + +{xref_checks, [undefined_function_calls, undefined_functions, + locals_not_used, deprecated_function_calls, + warnings_as_errors, deprecated_functions]}. +{cover_enabled, true}. +{cover_opts, [verbose]}. +{cover_export_enabled, true}. + +{profiles, + [{test, + [{deps, [{emqx_ct_helpers, {git, "http://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}}]} + ]} + ]}. diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src b/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src new file mode 100644 index 000000000..5c76d9114 --- /dev/null +++ b/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src @@ -0,0 +1,14 @@ +{application, emqx_auth_jwt, + [{description, "EMQ X Authentication with JWT"}, + {vsn, "git"}, + {modules, []}, + {registered, [emqx_auth_jwt_sup]}, + {applications, [kernel,stdlib,jwerl]}, + {mod, {emqx_auth_jwt_app, []}}, + {env, []}, + {licenses, ["Apache-2.0"]}, + {maintainers, ["EMQ X Team "]}, + {links, [{"Homepage", "https://emqx.io/"}, + {"Github", "https://github.com/emqx/emqx-auth-jwt"} + ]} + ]}. diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src.script b/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src.script new file mode 100644 index 000000000..0e14ff23f --- /dev/null +++ b/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src.script @@ -0,0 +1,24 @@ +%%-*- mode: erlang -*- +%% .app.src.script + +RemoveLeadingV = + fun(Tag) -> + case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of + nomatch -> + re:replace(Tag, "/", "-", [{return ,list}]); + _ -> + %% if it is a version number prefixed by 'v' or 'e', then remove it + re:replace(Tag, "[v|e]", "", [{return ,list}]) + end + end, + +case os:getenv("EMQX_DEPS_DEFAULT_VSN") of + false -> CONFIG; % env var not defined + [] -> CONFIG; % env var set to empty string + Tag -> + [begin + AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}), + {application, App, AppConf0} + end || Conf = {application, App, AppConf} <- CONFIG] +end. + diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt.erl b/apps/emqx_auth_jwt/src/emqx_auth_jwt.erl new file mode 100644 index 000000000..a00bc2577 --- /dev/null +++ b/apps/emqx_auth_jwt/src/emqx_auth_jwt.erl @@ -0,0 +1,146 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_jwt). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-logger_header("[JWT]"). + +-export([ register_metrics/0 + , check/3 + , description/0 + ]). + +-record(auth_metrics, { + success = 'client.auth.success', + failure = 'client.auth.failure', + ignore = 'client.auth.ignore' + }). + +-define(METRICS(Type), tl(tuple_to_list(#Type{}))). +-define(METRICS(Type, K), #Type{}#Type.K). + +-define(AUTH_METRICS, ?METRICS(auth_metrics)). +-define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). + +-spec(register_metrics() -> ok). +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). + +%%-------------------------------------------------------------------- +%% Authentication callbacks +%%-------------------------------------------------------------------- + +check(ClientInfo, AuthResult, Env = #{from := From, checklists := Checklists}) -> + case maps:find(From, ClientInfo) of + error -> + ok = emqx_metrics:inc(?AUTH_METRICS(ignore)), + {ok, AuthResult#{auth_result => token_undefined, anonymous => false}}; + {ok, Token} -> + try jwerl:header(Token) of + Headers -> + case verify_token(Headers, Token, Env) of + {ok, Claims} -> + {stop, maps:merge(AuthResult, verify_claims(Checklists, Claims, ClientInfo))}; + {error, Reason} -> + ok = emqx_metrics:inc(?AUTH_METRICS(failure)), + {stop, AuthResult#{auth_result => Reason, anonymous => false}} + end + catch + _Error:Reason -> + ?LOG(error, "Check token error: ~p", [Reason]), + emqx_metrics:inc(?AUTH_METRICS(ignore)) + end + end. + +description() -> "Authentication with JWT". + +%%-------------------------------------------------------------------- +%% Verify Token +%%-------------------------------------------------------------------- + +verify_token(#{alg := <<"HS", _/binary>>}, _Token, #{secret := undefined}) -> + {error, hmac_secret_undefined}; +verify_token(#{alg := Alg = <<"HS", _/binary>>}, Token, #{secret := Secret, opts := Opts}) -> + verify_token2(Alg, Token, Secret, Opts); + +verify_token(#{alg := <<"RS", _/binary>>}, _Token, #{pubkey := undefined}) -> + {error, rsa_pubkey_undefined}; +verify_token(#{alg := Alg = <<"RS", _/binary>>}, Token, #{pubkey := PubKey, opts := Opts}) -> + verify_token2(Alg, Token, PubKey, Opts); + +verify_token(#{alg := <<"ES", _/binary>>}, _Token, #{pubkey := undefined}) -> + {error, ecdsa_pubkey_undefined}; +verify_token(#{alg := Alg = <<"ES", _/binary>>}, Token, #{pubkey := PubKey, opts := Opts}) -> + verify_token2(Alg, Token, PubKey, Opts); + +verify_token(Header, _Token, _Env) -> + ?LOG(error, "Unsupported token algorithm: ~p", [Header]), + {error, token_unsupported}. + +verify_token2(Alg, Token, SecretOrKey, Opts) -> + try jwerl:verify(Token, decode_algo(Alg), SecretOrKey, #{}, Opts) of + {ok, Claims} -> + {ok, Claims}; + {error, Reason} -> + {error, Reason} + catch + _Error:Reason -> + {error, Reason} + end. + +decode_algo(<<"HS256">>) -> hs256; +decode_algo(<<"HS384">>) -> hs384; +decode_algo(<<"HS512">>) -> hs512; +decode_algo(<<"RS256">>) -> rs256; +decode_algo(<<"RS384">>) -> rs384; +decode_algo(<<"RS512">>) -> rs512; +decode_algo(<<"ES256">>) -> es256; +decode_algo(<<"ES384">>) -> es384; +decode_algo(<<"ES512">>) -> es512; +decode_algo(<<"none">>) -> none; +decode_algo(Alg) -> throw({error, {unsupported_algorithm, Alg}}). + +%%-------------------------------------------------------------------- +%% Verify Claims +%%-------------------------------------------------------------------- + +verify_claims(Checklists, Claims, ClientInfo) -> + case do_verify_claims(feedvar(Checklists, ClientInfo), Claims) of + {error, Reason} -> + ok = emqx_metrics:inc(?AUTH_METRICS(failure)), + #{auth_result => Reason, anonymous => false}; + ok -> + ok = emqx_metrics:inc(?AUTH_METRICS(success)), + #{auth_result => success, anonymous => false, jwt_claims => Claims} + end. + +do_verify_claims([], _Claims) -> + ok; +do_verify_claims([{Key, Expected} | L], Claims) -> + case maps:get(Key, Claims, undefined) =:= Expected of + true -> do_verify_claims(L, Claims); + false -> {error, {verify_claim_failed, Key}} + end. + +feedvar(Checklists, #{username := Username, clientid := ClientId}) -> + lists:map(fun({K, <<"%u">>}) -> {K, Username}; + ({K, <<"%c">>}) -> {K, ClientId}; + ({K, Expected}) -> {K, Expected} + end, Checklists). + diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt_app.erl b/apps/emqx_auth_jwt/src/emqx_auth_jwt_app.erl new file mode 100644 index 000000000..511f6e826 --- /dev/null +++ b/apps/emqx_auth_jwt/src/emqx_auth_jwt_app.erl @@ -0,0 +1,69 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_jwt_app). + +-behaviour(application). + +-behaviour(supervisor). + +-emqx_plugin(auth). + +-export([start/2, stop/1]). + +-export([init/1]). + +-define(APP, emqx_auth_jwt). + +-define(JWT_ACTION, {emqx_auth_jwt, check, [auth_env()]}). + +start(_Type, _Args) -> + ok = emqx_auth_jwt:register_metrics(), + emqx:hook('client.authenticate', ?JWT_ACTION), + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +stop(_State) -> + emqx:unhook('client.authenticate', ?JWT_ACTION). + +%%-------------------------------------------------------------------- +%% Dummy supervisor +%%-------------------------------------------------------------------- + +init([]) -> + {ok, { {one_for_all, 1, 10}, []} }. + +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- + +auth_env() -> + #{secret => env(secret, undefined), + from => env(from, password), + pubkey => read_pubkey(), + checklists => env(verify_claims, []), + opts => env(jwerl_opts, #{}) + }. + +read_pubkey() -> + case env(pubkey, undefined) of + undefined -> undefined; + Path -> + {ok, PubKey} = file:read_file(Path), PubKey + end. + +env(Key, Default) -> + application:get_env(?APP, Key, Default). + diff --git a/apps/emqx_auth_jwt/test/emqx_auth_jwt_SUITE.erl b/apps/emqx_auth_jwt/test/emqx_auth_jwt_SUITE.erl new file mode 100644 index 000000000..190c3db14 --- /dev/null +++ b/apps/emqx_auth_jwt/test/emqx_auth_jwt_SUITE.erl @@ -0,0 +1,137 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_jwt_SUITE). + +-compile(nowarn_export_all). +-compile(export_all). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). + +-define(APP, emqx_auth_jwt). + +all() -> + [{group, emqx_auth_jwt}]. + +groups() -> + [{emqx_auth_jwt, [sequence], [ t_check_auth + , t_check_claims + , t_check_claims_clientid + , t_check_claims_username + ]} + ]. + +init_per_suite(Config) -> + emqx_ct_helpers:start_apps([emqx, emqx_auth_jwt], fun set_special_configs/1), + Config. + +end_per_suite(_Config) -> + emqx_ct_helpers:stop_apps([emqx_auth_jwt, emqx]). + +set_special_configs(emqx) -> + application:set_env(emqx, allow_anonymous, false), + application:set_env(emqx, acl_nomatch, deny), + application:set_env(emqx, enable_acl_cache, false), + LoadedPluginPath = filename:join(["test", "emqx_SUITE_data", "loaded_plugins"]), + AclFilePath = filename:join(["test", "emqx_SUITE_data", "acl.conf"]), + application:set_env(emqx, plugins_loaded_file, + emqx_ct_helpers:deps_path(emqx, LoadedPluginPath)), + application:set_env(emqx, acl_file, + emqx_ct_helpers:deps_path(emqx, AclFilePath)); + +set_special_configs(emqx_auth_jwt) -> + application:set_env(emqx_auth_jwt, secret, "emqxsecret"), + application:set_env(emqx_auth_jwt, from, password); + +set_special_configs(_) -> + ok. + +%%------------------------------------------------------------------------------ +%% Testcases +%%------------------------------------------------------------------------------ + +t_check_auth(_) -> + Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external}, + Jwt = jwerl:sign([{clientid, <<"client1">>}, + {username, <<"plain">>}, + {exp, os:system_time(seconds) + 3}], hs256, <<"emqxsecret">>), + ct:pal("Jwt: ~p~n", [Jwt]), + + Result0 = emqx_access_control:authenticate(Plain#{password => Jwt}), + ct:pal("Auth result: ~p~n", [Result0]), + ?assertMatch({ok, #{auth_result := success, jwt_claims := #{clientid := <<"client1">>}}}, Result0), + + ct:sleep(3100), + Result1 = emqx_access_control:authenticate(Plain#{password => Jwt}), + ct:pal("Auth result after 1000ms: ~p~n", [Result1]), + ?assertMatch({error, _}, Result1), + + Jwt_Error = jwerl:sign([{clientid, <<"client1">>}, + {username, <<"plain">>}], hs256, <<"secret">>), + ct:pal("invalid jwt: ~p~n", [Jwt_Error]), + Result2 = emqx_access_control:authenticate(Plain#{password => Jwt_Error}), + ct:pal("Auth result for the invalid jwt: ~p~n", [Result2]), + ?assertEqual({error, invalid_signature}, Result2), + ?assertMatch({error, _}, emqx_access_control:authenticate(Plain#{password => <<"asd">>})). + +t_check_claims(_) -> + application:set_env(emqx_auth_jwt, verify_claims, [{sub, <<"value">>}]), + Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external}, + Jwt = jwerl:sign([{clientid, <<"client1">>}, + {username, <<"plain">>}, + {sub, value}, + {exp, os:system_time(seconds) + 3}], hs256, <<"emqxsecret">>), + Result0 = emqx_access_control:authenticate(Plain#{password => Jwt}), + ct:pal("Auth result: ~p~n", [Result0]), + ?assertMatch({ok, #{auth_result := success, jwt_claims := _}}, Result0), + Jwt_Error = jwerl:sign([{clientid, <<"client1">>}, + {username, <<"plain">>}], hs256, <<"secret">>), + Result2 = emqx_access_control:authenticate(Plain#{password => Jwt_Error}), + ct:pal("Auth result for the invalid jwt: ~p~n", [Result2]), + ?assertEqual({error, invalid_signature}, Result2). + +t_check_claims_clientid(_) -> + application:set_env(emqx_auth_jwt, verify_claims, [{clientid, <<"%c">>}]), + Plain = #{clientid => <<"client23">>, username => <<"plain">>, zone => external}, + Jwt = jwerl:sign([{clientid, <<"client23">>}, + {username, <<"plain">>}, + {exp, os:system_time(seconds) + 3}], hs256, <<"emqxsecret">>), + Result0 = emqx_access_control:authenticate(Plain#{password => Jwt}), + ct:pal("Auth result: ~p~n", [Result0]), + ?assertMatch({ok, #{auth_result := success, jwt_claims := _}}, Result0), + Jwt_Error = jwerl:sign([{clientid, <<"client1">>}, + {username, <<"plain">>}], hs256, <<"secret">>), + Result2 = emqx_access_control:authenticate(Plain#{password => Jwt_Error}), + ct:pal("Auth result for the invalid jwt: ~p~n", [Result2]), + ?assertEqual({error, invalid_signature}, Result2). + +t_check_claims_username(_) -> + application:set_env(emqx_auth_jwt, verify_claims, [{username, <<"%u">>}]), + Plain = #{clientid => <<"client23">>, username => <<"plain">>, zone => external}, + Jwt = jwerl:sign([{clientid, <<"client23">>}, + {username, <<"plain">>}, + {exp, os:system_time(seconds) + 3}], hs256, <<"emqxsecret">>), + Result0 = emqx_access_control:authenticate(Plain#{password => Jwt}), + ct:pal("Auth result: ~p~n", [Result0]), + ?assertMatch({ok, #{auth_result := success, jwt_claims := _}}, Result0), + Jwt_Error = jwerl:sign([{clientid, <<"client1">>}, + {username, <<"plain">>}], hs256, <<"secret">>), + Result3 = emqx_access_control:authenticate(Plain#{password => Jwt_Error}), + ct:pal("Auth result for the invalid jwt: ~p~n", [Result3]), + ?assertEqual({error, invalid_signature}, Result3). + diff --git a/apps/emqx_auth_ldap/.ci/docker-compose.yml b/apps/emqx_auth_ldap/.ci/docker-compose.yml new file mode 100644 index 000000000..bba9b711f --- /dev/null +++ b/apps/emqx_auth_ldap/.ci/docker-compose.yml @@ -0,0 +1,26 @@ +version: '3' + +services: + erlang: + image: erlang:22.1 + volumes: + - ../:/emqx_auth_ldap + networks: + - emqx_bridge + depends_on: + - ldap_server + tty: true + + ldap_server: + build: ./emqx-ldap + image: emqx-ldap:1.0 + restart: always + ports: + - 389:389 + - 636:636 + networks: + - emqx_bridge + +networks: + emqx_bridge: + driver: bridge diff --git a/apps/emqx_auth_ldap/.ci/emqx-ldap/Dockerfile b/apps/emqx_auth_ldap/.ci/emqx-ldap/Dockerfile new file mode 100644 index 000000000..0a01572c4 --- /dev/null +++ b/apps/emqx_auth_ldap/.ci/emqx-ldap/Dockerfile @@ -0,0 +1,26 @@ +FROM buildpack-deps:stretch + +ENV VERSION=2.4.50 + +RUN apt-get update && apt-get install -y groff groff-base +RUN wget ftp://ftp.openldap.org/pub/OpenLDAP/openldap-release/openldap-${VERSION}.tgz \ + && gunzip -c openldap-${VERSION}.tgz | tar xvfB - \ + && cd openldap-${VERSION} \ + && ./configure && make depend && make && make install \ + && cd .. && rm -rf openldap-${VERSION} + +COPY ./slapd.conf /usr/local/etc/openldap/slapd.conf +COPY ./emqx.io.ldif /usr/local/etc/openldap/schema/emqx.io.ldif +COPY ./emqx.schema /usr/local/etc/openldap/schema/emqx.schema +COPY ./*.pem /usr/local/etc/openldap/ + +RUN mkdir -p /usr/local/etc/openldap/data \ + && slapadd -l /usr/local/etc/openldap/schema/emqx.io.ldif -f /usr/local/etc/openldap/slapd.conf + +WORKDIR /usr/local/etc/openldap + +EXPOSE 389 636 + +ENTRYPOINT ["/usr/local/libexec/slapd", "-h", "ldap:/// ldaps:///", "-d", "3", "-f", "/usr/local/etc/openldap/slapd.conf"] + +CMD [] diff --git a/apps/emqx_auth_ldap/.ci/emqx-ldap/slapd.conf b/apps/emqx_auth_ldap/.ci/emqx-ldap/slapd.conf new file mode 100644 index 000000000..d6ba20caa --- /dev/null +++ b/apps/emqx_auth_ldap/.ci/emqx-ldap/slapd.conf @@ -0,0 +1,16 @@ +include /usr/local/etc/openldap/schema/core.schema +include /usr/local/etc/openldap/schema/cosine.schema +include /usr/local/etc/openldap/schema/inetorgperson.schema +include /usr/local/etc/openldap/schema/ppolicy.schema +include /usr/local/etc/openldap/schema/emqx.schema + +TLSCACertificateFile /usr/local/etc/openldap/cacert.pem +TLSCertificateFile /usr/local/etc/openldap/cert.pem +TLSCertificateKeyFile /usr/local/etc/openldap/key.pem + +database bdb +suffix "dc=emqx,dc=io" +rootdn "cn=root,dc=emqx,dc=io" +rootpw {SSHA}eoF7NhNrejVYYyGHqnt+MdKNBh4r1w3W + +directory /usr/local/etc/openldap/data diff --git a/apps/emqx_auth_ldap/.github/workflows/run_test_cases.yaml b/apps/emqx_auth_ldap/.github/workflows/run_test_cases.yaml new file mode 100644 index 000000000..dd6978406 --- /dev/null +++ b/apps/emqx_auth_ldap/.github/workflows/run_test_cases.yaml @@ -0,0 +1,49 @@ +name: Run test cases + +on: [push, pull_request] + +jobs: + run_test_cases: + runs-on: ubuntu-latest + + strategy: + matrix: + network_type: + - ipv4 + - ipv6 + + steps: + - name: install docker-compose + run: | + sudo curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + - uses: actions/checkout@v1 + - name: prepare + env: + NETWORK_TYPE: ${{ matrix.network_type }} + run: | + set -e -x -u + cp ./emqx.io.ldif ./emqx.schema ./.ci/emqx-ldap + cp ./test/certs/* ./.ci/emqx-ldap + docker-compose -f ./.ci/docker-compose.yml -p tests build + if [ "$NETWORK_TYPE" = "ipv6" ];then docker network create --driver bridge --ipv6 --subnet fd15:555::/64 tests_emqx_bridge --attachable; fi + docker-compose -f ./.ci/docker-compose.yml -p tests up -d + if [ "$NETWORK_TYPE" != "ipv6" ];then + docker exec -i tests_erlang_1 sh -c "sed -i '/auth.ldap.servers/c auth.ldap.servers = ldap_server' ./emqx_auth_ldap/etc/emqx_auth_ldap.conf" + else + ipv6_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' $(docker ps -a -f name=tests_ldap_server_1 -q)) + docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "sed -i '/auth.ldap.servers/c auth.ldap.servers = $ipv6_address' /emqx_auth_ldap/etc/emqx_auth_ldap.conf" + fi + - name: run test cases + run: | + set -e -x -u + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_ldap xref" + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_ldap eunit" + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_ldap ct" + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_ldap cover" + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: logs_${{ matrix.network_type }} + path: _build/test/logs + diff --git a/apps/emqx_auth_ldap/.gitignore b/apps/emqx_auth_ldap/.gitignore new file mode 100644 index 000000000..eb8f0639f --- /dev/null +++ b/apps/emqx_auth_ldap/.gitignore @@ -0,0 +1,25 @@ +.eunit +deps +*.o +*.beam +*.plt +erl_crash.dump +ebin +rel/example_project +.concrete/DEV_MODE +.rebar +.erlang.mk/ +emqx_auth_ldap.d +data/ +cover/ +ct.coverdata +eunit.coverdata +logs/ +test/ct.cover.spec +.DS_Store +_build/ +rebar.lock +erlang.mk +rebar3.crashdump +.rebar3/ +etc/emqx_auth_ldap.conf.rendered diff --git a/apps/emqx_auth_ldap/LICENSE b/apps/emqx_auth_ldap/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/apps/emqx_auth_ldap/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/apps/emqx_auth_ldap/README.md b/apps/emqx_auth_ldap/README.md new file mode 100644 index 000000000..c4d56c839 --- /dev/null +++ b/apps/emqx_auth_ldap/README.md @@ -0,0 +1,96 @@ +emqx_auth_ldap +============== + +EMQ X LDAP Authentication Plugin + +Build +----- + +``` +make +``` + +Load the Plugin +--------------- + +``` +# ./bin/emqx_ctl plugins load emqx_auth_ldap +``` + +Generate Password +--------------- + +``` +slappasswd -h '{ssha}' -s public +``` + +Configuration Open LDAP +----------------------- + +vim /etc/openldap/slapd.conf + +``` +include /etc/openldap/schema/core.schema +include /etc/openldap/schema/cosine.schema +include /etc/openldap/schema/inetorgperson.schema +include /etc/openldap/schema/ppolicy.schema +include /etc/openldap/schema/emqx.schema + +database bdb +suffix "dc=emqx,dc=io" +rootdn "cn=root,dc=emqx,dc=io" +rootpw {SSHA}eoF7NhNrejVYYyGHqnt+MdKNBh4r1w3W + +directory /etc/openldap/data +``` + +If the ldap launched with error below: +``` +Unrecognized database type (bdb) +5c4a72b9 slapd.conf: line 7: failed init (bdb) +slapadd: bad configuration file! +``` + +Insert lines to the slapd.conf +``` +modulepath /usr/lib/ldap +moduleload back_bdb.la +``` + +Import EMQX User Data +---------------------- + +Use ldapadd +``` +# ldapadd -x -D "cn=root,dc=emqx,dc=io" -w public -f emqx.com.ldif +``` + +Use slapadd +``` +# sudo slapadd -l schema/emqx.io.ldif -f slapd.conf +``` + +Launch slapd +``` +# sudo slapd -d 3 +``` + +Test +----- +After configure slapd correctly and launch slapd successfully. +You could execute + +``` bash +# make tests +``` + +License +------- + +Apache License Version 2.0 + +Author +------ + +EMQ X Team. + diff --git a/apps/emqx_auth_ldap/emqx.io.ldif b/apps/emqx_auth_ldap/emqx.io.ldif new file mode 100644 index 000000000..f9833cd88 --- /dev/null +++ b/apps/emqx_auth_ldap/emqx.io.ldif @@ -0,0 +1,135 @@ +## create emqx.io + +dn:dc=emqx,dc=io +objectclass: top +objectclass: dcobject +objectclass: organization +dc:emqx +o:emqx,Inc. + +# create testdevice.emqx.io +dn:ou=testdevice,dc=emqx,dc=io +objectClass: top +objectclass:organizationalUnit +ou:testdevice + +# create user admin +dn:uid=admin,ou=testdevice,dc=emqx,dc=io +objectClass: top +objectClass: simpleSecurityObject +objectClass: account +userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9 +uid: admin + +## create user=mqttuser0001, +# password=mqttuser0001, +# passhash={SHA}mlb3fat40MKBTXUVZwCKmL73R/0= +# base64passhash=e1NIQX1tbGIzZmF0NDBNS0JUWFVWWndDS21MNzNSLzA9 +dn:uid=mqttuser0001,ou=testdevice,dc=emqx,dc=io +objectClass: top +objectClass: mqttUser +objectClass: mqttDevice +objectClass: mqttSecurity +uid: mqttuser0001 +isEnabled: TRUE +mqttAccountName: user1 +mqttPublishTopic: mqttuser0001/pub/1 +mqttPublishTopic: mqttuser0001/pub/+ +mqttPublishTopic: mqttuser0001/pub/# +mqttSubscriptionTopic: mqttuser0001/sub/1 +mqttSubscriptionTopic: mqttuser0001/sub/+ +mqttSubscriptionTopic: mqttuser0001/sub/# +mqttPubSubTopic: mqttuser0001/pubsub/1 +mqttPubSubTopic: mqttuser0001/pubsub/+ +mqttPubSubTopic: mqttuser0001/pubsub/# +userPassword:: e1NIQX1tbGIzZmF0NDBNS0JUWFVWWndDS21MNzNSLzA9 + +## create user=mqttuser0002 +# password=mqttuser0002, +# passhash={SSHA}n9XdtoG4Q/TQ3TQF4Y+khJbMBH4qXj4M +# base64passhash=e1NTSEF9bjlYZHRvRzRRL1RRM1RRRjRZK2toSmJNQkg0cVhqNE0= +dn:uid=mqttuser0002,ou=testdevice,dc=emqx,dc=io +objectClass: top +objectClass: mqttUser +objectClass: mqttDevice +objectClass: mqttSecurity +uid: mqttuser0002 +isEnabled: TRUE +mqttAccountName: user2 +mqttPublishTopic: mqttuser0002/pub/1 +mqttPublishTopic: mqttuser0002/pub/+ +mqttPublishTopic: mqttuser0002/pub/# +mqttSubscriptionTopic: mqttuser0002/sub/1 +mqttSubscriptionTopic: mqttuser0002/sub/+ +mqttSubscriptionTopic: mqttuser0002/sub/# +mqttPubSubTopic: mqttuser0002/pubsub/1 +mqttPubSubTopic: mqttuser0002/pubsub/+ +mqttPubSubTopic: mqttuser0002/pubsub/# +userPassword:: e1NTSEF9bjlYZHRvRzRRL1RRM1RRRjRZK2toSmJNQkg0cVhqNE0= + +## create user mqttuser0003 +# password=mqttuser0003, +# passhash={MD5}ybsPGoaK3nDyiQvveiCOIw== +# base64passhash=e01ENX15YnNQR29hSzNuRHlpUXZ2ZWlDT0l3PT0= +dn:uid=mqttuser0003,ou=testdevice,dc=emqx,dc=io +objectClass: top +objectClass: mqttUser +objectClass: mqttDevice +objectClass: mqttSecurity +uid: mqttuser0003 +isEnabled: TRUE +mqttPublishTopic: mqttuser0003/pub/1 +mqttPublishTopic: mqttuser0003/pub/+ +mqttPublishTopic: mqttuser0003/pub/# +mqttSubscriptionTopic: mqttuser0003/sub/1 +mqttSubscriptionTopic: mqttuser0003/sub/+ +mqttSubscriptionTopic: mqttuser0003/sub/# +mqttPubSubTopic: mqttuser0003/pubsub/1 +mqttPubSubTopic: mqttuser0003/pubsub/+ +mqttPubSubTopic: mqttuser0003/pubsub/# +userPassword:: e01ENX15YnNQR29hSzNuRHlpUXZ2ZWlDT0l3PT0= + +## create user mqttuser0004 +# password=mqttuser0004, +# passhash={MD5}2Br6pPDSEDIEvUlu9+s+MA== +# base64passhash=e01ENX0yQnI2cFBEU0VESUV2VWx1OStzK01BPT0= +dn:uid=mqttuser0004,ou=testdevice,dc=emqx,dc=io +objectClass: top +objectClass: mqttUser +objectClass: mqttDevice +objectClass: mqttSecurity +uid: mqttuser0004 +isEnabled: TRUE +mqttPublishTopic: mqttuser0004/pub/1 +mqttPublishTopic: mqttuser0004/pub/+ +mqttPublishTopic: mqttuser0004/pub/# +mqttSubscriptionTopic: mqttuser0004/sub/1 +mqttSubscriptionTopic: mqttuser0004/sub/+ +mqttSubscriptionTopic: mqttuser0004/sub/# +mqttPubSubTopic: mqttuser0004/pubsub/1 +mqttPubSubTopic: mqttuser0004/pubsub/+ +mqttPubSubTopic: mqttuser0004/pubsub/# +userPassword: {MD5}2Br6pPDSEDIEvUlu9+s+MA== + +## create user mqttuser0005 +# password=mqttuser0005, +# passhash={SHA}jKnxeEDGR14kE8AR7yuVFOelhz4= +# base64passhash=e1NIQX1qS254ZUVER1IxNGtFOEFSN3l1VkZPZWxoejQ9 +objectClass: top +dn:uid=mqttuser0005,ou=testdevice,dc=emqx,dc=io +objectClass: mqttUser +objectClass: mqttDevice +objectClass: mqttSecurity +uid: mqttuser0005 +isEnabled: TRUE +mqttPublishTopic: mqttuser0005/pub/1 +mqttPublishTopic: mqttuser0005/pub/+ +mqttPublishTopic: mqttuser0005/pub/# +mqttSubscriptionTopic: mqttuser0005/sub/1 +mqttSubscriptionTopic: mqttuser0005/sub/+ +mqttSubscriptionTopic: mqttuser0005/sub/# +mqttPubSubTopic: mqttuser0005/pubsub/1 +mqttPubSubTopic: mqttuser0005/pubsub/+ +mqttPubSubTopic: mqttuser0005/pubsub/# +userPassword: {SHA}jKnxeEDGR14kE8AR7yuVFOelhz4= + diff --git a/apps/emqx_auth_ldap/emqx.schema b/apps/emqx_auth_ldap/emqx.schema new file mode 100644 index 000000000..55f92269b --- /dev/null +++ b/apps/emqx_auth_ldap/emqx.schema @@ -0,0 +1,46 @@ +# +# Preliminary Apple OS X Native LDAP Schema +# This file is subject to change. +# +attributetype ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.1.3 NAME 'isEnabled' + EQUALITY booleanMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 + SINGLE-VALUE + USAGE userApplications ) + +attributetype ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.4.1 NAME ( 'mqttPublishTopic' 'mpt' ) + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + USAGE userApplications ) +attributetype ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.4.2 NAME ( 'mqttSubscriptionTopic' 'mst' ) + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + USAGE userApplications ) +attributetype ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.4.3 NAME ( 'mqttPubSubTopic' 'mpst' ) + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + USAGE userApplications ) +attributetype ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.4.4 NAME ( 'mqttAccountName' 'man' ) + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + USAGE userApplications ) + + +objectclass ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.4 NAME 'mqttUser' + AUXILIARY + MAY ( mqttPublishTopic $ mqttSubscriptionTopic $ mqttPubSubTopic $ mqttAccountName) ) + +objectclass ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.2 NAME 'mqttDevice' + SUP top + STRUCTURAL + MUST ( uid ) + MAY ( isEnabled ) ) + +objectclass ( 1.3.6.1.4.1.11.2.53.2.2.3.1.2.3.3 NAME 'mqttSecurity' + SUP top + AUXILIARY + MAY ( userPassword $ userPKCS12 $ pwdAttribute $ pwdLockout ) ) diff --git a/apps/emqx_auth_ldap/etc/emqx_auth_ldap.conf b/apps/emqx_auth_ldap/etc/emqx_auth_ldap.conf new file mode 100644 index 000000000..746510fb3 --- /dev/null +++ b/apps/emqx_auth_ldap/etc/emqx_auth_ldap.conf @@ -0,0 +1,78 @@ +##-------------------------------------------------------------------- +## LDAP Auth Plugin +##-------------------------------------------------------------------- + +## LDAP server list, seperated by ','. +## +## Value: String +auth.ldap.servers = 127.0.0.1 + +## LDAP server port. +## +## Value: Port +auth.ldap.port = 389 + +## LDAP pool size +## +## Value: String +auth.ldap.pool = 8 + +## LDAP Bind DN. +## +## Value: DN +auth.ldap.bind_dn = cn=root,dc=emqx,dc=io + +## LDAP Bind Password. +## +## Value: String +auth.ldap.bind_password = public + +## LDAP query timeout. +## +## Value: Number +auth.ldap.timeout = 30s + +## Device DN. +## +## Variables: +## +## Value: DN +auth.ldap.device_dn = ou=device,dc=emqx,dc=io + +## Specified ObjectClass +## +## Variables: +## +## Value: string +auth.ldap.match_objectclass = mqttUser + +## attributetype for username +## +## Variables: +## +## Value: string +auth.ldap.username.attributetype = uid + +## attributetype for password +## +## Variables: +## +## Value: string +auth.ldap.password.attributetype = userPassword + +## Whether to enable SSL. +## +## Value: true | false +auth.ldap.ssl = false + +#auth.ldap.ssl.certfile = etc/certs/cert.pem + +#auth.ldap.ssl.keyfile = etc/certs/key.pem + +#auth.ldap.ssl.cacertfile = etc/certs/cacert.pem + +#auth.ldap.ssl.verify = verify_peer + +#auth.ldap.ssl.fail_if_no_peer_cert = true + +#auth.ldap.ssl.server_name_indication = your_server_name diff --git a/apps/emqx_auth_ldap/include/emqx_auth_ldap.hrl b/apps/emqx_auth_ldap/include/emqx_auth_ldap.hrl new file mode 100644 index 000000000..8950c0ec8 --- /dev/null +++ b/apps/emqx_auth_ldap/include/emqx_auth_ldap.hrl @@ -0,0 +1,23 @@ + +-define(APP, emqx_auth_ldap). + +-record(auth_metrics, { + success = 'client.auth.success', + failure = 'client.auth.failure', + ignore = 'client.auth.ignore' + }). + +-record(acl_metrics, { + allow = 'client.acl.allow', + deny = 'client.acl.deny', + ignore = 'client.acl.ignore' + }). + +-define(METRICS(Type), tl(tuple_to_list(#Type{}))). +-define(METRICS(Type, K), #Type{}#Type.K). + +-define(AUTH_METRICS, ?METRICS(auth_metrics)). +-define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). + +-define(ACL_METRICS, ?METRICS(acl_metrics)). +-define(ACL_METRICS(K), ?METRICS(acl_metrics, K)). diff --git a/apps/emqx_auth_ldap/priv/emqx_auth_ldap.schema b/apps/emqx_auth_ldap/priv/emqx_auth_ldap.schema new file mode 100644 index 000000000..554752a0b --- /dev/null +++ b/apps/emqx_auth_ldap/priv/emqx_auth_ldap.schema @@ -0,0 +1,176 @@ +%%-*- mode: erlang -*- +%% emqx_auth_ldap config mapping + +{mapping, "auth.ldap.servers", "emqx_auth_ldap.ldap", [ + {default, "127.0.0.1"}, + {datatype, string} +]}. + +{mapping, "auth.ldap.port", "emqx_auth_ldap.ldap", [ + {default, 389}, + {datatype, integer} +]}. + +{mapping, "auth.ldap.pool", "emqx_auth_ldap.ldap", [ + {default, 8}, + {datatype, integer} +]}. + +{mapping, "auth.ldap.bind_dn", "emqx_auth_ldap.ldap", [ + {datatype, string}, + {default, "cn=root,dc=emqx,dc=io"} +]}. + +{mapping, "auth.ldap.bind_password", "emqx_auth_ldap.ldap", [ + {datatype, string}, + {default, "public"} +]}. + +{mapping, "auth.ldap.timeout", "emqx_auth_ldap.ldap", [ + {default, "30s"}, + {datatype, {duration, ms}} +]}. + +{mapping, "auth.ldap.ssl", "emqx_auth_ldap.ldap", [ + {default, false}, + {datatype, {enum, [true, false]}} +]}. + +{mapping, "auth.ldap.ssl.certfile", "emqx_auth_ldap.ldap", [ + {datatype, string} +]}. + +{mapping, "auth.ldap.ssl.keyfile", "emqx_auth_ldap.ldap", [ + {datatype, string} +]}. + +{mapping, "auth.ldap.ssl.cacertfile", "emqx_auth_ldap.ldap", [ + {datatype, string} +]}. + +{mapping, "auth.ldap.ssl.verify", "emqx_auth_ldap.ldap", [ + {default, verify_none}, + {datatype, {enum, [verify_none, verify_peer]}} +]}. + +{mapping, "auth.ldap.ssl.fail_if_no_peer_cert", "emqx_auth_ldap.ldap", [ + {datatype, {enum, [true, false]}} +]}. + +{mapping, "auth.ldap.ssl.server_name_indication", "emqx_auth_ldap.ldap", [ + {datatype, string} +]}. + +{translation, "emqx_auth_ldap.ldap", fun(Conf) -> + A2N = fun(A) -> case inet:parse_address(A) of {ok, N} -> N; _ -> A end end, + Servers = [A2N(A) || A <- string:tokens(cuttlefish:conf_get("auth.ldap.servers", Conf), ",")], + Port = cuttlefish:conf_get("auth.ldap.port", Conf), + Pool = cuttlefish:conf_get("auth.ldap.pool", Conf), + BindDN = cuttlefish:conf_get("auth.ldap.bind_dn", Conf), + BindPassword = cuttlefish:conf_get("auth.ldap.bind_password", Conf), + Timeout = cuttlefish:conf_get("auth.ldap.timeout", Conf), + Filter = fun(Ls) -> [E || E = {_, V} <- Ls, V /= undefined]end, + SslOpts = fun() -> + [{certfile, cuttlefish:conf_get("auth.ldap.ssl.certfile", Conf)}, + {keyfile, cuttlefish:conf_get("auth.ldap.ssl.keyfile", Conf)}, + {cacertfile, cuttlefish:conf_get("auth.ldap.ssl.cacertfile", Conf, undefined)}, + {verify, cuttlefish:conf_get("auth.ldap.ssl.verify", Conf, undefined)}, + {server_name_indication, cuttlefish:conf_get("auth.ldap.ssl.server_name_indication", Conf, disable)}, + {fail_if_no_peer_cert, cuttlefish:conf_get("auth.ldap.ssl.fail_if_no_peer_cert", Conf, undefined)}] + end, + Opts = [{servers, Servers}, + {port, Port}, + {timeout, Timeout}, + {bind_dn, BindDN}, + {bind_password, BindPassword}, + {pool, Pool}, + {auto_reconnect, 2}], + case cuttlefish:conf_get("auth.ldap.ssl", Conf) of + true -> [{ssl, true}, {sslopts, Filter(SslOpts())}|Opts]; + false -> [{ssl, false}|Opts] + end +end}. + +{mapping, "auth.ldap.device_dn", "emqx_auth_ldap.device_dn", [ + {default, "ou=device,dc=emqx,dc=io"}, + {datatype, string} +]}. + +{mapping, "auth.ldap.match_objectclass", "emqx_auth_ldap.match_objectclass", [ + {default, "mqttUser"}, + {datatype, string} +]}. + +{mapping, "auth.ldap.custom_base_dn", "emqx_auth_ldap.custom_base_dn", [ + {default, "${username_attr}=${user},${device_dn}"}, + {datatype, string} +]}. + +%% auth.ldap.filters.1.key = "objectClass" +%% auth.ldap.filters.1.value = "mqttUser" +%% auth.ldap.filters.1.op = "and" +%% auth.ldap.filters.2.key = "uiAttr" +%% auth.ldap.filters.2.value "someAttr" +%% auth.ldap.filters.2.op = "or" +%% auth.ldap.filters.3.key = "someKey" +%% auth.ldap.filters.3.value = "someValue" +%% The configuratation structure sent to the application: +%% [{"objectClass","mqttUser"},"and",{"uiAttr","someAttr"},"or",{"someKey","someAttr"}] +%% The resulting LDAP filter would look like this: +%% ==> "|(&(objectClass=Class)(uiAttr=someAttr)(someKey=someValue))" +{translation, "emqx_auth_ldap.filters", +fun(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix("auth.ldap.filters", Conf), + Keys = [{Num, {key, V}} || {["auth","ldap","filters", Num, "key"], V} <- Settings], + Values = [{Num, {value, V}} || {["auth","ldap","filters", Num, "value"], V} <- Settings], + Ops = [{Num, {op, V}} || {["auth","ldap","filters", Num, "op"], V} <- Settings], + RawFilters = Keys ++ Values ++ Ops, + Filters = + lists:foldl( + fun({Num,{T,V}}, Acc)-> + maps:update_with(Num, + fun(F)-> + maps:put(T,V,F) + end, + #{T=>V}, Acc) + end, #{}, RawFilters), + Order=lists:usort(maps:keys(Filters)), + lists:reverse( + lists:foldl( + fun(F,Acc)-> + case F of + #{key:=K, op:=Op, value:=V} -> [Op,{K,V}|Acc]; + #{key:=K, value:=V} -> [{K,V}|Acc] + end + end, + [], + lists:map(fun(K) -> maps:get(K, Filters) end, Order))) +end}. + +{mapping, "auth.ldap.filters.$num.key", "emqx_auth_ldap.filters", [ + {datatype, string} +]}. + +{mapping, "auth.ldap.filters.$num.value", "emqx_auth_ldap.filters", [ + {datatype, string} +]}. + +{mapping, "auth.ldap.filters.$num.op", "emqx_auth_ldap.filters", [ + {datatype, {enum, [ "or", "and" ] } } +]}. + + +{mapping, "auth.ldap.bind_as_user", "emqx_auth_ldap.bind_as_user", [ + {default, false}, + {datatype, {enum, [true, false]}} +]}. + +{mapping, "auth.ldap.username.attributetype", "emqx_auth_ldap.username_attr", [ + {default, "uid"}, + {datatype, string} +]}. + +{mapping, "auth.ldap.password.attributetype", "emqx_auth_ldap.password_attr", [ + {default, "userPassword"}, + {datatype, string} +]}. diff --git a/apps/emqx_auth_ldap/rebar.config b/apps/emqx_auth_ldap/rebar.config new file mode 100644 index 000000000..983d6b88c --- /dev/null +++ b/apps/emqx_auth_ldap/rebar.config @@ -0,0 +1,27 @@ +{deps, + [{eldap2, {git, "https://github.com/emqx/eldap2", {tag, "v0.2.2"}}}, + {ecpool, {git, "https://github.com/emqx/ecpool", {tag, "v0.4.2"}}}, + {emqx_passwd, {git, "https://github.com/emqx/emqx-passwd", {tag, "v1.1.1"}}} + ]}. + +{profiles, + [{test, + [{deps, [{emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}}]} + ]} + ]}. + +{edoc_opts, [{preprocess, true}]}. +{erl_opts, [warn_unused_vars, + warn_shadow_vars, + warn_unused_import, + warn_obsolete_guard, + debug_info, + {parse_transform}]}. + +{xref_checks, [undefined_function_calls, undefined_functions, + locals_not_used, deprecated_function_calls, + warnings_as_errors, deprecated_functions]}. +{cover_enabled, true}. +{cover_opts, [verbose]}. +{cover_export_enabled, true}. + diff --git a/apps/emqx_auth_ldap/src/emqx_acl_ldap.erl b/apps/emqx_auth_ldap/src/emqx_acl_ldap.erl new file mode 100644 index 000000000..cfd51164a --- /dev/null +++ b/apps/emqx_auth_ldap/src/emqx_acl_ldap.erl @@ -0,0 +1,98 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_acl_ldap). + +-include("emqx_auth_ldap.hrl"). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("eldap/include/eldap.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-export([ register_metrics/0 + , check_acl/5 + , description/0 + ]). + +-import(proplists, [get_value/2]). + +-import(emqx_auth_ldap_cli, [search/4]). + +-spec(register_metrics() -> ok). +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS). + +check_acl(ClientInfo, PubSub, Topic, NoMatchAction, State) -> + case do_check_acl(ClientInfo, PubSub, Topic, NoMatchAction, State) of + ok -> emqx_metrics:inc(?ACL_METRICS(ignore)), ok; + {stop, allow} -> emqx_metrics:inc(?ACL_METRICS(allow)), {stop, allow}; + {stop, deny} -> emqx_metrics:inc(?ACL_METRICS(deny)), {stop, deny} + end. + +do_check_acl(#{username := <<$$, _/binary>>}, _PubSub, _Topic, _NoMatchAction, _State) -> + ok; + +do_check_acl(#{username := Username}, PubSub, Topic, _NoMatchAction, + #{device_dn := DeviceDn, + match_objectclass := ObjectClass, + username_attr := UidAttr, + custom_base_dn := CustomBaseDN, + pool := Pool} = Config) -> + + Filters = maps:get(filters, Config, []), + + ReplaceRules = [{"${username_attr}", UidAttr}, + {"${user}", binary_to_list(Username)}, + {"${device_dn}", DeviceDn}], + + Filter = emqx_auth_ldap:prepare_filter(Filters, UidAttr, ObjectClass, ReplaceRules), + + Attribute = case PubSub of + publish -> "mqttPublishTopic"; + subscribe -> "mqttSubscriptionTopic" + end, + Attribute1 = "mqttPubSubTopic", + ?LOG(debug, "[LDAP] search dn:~p filter:~p, attribute:~p", + [DeviceDn, Filter, Attribute]), + + BaseDN = emqx_auth_ldap:replace_vars(CustomBaseDN, ReplaceRules), + + case search(Pool, BaseDN, Filter, [Attribute, Attribute1]) of + {error, noSuchObject} -> + ok; + {ok, #eldap_search_result{entries = []}} -> + ok; + {ok, #eldap_search_result{entries = [Entry]}} -> + Topics = get_value(Attribute, Entry#eldap_entry.attributes) + ++ get_value(Attribute1, Entry#eldap_entry.attributes), + match(Topic, Topics); + Error -> + ?LOG(error, "[LDAP] search error:~p", [Error]), + {stop, deny} + end. + +match(_Topic, []) -> + ok; + +match(Topic, [Filter | Topics]) -> + case emqx_topic:match(Topic, list_to_binary(Filter)) of + true -> {stop, allow}; + false -> match(Topic, Topics) + end. + +description() -> + "ACL with LDAP". + diff --git a/apps/emqx_auth_ldap/src/emqx_auth_ldap.app.src b/apps/emqx_auth_ldap/src/emqx_auth_ldap.app.src new file mode 100644 index 000000000..51286dd9d --- /dev/null +++ b/apps/emqx_auth_ldap/src/emqx_auth_ldap.app.src @@ -0,0 +1,14 @@ +{application, emqx_auth_ldap, + [{description, "EMQ X Authentication/ACL with LDAP"}, + {vsn, "git"}, + {modules, []}, + {registered, [emqx_auth_ldap_sup]}, + {applications, [kernel,stdlib,eldap2,ecpool,emqx_passwd]}, + {mod, {emqx_auth_ldap_app,[]}}, + {env, []}, + {licenses, ["Apache-2.0"]}, + {maintainers, ["EMQ X Team "]}, + {links, [{"Homepage", "https://emqx.io/"}, + {"Github", "https://github.com/emqx/emqx-auth-ldap"} + ]} + ]}. diff --git a/apps/emqx_auth_ldap/src/emqx_auth_ldap.app.src.script b/apps/emqx_auth_ldap/src/emqx_auth_ldap.app.src.script new file mode 100644 index 000000000..0e14ff23f --- /dev/null +++ b/apps/emqx_auth_ldap/src/emqx_auth_ldap.app.src.script @@ -0,0 +1,24 @@ +%%-*- mode: erlang -*- +%% .app.src.script + +RemoveLeadingV = + fun(Tag) -> + case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of + nomatch -> + re:replace(Tag, "/", "-", [{return ,list}]); + _ -> + %% if it is a version number prefixed by 'v' or 'e', then remove it + re:replace(Tag, "[v|e]", "", [{return ,list}]) + end + end, + +case os:getenv("EMQX_DEPS_DEFAULT_VSN") of + false -> CONFIG; % env var not defined + [] -> CONFIG; % env var set to empty string + Tag -> + [begin + AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}), + {application, App, AppConf0} + end || Conf = {application, App, AppConf} <- CONFIG] +end. + diff --git a/apps/emqx_auth_ldap/src/emqx_auth_ldap.erl b/apps/emqx_auth_ldap/src/emqx_auth_ldap.erl new file mode 100644 index 000000000..01cbb0ecb --- /dev/null +++ b/apps/emqx_auth_ldap/src/emqx_auth_ldap.erl @@ -0,0 +1,210 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_ldap). + +-include("emqx_auth_ldap.hrl"). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("eldap/include/eldap.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-import(proplists, [get_value/2]). + +-import(emqx_auth_ldap_cli, [search/3]). + +-export([ register_metrics/0 + , check/3 + , description/0 + , prepare_filter/4 + , replace_vars/2 + ]). + +-spec(register_metrics() -> ok). +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). + +check(ClientInfo = #{username := Username, password := Password}, AuthResult, + State = #{password_attr := PasswdAttr, bind_as_user := BindAsUserRequired, pool := Pool}) -> + CheckResult = + case lookup_user(Username, State) of + undefined -> {error, not_found}; + {error, Error} -> {error, Error}; + Entry -> + PasswordString = binary_to_list(Password), + ObjectName = Entry#eldap_entry.object_name, + Attributes = Entry#eldap_entry.attributes, + case BindAsUserRequired of + true -> + emqx_auth_ldap_cli:post_bind(Pool, ObjectName, PasswordString); + false -> + case get_value(PasswdAttr, Attributes) of + undefined -> + logger:error("LDAP Search State: ~p, uid: ~p, result:~p", + [State, Username, Attributes]), + {error, not_found}; + [Passhash1] -> + format_password(Passhash1, Password, ClientInfo) + end + end + end, + case CheckResult of + ok -> + ok = emqx_metrics:inc(?AUTH_METRICS(success)), + {stop, AuthResult#{auth_result => success, anonymous => false}}; + {error, not_found} -> + emqx_metrics:inc(?AUTH_METRICS(ignore)); + {error, ResultCode} -> + ok = emqx_metrics:inc(?AUTH_METRICS(failure)), + ?LOG(error, "[LDAP] Auth from ldap failed: ~p", [ResultCode]), + {stop, AuthResult#{auth_result => ResultCode, anonymous => false}} + end. + +lookup_user(Username, #{username_attr := UidAttr, + match_objectclass := ObjectClass, + device_dn := DeviceDn, + custom_base_dn := CustomBaseDN, pool := Pool} = Config) -> + + Filters = maps:get(filters, Config, []), + + ReplaceRules = [{"${username_attr}", UidAttr}, + {"${user}", binary_to_list(Username)}, + {"${device_dn}", DeviceDn}], + + Filter = prepare_filter(Filters, UidAttr, ObjectClass, ReplaceRules), + + %% auth.ldap.custom_base_dn = "${username_attr}=${user},${device_dn}" + BaseDN = replace_vars(CustomBaseDN, ReplaceRules), + + case search(Pool, BaseDN, Filter) of + %% This clause seems to be impossible to match. `eldap2:search/2` does + %% not validates the result, so if it returns "successfully" from the + %% LDAP server, it always returns `{ok, #eldap_search_result{}}`. + {error, noSuchObject} -> + undefined; + %% In case no user was found by the search, but the search was completed + %% without error we get an empty `entries` list. + {ok, #eldap_search_result{entries = []}} -> + undefined; + {ok, #eldap_search_result{entries = [Entry]}} -> + Attributes = Entry#eldap_entry.attributes, + case get_value("isEnabled", Attributes) of + undefined -> + Entry; + [Val] -> + case list_to_atom(string:to_lower(Val)) of + true -> Entry; + false -> {error, username_disabled} + end + end; + {error, Error} -> + ?LOG(error, "[LDAP] Search dn: ~p, filter: ~p, fail:~p", [DeviceDn, Filter, Error]), + {error, username_or_password_error} + end. + +check_pass(Password, Password, _ClientInfo) -> ok; +check_pass(_, _, _) -> {error, bad_username_or_password}. + +format_password(Passhash, Password, ClientInfo) -> + case do_format_password(Passhash, Password) of + {error, Error2} -> + {error, Error2}; + {Passhash1, Password1} -> + check_pass(Passhash1, Password1, ClientInfo) + end. + +do_format_password(Passhash, Password) -> + Base64PasshashHandler = + handle_passhash(fun(HashType, Passhash1, Password1) -> + Passhash2 = binary_to_list(base64:decode(Passhash1)), + resolve_passhash(HashType, Passhash2, Password1) + end, + fun(_Passhash, _Password) -> + {error, password_error} + end), + PasshashHandler = handle_passhash(fun resolve_passhash/3, Base64PasshashHandler), + PasshashHandler(Passhash, Password). + +resolve_passhash(HashType, Passhash, Password) -> + [_, Passhash1] = string:tokens(Passhash, "}"), + do_resolve(HashType, Passhash1, Password). + +handle_passhash(HandleMatch, HandleNoMatch) -> + fun(Passhash, Password) -> + case re:run(Passhash, "(?<={)[^{}]+(?=})", [{capture, all, list}, global]) of + {match, [[HashType]]} -> + HandleMatch(list_to_atom(string:to_lower(HashType)), Passhash, Password); + _ -> + HandleNoMatch(Passhash, Password) + end + end. + +do_resolve(ssha, Passhash, Password) -> + D64 = base64:decode(Passhash), + {HashedData, Salt} = lists:split(20, binary_to_list(D64)), + NewHash = crypto:hash(sha, <>), + {list_to_binary(HashedData), NewHash}; +do_resolve(HashType, Passhash, Password) -> + Password1 = base64:encode(crypto:hash(HashType, Password)), + {list_to_binary(Passhash), Password1}. + +description() -> "LDAP Authentication Plugin". + +prepare_filter(Filters, _UidAttr, ObjectClass, ReplaceRules) -> + SubFilters = + lists:map(fun({K, V}) -> + {replace_vars(K, ReplaceRules), replace_vars(V, ReplaceRules)}; + (Op) -> + Op + end, Filters), + case SubFilters of + [] -> eldap2:equalityMatch("objectClass", ObjectClass); + _List -> compile_filters(SubFilters, []) + end. + + +compile_filters([{Key, Value}], []) -> + compile_equal(Key, Value); +compile_filters([{K1, V1}, "and", {K2, V2} | Rest], []) -> + compile_filters( + Rest, + eldap2:'and'([compile_equal(K1, V1), + compile_equal(K2, V2)])); +compile_filters([{K1, V1}, "or", {K2, V2} | Rest], []) -> + compile_filters( + Rest, + eldap2:'or'([compile_equal(K1, V1), + compile_equal(K2, V2)])); +compile_filters(["and", {K, V} | Rest], PartialFilter) -> + compile_filters( + Rest, + eldap2:'and'([PartialFilter, + compile_equal(K, V)])); +compile_filters(["or", {K, V} | Rest], PartialFilter) -> + compile_filters( + Rest, + eldap2:'or'([PartialFilter, + compile_equal(K, V)])); +compile_filters([], Filter) -> + Filter. + +compile_equal(Key, Value) -> + eldap2:equalityMatch(Key, Value). + +replace_vars(CustomBaseDN, ReplaceRules) -> + lists:foldl(fun({Pattern, Substitute}, DN) -> + lists:flatten(string:replace(DN, Pattern, Substitute)) + end, CustomBaseDN, ReplaceRules). diff --git a/apps/emqx_auth_ldap/src/emqx_auth_ldap_app.erl b/apps/emqx_auth_ldap/src/emqx_auth_ldap_app.erl new file mode 100644 index 000000000..5922950f0 --- /dev/null +++ b/apps/emqx_auth_ldap/src/emqx_auth_ldap_app.erl @@ -0,0 +1,78 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_ldap_app). + +-behaviour(application). + +-emqx_plugin(auth). + +-include("emqx_auth_ldap.hrl"). + +%% Application callbacks +-export([ start/2 + , prep_stop/1 + , stop/1 + ]). + +start(_StartType, _StartArgs) -> + {ok, Sup} = emqx_auth_ldap_sup:start_link(), + if_enabled([device_dn, match_objectclass, + username_attr, password_attr, + filters, custom_base_dn, bind_as_user], + fun load_auth_hook/1), + if_enabled([device_dn, match_objectclass, + username_attr, password_attr, + filters, custom_base_dn, bind_as_user], + fun load_acl_hook/1), + {ok, Sup}. + +prep_stop(State) -> + emqx:unhook('client.authenticate', fun emqx_auth_ldap:check/3), + emqx:unhook('client.check_acl', fun emqx_acl_ldap:check_acl/5), + State. + +stop(_State) -> + ok. + +load_auth_hook(DeviceDn) -> + ok = emqx_auth_ldap:register_metrics(), + Params = maps:from_list(DeviceDn), + emqx:hook('client.authenticate', fun emqx_auth_ldap:check/3, [Params#{pool => ?APP}]). + +load_acl_hook(DeviceDn) -> + ok = emqx_acl_ldap:register_metrics(), + Params = maps:from_list(DeviceDn), + emqx:hook('client.check_acl', fun emqx_acl_ldap:check_acl/5 , [Params#{pool => ?APP}]). + +if_enabled(Cfgs, Fun) -> + case get_env(Cfgs) of + {ok, InitArgs} -> Fun(InitArgs); + [] -> ok + end. + +get_env(Cfgs) -> + get_env(Cfgs, []). + +get_env([Cfg | LeftCfgs], ENVS) -> + case application:get_env(?APP, Cfg) of + {ok, ENV} -> + get_env(LeftCfgs, [{Cfg, ENV} | ENVS]); + undefined -> + get_env(LeftCfgs, ENVS) + end; +get_env([], ENVS) -> + {ok, ENVS}. diff --git a/apps/emqx_auth_ldap/src/emqx_auth_ldap_cli.erl b/apps/emqx_auth_ldap/src/emqx_auth_ldap_cli.erl new file mode 100644 index 000000000..2f6e8099c --- /dev/null +++ b/apps/emqx_auth_ldap/src/emqx_auth_ldap_cli.erl @@ -0,0 +1,150 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_ldap_cli). + +-behaviour(ecpool_worker). + +-include("emqx_auth_ldap.hrl"). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). + +%% ecpool callback +-export([connect/1]). + +-export([ search/3 + , search/4 + , post_bind/3 + , init_args/1 + ]). + +-import(proplists, + [ get_value/2 + , get_value/3 + ]). + +%%-------------------------------------------------------------------- +%% LDAP Connect/Search +%%-------------------------------------------------------------------- + +connect(Opts) -> + Servers = get_value(servers, Opts, ["localhost"]), + Port = get_value(port, Opts, 389), + Timeout = get_value(timeout, Opts, 30), + BindDn = get_value(bind_dn, Opts), + BindPassword = get_value(bind_password, Opts), + LdapOpts = case get_value(ssl, Opts, false)of + true -> + SslOpts = get_value(sslopts, Opts), + [{port, Port}, {timeout, Timeout}, {sslopts, SslOpts}]; + false -> + [{port, Port}, {timeout, Timeout}] + end, + ?LOG(debug, "[LDAP] Connecting to OpenLDAP server: ~p, Opts:~p ...", [Servers, LdapOpts]), + + case eldap2:open(Servers, LdapOpts) of + {ok, LDAP} -> + try eldap2:simple_bind(LDAP, BindDn, BindPassword) of + ok -> {ok, LDAP}; + {error, Error} -> + ?LOG(error, "[LDAP] Can't authenticated to OpenLDAP server: ~p", [Error]), + {error, Error} + catch + error:Reason -> + ?LOG(error, "[LDAP] Can't authenticated to OpenLDAP server: ~p", [Reason]), + {error, Reason} + end; + {error, Reason} -> + ?LOG(error, "[LDAP] Can't connect to OpenLDAP server: ~p", [Reason]), + {error, Reason} + end. + +search(Pool, Base, Filter) -> + ecpool:with_client(Pool, + fun(C) -> + case application:get_env(?APP, bind_as_user) of + {ok, true} -> + {ok, Opts} = application:get_env(?APP, ldap), + BindDn = get_value(bind_dn, Opts), + BindPassword = get_value(bind_password, Opts), + try eldap2:simple_bind(C, BindDn, BindPassword) of + ok -> + eldap2:search(C, [{base, Base}, + {filter, Filter}, + {deref, eldap2:derefFindingBaseObj()}]); + {error, Error} -> + {error, Error} + catch + error:Reason -> {error, Reason} + end; + {ok, false} -> + eldap2:search(C, [{base, Base}, + {filter, Filter}, + {deref, eldap2:derefFindingBaseObj()}]) + end + end). + +search(Pool, Base, Filter, Attributes) -> + ecpool:with_client(Pool, + fun(C) -> + case application:get_env(?APP, bind_as_user) of + {ok, true} -> + {ok, Opts} = application:get_env(?APP, ldap), + BindDn = get_value(bind_dn, Opts), + BindPassword = get_value(bind_password, Opts), + try eldap2:simple_bind(C, BindDn, BindPassword) of + ok -> + eldap2:search(C, [{base, Base}, + {filter, Filter}, + {attributes, Attributes}, + {deref, eldap2:derefFindingBaseObj()}]); + {error, Error} -> + {error, Error} + catch + error:Reason -> {error, Reason} + end; + {ok, false} -> + eldap2:search(C, [{base, Base}, + {filter, Filter}, + {attributes, Attributes}, + {deref, eldap2:derefFindingBaseObj()}]) + end + end). + +post_bind(Pool, BindDn, BindPassword) -> + ecpool:with_client(Pool, + fun(C) -> + try eldap2:simple_bind(C, BindDn, BindPassword) of + ok -> ok; + {error, Error} -> + {error, Error} + catch + error:Reason -> {error, Reason} + end + end). + + +init_args(ENVS) -> + DeviceDn = get_value(device_dn, ENVS), + ObjectClass = get_value(match_objectclass, ENVS), + UidAttr = get_value(username_attr, ENVS), + PasswdAttr = get_value(password_attr, ENVS), + {ok, #{device_dn => DeviceDn, + match_objectclass => ObjectClass, + username_attr => UidAttr, + password_attr => PasswdAttr}}. + diff --git a/apps/emqx_auth_ldap/src/emqx_auth_ldap_sup.erl b/apps/emqx_auth_ldap/src/emqx_auth_ldap_sup.erl new file mode 100644 index 000000000..ca4440f1a --- /dev/null +++ b/apps/emqx_auth_ldap/src/emqx_auth_ldap_sup.erl @@ -0,0 +1,35 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_ldap_sup). + +-behaviour(supervisor). + +-include("emqx_auth_ldap.hrl"). + +-export([start_link/0]). + +-export([init/1]). + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init([]) -> + %% LDAP Connection Pool. + {ok, Server} = application:get_env(?APP, ldap), + PoolSpec = ecpool:pool_spec(?APP, ?APP, emqx_auth_ldap_cli, Server), + {ok, {{one_for_one, 10, 100}, [PoolSpec]}}. + diff --git a/apps/emqx_auth_ldap/test/certs/cacert.pem b/apps/emqx_auth_ldap/test/certs/cacert.pem new file mode 100644 index 000000000..604fd2362 --- /dev/null +++ b/apps/emqx_auth_ldap/test/certs/cacert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDUTCCAjmgAwIBAgIJAPPYCjTmxdt/MA0GCSqGSIb3DQEBCwUAMD8xCzAJBgNV +BAYTAkNOMREwDwYDVQQIDAhoYW5nemhvdTEMMAoGA1UECgwDRU1RMQ8wDQYDVQQD +DAZSb290Q0EwHhcNMjAwNTA4MDgwNjUyWhcNMzAwNTA2MDgwNjUyWjA/MQswCQYD +VQQGEwJDTjERMA8GA1UECAwIaGFuZ3pob3UxDDAKBgNVBAoMA0VNUTEPMA0GA1UE +AwwGUm9vdENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzcgVLex1 +EZ9ON64EX8v+wcSjzOZpiEOsAOuSXOEN3wb8FKUxCdsGrsJYB7a5VM/Jot25Mod2 +juS3OBMg6r85k2TWjdxUoUs+HiUB/pP/ARaaW6VntpAEokpij/przWMPgJnBF3Ur +MjtbLayH9hGmpQrI5c2vmHQ2reRZnSFbY+2b8SXZ+3lZZgz9+BaQYWdQWfaUWEHZ +uDaNiViVO0OT8DRjCuiDp3yYDj3iLWbTA/gDL6Tf5XuHuEwcOQUrd+h0hyIphO8D +tsrsHZ14j4AWYLk1CPA6pq1HIUvEl2rANx2lVUNv+nt64K/Mr3RnVQd9s8bK+TXQ +KGHd2Lv/PALYuwIDAQABo1AwTjAdBgNVHQ4EFgQUGBmW+iDzxctWAWxmhgdlE8Pj +EbQwHwYDVR0jBBgwFoAUGBmW+iDzxctWAWxmhgdlE8PjEbQwDAYDVR0TBAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAQEAGbhRUjpIred4cFAFJ7bbYD9hKu/yzWPWkMRa +ErlCKHmuYsYk+5d16JQhJaFy6MGXfLgo3KV2itl0d+OWNH0U9ULXcglTxy6+njo5 +CFqdUBPwN1jxhzo9yteDMKF4+AHIxbvCAJa17qcwUKR5MKNvv09C6pvQDJLzid7y +E2dkgSuggik3oa0427KvctFf8uhOV94RvEDyqvT5+pgNYZ2Yfga9pD/jjpoHEUlo +88IGU8/wJCx3Ds2yc8+oBg/ynxG8f/HmCC1ET6EHHoe2jlo8FpU/SgGtghS1YL30 +IWxNsPrUP+XsZpBJy/mvOhE5QXo6Y35zDqqj8tI7AGmAWu22jg== +-----END CERTIFICATE----- diff --git a/apps/emqx_auth_ldap/test/certs/cert.pem b/apps/emqx_auth_ldap/test/certs/cert.pem new file mode 100644 index 000000000..092390b1d --- /dev/null +++ b/apps/emqx_auth_ldap/test/certs/cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDEzCCAfugAwIBAgIBAjANBgkqhkiG9w0BAQsFADA/MQswCQYDVQQGEwJDTjER +MA8GA1UECAwIaGFuZ3pob3UxDDAKBgNVBAoMA0VNUTEPMA0GA1UEAwwGUm9vdENB +MB4XDTIwMDUwODA4MDcwNVoXDTMwMDUwNjA4MDcwNVowPzELMAkGA1UEBhMCQ04x +ETAPBgNVBAgMCGhhbmd6aG91MQwwCgYDVQQKDANFTVExDzANBgNVBAMMBlNlcnZl +cjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALNeWT3pE+QFfiRJzKmn +AMUrWo3K2j/Tm3+Xnl6WLz67/0rcYrJbbKvS3uyRP/stXyXEKw9CepyQ1ViBVFkW +Aoy8qQEOWFDsZc/5UzhXUnb6LXr3qTkFEjNmhj+7uzv/lbBxlUG1NlYzSeOB6/RT +8zH/lhOeKhLnWYPXdXKsa1FL6ij4X8DeDO1kY7fvAGmBn/THh1uTpDizM4YmeI+7 +4dmayA5xXvARte5h4Vu5SIze7iC057N+vymToMk2Jgk+ZZFpyXrnq+yo6RaD3ANc +lrc4FbeUQZ5a5s5Sxgs9a0Y3WMG+7c5VnVXcbjBRz/aq2NtOnQQjikKKQA8GF080 +BQkCAwEAAaMaMBgwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQEL +BQADggEBAJefnMZpaRDHQSNUIEL3iwGXE9c6PmIsQVE2ustr+CakBp3TZ4l0enLt +iGMfEVFju69cO4oyokWv+hl5eCMkHBf14Kv51vj448jowYnF1zmzn7SEzm5Uzlsa +sqjtAprnLyof69WtLU1j5rYWBuFX86yOTwRAFNjm9fvhAcrEONBsQtqipBWkMROp +iUYMkRqbKcQMdwxov+lHBYKq9zbWRoqLROAn54SRqgQk6c15JdEfgOOjShbsOkIH +UhqcwRkQic7n1zwHVGVDgNIZVgmJ2IdIWBlPEC7oLrRrBD/X1iEEXtKab6p5o22n +KB5mN+iQaE+Oe2cpGKZJiJRdM+IqDDQ= +-----END CERTIFICATE----- diff --git a/apps/emqx_auth_ldap/test/certs/client-cert.pem b/apps/emqx_auth_ldap/test/certs/client-cert.pem new file mode 100644 index 000000000..09d855221 --- /dev/null +++ b/apps/emqx_auth_ldap/test/certs/client-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDEzCCAfugAwIBAgIBATANBgkqhkiG9w0BAQsFADA/MQswCQYDVQQGEwJDTjER +MA8GA1UECAwIaGFuZ3pob3UxDDAKBgNVBAoMA0VNUTEPMA0GA1UEAwwGUm9vdENB +MB4XDTIwMDUwODA4MDY1N1oXDTMwMDUwNjA4MDY1N1owPzELMAkGA1UEBhMCQ04x +ETAPBgNVBAgMCGhhbmd6aG91MQwwCgYDVQQKDANFTVExDzANBgNVBAMMBkNsaWVu +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMy4hoksKcZBDbY680u6 +TS25U51nuB1FBcGMlF9B/t057wPOlxF/OcmbxY5MwepS41JDGPgulE1V7fpsXkiW +1LUimYV/tsqBfymIe0mlY7oORahKji7zKQ2UBIVFhdlvQxunlIDnw6F9popUgyHt +dMhtlgZK8oqRwHxO5dbfoukYd6J/r+etS5q26sgVkf3C6dt0Td7B25H9qW+f7oLV +PbcHYCa+i73u9670nrpXsC+Qc7Mygwa2Kq/jwU+ftyLQnOeW07DuzOwsziC/fQZa +nbxR+8U9FNftgRcC3uP/JMKYUqsiRAuaDokARZxVTV5hUElfpO6z6/NItSDvvh3i +eikCAwEAAaMaMBgwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQEL +BQADggEBABchYxKo0YMma7g1qDswJXsR5s56Czx/I+B41YcpMBMTrRqpUC0nHtLk +M7/tZp592u/tT8gzEnQjZLKBAhFeZaR3aaKyknLqwiPqJIgg0pgsBGITrAK3Pv4z +5/YvAJJKgTe5UdeTz6U4lvNEux/4juZ4pmqH4qSFJTOzQS7LmgSmNIdd072rwXBd +UzcSHzsJgEMb88u/LDLjj1pQ7AtZ4Tta8JZTvcgBFmjB0QUi6fgkHY6oGat/W4kR +jSRUBlMUbM/drr2PVzRc2dwbFIl3X+ZE6n5Sl3ZwRAC/s92JU6CPMRW02muVu6xl +goraNgPISnrbpR6KjxLZkVembXzjNNc= +-----END CERTIFICATE----- diff --git a/apps/emqx_auth_ldap/test/certs/client-key.pem b/apps/emqx_auth_ldap/test/certs/client-key.pem new file mode 100644 index 000000000..2b3f30cf6 --- /dev/null +++ b/apps/emqx_auth_ldap/test/certs/client-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAzLiGiSwpxkENtjrzS7pNLblTnWe4HUUFwYyUX0H+3TnvA86X +EX85yZvFjkzB6lLjUkMY+C6UTVXt+mxeSJbUtSKZhX+2yoF/KYh7SaVjug5FqEqO +LvMpDZQEhUWF2W9DG6eUgOfDoX2milSDIe10yG2WBkryipHAfE7l1t+i6Rh3on+v +561LmrbqyBWR/cLp23RN3sHbkf2pb5/ugtU9twdgJr6Lve73rvSeulewL5BzszKD +BrYqr+PBT5+3ItCc55bTsO7M7CzOIL99BlqdvFH7xT0U1+2BFwLe4/8kwphSqyJE +C5oOiQBFnFVNXmFQSV+k7rPr80i1IO++HeJ6KQIDAQABAoIBAGWgvPjfuaU3qizq +uti/FY07USz0zkuJdkANH6LiSjlchzDmn8wJ0pApCjuIE0PV/g9aS8z4opp5q/gD +UBLM/a8mC/xf2EhTXOMrY7i9p/I3H5FZ4ZehEqIw9sWKK9YzC6dw26HabB2BGOnW +5nozPSQ6cp2RGzJ7BIkxSZwPzPnVTgy3OAuPOiJytvK+hGLhsNaT+Y9bNDvplVT2 +ZwYTV8GlHZC+4b2wNROILm0O86v96O+Qd8nn3fXjGHbMsAnONBq10bZS16L4fvkH +5G+W/1PeSXmtZFppdRRDxIW+DWcXK0D48WRliuxcV4eOOxI+a9N2ZJZZiNLQZGwg +w3A8+mECgYEA8HuJFrlRvdoBe2U/EwUtG74dcyy30L4yEBnN5QscXmEEikhaQCfX +Wm6EieMcIB/5I5TQmSw0cmBMeZjSXYoFdoI16/X6yMMuATdxpvhOZGdUGXxhAH+x +xoTUavWZnEqW3fkUU71kT5E2f2i+0zoatFESXHeslJyz85aAYpP92H0CgYEA2e5A +Yozt5eaA1Gyhd8SeptkEU4xPirNUnVQHStpMWUb1kzTNXrPmNWccQ7JpfpG6DcYl +zUF6p6mlzY+zkMiyPQjwEJlhiHM2NlL1QS7td0R8ewgsFoyn8WsBI4RejWrEG9td +EDniuIw+pBFkcWthnTLHwECHdzgquToyTMjrBB0CgYEA28tdGbrZXhcyAZEhHAZA +Gzog+pKlkpEzeonLKIuGKzCrEKRecIK5jrqyQsCjhS0T7ZRnL4g6i0s+umiV5M5w +fcc292pEA1h45L3DD6OlKplSQVTv55/OYS4oY3YEJtf5mfm8vWi9lQeY8sxOlQpn +O+VZTdBHmTC8PGeTAgZXHZUCgYA6Tyv88lYowB7SN2qQgBQu8jvdGtqhcs/99GCr +H3N0I69LPsKAR0QeH8OJPXBKhDUywESXAaEOwS5yrLNP1tMRz5Vj65YUCzeDG3kx +gpvY4IMp7ArX0bSRvJ6mYSFnVxy3k174G3TVCfksrtagHioVBGQ7xUg5ltafjrms +n8l55QKBgQDVzU8tQvBVqY8/1lnw11Vj4fkE/drZHJ5UkdC1eenOfSWhlSLfUJ8j +ds7vEWpRPPoVuPZYeR1y78cyxKe1GBx6Wa2lF5c7xjmiu0xbRnrxYeLolce9/ntp +asClqpnHT8/VJYTD7Kqj0fouTTZf0zkig/y+2XERppd8k+pSKjUCPQ== +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_ldap/test/certs/key.pem b/apps/emqx_auth_ldap/test/certs/key.pem new file mode 100644 index 000000000..6c338216e --- /dev/null +++ b/apps/emqx_auth_ldap/test/certs/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAs15ZPekT5AV+JEnMqacAxStajcraP9Obf5eeXpYvPrv/Stxi +sltsq9Le7JE/+y1fJcQrD0J6nJDVWIFUWRYCjLypAQ5YUOxlz/lTOFdSdvotevep +OQUSM2aGP7u7O/+VsHGVQbU2VjNJ44Hr9FPzMf+WE54qEudZg9d1cqxrUUvqKPhf +wN4M7WRjt+8AaYGf9MeHW5OkOLMzhiZ4j7vh2ZrIDnFe8BG17mHhW7lIjN7uILTn +s36/KZOgyTYmCT5lkWnJeuer7KjpFoPcA1yWtzgVt5RBnlrmzlLGCz1rRjdYwb7t +zlWdVdxuMFHP9qrY206dBCOKQopADwYXTzQFCQIDAQABAoIBAQCuvCbr7Pd3lvI/ +n7VFQG+7pHRe1VKwAxDkx2t8cYos7y/QWcm8Ptwqtw58HzPZGWYrgGMCRpzzkRSF +V9g3wP1S5Scu5C6dBu5YIGc157tqNGXB+SpdZddJQ4Nc6yGHXYERllT04ffBGc3N +WG/oYS/1cSteiSIrsDy/91FvGRCi7FPxH3wIgHssY/tw69s1Cfvaq5lr2NTFzxIG +xCvpJKEdSfVfS9I7LYiymVjst3IOR/w76/ZFY9cRa8ZtmQSWWsm0TUpRC1jdcbkm +ZoJptYWlP+gSwx/fpMYftrkJFGOJhHJHQhwxT5X/ajAISeqjjwkWSEJLwnHQd11C +Zy2+29lBAoGBANlEAIK4VxCqyPXNKfoOOi5dS64NfvyH4A1v2+KaHWc7lqaqPN49 +ezfN2n3X+KWx4cviDD914Yc2JQ1vVJjSaHci7yivocDo2OfZDmjBqzaMp/y+rX1R +/f3MmiTqMa468rjaxI9RRZu7vDgpTR+za1+OBCgMzjvAng8dJuN/5gjlAoGBANNY +uYPKtearBmkqdrSV7eTUe49Nhr0XotLaVBH37TCW0Xv9wjO2xmbm5Ga/DCtPIsBb +yPeYwX9FjoasuadUD7hRvbFu6dBa0HGLmkXRJZTcD7MEX2Lhu4BuC72yDLLFd0r+ +Ep9WP7F5iJyagYqIZtz+4uf7gBvUDdmvXz3sGr1VAoGAdXTD6eeKeiI6PlhKBztF +zOb3EQOO0SsLv3fnodu7ZaHbUgLaoTMPuB17r2jgrYM7FKQCBxTNdfGZmmfDjlLB +0xZ5wL8ibU30ZXL8zTlWPElST9sto4B+FYVVF/vcG9sWeUUb2ncPcJ/Po3UAktDG +jYQTTyuNGtSJHpad/YOZctkCgYBtWRaC7bq3of0rJGFOhdQT9SwItN/lrfj8hyHA +OjpqTV4NfPmhsAtu6j96OZaeQc+FHvgXwt06cE6Rt4RG4uNPRluTFgO7XYFDfitP +vCppnoIw6S5BBvHwPP+uIhUX2bsi/dm8vu8tb+gSvo4PkwtFhEr6I9HglBKmcmog +q6waEQKBgHyecFBeM6Ls11Cd64vborwJPAuxIW7HBAFj/BS99oeG4TjBx4Sz2dFd +rzUibJt4ndnHIvCN8JQkjNG14i9hJln+H3mRss8fbZ9vQdqG+2vOWADYSzzsNI55 +RFY7JjluKcVkp/zCDeUxTU3O6sS+v6/3VE11Cob6OYQx3lN5wrZ3 +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_ldap/test/emqx_auth_ldap_SUITE.erl b/apps/emqx_auth_ldap/test/emqx_auth_ldap_SUITE.erl new file mode 100644 index 000000000..513ad79e1 --- /dev/null +++ b/apps/emqx_auth_ldap/test/emqx_auth_ldap_SUITE.erl @@ -0,0 +1,153 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_ldap_SUITE). + +-compile(export_all). +-compile(nowarn_export_all). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). + +-define(PID, emqx_auth_ldap). + +-define(APP, emqx_auth_ldap). + +-define(DeviceDN, "ou=test_device,dc=emqx,dc=io"). + +-define(AuthDN, "ou=test_auth,dc=emqx,dc=io"). + +%%-------------------------------------------------------------------- +%% Setups +%%-------------------------------------------------------------------- + +all() -> + [{group, nossl}, {group, ssl}]. + +groups() -> + Cases = emqx_ct:all(?MODULE), + [{nossl, Cases}, {ssl, Cases}]. + +init_per_group(GrpName, Cfg) -> + Fun = fun(App) -> set_special_configs(GrpName, App) end, + emqx_ct_helpers:start_apps([emqx_auth_ldap], Fun), + emqx_mod_acl_internal:unload([]), + Cfg. + +end_per_group(_GrpName, _Cfg) -> + emqx_ct_helpers:stop_apps([emqx_auth_ldap]). + +%%-------------------------------------------------------------------- +%% Cases +%%-------------------------------------------------------------------- + +t_check_auth(_) -> + MqttUser1 = #{clientid => <<"mqttuser1">>, + username => <<"mqttuser0001">>, + password => <<"mqttuser0001">>, + zone => external}, + MqttUser2 = #{clientid => <<"mqttuser2">>, + username => <<"mqttuser0002">>, + password => <<"mqttuser0002">>, + zone => external}, + MqttUser3 = #{clientid => <<"mqttuser3">>, + username => <<"mqttuser0003">>, + password => <<"mqttuser0003">>, + zone => external}, + MqttUser4 = #{clientid => <<"mqttuser4">>, + username => <<"mqttuser0004">>, + password => <<"mqttuser0004">>, + zone => external}, + MqttUser5 = #{clientid => <<"mqttuser5">>, + username => <<"mqttuser0005">>, + password => <<"mqttuser0005">>, + zone => external}, + NonExistUser1 = #{clientid => <<"mqttuser6">>, + username => <<"mqttuser0006">>, + password => <<"mqttuser0006">>, + zone => external}, + NonExistUser2 = #{clientid => <<"mqttuser7">>, + username => <<"mqttuser0005">>, + password => <<"mqttuser0006">>, + zone => external}, + ct:log("MqttUser: ~p", [emqx_access_control:authenticate(MqttUser1)]), + ?assertMatch({ok, #{auth_result := success}}, emqx_access_control:authenticate(MqttUser1)), + ?assertMatch({ok, #{auth_result := success}}, emqx_access_control:authenticate(MqttUser2)), + ?assertMatch({ok, #{auth_result := success}}, emqx_access_control:authenticate(MqttUser3)), + ?assertMatch({ok, #{auth_result := success}}, emqx_access_control:authenticate(MqttUser4)), + ?assertMatch({ok, #{auth_result := success}}, emqx_access_control:authenticate(MqttUser5)), + ?assertEqual({error, not_authorized}, emqx_access_control:authenticate(NonExistUser1)), + ?assertEqual({error, bad_username_or_password}, emqx_access_control:authenticate(NonExistUser2)). + +t_check_acl(_) -> + MqttUser = #{clientid => <<"mqttuser1">>, username => <<"mqttuser0001">>, zone => external}, + NoMqttUser = #{clientid => <<"mqttuser2">>, username => <<"mqttuser0007">>, zone => external}, + allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pub/1">>), + allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pub/+">>), + allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pub/#">>), + + allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/sub/1">>), + allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/sub/+">>), + allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/sub/#">>), + + allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pubsub/1">>), + allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pubsub/+">>), + allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pubsub/#">>), + allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/pubsub/1">>), + allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/pubsub/+">>), + allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/pubsub/#">>), + + deny = emqx_access_control:check_acl(NoMqttUser, publish, <<"mqttuser0001/req/mqttuser0001/+">>), + deny = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/req/mqttuser0002/+">>), + deny = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/req/+/mqttuser0002">>), + ok. + +%%-------------------------------------------------------------------- +%% Helpers +%%-------------------------------------------------------------------- + +set_special_configs(_, emqx) -> + application:set_env(emqx, allow_anonymous, false), + application:set_env(emqx, enable_acl_cache, false), + application:set_env(emqx, acl_nomatch, deny), + AclFilePath = filename:join(["test", "emqx_SUITE_data", "acl.conf"]), + application:set_env(emqx, acl_file, + emqx_ct_helpers:deps_path(emqx, AclFilePath)), + LoadedPluginPath = filename:join(["test", "emqx_SUITE_data", "loaded_plugins"]), + application:set_env(emqx, plugins_loaded_file, + emqx_ct_helpers:deps_path(emqx, LoadedPluginPath)); + +set_special_configs(Ssl, emqx_auth_ldap) -> + case Ssl == ssl of + true -> + LdapOpts = application:get_env(emqx_auth_ldap, ldap, []), + Path = emqx_ct_helpers:deps_path(emqx_auth_ldap, "test/certs/"), + SslOpts = [{verify, verify_peer}, + {fail_if_no_peer_cert, true}, + {server_name_indication, disable}, + {keyfile, Path ++ "/client-key.pem"}, + {certfile, Path ++ "/client-cert.pem"}, + {cacertfile, Path ++ "/cacert.pem"}], + LdapOpts1 = lists:keystore(ssl, 1, LdapOpts, {ssl, true}), + LdapOpts2 = lists:keystore(sslopts, 1, LdapOpts1, {sslopts, SslOpts}), + LdapOpts3 = lists:keystore(port, 1, LdapOpts2, {port, 636}), + application:set_env(emqx_auth_ldap, ldap, LdapOpts3); + _ -> + ok + end, + application:set_env(emqx_auth_ldap, device_dn, "ou=testdevice, dc=emqx, dc=io"). + diff --git a/apps/emqx_auth_ldap/test/emqx_auth_ldap_bind_as_user_SUITE.erl b/apps/emqx_auth_ldap/test/emqx_auth_ldap_bind_as_user_SUITE.erl new file mode 100644 index 000000000..76f049cbc --- /dev/null +++ b/apps/emqx_auth_ldap/test/emqx_auth_ldap_bind_as_user_SUITE.erl @@ -0,0 +1,114 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_ldap_bind_as_user_SUITE). + +-compile(export_all). +-compile(no_warning_export). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). + +-define(PID, emqx_auth_ldap). + +-define(APP, emqx_auth_ldap). + +-define(DeviceDN, "ou=test_device,dc=emqx,dc=io"). + +-define(AuthDN, "ou=test_auth,dc=emqx,dc=io"). + +all() -> + [check_auth, + check_acl]. + +init_per_suite(Config) -> + emqx_ct_helpers:start_apps([emqx, emqx_auth_ldap], fun set_special_configs/1), + emqx_mod_acl_internal:unload([]), + Config. + +end_per_suite(_Config) -> + emqx_ct_helpers:stop_apps([emqx_auth_ldap, emqx]). + +check_auth(_) -> + MqttUser1 = #{clientid => <<"mqttuser1">>, + username => <<"user1">>, + password => <<"mqttuser0001">>, + zone => external}, + MqttUser2 = #{clientid => <<"mqttuser2">>, + username => <<"user2">>, + password => <<"mqttuser0002">>, + zone => external}, + NonExistUser1 = #{clientid => <<"mqttuser3">>, + username => <<"user3">>, + password => <<"mqttuser0003">>, + zone => external}, + ct:log("MqttUser: ~p", [emqx_access_control:authenticate(MqttUser1)]), + ?assertMatch({ok, #{auth_result := success}}, emqx_access_control:authenticate(MqttUser1)), + ?assertMatch({ok, #{auth_result := success}}, emqx_access_control:authenticate(MqttUser2)), + ?assertEqual({error, not_authorized}, emqx_access_control:authenticate(NonExistUser1)). + +check_acl(_) -> + % emqx_modules:load_module(emqx_mod_acl_internal, false), + MqttUser = #{clientid => <<"mqttuser1">>, username => <<"user1">>, zone => external}, + NoMqttUser = #{clientid => <<"mqttuser2">>, username => <<"user7">>, zone => external}, + allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pub/1">>), + allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pub/+">>), + allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pub/#">>), + + allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/sub/1">>), + allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/sub/+">>), + allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/sub/#">>), + + allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pubsub/1">>), + allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pubsub/+">>), + allow = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/pubsub/#">>), + allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/pubsub/1">>), + allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/pubsub/+">>), + allow = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/pubsub/#">>), + + deny = emqx_access_control:check_acl(NoMqttUser, publish, <<"mqttuser0001/req/mqttuser0001/+">>), + deny = emqx_access_control:check_acl(MqttUser, publish, <<"mqttuser0001/req/mqttuser0002/+">>), + deny = emqx_access_control:check_acl(MqttUser, subscribe, <<"mqttuser0001/req/+/mqttuser0002">>), + ok. + +set_special_configs(emqx) -> + application:set_env(emqx, allow_anonymous, false), + application:set_env(emqx, enable_acl_cache, false), + application:set_env(emqx, acl_nomatch, deny), + AclFilePath = filename:join(["test", "emqx_SUITE_data", "acl.conf"]), + application:set_env(emqx, acl_file, + emqx_ct_helpers:deps_path(emqx, AclFilePath)), + LoadedPluginPath = filename:join(["test", "emqx_SUITE_data", "loaded_plugins"]), + application:set_env(emqx, plugins_loaded_file, + emqx_ct_helpers:deps_path(emqx, LoadedPluginPath)); + +set_special_configs(emqx_auth_ldap) -> + application:set_env(emqx_auth_ldap, bind_as_user, true), + application:set_env(emqx_auth_ldap, device_dn, "ou=testdevice, dc=emqx, dc=io"), + application:set_env(emqx_auth_ldap, custom_base_dn, "${device_dn}"), + %% auth.ldap.filters.1.key = mqttAccountName + %% auth.ldap.filters.1.value = ${user} + %% auth.ldap.filters.1.op = and + %% auth.ldap.filters.2.key = objectClass + %% auth.ldap.filters.1.value = mqttUser + application:set_env(emqx_auth_ldap, filters, [{"mqttAccountName", "${user}"}, + "and", + {"objectClass", "mqttUser"}]); + +set_special_configs(_App) -> + ok. + diff --git a/apps/emqx_auth_mnesia/.github/workflows/run_test_cases.yaml b/apps/emqx_auth_mnesia/.github/workflows/run_test_cases.yaml new file mode 100644 index 000000000..31efc5f92 --- /dev/null +++ b/apps/emqx_auth_mnesia/.github/workflows/run_test_cases.yaml @@ -0,0 +1,29 @@ +name: Run test cases + +on: [push, pull_request] + +jobs: + run_test_cases: + runs-on: ubuntu-latest + + container: + image: erlang:22.1 + + steps: + - uses: actions/checkout@v1 + - name: run test cases + run: | + make xref + make eunit + make ct + make cover + - uses: actions/upload-artifact@v1 + if: always() + with: + name: logs + path: _build/test/logs + - uses: actions/upload-artifact@v1 + with: + name: cover + path: _build/test/cover + diff --git a/apps/emqx_auth_mnesia/.gitignore b/apps/emqx_auth_mnesia/.gitignore new file mode 100644 index 000000000..a4d9fea0a --- /dev/null +++ b/apps/emqx_auth_mnesia/.gitignore @@ -0,0 +1,26 @@ +.eunit +deps +*.o +*.beam +*.plt +erl_crash.dump +ebin +rel/example_project +.concrete/DEV_MODE +.rebar +.erlang.mk/ +emqx_auth_mnesia.d +data/ +_build/ +.DS_Store +cover/ +ct.coverdata +eunit.coverdata +logs/ +test/ct.cover.spec +rebar.lock +rebar3.crashdump +erlang.mk +.*.swp +.rebar3/ +etc/emqx_auth_mnesia.conf.rendered diff --git a/apps/emqx_auth_mnesia/LICENSE b/apps/emqx_auth_mnesia/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/apps/emqx_auth_mnesia/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/apps/emqx_auth_mnesia/README.md b/apps/emqx_auth_mnesia/README.md new file mode 100644 index 000000000..8b4c145a8 --- /dev/null +++ b/apps/emqx_auth_mnesia/README.md @@ -0,0 +1,2 @@ +emqx_auth_mnesia +=============== diff --git a/apps/emqx_auth_mnesia/etc/emqx_auth_mnesia.conf b/apps/emqx_auth_mnesia/etc/emqx_auth_mnesia.conf new file mode 100644 index 000000000..5782ed459 --- /dev/null +++ b/apps/emqx_auth_mnesia/etc/emqx_auth_mnesia.conf @@ -0,0 +1,20 @@ +## Examples: +##auth.mnesia.1.login = admin +##auth.mnesia.1.password = public +##auth.mnesia.1.is_superuser = true +##auth.mnesia.2.login = feng@emqtt.io +##auth.mnesia.2.password = public +##auth.mnesia.2.is_superuser = false +##auth.mnesia.3.login = name~!@#$%^&*()_+ +##auth.mnesia.3.password = pwsswd~!@#$%^&*()_+ +##auth.mnesia.3.is_superuser = false + +## Password hash. +## +## Value: plain | md5 | sha | sha256 +auth.mnesia.password_hash = sha256 + +## Auth as username or auth as clientid. +## +## Value: username | clientid +auth.mnesia.as = username diff --git a/apps/emqx_auth_mnesia/include/emqx_auth_mnesia.hrl b/apps/emqx_auth_mnesia/include/emqx_auth_mnesia.hrl new file mode 100644 index 000000000..31557ea63 --- /dev/null +++ b/apps/emqx_auth_mnesia/include/emqx_auth_mnesia.hrl @@ -0,0 +1,35 @@ +-define(APP, emqx_auth_mnesia). + +-record(emqx_user, { + login, + password, + is_superuser + }). + +-record(emqx_acl, { + login, + topic, + action, + allow + }). + +-record(auth_metrics, { + success = 'client.auth.success', + failure = 'client.auth.failure', + ignore = 'client.auth.ignore' + }). + +-record(acl_metrics, { + allow = 'client.acl.allow', + deny = 'client.acl.deny', + ignore = 'client.acl.ignore' + }). + +-define(METRICS(Type), tl(tuple_to_list(#Type{}))). +-define(METRICS(Type, K), #Type{}#Type.K). + +-define(AUTH_METRICS, ?METRICS(auth_metrics)). +-define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). + +-define(ACL_METRICS, ?METRICS(acl_metrics)). +-define(ACL_METRICS(K), ?METRICS(acl_metrics, K)). \ No newline at end of file diff --git a/apps/emqx_auth_mnesia/priv/emqx_auth_mnesia.schema b/apps/emqx_auth_mnesia/priv/emqx_auth_mnesia.schema new file mode 100644 index 000000000..34333b145 --- /dev/null +++ b/apps/emqx_auth_mnesia/priv/emqx_auth_mnesia.schema @@ -0,0 +1,35 @@ +%%-*- mode: erlang -*- +%% emqx_auth_mnesia config mapping + +{mapping, "auth.mnesia.as", "emqx_auth_mnesia.as", [ + {default, username}, + {datatype, {enum, [username, clientid]}} +]}. + +{mapping, "auth.mnesia.password_hash", "emqx_auth_mnesia.password_hash", [ + {default, sha256}, + {datatype, {enum, [plain, md5, sha, sha256]}} +]}. + +{mapping, "auth.mnesia.$id.login", "emqx_auth_mnesia.userlist", [ + {datatype, string} +]}. + +{mapping, "auth.mnesia.$id.password", "emqx_auth_mnesia.userlist", [ + {datatype, string} +]}. + +{mapping, "auth.mnesia.$id.is_superuser", "emqx_auth_mnesia.userlist", [ + {default, false}, + {datatype, {enum, [false, true]}} +]}. + +{translation, "emqx_auth_mnesia.userlist", fun(Conf) -> + Userlist = cuttlefish_variable:filter_by_prefix("auth.mnesia", Conf), + lists:foldl( + fun({["auth", "mnesia", Id, "login"], Username}, AccIn) -> + [{Username, cuttlefish:conf_get("auth.mnesia." ++ Id ++ ".password", Conf), cuttlefish:conf_get("auth.mnesia." ++ Id ++ ".is_superuser", Conf)} | AccIn]; + (_, AccIn) -> + AccIn + end, [], Userlist) +end}. diff --git a/apps/emqx_auth_mnesia/rebar.config b/apps/emqx_auth_mnesia/rebar.config new file mode 100644 index 000000000..f319eb80d --- /dev/null +++ b/apps/emqx_auth_mnesia/rebar.config @@ -0,0 +1,29 @@ +{minimum_otp_vsn, "21"}. + +{deps, + [{emqx_passwd, {git, "https://github.com/emqx/emqx-passwd.git", {tag, "v1.1.1"}}}, + {minirest, {git, "https://github.com/emqx/minirest.git", {tag, "0.3.1"}}} + ]}. + +{profiles, + [{test, + [{deps, + [{emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "v1.2.2"}}} + ]} + ]} + ]}. + +{erl_opts, [warn_unused_vars, + warn_shadow_vars, + warn_unused_import, + warn_obsolete_guard, + debug_info, + {parse_transform}]}. + +{xref_checks, [undefined_function_calls, undefined_functions, + locals_not_used, deprecated_function_calls, + warnings_as_errors, deprecated_functions]}. +{cover_enabled, true}. +{cover_opts, [verbose]}. +{cover_export_enabled, true}. + diff --git a/apps/emqx_auth_mnesia/src/emqx_acl_mnesia.erl b/apps/emqx_auth_mnesia/src/emqx_acl_mnesia.erl new file mode 100644 index 000000000..63a357979 --- /dev/null +++ b/apps/emqx_auth_mnesia/src/emqx_acl_mnesia.erl @@ -0,0 +1,83 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_acl_mnesia). + +-include("emqx_auth_mnesia.hrl"). + +%% ACL Callbacks +-export([ init/0 + , register_metrics/0 + , check_acl/5 + , description/0 + ]). + +init() -> + ok = ekka_mnesia:create_table(emqx_acl, [ + {type, bag}, + {disc_copies, [node()]}, + {attributes, record_info(fields, emqx_acl)}, + {storage_properties, [{ets, [{read_concurrency, true}]}]}]), + ok = ekka_mnesia:copy_table(emqx_user, disc_copies). + +-spec(register_metrics() -> ok). +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS). + +check_acl(ClientInfo, PubSub, Topic, NoMatchAction, #{key_as := As}) -> + Login = maps:get(As, ClientInfo), + case do_check_acl(Login, PubSub, Topic, NoMatchAction) of + ok -> emqx_metrics:inc(?ACL_METRICS(ignore)), ok; + {stop, allow} -> emqx_metrics:inc(?ACL_METRICS(allow)), {stop, allow}; + {stop, deny} -> emqx_metrics:inc(?ACL_METRICS(deny)), {stop, deny} + end. + +description() -> "Acl with Mnesia". + +%%-------------------------------------------------------------------- +%% Internal functions +%%------------------------------------------------------------------- + +do_check_acl(Login, PubSub, Topic, _NoMatchAction) -> + case match(PubSub, Topic, emqx_auth_mnesia_cli:lookup_acl(Login)) of + allow -> {stop, allow}; + deny -> {stop, deny}; + _ -> + case match(PubSub, Topic, emqx_auth_mnesia_cli:lookup_acl(<<"$all">>)) of + allow -> {stop, allow}; + deny -> {stop, deny}; + _ -> ok + end + end. + +match(_PubSub, _Topic, []) -> + nomatch; +match(PubSub, Topic, [ #emqx_acl{topic = ACLTopic, action = Action, allow = Allow} | UserAcl]) -> + case match_actions(PubSub, Action) andalso match_topic(Topic, ACLTopic) of + true -> case Allow of + true -> allow; + _ -> deny + end; + false -> match(PubSub, Topic, UserAcl) + end. + +match_topic(Topic, ACLTopic) when is_binary(Topic) -> + emqx_topic:match(Topic, ACLTopic). + +match_actions(_, <<"pubsub">>) -> true; +match_actions(subscribe, <<"sub">>) -> true; +match_actions(publish, <<"pub">>) -> true; +match_actions(_, _) -> false. diff --git a/apps/emqx_auth_mnesia/src/emqx_acl_mnesia_api.erl b/apps/emqx_auth_mnesia/src/emqx_acl_mnesia_api.erl new file mode 100644 index 000000000..3891b7ffd --- /dev/null +++ b/apps/emqx_auth_mnesia/src/emqx_acl_mnesia_api.erl @@ -0,0 +1,148 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_acl_mnesia_api). + +-include("emqx_auth_mnesia.hrl"). + +-import(proplists, [get_value/2]). + +-import(minirest, [return/1]). + +-rest_api(#{name => list_emqx_acl, + method => 'GET', + path => "/mqtt_acl", + func => list, + descr => "List available mnesia in the cluster" + }). + +-rest_api(#{name => lookup_emqx_acl, + method => 'GET', + path => "/mqtt_acl/:bin:login", + func => lookup, + descr => "Lookup mnesia in the cluster" + }). + +-rest_api(#{name => add_emqx_acl, + method => 'POST', + path => "/mqtt_acl", + func => add, + descr => "Add mnesia in the cluster" + }). + +-rest_api(#{name => delete_emqx_acl, + method => 'DELETE', + path => "/mqtt_acl/:bin:login/:bin:topic", + func => delete, + descr => "Delete mnesia in the cluster" + }). + +-export([ list/2 + , lookup/2 + , add/2 + , delete/2 + ]). + +list(_Bindings, Params) -> + return({ok, emqx_auth_mnesia_api:paginate(emqx_acl, Params, fun format/1)}). + +lookup(#{login := Login}, _Params) -> + return({ok, format(emqx_auth_mnesia_cli:lookup_acl(urldecode(Login)))}). + +add(_Bindings, Params) -> + [ P | _] = Params, + case is_list(P) of + true -> return(add_acl(Params, [])); + false -> return(add_acl([Params], [])) + end. + +add_acl([ Params | ParamsN ], ReList ) -> + Login = urldecode(get_value(<<"login">>, Params)), + Topic = urldecode(get_value(<<"topic">>, Params)), + Action = urldecode(get_value(<<"action">>, Params)), + Allow = get_value(<<"allow">>, Params), + Re = case validate([login, topic, action, allow], [Login, Topic, Action, Allow]) of + ok -> + emqx_auth_mnesia_cli:add_acl(Login, Topic, Action, Allow); + Err -> Err + end, + add_acl(ParamsN, [{Login, format_msg(Re)} | ReList]); + +add_acl([], ReList) -> + {ok, ReList}. + +delete(#{login := Login, topic := Topic}, _) -> + return(emqx_auth_mnesia_cli:remove_acl(urldecode(Login), urldecode(Topic))). + +%%------------------------------------------------------------------------------ +%% Interval Funcs +%%------------------------------------------------------------------------------ + +format(#emqx_acl{login = Login, topic = Topic, action = Action, allow = Allow}) -> + #{login => Login, topic => Topic, action => Action, allow => Allow }; + +format([]) -> + #{}; + +format([#emqx_acl{login = Login, topic = Topic, action = Action, allow = Allow}]) -> + format(#emqx_acl{login = Login, topic = Topic, action = Action, allow = Allow}); + +format([ #emqx_acl{login = _Key, topic = _Topic, action = _Action, allow = _Allow}| _] = List) -> + format(List, []). + +format([#emqx_acl{login = Login, topic = Topic, action = Action, allow = Allow} | List], ReList) -> + format(List, [ format(#emqx_acl{login = Login, topic = Topic, action = Action, allow = Allow}) | ReList]); +format([], ReList) -> ReList. + +validate([], []) -> + ok; +validate([K|Keys], [V|Values]) -> + case do_validation(K, V) of + false -> {error, K}; + true -> validate(Keys, Values) + end. + +do_validation(login, V) when is_binary(V) + andalso byte_size(V) > 0 -> + true; +do_validation(topic, V) when is_binary(V) + andalso byte_size(V) > 0 -> + true; +do_validation(action, V) when is_binary(V) -> + case V =:= <<"pub">> orelse V =:= <<"sub">> orelse V =:= <<"pubsub">> of + true -> true; + false -> false + end; +do_validation(allow, V) when is_boolean(V) -> + true; +do_validation(_, _) -> + false. + +format_msg(Message) + when is_atom(Message); + is_binary(Message) -> Message; + +format_msg(Message) when is_tuple(Message) -> + iolist_to_binary(io_lib:format("~p", [Message])). + +-if(?OTP_RELEASE >= 23). +urldecode(S) -> + [{R, _}] = uri_string:dissect_query(S), R. +-else. +urldecode(S) -> + http_uri:decode(S). +-endif. + diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src new file mode 100644 index 000000000..debd8259b --- /dev/null +++ b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src @@ -0,0 +1,14 @@ +{application, emqx_auth_mnesia, + [{description, "EMQ X Authentication with Mnesia"}, + {vsn, "git"}, + {modules, []}, + {registered, []}, + {applications, [kernel,stdlib,mnesia]}, + {mod, {emqx_auth_mnesia_app,[]}}, + {env, []}, + {licenses, ["Apache-2.0"]}, + {maintainers, ["EMQ X Team "]}, + {links, [{"Homepage", "https://emqx.io/"}, + {"Github", "https://github.com/emqx/emqx-auth-mnesia"} + ]} + ]}. diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src.script b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src.script new file mode 100644 index 000000000..0e14ff23f --- /dev/null +++ b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.app.src.script @@ -0,0 +1,24 @@ +%%-*- mode: erlang -*- +%% .app.src.script + +RemoveLeadingV = + fun(Tag) -> + case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of + nomatch -> + re:replace(Tag, "/", "-", [{return ,list}]); + _ -> + %% if it is a version number prefixed by 'v' or 'e', then remove it + re:replace(Tag, "[v|e]", "", [{return ,list}]) + end + end, + +case os:getenv("EMQX_DEPS_DEFAULT_VSN") of + false -> CONFIG; % env var not defined + [] -> CONFIG; % env var set to empty string + Tag -> + [begin + AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}), + {application, App, AppConf0} + end || Conf = {application, App, AppConf} <- CONFIG] +end. + diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl new file mode 100644 index 000000000..4dec64570 --- /dev/null +++ b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia.erl @@ -0,0 +1,76 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_mnesia). + +-include("emqx_auth_mnesia.hrl"). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). +-include_lib("emqx/include/types.hrl"). + +%% Auth callbacks +-export([ init/1 + , register_metrics/0 + , check/3 + , description/0 + ]). + +init(DefaultUsers) -> + ok = ekka_mnesia:create_table(emqx_user, [ + {disc_copies, [node()]}, + {attributes, record_info(fields, emqx_user)}, + {storage_properties, [{ets, [{read_concurrency, true}]}]}]), + ok = lists:foreach(fun add_default_user/1, DefaultUsers), + ok = ekka_mnesia:copy_table(emqx_user, disc_copies). + +%% @private +add_default_user({Login, Password, IsSuperuser}) -> + emqx_auth_mnesia_cli:add_user(iolist_to_binary(Login), iolist_to_binary(Password), IsSuperuser). + +-spec(register_metrics() -> ok). +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). + +check(ClientInfo = #{password := Password}, AuthResult, #{hash_type := HashType, key_as := As}) -> + Login = maps:get(As, ClientInfo), + case emqx_auth_mnesia_cli:lookup_user(Login) of + [] -> + emqx_metrics:inc(?AUTH_METRICS(ignore)), + ok; + [User] -> + case emqx_passwd:check_pass({User#emqx_user.password, Password}, HashType) of + ok -> + emqx_metrics:inc(?AUTH_METRICS(success)), + {stop, AuthResult#{is_superuser => is_superuser(User), + anonymous => false, + auth_result => success}}; + {error, Reason} -> + ?LOG(error, "[Mnesia] Auth from mnesia failed: ~p", [Reason]), + emqx_metrics:inc(?AUTH_METRICS(failure)), + {stop, AuthResult#{auth_result => password_error, anonymous => false}} + end + end. + +description() -> "Authentication with Mnesia". + +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- +is_superuser(#emqx_user{is_superuser = true}) -> + true; +is_superuser(_) -> + false. diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_api.erl b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_api.erl new file mode 100644 index 000000000..7c527b0fd --- /dev/null +++ b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_api.erl @@ -0,0 +1,201 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_mnesia_api). + +-include_lib("stdlib/include/qlc.hrl"). + +-import(proplists, [get_value/2]). + +-import(minirest, [return/1]). + +-rest_api(#{name => list_emqx_user, + method => 'GET', + path => "/mqtt_user", + func => list, + descr => "List available mnesia in the cluster" + }). + +-rest_api(#{name => lookup_emqx_user, + method => 'GET', + path => "/mqtt_user/:bin:login", + func => lookup, + descr => "Lookup mnesia in the cluster" + }). + +-rest_api(#{name => add_emqx_user, + method => 'POST', + path => "/mqtt_user", + func => add, + descr => "Add mnesia in the cluster" + }). + +-rest_api(#{name => update_emqx_user, + method => 'PUT', + path => "/mqtt_user/:bin:login", + func => update, + descr => "Update mnesia in the cluster" + }). + +-rest_api(#{name => delete_emqx_user, + method => 'DELETE', + path => "/mqtt_user/:bin:login", + func => delete, + descr => "Delete mnesia in the cluster" + }). + +-export([ list/2 + , lookup/2 + , add/2 + , update/2 + , delete/2 + ]). + +-export([paginate/3]). + +list(_Bindings, Params) -> + return({ok, paginate(emqx_user, Params, fun format/1)}). + +lookup(#{login := Login}, _Params) -> + return({ok, format(emqx_auth_mnesia_cli:lookup_user(urldecode(Login)))}). + +add(_Bindings, Params) -> + [ P | _] = Params, + case is_list(P) of + true -> return(add_user(Params, [])); + false -> return(add_user([Params], [])) + end. + +add_user([ Params | ParamsN ], ReList ) -> + Login = urldecode(get_value(<<"login">>, Params)), + Password = urldecode(get_value(<<"password">>, Params)), + IsSuperuser = get_value(<<"is_superuser">>, Params), + Re = case validate([login, password, is_superuser], [Login, Password, IsSuperuser]) of + ok -> + emqx_auth_mnesia_cli:add_user(Login, Password, IsSuperuser); + Err -> Err + end, + add_user(ParamsN, [{Login, format_msg(Re)} | ReList]); + +add_user([], ReList) -> + {ok, ReList}. + +update(#{login := Login}, Params) -> + Password = get_value(<<"password">>, Params), + IsSuperuser = get_value(<<"is_superuser">>, Params), + case validate([password, is_superuser], [Password, IsSuperuser]) of + ok -> return(emqx_auth_mnesia_cli:update_user(urldecode(Login), urldecode(Password), IsSuperuser)); + Err -> return(Err) + end. + +delete(#{login := Login}, _) -> + return(emqx_auth_mnesia_cli:remove_user(urldecode(Login))). + +%%------------------------------------------------------------------------------ +%% Paging Query +%%------------------------------------------------------------------------------ + +paginate(Tables, Params, RowFun) -> + Qh = query_handle(Tables), + Count = count(Tables), + Page = page(Params), + Limit = limit(Params), + Cursor = qlc:cursor(Qh), + case Page > 1 of + true -> qlc:next_answers(Cursor, (Page - 1) * Limit); + false -> ok + end, + Rows = qlc:next_answers(Cursor, Limit), + qlc:delete_cursor(Cursor), + #{meta => #{page => Page, limit => Limit, count => Count}, + data => [RowFun(Row) || Row <- Rows]}. + +query_handle(Table) when is_atom(Table) -> + qlc:q([R|| R <- ets:table(Table)]); +query_handle([Table]) when is_atom(Table) -> + qlc:q([R|| R <- ets:table(Table)]); +query_handle(Tables) -> + qlc:append([qlc:q([E || E <- ets:table(T)]) || T <- Tables]). + +count(Table) when is_atom(Table) -> + ets:info(Table, size); +count([Table]) when is_atom(Table) -> + ets:info(Table, size); +count(Tables) -> + lists:sum([count(T) || T <- Tables]). + +page(Params) -> + binary_to_integer(proplists:get_value(<<"_page">>, Params, <<"1">>)). + +limit(Params) -> + case proplists:get_value(<<"_limit">>, Params) of + undefined -> 10; + Size -> binary_to_integer(Size) + end. + + + +%%------------------------------------------------------------------------------ +%% Interval Funcs +%%------------------------------------------------------------------------------ + +format({emqx_user, Login, Password, IsSuperuser}) -> + #{login => Login, + password => Password, + is_superuser => IsSuperuser}; + +format([]) -> + #{}; + +format([{emqx_user, Login, Password, IsSuperuser}]) -> + #{login => Login, + password => Password, + is_superuser => IsSuperuser}. + +validate([], []) -> + ok; +validate([K|Keys], [V|Values]) -> + case do_validation(K, V) of + false -> {error, K}; + true -> validate(Keys, Values) + end. + +do_validation(login, V) when is_binary(V) + andalso byte_size(V) > 0 -> + true; +do_validation(password, V) when is_binary(V) + andalso byte_size(V) > 0 -> + true; +do_validation(is_superuser, V) when is_boolean(V) -> + true; +do_validation(_, _) -> + false. + +format_msg(Message) + when is_atom(Message); + is_binary(Message) -> Message; + +format_msg(Message) when is_tuple(Message) -> + iolist_to_binary(io_lib:format("~p", [Message])). + +-if(?OTP_RELEASE >= 23). +urldecode(S) -> + [{R, _}] = uri_string:dissect_query(S), R. +-else. +urldecode(S) -> + http_uri:decode(S). +-endif. + diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_app.erl b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_app.erl new file mode 100644 index 000000000..094eba0ea --- /dev/null +++ b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_app.erl @@ -0,0 +1,70 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_mnesia_app). + +-behaviour(application). + +-emqx_plugin(auth). + +-include("emqx_auth_mnesia.hrl"). + +%% Application callbacks +-export([ start/2 + , prep_stop/1 + , stop/1 + ]). + +%%-------------------------------------------------------------------- +%% Application callbacks +%%-------------------------------------------------------------------- + +start(_StartType, _StartArgs) -> + {ok, Sup} = emqx_auth_mnesia_sup:start_link(), + emqx_ctl:register_command('mqtt-user', {emqx_auth_mnesia_cli, auth_cli}, []), + emqx_ctl:register_command('mqtt-acl', {emqx_auth_mnesia_cli, acl_cli}, []), + load_auth_hook(), + load_acl_hook(), + {ok, Sup}. + +prep_stop(State) -> + emqx:unhook('client.authenticate', fun emqx_auth_mnesia:check/3), + emqx:unhook('client.check_acl', fun emqx_acl_mnesia:check_acl/5), + emqx_ctl:unregister_command('mqtt-user'), + emqx_ctl:unregister_command('mqtt-acl'), + State. + +stop(_State) -> + ok. + +load_auth_hook() -> + DefaultUsers = application:get_env(?APP, userlist, []), + ok = emqx_auth_mnesia:init(DefaultUsers), + ok = emqx_auth_mnesia:register_metrics(), + Params = #{ + hash_type => application:get_env(emqx_auth_mnesia, hash_type, sha256), + key_as => application:get_env(emqx_auth_mnesia, as, username) + }, + emqx:hook('client.authenticate', fun emqx_auth_mnesia:check/3, [Params]). + +load_acl_hook() -> + ok = emqx_acl_mnesia:init(), + ok = emqx_acl_mnesia:register_metrics(), + Params = #{ + key_as => application:get_env(emqx_auth_mnesia, as, username) + }, + emqx:hook('client.check_acl', fun emqx_acl_mnesia:check_acl/5, [Params]). + diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_cli.erl b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_cli.erl new file mode 100644 index 000000000..83b1f32c0 --- /dev/null +++ b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_cli.erl @@ -0,0 +1,193 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_mnesia_cli). + +-include("emqx_auth_mnesia.hrl"). +-include_lib("emqx/include/logger.hrl"). +-define(TABLE, emqx_user). +%% Auth APIs +-export([ add_user/3 + , update_user/3 + , remove_user/1 + , lookup_user/1 + , all_users/0 + ]). +%% Acl APIs +-export([ add_acl/4 + , remove_acl/2 + , lookup_acl/1 + , all_acls/0 + ]). +%% Cli +-export([ auth_cli/1 + , acl_cli/1]). +%%-------------------------------------------------------------------- +%% Auth APIs +%%-------------------------------------------------------------------- + +%% @doc Add User +-spec(add_user(binary(), binary(), atom()) -> ok | {error, any()}). +add_user(Login, Password, IsSuperuser) -> + User = #emqx_user{login = Login, password = encrypted_data(Password), is_superuser = IsSuperuser}, + ret(mnesia:transaction(fun insert_user/1, [User])). + +insert_user(User = #emqx_user{login = Login}) -> + case mnesia:read(?TABLE, Login) of + [] -> mnesia:write(User); + [_|_] -> mnesia:abort(existed) + end. + +%% @doc Update User +-spec(update_user(binary(), binary(), atom()) -> ok | {error, any()}). +update_user(Login, NewPassword, IsSuperuser) -> + User = #emqx_user{login = Login, password = encrypted_data(NewPassword), is_superuser = IsSuperuser}, + ret(mnesia:transaction(fun do_update_user/1, [User])). + +do_update_user(User = #emqx_user{login = Login}) -> + case mnesia:read(?TABLE, Login) of + [_|_] -> mnesia:write(User); + [] -> mnesia:abort(noexisted) + end. + +%% @doc Lookup user by login +-spec(lookup_user(binary()) -> list()). +lookup_user(undefined) -> []; +lookup_user(Login) -> + case mnesia:dirty_read(?TABLE, Login) of + {error, Reason} -> + ?LOG(error, "[Mnesia] do_check_user error: ~p~n", [Reason]), + []; + Re -> Re + end. + +%% @doc Remove user +-spec(remove_user(binary()) -> ok | {error, any()}). +remove_user(Login) -> + ret(mnesia:transaction(fun mnesia:delete/1, [{?TABLE, Login}])). + +%% @doc All logins +-spec(all_users() -> list()). +all_users() -> mnesia:dirty_all_keys(?TABLE). + +%%-------------------------------------------------------------------- +%% Acl API +%%-------------------------------------------------------------------- + +%% @doc Add Acls +-spec(add_acl(binary(), binary(), binary(), atom()) -> ok | {error, any()}). +add_acl(Login, Topic, Action, Allow) -> + Acls = #emqx_acl{login = Login, topic = Topic, action = Action, allow = Allow}, + ret(mnesia:transaction(fun mnesia:write/1, [Acls])). + +%% @doc Lookup acl by login +-spec(lookup_acl(binary()) -> list()). +lookup_acl(undefined) -> []; +lookup_acl(Login) -> + case mnesia:dirty_read(emqx_acl, Login) of + {error, Reason} -> + ?LOG(error, "[Mnesia] do_check_acl error: ~p~n", [Reason]), + []; + Re -> Re + end. + +%% @doc Remove acl +-spec(remove_acl(binary(), binary()) -> ok | {error, any()}). +remove_acl(Login, Topic) -> + [ ok = mnesia:dirty_delete_object(emqx_acl, #emqx_acl{login = Login, topic = Topic, action = Action, allow = Allow}) || [Action, Allow] <- ets:select(emqx_acl, [{{emqx_acl, Login, Topic,'$1','$2'}, [], ['$$']}])], + ok. + + +%% @doc All logins +-spec(all_acls() -> list()). +all_acls() -> mnesia:dirty_all_keys(emqx_acl). + + +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- + +ret({atomic, ok}) -> ok; +ret({aborted, Error}) -> {error, Error}. + +encrypted_data(Password) -> + HashType = application:get_env(emqx_auth_mnesia, hash_type, sha256), + emqx_passwd:hash(HashType, Password). + +%%-------------------------------------------------------------------- +%% Auth APIs +%%-------------------------------------------------------------------- + +%% User +auth_cli(["add", Login, Password, IsSuperuser]) -> + case add_user(iolist_to_binary(Login), iolist_to_binary(Password), IsSuperuser) of + ok -> emqx_ctl:print("ok~n"); + {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) + end; + +auth_cli(["update", Login, NewPassword, IsSuperuser]) -> + case update_user(iolist_to_binary(Login), iolist_to_binary(NewPassword), IsSuperuser) of + ok -> emqx_ctl:print("ok~n"); + {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) + end; + +auth_cli(["del", Login]) -> + case remove_user(iolist_to_binary(Login)) of + ok -> emqx_ctl:print("ok~n"); + {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) + end; + +auth_cli(["show", P]) -> + [emqx_ctl:print("User(login = ~p is_super = ~p)~n", [Login, IsSuperuser]) + || {_, Login, _Password, IsSuperuser} <- lookup_user(iolist_to_binary(P))]; + +auth_cli(["list"]) -> + [emqx_ctl:print("User(login = ~p)~n",[E]) + || E <- all_users()]; + +auth_cli(_) -> + emqx_ctl:usage([{"mqtt-user add ", "Add user"}, + {"mqtt-user update ", "Update user"}, + {"mqtt-user delete ", "Delete user"}, + {"mqtt-user show ", "Lookup user detail"}, + {"mqtt-user list", "List all users"}]). + +%% Acl +acl_cli(["add", Login, Topic, Action, Allow]) -> + case add_acl(iolist_to_binary(Login), iolist_to_binary(Topic), iolist_to_binary(Action), Allow) of + ok -> emqx_ctl:print("ok~n"); + {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) + end; + +acl_cli(["del", Login, Topic])-> + case remove_acl(iolist_to_binary(Login), iolist_to_binary(Topic)) of + ok -> emqx_ctl:print("ok~n"); + {error, Reason} -> emqx_ctl:print("Error: ~p~n", [Reason]) + end; + +acl_cli(["show", P]) -> + [emqx_ctl:print("Acl(login = ~p topic = ~p action = ~p allow = ~p)~n",[Login, Topic, Action, Allow]) + || {_, Login, Topic, Action, Allow} <- lookup_acl(iolist_to_binary(P)) ]; + +acl_cli(["list"]) -> + [emqx_ctl:print("Acl(login = ~p)~n",[E]) + || E <- all_acls() ]; + +acl_cli(_) -> + emqx_ctl:usage([{"mqtt-acl add ", "Add acl"}, + {"mqtt-acl show ", "Lookup acl detail"}, + {"mqtt-acl del ", "Delete acl"}, + {"mqtt-acl list","List all acls"}]). diff --git a/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_sup.erl b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_sup.erl new file mode 100644 index 000000000..ed32b31e7 --- /dev/null +++ b/apps/emqx_auth_mnesia/src/emqx_auth_mnesia_sup.erl @@ -0,0 +1,36 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_mnesia_sup). + +-behaviour(supervisor). + +-include("emqx_auth_mnesia.hrl"). + +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%%-------------------------------------------------------------------- +%% Supervisor callbacks +%%-------------------------------------------------------------------- + +init([]) -> + {ok, {{one_for_one, 10, 100}, []}}. \ No newline at end of file diff --git a/apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl b/apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl new file mode 100644 index 000000000..11cfca521 --- /dev/null +++ b/apps/emqx_auth_mnesia/test/emqx_acl_mnesia_SUITE.erl @@ -0,0 +1,279 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_acl_mnesia_SUITE). + +-compile(export_all). + +-include("emqx_auth_mnesia.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). + +-import(emqx_ct_http, [ request_api/3 + , request_api/5 + , get_http_data/1 + , create_default_app/0 + , default_auth_header/0 + ]). + +-define(HOST, "http://127.0.0.1:8081/"). +-define(API_VERSION, "v4"). +-define(BASE_PATH, "api"). + +all() -> + emqx_ct:all(?MODULE). + +groups() -> + []. + +init_per_suite(Config) -> + emqx_ct_helpers:start_apps([emqx_management, emqx_auth_mnesia], fun set_special_configs/1), + create_default_app(), + Config. + +end_per_suite(_Config) -> + emqx_ct_helpers:stop_apps([emqx_management, emqx_auth_mnesia]). + +init_per_testcase(t_check_acl_as_clientid, Config) -> + emqx:hook('client.check_acl', fun emqx_acl_mnesia:check_acl/5, [#{key_as => clientid}]), + Config; + +init_per_testcase(_, Config) -> + emqx:hook('client.check_acl', fun emqx_acl_mnesia:check_acl/5, [#{key_as => username}]), + Config. + +end_per_testcase(_, Config) -> + emqx:unhook('client.check_acl', fun emqx_acl_mnesia:check_acl/5), + Config. + +set_special_configs(emqx) -> + application:set_env(emqx, allow_anonymous, true), + application:set_env(emqx, enable_acl_cache, false), + LoadedPluginPath = filename:join(["test", "emqx_SUITE_data", "loaded_plugins"]), + application:set_env(emqx, plugins_loaded_file, + emqx_ct_helpers:deps_path(emqx, LoadedPluginPath)); + +set_special_configs(_App) -> + ok. + +%%------------------------------------------------------------------------------ +%% Testcases +%%------------------------------------------------------------------------------ + +t_management(_Config) -> + clean_all_acls(), + ?assertEqual("Acl with Mnesia", emqx_acl_mnesia:description()), + ?assertEqual([], emqx_auth_mnesia_cli:all_acls()), + + ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/A">>, <<"sub">>, true), + ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/B">>, <<"pub">>, true), + ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/C">>, <<"pubsub">>, true), + + ?assertEqual([{emqx_acl,<<"test_username">>,<<"Topic/A">>,<<"sub">>, true}, + {emqx_acl,<<"test_username">>,<<"Topic/B">>,<<"pub">>, true}, + {emqx_acl,<<"test_username">>,<<"Topic/C">>,<<"pubsub">>, true}],emqx_auth_mnesia_cli:lookup_acl(<<"test_username">>)), + ok = emqx_auth_mnesia_cli:remove_acl(<<"test_username">>, <<"Topic/A">>), + ?assertEqual([{emqx_acl,<<"test_username">>,<<"Topic/B">>,<<"pub">>, true}, + {emqx_acl,<<"test_username">>,<<"Topic/C">>,<<"pubsub">>, true}], emqx_auth_mnesia_cli:lookup_acl(<<"test_username">>)), + + + ok = emqx_auth_mnesia_cli:add_acl(<<"$all">>, <<"Topic/A">>, <<"sub">>, true), + ok = emqx_auth_mnesia_cli:add_acl(<<"$all">>, <<"Topic/B">>, <<"pub">>, true), + ok = emqx_auth_mnesia_cli:add_acl(<<"$all">>, <<"Topic/C">>, <<"pubsub">>, true), + + ?assertEqual([{emqx_acl,<<"$all">>,<<"Topic/A">>,<<"sub">>, true}, + {emqx_acl,<<"$all">>,<<"Topic/B">>,<<"pub">>, true}, + {emqx_acl,<<"$all">>,<<"Topic/C">>,<<"pubsub">>, true}],emqx_auth_mnesia_cli:lookup_acl(<<"$all">>)), + ok = emqx_auth_mnesia_cli:remove_acl(<<"$all">>, <<"Topic/A">>), + ?assertEqual([{emqx_acl,<<"$all">>,<<"Topic/B">>,<<"pub">>, true}, + {emqx_acl,<<"$all">>,<<"Topic/C">>,<<"pubsub">>, true}], emqx_auth_mnesia_cli:lookup_acl(<<"$all">>)). + +t_check_acl_as_clientid(_) -> + clean_all_acls(), + emqx_modules:load_module(emqx_mod_acl_internal, false), + + User1 = #{zone => external, clientid => <<"test_clientid">>}, + User2 = #{zone => external, clientid => <<"no_exist">>}, + + ok = emqx_auth_mnesia_cli:add_acl(<<"test_clientid">>, <<"#">>, <<"sub">>, false), + ok = emqx_auth_mnesia_cli:add_acl(<<"test_clientid">>, <<"+/A">>, <<"pub">>, false), + ok = emqx_auth_mnesia_cli:add_acl(<<"test_clientid">>, <<"Topic/A/B">>, <<"pubsub">>, true), + + deny = emqx_access_control:check_acl(User1, subscribe, <<"Any">>), + deny = emqx_access_control:check_acl(User1, publish, <<"Any/A">>), + allow = emqx_access_control:check_acl(User1, publish, <<"Any/C">>), + allow = emqx_access_control:check_acl(User1, publish, <<"Topic/A/B">>), + + allow = emqx_access_control:check_acl(User2, subscribe, <<"Topic/C">>), + allow = emqx_access_control:check_acl(User2, publish, <<"Topic/D">>). + +t_check_acl_as_username(_Config) -> + clean_all_acls(), + emqx_modules:load_module(emqx_mod_acl_internal, false), + + User1 = #{zone => external, username => <<"test_username">>}, + User2 = #{zone => external, username => <<"no_exist">>}, + + ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/A">>, <<"sub">>, true), + ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/B">>, <<"pub">>, true), + ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/A/B">>, <<"pubsub">>, false), + allow = emqx_access_control:check_acl(User1, subscribe, <<"Topic/A">>), + allow = emqx_access_control:check_acl(User1, subscribe, <<"Topic/B">>), + deny = emqx_access_control:check_acl(User1, subscribe, <<"Topic/A/B">>), + allow = emqx_access_control:check_acl(User1, publish, <<"Topic/A">>), + allow = emqx_access_control:check_acl(User1, publish, <<"Topic/B">>), + deny = emqx_access_control:check_acl(User1, publish, <<"Topic/A/B">>), + + allow = emqx_access_control:check_acl(User2, subscribe, <<"Topic/C">>), + allow = emqx_access_control:check_acl(User2, publish, <<"Topic/D">>). + +t_check_acl_as_all(_) -> + clean_all_acls(), + emqx_modules:load_module(emqx_mod_acl_internal, false), + + ok = emqx_auth_mnesia_cli:add_acl(<<"$all">>, <<"Topic/A">>, <<"sub">>, false), + ok = emqx_auth_mnesia_cli:add_acl(<<"$all">>, <<"Topic/B">>, <<"pub">>, false), + ok = emqx_auth_mnesia_cli:add_acl(<<"$all">>, <<"Topic/A/B">>, <<"pubsub">>, true), + + User1 = #{zone => external, username => <<"test_username">>}, + User2 = #{zone => external, username => <<"no_exist">>}, + + ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/A">>, <<"sub">>, true), + ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/B">>, <<"pub">>, true), + ok = emqx_auth_mnesia_cli:add_acl(<<"test_username">>, <<"Topic/A/B">>, <<"pubsub">>, false), + + allow = emqx_access_control:check_acl(User1, subscribe, <<"Topic/A">>), + allow = emqx_access_control:check_acl(User1, subscribe, <<"Topic/B">>), + deny = emqx_access_control:check_acl(User1, subscribe, <<"Topic/A/B">>), + allow = emqx_access_control:check_acl(User1, publish, <<"Topic/A">>), + allow = emqx_access_control:check_acl(User1, publish, <<"Topic/B">>), + deny = emqx_access_control:check_acl(User1, publish, <<"Topic/A/B">>), + + deny = emqx_access_control:check_acl(User2, subscribe, <<"Topic/A">>), + deny = emqx_access_control:check_acl(User2, publish, <<"Topic/B">>), + allow = emqx_access_control:check_acl(User2, subscribe, <<"Topic/A/B">>), + allow = emqx_access_control:check_acl(User2, publish, <<"Topic/A/B">>), + allow = emqx_access_control:check_acl(User2, subscribe, <<"Topic/C">>), + allow = emqx_access_control:check_acl(User2, publish, <<"Topic/D">>). + +t_rest_api(_Config) -> + clean_all_acls(), + + {ok, Result} = request_http_rest_list(), + [] = get_http_data(Result), + + Params = #{<<"login">> => <<"test_username">>, <<"topic">> => <<"Topic/A">>, <<"action">> => <<"pubsub">>, <<"allow">> => true}, + {ok, _} = request_http_rest_add(Params), + {ok, Result1} = request_http_rest_lookup(<<"test_username">>), + #{<<"login">> := <<"test_username">>, <<"topic">> := <<"Topic/A">>, <<"action">> := <<"pubsub">>, <<"allow">> := true} = get_http_data(Result1), + + Params1 = [ + #{<<"login">> => <<"$all">>, <<"topic">> => <<"+/A">>, <<"action">> => <<"pub">>, <<"allow">> => true}, + #{<<"login">> => <<"test_username">>, <<"topic">> => <<"+/A">>, <<"action">> => <<"pub">>, <<"allow">> => true}, + #{<<"login">> => <<"test_username/1">>, <<"topic">> => <<"#">>, <<"action">> => <<"sub">>, <<"allow">> => true}, + #{<<"login">> => <<"test_username/2">>, <<"topic">> => <<"+/A">>, <<"action">> => <<"error_format">>, <<"allow">> => true} + ], + {ok, Result2} = request_http_rest_add(Params1), + #{ + <<"$all">> := <<"ok">>, + <<"test_username">> := <<"ok">>, + <<"test_username/1">> := <<"ok">>, + <<"test_username/2">> := <<"{error,action}">> + } = get_http_data(Result2), + + {ok, Result3} = request_http_rest_lookup(<<"test_username">>), + [#{<<"login">> := <<"test_username">>, <<"topic">> := <<"+/A">>, <<"action">> := <<"pub">>, <<"allow">> := true}, + #{<<"login">> := <<"test_username">>, <<"topic">> := <<"Topic/A">>, <<"action">> := <<"pubsub">>, <<"allow">> := true}] + = get_http_data(Result3), + + {ok, Result4} = request_http_rest_lookup(<<"$all">>), + #{<<"login">> := <<"$all">>, <<"topic">> := <<"+/A">>, <<"action">> := <<"pub">>, <<"allow">> := true} + = get_http_data(Result4), + + {ok, _} = request_http_rest_delete(<<"$all">>, <<"+/A">>), + {ok, _} = request_http_rest_delete(<<"test_username">>, <<"+/A">>), + {ok, _} = request_http_rest_delete(<<"test_username">>, <<"Topic/A">>), + {ok, _} = request_http_rest_delete(<<"test_username/1">>, <<"#">>), + {ok, Result5} = request_http_rest_list(), + [] = get_http_data(Result5). + + +t_run_command(_) -> + clean_all_acls(), + ?assertEqual(ok, emqx_ctl:run_command(["mqtt-acl", "add", "TestUser", "Topic/A", "sub", true])), + ?assertEqual([{emqx_acl,<<"TestUser">>,<<"Topic/A">>,<<"sub">>, true}],emqx_auth_mnesia_cli:lookup_acl(<<"TestUser">>)), + + ?assertEqual(ok, emqx_ctl:run_command(["mqtt-acl", "del", "TestUser", "Topic/A"])), + ?assertEqual([],emqx_auth_mnesia_cli:lookup_acl(<<"TestUser">>)), + + ?assertEqual(ok, emqx_ctl:run_command(["mqtt-acl", "show", "TestUser"])), + ?assertEqual(ok, emqx_ctl:run_command(["mqtt-acl", "list"])), + ?assertEqual(ok, emqx_ctl:run_command(["mqtt-acl"])). + +t_cli(_) -> + meck:new(emqx_ctl, [non_strict, passthrough]), + meck:expect(emqx_ctl, print, fun(Arg) -> emqx_ctl:format(Arg) end), + meck:expect(emqx_ctl, print, fun(Msg, Arg) -> emqx_ctl:format(Msg, Arg) end), + meck:expect(emqx_ctl, usage, fun(Usages) -> emqx_ctl:format_usage(Usages) end), + meck:expect(emqx_ctl, usage, fun(Cmd, Descr) -> emqx_ctl:format_usage(Cmd, Descr) end), + + clean_all_acls(), + ?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:acl_cli(["add", "TestUser", "Topic/A", "sub", true]), "ok")), + ?assertMatch(["Acl(login = <<\"TestUser\">> topic = <<\"Topic/A\">> action = <<\"sub\">> allow = true)\n"], emqx_auth_mnesia_cli:acl_cli(["show", "TestUser"])), + ?assertMatch(["Acl(login = <<\"TestUser\">>)\n"], emqx_auth_mnesia_cli:acl_cli(["list"])), + + ?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:acl_cli(["del", "TestUser", "Topic/A"]), "ok")), + ?assertMatch([], emqx_auth_mnesia_cli:acl_cli(["show", "TestUser"])), + ?assertMatch([], emqx_auth_mnesia_cli:acl_cli(["list"])), + + ?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:acl_cli([]), "mqtt-acl")), + + meck:unload(emqx_ctl). + +%%------------------------------------------------------------------------------ +%% Helpers +%%------------------------------------------------------------------------------ + +clean_all_acls() -> + [ mnesia:dirty_delete({emqx_acl, Login}) + || Login <- mnesia:dirty_all_keys(emqx_acl)]. + +%%-------------------------------------------------------------------- +%% HTTP Request +%%-------------------------------------------------------------------- + +request_http_rest_list() -> + request_api(get, uri(), default_auth_header()). + +request_http_rest_lookup(Login) -> + request_api(get, uri([Login]), default_auth_header()). + +request_http_rest_add(Params) -> + request_api(post, uri(), [], default_auth_header(), Params). + +request_http_rest_delete(Login, Topic) -> + request_api(delete, uri([Login, Topic]), default_auth_header()). + +uri() -> uri([]). +uri(Parts) when is_list(Parts) -> + NParts = [b2l(E) || E <- Parts], + ?HOST ++ filename:join([?BASE_PATH, ?API_VERSION, "mqtt_acl"| NParts]). + +%% @private +b2l(B) when is_binary(B) -> + http_uri:encode(binary_to_list(B)); +b2l(L) when is_list(L) -> + http_uri:encode(L). diff --git a/apps/emqx_auth_mnesia/test/emqx_auth_mnesia_SUITE.erl b/apps/emqx_auth_mnesia/test/emqx_auth_mnesia_SUITE.erl new file mode 100644 index 000000000..8979f4755 --- /dev/null +++ b/apps/emqx_auth_mnesia/test/emqx_auth_mnesia_SUITE.erl @@ -0,0 +1,259 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_mnesia_SUITE). + +-compile(export_all). + +-include("emqx_auth_mnesia.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). + +-import(emqx_ct_http, [ request_api/3 + , request_api/5 + , get_http_data/1 + , create_default_app/0 + , default_auth_header/0 + ]). + +-define(HOST, "http://127.0.0.1:8081/"). +-define(API_VERSION, "v4"). +-define(BASE_PATH, "api"). + +all() -> + emqx_ct:all(?MODULE). + +groups() -> + []. + +init_per_suite(Config) -> + ok = emqx_ct_helpers:start_apps([emqx_management, emqx_auth_mnesia], fun set_special_configs/1), + create_default_app(), + Config. + +end_per_suite(_Config) -> + emqx_ct_helpers:stop_apps([emqx_management, emqx_auth_mnesia]). + +init_per_testcase(t_check_as_clientid, Config) -> + Params = #{ + hash_type => application:get_env(emqx_auth_mnesia, hash_type, sha256), + key_as => clientid + }, + emqx:hook('client.authenticate', fun emqx_auth_mnesia:check/3, [Params]), + Config; + +init_per_testcase(_, Config) -> + Params = #{ + hash_type => application:get_env(emqx_auth_mnesia, hash_type, sha256), + key_as => username + }, + emqx:hook('client.authenticate', fun emqx_auth_mnesia:check/3, [Params]), + Config. + +end_per_suite(_, Config) -> + emqx:unhook('client.authenticate', fun emqx_auth_mnesia:check/3), + Config. + +set_special_configs(emqx) -> + application:set_env(emqx, allow_anonymous, true), + application:set_env(emqx, enable_acl_cache, false), + LoadedPluginPath = filename:join(["test", "emqx_SUITE_data", "loaded_plugins"]), + application:set_env(emqx, plugins_loaded_file, + emqx_ct_helpers:deps_path(emqx, LoadedPluginPath)); + +set_special_configs(_App) -> + ok. + +%%------------------------------------------------------------------------------ +%% Testcases +%%------------------------------------------------------------------------------ + +t_check_as_username(_Config) -> + clean_all_users(), + + ok = emqx_auth_mnesia_cli:add_user(<<"test_username">>, <<"password">>, true), + {error, existed} = emqx_auth_mnesia_cli:add_user(<<"test_username">>, <<"password">>, true), + + ok = emqx_auth_mnesia_cli:update_user(<<"test_username">>, <<"new_password">>, false), + {error,noexisted} = emqx_auth_mnesia_cli:update_user(<<"no_existed_user">>, <<"password">>, true), + + [<<"test_username">>] = emqx_auth_mnesia_cli:all_users(), + [{emqx_user, <<"test_username">>, _HashedPass, false}] = + emqx_auth_mnesia_cli:lookup_user(<<"test_username">>), + + User1 = #{username => <<"test_username">>, + password => <<"new_password">>, + zone => external}, + + {ok, #{is_superuser := false, + auth_result := success, + anonymous := false}} = emqx_access_control:authenticate(User1), + + {error,password_error} = emqx_access_control:authenticate(User1#{password => <<"error_password">>}), + + ok = emqx_auth_mnesia_cli:remove_user(<<"test_username">>), + {ok, #{auth_result := success, + anonymous := true }} = emqx_access_control:authenticate(User1). + +t_check_as_clientid(_Config) -> + clean_all_users(), + + ok = emqx_auth_mnesia_cli:add_user(<<"test_clientid">>, <<"password">>, false), + {error, existed} = emqx_auth_mnesia_cli:add_user(<<"test_clientid">>, <<"password">>, false), + + ok = emqx_auth_mnesia_cli:update_user(<<"test_clientid">>, <<"new_password">>, true), + {error,noexisted} = emqx_auth_mnesia_cli:update_user(<<"no_existed_user">>, <<"password">>, true), + + [<<"test_clientid">>] = emqx_auth_mnesia_cli:all_users(), + [{emqx_user, <<"test_clientid">>, _HashedPass, true}] = + emqx_auth_mnesia_cli:lookup_user(<<"test_clientid">>), + + User1 = #{clientid => <<"test_clientid">>, + password => <<"new_password">>, + zone => external}, + + {ok, #{is_superuser := true, + auth_result := success, + anonymous := false}} = emqx_access_control:authenticate(User1), + + {error,password_error} = emqx_access_control:authenticate(User1#{password => <<"error_password">>}), + + ok = emqx_auth_mnesia_cli:remove_user(<<"test_clientid">>), + {ok, #{auth_result := success, + anonymous := true }} = emqx_access_control:authenticate(User1). + +t_rest_api(_Config) -> + clean_all_users(), + + {ok, Result1} = request_http_rest_list(), + [] = get_http_data(Result1), + + Params = #{<<"login">> => <<"test_username">>, <<"password">> => <<"password">>, <<"is_superuser">> => true}, + {ok, _} = request_http_rest_add(Params), + + Params1 = [ + #{<<"login">> => <<"test_username">>, <<"password">> => <<"password">>, <<"is_superuser">> => true}, + #{<<"login">> => <<"test_username/1">>, <<"password">> => <<"password">>, <<"is_superuser">> => error_format}, + #{<<"login">> => <<"test_username/2">>, <<"password">> => <<"password">>, <<"is_superuser">> => true} + ], + {ok, Result2} = request_http_rest_add(Params1), + #{ + <<"test_username">> := <<"{error,existed}">>, + <<"test_username/1">> := <<"{error,is_superuser}">>, + <<"test_username/2">> := <<"ok">> + } = get_http_data(Result2), + + {ok, Result3} = request_http_rest_lookup(<<"test_username">>), + #{<<"login">> := <<"test_username">>, <<"is_superuser">> := true} = get_http_data(Result3), + + {ok, _} = request_http_rest_update(<<"test_username">>, <<"new_password">>, error_format), + {ok, _} = request_http_rest_update(<<"error_username">>, <<"new_password">>, false), + + {ok, _} = request_http_rest_update(<<"test_username">>, <<"new_password">>, false), + {ok, Result4} = request_http_rest_lookup(<<"test_username">>), + #{<<"login">> := <<"test_username">>, <<"is_superuser">> := false} = get_http_data(Result4), + + User1 = #{username => <<"test_username">>, + password => <<"new_password">>, + zone => external}, + + {ok, #{is_superuser := false, + auth_result := success, + anonymous := false}} = emqx_access_control:authenticate(User1), + + {ok, _} = request_http_rest_delete(<<"test_username">>), + {ok, #{auth_result := success, + anonymous := true }} = emqx_access_control:authenticate(User1). + +t_run_command(_) -> + clean_all_users(), + ?assertEqual(ok, emqx_ctl:run_command(["mqtt-user", "add", "TestUser", "Password", false])), + ?assertMatch([{emqx_user, <<"TestUser">>, _, false}], emqx_auth_mnesia_cli:lookup_user(<<"TestUser">>)), + + ?assertEqual(ok, emqx_ctl:run_command(["mqtt-user", "update", "TestUser", "NewPassword", true])), + ?assertMatch([{emqx_user, <<"TestUser">>, _, true}], emqx_auth_mnesia_cli:lookup_user(<<"TestUser">>)), + + ?assertEqual(ok, emqx_ctl:run_command(["mqtt-user", "del", "TestUser"])), + ?assertMatch([], emqx_auth_mnesia_cli:lookup_user(<<"TestUser">>)), + + ?assertEqual(ok, emqx_ctl:run_command(["mqtt-user", "show", "TestUser"])), + ?assertEqual(ok, emqx_ctl:run_command(["mqtt-user", "list"])), + ?assertEqual(ok, emqx_ctl:run_command(["mqtt-user"])). + +t_cli(_) -> + meck:new(emqx_ctl, [non_strict, passthrough]), + meck:expect(emqx_ctl, print, fun(Arg) -> emqx_ctl:format(Arg) end), + meck:expect(emqx_ctl, print, fun(Msg, Arg) -> emqx_ctl:format(Msg, Arg) end), + meck:expect(emqx_ctl, usage, fun(Usages) -> emqx_ctl:format_usage(Usages) end), + meck:expect(emqx_ctl, usage, fun(Cmd, Descr) -> emqx_ctl:format_usage(Cmd, Descr) end), + + clean_all_users(), + + ?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:auth_cli(["add", "TestUser", "Password", true]), "ok")), + ?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:auth_cli(["add", "TestUser", "Password", true]), "Error")), + + ?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:auth_cli(["update", "NoExisted", "Password", false]), "Error")), + ?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:auth_cli(["update", "TestUser", "Password", false]), "ok")), + + ?assertMatch(["User(login = <<\"TestUser\">> is_super = false)\n"], emqx_auth_mnesia_cli:auth_cli(["show", "TestUser"])), + ?assertMatch(["User(login = <<\"TestUser\">>)\n"], emqx_auth_mnesia_cli:auth_cli(["list"])), + + ?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:auth_cli(["del", "TestUser"]), "ok")), + ?assertMatch([], emqx_auth_mnesia_cli:auth_cli(["show", "TestUser"])), + ?assertMatch([], emqx_auth_mnesia_cli:auth_cli(["list"])), + + ?assertMatch({match, _}, re:run(emqx_auth_mnesia_cli:auth_cli([]), "mqtt-user")), + + meck:unload(emqx_ctl). + +%%------------------------------------------------------------------------------ +%% Helpers +%%------------------------------------------------------------------------------ + +clean_all_users() -> + [ mnesia:dirty_delete({emqx_user, Login}) + || Login <- mnesia:dirty_all_keys(emqx_user)]. + +%%-------------------------------------------------------------------- +%% HTTP Request +%%-------------------------------------------------------------------- + +request_http_rest_list() -> + request_api(get, uri(), default_auth_header()). + +request_http_rest_lookup(Login) -> + request_api(get, uri([Login]), default_auth_header()). + +request_http_rest_add(Params) -> + request_api(post, uri(), [], default_auth_header(), Params). + +request_http_rest_update(Login, Password, IsSuperuser) -> + Params = #{<<"password">> => Password, <<"is_superuser">> => IsSuperuser}, + request_api(put, uri([Login]), [], default_auth_header(), Params). + +request_http_rest_delete(Login) -> + request_api(delete, uri([Login]), default_auth_header()). + +uri() -> uri([]). +uri(Parts) when is_list(Parts) -> + NParts = [b2l(E) || E <- Parts], + ?HOST ++ filename:join([?BASE_PATH, ?API_VERSION, "mqtt_user"| NParts]). + +%% @private +b2l(B) when is_binary(B) -> + binary_to_list(B); +b2l(L) when is_list(L) -> + L. diff --git a/apps/emqx_auth_mongo/.github/workflows/run_test_cases.yaml b/apps/emqx_auth_mongo/.github/workflows/run_test_cases.yaml new file mode 100644 index 000000000..94217cacb --- /dev/null +++ b/apps/emqx_auth_mongo/.github/workflows/run_test_cases.yaml @@ -0,0 +1,59 @@ +name: Run test cases + +on: [push, pull_request] + +jobs: + run_test_cases: + runs-on: ubuntu-latest + + strategy: + matrix: + mongo_tag: + - 3 + - 4 + network_type: + - ipv4 + - ipv6 + connect_type: + - ssl + - tcp + + steps: + - name: install docker-compose + run: | + sudo curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + - uses: actions/checkout@v1 + - name: run test cases + env: + MONGO_TAG: ${{ matrix.mongo_tag }} + NETWORK_TYPE: ${{ matrix.network_type }} + CONNECT_TYPE: ${{ matrix.connect_type }} + run: | + set -e -u -x + if [ "$NETWORK_TYPE" = "ipv6" ];then docker network create --driver bridge --ipv6 --subnet fd15:555::/64 tests_emqx_bridge --attachable; fi + if [ "$CONNECT_TYPE" = "ssl" ]; then + docker-compose -f ./docker-compose-ssl.yml -p tests up -d + docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "echo 'auth.mongo.ssl = true' >> /emqx_auth_mongo/etc/emqx_auth_mongo.conf" + docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "echo 'auth.mongo.ssl_opts.cacertfile = /emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca.pem' >> /emqx_auth_mongo/etc/emqx_auth_mongo.conf" + docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "echo 'auth.mongo.ssl_opts.certfile = /emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-cert.pem' >> /emqx_auth_mongo/etc/emqx_auth_mongo.conf" + docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "echo 'auth.mongo.ssl_opts.keyfile = /emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-key.pem' >> /emqx_auth_mongo/etc/emqx_auth_mongo.conf" + else + docker-compose -f ./docker-compose.yml -p tests up -d + fi + if [ "$NETWORK_TYPE" != "ipv6" ];then + docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "sed -i '/auth.mongo.server/c auth.mongo.server = mongo_server:27017' /emqx_auth_mongo/etc/emqx_auth_mongo.conf" + else + ipv6_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' $(docker ps -a -f name=tests_mongo_server_1 -q)) + docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "sed -i '/auth.mongo.server/c auth.mongo.server = $ipv6_address:27017' /emqx_auth_mongo/etc/emqx_auth_mongo.conf" + fi + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_mongo xref" + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_mongo eunit" + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_mongo ct" + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_mongo cover" + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: logs_mongo${{ matrix.mongo_tag}}_${{ matrix.network_type }} + path: _build/test/logs + diff --git a/apps/emqx_auth_mongo/.gitignore b/apps/emqx_auth_mongo/.gitignore new file mode 100644 index 000000000..a6635ffa0 --- /dev/null +++ b/apps/emqx_auth_mongo/.gitignore @@ -0,0 +1,24 @@ +.eunit +deps +*.o +*.beam +*.plt +erl_crash.dump +ebin +rel/example_project +.concrete/DEV_MODE +.rebar +.DS_Store +.erlang.mk/ +emqx_auth_mongo.d +ct.coverdata +logs/ +test/ct.cover.spec +data/ +cover/ +eunit.coverdata +_build/ +rebar.lock +erlang.mk +etc/emqx_auth_mongo.conf.rendered +.rebar3 diff --git a/apps/emqx_auth_mongo/CHANGES b/apps/emqx_auth_mongo/CHANGES new file mode 100644 index 000000000..4bddd63a9 --- /dev/null +++ b/apps/emqx_auth_mongo/CHANGES @@ -0,0 +1,31 @@ + +2.0.7 (2017-01-20) +------------------ + +Tag 2.0.7 - use `cuttlefish:unset()` for commented ACL/super config + +2.0.1 (2016-11-30) +------------------ + +Tag 2.0.1 + +2.0-beta.1 (2016-08-24) +----------------------- + +gen_conf + +1.1.3-beta (2016-08-19) +----------------------- + +Bump version to 1.1.3 + +1.1.2-beta (2016-06-30) +----------------------- + +Bump version to 1.1.2 + +1.1-beta (2016-05-28) +--------------------- + +First public release + diff --git a/apps/emqx_auth_mongo/LICENSE b/apps/emqx_auth_mongo/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/apps/emqx_auth_mongo/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/apps/emqx_auth_mongo/README.md b/apps/emqx_auth_mongo/README.md new file mode 100644 index 000000000..3bacfca5b --- /dev/null +++ b/apps/emqx_auth_mongo/README.md @@ -0,0 +1,192 @@ +emqx_auth_mongo +=============== + +EMQ X Authentication/ACL with MongoDB + +Build the Plugin +---------------- + +``` +make & make tests +``` + +Configuration +------------- + +File: etc/emqx_auth_mongo.conf + +``` +## MongoDB Topology Type. +## +## Value: single | unknown | sharded | rs +auth.mongo.type = single + +## Sets the set name if type is rs. +## +## Value: String +## auth.mongo.rs_set_name = + +## MongoDB server list. +## +## Value: String +## +## Examples: 127.0.0.1:27017,127.0.0.2:27017... +auth.mongo.server = 127.0.0.1:27017 + +## MongoDB pool size +## +## Value: Number +auth.mongo.pool = 8 + +## MongoDB login user. +## +## Value: String +## auth.mongo.login = + +## MongoDB password. +## +## Value: String +## auth.mongo.password = + +## MongoDB AuthSource +## +## Value: String +## Default: mqtt +## auth.mongo.auth_source = admin + +## MongoDB database +## +## Value: String +auth.mongo.database = mqtt + +## MongoDB write mode. +## +## Value: unsafe | safe +## auth.mongo.w_mode = + +## Mongo read mode. +## +## Value: master | slave_ok +## auth.mongo.r_mode = + +## MongoDB topology options. +auth.mongo.topology.pool_size = 1 +auth.mongo.topology.max_overflow = 0 +## auth.mongo.topology.overflow_ttl = 1000 +## auth.mongo.topology.overflow_check_period = 1000 +## auth.mongo.topology.local_threshold_ms = 1000 +## auth.mongo.topology.connect_timeout_ms = 20000 +## auth.mongo.topology.socket_timeout_ms = 100 +## auth.mongo.topology.server_selection_timeout_ms = 30000 +## auth.mongo.topology.wait_queue_timeout_ms = 1000 +## auth.mongo.topology.heartbeat_frequency_ms = 10000 +## auth.mongo.topology.min_heartbeat_frequency_ms = 1000 + +## Authentication query. +auth.mongo.auth_query.collection = mqtt_user + +auth.mongo.auth_query.password_field = password + +## Password hash. +## +## Value: plain | md5 | sha | sha256 | bcrypt +auth.mongo.auth_query.password_hash = sha256 + +## sha256 with salt suffix +## auth.mongo.auth_query.password_hash = sha256,salt + +## sha256 with salt prefix +## auth.mongo.auth_query.password_hash = salt,sha256 + +## bcrypt with salt prefix +## auth.mongo.auth_query.password_hash = salt,bcrypt + +## pbkdf2 with macfun iterations dklen +## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 +## auth.mongo.auth_query.password_hash = pbkdf2,sha256,1000,20 + +auth.mongo.auth_query.selector = username=%u + +## Enable superuser query. +auth.mongo.super_query = on + +auth.mongo.super_query.collection = mqtt_user + +auth.mongo.super_query.super_field = is_superuser + +auth.mongo.super_query.selector = username=%u + +## Enable ACL query. +auth.mongo.acl_query = on + +auth.mongo.acl_query.collection = mqtt_acl + +auth.mongo.acl_query.selector = username=%u +``` + +Load the Plugin +--------------- + +``` +./bin/emqx_ctl plugins load emqx_auth_mongo +``` + +MongoDB Database +---------------- + +``` +use mqtt +db.createCollection("mqtt_user") +db.createCollection("mqtt_acl") +db.mqtt_user.ensureIndex({"username":1}) +``` + +mqtt_user Collection +-------------------- + +``` +{ + username: "user", + password: "password hash", + salt: "password salt", + is_superuser: boolean (true, false), + created: "datetime" +} +``` + +For example: +``` +db.mqtt_user.insert({username: "test", password: "password hash", salt: "password salt", is_superuser: false}) +db.mqtt_user.insert({username: "root", is_superuser: true}) +``` + +mqtt_acl Collection +------------------- + +``` +{ + username: "username", + clientid: "clientid", + publish: ["topic1", "topic2", ...], + subscribe: ["subtop1", "subtop2", ...], + pubsub: ["topic/#", "topic1", ...] +} +``` + +For example: + +``` +db.mqtt_acl.insert({username: "test", publish: ["t/1", "t/2"], subscribe: ["user/%u", "client/%c"]}) +db.mqtt_acl.insert({username: "admin", pubsub: ["#"]}) +``` + +License +------- + +Apache License Version 2.0 + +Author +------ + +EMQ X Team. + diff --git a/apps/emqx_auth_mongo/docker-compose-ssl.yml b/apps/emqx_auth_mongo/docker-compose-ssl.yml new file mode 100644 index 000000000..7af0981b1 --- /dev/null +++ b/apps/emqx_auth_mongo/docker-compose-ssl.yml @@ -0,0 +1,31 @@ +version: '3' + +services: + erlang: + image: erlang:22.1 + volumes: + - ./:/emqx_auth_mongo + networks: + - emqx_bridge + depends_on: + - mongo_server + tty: true + + mongo_server: + image: mongo:${MONGO_TAG} + restart: always + environment: + MONGO_INITDB_DATABASE: mqtt + volumes: + - ./test/emqx_auth_mongo_SUITE_data/mongodb.pem/:/etc/certs/mongodb.pem + networks: + - emqx_bridge + command: + --ipv6 + --bind_ip_all + --sslMode requireSSL + --sslPEMKeyFile /etc/certs/mongodb.pem + +networks: + emqx_bridge: + driver: bridge diff --git a/apps/emqx_auth_mongo/docker-compose.yml b/apps/emqx_auth_mongo/docker-compose.yml new file mode 100644 index 000000000..9ff9cce23 --- /dev/null +++ b/apps/emqx_auth_mongo/docker-compose.yml @@ -0,0 +1,27 @@ +version: '3' + +services: + erlang: + image: erlang:22.1 + volumes: + - ./:/emqx_auth_mongo + networks: + - emqx_bridge + depends_on: + - mongo_server + tty: true + + mongo_server: + image: mongo:${MONGO_TAG} + restart: always + environment: + MONGO_INITDB_DATABASE: mqtt + networks: + - emqx_bridge + command: + --ipv6 + --bind_ip_all + +networks: + emqx_bridge: + driver: bridge diff --git a/apps/emqx_auth_mongo/etc/emqx_auth_mongo.conf b/apps/emqx_auth_mongo/etc/emqx_auth_mongo.conf new file mode 100644 index 000000000..cf1614efa --- /dev/null +++ b/apps/emqx_auth_mongo/etc/emqx_auth_mongo.conf @@ -0,0 +1,172 @@ +##-------------------------------------------------------------------- +## MongoDB Auth/ACL Plugin +##-------------------------------------------------------------------- + +## MongoDB Topology Type. +## +## Value: single | unknown | sharded | rs +auth.mongo.type = single + +## The set name if type is rs. +## +## Value: String +## auth.mongo.rs_set_name = + +## MongoDB server list. +## +## Value: String +## +## Examples: 127.0.0.1:27017,127.0.0.2:27017... +auth.mongo.server = 127.0.0.1:27017 + +## MongoDB pool size +## +## Value: Number +auth.mongo.pool = 8 + +## MongoDB login user. +## +## Value: String +## auth.mongo.login = + +## MongoDB password. +## +## Value: String +## auth.mongo.password = + +## MongoDB AuthSource +## +## Value: String +## Default: mqtt +## auth.mongo.auth_source = admin + +## MongoDB database +## +## Value: String +auth.mongo.database = mqtt + +## MongoDB query timeout +## +## Value: Duration +## auth.mongo.query_timeout = 5s + +## Whether to enable SSL connection. +## +## Value: true | false +## auth.mongo.ssl = false + +## SSL keyfile. +## +## Value: File +## auth.mongo.ssl_opts.keyfile = + +## SSL certfile. +## +## Value: File +## auth.mongo.ssl_opts.certfile = + +## SSL cacertfile. +## +## Value: File +## auth.mongo.ssl_opts.cacertfile = + +## MongoDB write mode. +## +## Value: unsafe | safe +## auth.mongo.w_mode = + +## Mongo read mode. +## +## Value: master | slave_ok +## auth.mongo.r_mode = + +## MongoDB topology options. +auth.mongo.topology.pool_size = 1 +auth.mongo.topology.max_overflow = 0 +## auth.mongo.topology.overflow_ttl = 1000 +## auth.mongo.topology.overflow_check_period = 1000 +## auth.mongo.topology.local_threshold_ms = 1000 +## auth.mongo.topology.connect_timeout_ms = 20000 +## auth.mongo.topology.socket_timeout_ms = 100 +## auth.mongo.topology.server_selection_timeout_ms = 30000 +## auth.mongo.topology.wait_queue_timeout_ms = 1000 +## auth.mongo.topology.heartbeat_frequency_ms = 10000 +## auth.mongo.topology.min_heartbeat_frequency_ms = 1000 + +## ------------------------------------------------- +## Auth Query +## ------------------------------------------------- +## Password hash. +## +## Value: plain | md5 | sha | sha256 | bcrypt +auth.mongo.auth_query.password_hash = sha256 + +## sha256 with salt suffix +## auth.mongo.auth_query.password_hash = sha256,salt + +## sha256 with salt prefix +## auth.mongo.auth_query.password_hash = salt,sha256 + +## bcrypt with salt prefix +## auth.mongo.auth_query.password_hash = salt,bcrypt + +## pbkdf2 with macfun iterations dklen +## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 +## auth.mongo.auth_query.password_hash = pbkdf2,sha256,1000,20 + +## Authentication query. +auth.mongo.auth_query.collection = mqtt_user + +## Password mainly fields +## +## Value: password | password,salt +auth.mongo.auth_query.password_field = password + +## Authentication Selector. +## +## Variables: +## - %u: username +## - %c: clientid +## - %C: common name of client TLS cert +## - %d: subject of client TLS cert +## +## auth.mongo.auth_query.selector = {Field}={Placeholder} +auth.mongo.auth_query.selector = username=%u + +## ------------------------------------------------- +## Super User Query +## ------------------------------------------------- +auth.mongo.super_query.collection = mqtt_user +auth.mongo.super_query.super_field = is_superuser +#auth.mongo.super_query.selector = username=%u, clientid=%c +auth.mongo.super_query.selector = username=%u + +## ACL Selector. +## +## Multiple selectors could be combined with '$or' +## when query acl from mongo. +## +## e.g. +## +## With following 2 selectors configured: +## +## auth.mongo.acl_query.selector.1 = username=%u +## auth.mongo.acl_query.selector.2 = username=$all +## +## And if a client connected using username 'ilyas', +## then the following mongo command will be used to +## retrieve acl entries: +## +## db.mqtt_acl.find({$or: [{username: "ilyas"}, {username: "$all"}]}); +## +## Variables: +## - %u: username +## - %c: clientid +## +## Examples: +## +## auth.mongo.acl_query.selector.1 = username=%u,clientid=%c +## auth.mongo.acl_query.selector.2 = username=$all +## auth.mongo.acl_query.selector.3 = clientid=$all +auth.mongo.acl_query.collection = mqtt_acl +auth.mongo.acl_query.selector = username=%u diff --git a/apps/emqx_auth_mongo/include/emqx_auth_mongo.hrl b/apps/emqx_auth_mongo/include/emqx_auth_mongo.hrl new file mode 100644 index 000000000..97ecf9973 --- /dev/null +++ b/apps/emqx_auth_mongo/include/emqx_auth_mongo.hrl @@ -0,0 +1,37 @@ + +-define(APP, emqx_auth_mongo). + +-define(DEFAULT_SELECTORS, [{<<"username">>, <<"%u">>}]). + +-record(superquery, {collection = <<"mqtt_user">>, + field = <<"is_superuser">>, + selector = {<<"username">>, <<"%u">>}}). + +-record(authquery, {collection = <<"mqtt_user">>, + field = <<"password">>, + hash = sha256, + selector = {<<"username">>, <<"%u">>}}). + +-record(aclquery, {collection = <<"mqtt_acl">>, + selector = {<<"username">>, <<"%u">>}}). + +-record(auth_metrics, { + success = 'client.auth.success', + failure = 'client.auth.failure', + ignore = 'client.auth.ignore' + }). + +-record(acl_metrics, { + allow = 'client.acl.allow', + deny = 'client.acl.deny', + ignore = 'client.acl.ignore' + }). + +-define(METRICS(Type), tl(tuple_to_list(#Type{}))). +-define(METRICS(Type, K), #Type{}#Type.K). + +-define(AUTH_METRICS, ?METRICS(auth_metrics)). +-define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). + +-define(ACL_METRICS, ?METRICS(acl_metrics)). +-define(ACL_METRICS(K), ?METRICS(acl_metrics, K)). diff --git a/apps/emqx_auth_mongo/priv/emqx_auth_mongo.schema b/apps/emqx_auth_mongo/priv/emqx_auth_mongo.schema new file mode 100644 index 000000000..9b331e6c4 --- /dev/null +++ b/apps/emqx_auth_mongo/priv/emqx_auth_mongo.schema @@ -0,0 +1,292 @@ +%%-*- mode: erlang -*- +%% emqx_auth_mongo config mapping + +{mapping, "auth.mongo.type", "emqx_auth_mongo.server", [ + {default, single}, + {datatype, {enum, [single, unknown, sharded, rs]}} +]}. + +{mapping, "auth.mongo.rs_set_name", "emqx_auth_mongo.server", [ + {default, "mqtt"}, + {datatype, string} +]}. + +{mapping, "auth.mongo.server", "emqx_auth_mongo.server", [ + {default, "127.0.0.1:27017"}, + {datatype, string} +]}. + +{mapping, "auth.mongo.pool", "emqx_auth_mongo.server", [ + {default, 8}, + {datatype, integer} +]}. + +{mapping, "auth.mongo.login", "emqx_auth_mongo.server", [ + {default, ""}, + {datatype, string} +]}. + +{mapping, "auth.mongo.password", "emqx_auth_mongo.server", [ + {default, ""}, + {datatype, string} +]}. + +{mapping, "auth.mongo.database", "emqx_auth_mongo.server", [ + {default, "mqtt"}, + {datatype, string} +]}. + +{mapping, "auth.mongo.auth_source", "emqx_auth_mongo.server", [ + {default, "mqtt"}, + {datatype, string} +]}. + +{mapping, "auth.mongo.ssl", "emqx_auth_mongo.server", [ + {default, false}, + {datatype, {enum, [true, false]}} +]}. + +{mapping, "auth.mongo.ssl_opts.keyfile", "emqx_auth_mongo.server", [ + {datatype, string} +]}. + +{mapping, "auth.mongo.ssl_opts.certfile", "emqx_auth_mongo.server", [ + {datatype, string} +]}. + +{mapping, "auth.mongo.ssl_opts.cacertfile", "emqx_auth_mongo.server", [ + {datatype, string} +]}. + +{mapping, "auth.mongo.w_mode", "emqx_auth_mongo.server", [ + {default, undef}, + {datatype, {enum, [safe, unsafe, undef]}} +]}. + +{mapping, "auth.mongo.r_mode", "emqx_auth_mongo.server", [ + {default, undef}, + {datatype, {enum, [master, slave_ok, undef]}} +]}. + +{mapping, "auth.mongo.topology.$name", "emqx_auth_mongo.server", [ + {datatype, integer} +]}. + +{translation, "emqx_auth_mongo.server", fun(Conf) -> + H = cuttlefish:conf_get("auth.mongo.server", Conf), + Hosts = string:tokens(H, ","), + Type0 = cuttlefish:conf_get("auth.mongo.type", Conf), + Pool = cuttlefish:conf_get("auth.mongo.pool", Conf), + Login = cuttlefish:conf_get("auth.mongo.login", Conf), + Passwd = cuttlefish:conf_get("auth.mongo.password", Conf), + DB = cuttlefish:conf_get("auth.mongo.database", Conf), + AuthSrc = cuttlefish:conf_get("auth.mongo.auth_source", Conf), + R = cuttlefish:conf_get("auth.mongo.w_mode", Conf), + W = cuttlefish:conf_get("auth.mongo.r_mode", Conf), + Login0 = case Login =:= [] of + true -> []; + false -> [{login, list_to_binary(Login)}] + end, + Passwd0 = case Passwd =:= [] of + true -> []; + false -> [{password, list_to_binary(Passwd)}] + end, + W0 = case W =:= undef of + true -> []; + false -> [{w_mode, W}] + end, + R0 = case R =:= undef of + true -> []; + false -> [{r_mode, R}] + end, + Ssl = case cuttlefish:conf_get("auth.mongo.ssl", Conf) of + true -> + Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end, + SslOpts = fun(Prefix) -> + Filter([{keyfile, cuttlefish:conf_get(Prefix ++ ".keyfile", Conf, undefined)}, + {certfile, cuttlefish:conf_get(Prefix ++ ".certfile", Conf, undefined)}, + {cacertfile, cuttlefish:conf_get(Prefix ++ ".cacertfile", Conf, undefined)}]) + end, + [{ssl, true}, {ssl_opts, SslOpts("auth.mongo.ssl_opts")}]; + false -> + [] + end, + WorkerOptions = [{database, list_to_binary(DB)}, {auth_source, list_to_binary(AuthSrc)}] + ++ Login0 ++ Passwd0 ++ W0 ++ R0 ++ Ssl, + + Vars = cuttlefish_variable:fuzzy_matches(["auth", "mongo", "topology", "$name"], Conf), + Options = lists:map(fun({_, Name}) -> + Name2 = case Name of + "local_threshold_ms" -> "localThresholdMS"; + "connect_timeout_ms" -> "connectTimeoutMS"; + "socket_timeout_ms" -> "socketTimeoutMS"; + "server_selection_timeout_ms" -> "serverSelectionTimeoutMS"; + "wait_queue_timeout_ms" -> "waitQueueTimeoutMS"; + "heartbeat_frequency_ms" -> "heartbeatFrequencyMS"; + "min_heartbeat_frequency_ms" -> "minHeartbeatFrequencyMS"; + _ -> Name + end, + {list_to_atom(Name2), cuttlefish:conf_get("auth.mongo.topology."++Name, Conf)} + end, Vars), + + Type = case Type0 =:= rs of + true -> {Type0, list_to_binary(cuttlefish:conf_get("auth.mongo.rs_set_name", Conf))}; + false -> Type0 + end, + [{type, Type}, + {hosts, Hosts}, + {options, Options}, + {worker_options, WorkerOptions}, + {auto_reconnect, 1}, + {pool_size, Pool}] +end}. + +%% The mongodb operation timeout is specified by the value of `cursor_timeout` from application config, +%% or `infinity` if `cursor_timeout` not specified +{mapping, "auth.mongo.query_timeout", "mongodb.cursor_timeout", [ + {datatype, string} +]}. + +{translation, "mongodb.cursor_timeout", fun(Conf) -> + case cuttlefish:conf_get("auth.mongo.query_timeout", Conf, undefined) of + undefined -> infinity; + Duration -> + case cuttlefish_duration:parse(Duration, ms) of + {error, Reason} -> error(Reason); + Ms when is_integer(Ms) -> Ms + end + end +end}. + +{mapping, "auth.mongo.auth_query.collection", "emqx_auth_mongo.auth_query", [ + {default, "mqtt_user"}, + {datatype, string} +]}. + +{mapping, "auth.mongo.auth_query.password_field", "emqx_auth_mongo.auth_query", [ + {default, "password"}, + {datatype, string} +]}. + +{mapping, "auth.mongo.auth_query.password_hash", "emqx_auth_mongo.auth_query", [ + {datatype, string} +]}. + +{mapping, "auth.mongo.auth_query.selector", "emqx_auth_mongo.auth_query", [ + {default, ""}, + {datatype, string} +]}. + +{translation, "emqx_auth_mongo.auth_query", fun(Conf) -> + case cuttlefish:conf_get("auth.mongo.auth_query.collection", Conf) of + undefined -> cuttlefish:unset(); + Collection -> + PasswordField = cuttlefish:conf_get("auth.mongo.auth_query.password_field", Conf), + PasswordHash = cuttlefish:conf_get("auth.mongo.auth_query.password_hash", Conf), + SelectorStr = cuttlefish:conf_get("auth.mongo.auth_query.selector", Conf), + SelectorList = + lists:map(fun(Selector) -> + case string:tokens(Selector, "=") of + [Field, Val] -> {list_to_binary(Field), list_to_binary(Val)}; + _ -> {<<"username">>, <<"%u">>} + end + end, string:tokens(SelectorStr, ", ")), + + PasswordFields = [list_to_binary(Field) || Field <- string:tokens(PasswordField, ",")], + HashValue = + case string:tokens(PasswordHash, ",") of + [Hash] -> list_to_atom(Hash); + [Prefix, Suffix] -> {list_to_atom(Prefix), list_to_atom(Suffix)}; + [Hash, MacFun, Iterations, Dklen] -> {list_to_atom(Hash), list_to_atom(MacFun), list_to_integer(Iterations), list_to_integer(Dklen)}; + _ -> plain + end, + [{collection, Collection}, + {password_field, PasswordFields}, + {password_hash, HashValue}, + {selector, SelectorList}] + end +end}. + +{mapping, "auth.mongo.super_query", "emqx_auth_mongo.super_query", [ + {default, off}, + {datatype, flag} +]}. + +{mapping, "auth.mongo.super_query.collection", "emqx_auth_mongo.super_query", [ + {default, "mqtt_user"}, + {datatype, string} +]}. + +{mapping, "auth.mongo.super_query.super_field", "emqx_auth_mongo.super_query", [ + {default, "is_superuser"}, + {datatype, string} +]}. + +{mapping, "auth.mongo.super_query.selector", "emqx_auth_mongo.super_query", [ + {default, ""}, + {datatype, string} +]}. + +{translation, "emqx_auth_mongo.super_query", fun(Conf) -> + case cuttlefish:conf_get("auth.mongo.super_query.collection", Conf) of + undefined -> cuttlefish:unset(); + Collection -> + SuperField = cuttlefish:conf_get("auth.mongo.super_query.super_field", Conf), + SelectorStr = cuttlefish:conf_get("auth.mongo.super_query.selector", Conf), + SelectorList = + lists:map(fun(Selector) -> + case string:tokens(Selector, "=") of + [Field, Val] -> {list_to_binary(Field), list_to_binary(Val)}; + _ -> {<<"username">>, <<"%u">>} + end + end, string:tokens(SelectorStr, ", ")), + [{collection, Collection}, {super_field, SuperField}, {selector, SelectorList}] + end +end}. + +{mapping, "auth.mongo.acl_query", "emqx_auth_mongo.acl_query", [ + {default, off}, + {datatype, flag} +]}. + +{mapping, "auth.mongo.acl_query.collection", "emqx_auth_mongo.acl_query", [ + {default, "mqtt_user"}, + {datatype, string} +]}. + +{mapping, "auth.mongo.acl_query.selector", "emqx_auth_mongo.acl_query", [ + {default, ""}, + {datatype, string} +]}. +{mapping, "auth.mongo.acl_query.selector.$id", "emqx_auth_mongo.acl_query", [ + {default, ""}, + {datatype, string} +]}. + +{translation, "emqx_auth_mongo.acl_query", fun(Conf) -> + case cuttlefish:conf_get("auth.mongo.acl_query.collection", Conf) of + undefined -> cuttlefish:unset(); + Collection -> + SelectorStrList = + lists:map( + fun + ({["auth","mongo","acl_query","selector"], ConfEntry}) -> + ConfEntry; + ({["auth","mongo","acl_query","selector", _], ConfEntry}) -> + ConfEntry + end, + cuttlefish_variable:filter_by_prefix("auth.mongo.acl_query.selector", Conf)), + SelectorListList = + lists:map( + fun(SelectorStr) -> + lists:map(fun(Selector) -> + case string:tokens(Selector, "=") of + [Field, Val] -> {list_to_binary(Field), list_to_binary(Val)}; + _ -> {<<"username">>, <<"%u">>} + end + end, string:tokens(SelectorStr, ", ")) + end, + SelectorStrList), + [{collection, Collection}, {selector, SelectorListList}] + end +end}. diff --git a/apps/emqx_auth_mongo/rebar.config b/apps/emqx_auth_mongo/rebar.config new file mode 100644 index 000000000..cebbf68f9 --- /dev/null +++ b/apps/emqx_auth_mongo/rebar.config @@ -0,0 +1,35 @@ +{deps, + [{mongodb, {git,"https://github.com/emqx/mongodb-erlang", {tag, "v3.0.7"}}}, + {ecpool, {git,"https://github.com/emqx/ecpool", {tag, "v0.4.2"}}}, + {emqx_passwd, {git, "https://github.com/emqx/emqx-passwd", {tag, "v1.1.1"}}} + ]}. + +{edoc_opts, [{preprocess, true}]}. +{erl_opts, [warn_unused_vars, + warn_shadow_vars, + warn_unused_import, + warn_obsolete_guard, + debug_info, + compressed, + {parse_transform} + ]}. +{overrides, [{add, [{erl_opts, [compressed]}]}]}. + +{xref_checks, [undefined_function_calls, undefined_functions, + locals_not_used, deprecated_function_calls, + warnings_as_errors, deprecated_functions + ]}. + +{cover_enabled, true}. +{cover_opts, [verbose]}. +{cover_export_enabled, true}. + +{profiles, + [{test, + [{deps, + [{emqx_ct_helper, {git, "https://github.com/emqx/emqx-ct-helper", {tag, "1.2.2"}}}, + {emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.2.0"}}} + ]}, + {erl_opts, [debug_info]} + ]} + ]}. diff --git a/apps/emqx_auth_mongo/src/emqx_acl_mongo.erl b/apps/emqx_auth_mongo/src/emqx_acl_mongo.erl new file mode 100644 index 000000000..c0ff5f8ac --- /dev/null +++ b/apps/emqx_auth_mongo/src/emqx_acl_mongo.erl @@ -0,0 +1,91 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_acl_mongo). + +-include("emqx_auth_mongo.hrl"). +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). + +%% ACL callbacks +-export([ register_metrics/0 + , check_acl/5 + , description/0 + ]). +-spec(register_metrics() -> ok). +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS). + +check_acl(#{username := <<$$, _/binary>>}, _PubSub, _Topic, _AclResult, _State) -> + ok; + +check_acl(ClientInfo, PubSub, Topic, _AclResult, Env = #{aclquery := AclQuery}) -> + #aclquery{collection = Coll, selector = SelectorList} = AclQuery, + Pool = maps:get(pool, Env, ?APP), + SelectorMapList = + lists:map(fun(Selector) -> + maps:from_list(emqx_auth_mongo:replvars(Selector, ClientInfo)) + end, SelectorList), + case emqx_auth_mongo:query_multi(Pool, Coll, SelectorMapList) of + [] -> ok; + Rows -> + try match(ClientInfo, Topic, topics(PubSub, Rows)) of + matched -> emqx_metrics:inc(?ACL_METRICS(allow)), + {stop, allow}; + nomatch -> emqx_metrics:inc(?ACL_METRICS(deny)), + {stop, deny} + catch + _Err:Reason-> + ?LOG(error, "[MongoDB] Check mongo ~p ACL failed, got ACL config: ~p, error: :~p", + [PubSub, Rows, Reason]), + emqx_metrics:inc(?ACL_METRICS(ignore)), + ignore + end + end. + + +match(_ClientInfo, _Topic, []) -> + nomatch; +match(ClientInfo, Topic, [TopicFilter|More]) -> + case emqx_topic:match(Topic, feedvar(ClientInfo, TopicFilter)) of + true -> matched; + false -> match(ClientInfo, Topic, More) + end. + +topics(publish, Rows) -> + lists:foldl(fun(Row, Acc) -> + Topics = maps:get(<<"publish">>, Row, []) ++ maps:get(<<"pubsub">>, Row, []), + lists:umerge(Acc, Topics) + end, [], Rows); + +topics(subscribe, Rows) -> + lists:foldl(fun(Row, Acc) -> + Topics = maps:get(<<"subscribe">>, Row, []) ++ maps:get(<<"pubsub">>, Row, []), + lists:umerge(Acc, Topics) + end, [], Rows). + +feedvar(#{clientid := ClientId, username := Username}, Str) -> + lists:foldl(fun({Var, Val}, Acc) -> + feedvar(Acc, Var, Val) + end, Str, [{"%u", Username}, {"%c", ClientId}]). + +feedvar(Str, _Var, undefined) -> + Str; +feedvar(Str, Var, Val) -> + re:replace(Str, Var, Val, [global, {return, binary}]). + +description() -> "ACL with MongoDB". + diff --git a/apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src b/apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src new file mode 100644 index 000000000..71bb7fa92 --- /dev/null +++ b/apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src @@ -0,0 +1,14 @@ +{application, emqx_auth_mongo, + [{description, "EMQ X Authentication/ACL with MongoDB"}, + {vsn, "git"}, + {modules, []}, + {registered, [emqx_auth_mongo_sup]}, + {applications, [kernel,stdlib,mongodb,ecpool,emqx_passwd]}, + {mod, {emqx_auth_mongo_app,[]}}, + {env, []}, + {licenses, ["Apache-2.0"]}, + {maintainers, ["EMQ X Team "]}, + {links, [{"Homepage", "https://emqx.io/"}, + {"Github", "https://github.com/emqx/emqx-auth-mongo"} + ]} + ]}. diff --git a/apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src.script b/apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src.script new file mode 100644 index 000000000..0e14ff23f --- /dev/null +++ b/apps/emqx_auth_mongo/src/emqx_auth_mongo.app.src.script @@ -0,0 +1,24 @@ +%%-*- mode: erlang -*- +%% .app.src.script + +RemoveLeadingV = + fun(Tag) -> + case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of + nomatch -> + re:replace(Tag, "/", "-", [{return ,list}]); + _ -> + %% if it is a version number prefixed by 'v' or 'e', then remove it + re:replace(Tag, "[v|e]", "", [{return ,list}]) + end + end, + +case os:getenv("EMQX_DEPS_DEFAULT_VSN") of + false -> CONFIG; % env var not defined + [] -> CONFIG; % env var set to empty string + Tag -> + [begin + AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}), + {application, App, AppConf0} + end || Conf = {application, App, AppConf} <- CONFIG] +end. + diff --git a/apps/emqx_auth_mongo/src/emqx_auth_mongo.appup.src b/apps/emqx_auth_mongo/src/emqx_auth_mongo.appup.src new file mode 100644 index 000000000..d4e259d5e --- /dev/null +++ b/apps/emqx_auth_mongo/src/emqx_auth_mongo.appup.src @@ -0,0 +1,37 @@ +%%-*-: erlang -*- +{"4.2.3", + [ + {"4.2.2", [ + {load_module, emqx_auth_mongo_app, brutal_purge, soft_purge, []}, + {load_module, emqx_auth_mongo, brutal_purge, soft_purge, []}, + {load_module, emqx_acl_mongo, brutal_purge, soft_purge, [emqx_auth_mongo]} + ]}, + {"4.2.1", [ + {load_module, emqx_auth_mongo_app, brutal_purge, soft_purge, []}, + {load_module, emqx_auth_mongo, brutal_purge, soft_purge, []}, + {load_module, emqx_acl_mongo, brutal_purge, soft_purge, [emqx_auth_mongo]} + ]}, + {"4.2.0", [ + {load_module, emqx_auth_mongo_app, brutal_purge, soft_purge, []}, + {load_module, emqx_auth_mongo, brutal_purge, soft_purge, []}, + {load_module, emqx_acl_mongo, brutal_purge, soft_purge, [emqx_auth_mongo]} + ]} + ], + [ + {"4.2.2", [ + {load_module, emqx_auth_mongo_app, brutal_purge, soft_purge, []}, + {load_module, emqx_auth_mongo, brutal_purge, soft_purge, []}, + {load_module, emqx_acl_mongo, brutal_purge, soft_purge, [emqx_auth_mongo]} + ]}, + {"4.2.1", [ + {load_module, emqx_auth_mongo_app, brutal_purge, soft_purge, []}, + {load_module, emqx_auth_mongo, brutal_purge, soft_purge, []}, + {load_module, emqx_acl_mongo, brutal_purge, soft_purge, [emqx_auth_mongo]} + ]}, + {"4.2.0", [ + {load_module, emqx_auth_mongo_app, brutal_purge, soft_purge, []}, + {load_module, emqx_auth_mongo, brutal_purge, soft_purge, []}, + {load_module, emqx_acl_mongo, brutal_purge, soft_purge, [emqx_auth_mongo]} + ]} + ] +}. diff --git a/apps/emqx_auth_mongo/src/emqx_auth_mongo.erl b/apps/emqx_auth_mongo/src/emqx_auth_mongo.erl new file mode 100644 index 000000000..4880e6fb9 --- /dev/null +++ b/apps/emqx_auth_mongo/src/emqx_auth_mongo.erl @@ -0,0 +1,134 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_mongo). + +-behaviour(ecpool_worker). + +-include("emqx_auth_mongo.hrl"). +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). +-include_lib("emqx/include/types.hrl"). + +-export([ register_metrics/0 + , check/3 + , description/0 + ]). + +-export([ replvar/2 + , replvars/2 + , connect/1 + , query/3 + , query_multi/3 + ]). + +-spec(register_metrics() -> ok). +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). + +check(ClientInfo = #{password := Password}, AuthResult, + Env = #{authquery := AuthQuery, superquery := SuperQuery}) -> + #authquery{collection = Collection, field = Fields, + hash = HashType, selector = Selector} = AuthQuery, + Pool = maps:get(pool, Env, ?APP), + case query(Pool, Collection, maps:from_list(replvars(Selector, ClientInfo))) of + undefined -> emqx_metrics:inc(?AUTH_METRICS(ignore)); + {error, Reason} -> + ?LOG(error, "[MongoDB] Can't connect to MongoDB server: ~0p", [Reason]), + ok = emqx_metrics:inc(?AUTH_METRICS(failure)), + {stop, AuthResult#{auth_result => not_authorized, anonymous => false}}; + UserMap -> + Result = case [maps:get(Field, UserMap, undefined) || Field <- Fields] of + [undefined] -> {error, password_error}; + [PassHash] -> + check_pass({PassHash, Password}, HashType); + [PassHash, Salt|_] -> + check_pass({PassHash, Salt, Password}, HashType) + end, + case Result of + ok -> + ok = emqx_metrics:inc(?AUTH_METRICS(success)), + {stop, AuthResult#{is_superuser => is_superuser(Pool, SuperQuery, ClientInfo), + anonymous => false, + auth_result => success}}; + {error, Error} -> + ?LOG(error, "[MongoDB] check auth fail: ~p", [Error]), + ok = emqx_metrics:inc(?AUTH_METRICS(failure)), + {stop, AuthResult#{auth_result => Error, anonymous => false}} + end + end. + +check_pass(Password, HashType) -> + case emqx_passwd:check_pass(Password, HashType) of + ok -> ok; + {error, _Reason} -> {error, not_authorized} + end. + +description() -> "Authentication with MongoDB". + +%%-------------------------------------------------------------------- +%% Is Superuser? +%%-------------------------------------------------------------------- + +-spec(is_superuser(string(), maybe(#superquery{}), emqx_types:clientinfo()) -> boolean()). +is_superuser(_Pool, undefined, _ClientInfo) -> + false; +is_superuser(Pool, #superquery{collection = Coll, field = Field, selector = Selector}, ClientInfo) -> + Row = query(Pool, Coll, maps:from_list(replvars(Selector, ClientInfo))), + case maps:get(Field, Row, false) of + true -> true; + _False -> false + end. + +replvars(VarList, ClientInfo) -> + lists:map(fun(Var) -> replvar(Var, ClientInfo) end, VarList). + +replvar({Field, <<"%u">>}, #{username := Username}) -> + {Field, Username}; +replvar({Field, <<"%c">>}, #{clientid := ClientId}) -> + {Field, ClientId}; +replvar({Field, <<"%C">>}, #{cn := CN}) -> + {Field, CN}; +replvar({Field, <<"%d">>}, #{dn := DN}) -> + {Field, DN}; +replvar(Selector, _ClientInfo) -> + Selector. + +%%-------------------------------------------------------------------- +%% MongoDB Connect/Query +%%-------------------------------------------------------------------- + +connect(Opts) -> + Type = proplists:get_value(type, Opts, single), + Hosts = proplists:get_value(hosts, Opts, []), + Options = proplists:get_value(options, Opts, []), + WorkerOptions = proplists:get_value(worker_options, Opts, []), + mongo_api:connect(Type, Hosts, Options, WorkerOptions). + +query(Pool, Collection, Selector) -> + ecpool:with_client(Pool, fun(Conn) -> mongo_api:find_one(Conn, Collection, Selector, #{}) end). + +query_multi(Pool, Collection, SelectorList) -> + lists:reverse(lists:flatten(lists:foldl(fun(Selector, Acc1) -> + Batch = ecpool:with_client(Pool, fun(Conn) -> + case mongo_api:find(Conn, Collection, Selector, #{}) of + [] -> []; + {ok, Cursor} -> + mc_cursor:foldl(fun(O, Acc2) -> [O|Acc2] end, [], Cursor, 1000) + end + end), + [Batch|Acc1] + end, [], SelectorList))). diff --git a/apps/emqx_auth_mongo/src/emqx_auth_mongo_app.erl b/apps/emqx_auth_mongo/src/emqx_auth_mongo_app.erl new file mode 100644 index 000000000..e13fe30b7 --- /dev/null +++ b/apps/emqx_auth_mongo/src/emqx_auth_mongo_app.erl @@ -0,0 +1,87 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_mongo_app). + +-behaviour(application). + +-emqx_plugin(auth). + +-include("emqx_auth_mongo.hrl"). + +-import(proplists, [get_value/3]). + +%% Application callbacks +-export([ start/2 + , prep_stop/1 + , stop/1 + ]). + +%%-------------------------------------------------------------------- +%% Application callbacks +%%-------------------------------------------------------------------- + +start(_StartType, _StartArgs) -> + {ok, Sup} = emqx_auth_mongo_sup:start_link(), + with_env(auth_query, fun reg_authmod/1), + with_env(acl_query, fun reg_aclmod/1), + {ok, Sup}. + +prep_stop(State) -> + ok = emqx:unhook('client.authenticate', fun emqx_auth_mongo:check/3), + ok = emqx:unhook('client.check_acl', fun emqx_acl_mongo:check_acl/5), + State. + +stop(_State) -> + ok. + +reg_authmod(AuthQuery) -> + emqx_auth_mongo:register_metrics(), + SuperQuery = r(super_query, application:get_env(?APP, super_query, undefined)), + ok = emqx:hook('client.authenticate', fun emqx_auth_mongo:check/3, + [#{authquery => AuthQuery, superquery => SuperQuery, pool => ?APP}]). + +reg_aclmod(AclQuery) -> + emqx_acl_mongo:register_metrics(), + ok = emqx:hook('client.check_acl', fun emqx_acl_mongo:check_acl/5, [#{aclquery => AclQuery, pool => ?APP}]). + +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- + +with_env(Name, Fun) -> + case application:get_env(?APP, Name) of + undefined -> ok; + {ok, Config} -> Fun(r(Name, Config)) + end. + +r(super_query, undefined) -> + undefined; +r(super_query, Config) -> + #superquery{collection = list_to_binary(get_value(collection, Config, "mqtt_user")), + field = list_to_binary(get_value(super_field, Config, "is_superuser")), + selector = get_value(selector, Config, ?DEFAULT_SELECTORS)}; + +r(auth_query, Config) -> + #authquery{collection = list_to_binary(get_value(collection, Config, "mqtt_user")), + field = get_value(password_field, Config, [<<"password">>]), + hash = get_value(password_hash, Config, sha256), + selector = get_value(selector, Config, ?DEFAULT_SELECTORS)}; + +r(acl_query, Config) -> + #aclquery{collection = list_to_binary(get_value(collection, Config, "mqtt_acl")), + selector = get_value(selector, Config, [?DEFAULT_SELECTORS])}. + diff --git a/apps/emqx_auth_mongo/src/emqx_auth_mongo_sup.erl b/apps/emqx_auth_mongo/src/emqx_auth_mongo_sup.erl new file mode 100644 index 000000000..dc2c37fd4 --- /dev/null +++ b/apps/emqx_auth_mongo/src/emqx_auth_mongo_sup.erl @@ -0,0 +1,34 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_mongo_sup). + +-behaviour(supervisor). + +-include("emqx_auth_mongo.hrl"). + +-export([start_link/0]). + +-export([init/1]). + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init([]) -> + {ok, PoolEnv} = application:get_env(?APP, server), + PoolSpec = ecpool:pool_spec(?APP, ?APP, ?APP, PoolEnv), + {ok, {{one_for_all, 10, 100}, [PoolSpec]}}. + diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE.erl b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE.erl new file mode 100644 index 000000000..4e76026ec --- /dev/null +++ b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE.erl @@ -0,0 +1,174 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_mongo_SUITE). + +-compile(export_all). +-compile(nowarn_export_all). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +-define(APP, emqx_auth_mongo). + +-define(POOL(App), ecpool_worker:client(gproc_pool:pick_worker({ecpool, App}))). + +-define(MONGO_CL_ACL, <<"mqtt_acl">>). +-define(MONGO_CL_USER, <<"mqtt_user">>). + +-define(INIT_ACL, [{<<"username">>, <<"testuser">>, <<"clientid">>, <<"null">>, <<"subscribe">>, [<<"#">>]}, + {<<"username">>, <<"dashboard">>, <<"clientid">>, <<"null">>, <<"pubsub">>, [<<"$SYS/#">>]}, + {<<"username">>, <<"user3">>, <<"clientid">>, <<"null">>, <<"publish">>, [<<"a/b/c">>]}]). + +-define(INIT_AUTH, [{<<"username">>, <<"plain">>, <<"password">>, <<"plain">>, <<"salt">>, <<"salt">>, <<"is_superuser">>, true}, + {<<"username">>, <<"md5">>, <<"password">>, <<"1bc29b36f623ba82aaf6724fd3b16718">>, <<"salt">>, <<"salt">>, <<"is_superuser">>, false}, + {<<"username">>, <<"sha">>, <<"password">>, <<"d8f4590320e1343a915b6394170650a8f35d6926">>, <<"salt">>, <<"salt">>, <<"is_superuser">>, false}, + {<<"username">>, <<"sha256">>, <<"password">>, <<"5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e">>, <<"salt">>, <<"salt">>, <<"is_superuser">>, false}, + {<<"username">>, <<"pbkdf2_password">>, <<"password">>, <<"cdedb5281bb2f801565a1122b2563515">>, <<"salt">>, <<"ATHENA.MIT.EDUraeburn">>, <<"is_superuser">>, false}, + {<<"username">>, <<"bcrypt_foo">>, <<"password">>, <<"$2a$12$sSS8Eg.ovVzaHzi1nUHYK.HbUIOdlQI0iS22Q5rd5z.JVVYH6sfm6">>, <<"salt">>, <<"$2a$12$sSS8Eg.ovVzaHzi1nUHYK.">>, <<"is_superuser">>, false} + ]). + +%%-------------------------------------------------------------------- +%% Setups +%%-------------------------------------------------------------------- + +all() -> + emqx_ct:all(?MODULE). + +init_per_suite(Cfg) -> + emqx_ct_helpers:start_apps([emqx_auth_mongo], fun set_special_confs/1), + emqx_modules:load_module(emqx_mod_acl_internal, false), + init_mongo_data(), + Cfg. + +end_per_suite(_Cfg) -> + deinit_mongo_data(), + emqx_ct_helpers:stop_apps([emqx_auth_mongo]). + +set_special_confs(emqx) -> + application:set_env(emqx, acl_nomatch, deny), + application:set_env(emqx, acl_file, + emqx_ct_helpers:deps_path(emqx, "test/emqx_SUITE_data/acl.conf")), + application:set_env(emqx, allow_anonymous, false), + application:set_env(emqx, enable_acl_cache, false), + application:set_env(emqx, plugins_loaded_file, + emqx_ct_helpers:deps_path(emqx, "test/emqx_SUITE_data/loaded_plugins")); +set_special_confs(_App) -> + ok. + +init_mongo_data() -> + %% Users + {ok, Connection} = ?POOL(?APP), + mongo_api:delete(Connection, ?MONGO_CL_USER, {}), + ?assertMatch({{true, _}, _}, mongo_api:insert(Connection, ?MONGO_CL_USER, ?INIT_AUTH)), + %% ACLs + mongo_api:delete(Connection, ?MONGO_CL_ACL, {}), + ?assertMatch({{true, _}, _}, mongo_api:insert(Connection, ?MONGO_CL_ACL, ?INIT_ACL)). + +deinit_mongo_data() -> + {ok, Connection} = ?POOL(?APP), + mongo_api:delete(Connection, ?MONGO_CL_USER, {}), + mongo_api:delete(Connection, ?MONGO_CL_ACL, {}). + +%%-------------------------------------------------------------------- +%% Test cases +%%-------------------------------------------------------------------- + +t_check_auth(_) -> + Plain = #{zone => external, clientid => <<"client1">>, username => <<"plain">>}, + Plain1 = #{zone => external, clientid => <<"client1">>, username => <<"plain2">>}, + Md5 = #{zone => external, clientid => <<"md5">>, username => <<"md5">>}, + Sha = #{zone => external, clientid => <<"sha">>, username => <<"sha">>}, + Sha256 = #{zone => external, clientid => <<"sha256">>, username => <<"sha256">>}, + Pbkdf2 = #{zone => external, clientid => <<"pbkdf2_password">>, username => <<"pbkdf2_password">>}, + Bcrypt = #{zone => external, clientid => <<"bcrypt_foo">>, username => <<"bcrypt_foo">>}, + User1 = #{zone => external, clientid => <<"bcrypt_foo">>, username => <<"user">>}, + reload({auth_query, [{password_hash, plain}]}), + %% With exactly username/password, connection success + {ok, #{is_superuser := true}} = emqx_access_control:authenticate(Plain#{password => <<"plain">>}), + %% With exactly username and wrong password, connection fail + {error, _} = emqx_access_control:authenticate(Plain#{password => <<"error_pwd">>}), + %% With wrong username and wrong password, emqx_auth_mongo auth fail, then allow anonymous authentication + {error, _} = emqx_access_control:authenticate(Plain1#{password => <<"error_pwd">>}), + %% With wrong username and exactly password, emqx_auth_mongo auth fail, then allow anonymous authentication + {error, _} = emqx_access_control:authenticate(Plain1#{password => <<"plain">>}), + reload({auth_query, [{password_hash, md5}]}), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Md5#{password => <<"md5">>}), + reload({auth_query, [{password_hash, sha}]}), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Sha#{password => <<"sha">>}), + reload({auth_query, [{password_hash, sha256}]}), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Sha256#{password => <<"sha256">>}), + %%pbkdf2 sha + reload({auth_query, [{password_hash, {pbkdf2, sha, 1, 16}}, {password_field, [<<"password">>, <<"salt">>]}]}), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Pbkdf2#{password => <<"password">>}), + reload({auth_query, [{password_hash, {salt, bcrypt}}]}), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Bcrypt#{password => <<"foo">>}), + {error, _} = emqx_access_control:authenticate(User1#{password => <<"foo">>}). + +t_check_acl(_) -> + {ok, Connection} = ?POOL(?APP), + User1 = #{zone => external, clientid => <<"client1">>, username => <<"testuser">>}, + User2 = #{zone => external, clientid => <<"client2">>, username => <<"dashboard">>}, + User3 = #{zone => external, clientid => <<"client2">>, username => <<"user3">>}, + User4 = #{zone => external, clientid => <<"$$client2">>, username => <<"$$user3">>}, + 3 = mongo_api:count(Connection, ?MONGO_CL_ACL, {}, 17), + %% ct log output + allow = emqx_access_control:check_acl(User1, subscribe, <<"users/testuser/1">>), + deny = emqx_access_control:check_acl(User1, subscribe, <<"$SYS/testuser/1">>), + deny = emqx_access_control:check_acl(User2, subscribe, <<"a/b/c">>), + allow = emqx_access_control:check_acl(User2, subscribe, <<"$SYS/testuser/1">>), + allow = emqx_access_control:check_acl(User3, publish, <<"a/b/c">>), + deny = emqx_access_control:check_acl(User3, publish, <<"c">>), + allow = emqx_access_control:check_acl(User4, publish, <<"a/b/c">>). + +t_acl_super(_) -> + reload({auth_query, [{password_hash, plain}, {password_field, [<<"password">>]}]}), + {ok, C} = emqtt:start_link([{clientid, <<"simpleClient">>}, + {username, <<"plain">>}, + {password, <<"plain">>}]), + {ok, _} = emqtt:connect(C), + timer:sleep(10), + emqtt:subscribe(C, <<"TopicA">>, qos2), + timer:sleep(1000), + emqtt:publish(C, <<"TopicA">>, <<"Payload">>, qos2), + timer:sleep(1000), + receive + {publish, #{payload := Payload}} -> + ?assertEqual(<<"Payload">>, Payload) + after + 1000 -> + ct:fail({receive_timeout, <<"Payload">>}), + ok + end, + emqtt:disconnect(C). + +%%-------------------------------------------------------------------- +%% Utils +%%-------------------------------------------------------------------- + +reload({Par, Vals}) when is_list(Vals) -> + application:stop(?APP), + {ok, TupleVals} = application:get_env(?APP, Par), + NewVals = + lists:filtermap(fun({K, V}) -> + case lists:keymember(K, 1, Vals) of + false ->{true, {K, V}}; + _ -> false + end + end, TupleVals), + application:set_env(?APP, Par, lists:append(NewVals, Vals)), + application:start(?APP). diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca-key.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca-key.pem new file mode 100644 index 000000000..e9717011e --- /dev/null +++ b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA0kGUBi9NDp65jgdxKfizIfuSr2wpwb44yM9SuP4oUQSULOA2 +4iFpLR/c5FAYHU81y9Vx91dQjdZfffaBZuv2zVvteXUkol8Nez7boKbo2E41MTew +8edtNKZAQVvnaHAC2NCZxjchCzUCDEoUUcl+cIERZ8R48FBqK5iTVcMRIx1akwus ++dhBqP0ykA5TGOWZkJrLM9aUXSPQha9+wXlOpkvu0Ur2nkX8PPJnifWao9UShSar +ll1IqPZNCSlZMwcFYcQNBCpdvITUUYlHvMRQV64bUpOxUGDuJkQL3dLKBlNuBRlJ +BcjBAKw7rFnwwHZcMmQ9tan/dZzpzwjo/T0XjwIDAQABAoIBAQCSHvUqnzDkWjcG +l/Fzg92qXlYBCCC0/ugj1sHcwvVt6Mq5rVE3MpUPwTcYjPlVVTlD4aEEjm/zQuq2 +ddxUlOS+r4aIhHrjRT/vSS4FpjnoKeIZxGR6maVxk6DQS3i1QjMYT1CvSpzyVvKH +a+xXMrtmoKxh+085ZAmFJtIuJhUA2yEa4zggCxWnvz8ecLClUPfVDPhdLBHc3KmL +CRpHEC6L/wanvDPRdkkzfKyaJuIJlTDaCg63AY5sDkTW2I57iI/nJ3haSeidfQKz +39EfbnM1A/YprIakafjAu3frBIsjBVcxwGihZmL/YriTHjOggJF841kT5zFkkv2L +/530Wk6xAoGBAOqZLZ4DIi/zLndEOz1mRbUfjc7GQUdYplBnBwJ22VdS0P4TOXnd +UbJth2MA92NM7ocTYVFl4TVIZY/Y+Prxk7KQdHWzR7JPpKfx9OEVgtSqV0vF9eGI +rKp79Y1T4Mvc3UcQCXX6TP7nHLihEzpS8odm2LW4txrOiLsn4Fq/IWrLAoGBAOVv +6U4tm3lImotUupKLZPKEBYwruo9qRysoug9FiorP4TjaBVOfltiiHbAQD6aGfVtN +SZpZZtrs17wL7Xl4db5asgMcZd+8Hkfo5siR7AuGW9FZloOjDcXb5wCh9EvjJ74J +Cjw7RqyVymq9t7IP6wnVwj5Ck48YhlOZCz/mzlnNAoGAWq7NYFgLvgc9feLFF23S +IjpJQZWHJEITP98jaYNxbfzYRm49+GphqxwFinKULjFNvq7yHlnIXSVYBOu1CqOZ +GRwXuGuNmlKI7lZr9xmukfAqgGLMMdr4C4qRF4lFyufcLRz42z7exmWlx4ST/yaT +E13hBRWayeTuG5JFei6Jh1MCgYEAqmX4LyC+JFBgvvQZcLboLRkSCa18bADxhENG +FAuAvmFvksqRRC71WETmqZj0Fqgxt7pp3KFjO1rFSprNLvbg85PmO1s+6fCLyLpX +lESTu2d5D71qhK93jigooxalGitFm+SY3mzjq0/AOpBWOn+J/w7rqVPGxXLgaHv0 +l+vx+00CgYBOvo9/ImjwYii2jFl+sHEoCzlvpITi2temRlT2j6ulSjCLJgjwEFw9 +8e+vvfQumQOsutakUVyURrkMGNDiNlIv8kv5YLCCkrwN22E6Ghyi69MJUvHQXkc/ +QZhjn/luyfpB5f/BeHFS2bkkxAXo+cfG45ApY3Qfz6/7o+H+vDa6/A== +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca.pem new file mode 100644 index 000000000..00b31d8a4 --- /dev/null +++ b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDAzCCAeugAwIBAgIBATANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR +TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X +DTIwMDYxMTAzMzg0NloXDTMwMDYwOTAzMzg0NlowPDE6MDgGA1UEAwwxTXlTUUxf +U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9DQV9DZXJ0aWZpY2F0ZTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANJBlAYvTQ6euY4HcSn4syH7kq9s +KcG+OMjPUrj+KFEElCzgNuIhaS0f3ORQGB1PNcvVcfdXUI3WX332gWbr9s1b7Xl1 +JKJfDXs+26Cm6NhONTE3sPHnbTSmQEFb52hwAtjQmcY3IQs1AgxKFFHJfnCBEWfE +ePBQaiuYk1XDESMdWpMLrPnYQaj9MpAOUxjlmZCayzPWlF0j0IWvfsF5TqZL7tFK +9p5F/DzyZ4n1mqPVEoUmq5ZdSKj2TQkpWTMHBWHEDQQqXbyE1FGJR7zEUFeuG1KT +sVBg7iZEC93SygZTbgUZSQXIwQCsO6xZ8MB2XDJkPbWp/3Wc6c8I6P09F48CAwEA +AaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEADKz6bIpP5anp +GgLB0jkclRWuMlS4qqIt4itSsMXPJ/ezpHwECixmgW2TIQl6S1woRkUeMxhT2/Ay +Sn/7aKxuzRagyE5NEGOvrOuAP5RO2ZdNJ/X3/Rh533fK1sOTEEbSsWUvW6iSkZef +rsfZBVP32xBhRWkKRdLeLB4W99ADMa0IrTmZPCXHSSE2V4e1o6zWLXcOZeH1Qh8N +SkelBweR+8r1Fbvy1r3s7eH7DCbYoGEDVLQGOLvzHKBisQHmoDnnF5E9g1eeNRdg +o+vhOKfYCOzeNREJIqS42PHcGhdNRk90ycigPmfUJclz1mDHoMjKR2S5oosTpr65 +tNPx3CL7GA== +-----END CERTIFICATE----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-cert.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-cert.pem new file mode 100644 index 000000000..aad1404ca --- /dev/null +++ b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBDCCAeygAwIBAgIBAzANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR +TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X +DTIwMDYxMTAzMzg0N1oXDTMwMDYwOTAzMzg0N1owQDE+MDwGA1UEAww1TXlTUUxf +U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9DbGllbnRfQ2VydGlmaWNhdGUw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVYSWpOvCTupz82fc85Opv +EQ7rkB8X2oOMyBCpkyHKBIr1ZQgRDWBp9UVOASq3GnSElm6+T3Kb1QbOffa8GIlw +sjAueKdq5L2eSkmPIEQ7eoO5kEW+4V866hE1LeL/PmHg2lGP0iqZiJYtElhHNQO8 +3y9I7cm3xWMAA3SSWikVtpJRn3qIp2QSrH+tK+/HHbE5QwtPxdir4ULSCSOaM5Yh +Wi5Oto88TZqe1v7SXC864JVvO4LuS7TuSreCdWZyPXTJFBFeCEWSAxonKZrqHbBe +CwKML6/0NuzjaQ51c2tzmVI6xpHj3nnu4cSRx6Jf9WBm+35vm0wk4pohX3ptdzeV +AgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAByQ5zSNeFUH +Aw7JlpZHtHaSEeiiyBHke20ziQ07BK1yi/ms2HAWwQkpZv149sjNuIRH8pkTmkZn +g8PDzSefjLbC9AsWpWV0XNV22T/cdobqLqMBDDZ2+5bsV+jTrOigWd9/AHVZ93PP +IJN8HJn6rtvo2l1bh/CdsX14uVSdofXnuWGabNTydqtMvmCerZsdf6qKqLL+PYwm +RDpgWiRUY7KPBSSlKm/9lJzA+bOe4dHeJzxWFVCJcbpoiTFs1je1V8kKQaHtuW39 +ifX6LTKUMlwEECCbDKM8Yq2tm8NjkjCcnFDtKg8zKGPUu+jrFMN5otiC3wnKcP7r +O9EkaPcgYH8= +-----END CERTIFICATE----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-key.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-key.pem new file mode 100644 index 000000000..6789d0291 --- /dev/null +++ b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA1WElqTrwk7qc/Nn3POTqbxEO65AfF9qDjMgQqZMhygSK9WUI +EQ1gafVFTgEqtxp0hJZuvk9ym9UGzn32vBiJcLIwLninauS9nkpJjyBEO3qDuZBF +vuFfOuoRNS3i/z5h4NpRj9IqmYiWLRJYRzUDvN8vSO3Jt8VjAAN0klopFbaSUZ96 +iKdkEqx/rSvvxx2xOUMLT8XYq+FC0gkjmjOWIVouTraPPE2antb+0lwvOuCVbzuC +7ku07kq3gnVmcj10yRQRXghFkgMaJyma6h2wXgsCjC+v9Dbs42kOdXNrc5lSOsaR +49557uHEkceiX/VgZvt+b5tMJOKaIV96bXc3lQIDAQABAoIBAF7yjXmSOn7h6P0y +WCuGiTLG2mbDiLJqj2LTm2Z5i+2Cu/qZ7E76Ls63TxF4v3MemH5vGfQhEhR5ZD/6 +GRJ1sKKvB3WGRqjwA9gtojHH39S/nWGy6vYW/vMOOH37XyjIr3EIdIaUtFQBTSHd +Kd71niYrAbVn6fyWHolhADwnVmTMOl5OOAhCdEF4GN3b5aIhIu8BJ7EUzTtHBJIj +CAEfjZFjDs1y1cIgGFJkuIQxMfCpq5recU2qwip7YO6fk//WEjOPu7kSf5IEswL8 +jg1dea9rGBV6KaD2xsgsC6Ll6Sb4BbsrHMfflG3K2Lk3RdVqqTFp1Fn1PTLQE/1S +S/SZPYECgYEA9qYcHKHd0+Q5Ty5wgpxKGa4UCWkpwvfvyv4bh8qlmxueB+l2AIdo +ZvkM8gTPagPQ3WypAyC2b9iQu70uOJo1NizTtKnpjDdN1YpDjISJuS/P0x73gZwy +gmoM5AzMtN4D6IbxXtXnPaYICvwLKU80ouEN5ZPM4/ODLUu6gsp0v2UCgYEA3Xgi +zMC4JF0vEKEaK0H6QstaoXUmw/lToZGH3TEojBIkb/2LrHUclygtONh9kJSFb89/ +jbmRRLAOrx3HZKCNGUmF4H9k5OQyAIv6OGBinvLGqcbqnyNlI+Le8zxySYwKMlEj +EMrBCLmSyi0CGFrbZ3mlj/oCET/ql9rNvcK+DHECgYAEx5dH3sMjtgp+RFId1dWB +xePRgt4yTwewkVgLO5wV82UOljGZNQaK6Eyd7AXw8f38LHzh+KJQbIvxd2sL4cEi +OaAoohpKg0/Y0YMZl//rPMf0OWdmdZZs/I0fZjgZUSwWN3c59T8z7KG/RL8an9RP +S7kvN7wCttdV61/D5RR6GQKBgDxCe/WKWpBKaovzydMLWLTj7/0Oi0W3iXHkzzr4 +LTgvl4qBSofaNbVLUUKuZTv5rXUG2IYPf99YqCYtzBstNDc1MiAriaBeFtzfOW4t +i6gEFtoLLbuvPc3N5Sv5vn8Ug5G9UfU3td5R4AbyyCcoUZqOFuZd+EIJSiOXfXOs +kVmBAoGBAIU9aPAqhU5LX902oq8KsrpdySONqv5mtoStvl3wo95WIqXNEsFY60wO +q02jKQmJJ2MqhkJm2EoF2Mq8+40EZ5sz8LdgeQ/M0yQ9lAhPi4rftwhpe55Ma9dk +SE9X1c/DMCBEaIjJqVXdy0/EeArwpb8sHkguVVAZUWxzD+phm1gs +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/mongodb.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/mongodb.pem new file mode 100644 index 000000000..1fe94891a --- /dev/null +++ b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/mongodb.pem @@ -0,0 +1,46 @@ +-----BEGIN CERTIFICATE----- +MIIDBDCCAeygAwIBAgIBAjANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR +TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X +DTIwMDYxMTAzMzg0NloXDTMwMDYwOTAzMzg0NlowQDE+MDwGA1UEAww1TXlTUUxf +U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9TZXJ2ZXJfQ2VydGlmaWNhdGUw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcEnEm5hqP1EbEJycOz8Ua +NWp29QdpFUzTWhkKGhVXk+0msmNTw4NBAFB42moY44OU8wvDideOlJNhPRWveD8z +G2lxzJA91p0UK4et8ia9MmeuCGhdC9jxJ8X69WNlUiPyy0hI/ZsqRq9Z0C2eW0iL +JPXsy4X8Xpw3SFwoXf5pR9RFY5Pb2tuyxqmSestu2VXT/NQjJg4CVDR3mFcHPXZB +4elRzH0WshExEGkgy0bg20MJeRc2Qdb5Xx+EakbmwroDWaCn3NSGqQ7jv6Vw0doy +TGvS6h6RHBxnyqRfRgKGlCoOMG9/5+rFJC00QpCUG2vHXHWGoWlMlJ3foN7rj5v9 +AgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAJ5zt2rj4Ag6 +zpN59AWC1Fur8g8l41ksHkSpKPp+PtyO/ngvbMqBpfmK1e7JCKZv/68QXfMyWWAI +hwalqZkXXWHKjuz3wE7dE25PXFXtGJtcZAaj10xt98fzdqt8lQSwh2kbfNwZIz1F +sgAStgE7+ZTcqTgvNB76Os1UK0to+/P0VBWktaVFdyub4Nc2SdPVnZNvrRBXBwOD +3V8ViwywDOFoE7DvCvwx/SVsvoC0Z4j3AMMovO6oHicP7uU83qsQgm1Qru3YeoLR ++DoVi7IPHbWvN7MqFYn3YjNlByO2geblY7MR0BlqbFlmFrqLsUfjsh2ys7/U/knC +dN/klu446fI= +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAnBJxJuYaj9RGxCcnDs/FGjVqdvUHaRVM01oZChoVV5PtJrJj +U8ODQQBQeNpqGOODlPMLw4nXjpSTYT0Vr3g/MxtpccyQPdadFCuHrfImvTJnrgho +XQvY8SfF+vVjZVIj8stISP2bKkavWdAtnltIiyT17MuF/F6cN0hcKF3+aUfURWOT +29rbssapknrLbtlV0/zUIyYOAlQ0d5hXBz12QeHpUcx9FrIRMRBpIMtG4NtDCXkX +NkHW+V8fhGpG5sK6A1mgp9zUhqkO47+lcNHaMkxr0uoekRwcZ8qkX0YChpQqDjBv +f+fqxSQtNEKQlBtrx1x1hqFpTJSd36De64+b/QIDAQABAoIBAFiah66Dt9SruLkn +WR8piUaFyLlcBib8Nq9OWSTJBhDAJERxxb4KIvvGB+l0ZgNXNp5bFPSfzsZdRwZP +PX5uj8Kd71Dxx3mz211WESMJdEC42u+MSmN4lGLkJ5t/sDwXU91E1vbJM0ve8THV +4/Ag9qA4DX2vVZOeyqT/6YHpSsPNZplqzrbAiwrfHwkctHfgqwOf3QLfhmVQgfCS +VwidBldEUv2whSIiIxh4Rv5St4kA68IBCbJxdpOpyuQBkk6CkxZ7VN9FqOuSd4Pk +Wm7iWyBMZsCmELZh5XAXld4BEt87C5R4CvbPBDZxAv3THk1DNNvpy3PFQfwARRFb +SAToYMECgYEAyL7U8yxpzHDYWd3oCx6vTi9p9N/z0FfAkWrRF6dm4UcSklNiT1Aq +EOnTA+SaW8tV3E64gCWcY23gNP8so/ZseWj6L+peHwtchaP9+KB7yGw2A+05+lOx +VetLTjAOmfpiUXFe5w1q4C1RGhLjZjjzW+GvwdAuchQgUEFaomrV+PUCgYEAxwfH +cmVGFbAktcjU4HSRjKSfawCrut+3YUOLybyku3Q/hP9amG8qkVTFe95CTLjLe2D0 +ccaTTpofFEJ32COeck0g0Ujn/qQ+KXRoauOYs4FB1DtqMpqB78wufWEUpDpbd9/h +J+gJdC/IADd4tJW9zA92g8IA7ZtFmqDtiSpQ0ekCgYAQGkaorvJZpN+l7cf0RGTZ +h7IfI2vCVZer0n6tQA9fmLzjoe6r4AlPzAHSOR8sp9XeUy43kUzHKQQoHCPvjw/K +eWJAP7OHF/k2+x2fOPhU7mEy1W+mJdp+wt4Kio5RSaVjVQ3AyPG+w8PSrJszEvRq +dWMMz+851WV2KpfjmWBKlQKBgQC++4j4DZQV5aMkSKV1CIZOBf3vaIJhXKEUFQPD +PmB4fBEjpwCg+zNGp6iktt65zi17o8qMjrb1mtCt2SY04eD932LZUHNFlwcLMmes +Ad+aiDLJ24WJL1f16eDGcOyktlblDZB5gZ/ovJzXEGOkLXglosTfo77OQculmDy2 +/UL2WQKBgGeKasmGNfiYAcWio+KXgFkHXWtAXB9B91B1OFnCa40wx+qnl71MIWQH +PQ/CZFNWOfGiNEJIZjrHsfNJoeXkhq48oKcT0AVCDYyLV0VxDO4ejT95mGW6njNd +JpvmhwwAjOvuWVr0tn4iXlSK8irjlJHmwcRjLTJq97vE9fsA2MjI +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/private_key.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/private_key.pem new file mode 100644 index 000000000..8fbf6bdec --- /dev/null +++ b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/private_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA1zVmMhPqpSPMmYkKh5wwlRD5XuS8YWJKEM6tjFx61VK8qxHE +YngkC2KnL5EuKAjQZIF3tJskwt0hAat047CCCZxrkNEpbVvSnvnk+A/8bg/Ww1n3 +qxzfifhsWfpUKlDnwrtH+ftt+5rZeEkf37XAPy7ZjzecAF9SDV6WSiPeAxUX2+hN +dId42Pf45woo4LFGUlQeagCFkD/R0dpNIMGwcnkKCUikiBqr2ijSIgvRtBfZ9fBG +jFGER2uE/Eay4AgcQsHue8skRwDCng8OnqtPnBtTytmqTy9V/BRgsVKUoksm6wsx +kUYwgHeaq7UCvlCm25SZ7yRyd4k8t0BKDf2h+wIDAQABAoIBAEQcrHmRACTADdNS +IjkFYALt2l8EOfMAbryfDSJtapr1kqz59JPNvmq0EIHnixo0n/APYdmReLML1ZR3 +tYkSpjVwgkLVUC1CcIjMQoGYXaZf8PLnGJHZk45RR8m6hsTV0mQ5bfBaeVa2jbma +OzJMjcnxg/3l9cPQZ2G/3AUfEPccMxOXp1KRz3mUQcGnKJGtDbN/kfmntcwYoxaE +Zg4RoeKAoMpK1SSHAiJKe7TnztINJ7uygR9XSzNd6auY8A3vomSIjpYO7XL+lh7L +izm4Ir3Gb/eCYBvWgQyQa2KCJgK/sQyEs3a09ngofSEUhQJQYhgZDwUj+fDDOGqj +hCZOA8ECgYEA+ZWuHdcUQ3ygYhLds2QcogUlIsx7C8n/Gk/FUrqqXJrTkuO0Eqqa +B47lCITvmn2zm0ODfSFIARgKEUEDLS/biZYv7SUTrFqBLcet+aGI7Dpv91CgB75R +tNzcIf8VxoiP0jPqdbh9mLbbxGi5Uc4p9TVXRljC4hkswaouebWee0sCgYEA3L2E +YB3kiHrhPI9LHS5Px9C1w+NOu5wP5snxrDGEgaFCvL6zgY6PflacppgnmTXl8D1x +im0IDKSw5dP3FFonSVXReq3CXDql7UnhfTCiLDahV7bLxTH42FofcBpDN3ERdOal +58RwQh6VrLkzQRVoObo+hbGlFiwwSAfQC509FhECgYBsRSBpVXo25IN2yBRg09cP ++gdoFyhxrsj5kw1YnB13WrrZh+oABv4WtUhp77E5ZbpaamlKCPwBbXpAjeFg4tfr +0bksuN7V79UGFQ9FsWuCfr8/nDwv38H2IbFlFhFONMOfPmJBey0Q6JJhm8R41mSh +OOiJXcv85UrjIH5U0hLUDQKBgQDVLOU5WcUJlPoOXSgiT0ZW5xWSzuOLRUUKEf6l +19BqzAzCcLy0orOrRAPW01xylt2v6/bJw1Ahva7k1ZZo/kOwjANYoZPxM+ZoSZBN +MXl8j2mzZuJVV1RFxItV3NcLJNPB/Lk+IbRz9kt/2f9InF7iWR3mSU/wIM6j0X+2 +p6yFsQKBgQCM/ldWb511lA+SNkqXB2P6WXAgAM/7+jwsNHX2ia2Ikufm4SUEKMSv +mti/nZkHDHsrHU4wb/2cOAywMELzv9EHzdcoenjBQP65OAc/1qWJs+LnBcCXfqKk +aHjEZW6+brkHdRGLLY3YAHlt/AUL+RsKPJfN72i/FSpmu+52G36eeQ== +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/public_key.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/public_key.pem new file mode 100644 index 000000000..f9772b533 --- /dev/null +++ b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/public_key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1zVmMhPqpSPMmYkKh5ww +lRD5XuS8YWJKEM6tjFx61VK8qxHEYngkC2KnL5EuKAjQZIF3tJskwt0hAat047CC +CZxrkNEpbVvSnvnk+A/8bg/Ww1n3qxzfifhsWfpUKlDnwrtH+ftt+5rZeEkf37XA +Py7ZjzecAF9SDV6WSiPeAxUX2+hNdId42Pf45woo4LFGUlQeagCFkD/R0dpNIMGw +cnkKCUikiBqr2ijSIgvRtBfZ9fBGjFGER2uE/Eay4AgcQsHue8skRwDCng8OnqtP +nBtTytmqTy9V/BRgsVKUoksm6wsxkUYwgHeaq7UCvlCm25SZ7yRyd4k8t0BKDf2h ++wIDAQAB +-----END PUBLIC KEY----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/server-cert.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/server-cert.pem new file mode 100644 index 000000000..a2f9688df --- /dev/null +++ b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/server-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBDCCAeygAwIBAgIBAjANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR +TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X +DTIwMDYxMTAzMzg0NloXDTMwMDYwOTAzMzg0NlowQDE+MDwGA1UEAww1TXlTUUxf +U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9TZXJ2ZXJfQ2VydGlmaWNhdGUw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcEnEm5hqP1EbEJycOz8Ua +NWp29QdpFUzTWhkKGhVXk+0msmNTw4NBAFB42moY44OU8wvDideOlJNhPRWveD8z +G2lxzJA91p0UK4et8ia9MmeuCGhdC9jxJ8X69WNlUiPyy0hI/ZsqRq9Z0C2eW0iL +JPXsy4X8Xpw3SFwoXf5pR9RFY5Pb2tuyxqmSestu2VXT/NQjJg4CVDR3mFcHPXZB +4elRzH0WshExEGkgy0bg20MJeRc2Qdb5Xx+EakbmwroDWaCn3NSGqQ7jv6Vw0doy +TGvS6h6RHBxnyqRfRgKGlCoOMG9/5+rFJC00QpCUG2vHXHWGoWlMlJ3foN7rj5v9 +AgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAJ5zt2rj4Ag6 +zpN59AWC1Fur8g8l41ksHkSpKPp+PtyO/ngvbMqBpfmK1e7JCKZv/68QXfMyWWAI +hwalqZkXXWHKjuz3wE7dE25PXFXtGJtcZAaj10xt98fzdqt8lQSwh2kbfNwZIz1F +sgAStgE7+ZTcqTgvNB76Os1UK0to+/P0VBWktaVFdyub4Nc2SdPVnZNvrRBXBwOD +3V8ViwywDOFoE7DvCvwx/SVsvoC0Z4j3AMMovO6oHicP7uU83qsQgm1Qru3YeoLR ++DoVi7IPHbWvN7MqFYn3YjNlByO2geblY7MR0BlqbFlmFrqLsUfjsh2ys7/U/knC +dN/klu446fI= +-----END CERTIFICATE----- diff --git a/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/server-key.pem b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/server-key.pem new file mode 100644 index 000000000..a1dfd5f78 --- /dev/null +++ b/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/server-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAnBJxJuYaj9RGxCcnDs/FGjVqdvUHaRVM01oZChoVV5PtJrJj +U8ODQQBQeNpqGOODlPMLw4nXjpSTYT0Vr3g/MxtpccyQPdadFCuHrfImvTJnrgho +XQvY8SfF+vVjZVIj8stISP2bKkavWdAtnltIiyT17MuF/F6cN0hcKF3+aUfURWOT +29rbssapknrLbtlV0/zUIyYOAlQ0d5hXBz12QeHpUcx9FrIRMRBpIMtG4NtDCXkX +NkHW+V8fhGpG5sK6A1mgp9zUhqkO47+lcNHaMkxr0uoekRwcZ8qkX0YChpQqDjBv +f+fqxSQtNEKQlBtrx1x1hqFpTJSd36De64+b/QIDAQABAoIBAFiah66Dt9SruLkn +WR8piUaFyLlcBib8Nq9OWSTJBhDAJERxxb4KIvvGB+l0ZgNXNp5bFPSfzsZdRwZP +PX5uj8Kd71Dxx3mz211WESMJdEC42u+MSmN4lGLkJ5t/sDwXU91E1vbJM0ve8THV +4/Ag9qA4DX2vVZOeyqT/6YHpSsPNZplqzrbAiwrfHwkctHfgqwOf3QLfhmVQgfCS +VwidBldEUv2whSIiIxh4Rv5St4kA68IBCbJxdpOpyuQBkk6CkxZ7VN9FqOuSd4Pk +Wm7iWyBMZsCmELZh5XAXld4BEt87C5R4CvbPBDZxAv3THk1DNNvpy3PFQfwARRFb +SAToYMECgYEAyL7U8yxpzHDYWd3oCx6vTi9p9N/z0FfAkWrRF6dm4UcSklNiT1Aq +EOnTA+SaW8tV3E64gCWcY23gNP8so/ZseWj6L+peHwtchaP9+KB7yGw2A+05+lOx +VetLTjAOmfpiUXFe5w1q4C1RGhLjZjjzW+GvwdAuchQgUEFaomrV+PUCgYEAxwfH +cmVGFbAktcjU4HSRjKSfawCrut+3YUOLybyku3Q/hP9amG8qkVTFe95CTLjLe2D0 +ccaTTpofFEJ32COeck0g0Ujn/qQ+KXRoauOYs4FB1DtqMpqB78wufWEUpDpbd9/h +J+gJdC/IADd4tJW9zA92g8IA7ZtFmqDtiSpQ0ekCgYAQGkaorvJZpN+l7cf0RGTZ +h7IfI2vCVZer0n6tQA9fmLzjoe6r4AlPzAHSOR8sp9XeUy43kUzHKQQoHCPvjw/K +eWJAP7OHF/k2+x2fOPhU7mEy1W+mJdp+wt4Kio5RSaVjVQ3AyPG+w8PSrJszEvRq +dWMMz+851WV2KpfjmWBKlQKBgQC++4j4DZQV5aMkSKV1CIZOBf3vaIJhXKEUFQPD +PmB4fBEjpwCg+zNGp6iktt65zi17o8qMjrb1mtCt2SY04eD932LZUHNFlwcLMmes +Ad+aiDLJ24WJL1f16eDGcOyktlblDZB5gZ/ovJzXEGOkLXglosTfo77OQculmDy2 +/UL2WQKBgGeKasmGNfiYAcWio+KXgFkHXWtAXB9B91B1OFnCa40wx+qnl71MIWQH +PQ/CZFNWOfGiNEJIZjrHsfNJoeXkhq48oKcT0AVCDYyLV0VxDO4ejT95mGW6njNd +JpvmhwwAjOvuWVr0tn4iXlSK8irjlJHmwcRjLTJq97vE9fsA2MjI +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mysql/.github/workflows/run_test_cases.yaml b/apps/emqx_auth_mysql/.github/workflows/run_test_cases.yaml new file mode 100644 index 000000000..90f2d7437 --- /dev/null +++ b/apps/emqx_auth_mysql/.github/workflows/run_test_cases.yaml @@ -0,0 +1,59 @@ +name: Run test cases + +on: [push, pull_request] + +jobs: + run_test_cases: + runs-on: ubuntu-latest + + strategy: + matrix: + mysql_tag: + - 5.7 + - 8 + network_type: + - ipv4 + - ipv6 + connect_type: + - ssl + - tcp + + steps: + - name: install docker-compose + run: | + sudo curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + - uses: actions/checkout@v1 + - name: run test cases + env: + MYSQL_TAG: ${{ matrix.mysql_tag }} + NETWORK_TYPE: ${{ matrix.network_type }} + CONNECT_TYPE: ${{ matrix.connect_type }} + run: | + if [ "$NETWORK_TYPE" = "ipv6" ];then docker network create --driver bridge --ipv6 --subnet fd15:555::/64 test_emqx_bridge --attachable; fi + if [ "$CONNECT_TYPE" = "ssl" ]; then + docker-compose -f ./docker-compose-ssl.yml -p test up -d + docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "echo 'auth.mysql.ssl.cafile = /emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca.pem' >> /emqx_auth_mysql/etc/emqx_auth_mysql.conf" + docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "echo 'auth.mysql.ssl.certfile = /emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-cert.pem' >> /emqx_auth_mysql/etc/emqx_auth_mysql.conf" + docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "echo 'auth.mysql.ssl.keyfile = /emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-key.pem' >> /emqx_auth_mysql/etc/emqx_auth_mysql.conf" + else + docker-compose -f ./docker-compose.yml -p test up -d + fi + docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "echo 'auth.mysql.username = root' >> /emqx_auth_mysql/etc/emqx_auth_mysql.conf " + docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "echo 'auth.mysql.password = public' >> /emqx_auth_mysql/etc/emqx_auth_mysql.conf" + if [ "$NETWORK_TYPE" != "ipv6" ];then + docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "sed -i '/auth.mysql.server/c auth.mysql.server = mysql_server:3306' /emqx_auth_mysql/etc/emqx_auth_mysql.conf" + else + ipv6_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' $(docker ps -a -f name=test_mysql_server_1 -q)) + docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "sed -i '/auth.mysql.server/c auth.mysql.server = $ipv6_address:3306' /emqx_auth_mysql/etc/emqx_auth_mysql.conf" + fi + docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "make -C /emqx_auth_mysql xref" + docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "make -C /emqx_auth_mysql eunit" + docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "make -C /emqx_auth_mysql ct" + docker exec -i $(docker ps -a -f name=test_erlang_1 -q) sh -c "make -C /emqx_auth_mysql cover" + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: logs_mysql${{ matrix.mysql_tag }}_${{ matrix.network_type }} + path: _build/test/logs + diff --git a/apps/emqx_auth_mysql/.gitignore b/apps/emqx_auth_mysql/.gitignore new file mode 100644 index 000000000..bc6fa0f2f --- /dev/null +++ b/apps/emqx_auth_mysql/.gitignore @@ -0,0 +1,31 @@ +.eunit +deps +*.so +.iml +.idea +*.o +*.beam +*.plt +erl_crash.dump +ebin +rel/example_project +.concrete/DEV_MODE +.rebar +.erlang.mk/ +emqx_auth_mysql.d +ct.coverdata +logs/ +test/ct.cover.spec +test/*.beam +cover/ +eunit.coverdata +data +.placeholder +_build/ +rebar.lock +erlang.mk +rebar3.crashdump +etc/emqx_auth_mysql.conf.rendered +.rebar3/ +*.swp +.DS_Store diff --git a/apps/emqx_auth_mysql/LICENSE b/apps/emqx_auth_mysql/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/apps/emqx_auth_mysql/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/apps/emqx_auth_mysql/README.md b/apps/emqx_auth_mysql/README.md new file mode 100644 index 000000000..e55a2103f --- /dev/null +++ b/apps/emqx_auth_mysql/README.md @@ -0,0 +1,167 @@ +emqx_auth_mysql +=============== + +Authentication, ACL with MySQL Database. + +Notice: changed mysql driver to [mysql-otp](https://github.com/mysql-otp/mysql-otp). + +Features +--------- + +- Full *Authentication*, *Superuser*, *ACL* support +- IPv4, IPv6 and TLS support +- Connection pool by [ecpool](https://github.com/emqx/ecpool) +- Completely cover MySQL 5.7, MySQL 8 in our tests + +Build Plugin +------------- + +make && make tests + +Configure Plugin +---------------- + +File: etc/emqx_auth_mysql.conf + +``` +## MySQL server address. +## +## Value: Port | IP:Port +## +## Examples: 3306, 127.0.0.1:3306, localhost:3306 +auth.mysql.server = 127.0.0.1:3306 + +## MySQL pool size. +## +## Value: Number +auth.mysql.pool = 8 + +## MySQL username. +## +## Value: String +## auth.mysql.username = + +## MySQL Password. +## +## Value: String +## auth.mysql.password = + +## MySQL database. +## +## Value: String +auth.mysql.database = mqtt + +## Variables: %u = username, %c = clientid + +## Authentication query. +## +## Note that column names should be 'password' and 'salt' (if used). +## In case column names differ in your DB - please use aliases, +## e.g. "my_column_name as password". +## +## Value: SQL +## +## Variables: +## - %u: username +## - %c: clientid +## - %C: common name of client TLS cert +## - %d: subject of client TLS cert +## +auth.mysql.auth_query = select password from mqtt_user where username = '%u' limit 1 +## auth.mysql.auth_query = select password_hash as password from mqtt_user where username = '%u' limit 1 + +## Password hash. +## +## Value: plain | md5 | sha | sha256 | bcrypt +auth.mysql.password_hash = sha256 + +## sha256 with salt prefix +## auth.mysql.password_hash = salt,sha256 + +## bcrypt with salt only prefix +## auth.mysql.password_hash = salt,bcrypt + +## sha256 with salt suffix +## auth.mysql.password_hash = sha256,salt + +## pbkdf2 with macfun iterations dklen +## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 +## auth.mysql.password_hash = pbkdf2,sha256,1000,20 + +## Superuser query. +## +## Value: SQL +## +## Variables: +## - %u: username +## - %c: clientid +## - %C: common name of client TLS cert +## - %d: subject of client TLS cert +auth.mysql.super_query = select is_superuser from mqtt_user where username = '%u' limit 1 + +## ACL query. +## +## Value: SQL +## +## Variables: +## - %a: ipaddr +## - %u: username +## - %c: clientid +## Note: You can add the 'ORDER BY' statement to control the rules match order +auth.mysql.acl_query = select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c' + +``` + +Import mqtt.sql +--------------- + +Import mqtt.sql into your database. + +Load Plugin +----------- + +./bin/emqx_ctl plugins load emqx_auth_mysql + +Auth Table +---------- + +Notice: This is a demo table. You could authenticate with any user table. + +```sql +CREATE TABLE `mqtt_user` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `username` varchar(100) DEFAULT NULL, + `password` varchar(100) DEFAULT NULL, + `salt` varchar(35) DEFAULT NULL, + `is_superuser` tinyint(1) DEFAULT 0, + `created` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `mqtt_username` (`username`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +``` + +ACL Table +---------- + +```sql +CREATE TABLE `mqtt_acl` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `allow` int(1) DEFAULT NULL COMMENT '0: deny, 1: allow', + `ipaddr` varchar(60) DEFAULT NULL COMMENT 'IpAddress', + `username` varchar(100) DEFAULT NULL COMMENT 'Username', + `clientid` varchar(100) DEFAULT NULL COMMENT 'ClientId', + `access` int(2) NOT NULL COMMENT '1: subscribe, 2: publish, 3: pubsub', + `topic` varchar(100) NOT NULL DEFAULT '' COMMENT 'Topic Filter', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +``` + +License +------- + +Apache License Version 2.0 + +Author +------ + +EMQ X Team. diff --git a/apps/emqx_auth_mysql/docker-compose-ssl.yml b/apps/emqx_auth_mysql/docker-compose-ssl.yml new file mode 100644 index 000000000..fe7d442fe --- /dev/null +++ b/apps/emqx_auth_mysql/docker-compose-ssl.yml @@ -0,0 +1,41 @@ +version: '3' + +services: + erlang: + image: erlang:22.3 + volumes: + - ./:/emqx_auth_mysql + networks: + - emqx_bridge + depends_on: + - mysql_server + tty: true + + mysql_server: + image: mysql:${MYSQL_TAG} + restart: always + environment: + MYSQL_ROOT_PASSWORD: public + MYSQL_DATABASE: mqtt + volumes: + - ./test/emqx_auth_mysql_SUITE_data/ca.pem:/etc/certs/ca-cert.pem + - ./test/emqx_auth_mysql_SUITE_data/server-cert.pem:/etc/certs/server-cert.pem + - ./test/emqx_auth_mysql_SUITE_data/server-key.pem:/etc/certs/server-key.pem + networks: + - emqx_bridge + command: + --bind-address "::" + --default-authentication-plugin=mysql_native_password + --character-set-server=utf8mb4 + --collation-server=utf8mb4_general_ci + --explicit_defaults_for_timestamp=true + --lower_case_table_names=1 + --max_allowed_packet=128M + --skip-symbolic-links + --ssl-ca=/etc/certs/ca.pem + --ssl-cert=/etc/certs/server-cert.pem + --ssl-key=/etc/certs/server-key.pem + +networks: + emqx_bridge: + driver: bridge diff --git a/apps/emqx_auth_mysql/docker-compose.yml b/apps/emqx_auth_mysql/docker-compose.yml new file mode 100644 index 000000000..c37bf983b --- /dev/null +++ b/apps/emqx_auth_mysql/docker-compose.yml @@ -0,0 +1,34 @@ +version: '3' + +services: + erlang: + image: erlang:22.3 + volumes: + - ./:/emqx_auth_mysql + networks: + - emqx_bridge + depends_on: + - mysql_server + tty: true + + mysql_server: + image: mysql:${MYSQL_TAG} + restart: always + environment: + MYSQL_ROOT_PASSWORD: public + MYSQL_DATABASE: mqtt + networks: + - emqx_bridge + command: + --bind-address "::" + --default-authentication-plugin=mysql_native_password + --character-set-server=utf8mb4 + --collation-server=utf8mb4_general_ci + --explicit_defaults_for_timestamp=true + --lower_case_table_names=1 + --max_allowed_packet=128M + --skip-symbolic-links + +networks: + emqx_bridge: + driver: bridge diff --git a/apps/emqx_auth_mysql/etc/emqx_auth_mysql.conf b/apps/emqx_auth_mysql/etc/emqx_auth_mysql.conf new file mode 100644 index 000000000..0efccce29 --- /dev/null +++ b/apps/emqx_auth_mysql/etc/emqx_auth_mysql.conf @@ -0,0 +1,116 @@ +##-------------------------------------------------------------------- +## MySQL Auth/ACL Plugin +##-------------------------------------------------------------------- + +## MySQL server address. +## +## Value: Port | IP:Port +## +## Examples: 3306, 127.0.0.1:3306, localhost:3306 +auth.mysql.server = 127.0.0.1:3306 + +## MySQL pool size. +## +## Value: Number +auth.mysql.pool = 8 + +## MySQL username. +## +## Value: String +## auth.mysql.username = + +## MySQL password. +## +## Value: String +## auth.mysql.password = + +## MySQL database. +## +## Value: String +auth.mysql.database = mqtt + +## MySQL query timeout +## +## Value: Duration +## auth.mysql.query_timeout = 5s + +## Variables: %u = username, %c = clientid + +## Authentication query. +## +## Note that column names should be 'password' and 'salt' (if used). +## In case column names differ in your DB - please use aliases, +## e.g. "my_column_name as password". +## +## Value: SQL +## +## Variables: +## - %u: username +## - %c: clientid +## - %C: common name of client TLS cert +## - %d: subject of client TLS cert +## +auth.mysql.auth_query = select password from mqtt_user where username = '%u' limit 1 +## auth.mysql.auth_query = select password_hash as password from mqtt_user where username = '%u' limit 1 + +## Password hash. +## +## Value: plain | md5 | sha | sha256 | bcrypt +auth.mysql.password_hash = sha256 + +## sha256 with salt prefix +## auth.mysql.password_hash = salt,sha256 + +## bcrypt with salt only prefix +## auth.mysql.password_hash = salt,bcrypt + +## sha256 with salt suffix +## auth.mysql.password_hash = sha256,salt + +## pbkdf2 with macfun iterations dklen +## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 +## auth.mysql.password_hash = pbkdf2,sha256,1000,20 + +## Superuser query. +## +## Value: SQL +## +## Variables: +## - %u: username +## - %c: clientid +## - %C: common name of client TLS cert +## - %d: subject of client TLS cert +## +auth.mysql.super_query = select is_superuser from mqtt_user where username = '%u' limit 1 + +## ACL query. +## +## Value: SQL +## +## Variables: +## - %a: ipaddr +## - %u: username +## - %c: clientid +## +## Note: You can add the 'ORDER BY' statement to control the rules match order +auth.mysql.acl_query = select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c' + +## Mysql ssl configuration. +## +## Value: on | off +auth.mysql.ssl = off + +## CA certificate. +## +## Value: File +## auth.mysql.ssl.cafile = path to your ca file + +## Client ssl certificate. +## +## Value: File +## auth.mysql.ssl.certfile = path to your clientcert file + +## Client ssl keyfile. +## +## Value: File +## auth.mysql.ssl.keyfile = path to your clientkey file diff --git a/apps/emqx_auth_mysql/include/emqx_auth_mysql.hrl b/apps/emqx_auth_mysql/include/emqx_auth_mysql.hrl new file mode 100644 index 000000000..fca431e81 --- /dev/null +++ b/apps/emqx_auth_mysql/include/emqx_auth_mysql.hrl @@ -0,0 +1,23 @@ + +-define(APP, emqx_auth_mysql). + +-record(auth_metrics, { + success = 'client.auth.success', + failure = 'client.auth.failure', + ignore = 'client.auth.ignore' + }). + +-record(acl_metrics, { + allow = 'client.acl.allow', + deny = 'client.acl.deny', + ignore = 'client.acl.ignore' + }). + +-define(METRICS(Type), tl(tuple_to_list(#Type{}))). +-define(METRICS(Type, K), #Type{}#Type.K). + +-define(AUTH_METRICS, ?METRICS(auth_metrics)). +-define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). + +-define(ACL_METRICS, ?METRICS(acl_metrics)). +-define(ACL_METRICS(K), ?METRICS(acl_metrics, K)). diff --git a/apps/emqx_auth_mysql/mqtt.sql b/apps/emqx_auth_mysql/mqtt.sql new file mode 100644 index 000000000..9635bee58 --- /dev/null +++ b/apps/emqx_auth_mysql/mqtt.sql @@ -0,0 +1,41 @@ + +DROP TABLE IF EXISTS `mqtt_acl`; + +CREATE TABLE `mqtt_acl` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `allow` int(1) DEFAULT NULL COMMENT '0: deny, 1: allow', + `ipaddr` varchar(60) DEFAULT NULL COMMENT 'IpAddress', + `username` varchar(100) DEFAULT NULL COMMENT 'Username', + `clientid` varchar(100) DEFAULT NULL COMMENT 'ClientId', + `access` int(2) NOT NULL COMMENT '1: subscribe, 2: publish, 3: pubsub', + `topic` varchar(100) NOT NULL DEFAULT '' COMMENT 'Topic Filter', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +LOCK TABLES `mqtt_acl` WRITE; + +INSERT INTO `mqtt_acl` (`id`, `allow`, `ipaddr`, `username`, `clientid`, `access`, `topic`) +VALUES + (1,1,NULL,'$all',NULL,2,'#'), + (2,0,NULL,'$all',NULL,1,'$SYS/#'), + (3,0,NULL,'$all',NULL,1,'eq #'), + (4,1,'127.0.0.1',NULL,NULL,2,'$SYS/#'), + (5,1,'127.0.0.1',NULL,NULL,2,'#'), + (6,1,NULL,'dashboard',NULL,1,'$SYS/#'); + +UNLOCK TABLES; + + +DROP TABLE IF EXISTS `mqtt_user`; + +CREATE TABLE `mqtt_user` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `username` varchar(100) DEFAULT NULL, + `password` varchar(100) DEFAULT NULL, + `salt` varchar(35) DEFAULT NULL, + `is_superuser` tinyint(1) DEFAULT 0, + `created` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `mqtt_username` (`username`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; + diff --git a/apps/emqx_auth_mysql/priv/emqx_auth_mysql.schema b/apps/emqx_auth_mysql/priv/emqx_auth_mysql.schema new file mode 100644 index 000000000..37eed8a5f --- /dev/null +++ b/apps/emqx_auth_mysql/priv/emqx_auth_mysql.schema @@ -0,0 +1,129 @@ +%%-*- mode: erlang -*- +%% emqx_auth_mysql config mapping +{mapping, "auth.mysql.server", "emqx_auth_mysql.server", [ + {default, {"127.0.0.1", 3306}}, + {datatype, [integer, ip, string]} +]}. + +{mapping, "auth.mysql.pool", "emqx_auth_mysql.server", [ + {default, 8}, + {datatype, integer} +]}. + +{mapping, "auth.mysql.username", "emqx_auth_mysql.server", [ + {default, ""}, + {datatype, string} +]}. + +{mapping, "auth.mysql.password", "emqx_auth_mysql.server", [ + {default, ""}, + {datatype, string} +]}. + +{mapping, "auth.mysql.database", "emqx_auth_mysql.server", [ + {default, "mqtt"}, + {datatype, string} +]}. + +{mapping, "auth.mysql.query_timeout", "emqx_auth_mysql.server", [ + {default, ""}, + {datatype, string} +]}. + +{mapping, "auth.mysql.ssl", "emqx_auth_mysql.server", [ + {default, off}, + {datatype, flag} +]}. + +{mapping, "auth.mysql.ssl.cafile", "emqx_auth_mysql.server", [ + {default, ""}, + {datatype, string} +]}. + +{mapping, "auth.mysql.ssl.certfile", "emqx_auth_mysql.server", [ + {default, ""}, + {datatype, string} +]}. + +{mapping, "auth.mysql.ssl.keyfile", "emqx_auth_mysql.server", [ + {default, ""}, + {datatype, string} +]}. + +{translation, "emqx_auth_mysql.server", fun(Conf) -> + {MyHost, MyPort} = + case cuttlefish:conf_get("auth.mysql.server", Conf) of + {Ip, Port} -> {Ip, Port}; + S -> case string:tokens(S, ":") of + [Domain] -> {Domain, 3306}; + [Domain, Port] -> {Domain, list_to_integer(Port)} + end + end, + Pool = cuttlefish:conf_get("auth.mysql.pool", Conf), + Username = cuttlefish:conf_get("auth.mysql.username", Conf), + Passwd = cuttlefish:conf_get("auth.mysql.password", Conf), + DB = cuttlefish:conf_get("auth.mysql.database", Conf), + Timeout = case cuttlefish:conf_get("auth.mysql.query_timeout", Conf) of + "" -> 300000; + Duration -> + case cuttlefish_duration:parse(Duration, ms) of + {error, Reason} -> error(Reason); + Ms when is_integer(Ms) -> Ms + end + end, + Options = [{pool_size, Pool}, + {auto_reconnect, 1}, + {host, MyHost}, + {port, MyPort}, + {user, Username}, + {password, Passwd}, + {database, DB}, + {encoding, utf8}, + {query_timeout, Timeout}, + {keep_alive, true}], + Options1 = + case cuttlefish:conf_get("auth.mysql.ssl", Conf) of + true -> + CA = cuttlefish:conf_get("auth.mysql.ssl.cafile", Conf), + Cert = cuttlefish:conf_get("auth.mysql.ssl.certfile", Conf), + Key = cuttlefish:conf_get("auth.mysql.ssl.keyfile", Conf), + Options ++ [{ssl, {server_name_indication, disable}, + {cacertfile, CA}, + {certfile, Cert}, + {keyfile, Key}}]; + _ -> + Options + end, + case inet:parse_address(MyHost) of + {ok, IpAddr} when tuple_size(IpAddr) =:= 8 -> + [{tcp_options, [inet6]} | Options1]; + _ -> + Options1 + end +end}. + +{mapping, "auth.mysql.auth_query", "emqx_auth_mysql.auth_query", [ + {datatype, string} +]}. + +{mapping, "auth.mysql.password_hash", "emqx_auth_mysql.password_hash", [ + {datatype, string} +]}. + +{mapping, "auth.mysql.super_query", "emqx_auth_mysql.super_query", [ + {datatype, string} +]}. + +{mapping, "auth.mysql.acl_query", "emqx_auth_mysql.acl_query", [ + {datatype, string} +]}. + +{translation, "emqx_auth_mysql.password_hash", fun(Conf) -> + HashValue = cuttlefish:conf_get("auth.mysql.password_hash", Conf), + case string:tokens(HashValue, ",") of + [Hash] -> list_to_atom(Hash); + [Prefix, Suffix] -> {list_to_atom(Prefix), list_to_atom(Suffix)}; + [Hash, MacFun, Iterations, Dklen] -> {list_to_atom(Hash), list_to_atom(MacFun), list_to_integer(Iterations), list_to_integer(Dklen)}; + _ -> plain + end +end}. diff --git a/apps/emqx_auth_mysql/rebar.config b/apps/emqx_auth_mysql/rebar.config new file mode 100644 index 000000000..c77aef44c --- /dev/null +++ b/apps/emqx_auth_mysql/rebar.config @@ -0,0 +1,35 @@ +{deps, + [{ecpool, {git, "https://github.com/emqx/ecpool", {tag, "v0.4.2"}}}, + {emqx_passwd, {git, "https://github.com/emqx/emqx-passwd", {tag, "v1.1.1"}}}, + {mysql, {git, "https://github.com/emqx/mysql-otp", {tag, "1.6.1"}}} + ]}. + +{edoc_opts, [{preprocess, true}]}. +{erl_opts, [warn_unused_vars, + warn_shadow_vars, + warn_unused_import, + warn_obsolete_guard, + debug_info, + compressed, + {parse_transform} + ]}. +{overrides, [{add, [{erl_opts, [compressed]}]}]}. + +{xref_checks, [undefined_function_calls, undefined_functions, + locals_not_used, deprecated_function_calls, + warnings_as_errors, deprecated_functions + ]}. + +{cover_enabled, true}. +{cover_opts, [verbose]}. +{cover_export_enabled, true}. + +{profiles, + [{test, + [{deps, + [{emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}}, + {emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.2.0"}}} + ]}, + {erl_opts, [debug_info]} + ]} + ]}. diff --git a/apps/emqx_auth_mysql/src/emqx_acl_mysql.erl b/apps/emqx_auth_mysql/src/emqx_acl_mysql.erl new file mode 100644 index 000000000..174e3cd27 --- /dev/null +++ b/apps/emqx_auth_mysql/src/emqx_acl_mysql.erl @@ -0,0 +1,119 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_acl_mysql). + +-include("emqx_auth_mysql.hrl"). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). + +%% ACL Callbacks +-export([ register_metrics/0 + , check_acl/5 + , description/0 + ]). + +-spec(register_metrics() -> ok). +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS). + +check_acl(ClientInfo, PubSub, Topic, NoMatchAction, #{pool := Pool} = State) -> + case do_check_acl(Pool, ClientInfo, PubSub, Topic, NoMatchAction, State) of + ok -> emqx_metrics:inc(?ACL_METRICS(ignore)), ok; + {stop, allow} -> emqx_metrics:inc(?ACL_METRICS(allow)), {stop, allow}; + {stop, deny} -> emqx_metrics:inc(?ACL_METRICS(deny)), {stop, deny} + end. + +do_check_acl(_Pool, #{username := <<$$, _/binary>>}, _PubSub, _Topic, _NoMatchAction, _State) -> + ok; +do_check_acl(Pool, ClientInfo, PubSub, Topic, _NoMatchAction, #{acl_query := {AclSql, AclParams}}) -> + case emqx_auth_mysql_cli:query(Pool, AclSql, AclParams, ClientInfo) of + {ok, _Columns, []} -> ok; + {ok, _Columns, Rows} -> + Rules = filter(PubSub, compile(Rows)), + case match(ClientInfo, Topic, Rules) of + {matched, allow} -> {stop, allow}; + {matched, deny} -> {stop, deny}; + nomatch -> ok + end; + {error, Reason} -> + ?LOG(error, "[MySQL] do_check_acl error: ~p~n", [Reason]), + ok + end. + +match(_ClientInfo, _Topic, []) -> + nomatch; + +match(ClientInfo, Topic, [Rule|Rules]) -> + case emqx_access_rule:match(ClientInfo, Topic, Rule) of + nomatch -> + match(ClientInfo, Topic, Rules); + {matched, AllowDeny} -> + {matched, AllowDeny} + end. + +filter(PubSub, Rules) -> + [Term || Term = {_, _, Access, _} <- Rules, + Access =:= PubSub orelse Access =:= pubsub]. + +compile(Rows) -> + compile(Rows, []). +compile([], Acc) -> + Acc; +compile([[Allow, IpAddr, Username, ClientId, Access, Topic]|T], Acc) -> + Who = who(IpAddr, Username, ClientId), + Term = {allow(Allow), Who, access(Access), [topic(Topic)]}, + compile(T, [emqx_access_rule:compile(Term) | Acc]). + +who(_, <<"$all">>, _) -> + all; +who(null, null, null) -> + throw(undefined_who); +who(CIDR, Username, ClientId) -> + Cols = [{ipaddr, b2l(CIDR)}, {user, Username}, {client, ClientId}], + case [{C, V} || {C, V} <- Cols, not empty(V)] of + [Who] -> Who; + Conds -> {'and', Conds} + end. + +allow(1) -> allow; +allow(0) -> deny; +allow(<<"1">>) -> allow; +allow(<<"0">>) -> deny. + +access(1) -> subscribe; +access(2) -> publish; +access(3) -> pubsub; +access(<<"1">>) -> subscribe; +access(<<"2">>) -> publish; +access(<<"3">>) -> pubsub. + +topic(<<"eq ", Topic/binary>>) -> + {eq, Topic}; +topic(Topic) -> + Topic. + +description() -> + "ACL with Mysql". + +b2l(null) -> null; +b2l(B) -> binary_to_list(B). + +empty(null) -> true; +empty("") -> true; +empty(<<>>) -> true; +empty(_) -> false. diff --git a/apps/emqx_auth_mysql/src/emqx_auth_mysql.app.src b/apps/emqx_auth_mysql/src/emqx_auth_mysql.app.src new file mode 100644 index 000000000..3e21de565 --- /dev/null +++ b/apps/emqx_auth_mysql/src/emqx_auth_mysql.app.src @@ -0,0 +1,14 @@ +{application, emqx_auth_mysql, + [{description, "EMQ X Authentication/ACL with MySQL"}, + {vsn, "git"}, + {modules, []}, + {registered, [emqx_auth_mysql_sup]}, + {applications, [kernel,stdlib,mysql,ecpool,emqx_passwd]}, + {mod, {emqx_auth_mysql_app,[]}}, + {env, []}, + {licenses, ["Apache-2.0"]}, + {maintainers, ["EMQ X Team "]}, + {links, [{"Homepage", "https://emqx.io/"}, + {"Github", "https://github.com/emqx/emqx-auth-mysql"} + ]} + ]}. diff --git a/apps/emqx_auth_mysql/src/emqx_auth_mysql.app.src.script b/apps/emqx_auth_mysql/src/emqx_auth_mysql.app.src.script new file mode 100644 index 000000000..0e14ff23f --- /dev/null +++ b/apps/emqx_auth_mysql/src/emqx_auth_mysql.app.src.script @@ -0,0 +1,24 @@ +%%-*- mode: erlang -*- +%% .app.src.script + +RemoveLeadingV = + fun(Tag) -> + case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of + nomatch -> + re:replace(Tag, "/", "-", [{return ,list}]); + _ -> + %% if it is a version number prefixed by 'v' or 'e', then remove it + re:replace(Tag, "[v|e]", "", [{return ,list}]) + end + end, + +case os:getenv("EMQX_DEPS_DEFAULT_VSN") of + false -> CONFIG; % env var not defined + [] -> CONFIG; % env var set to empty string + Tag -> + [begin + AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}), + {application, App, AppConf0} + end || Conf = {application, App, AppConf} <- CONFIG] +end. + diff --git a/apps/emqx_auth_mysql/src/emqx_auth_mysql.appup.src b/apps/emqx_auth_mysql/src/emqx_auth_mysql.appup.src new file mode 100644 index 000000000..e4c16f92b --- /dev/null +++ b/apps/emqx_auth_mysql/src/emqx_auth_mysql.appup.src @@ -0,0 +1,25 @@ +%% -*-: erlang -*- +{"4.2.3", + [ + {"4.2.2", [ + {load_module, emqx_auth_mysql_cli, brutal_purge, soft_purge, []} + ]}, + {"4.2.1", [ + {load_module, emqx_auth_mysql_cli, brutal_purge, soft_purge, []} + ]}, + {"4.2.0", [ + {load_module, emqx_auth_mysql_cli, brutal_purge, soft_purge, []} + ]} + ], + [ + {"4.2.2", [ + {load_module, emqx_auth_mysql_cli, brutal_purge, soft_purge, []} + ]}, + {"4.2.1", [ + {load_module, emqx_auth_mysql_cli, brutal_purge, soft_purge, []} + ]}, + {"4.2.0", [ + {load_module, emqx_auth_mysql_cli, brutal_purge, soft_purge, []} + ]} + ] +}. diff --git a/apps/emqx_auth_mysql/src/emqx_auth_mysql.erl b/apps/emqx_auth_mysql/src/emqx_auth_mysql.erl new file mode 100644 index 000000000..9cd9d5a25 --- /dev/null +++ b/apps/emqx_auth_mysql/src/emqx_auth_mysql.erl @@ -0,0 +1,91 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_mysql). + +-include("emqx_auth_mysql.hrl"). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). +-include_lib("emqx/include/types.hrl"). + +-export([ register_metrics/0 + , check/3 + , description/0 + ]). + +-define(EMPTY(Username), (Username =:= undefined orelse Username =:= <<>>)). + +-spec(register_metrics() -> ok). +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). + +check(ClientInfo = #{password := Password}, AuthResult, + #{auth_query := {AuthSql, AuthParams}, + super_query := SuperQuery, + hash_type := HashType, + pool := Pool}) -> + CheckPass = case emqx_auth_mysql_cli:query(Pool, AuthSql, AuthParams, ClientInfo) of + {ok, [<<"password">>], [[PassHash]]} -> + check_pass({PassHash, Password}, HashType); + {ok, [<<"password">>, <<"salt">>], [[PassHash, Salt]]} -> + check_pass({PassHash, Salt, Password}, HashType); + {ok, _Columns, []} -> + {error, not_found}; + {error, Reason} -> + ?LOG(error, "[MySQL] query '~p' failed: ~p", [AuthSql, Reason]), + {error, Reason} + end, + case CheckPass of + ok -> + emqx_metrics:inc(?AUTH_METRICS(success)), + {stop, AuthResult#{is_superuser => is_superuser(Pool, SuperQuery, ClientInfo), + anonymous => false, + auth_result => success}}; + {error, not_found} -> + emqx_metrics:inc(?AUTH_METRICS(ignore)), ok; + {error, ResultCode} -> + ?LOG(error, "[MySQL] Auth from mysql failed: ~p", [ResultCode]), + emqx_metrics:inc(?AUTH_METRICS(failure)), + {stop, AuthResult#{auth_result => ResultCode, anonymous => false}} + end. + +%%-------------------------------------------------------------------- +%% Is Superuser? +%%-------------------------------------------------------------------- + +-spec(is_superuser(atom(), maybe({string(), list()}), emqx_types:client()) -> boolean()). +is_superuser(_Pool, undefined, _ClientInfo) -> false; +is_superuser(Pool, {SuperSql, Params}, ClientInfo) -> + case emqx_auth_mysql_cli:query(Pool, SuperSql, Params, ClientInfo) of + {ok, [_Super], [[1]]} -> + true; + {ok, [_Super], [[_False]]} -> + false; + {ok, [_Super], []} -> + false; + {error, _Error} -> + false + end. + +check_pass(Password, HashType) -> + case emqx_passwd:check_pass(Password, HashType) of + ok -> ok; + {error, _Reason} -> {error, not_authorized} + end. + +description() -> "Authentication with MySQL". + diff --git a/apps/emqx_auth_mysql/src/emqx_auth_mysql_app.erl b/apps/emqx_auth_mysql/src/emqx_auth_mysql_app.erl new file mode 100644 index 000000000..26895cd9a --- /dev/null +++ b/apps/emqx_auth_mysql/src/emqx_auth_mysql_app.erl @@ -0,0 +1,74 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_mysql_app). + +-behaviour(application). + +-emqx_plugin(auth). + +-include("emqx_auth_mysql.hrl"). + +-import(emqx_auth_mysql_cli, [parse_query/1]). + +%% Application callbacks +-export([ start/2 + , prep_stop/1 + , stop/1 + ]). + +%%-------------------------------------------------------------------- +%% Application callbacks +%%-------------------------------------------------------------------- + +start(_StartType, _StartArgs) -> + {ok, Sup} = emqx_auth_mysql_sup:start_link(), + if_enabled(auth_query, fun load_auth_hook/1), + if_enabled(acl_query, fun load_acl_hook/1), + + {ok, Sup}. + +prep_stop(State) -> + emqx:unhook('client.authenticate', fun emqx_auth_mysql:check/3), + emqx:unhook('client.check_acl', fun emqx_acl_mysql:check_acl/5), + State. + +stop(_State) -> + ok. + +load_auth_hook(AuthQuery) -> + ok = emqx_auth_mysql:register_metrics(), + SuperQuery = parse_query(application:get_env(?APP, super_query, undefined)), + {ok, HashType} = application:get_env(?APP, password_hash), + Params = #{auth_query => AuthQuery, + super_query => SuperQuery, + hash_type => HashType, + pool => ?APP}, + emqx:hook('client.authenticate', fun emqx_auth_mysql:check/3, [Params]). + +load_acl_hook(AclQuery) -> + ok = emqx_acl_mysql:register_metrics(), + emqx:hook('client.check_acl', fun emqx_acl_mysql:check_acl/5, [#{acl_query => AclQuery, pool =>?APP}]). + +%%-------------------------------------------------------------------- +%% Internal function +%%-------------------------------------------------------------------- + +if_enabled(Cfg, Fun) -> + case application:get_env(?APP, Cfg) of + {ok, Query} -> Fun(parse_query(Query)); + undefined -> ok + end. diff --git a/apps/emqx_auth_mysql/src/emqx_auth_mysql_cli.erl b/apps/emqx_auth_mysql/src/emqx_auth_mysql_cli.erl new file mode 100644 index 000000000..c3ee3b02a --- /dev/null +++ b/apps/emqx_auth_mysql/src/emqx_auth_mysql_cli.erl @@ -0,0 +1,91 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_mysql_cli). + +-behaviour(ecpool_worker). + +-include("emqx_auth_mysql.hrl"). +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-export([ parse_query/1 + , connect/1 + , query/4 + ]). + +%%-------------------------------------------------------------------- +%% Avoid SQL Injection: Parse SQL to Parameter Query. +%%-------------------------------------------------------------------- + +parse_query(undefined) -> + undefined; +parse_query(Sql) -> + case re:run(Sql, "'%[ucCad]'", [global, {capture, all, list}]) of + {match, Variables} -> + Params = [Var || [Var] <- Variables], + {re:replace(Sql, "'%[ucCad]'", "?", [global, {return, list}]), Params}; + nomatch -> + {Sql, []} + end. + +%%-------------------------------------------------------------------- +%% MySQL Connect/Query +%%-------------------------------------------------------------------- + +connect(Options) -> + case mysql:start_link(Options) of + {ok, Pid} -> {ok, Pid}; + ignore -> {error, ignore}; + {error, Reason = {{_, {error, econnrefused}}, _}} -> + ?LOG(error, "[MySQL] Can't connect to MySQL server: Connection refused."), + {error, Reason}; + {error, Reason = {ErrorCode, _, Error}} -> + ?LOG(error, "[MySQL] Can't connect to MySQL server: ~p - ~p", [ErrorCode, Error]), + {error, Reason}; + {error, Reason} -> + ?LOG(error, "[MySQL] Can't connect to MySQL server: ~p", [Reason]), + {error, Reason} + end. + +query(Pool, Sql, Params, ClientInfo) -> + ecpool:with_client(Pool, fun(C) -> mysql:query(C, Sql, replvar(Params, ClientInfo)) end). + +replvar(Params, ClientInfo) -> + replvar(Params, ClientInfo, []). + +replvar([], _ClientInfo, Acc) -> + lists:reverse(Acc); + +replvar(["'%u'" | Params], ClientInfo, Acc) -> + replvar(Params, ClientInfo, [safe_get(username, ClientInfo) | Acc]); +replvar(["'%c'" | Params], ClientInfo = #{clientid := ClientId}, Acc) -> + replvar(Params, ClientInfo, [ClientId | Acc]); +replvar(["'%a'" | Params], ClientInfo = #{peerhost := IpAddr}, Acc) -> + replvar(Params, ClientInfo, [inet_parse:ntoa(IpAddr) | Acc]); +replvar(["'%C'" | Params], ClientInfo, Acc) -> + replvar(Params, ClientInfo, [safe_get(cn, ClientInfo)| Acc]); +replvar(["'%d'" | Params], ClientInfo, Acc) -> + replvar(Params, ClientInfo, [safe_get(dn, ClientInfo)| Acc]); +replvar([Param | Params], ClientInfo, Acc) -> + replvar(Params, ClientInfo, [Param | Acc]). + +safe_get(K, ClientInfo) -> + bin(maps:get(K, ClientInfo, "undefined")). + +bin(A) when is_atom(A) -> atom_to_binary(A, utf8); +bin(B) when is_binary(B) -> B; +bin(X) -> X. diff --git a/apps/emqx_auth_mysql/src/emqx_auth_mysql_sup.erl b/apps/emqx_auth_mysql/src/emqx_auth_mysql_sup.erl new file mode 100644 index 000000000..0b2bf2c38 --- /dev/null +++ b/apps/emqx_auth_mysql/src/emqx_auth_mysql_sup.erl @@ -0,0 +1,40 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_mysql_sup). + +-behaviour(supervisor). + +-include("emqx_auth_mysql.hrl"). + +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%%-------------------------------------------------------------------- +%% Supervisor callbacks +%%-------------------------------------------------------------------- + +init([]) -> + %% MySQL Connection Pool. + {ok, Server} = application:get_env(?APP, server), + PoolSpec = ecpool:pool_spec(?APP, ?APP, emqx_auth_mysql_cli, Server), + {ok, {{one_for_one, 10, 100}, [PoolSpec]}}. + diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE.erl b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE.erl new file mode 100644 index 000000000..044655ac1 --- /dev/null +++ b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE.erl @@ -0,0 +1,234 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_mysql_SUITE). + +-compile(export_all). +-compile(nowarn_export_all). + +-define(APP, emqx_auth_mysql). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). + +-define(DROP_ACL_TABLE, <<"DROP TABLE IF EXISTS mqtt_acl">>). + +-define(CREATE_ACL_TABLE, <<"CREATE TABLE mqtt_acl (" + " id int(11) unsigned NOT NULL AUTO_INCREMENT," + " allow int(1) DEFAULT NULL COMMENT '0: deny, 1: allow'," + " ipaddr varchar(60) DEFAULT NULL COMMENT 'IpAddress'," + " username varchar(100) DEFAULT NULL COMMENT 'Username'," + " clientid varchar(100) DEFAULT NULL COMMENT 'ClientId'," + " access int(2) NOT NULL COMMENT '1: subscribe, 2: publish, 3: pubsub'," + " topic varchar(100) NOT NULL DEFAULT '' COMMENT 'Topic Filter'," + " PRIMARY KEY (`id`)" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4">>). + +-define(INIT_ACL, <<"INSERT INTO mqtt_acl (id, allow, ipaddr, username, clientid, access, topic)" + "VALUES (1,1,'127.0.0.1','u1','c1',1,'t1')," + "(2,0,'127.0.0.1','u2','c2',1,'t1')," + "(3,1,'10.10.0.110','u1','c1',1,'t1')," + "(4,1,'127.0.0.1','u3','c3',3,'t1')">>). + +-define(DROP_AUTH_TABLE, <<"DROP TABLE IF EXISTS `mqtt_user`">>). + +-define(CREATE_AUTH_TABLE, <<"CREATE TABLE `mqtt_user` (" + "`id` int(11) unsigned NOT NULL AUTO_INCREMENT," + "`username` varchar(100) DEFAULT NULL," + "`password` varchar(100) DEFAULT NULL," + "`salt` varchar(100) DEFAULT NULL," + "`is_superuser` tinyint(1) DEFAULT 0," + "`created` datetime DEFAULT NULL," + "PRIMARY KEY (`id`)," + "UNIQUE KEY `mqtt_username` (`username`)" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4">>). + +-define(INIT_AUTH, <<"INSERT INTO mqtt_user (id, is_superuser, username, password, salt)" + "VALUES (1, 1, 'plain', 'plain', 'salt')," + "(2, 0, 'md5', '1bc29b36f623ba82aaf6724fd3b16718', 'salt')," + "(3, 0, 'sha', 'd8f4590320e1343a915b6394170650a8f35d6926', 'salt')," + "(4, 0, 'sha256', '5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e', 'salt')," + "(5, 0, 'pbkdf2_password', 'cdedb5281bb2f801565a1122b2563515', 'ATHENA.MIT.EDUraeburn')," + "(6, 0, 'bcrypt_foo', '$2a$12$sSS8Eg.ovVzaHzi1nUHYK.HbUIOdlQI0iS22Q5rd5z.JVVYH6sfm6', '$2a$12$sSS8Eg.ovVzaHzi1nUHYK.')," + "(7, 0, 'bcrypt', '$2y$16$rEVsDarhgHYB0TGnDFJzyu5f.T.Ha9iXMTk9J36NCMWWM7O16qyaK', 'salt')," + "(8, 0, 'bcrypt_wrong', '$2y$16$rEVsDarhgHYB0TGnDFJzyu', 'salt')">>). + +%%-------------------------------------------------------------------- +%% Setups +%%-------------------------------------------------------------------- + +all() -> + emqx_ct:all(?MODULE). + +init_per_suite(Cfg) -> + emqx_ct_helpers:start_apps([emqx_auth_mysql], fun set_special_configs/1), + init_mysql_data(), + Cfg. + +end_per_suite(_) -> + deinit_mysql_data(), + emqx_ct_helpers:stop_apps([emqx_auth_mysql]), + ok. + +set_special_configs(emqx) -> + application:set_env(emqx, allow_anonymous, false), + application:set_env(emqx, enable_acl_cache, false), + application:set_env(emqx, plugins_loaded_file, + emqx_ct_helpers:deps_path(emqx, "test/emqx_SUITE_data/loaded_plugins")); + +set_special_configs(_App) -> + ok. + +init_mysql_data() -> + {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?APP})), + %% Users + ok = mysql:query(Pid, ?DROP_AUTH_TABLE), + ok = mysql:query(Pid, ?CREATE_AUTH_TABLE), + ok = mysql:query(Pid, ?INIT_AUTH), + + %% ACLs + ok = mysql:query(Pid, ?DROP_ACL_TABLE), + ok = mysql:query(Pid, ?CREATE_ACL_TABLE), + ok = mysql:query(Pid, ?INIT_ACL). + +deinit_mysql_data() -> + {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?APP})), + ok = mysql:query(Pid, ?DROP_AUTH_TABLE), + ok = mysql:query(Pid, ?DROP_ACL_TABLE). + +%%-------------------------------------------------------------------- +%% Test cases +%%-------------------------------------------------------------------- + +t_check_acl(_) -> + User0 = #{zone => external,peerhost => {127,0,0,1}}, + allow = emqx_access_control:check_acl(User0, subscribe, <<"t1">>), + User1 = #{zone => external, clientid => <<"c1">>, username => <<"u1">>, peerhost => {127,0,0,1}}, + User2 = #{zone => external, clientid => <<"c2">>, username => <<"u2">>, peerhost => {127,0,0,1}}, + allow = emqx_access_control:check_acl(User1, subscribe, <<"t1">>), + deny = emqx_access_control:check_acl(User2, subscribe, <<"t1">>), + + User3 = #{zone => external, peerhost => {10,10,0,110}, clientid => <<"c1">>, username => <<"u1">>}, + User4 = #{zone => external, peerhost => {10,10,10,110}, clientid => <<"c1">>, username => <<"u1">>}, + allow = emqx_access_control:check_acl(User3, subscribe, <<"t1">>), + allow = emqx_access_control:check_acl(User3, subscribe, <<"t1">>), + allow = emqx_access_control:check_acl(User3, subscribe, <<"t2">>),%% nomatch -> ignore -> emqx acl + allow = emqx_access_control:check_acl(User4, subscribe, <<"t1">>),%% nomatch -> ignore -> emqx acl + User5 = #{zone => external, peerhost => {127,0,0,1}, clientid => <<"c3">>, username => <<"u3">>}, + allow = emqx_access_control:check_acl(User5, subscribe, <<"t1">>), + allow = emqx_access_control:check_acl(User5, publish, <<"t1">>). + +t_acl_super(_Config) -> + reload([{password_hash, plain}, + {auth_query, "select password from mqtt_user where username = '%u' limit 1"}]), + {ok, C} = emqtt:start_link([{host, "localhost"}, + {clientid, <<"simpleClient">>}, + {username, <<"plain">>}, + {password, <<"plain">>}]), + {ok, _} = emqtt:connect(C), + timer:sleep(10), + emqtt:subscribe(C, <<"TopicA">>, qos2), + timer:sleep(1000), + emqtt:publish(C, <<"TopicA">>, <<"Payload">>, qos2), + timer:sleep(1000), + receive + {publish, #{payload := Payload}} -> + ?assertEqual(<<"Payload">>, Payload) + after + 1000 -> + ct:fail({receive_timeout, <<"Payload">>}), + ok + end, + emqtt:disconnect(C). + +t_check_auth(_) -> + Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external}, + Md5 = #{clientid => <<"md5">>, username => <<"md5">>, zone => external}, + Sha = #{clientid => <<"sha">>, username => <<"sha">>, zone => external}, + Sha256 = #{clientid => <<"sha256">>, username => <<"sha256">>, zone => external}, + Pbkdf2 = #{clientid => <<"pbkdf2_password">>, username => <<"pbkdf2_password">>, zone => external}, + BcryptFoo = #{clientid => <<"bcrypt_foo">>, username => <<"bcrypt_foo">>, zone => external}, + User1 = #{clientid => <<"bcrypt_foo">>, username => <<"user">>, zone => external}, + Bcrypt = #{clientid => <<"bcrypt">>, username => <<"bcrypt">>, zone => external}, + BcryptWrong = #{clientid => <<"bcrypt_wrong">>, username => <<"bcrypt_wrong">>, zone => external}, + reload([{password_hash, plain}]), + {ok,#{is_superuser := true}} = + emqx_access_control:authenticate(Plain#{password => <<"plain">>}), + reload([{password_hash, md5}]), + {ok,#{is_superuser := false}} = + emqx_access_control:authenticate(Md5#{password => <<"md5">>}), + reload([{password_hash, sha}]), + {ok,#{is_superuser := false}} = + emqx_access_control:authenticate(Sha#{password => <<"sha">>}), + reload([{password_hash, sha256}]), + {ok,#{is_superuser := false}} = + emqx_access_control:authenticate(Sha256#{password => <<"sha256">>}), + reload([{password_hash, bcrypt}]), + {ok,#{is_superuser := false}} = + emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}), + {error, not_authorized} = + emqx_access_control:authenticate(BcryptWrong#{password => <<"password">>}), + %%pbkdf2 sha + reload([{password_hash, {pbkdf2, sha, 1, 16}}, + {auth_query, "select password, salt from mqtt_user where username = '%u' limit 1"}]), + {ok,#{is_superuser := false}} = + emqx_access_control:authenticate(Pbkdf2#{password => <<"password">>}), + reload([{password_hash, {salt, bcrypt}}]), + {ok,#{is_superuser := false}} = + emqx_access_control:authenticate(BcryptFoo#{password => <<"foo">>}), + {error, _} = emqx_access_control:authenticate(User1#{password => <<"foo">>}), + {error, not_authorized} = emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}). + +t_comment_config(_) -> + application:stop(?APP), + [application:unset_env(?APP, Par) || Par <- [acl_query, auth_query]], + application:start(?APP). + +t_placeholders(_) -> + ClientA = #{username => <<"plain">>, clientid => <<"plain">>, zone => external}, + + reload([{password_hash, plain}, + {auth_query, "select password from mqtt_user where username = '%u' and 'a_cn_val' = '%C' limit 1"}]), + {error, not_authorized} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>}), + {error, not_authorized} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>, cn => undefined}), + {ok, _} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>, cn => <<"a_cn_val">>}), + + reload([{auth_query, "select password from mqtt_user where username = '%c' and 'a_dn_val' = '%d' limit 1"}]), + {error, not_authorized} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>}), + {error, not_authorized} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>, dn => undefined}), + {ok, _} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>, dn => <<"a_dn_val">>}), + + reload([{auth_query, "select password from mqtt_user where username = '%u' and '192.168.1.5' = '%a' limit 1"}]), + {error, not_authorized} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>}), + {ok, _} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>, peerhost => {192,168,1,5}}). + +%%-------------------------------------------------------------------- +%% Internal funcs +%%-------------------------------------------------------------------- + +reload(Config) when is_list(Config) -> + application:stop(?APP), + [application:set_env(?APP, K, V) || {K, V} <- Config], + application:start(?APP). diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca-key.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca-key.pem new file mode 100644 index 000000000..e9717011e --- /dev/null +++ b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA0kGUBi9NDp65jgdxKfizIfuSr2wpwb44yM9SuP4oUQSULOA2 +4iFpLR/c5FAYHU81y9Vx91dQjdZfffaBZuv2zVvteXUkol8Nez7boKbo2E41MTew +8edtNKZAQVvnaHAC2NCZxjchCzUCDEoUUcl+cIERZ8R48FBqK5iTVcMRIx1akwus ++dhBqP0ykA5TGOWZkJrLM9aUXSPQha9+wXlOpkvu0Ur2nkX8PPJnifWao9UShSar +ll1IqPZNCSlZMwcFYcQNBCpdvITUUYlHvMRQV64bUpOxUGDuJkQL3dLKBlNuBRlJ +BcjBAKw7rFnwwHZcMmQ9tan/dZzpzwjo/T0XjwIDAQABAoIBAQCSHvUqnzDkWjcG +l/Fzg92qXlYBCCC0/ugj1sHcwvVt6Mq5rVE3MpUPwTcYjPlVVTlD4aEEjm/zQuq2 +ddxUlOS+r4aIhHrjRT/vSS4FpjnoKeIZxGR6maVxk6DQS3i1QjMYT1CvSpzyVvKH +a+xXMrtmoKxh+085ZAmFJtIuJhUA2yEa4zggCxWnvz8ecLClUPfVDPhdLBHc3KmL +CRpHEC6L/wanvDPRdkkzfKyaJuIJlTDaCg63AY5sDkTW2I57iI/nJ3haSeidfQKz +39EfbnM1A/YprIakafjAu3frBIsjBVcxwGihZmL/YriTHjOggJF841kT5zFkkv2L +/530Wk6xAoGBAOqZLZ4DIi/zLndEOz1mRbUfjc7GQUdYplBnBwJ22VdS0P4TOXnd +UbJth2MA92NM7ocTYVFl4TVIZY/Y+Prxk7KQdHWzR7JPpKfx9OEVgtSqV0vF9eGI +rKp79Y1T4Mvc3UcQCXX6TP7nHLihEzpS8odm2LW4txrOiLsn4Fq/IWrLAoGBAOVv +6U4tm3lImotUupKLZPKEBYwruo9qRysoug9FiorP4TjaBVOfltiiHbAQD6aGfVtN +SZpZZtrs17wL7Xl4db5asgMcZd+8Hkfo5siR7AuGW9FZloOjDcXb5wCh9EvjJ74J +Cjw7RqyVymq9t7IP6wnVwj5Ck48YhlOZCz/mzlnNAoGAWq7NYFgLvgc9feLFF23S +IjpJQZWHJEITP98jaYNxbfzYRm49+GphqxwFinKULjFNvq7yHlnIXSVYBOu1CqOZ +GRwXuGuNmlKI7lZr9xmukfAqgGLMMdr4C4qRF4lFyufcLRz42z7exmWlx4ST/yaT +E13hBRWayeTuG5JFei6Jh1MCgYEAqmX4LyC+JFBgvvQZcLboLRkSCa18bADxhENG +FAuAvmFvksqRRC71WETmqZj0Fqgxt7pp3KFjO1rFSprNLvbg85PmO1s+6fCLyLpX +lESTu2d5D71qhK93jigooxalGitFm+SY3mzjq0/AOpBWOn+J/w7rqVPGxXLgaHv0 +l+vx+00CgYBOvo9/ImjwYii2jFl+sHEoCzlvpITi2temRlT2j6ulSjCLJgjwEFw9 +8e+vvfQumQOsutakUVyURrkMGNDiNlIv8kv5YLCCkrwN22E6Ghyi69MJUvHQXkc/ +QZhjn/luyfpB5f/BeHFS2bkkxAXo+cfG45ApY3Qfz6/7o+H+vDa6/A== +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca.pem new file mode 100644 index 000000000..00b31d8a4 --- /dev/null +++ b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDAzCCAeugAwIBAgIBATANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR +TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X +DTIwMDYxMTAzMzg0NloXDTMwMDYwOTAzMzg0NlowPDE6MDgGA1UEAwwxTXlTUUxf +U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9DQV9DZXJ0aWZpY2F0ZTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANJBlAYvTQ6euY4HcSn4syH7kq9s +KcG+OMjPUrj+KFEElCzgNuIhaS0f3ORQGB1PNcvVcfdXUI3WX332gWbr9s1b7Xl1 +JKJfDXs+26Cm6NhONTE3sPHnbTSmQEFb52hwAtjQmcY3IQs1AgxKFFHJfnCBEWfE +ePBQaiuYk1XDESMdWpMLrPnYQaj9MpAOUxjlmZCayzPWlF0j0IWvfsF5TqZL7tFK +9p5F/DzyZ4n1mqPVEoUmq5ZdSKj2TQkpWTMHBWHEDQQqXbyE1FGJR7zEUFeuG1KT +sVBg7iZEC93SygZTbgUZSQXIwQCsO6xZ8MB2XDJkPbWp/3Wc6c8I6P09F48CAwEA +AaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEADKz6bIpP5anp +GgLB0jkclRWuMlS4qqIt4itSsMXPJ/ezpHwECixmgW2TIQl6S1woRkUeMxhT2/Ay +Sn/7aKxuzRagyE5NEGOvrOuAP5RO2ZdNJ/X3/Rh533fK1sOTEEbSsWUvW6iSkZef +rsfZBVP32xBhRWkKRdLeLB4W99ADMa0IrTmZPCXHSSE2V4e1o6zWLXcOZeH1Qh8N +SkelBweR+8r1Fbvy1r3s7eH7DCbYoGEDVLQGOLvzHKBisQHmoDnnF5E9g1eeNRdg +o+vhOKfYCOzeNREJIqS42PHcGhdNRk90ycigPmfUJclz1mDHoMjKR2S5oosTpr65 +tNPx3CL7GA== +-----END CERTIFICATE----- diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-cert.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-cert.pem new file mode 100644 index 000000000..aad1404ca --- /dev/null +++ b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBDCCAeygAwIBAgIBAzANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR +TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X +DTIwMDYxMTAzMzg0N1oXDTMwMDYwOTAzMzg0N1owQDE+MDwGA1UEAww1TXlTUUxf +U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9DbGllbnRfQ2VydGlmaWNhdGUw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVYSWpOvCTupz82fc85Opv +EQ7rkB8X2oOMyBCpkyHKBIr1ZQgRDWBp9UVOASq3GnSElm6+T3Kb1QbOffa8GIlw +sjAueKdq5L2eSkmPIEQ7eoO5kEW+4V866hE1LeL/PmHg2lGP0iqZiJYtElhHNQO8 +3y9I7cm3xWMAA3SSWikVtpJRn3qIp2QSrH+tK+/HHbE5QwtPxdir4ULSCSOaM5Yh +Wi5Oto88TZqe1v7SXC864JVvO4LuS7TuSreCdWZyPXTJFBFeCEWSAxonKZrqHbBe +CwKML6/0NuzjaQ51c2tzmVI6xpHj3nnu4cSRx6Jf9WBm+35vm0wk4pohX3ptdzeV +AgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAByQ5zSNeFUH +Aw7JlpZHtHaSEeiiyBHke20ziQ07BK1yi/ms2HAWwQkpZv149sjNuIRH8pkTmkZn +g8PDzSefjLbC9AsWpWV0XNV22T/cdobqLqMBDDZ2+5bsV+jTrOigWd9/AHVZ93PP +IJN8HJn6rtvo2l1bh/CdsX14uVSdofXnuWGabNTydqtMvmCerZsdf6qKqLL+PYwm +RDpgWiRUY7KPBSSlKm/9lJzA+bOe4dHeJzxWFVCJcbpoiTFs1je1V8kKQaHtuW39 +ifX6LTKUMlwEECCbDKM8Yq2tm8NjkjCcnFDtKg8zKGPUu+jrFMN5otiC3wnKcP7r +O9EkaPcgYH8= +-----END CERTIFICATE----- diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-key.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-key.pem new file mode 100644 index 000000000..6789d0291 --- /dev/null +++ b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA1WElqTrwk7qc/Nn3POTqbxEO65AfF9qDjMgQqZMhygSK9WUI +EQ1gafVFTgEqtxp0hJZuvk9ym9UGzn32vBiJcLIwLninauS9nkpJjyBEO3qDuZBF +vuFfOuoRNS3i/z5h4NpRj9IqmYiWLRJYRzUDvN8vSO3Jt8VjAAN0klopFbaSUZ96 +iKdkEqx/rSvvxx2xOUMLT8XYq+FC0gkjmjOWIVouTraPPE2antb+0lwvOuCVbzuC +7ku07kq3gnVmcj10yRQRXghFkgMaJyma6h2wXgsCjC+v9Dbs42kOdXNrc5lSOsaR +49557uHEkceiX/VgZvt+b5tMJOKaIV96bXc3lQIDAQABAoIBAF7yjXmSOn7h6P0y +WCuGiTLG2mbDiLJqj2LTm2Z5i+2Cu/qZ7E76Ls63TxF4v3MemH5vGfQhEhR5ZD/6 +GRJ1sKKvB3WGRqjwA9gtojHH39S/nWGy6vYW/vMOOH37XyjIr3EIdIaUtFQBTSHd +Kd71niYrAbVn6fyWHolhADwnVmTMOl5OOAhCdEF4GN3b5aIhIu8BJ7EUzTtHBJIj +CAEfjZFjDs1y1cIgGFJkuIQxMfCpq5recU2qwip7YO6fk//WEjOPu7kSf5IEswL8 +jg1dea9rGBV6KaD2xsgsC6Ll6Sb4BbsrHMfflG3K2Lk3RdVqqTFp1Fn1PTLQE/1S +S/SZPYECgYEA9qYcHKHd0+Q5Ty5wgpxKGa4UCWkpwvfvyv4bh8qlmxueB+l2AIdo +ZvkM8gTPagPQ3WypAyC2b9iQu70uOJo1NizTtKnpjDdN1YpDjISJuS/P0x73gZwy +gmoM5AzMtN4D6IbxXtXnPaYICvwLKU80ouEN5ZPM4/ODLUu6gsp0v2UCgYEA3Xgi +zMC4JF0vEKEaK0H6QstaoXUmw/lToZGH3TEojBIkb/2LrHUclygtONh9kJSFb89/ +jbmRRLAOrx3HZKCNGUmF4H9k5OQyAIv6OGBinvLGqcbqnyNlI+Le8zxySYwKMlEj +EMrBCLmSyi0CGFrbZ3mlj/oCET/ql9rNvcK+DHECgYAEx5dH3sMjtgp+RFId1dWB +xePRgt4yTwewkVgLO5wV82UOljGZNQaK6Eyd7AXw8f38LHzh+KJQbIvxd2sL4cEi +OaAoohpKg0/Y0YMZl//rPMf0OWdmdZZs/I0fZjgZUSwWN3c59T8z7KG/RL8an9RP +S7kvN7wCttdV61/D5RR6GQKBgDxCe/WKWpBKaovzydMLWLTj7/0Oi0W3iXHkzzr4 +LTgvl4qBSofaNbVLUUKuZTv5rXUG2IYPf99YqCYtzBstNDc1MiAriaBeFtzfOW4t +i6gEFtoLLbuvPc3N5Sv5vn8Ug5G9UfU3td5R4AbyyCcoUZqOFuZd+EIJSiOXfXOs +kVmBAoGBAIU9aPAqhU5LX902oq8KsrpdySONqv5mtoStvl3wo95WIqXNEsFY60wO +q02jKQmJJ2MqhkJm2EoF2Mq8+40EZ5sz8LdgeQ/M0yQ9lAhPi4rftwhpe55Ma9dk +SE9X1c/DMCBEaIjJqVXdy0/EeArwpb8sHkguVVAZUWxzD+phm1gs +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/private_key.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/private_key.pem new file mode 100644 index 000000000..8fbf6bdec --- /dev/null +++ b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/private_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA1zVmMhPqpSPMmYkKh5wwlRD5XuS8YWJKEM6tjFx61VK8qxHE +YngkC2KnL5EuKAjQZIF3tJskwt0hAat047CCCZxrkNEpbVvSnvnk+A/8bg/Ww1n3 +qxzfifhsWfpUKlDnwrtH+ftt+5rZeEkf37XAPy7ZjzecAF9SDV6WSiPeAxUX2+hN +dId42Pf45woo4LFGUlQeagCFkD/R0dpNIMGwcnkKCUikiBqr2ijSIgvRtBfZ9fBG +jFGER2uE/Eay4AgcQsHue8skRwDCng8OnqtPnBtTytmqTy9V/BRgsVKUoksm6wsx +kUYwgHeaq7UCvlCm25SZ7yRyd4k8t0BKDf2h+wIDAQABAoIBAEQcrHmRACTADdNS +IjkFYALt2l8EOfMAbryfDSJtapr1kqz59JPNvmq0EIHnixo0n/APYdmReLML1ZR3 +tYkSpjVwgkLVUC1CcIjMQoGYXaZf8PLnGJHZk45RR8m6hsTV0mQ5bfBaeVa2jbma +OzJMjcnxg/3l9cPQZ2G/3AUfEPccMxOXp1KRz3mUQcGnKJGtDbN/kfmntcwYoxaE +Zg4RoeKAoMpK1SSHAiJKe7TnztINJ7uygR9XSzNd6auY8A3vomSIjpYO7XL+lh7L +izm4Ir3Gb/eCYBvWgQyQa2KCJgK/sQyEs3a09ngofSEUhQJQYhgZDwUj+fDDOGqj +hCZOA8ECgYEA+ZWuHdcUQ3ygYhLds2QcogUlIsx7C8n/Gk/FUrqqXJrTkuO0Eqqa +B47lCITvmn2zm0ODfSFIARgKEUEDLS/biZYv7SUTrFqBLcet+aGI7Dpv91CgB75R +tNzcIf8VxoiP0jPqdbh9mLbbxGi5Uc4p9TVXRljC4hkswaouebWee0sCgYEA3L2E +YB3kiHrhPI9LHS5Px9C1w+NOu5wP5snxrDGEgaFCvL6zgY6PflacppgnmTXl8D1x +im0IDKSw5dP3FFonSVXReq3CXDql7UnhfTCiLDahV7bLxTH42FofcBpDN3ERdOal +58RwQh6VrLkzQRVoObo+hbGlFiwwSAfQC509FhECgYBsRSBpVXo25IN2yBRg09cP ++gdoFyhxrsj5kw1YnB13WrrZh+oABv4WtUhp77E5ZbpaamlKCPwBbXpAjeFg4tfr +0bksuN7V79UGFQ9FsWuCfr8/nDwv38H2IbFlFhFONMOfPmJBey0Q6JJhm8R41mSh +OOiJXcv85UrjIH5U0hLUDQKBgQDVLOU5WcUJlPoOXSgiT0ZW5xWSzuOLRUUKEf6l +19BqzAzCcLy0orOrRAPW01xylt2v6/bJw1Ahva7k1ZZo/kOwjANYoZPxM+ZoSZBN +MXl8j2mzZuJVV1RFxItV3NcLJNPB/Lk+IbRz9kt/2f9InF7iWR3mSU/wIM6j0X+2 +p6yFsQKBgQCM/ldWb511lA+SNkqXB2P6WXAgAM/7+jwsNHX2ia2Ikufm4SUEKMSv +mti/nZkHDHsrHU4wb/2cOAywMELzv9EHzdcoenjBQP65OAc/1qWJs+LnBcCXfqKk +aHjEZW6+brkHdRGLLY3YAHlt/AUL+RsKPJfN72i/FSpmu+52G36eeQ== +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/public_key.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/public_key.pem new file mode 100644 index 000000000..f9772b533 --- /dev/null +++ b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/public_key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1zVmMhPqpSPMmYkKh5ww +lRD5XuS8YWJKEM6tjFx61VK8qxHEYngkC2KnL5EuKAjQZIF3tJskwt0hAat047CC +CZxrkNEpbVvSnvnk+A/8bg/Ww1n3qxzfifhsWfpUKlDnwrtH+ftt+5rZeEkf37XA +Py7ZjzecAF9SDV6WSiPeAxUX2+hNdId42Pf45woo4LFGUlQeagCFkD/R0dpNIMGw +cnkKCUikiBqr2ijSIgvRtBfZ9fBGjFGER2uE/Eay4AgcQsHue8skRwDCng8OnqtP +nBtTytmqTy9V/BRgsVKUoksm6wsxkUYwgHeaq7UCvlCm25SZ7yRyd4k8t0BKDf2h ++wIDAQAB +-----END PUBLIC KEY----- diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/server-cert.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/server-cert.pem new file mode 100644 index 000000000..a2f9688df --- /dev/null +++ b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/server-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBDCCAeygAwIBAgIBAjANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR +TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X +DTIwMDYxMTAzMzg0NloXDTMwMDYwOTAzMzg0NlowQDE+MDwGA1UEAww1TXlTUUxf +U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9TZXJ2ZXJfQ2VydGlmaWNhdGUw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcEnEm5hqP1EbEJycOz8Ua +NWp29QdpFUzTWhkKGhVXk+0msmNTw4NBAFB42moY44OU8wvDideOlJNhPRWveD8z +G2lxzJA91p0UK4et8ia9MmeuCGhdC9jxJ8X69WNlUiPyy0hI/ZsqRq9Z0C2eW0iL +JPXsy4X8Xpw3SFwoXf5pR9RFY5Pb2tuyxqmSestu2VXT/NQjJg4CVDR3mFcHPXZB +4elRzH0WshExEGkgy0bg20MJeRc2Qdb5Xx+EakbmwroDWaCn3NSGqQ7jv6Vw0doy +TGvS6h6RHBxnyqRfRgKGlCoOMG9/5+rFJC00QpCUG2vHXHWGoWlMlJ3foN7rj5v9 +AgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAJ5zt2rj4Ag6 +zpN59AWC1Fur8g8l41ksHkSpKPp+PtyO/ngvbMqBpfmK1e7JCKZv/68QXfMyWWAI +hwalqZkXXWHKjuz3wE7dE25PXFXtGJtcZAaj10xt98fzdqt8lQSwh2kbfNwZIz1F +sgAStgE7+ZTcqTgvNB76Os1UK0to+/P0VBWktaVFdyub4Nc2SdPVnZNvrRBXBwOD +3V8ViwywDOFoE7DvCvwx/SVsvoC0Z4j3AMMovO6oHicP7uU83qsQgm1Qru3YeoLR ++DoVi7IPHbWvN7MqFYn3YjNlByO2geblY7MR0BlqbFlmFrqLsUfjsh2ys7/U/knC +dN/klu446fI= +-----END CERTIFICATE----- diff --git a/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/server-key.pem b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/server-key.pem new file mode 100644 index 000000000..a1dfd5f78 --- /dev/null +++ b/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/server-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAnBJxJuYaj9RGxCcnDs/FGjVqdvUHaRVM01oZChoVV5PtJrJj +U8ODQQBQeNpqGOODlPMLw4nXjpSTYT0Vr3g/MxtpccyQPdadFCuHrfImvTJnrgho +XQvY8SfF+vVjZVIj8stISP2bKkavWdAtnltIiyT17MuF/F6cN0hcKF3+aUfURWOT +29rbssapknrLbtlV0/zUIyYOAlQ0d5hXBz12QeHpUcx9FrIRMRBpIMtG4NtDCXkX +NkHW+V8fhGpG5sK6A1mgp9zUhqkO47+lcNHaMkxr0uoekRwcZ8qkX0YChpQqDjBv +f+fqxSQtNEKQlBtrx1x1hqFpTJSd36De64+b/QIDAQABAoIBAFiah66Dt9SruLkn +WR8piUaFyLlcBib8Nq9OWSTJBhDAJERxxb4KIvvGB+l0ZgNXNp5bFPSfzsZdRwZP +PX5uj8Kd71Dxx3mz211WESMJdEC42u+MSmN4lGLkJ5t/sDwXU91E1vbJM0ve8THV +4/Ag9qA4DX2vVZOeyqT/6YHpSsPNZplqzrbAiwrfHwkctHfgqwOf3QLfhmVQgfCS +VwidBldEUv2whSIiIxh4Rv5St4kA68IBCbJxdpOpyuQBkk6CkxZ7VN9FqOuSd4Pk +Wm7iWyBMZsCmELZh5XAXld4BEt87C5R4CvbPBDZxAv3THk1DNNvpy3PFQfwARRFb +SAToYMECgYEAyL7U8yxpzHDYWd3oCx6vTi9p9N/z0FfAkWrRF6dm4UcSklNiT1Aq +EOnTA+SaW8tV3E64gCWcY23gNP8so/ZseWj6L+peHwtchaP9+KB7yGw2A+05+lOx +VetLTjAOmfpiUXFe5w1q4C1RGhLjZjjzW+GvwdAuchQgUEFaomrV+PUCgYEAxwfH +cmVGFbAktcjU4HSRjKSfawCrut+3YUOLybyku3Q/hP9amG8qkVTFe95CTLjLe2D0 +ccaTTpofFEJ32COeck0g0Ujn/qQ+KXRoauOYs4FB1DtqMpqB78wufWEUpDpbd9/h +J+gJdC/IADd4tJW9zA92g8IA7ZtFmqDtiSpQ0ekCgYAQGkaorvJZpN+l7cf0RGTZ +h7IfI2vCVZer0n6tQA9fmLzjoe6r4AlPzAHSOR8sp9XeUy43kUzHKQQoHCPvjw/K +eWJAP7OHF/k2+x2fOPhU7mEy1W+mJdp+wt4Kio5RSaVjVQ3AyPG+w8PSrJszEvRq +dWMMz+851WV2KpfjmWBKlQKBgQC++4j4DZQV5aMkSKV1CIZOBf3vaIJhXKEUFQPD +PmB4fBEjpwCg+zNGp6iktt65zi17o8qMjrb1mtCt2SY04eD932LZUHNFlwcLMmes +Ad+aiDLJ24WJL1f16eDGcOyktlblDZB5gZ/ovJzXEGOkLXglosTfo77OQculmDy2 +/UL2WQKBgGeKasmGNfiYAcWio+KXgFkHXWtAXB9B91B1OFnCa40wx+qnl71MIWQH +PQ/CZFNWOfGiNEJIZjrHsfNJoeXkhq48oKcT0AVCDYyLV0VxDO4ejT95mGW6njNd +JpvmhwwAjOvuWVr0tn4iXlSK8irjlJHmwcRjLTJq97vE9fsA2MjI +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_pgsql/.ci/docker-compose.yml b/apps/emqx_auth_pgsql/.ci/docker-compose.yml new file mode 100644 index 000000000..8782a841d --- /dev/null +++ b/apps/emqx_auth_pgsql/.ci/docker-compose.yml @@ -0,0 +1,30 @@ +version: '3' + +services: + erlang: + image: erlang:22.3 + volumes: + - ../:/emqx_auth_pgsql + networks: + - emqx_bridge + depends_on: + - pgsql_server + tty: true + + pgsql_server: + build: + context: ./pgsql + args: + BUILD_FROM: postgres:${PGSQL_TAG} + image: emqx-pgsql + restart: always + environment: + POSTGRES_PASSWORD: public + POSTGRES_USER: root + POSTGRES_DB: mqtt + networks: + - emqx_bridge + +networks: + emqx_bridge: + driver: bridge diff --git a/apps/emqx_auth_pgsql/.ci/pgsql/Dockerfile b/apps/emqx_auth_pgsql/.ci/pgsql/Dockerfile new file mode 100644 index 000000000..785bb875f --- /dev/null +++ b/apps/emqx_auth_pgsql/.ci/pgsql/Dockerfile @@ -0,0 +1,8 @@ +ARG BUILD_FROM=postgres:11 +FROM ${BUILD_FROM} +COPY pg.conf /etc/postgresql/postgresql.conf +COPY server-cert.pem /etc/postgresql/server-cert.pem +COPY server-key.pem /etc/postgresql/server-key.pem +RUN chown -R postgres:postgres /etc/postgresql \ + && chmod 600 /etc/postgresql/*.pem +CMD ["-c", "config_file=/etc/postgresql/postgresql.conf"] diff --git a/apps/emqx_auth_pgsql/.github/workflows/run_test_cases.yaml b/apps/emqx_auth_pgsql/.github/workflows/run_test_cases.yaml new file mode 100644 index 000000000..389359fb3 --- /dev/null +++ b/apps/emqx_auth_pgsql/.github/workflows/run_test_cases.yaml @@ -0,0 +1,51 @@ +name: Run test cases + +on: [push, pull_request] + +jobs: + run_test_cases: + runs-on: ubuntu-latest + + strategy: + matrix: + pgsql_tag: + - 9 + - 10 + - 11 + - 12 + - 13 + network_type: + - ipv4 + - ipv6 + steps: + - name: install docker-compose + run: | + sudo curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + - uses: actions/checkout@v1 + - name: run test cases + env: + PGSQL_TAG: ${{ matrix.pgsql_tag }} + NETWORK_TYPE: ${{ matrix.network_type }} + run: | + set -e -u -x + if [ "$NETWORK_TYPE" = "ipv6" ]; then docker network create --driver bridge --ipv6 --subnet fd15:555::/64 tests_emqx_bridge --attachable; fi + + cp test/emqx_auth_pgsql_SUITE_data/* .ci/pgsql/ + docker-compose -f .ci/docker-compose.yml -p tests up -d --build + if [ "$NETWORK_TYPE" != "ipv6" ]; then + docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "sed -i '/auth.pgsql.server/c auth.pgsql.server = pgsql_server:5432' /emqx_auth_pgsql/etc/emqx_auth_pgsql.conf" + else + ipv6_address=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' $(docker ps -a -f name=tests_pgsql_server_1 -q)) + docker exec -i $(docker ps -a -f name=tests_erlang_1 -q) sh -c "sed -i '/auth.pgsql.server/c auth.pgsql.server = $ipv6_address:5432' /emqx_auth_pgsql/etc/emqx_auth_pgsql.conf" + fi + + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_pgsql xref" + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_pgsql eunit" + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_pgsql ct" + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_pgsql cover" + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: logs_for_pgsql${{ matrix.pgsql_tag }}_{{matrix.network_type}} + path: _build/test/logs diff --git a/apps/emqx_auth_pgsql/.gitignore b/apps/emqx_auth_pgsql/.gitignore new file mode 100644 index 000000000..672d34c0c --- /dev/null +++ b/apps/emqx_auth_pgsql/.gitignore @@ -0,0 +1,20 @@ +ebin +.rebar +.eunit +.DS_Store +.erlang.mk/ +deps/ +emqx_auth_pgsql.d +ct.coverdata +logs/ +test/ct.cover.spec +test/*.beam +data/ +.DS_Store +cover/ +eunit.coverdata +_build/ +rebar.lock +erlang.mk +*.conf.rendered +.rebar3/ diff --git a/apps/emqx_auth_pgsql/LICENSE b/apps/emqx_auth_pgsql/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/apps/emqx_auth_pgsql/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/apps/emqx_auth_pgsql/README.md b/apps/emqx_auth_pgsql/README.md new file mode 100644 index 000000000..2dccd6f53 --- /dev/null +++ b/apps/emqx_auth_pgsql/README.md @@ -0,0 +1,183 @@ +emqx_auth_pgsql +=============== + +Authentication/ACL with PostgreSQL Database. + +Build Plugin +------------ + +make && make tests + +Configuration +------------- + +File: etc/emqx_auth_pgsql.conf + +``` +## PostgreSQL server address. +## +## Value: Port | IP:Port +## +## Examples: 5432, 127.0.0.1:5432, localhost:5432 +auth.pgsql.server = 127.0.0.1:5432 + +## PostgreSQL pool size. +## +## Value: Number +auth.pgsql.pool = 8 + +## PostgreSQL username. +## +## Value: String +auth.pgsql.username = root + +## PostgreSQL password. +## +## Value: String +## auth.pgsql.password = + +## PostgreSQL database. +## +## Value: String +auth.pgsql.database = mqtt + +## PostgreSQL database encoding. +## +## Value: String +auth.pgsql.encoding = utf8 + +## Whether to enable SSL connection. +## +## Value: true | false +auth.pgsql.ssl = false + +## SSL keyfile. +## +## Value: File +## auth.pgsql.ssl_opts.keyfile = + +## SSL certfile. +## +## Value: File +## auth.pgsql.ssl_opts.certfile = + +## SSL cacertfile. +## +## Value: File +## auth.pgsql.ssl_opts.cacertfile = + +## Authentication query. +## +## Value: SQL +## +## Variables: +## - %u: username +## - %c: clientid +## +auth.pgsql.auth_query = select password from mqtt_user where username = '%u' limit 1 + +## Password hash. +## +## Value: plain | md5 | sha | sha256 | bcrypt +auth.pgsql.password_hash = sha256 + +## sha256 with salt prefix +## auth.pgsql.password_hash = salt,sha256 + +## sha256 with salt suffix +## auth.pgsql.password_hash = sha256,salt + +## bcrypt with salt prefix +## auth.pgsql.password_hash = salt,bcrypt + +## pbkdf2 with macfun iterations dklen +## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 +## auth.pgsql.password_hash = pbkdf2,sha256,1000,20 + +## Superuser query. +## +## Value: SQL +## +## Variables: +## - %u: username +## - %c: clientid +auth.pgsql.super_query = select is_superuser from mqtt_user where username = '%u' limit 1 + +## ACL query. Comment this query, the ACL will be disabled. +## +## Value: SQL +## +## Variables: +## - %a: ipaddress +## - %u: username +## - %c: clientid +auth.pgsql.acl_query = select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c' +``` + +Load Plugin +----------- + +./bin/emqx_ctl plugins load emqx_auth_pgsql + +Auth Table +---------- + +Notice: This is a demo table. You could authenticate with any user table. + +```sql +CREATE TABLE mqtt_user ( + id SERIAL primary key, + is_superuser boolean, + username character varying(100), + password character varying(100), + salt character varying(40) +) +``` + +ACL Table +--------- + +```sql +CREATE TABLE mqtt_acl ( + id SERIAL primary key, + allow integer, + ipaddr character varying(60), + username character varying(100), + clientid character varying(100), + access integer, + topic character varying(100) +) + +INSERT INTO mqtt_acl (id, allow, ipaddr, username, clientid, access, topic) +VALUES + (1,1,NULL,'$all',NULL,2,'#'), + (2,0,NULL,'$all',NULL,1,'$SYS/#'), + (3,0,NULL,'$all',NULL,1,'eq #'), + (5,1,'127.0.0.1',NULL,NULL,2,'$SYS/#'), + (6,1,'127.0.0.1',NULL,NULL,2,'#'), + (7,1,NULL,'dashboard',NULL,1,'$SYS/#'); +``` +**allow:** Client's permission to access a topic. '0' means that the client does not have permission to access the topic, '1' means that the client have permission to access the topic. + +**ipaddr:** Client IP address. For all ip addresses it can be '$all' or 'NULL'. + +**username:** Client username. For all users it can be '$all' or 'NULL'. + +**clientid:** Client id. For all client ids it can be '$all' or 'NULL'. + +**access:** Operations that the client can perform. '1' means that the client can subscribe to a topic, '2' means that the client can publish to a topic, '3' means that the client can subscribe and can publish to a topic. + +**topic:** Topic name. Topic wildcards are supported. + +**Notice that only one value allowed for ipaddr, username and clientid fields.** + +License +------- + +Apache License Version 2.0 + +Author +------ + +EMQ X Team. + diff --git a/apps/emqx_auth_pgsql/etc/emqx_auth_pgsql.conf b/apps/emqx_auth_pgsql/etc/emqx_auth_pgsql.conf new file mode 100644 index 000000000..3e79d96d8 --- /dev/null +++ b/apps/emqx_auth_pgsql/etc/emqx_auth_pgsql.conf @@ -0,0 +1,110 @@ +##-------------------------------------------------------------------- +## PostgreSQL Auth/ACL Plugin +##-------------------------------------------------------------------- + +## PostgreSQL server address. +## +## Value: Port | IP:Port +## +## Examples: 5432, 127.0.0.1:5432, localhost:5432 +auth.pgsql.server = 127.0.0.1:5432 + +## PostgreSQL pool size. +## +## Value: Number +auth.pgsql.pool = 8 + +## PostgreSQL username. +## +## Value: String +auth.pgsql.username = root + +## PostgreSQL password. +## +## Value: String +## auth.pgsql.password = + +## PostgreSQL database. +## +## Value: String +auth.pgsql.database = mqtt + +## PostgreSQL database encoding. +## +## Value: String +auth.pgsql.encoding = utf8 + +## Whether to enable SSL connection. +## +## Value: true | false +auth.pgsql.ssl = false + +## SSL keyfile. +## +## Value: File +## auth.pgsql.ssl_opts.keyfile = + +## SSL certfile. +## +## Value: File +## auth.pgsql.ssl_opts.certfile = + +## SSL cacertfile. +## +## Value: File +## auth.pgsql.ssl_opts.cacertfile = + +## Authentication query. +## +## Value: SQL +## +## Variables: +## - %u: username +## - %c: clientid +## - %C: common name of client TLS cert +## - %d: subject of client TLS cert +## +auth.pgsql.auth_query = select password from mqtt_user where username = '%u' limit 1 + +## Password hash. +## +## Value: plain | md5 | sha | sha256 | bcrypt +auth.pgsql.password_hash = sha256 + +## sha256 with salt prefix +## auth.pgsql.password_hash = salt,sha256 + +## sha256 with salt suffix +## auth.pgsql.password_hash = sha256,salt + +## bcrypt with salt prefix +## auth.pgsql.password_hash = salt,bcrypt + +## pbkdf2 with macfun iterations dklen +## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 +## auth.pgsql.password_hash = pbkdf2,sha256,1000,20 + +## Superuser query. +## +## Value: SQL +## +## Variables: +## - %u: username +## - %c: clientid +## - %C: common name of client TLS cert +## - %d: subject of client TLS cert +## +auth.pgsql.super_query = select is_superuser from mqtt_user where username = '%u' limit 1 + +## ACL query. Comment this query, the ACL will be disabled. +## +## Value: SQL +## +## Variables: +## - %a: ipaddress +## - %u: username +## - %c: clientid +## +## Note: You can add the 'ORDER BY' statement to control the rules match order +auth.pgsql.acl_query = select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c' + diff --git a/apps/emqx_auth_pgsql/include/emqx_auth_pgsql.hrl b/apps/emqx_auth_pgsql/include/emqx_auth_pgsql.hrl new file mode 100644 index 000000000..b86692752 --- /dev/null +++ b/apps/emqx_auth_pgsql/include/emqx_auth_pgsql.hrl @@ -0,0 +1,23 @@ +-define(APP, emqx_auth_pgsql). + +-record(auth_metrics, { + success = 'client.auth.success', + failure = 'client.auth.failure', + ignore = 'client.auth.ignore' + }). + +-record(acl_metrics, { + allow = 'client.acl.allow', + deny = 'client.acl.deny', + ignore = 'client.acl.ignore' + }). + +-define(METRICS(Type), tl(tuple_to_list(#Type{}))). +-define(METRICS(Type, K), #Type{}#Type.K). + +-define(AUTH_METRICS, ?METRICS(auth_metrics)). +-define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). + +-define(ACL_METRICS, ?METRICS(acl_metrics)). +-define(ACL_METRICS(K), ?METRICS(acl_metrics, K)). + diff --git a/apps/emqx_auth_pgsql/mqtt.sql b/apps/emqx_auth_pgsql/mqtt.sql new file mode 100644 index 000000000..933b0058a --- /dev/null +++ b/apps/emqx_auth_pgsql/mqtt.sql @@ -0,0 +1,28 @@ + +CREATE TABLE mqtt_user ( + id SERIAL primary key, + is_superuser boolean, + username character varying(100), + password character varying(100), + salt character varying(40) +); + +CREATE TABLE mqtt_acl ( + id SERIAL primary key, + allow integer, + ipaddr character varying(60), + username character varying(100), + clientid character varying(100), + access integer, + topic character varying(100) +); + +INSERT INTO mqtt_acl (id, allow, ipaddr, username, clientid, access, topic) +VALUES + (1,1,NULL,'$all',NULL,2,'#'), + (2,0,NULL,'$all',NULL,1,'$SYS/#'), + (3,0,NULL,'$all',NULL,1,'eq #'), + (5,1,'127.0.0.1',NULL,NULL,2,'$SYS/#'), + (6,1,'127.0.0.1',NULL,NULL,2,'#'), + (7,1,NULL,'dashboard',NULL,1,'$SYS/#'); + diff --git a/apps/emqx_auth_pgsql/priv/emqx_auth_pgsql.schema b/apps/emqx_auth_pgsql/priv/emqx_auth_pgsql.schema new file mode 100644 index 000000000..4a475ee0e --- /dev/null +++ b/apps/emqx_auth_pgsql/priv/emqx_auth_pgsql.schema @@ -0,0 +1,127 @@ +%%-*- mode: erlang -*- +%% emqx_auth_pgsl config mapping + +{mapping, "auth.pgsql.server", "emqx_auth_pgsql.server", [ + {default, {"127.0.0.1", 5432}}, + {datatype, [integer, ip, string]} +]}. + +{mapping, "auth.pgsql.pool", "emqx_auth_pgsql.server", [ + {default, 8}, + {datatype, integer} +]}. + +{mapping, "auth.pgsql.database", "emqx_auth_pgsql.server", [ + {datatype, string} +]}. + +{mapping, "auth.pgsql.username", "emqx_auth_pgsql.server", [ + {default, ""}, + {datatype, string} +]}. + +{mapping, "auth.pgsql.password", "emqx_auth_pgsql.server", [ + {default, ""}, + {datatype, string} +]}. + +{mapping, "auth.pgsql.encoding", "emqx_auth_pgsql.server", [ + {default, utf8}, + {datatype, atom} +]}. + +{mapping, "auth.pgsql.ssl", "emqx_auth_pgsql.server", [ + {default, false}, + {datatype, {enum, [true, false]}} +]}. + +{mapping, "auth.pgsql.ssl_opts.keyfile", "emqx_auth_pgsql.server", [ + {datatype, string} +]}. + +{mapping, "auth.pgsql.ssl_opts.certfile", "emqx_auth_pgsql.server", [ + {datatype, string} +]}. + +{mapping, "auth.pgsql.ssl_opts.cacertfile", "emqx_auth_pgsql.server", [ + {datatype, string} +]}. + +{translation, "emqx_auth_pgsql.server", fun(Conf) -> + {PgHost, PgPort} = + case cuttlefish:conf_get("auth.pgsql.server", Conf) of + {Ip, Port} -> {Ip, Port}; + S -> case string:tokens(S, ":") of + [Domain] -> {Domain, 5432}; + [Domain, Port] -> {Domain, list_to_integer(Port)} + end + end, + Pool = cuttlefish:conf_get("auth.pgsql.pool", Conf), + Username = cuttlefish:conf_get("auth.pgsql.username", Conf), + Passwd = cuttlefish:conf_get("auth.pgsql.password", Conf, ""), + DB = cuttlefish:conf_get("auth.pgsql.database", Conf), + Encoding = cuttlefish:conf_get("auth.pgsql.encoding", Conf), + Ssl = cuttlefish:conf_get("auth.pgsql.ssl", Conf), + + Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end, + SslOpts = fun(Prefix) -> + Filter([{keyfile, cuttlefish:conf_get(Prefix ++ ".keyfile", Conf, undefined)}, + {certfile, cuttlefish:conf_get(Prefix ++ ".certfile", Conf, undefined)}, + {cacertfile, cuttlefish:conf_get(Prefix ++ ".cacertfile", Conf, undefined)}]) + end, + + TempHost = case inet:parse_address(PgHost) of + {ok, IpAddr} -> + IpAddr; + _ -> + PgHost + end, + [{pool_size, Pool}, + {auto_reconnect, 1}, + {host, TempHost}, + {port, PgPort}, + {username, Username}, + {password, Passwd}, + {database, DB}, + {encoding, Encoding}, + {ssl, Ssl}, + {ssl_opts, SslOpts("auth.pgsql.ssl_opts")}] +end}. + +{mapping, "auth.pgsql.auth_query", "emqx_auth_pgsql.auth_query", [ + {datatype, string} +]}. + +{mapping, "auth.pgsql.password_hash", "emqx_auth_pgsql.password_hash", [ + {datatype, string} +]}. + +{mapping, "auth.pgsql.pbkdf2_macfun", "emqx_auth_pgsql.pbkdf2_macfun", [ + {datatype, atom} +]}. + +{mapping, "auth.pgsql.pbkdf2_iterations", "emqx_auth_pgsql.pbkdf2_iterations", [ + {datatype, integer} +]}. + +{mapping, "auth.pgsql.pbkdf2_dklen", "emqx_auth_pgsql.pbkdf2_dklen", [ + {datatype, integer} +]}. + +{mapping, "auth.pgsql.super_query", "emqx_auth_pgsql.super_query", [ + {datatype, string} +]}. + +{mapping, "auth.pgsql.acl_query", "emqx_auth_pgsql.acl_query", [ + {datatype, string} +]}. + +{translation, "emqx_auth_pgsql.password_hash", fun(Conf) -> + HashValue = cuttlefish:conf_get("auth.pgsql.password_hash", Conf), + case string:tokens(HashValue, ",") of + [Hash] -> list_to_atom(Hash); + [Prefix, Suffix] -> {list_to_atom(Prefix), list_to_atom(Suffix)}; + [Hash, MacFun, Iterations, Dklen] -> {list_to_atom(Hash), list_to_atom(MacFun), list_to_integer(Iterations), list_to_integer(Dklen)}; + _ -> plain + end +end}. diff --git a/apps/emqx_auth_pgsql/rebar.config b/apps/emqx_auth_pgsql/rebar.config new file mode 100644 index 000000000..469412195 --- /dev/null +++ b/apps/emqx_auth_pgsql/rebar.config @@ -0,0 +1,34 @@ +{deps, + [{epgsql, {git, "https://github.com/epgsql/epgsql", {tag, "4.4.0"}}}, + {ecpool, {git, "https://github.com/emqx/ecpool", {tag, "v0.4.2"}}}, + {emqx_passwd, {git, "https://github.com/emqx/emqx-passwd", {tag, "v1.1.1"}}} + ]}. + +{erl_opts, [warn_unused_vars, + warn_shadow_vars, + warn_unused_import, + warn_obsolete_guard, + debug_info, + compressed + ]}. +{overrides, [{add, [{erl_opts, [compressed]}]}]}. + +{xref_checks, [undefined_function_calls, undefined_functions, + locals_not_used, deprecated_function_calls, + warnings_as_errors, deprecated_functions + ]}. + +{cover_enabled, true}. +{cover_opts, [verbose]}. +{cover_export_enabled, true}. + +{profiles, + [{test, + [{deps, + [{emqx_ct_helper, {git, "https://github.com/emqx/emqx-ct-helper", {branch, "1.2.2"}}}, + {emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.2.0"}}} + ]}, + {erl_opts, [debug_info]} + ]} + ]}. + diff --git a/apps/emqx_auth_pgsql/src/emqx_acl_pgsql.erl b/apps/emqx_auth_pgsql/src/emqx_acl_pgsql.erl new file mode 100644 index 000000000..c1792f1e2 --- /dev/null +++ b/apps/emqx_auth_pgsql/src/emqx_acl_pgsql.erl @@ -0,0 +1,117 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_acl_pgsql). + +-include("emqx_auth_pgsql.hrl"). +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). + +%% ACL callbacks +-export([ register_metrics/0 + , check_acl/5 + , description/0 + ]). + +-spec(register_metrics() -> ok). +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS). + +check_acl(ClientInfo, PubSub, Topic, NoMatchAction, #{pool := Pool} = State) -> + case do_check_acl(Pool, ClientInfo, PubSub, Topic, NoMatchAction, State) of + ok -> emqx_metrics:inc(?ACL_METRICS(ignore)), ok; + {stop, allow} -> emqx_metrics:inc(?ACL_METRICS(allow)), {stop, allow}; + {stop, deny} -> emqx_metrics:inc(?ACL_METRICS(deny)), {stop, deny} + end. + +do_check_acl(_Pool, #{username := <<$$, _/binary>>}, _PubSub, _Topic, _NoMatchAction, _State) -> + ok; +do_check_acl(Pool, ClientInfo, PubSub, Topic, _NoMatchAction, #{acl_query := {AclSql, AclParams}}) -> + case emqx_auth_pgsql_cli:equery(Pool, AclSql, AclParams, ClientInfo) of + {ok, _, []} -> ok; + {ok, _, Rows} -> + Rules = filter(PubSub, compile(Rows)), + case match(ClientInfo, Topic, Rules) of + {matched, allow} -> {stop, allow}; + {matched, deny} -> {stop, deny}; + nomatch -> ok + end; + {error, Reason} -> + ?LOG(error, "[Postgres] do_check_acl error: ~p~n", [Reason]), + ok + end. + +match(_ClientInfo, _Topic, []) -> + nomatch; + +match(ClientInfo, Topic, [Rule|Rules]) -> + case emqx_access_rule:match(ClientInfo, Topic, Rule) of + nomatch -> match(ClientInfo, Topic, Rules); + {matched, AllowDeny} -> {matched, AllowDeny} + end. + +filter(PubSub, Rules) -> + [Term || Term = {_, _, Access, _} <- Rules, + Access =:= PubSub orelse Access =:= pubsub]. + +compile(Rows) -> + compile(Rows, []). +compile([], Acc) -> + Acc; +compile([{Allow, IpAddr, Username, ClientId, Access, Topic}|T], Acc) -> + Who = who(IpAddr, Username, ClientId), + Term = {allow(Allow), Who, access(Access), [topic(Topic)]}, + compile(T, [emqx_access_rule:compile(Term) | Acc]). + +who(_, <<"$all">>, _) -> + all; +who(null, null, null) -> + throw(undefined_who); +who(CIDR, Username, ClientId) -> + Cols = [{ipaddr, b2l(CIDR)}, {user, Username}, {client, ClientId}], + case [{C, V} || {C, V} <- Cols, not empty(V)] of + [Who] -> Who; + Conds -> {'and', Conds} + end. + +allow(1) -> allow; +allow(0) -> deny; +allow(<<"1">>) -> allow; +allow(<<"0">>) -> deny. + +access(1) -> subscribe; +access(2) -> publish; +access(3) -> pubsub; +access(<<"1">>) -> subscribe; +access(<<"2">>) -> publish; +access(<<"3">>) -> pubsub. + +topic(<<"eq ", Topic/binary>>) -> + {eq, Topic}; +topic(Topic) -> + Topic. + +description() -> + "ACL with Postgres". + +b2l(null) -> null; +b2l(B) -> binary_to_list(B). + +empty(null) -> true; +empty("") -> true; +empty(<<>>) -> true; +empty(_) -> false. + diff --git a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.app.src b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.app.src new file mode 100644 index 000000000..fcfb74d33 --- /dev/null +++ b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.app.src @@ -0,0 +1,14 @@ +{application, emqx_auth_pgsql, + [{description, "EMQ X Authentication/ACL with PostgreSQL"}, + {vsn, "git"}, + {modules, []}, + {registered, [emqx_auth_pgsql_sup]}, + {applications, [kernel,stdlib,epgsql,ecpool,emqx_passwd]}, + {mod, {emqx_auth_pgsql_app,[]}}, + {env, []}, + {licenses, ["Apache-2.0"]}, + {maintainers, ["EMQ X Team "]}, + {links, [{"Homepage", "https://emqx.io/"}, + {"Github", "https://github.com/emqx/emqx-auth-pgsql"} + ]} + ]}. diff --git a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.app.src.script b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.app.src.script new file mode 100644 index 000000000..0e14ff23f --- /dev/null +++ b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.app.src.script @@ -0,0 +1,24 @@ +%%-*- mode: erlang -*- +%% .app.src.script + +RemoveLeadingV = + fun(Tag) -> + case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of + nomatch -> + re:replace(Tag, "/", "-", [{return ,list}]); + _ -> + %% if it is a version number prefixed by 'v' or 'e', then remove it + re:replace(Tag, "[v|e]", "", [{return ,list}]) + end + end, + +case os:getenv("EMQX_DEPS_DEFAULT_VSN") of + false -> CONFIG; % env var not defined + [] -> CONFIG; % env var set to empty string + Tag -> + [begin + AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}), + {application, App, AppConf0} + end || Conf = {application, App, AppConf} <- CONFIG] +end. + diff --git a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.appup.src b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.appup.src new file mode 100644 index 000000000..657b119a1 --- /dev/null +++ b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.appup.src @@ -0,0 +1,25 @@ +%% -*-: erlang -*- +{"4.2.3", + [ + {"4.2.2", [ + {load_module, emqx_auth_pgsql_cli, brutal_purge, soft_purge, []} + ]}, + {"4.2.1", [ + {load_module, emqx_auth_pgsql_cli, brutal_purge, soft_purge, []} + ]}, + {"4.2.0", [ + {load_module, emqx_auth_pgsql_cli, brutal_purge, soft_purge, []} + ]} + ], + [ + {"4.2.2", [ + {load_module, emqx_auth_pgsql_cli, brutal_purge, soft_purge, []} + ]}, + {"4.2.1", [ + {load_module, emqx_auth_pgsql_cli, brutal_purge, soft_purge, []} + ]}, + {"4.2.0", [ + {load_module, emqx_auth_pgsql_cli, brutal_purge, soft_purge, []} + ]} + ] +}. diff --git a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.erl b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.erl new file mode 100644 index 000000000..2dee5ef50 --- /dev/null +++ b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql.erl @@ -0,0 +1,91 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_pgsql). + +-include("emqx_auth_pgsql.hrl"). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-export([ register_metrics/0 + , check/3 + , description/0 + ]). + +-spec(register_metrics() -> ok). +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). + +%%-------------------------------------------------------------------- +%% Auth Module Callbacks +%%-------------------------------------------------------------------- + +check(ClientInfo = #{password := Password}, AuthResult, + #{auth_query := {AuthSql, AuthParams}, + super_query := SuperQuery, + hash_type := HashType, + pool := Pool}) -> + CheckPass = case emqx_auth_pgsql_cli:equery(Pool, AuthSql, AuthParams, ClientInfo) of + {ok, _, [Record]} -> + check_pass(erlang:append_element(Record, Password), HashType); + {ok, _, []} -> + {error, not_found}; + {error, Reason} -> + ?LOG(error, "[Postgres] query '~p' failed: ~p", [AuthSql, Reason]), + {error, not_found} + end, + case CheckPass of + ok -> + emqx_metrics:inc(?AUTH_METRICS(success)), + {stop, AuthResult#{is_superuser => is_superuser(Pool, SuperQuery, ClientInfo), + anonymous => false, + auth_result => success}}; + {error, not_found} -> + emqx_metrics:inc(?AUTH_METRICS(ignore)), ok; + {error, ResultCode} -> + ?LOG(error, "[Postgres] Auth from pgsql failed: ~p", [ResultCode]), + emqx_metrics:inc(?AUTH_METRICS(failure)), + {stop, AuthResult#{auth_result => ResultCode, anonymous => false}} + end. + +%%-------------------------------------------------------------------- +%% Is Superuser? +%%-------------------------------------------------------------------- + +-spec(is_superuser(atom(),undefined | {string(), list()}, emqx_types:client()) -> boolean()). +is_superuser(_Pool, undefined, _Client) -> + false; +is_superuser(Pool, {SuperSql, Params}, ClientInfo) -> + case emqx_auth_pgsql_cli:equery(Pool, SuperSql, Params, ClientInfo) of + {ok, [_Super], [{true}]} -> + true; + {ok, [_Super], [_False]} -> + false; + {ok, [_Super], []} -> + false; + {error, _Error} -> + false + end. + +check_pass(Password, HashType) -> + case emqx_passwd:check_pass(Password, HashType) of + ok -> ok; + {error, _Reason} -> {error, not_authorized} + end. + +description() -> "Authentication with PostgreSQL". + diff --git a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_app.erl b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_app.erl new file mode 100644 index 000000000..1d05f6b8a --- /dev/null +++ b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_app.erl @@ -0,0 +1,63 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_pgsql_app). + +-behaviour(application). + +-emqx_plugin(auth). + +-include("emqx_auth_pgsql.hrl"). + +-import(emqx_auth_pgsql_cli, [parse_query/2]). + +%% Application callbacks +-export([ start/2 + , stop/1 + ]). + +%%-------------------------------------------------------------------- +%% Application callbacks +%%-------------------------------------------------------------------- + +start(_StartType, _StartArgs) -> + {ok, Sup} = emqx_auth_pgsql_sup:start_link(), + if_enabled(auth_query, fun(AuthQuery) -> + SuperQuery = parse_query(super_query, application:get_env(?APP, super_query, undefined)), + {ok, HashType} = application:get_env(?APP, password_hash), + AuthEnv = #{auth_query => AuthQuery, + super_query => SuperQuery, + hash_type => HashType, + pool => ?APP}, + ok = emqx_auth_pgsql:register_metrics(), + ok = emqx:hook('client.authenticate', fun emqx_auth_pgsql:check/3, [AuthEnv]) + end), + if_enabled(acl_query, fun(AclQuery) -> + ok = emqx_acl_pgsql:register_metrics(), + ok = emqx:hook('client.check_acl', fun emqx_acl_pgsql:check_acl/5, [#{acl_query => AclQuery, pool => ?APP}]) + end), + {ok, Sup}. + +stop(_State) -> + ok = emqx:unhook('client.authenticate', fun emqx_auth_pgsql:check/3), + ok = emqx:unhook('client.check_acl', fun emqx_acl_pgsql:check_acl/5). + +if_enabled(Par, Fun) -> + case application:get_env(?APP, Par) of + {ok, Query} -> Fun(parse_query(Par, Query)); + undefined -> ok + end. + diff --git a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_cli.erl b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_cli.erl new file mode 100644 index 000000000..27d25f35b --- /dev/null +++ b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_cli.erl @@ -0,0 +1,138 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_pgsql_cli). + +-behaviour(ecpool_worker). + +-include("emqx_auth_pgsql.hrl"). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-export([connect/1]). +-export([parse_query/2]). +-export([ equery/4 + , equery/3 + ]). + +%%-------------------------------------------------------------------- +%% Avoid SQL Injection: Parse SQL to Parameter Query. +%%-------------------------------------------------------------------- + +parse_query(_Par, undefined) -> + undefined; +parse_query(Par, Sql) -> + case re:run(Sql, "'%[ucCad]'", [global, {capture, all, list}]) of + {match, Variables} -> + Params = [Var || [Var] <- Variables], + {atom_to_list(Par), Params}; + nomatch -> + {atom_to_list(Par), []} + end. + +pgvar(Sql, Params) -> + Vars = ["$" ++ integer_to_list(I) || I <- lists:seq(1, length(Params))], + lists:foldl(fun({Param, Var}, S) -> + re:replace(S, Param, Var, [{return, list}]) + end, Sql, lists:zip(Params, Vars)). + +%%-------------------------------------------------------------------- +%% PostgreSQL Connect/Query +%%-------------------------------------------------------------------- + +connect(Opts) -> + Host = proplists:get_value(host, Opts), + Username = proplists:get_value(username, Opts), + Password = proplists:get_value(password, Opts), + case epgsql:connect(Host, Username, Password, conn_opts(Opts)) of + {ok, C} -> + conn_post(C), + {ok, C}; + {error, Reason = econnrefused} -> + ?LOG(error, "[Postgres] Can't connect to Postgres server: Connection refused."), + {error, Reason}; + {error, Reason = invalid_authorization_specification} -> + ?LOG(error, "[Postgres] Can't connect to Postgres server: Invalid authorization specification."), + {error, Reason}; + {error, Reason = invalid_password} -> + ?LOG(error, "[Postgres] Can't connect to Postgres server: Invalid password."), + {error, Reason}; + {error, Reason} -> + ?LOG(error, "[Postgres] Can't connect to Postgres server: ~p", [Reason]), + {error, Reason} + end. + +conn_post(Connection) -> + lists:foreach(fun(Par) -> + Sql0 = application:get_env(?APP, Par, undefined), + case parse_query(Par, Sql0) of + undefined -> ok; + {_, Params} -> + Sql = pgvar(Sql0, Params), + epgsql:parse(Connection, atom_to_list(Par), Sql, []) + end + end, [auth_query, acl_query, super_query]). + +conn_opts(Opts) -> + conn_opts(Opts, []). +conn_opts([], Acc) -> + Acc; +conn_opts([Opt = {database, _}|Opts], Acc) -> + conn_opts(Opts, [Opt|Acc]); +conn_opts([Opt = {ssl, _}|Opts], Acc) -> + conn_opts(Opts, [Opt|Acc]); +conn_opts([Opt = {port, _}|Opts], Acc) -> + conn_opts(Opts, [Opt|Acc]); +conn_opts([Opt = {timeout, _}|Opts], Acc) -> + conn_opts(Opts, [Opt|Acc]); +conn_opts([Opt = {ssl_opts, _}|Opts], Acc) -> + conn_opts(Opts, [Opt|Acc]); +conn_opts([_Opt|Opts], Acc) -> + conn_opts(Opts, Acc). + +equery(Pool, Sql, Params) -> + ecpool:with_client(Pool, fun(C) -> epgsql:prepared_query(C, Sql, Params) end). + +equery(Pool, Sql, Params, ClientInfo) -> + ecpool:with_client(Pool, fun(C) -> epgsql:prepared_query(C, Sql, replvar(Params, ClientInfo)) end). + +replvar(Params, ClientInfo) -> + replvar(Params, ClientInfo, []). + +replvar([], _ClientInfo, Acc) -> + lists:reverse(Acc); + +replvar(["'%u'" | Params], ClientInfo = #{username := Username}, Acc) -> + replvar(Params, ClientInfo, [Username | Acc]); +replvar(["'%c'" | Params], ClientInfo = #{clientid := ClientId}, Acc) -> + replvar(Params, ClientInfo, [ClientId | Acc]); +replvar(["'%a'" | Params], ClientInfo = #{peerhost := IpAddr}, Acc) -> + replvar(Params, ClientInfo, [inet_parse:ntoa(IpAddr) | Acc]); +replvar(["'%C'" | Params], ClientInfo, Acc) -> + replvar(Params, ClientInfo, [safe_get(cn, ClientInfo)| Acc]); +replvar(["'%d'" | Params], ClientInfo, Acc) -> + replvar(Params, ClientInfo, [safe_get(dn, ClientInfo)| Acc]); +replvar([Param | Params], ClientInfo, Acc) -> + replvar(Params, ClientInfo, [Param | Acc]). + +safe_get(K, ClientInfo) -> + bin(maps:get(K, ClientInfo, undefined)). + +bin(A) when is_atom(A) -> atom_to_binary(A, utf8); +bin(B) when is_binary(B) -> B; +bin(X) -> X. + diff --git a/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_sup.erl b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_sup.erl new file mode 100644 index 000000000..162d04747 --- /dev/null +++ b/apps/emqx_auth_pgsql/src/emqx_auth_pgsql_sup.erl @@ -0,0 +1,37 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_pgsql_sup). + +-behaviour(supervisor). + +-include("emqx_auth_pgsql.hrl"). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init([]) -> + %% PgSQL Connection Pool + {ok, Opts} = application:get_env(?APP, server), + PoolSpec = ecpool:pool_spec(?APP, ?APP, emqx_auth_pgsql_cli, Opts), + {ok, {{one_for_one, 10, 100}, [PoolSpec]}}. + diff --git a/apps/emqx_auth_pgsql/test/.placeholder b/apps/emqx_auth_pgsql/test/.placeholder new file mode 100644 index 000000000..e69de29bb diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE.erl b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE.erl new file mode 100644 index 000000000..e4aefdcd3 --- /dev/null +++ b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE.erl @@ -0,0 +1,247 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_pgsql_SUITE). + +-compile(export_all). + +-define(POOL, emqx_auth_pgsql). + +-define(APP, emqx_auth_pgsql). + +-include_lib("emqx/include/emqx.hrl"). + +-include_lib("eunit/include/eunit.hrl"). + +-include_lib("common_test/include/ct.hrl"). + +%%setp1 init table +-define(DROP_ACL_TABLE, "DROP TABLE IF EXISTS mqtt_acl_test"). + +-define(CREATE_ACL_TABLE, "CREATE TABLE mqtt_acl_test ( + id SERIAL primary key, + allow integer, + ipaddr character varying(60), + username character varying(100), + clientid character varying(100), + access integer, + topic character varying(100))"). + +-define(INIT_ACL, "INSERT INTO mqtt_acl_test (id, allow, ipaddr, username, clientid, access, topic) + VALUES + (1,1,'127.0.0.1','u1','c1',1,'t1'), + (2,0,'127.0.0.1','u2','c2',1,'t1'), + (3,1,'10.10.0.110','u1','c1',1,'t1'), + (4,1,'127.0.0.1','u3','c3',3,'t1')"). + +-define(DROP_AUTH_TABLE, "DROP TABLE IF EXISTS mqtt_user_test"). + +-define(CREATE_AUTH_TABLE, "CREATE TABLE mqtt_user_test ( + id SERIAL primary key, + is_superuser boolean, + username character varying(100), + password character varying(100), + salt character varying(40))"). + +-define(INIT_AUTH, "INSERT INTO mqtt_user_test (id, is_superuser, username, password, salt) + VALUES + (1, true, 'plain', 'plain', 'salt'), + (2, false, 'md5', '1bc29b36f623ba82aaf6724fd3b16718', 'salt'), + (3, false, 'sha', 'd8f4590320e1343a915b6394170650a8f35d6926', 'salt'), + (4, false, 'sha256', '5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e', 'salt'), + (5, false, 'pbkdf2_password', 'cdedb5281bb2f801565a1122b2563515', 'ATHENA.MIT.EDUraeburn'), + (6, false, 'bcrypt_foo', '$2a$12$sSS8Eg.ovVzaHzi1nUHYK.HbUIOdlQI0iS22Q5rd5z.JVVYH6sfm6', '$2a$12$sSS8Eg.ovVzaHzi1nUHYK.'), + (7, false, 'bcrypt', '$2y$16$rEVsDarhgHYB0TGnDFJzyu5f.T.Ha9iXMTk9J36NCMWWM7O16qyaK', 'salt')"). + +all() -> + [{group, ssl}, {group, nossl}]. + +groups() -> + Cases = emqx_ct:all(?MODULE), + [{ssl, [sequence], Cases}, {nossl, [sequence], Cases}]. + +init_per_group(Name, Config) -> + case Name of + ssl -> + emqx_ct_helpers:start_apps([emqx_auth_pgsql], fun set_special_configs_ssl/1); + nossl -> + emqx_ct_helpers:start_apps([emqx_auth_pgsql], fun set_special_configs/1) + end, + init_auth_(), + init_acl_(), + Config. + +end_per_group(_, Config) -> + drop_auth_(), + drop_acl_(), + emqx_ct_helpers:stop_apps([emqx_auth_pgsql]), + Config. + +set_special_configs_ssl(Name) -> + Server = application:get_env(?APP, server, []), + Path = emqx_ct_helpers:deps_path(emqx_auth_pgsql, "test/emqx_auth_pgsql_SUITE_data/"), + Sslopts = [{keyfile, Path ++ "/client-key.pem"}, + {certfile, Path ++ "/client-cert.pem"}, + {cacertfile, Path ++ "/ca.pem"}], + Temp = lists:keyreplace(ssl, 1, Server, {ssl, true}), + application:set_env(?APP, server, Temp), + application:set_env(?APP, server, lists:keyreplace(ssl_opts, 1, Temp, {ssl_opts, Sslopts})), + set_special_configs(Name). + +set_special_configs(emqx) -> + application:set_env(emqx, acl_nomatch, deny), + application:set_env(emqx, acl_file, + emqx_ct_helpers:deps_path(emqx, "test/emqx_SUITE_data/acl.conf")), + application:set_env(emqx, allow_anonymous, false), + application:set_env(emqx, enable_acl_cache, false), + application:set_env(emqx, plugins_loaded_file, + emqx_ct_helpers:deps_path(emqx, "test/emqx_SUITE_data/loaded_plugins")); + +set_special_configs(emqx_auth_pgsql) -> + Server = application:get_env(?APP, server, []), + application:set_env(?APP, server, + lists:keyreplace(password, + 1, + lists:keyreplace(pool_size, 1, Server, {pool_size, 1}), + {password, "public"})), + application:set_env(?APP, acl_query, "select allow, ipaddr, username, clientid, access, topic from mqtt_acl_test where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c'"), + application:set_env(?APP, super_query, "select is_superuser from mqtt_user_test where username = '%u' limit 1"), + application:set_env(?APP, auth_query, "select password from mqtt_user_test where username = '%u' limit 1"); +set_special_configs(_App) -> + ok. + +t_comment_config(_) -> + AuthCount = length(emqx_hooks:lookup('client.authenticate')), + AclCount = length(emqx_hooks:lookup('client.check_acl')), + application:stop(?APP), + [application:unset_env(?APP, Par) || Par <- [acl_query, auth_query]], + application:start(?APP), + ?assertEqual([], emqx_hooks:lookup('client.authenticate')), + ?assertEqual(AuthCount - 1, length(emqx_hooks:lookup('client.authenticate'))), + ?assertEqual(AclCount - 1, length(emqx_hooks:lookup('client.check_acl'))). + +t_placeholders(_) -> + ClientA = #{username => <<"plain">>, clientid => <<"plain">>, zone => external}, + reload([{password_hash, plain}, + {auth_query, "select password from mqtt_user_test where username = '%u' and 'a_cn_val' = '%C' limit 1"}]), + {error, not_authorized} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>}), + {error, not_authorized} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>, cn => undefined}), + {ok, _} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>, cn => <<"a_cn_val">>}), + + reload([{auth_query, "select password from mqtt_user_test where username = '%c' and 'a_dn_val' = '%d' limit 1"}]), + {error, not_authorized} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>}), + {error, not_authorized} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>, dn => undefined}), + {ok, _} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>, dn => <<"a_dn_val">>}), + + reload([{auth_query, "select password from mqtt_user_test where username = '%u' and '192.168.1.5' = '%a' limit 1"}]), + {error, not_authorized} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>}), + {ok, _} = + emqx_access_control:authenticate(ClientA#{password => <<"plain">>, peerhost => {192,168,1,5}}). + +t_check_auth(_) -> + Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external}, + Md5 = #{clientid => <<"md5">>, username => <<"md5">>, zone => external}, + Sha = #{clientid => <<"sha">>, username => <<"sha">>, zone => external}, + Sha256 = #{clientid => <<"sha256">>, username => <<"sha256">>, zone => external}, + Pbkdf2 = #{clientid => <<"pbkdf2_password">>, username => <<"pbkdf2_password">>, zone => external}, + BcryptFoo = #{clientid => <<"bcrypt_foo">>, username => <<"bcrypt_foo">>, zone => external}, + User1 = #{clientid => <<"bcrypt_foo">>, username => <<"user">>, zone => external}, + Bcrypt = #{clientid => <<"bcrypt">>, username => <<"bcrypt">>, zone => external}, + reload([{password_hash, plain}, + {auth_query, "select password from mqtt_user_test where username = '%u' limit 1"}]), + {ok, #{is_superuser := true}} = emqx_access_control:authenticate(Plain#{password => <<"plain">>}), + reload([{password_hash, md5}]), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Md5#{password => <<"md5">>}), + reload([{password_hash, sha}]), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Sha#{password => <<"sha">>}), + reload([{password_hash, sha256}]), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Sha256#{password => <<"sha256">>}), + reload([{password_hash, bcrypt}]), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}), + %%pbkdf2 sha + reload([{password_hash, {pbkdf2, sha, 1, 16}}, {auth_query, "select password, salt from mqtt_user_test where username = '%u' limit 1"}]), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Pbkdf2#{password => <<"password">>}), + reload([{password_hash, {salt, bcrypt}}]), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(BcryptFoo#{password => <<"foo">>}), + {error, _} = emqx_access_control:authenticate(User1#{password => <<"foo">>}), + {error, not_authorized} = emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}). +t_check_acl(_) -> + emqx_modules:load_module(emqx_mod_acl_internal, false), + User1 = #{zone => external, peerhost => {127,0,0,1}, clientid => <<"c1">>, username => <<"u1">>}, + User2 = #{zone => external, peerhost => {127,0,0,1}, clientid => <<"c2">>, username => <<"u2">>}, + reload([{acl_query, "select allow, ipaddr, username, clientid, access, topic from mqtt_acl_test where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c'"}]), + allow = emqx_access_control:check_acl(User1, subscribe, <<"t1">>), + deny = emqx_access_control:check_acl(User2, subscribe, <<"t1">>), + User3 = #{zone => external, peerhost => {10,10,0,110}, clientid => <<"c1">>, username => <<"u1">>}, + User4 = #{zone => external, peerhost => {10,10,10,110}, clientid => <<"c1">>, username => <<"u1">>}, + allow = emqx_access_control:check_acl(User3, subscribe, <<"t1">>), + allow = emqx_access_control:check_acl(User3, subscribe, <<"t1">>), + allow = emqx_access_control:check_acl(User3, subscribe, <<"t2">>),%% nomatch -> ignore -> emqttd acl + allow = emqx_access_control:check_acl(User4, subscribe, <<"t1">>),%% nomatch -> ignore -> emqttd acl + User5 = #{zone => external, peerhost => {127,0,0,1}, clientid => <<"c3">>, username => <<"u3">>}, + allow = emqx_access_control:check_acl(User5, subscribe, <<"t1">>), + allow = emqx_access_control:check_acl(User5, publish, <<"t1">>). + +t_acl_super(_) -> + reload([{password_hash, plain}, {auth_query, "select password from mqtt_user_test where username = '%u' limit 1"}]), + {ok, C} = emqtt:start_link([{host, "localhost"}, {clientid, <<"simpleClient">>}, + {username, <<"plain">>}, {password, <<"plain">>}]), + {ok, _} = emqtt:connect(C), + timer:sleep(10), + emqtt:subscribe(C, <<"TopicA">>, qos2), + emqtt:publish(C, <<"TopicA">>, <<"Payload">>, qos2), + timer:sleep(1000), + receive + {publish, #{payload := Payload}} -> + ?assertEqual(<<"Payload">>, Payload) + after + 1000 -> + ct:fail({receive_timeout, <<"Payload">>}), + ok + end, + emqtt:disconnect(C). + +reload(Config) when is_list(Config) -> + application:stop(?APP), + [application:set_env(?APP, K, V) || {K, V} <- Config], + application:start(?APP). + +init_acl_() -> + {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?POOL})), + {ok, [], []} = epgsql:squery(Pid, ?DROP_ACL_TABLE), + {ok, [], []} = epgsql:squery(Pid, ?CREATE_ACL_TABLE), + {ok, _} = epgsql:equery(Pid, ?INIT_ACL). + +drop_acl_() -> + {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?POOL})), + {ok, [], []}= epgsql:squery(Pid, ?DROP_ACL_TABLE). + +init_auth_() -> + {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?POOL})), + {ok, [], []} = epgsql:squery(Pid, ?DROP_AUTH_TABLE), + {ok, [], []} = epgsql:squery(Pid, ?CREATE_AUTH_TABLE), + {ok, _} = epgsql:equery(Pid, ?INIT_AUTH). + +drop_auth_() -> + {ok, Pid} = ecpool_worker:client(gproc_pool:pick_worker({ecpool, ?POOL})), + {ok, [], []} = epgsql:squery(Pid, ?DROP_AUTH_TABLE). diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/ca.pem b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/ca.pem new file mode 100644 index 000000000..00b31d8a4 --- /dev/null +++ b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/ca.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDAzCCAeugAwIBAgIBATANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR +TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X +DTIwMDYxMTAzMzg0NloXDTMwMDYwOTAzMzg0NlowPDE6MDgGA1UEAwwxTXlTUUxf +U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9DQV9DZXJ0aWZpY2F0ZTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANJBlAYvTQ6euY4HcSn4syH7kq9s +KcG+OMjPUrj+KFEElCzgNuIhaS0f3ORQGB1PNcvVcfdXUI3WX332gWbr9s1b7Xl1 +JKJfDXs+26Cm6NhONTE3sPHnbTSmQEFb52hwAtjQmcY3IQs1AgxKFFHJfnCBEWfE +ePBQaiuYk1XDESMdWpMLrPnYQaj9MpAOUxjlmZCayzPWlF0j0IWvfsF5TqZL7tFK +9p5F/DzyZ4n1mqPVEoUmq5ZdSKj2TQkpWTMHBWHEDQQqXbyE1FGJR7zEUFeuG1KT +sVBg7iZEC93SygZTbgUZSQXIwQCsO6xZ8MB2XDJkPbWp/3Wc6c8I6P09F48CAwEA +AaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEADKz6bIpP5anp +GgLB0jkclRWuMlS4qqIt4itSsMXPJ/ezpHwECixmgW2TIQl6S1woRkUeMxhT2/Ay +Sn/7aKxuzRagyE5NEGOvrOuAP5RO2ZdNJ/X3/Rh533fK1sOTEEbSsWUvW6iSkZef +rsfZBVP32xBhRWkKRdLeLB4W99ADMa0IrTmZPCXHSSE2V4e1o6zWLXcOZeH1Qh8N +SkelBweR+8r1Fbvy1r3s7eH7DCbYoGEDVLQGOLvzHKBisQHmoDnnF5E9g1eeNRdg +o+vhOKfYCOzeNREJIqS42PHcGhdNRk90ycigPmfUJclz1mDHoMjKR2S5oosTpr65 +tNPx3CL7GA== +-----END CERTIFICATE----- diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-cert.pem b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-cert.pem new file mode 100644 index 000000000..aad1404ca --- /dev/null +++ b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBDCCAeygAwIBAgIBAzANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR +TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X +DTIwMDYxMTAzMzg0N1oXDTMwMDYwOTAzMzg0N1owQDE+MDwGA1UEAww1TXlTUUxf +U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9DbGllbnRfQ2VydGlmaWNhdGUw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVYSWpOvCTupz82fc85Opv +EQ7rkB8X2oOMyBCpkyHKBIr1ZQgRDWBp9UVOASq3GnSElm6+T3Kb1QbOffa8GIlw +sjAueKdq5L2eSkmPIEQ7eoO5kEW+4V866hE1LeL/PmHg2lGP0iqZiJYtElhHNQO8 +3y9I7cm3xWMAA3SSWikVtpJRn3qIp2QSrH+tK+/HHbE5QwtPxdir4ULSCSOaM5Yh +Wi5Oto88TZqe1v7SXC864JVvO4LuS7TuSreCdWZyPXTJFBFeCEWSAxonKZrqHbBe +CwKML6/0NuzjaQ51c2tzmVI6xpHj3nnu4cSRx6Jf9WBm+35vm0wk4pohX3ptdzeV +AgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAByQ5zSNeFUH +Aw7JlpZHtHaSEeiiyBHke20ziQ07BK1yi/ms2HAWwQkpZv149sjNuIRH8pkTmkZn +g8PDzSefjLbC9AsWpWV0XNV22T/cdobqLqMBDDZ2+5bsV+jTrOigWd9/AHVZ93PP +IJN8HJn6rtvo2l1bh/CdsX14uVSdofXnuWGabNTydqtMvmCerZsdf6qKqLL+PYwm +RDpgWiRUY7KPBSSlKm/9lJzA+bOe4dHeJzxWFVCJcbpoiTFs1je1V8kKQaHtuW39 +ifX6LTKUMlwEECCbDKM8Yq2tm8NjkjCcnFDtKg8zKGPUu+jrFMN5otiC3wnKcP7r +O9EkaPcgYH8= +-----END CERTIFICATE----- diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-key.pem b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-key.pem new file mode 100644 index 000000000..6789d0291 --- /dev/null +++ b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA1WElqTrwk7qc/Nn3POTqbxEO65AfF9qDjMgQqZMhygSK9WUI +EQ1gafVFTgEqtxp0hJZuvk9ym9UGzn32vBiJcLIwLninauS9nkpJjyBEO3qDuZBF +vuFfOuoRNS3i/z5h4NpRj9IqmYiWLRJYRzUDvN8vSO3Jt8VjAAN0klopFbaSUZ96 +iKdkEqx/rSvvxx2xOUMLT8XYq+FC0gkjmjOWIVouTraPPE2antb+0lwvOuCVbzuC +7ku07kq3gnVmcj10yRQRXghFkgMaJyma6h2wXgsCjC+v9Dbs42kOdXNrc5lSOsaR +49557uHEkceiX/VgZvt+b5tMJOKaIV96bXc3lQIDAQABAoIBAF7yjXmSOn7h6P0y +WCuGiTLG2mbDiLJqj2LTm2Z5i+2Cu/qZ7E76Ls63TxF4v3MemH5vGfQhEhR5ZD/6 +GRJ1sKKvB3WGRqjwA9gtojHH39S/nWGy6vYW/vMOOH37XyjIr3EIdIaUtFQBTSHd +Kd71niYrAbVn6fyWHolhADwnVmTMOl5OOAhCdEF4GN3b5aIhIu8BJ7EUzTtHBJIj +CAEfjZFjDs1y1cIgGFJkuIQxMfCpq5recU2qwip7YO6fk//WEjOPu7kSf5IEswL8 +jg1dea9rGBV6KaD2xsgsC6Ll6Sb4BbsrHMfflG3K2Lk3RdVqqTFp1Fn1PTLQE/1S +S/SZPYECgYEA9qYcHKHd0+Q5Ty5wgpxKGa4UCWkpwvfvyv4bh8qlmxueB+l2AIdo +ZvkM8gTPagPQ3WypAyC2b9iQu70uOJo1NizTtKnpjDdN1YpDjISJuS/P0x73gZwy +gmoM5AzMtN4D6IbxXtXnPaYICvwLKU80ouEN5ZPM4/ODLUu6gsp0v2UCgYEA3Xgi +zMC4JF0vEKEaK0H6QstaoXUmw/lToZGH3TEojBIkb/2LrHUclygtONh9kJSFb89/ +jbmRRLAOrx3HZKCNGUmF4H9k5OQyAIv6OGBinvLGqcbqnyNlI+Le8zxySYwKMlEj +EMrBCLmSyi0CGFrbZ3mlj/oCET/ql9rNvcK+DHECgYAEx5dH3sMjtgp+RFId1dWB +xePRgt4yTwewkVgLO5wV82UOljGZNQaK6Eyd7AXw8f38LHzh+KJQbIvxd2sL4cEi +OaAoohpKg0/Y0YMZl//rPMf0OWdmdZZs/I0fZjgZUSwWN3c59T8z7KG/RL8an9RP +S7kvN7wCttdV61/D5RR6GQKBgDxCe/WKWpBKaovzydMLWLTj7/0Oi0W3iXHkzzr4 +LTgvl4qBSofaNbVLUUKuZTv5rXUG2IYPf99YqCYtzBstNDc1MiAriaBeFtzfOW4t +i6gEFtoLLbuvPc3N5Sv5vn8Ug5G9UfU3td5R4AbyyCcoUZqOFuZd+EIJSiOXfXOs +kVmBAoGBAIU9aPAqhU5LX902oq8KsrpdySONqv5mtoStvl3wo95WIqXNEsFY60wO +q02jKQmJJ2MqhkJm2EoF2Mq8+40EZ5sz8LdgeQ/M0yQ9lAhPi4rftwhpe55Ma9dk +SE9X1c/DMCBEaIjJqVXdy0/EeArwpb8sHkguVVAZUWxzD+phm1gs +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/pg.conf b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/pg.conf new file mode 100644 index 000000000..7b78cd1e3 --- /dev/null +++ b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/pg.conf @@ -0,0 +1,21 @@ +# - Connection Settings - + +listen_addresses = '*' +port = 5432 # (change requires restart) +max_connections = 100 # (change requires restart) +# - SSL - + +ssl = on +ssl_cert_file = '/etc/postgresql/server-cert.pem' +ssl_key_file = '/etc/postgresql/server-key.pem' +shared_buffers = 128MB # min 128kB +checkpoint_timeout = 5min # range 30s-1d +max_wal_size = 1GB +min_wal_size = 80MB +datestyle = 'iso, mdy' +timezone = 'Etc/UTC' +lc_messages = 'en_US.utf8' # locale for system error message +lc_monetary = 'en_US.utf8' # locale for monetary formatting +lc_numeric = 'en_US.utf8' # locale for number formatting +lc_time = 'en_US.utf8' # locale for time formatting +default_text_search_config = 'pg_catalog.english' diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/server-cert.pem b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/server-cert.pem new file mode 100644 index 000000000..a2f9688df --- /dev/null +++ b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/server-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBDCCAeygAwIBAgIBAjANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR +TF9TZXJ2ZXJfOC4wLjE5X0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X +DTIwMDYxMTAzMzg0NloXDTMwMDYwOTAzMzg0NlowQDE+MDwGA1UEAww1TXlTUUxf +U2VydmVyXzguMC4xOV9BdXRvX0dlbmVyYXRlZF9TZXJ2ZXJfQ2VydGlmaWNhdGUw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcEnEm5hqP1EbEJycOz8Ua +NWp29QdpFUzTWhkKGhVXk+0msmNTw4NBAFB42moY44OU8wvDideOlJNhPRWveD8z +G2lxzJA91p0UK4et8ia9MmeuCGhdC9jxJ8X69WNlUiPyy0hI/ZsqRq9Z0C2eW0iL +JPXsy4X8Xpw3SFwoXf5pR9RFY5Pb2tuyxqmSestu2VXT/NQjJg4CVDR3mFcHPXZB +4elRzH0WshExEGkgy0bg20MJeRc2Qdb5Xx+EakbmwroDWaCn3NSGqQ7jv6Vw0doy +TGvS6h6RHBxnyqRfRgKGlCoOMG9/5+rFJC00QpCUG2vHXHWGoWlMlJ3foN7rj5v9 +AgMBAAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAJ5zt2rj4Ag6 +zpN59AWC1Fur8g8l41ksHkSpKPp+PtyO/ngvbMqBpfmK1e7JCKZv/68QXfMyWWAI +hwalqZkXXWHKjuz3wE7dE25PXFXtGJtcZAaj10xt98fzdqt8lQSwh2kbfNwZIz1F +sgAStgE7+ZTcqTgvNB76Os1UK0to+/P0VBWktaVFdyub4Nc2SdPVnZNvrRBXBwOD +3V8ViwywDOFoE7DvCvwx/SVsvoC0Z4j3AMMovO6oHicP7uU83qsQgm1Qru3YeoLR ++DoVi7IPHbWvN7MqFYn3YjNlByO2geblY7MR0BlqbFlmFrqLsUfjsh2ys7/U/knC +dN/klu446fI= +-----END CERTIFICATE----- diff --git a/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/server-key.pem b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/server-key.pem new file mode 100644 index 000000000..a1dfd5f78 --- /dev/null +++ b/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/server-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAnBJxJuYaj9RGxCcnDs/FGjVqdvUHaRVM01oZChoVV5PtJrJj +U8ODQQBQeNpqGOODlPMLw4nXjpSTYT0Vr3g/MxtpccyQPdadFCuHrfImvTJnrgho +XQvY8SfF+vVjZVIj8stISP2bKkavWdAtnltIiyT17MuF/F6cN0hcKF3+aUfURWOT +29rbssapknrLbtlV0/zUIyYOAlQ0d5hXBz12QeHpUcx9FrIRMRBpIMtG4NtDCXkX +NkHW+V8fhGpG5sK6A1mgp9zUhqkO47+lcNHaMkxr0uoekRwcZ8qkX0YChpQqDjBv +f+fqxSQtNEKQlBtrx1x1hqFpTJSd36De64+b/QIDAQABAoIBAFiah66Dt9SruLkn +WR8piUaFyLlcBib8Nq9OWSTJBhDAJERxxb4KIvvGB+l0ZgNXNp5bFPSfzsZdRwZP +PX5uj8Kd71Dxx3mz211WESMJdEC42u+MSmN4lGLkJ5t/sDwXU91E1vbJM0ve8THV +4/Ag9qA4DX2vVZOeyqT/6YHpSsPNZplqzrbAiwrfHwkctHfgqwOf3QLfhmVQgfCS +VwidBldEUv2whSIiIxh4Rv5St4kA68IBCbJxdpOpyuQBkk6CkxZ7VN9FqOuSd4Pk +Wm7iWyBMZsCmELZh5XAXld4BEt87C5R4CvbPBDZxAv3THk1DNNvpy3PFQfwARRFb +SAToYMECgYEAyL7U8yxpzHDYWd3oCx6vTi9p9N/z0FfAkWrRF6dm4UcSklNiT1Aq +EOnTA+SaW8tV3E64gCWcY23gNP8so/ZseWj6L+peHwtchaP9+KB7yGw2A+05+lOx +VetLTjAOmfpiUXFe5w1q4C1RGhLjZjjzW+GvwdAuchQgUEFaomrV+PUCgYEAxwfH +cmVGFbAktcjU4HSRjKSfawCrut+3YUOLybyku3Q/hP9amG8qkVTFe95CTLjLe2D0 +ccaTTpofFEJ32COeck0g0Ujn/qQ+KXRoauOYs4FB1DtqMpqB78wufWEUpDpbd9/h +J+gJdC/IADd4tJW9zA92g8IA7ZtFmqDtiSpQ0ekCgYAQGkaorvJZpN+l7cf0RGTZ +h7IfI2vCVZer0n6tQA9fmLzjoe6r4AlPzAHSOR8sp9XeUy43kUzHKQQoHCPvjw/K +eWJAP7OHF/k2+x2fOPhU7mEy1W+mJdp+wt4Kio5RSaVjVQ3AyPG+w8PSrJszEvRq +dWMMz+851WV2KpfjmWBKlQKBgQC++4j4DZQV5aMkSKV1CIZOBf3vaIJhXKEUFQPD +PmB4fBEjpwCg+zNGp6iktt65zi17o8qMjrb1mtCt2SY04eD932LZUHNFlwcLMmes +Ad+aiDLJ24WJL1f16eDGcOyktlblDZB5gZ/ovJzXEGOkLXglosTfo77OQculmDy2 +/UL2WQKBgGeKasmGNfiYAcWio+KXgFkHXWtAXB9B91B1OFnCa40wx+qnl71MIWQH +PQ/CZFNWOfGiNEJIZjrHsfNJoeXkhq48oKcT0AVCDYyLV0VxDO4ejT95mGW6njNd +JpvmhwwAjOvuWVr0tn4iXlSK8irjlJHmwcRjLTJq97vE9fsA2MjI +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_redis/.ci/docker-compose-tls.yml b/apps/emqx_auth_redis/.ci/docker-compose-tls.yml new file mode 100644 index 000000000..2247cd21b --- /dev/null +++ b/apps/emqx_auth_redis/.ci/docker-compose-tls.yml @@ -0,0 +1,31 @@ +version: '3' + +services: + erlang: + image: erlang:22.3 + volumes: + - ../:/emqx_auth_redis + networks: + - emqx_bridge + depends_on: + - redis_server + tty: true + + redis_server: + image: redis:6.0.9 + volumes: + - ../test/emqx_auth_redis_SUITE_data/certs:/tls + command: + - redis-server + - "--bind 0.0.0.0 ::" + - --tls-port 6380 + - --tls-cert-file /tls/redis.crt + - --tls-key-file /tls/redis.key + - --tls-ca-cert-file /tls/ca.crt + restart: always + networks: + - emqx_bridge + +networks: + emqx_bridge: + driver: bridge diff --git a/apps/emqx_auth_redis/.ci/docker-compose.yml b/apps/emqx_auth_redis/.ci/docker-compose.yml new file mode 100644 index 000000000..6716262ad --- /dev/null +++ b/apps/emqx_auth_redis/.ci/docker-compose.yml @@ -0,0 +1,25 @@ +version: '3' + +services: + erlang: + image: erlang:22.3 + volumes: + - ../:/emqx_auth_redis + networks: + - emqx_bridge + depends_on: + - redis_server + tty: true + + redis_server: + image: redis:${REDIS_TAG} + command: + - redis-server + - "--bind 0.0.0.0 ::" + restart: always + networks: + - emqx_bridge + +networks: + emqx_bridge: + driver: bridge diff --git a/apps/emqx_auth_redis/.github/workflows/run_test_cases.yaml b/apps/emqx_auth_redis/.github/workflows/run_test_cases.yaml new file mode 100644 index 000000000..5af7bdbc3 --- /dev/null +++ b/apps/emqx_auth_redis/.github/workflows/run_test_cases.yaml @@ -0,0 +1,80 @@ +name: Run test cases + +on: [push, pull_request] + +jobs: + run_tests_cases: + runs-on: ubuntu-latest + + strategy: + matrix: + redis_tag: + - 5.0.9 + - 6.0.9 + network_type: + - ipv4 + - ipv6 + connect_type: + - tcp + - tls + + steps: + - name: install docker-compose + run: | + sudo curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + - uses: actions/checkout@v1 + - name: setup + if: matrix.connect_type == 'tcp' && matrix.network_type == 'ipv6' + env: + REDIS_TAG: ${{ matrix.redis_tag}} + run: | + set -e -u -x + docker network create --driver bridge --ipv6 --subnet fd15:555::/64 tests_emqx_bridge --attachable; + docker-compose -f ./.ci/docker-compose.yml -p tests up -d + ipv6_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' $(docker ps -a -f name=tests_redis_server_1 -q)) + sed -i "/auth.redis.server/c auth.redis.server = $ipv6_address:6379" ./etc/emqx_auth_redis.conf + - name: setup + if: matrix.connect_type == 'tcp' && matrix.network_type == 'ipv4' + env: + REDIS_TAG: ${{ matrix.redis_tag}} + run: | + set -e -u -x + docker-compose -f ./.ci/docker-compose.yml -p tests up -d + sed -i '/auth.redis.server/c auth.redis.server = redis_server:6379' ./etc/emqx_auth_redis.conf + - name: setup + if: matrix.connect_type == 'tls' && matrix.network_type == 'ipv6' && matrix.redis_tag == '6.0.9' + run: | + set -e -u -x + docker network create --driver bridge --ipv6 --subnet fd15:555::/64 tests_emqx_bridge --attachable; + docker-compose -f ./.ci/docker-compose-tls.yml -p tests up -d + ipv6_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' $(docker ps -a -f name=tests_redis_server_1 -q)) + sed -i "/auth.redis.server/c auth.redis.server = $ipv6_address:6380" ./etc/emqx_auth_redis.conf + echo '\n' >> ./etc/emqx_auth_redis.conf + echo 'auth.redis.ssl = on' >> ./etc/emqx_auth_redis.conf + echo 'auth.redis.cafile = /emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.crt' >> ./etc/emqx_auth_redis.conf + echo 'auth.redis.certfile = /emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.crt' >> ./etc/emqx_auth_redis.conf + echo 'auth.redis.keyfile = /emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.key' >> ./etc/emqx_auth_redis.conf + - name: setup + if: matrix.connect_type == 'tls' && matrix.network_type == 'ipv4' && matrix.redis_tag == '6.0.9' + run: | + set -e -u -x + docker-compose -f ./.ci/docker-compose-tls.yml -p tests up -d + sed -i '/auth.redis.server/c auth.redis.server = redis_server:6380' ./etc/emqx_auth_redis.conf + echo '\n' >> ./etc/emqx_auth_redis.conf + echo 'auth.redis.ssl = on' >> ./etc/emqx_auth_redis.conf + echo 'auth.redis.cafile = /emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.crt' >> ./etc/emqx_auth_redis.conf + echo 'auth.redis.certfile = /emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.crt' >> ./etc/emqx_auth_redis.conf + echo 'auth.redis.keyfile = /emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.key' >> ./etc/emqx_auth_redis.conf + - name: run test cases + if: matrix.connect_type == 'tcp' || (matrix.connect_type == 'tls' && matrix.redis_tag == '6.0.9') + run: | + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_redis xref" + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_redis eunit" + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_redis ct" + docker exec -i tests_erlang_1 sh -c "make -C /emqx_auth_redis cover" + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: logs_redis${{ matrix.redis_tag}}_${{ matrix.network_type }}_${{ matrix.connect_type }} + path: _build/test/logs diff --git a/apps/emqx_auth_redis/.gitignore b/apps/emqx_auth_redis/.gitignore new file mode 100644 index 000000000..d7472fa8f --- /dev/null +++ b/apps/emqx_auth_redis/.gitignore @@ -0,0 +1,24 @@ +.rebar/ +.eunit/ +.erlang.mk/ +emqttd_auth_redis.d +deps/ +ct.coverdata +logs/ +test/ct.cover.spec +ebin/ +*.o +*.beam +*.plt +erl_crash.dump +data +emqx_auth_redis.d +cover/ +eunit.coverdata +_build/ +rebar.lock +erlang.mk +*.conf.rendered +.rebar3/ +*.swp +rebar.lock diff --git a/apps/emqx_auth_redis/LICENSE b/apps/emqx_auth_redis/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/apps/emqx_auth_redis/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/apps/emqx_auth_redis/README.md b/apps/emqx_auth_redis/README.md new file mode 100644 index 000000000..9aa851f88 --- /dev/null +++ b/apps/emqx_auth_redis/README.md @@ -0,0 +1,171 @@ +emqx_auth_redis +=============== + +EMQ X Redis Authentication/ACL Plugin + +Features +--------- + +- Full *Authentication*, *Superuser*, *ACL* support +- IPv4, IPv6 support +- Connection pool by [ecpool](https://github.com/emqx/ecpool) +- Support `single`, `sentinel`, `cluster` deployment structures of Redis +- Completely cover Redis 5, Redis 6 in our tests + + +Build Plugin +------------ + +``` +make && make tests +``` + +Configure Plugin +---------------- + +File: etc/emqx_auth_redis.conf + +``` +## Redis server address. +## +## Value: Port | IP:Port +## +## Redis Server: 6379, 127.0.0.1:6379, localhost:6379, Redis Sentinel: 127.0.0.1:26379 +auth.redis.server = 127.0.0.1:6379 + +## redis sentinel cluster name +## auth.redis.sentinel = mymaster + +## Redis pool size. +## +## Value: Number +auth.redis.pool = 8 + +## Redis database no. +## +## Value: Number +auth.redis.database = 0 + +## Redis password. +## +## Value: String +## auth.redis.password = + +## Authentication query command. +## +## Value: Redis cmd +## +## Variables: +## - %u: username +## - %c: clientid +## - %C: common name of client TLS cert +## - %d: subject of client TLS cert +## +## Examples: +## - HGET mqtt_user:%u password +## - HMGET mqtt_user:%u password +## - HMGET mqtt_user:%u password salt +auth.redis.auth_cmd = HMGET mqtt_user:%u password + +## Password hash. +## +## Value: plain | md5 | sha | sha256 | bcrypt +auth.redis.password_hash = plain + +## sha256 with salt prefix +## auth.redis.password_hash = salt,sha256 + +## sha256 with salt suffix +## auth.redis.password_hash = sha256,salt + +## bcrypt with salt prefix +## auth.redis.password_hash = salt,bcrypt + +## pbkdf2 with macfun iterations dklen +## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 +## auth.redis.password_hash = pbkdf2,sha256,1000,20 + +## Superuser query command. +## +## Value: Redis cmd +## +## Variables: +## - %u: username +## - %c: clientid +## - %C: common name of client TLS cert +## - %d: subject of client TLS cert +auth.redis.super_cmd = HGET mqtt_user:%u is_superuser + +## ACL query command. +## +## Value: Redis cmd +## +## Variables: +## - %u: username +## - %c: clientid +auth.redis.acl_cmd = HGETALL mqtt_acl:%u +``` + +SuperUser +--------- + +``` +HSET mqtt_user: is_superuser 1 +``` + +User Hash with Password Salt +---------------------------- + +Set a 'user' hash with 'password' 'salt' field, for example: + +``` +HMSET mqtt_user: password "password" salt "salt" +``` + +User Set with Password +----------------------- + +Set a 'user' Set with 'password' field for example: + +``` +HSET mqtt_user: password "password" +``` + +ACL Rule Hash +------------- + +The plugin uses a redis hash to store ACL rules: + +``` +HSET mqtt_acl: topic1 1 +HSET mqtt_acl: topic2 2 +HSET mqtt_acl: topic3 3 +``` + +NOTE: 1: subscribe, 2: publish, 3: pubsub + +Subscription Hash +----------------- + +NOTICE: Move to emqx_backend_redis... + +The plugin could store the static subscriptions into a redis Hash: + +``` +HSET mqtt_sub: topic1 0 +HSET mqtt_sub: topic2 1 +HSET mqtt_sub: topic3 2 +``` + +Load Plugin +----------- + +``` +./bin/emqx_ctl plugins load emqx_auth_redis +``` + +Author +------ + +EMQ X Team. + diff --git a/apps/emqx_auth_redis/etc/emqx_auth_redis.conf b/apps/emqx_auth_redis/etc/emqx_auth_redis.conf new file mode 100644 index 000000000..4d6b19b15 --- /dev/null +++ b/apps/emqx_auth_redis/etc/emqx_auth_redis.conf @@ -0,0 +1,116 @@ +##-------------------------------------------------------------------- +## Redis Auth/ACL Plugin +##-------------------------------------------------------------------- +## Redis Server cluster type +## single Single redis server +## sentinel Redis cluster through sentinel +## cluster Redis through cluster +auth.redis.type = single + +## Redis server address. +## +## Value: Port | IP:Port +## +## Single Redis Server: 127.0.0.1:6379, localhost:6379 +## Redis Sentinel: 127.0.0.1:26379,127.0.0.2:26379,127.0.0.3:26379 +## Redis Cluster: 127.0.0.1:6379,127.0.0.2:6379,127.0.0.3:6379 +auth.redis.server = 127.0.0.1:6379 + +## Redis sentinel cluster name. +## +## Value: String +## auth.redis.sentinel = mymaster + +## Redis pool size. +## +## Value: Number +auth.redis.pool = 8 + +## Redis database no. +## +## Value: Number +auth.redis.database = 0 + +## Redis password. +## +## Value: String +## auth.redis.password = + +## Redis query timeout +## +## Value: Duration +## auth.redis.query_timeout = 5s + +## Authentication query command. +## +## Value: Redis cmd +## +## Variables: +## - %u: username +## - %c: clientid +## - %C: common name of client TLS cert +## - %d: subject of client TLS cert +## +## Examples: +## - HGET mqtt_user:%u password +## - HMGET mqtt_user:%u password +## - HMGET mqtt_user:%u password salt +auth.redis.auth_cmd = HMGET mqtt_user:%u password + +## Password hash. +## +## Value: plain | md5 | sha | sha256 | bcrypt +auth.redis.password_hash = plain + +## sha256 with salt prefix +## auth.redis.password_hash = salt,sha256 + +## sha256 with salt suffix +## auth.redis.password_hash = sha256,salt + +## bcrypt with salt prefix +## auth.redis.password_hash = salt,bcrypt + +## pbkdf2 with macfun iterations dklen +## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 +## auth.redis.password_hash = pbkdf2,sha256,1000,20 + +## Superuser query command. +## +## Value: Redis cmd +## +## Variables: +## - %u: username +## - %c: clientid +## - %C: common name of client TLS cert +## - %d: subject of client TLS cert +auth.redis.super_cmd = HGET mqtt_user:%u is_superuser + +## ACL query command. +## +## Value: Redis cmd +## +## Variables: +## - %u: username +## - %c: clientid +auth.redis.acl_cmd = HGETALL mqtt_acl:%u + +## Redis ssl configuration. +## +## Value: on | off +#auth.redis.ssl = off + +## CA certificate. +## +## Value: File +#auth.redis.cafile = path/to/your/cafile + +## Client ssl certificate. +## +## Value: File +#auth.redis.certfile = path/to/your/certfile + +## Client ssl keyfile. +## +## Value: File +#auth.redis.keyfile = path/to/your/keyfile \ No newline at end of file diff --git a/apps/emqx_auth_redis/include/emqx_auth_redis.hrl b/apps/emqx_auth_redis/include/emqx_auth_redis.hrl new file mode 100644 index 000000000..204d8ef70 --- /dev/null +++ b/apps/emqx_auth_redis/include/emqx_auth_redis.hrl @@ -0,0 +1,23 @@ + +-define(APP, emqx_auth_redis). + +-record(auth_metrics, { + success = 'client.auth.success', + failure = 'client.auth.failure', + ignore = 'client.auth.ignore' + }). + +-record(acl_metrics, { + allow = 'client.acl.allow', + deny = 'client.acl.deny', + ignore = 'client.acl.ignore' + }). + +-define(METRICS(Type), tl(tuple_to_list(#Type{}))). +-define(METRICS(Type, K), #Type{}#Type.K). + +-define(AUTH_METRICS, ?METRICS(auth_metrics)). +-define(AUTH_METRICS(K), ?METRICS(auth_metrics, K)). + +-define(ACL_METRICS, ?METRICS(acl_metrics)). +-define(ACL_METRICS(K), ?METRICS(acl_metrics, K)). diff --git a/apps/emqx_auth_redis/priv/emqx_auth_redis.schema b/apps/emqx_auth_redis/priv/emqx_auth_redis.schema new file mode 100644 index 000000000..d51b9c1b2 --- /dev/null +++ b/apps/emqx_auth_redis/priv/emqx_auth_redis.schema @@ -0,0 +1,139 @@ +%%-*- mode: erlang -*- +%% emqx_auth_redis config mapping + +{mapping, "auth.redis.type", "emqx_auth_redis.server", [ + {default, single}, + {datatype, {enum, [single, sentinel, cluster]}} +]}. + +{mapping, "auth.redis.server", "emqx_auth_redis.server", [ + {default, "127.0.0.1:6379"}, + {datatype, [string]} +]}. + +{mapping, "auth.redis.sentinel", "emqx_auth_redis.server", [ + {default, ""}, + {datatype, string}, + hidden +]}. + +{mapping, "auth.redis.pool", "emqx_auth_redis.server", [ + {default, 8}, + {datatype, integer} +]}. + +{mapping, "auth.redis.database", "emqx_auth_redis.server", [ + {default, 0}, + {datatype, integer} +]}. + +{mapping, "auth.redis.password", "emqx_auth_redis.server", [ + {default, ""}, + {datatype, string}, + hidden +]}. + +{mapping, "auth.redis.ssl", "emqx_auth_redis.options", [ + {default, off}, + {datatype, flag} +]}. + +{mapping, "auth.redis.cafile", "emqx_auth_redis.options", [ + {default, ""}, + {datatype, string} +]}. + +{mapping, "auth.redis.certfile", "emqx_auth_redis.options", [ + {default, ""}, + {datatype, string} +]}. + +{mapping, "auth.redis.keyfile", "emqx_auth_redis.options", [ + {default, ""}, + {datatype, string} +]}. + +{translation, "emqx_auth_redis.options", fun(Conf) -> + Ssl = cuttlefish:conf_get("auth.redis.ssl", Conf, false), + case Ssl of + true -> + CA = cuttlefish:conf_get("auth.redis.cafile", Conf), + Cert = cuttlefish:conf_get("auth.redis.certfile", Conf), + Key = cuttlefish:conf_get("auth.redis.keyfile", Conf), + [{options, [{ssl_options, [{cacertfile, CA}, + {certfile, Cert}, + {keyfile, Key}]}]}]; + _ -> [{options, []}] + end +end}. + +{translation, "emqx_auth_redis.server", fun(Conf) -> + Fun = fun(S) -> + case string:split(S, ":", trailing) of + [Domain] -> {Domain, 6379}; + [Domain, Port] -> {Domain, list_to_integer(Port)} + end + end, + Servers = cuttlefish:conf_get("auth.redis.server", Conf), + Type = cuttlefish:conf_get("auth.redis.type", Conf), + Server = case Type of + single -> + {Host, Port} = Fun(Servers), + [{host, Host}, {port, Port}]; + _ -> + S = string:tokens(Servers, ","), + [{servers, [Fun(S1) || S1 <- S]}] + end, + Pool = cuttlefish:conf_get("auth.redis.pool", Conf), + Passwd = cuttlefish:conf_get("auth.redis.password", Conf), + DB = cuttlefish:conf_get("auth.redis.database", Conf), + Sentinel = cuttlefish:conf_get("auth.redis.sentinel", Conf), + [{type, Type}, + {pool_size, Pool}, + {auto_reconnect, 1}, + {database, DB}, + {password, Passwd}, + {sentinel, Sentinel}] ++ Server +end}. + +{mapping, "auth.redis.query_timeout", "emqx_auth_redis.query_timeout", [ + {default, ""}, + {datatype, string} +]}. + +{translation, "emqx_auth_redis.query_timeout", fun(Conf) -> + case cuttlefish:conf_get("auth.redis.query_timeout", Conf) of + "" -> infinity; + Duration -> + case cuttlefish_duration:parse(Duration, ms) of + {error, Reason} -> error(Reason); + Ms when is_integer(Ms) -> Ms + end + end +end}. + +{mapping, "auth.redis.auth_cmd", "emqx_auth_redis.auth_cmd", [ + {datatype, string} +]}. + +{mapping, "auth.redis.password_hash", "emqx_auth_redis.password_hash", [ + {datatype, string} +]}. + +{mapping, "auth.redis.super_cmd", "emqx_auth_redis.super_cmd", [ + {datatype, string} +]}. + +{mapping, "auth.redis.acl_cmd", "emqx_auth_redis.acl_cmd", [ + {datatype, string} +]}. + +{translation, "emqx_auth_redis.password_hash", fun(Conf) -> + HashValue = cuttlefish:conf_get("auth.redis.password_hash", Conf), + case string:tokens(HashValue, ",") of + [Hash] -> list_to_atom(Hash); + [Prefix, Suffix] -> {list_to_atom(Prefix), list_to_atom(Suffix)}; + [Hash, MacFun, Iterations, Dklen] -> {list_to_atom(Hash), list_to_atom(MacFun), list_to_integer(Iterations), list_to_integer(Dklen)}; + _ -> plain + end +end}. diff --git a/apps/emqx_auth_redis/rebar.config b/apps/emqx_auth_redis/rebar.config new file mode 100644 index 000000000..5fac9befb --- /dev/null +++ b/apps/emqx_auth_redis/rebar.config @@ -0,0 +1,33 @@ +{deps, + [{eredis_cluster, {git, "https://github.com/emqx/eredis_cluster", {tag, "0.6.2"}}}, + {ecpool, {git, "https://github.com/emqx/ecpool", {tag, "v0.4.2"}}}, + {emqx_passwd, {git, "https://github.com/emqx/emqx-passwd", {tag, "v1.1.1"}}} + ]}. + +{erl_opts, [warn_unused_vars, + warn_shadow_vars, + warn_unused_import, + warn_obsolete_guard, + debug_info, + compressed + ]}. +{overrides, [{add, [{erl_opts, [compressed]}]}]}. + +{xref_checks, [undefined_function_calls, undefined_functions, + locals_not_used, deprecated_function_calls, + warnings_as_errors, deprecated_functions + ]}. + +{cover_enabled, true}. +{cover_opts, [verbose]}. +{cover_export_enabled, true}. + +{profiles, + [{test, + [{deps, + [{emqx_ct_helper, {git, "https://github.com/emqx/emqx-ct-helper", {tag, "1.2.2"}}}, + {emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.2.0"}}} + ]}, + {erl_opts, [debug_info]} + ]} + ]}. diff --git a/apps/emqx_auth_redis/src/emqx_acl_redis.erl b/apps/emqx_auth_redis/src/emqx_acl_redis.erl new file mode 100644 index 000000000..096523487 --- /dev/null +++ b/apps/emqx_auth_redis/src/emqx_acl_redis.erl @@ -0,0 +1,86 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_acl_redis). + +-include("emqx_auth_redis.hrl"). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-export([ register_metrics/0 + , check_acl/5 + , description/0 + ]). + +-spec(register_metrics() -> ok). +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, ?ACL_METRICS). + +check_acl(ClientInfo, PubSub, Topic, AclResult, Config) -> + case do_check_acl(ClientInfo, PubSub, Topic, AclResult, Config) of + ok -> emqx_metrics:inc(?ACL_METRICS(ignore)), ok; + {stop, allow} -> emqx_metrics:inc(?ACL_METRICS(allow)), {stop, allow}; + {stop, deny} -> emqx_metrics:inc(?ACL_METRICS(deny)), {stop, deny} + end. + +do_check_acl(#{username := <<$$, _/binary>>}, _PubSub, _Topic, _AclResult, _Config) -> + ok; +do_check_acl(ClientInfo, PubSub, Topic, _AclResult, + #{acl_cmd := AclCmd, timeout := Timeout, type := Type, pool := Pool}) -> + case emqx_auth_redis_cli:q(Pool, Type, AclCmd, ClientInfo, Timeout) of + {ok, []} -> ok; + {ok, Rules} -> + case match(ClientInfo, PubSub, Topic, Rules) of + allow -> {stop, allow}; + nomatch -> {stop, deny} + end; + {error, Reason} -> + ?LOG(error, "[Redis] do_check_acl error: ~p", [Reason]), + ok + end. + +match(_ClientInfo, _PubSub, _Topic, []) -> + nomatch; +match(ClientInfo, PubSub, Topic, [Filter, Access | Rules]) -> + case {match_topic(Topic, feed_var(ClientInfo, Filter)), + match_access(PubSub, b2i(Access))} of + {true, true} -> allow; + {_, _} -> match(ClientInfo, PubSub, Topic, Rules) + end. + +match_topic(Topic, Filter) -> + emqx_topic:match(Topic, Filter). + +match_access(subscribe, Access) -> + (1 band Access) > 0; +match_access(publish, Access) -> + (2 band Access) > 0. + +feed_var(#{clientid := ClientId, username := Username}, Str) -> + lists:foldl(fun({Var, Val}, Acc) -> + feed_var(Acc, Var, Val) + end, Str, [{"%u", Username}, {"%c", ClientId}]). + +feed_var(Str, _Var, undefined) -> + Str; +feed_var(Str, Var, Val) -> + re:replace(Str, Var, Val, [global, {return, binary}]). + +b2i(Bin) -> list_to_integer(binary_to_list(Bin)). + +description() -> "Redis ACL Module". + diff --git a/apps/emqx_auth_redis/src/emqx_auth_redis.app.src b/apps/emqx_auth_redis/src/emqx_auth_redis.app.src new file mode 100644 index 000000000..1bd044e7f --- /dev/null +++ b/apps/emqx_auth_redis/src/emqx_auth_redis.app.src @@ -0,0 +1,14 @@ +{application, emqx_auth_redis, + [{description, "EMQ X Authentication/ACL with Redis"}, + {vsn, "git"}, + {modules, []}, + {registered, [emqx_auth_redis_sup]}, + {applications, [kernel,stdlib,eredis,eredis_cluster,ecpool,emqx_passwd]}, + {mod, {emqx_auth_redis_app, []}}, + {env, []}, + {licenses, ["Apache-2.0"]}, + {maintainers, ["EMQ X Team "]}, + {links, [{"Homepage", "https://emqx.io/"}, + {"Github", "https://github.com/emqx/emqx-auth-redis"} + ]} + ]}. diff --git a/apps/emqx_auth_redis/src/emqx_auth_redis.app.src.script b/apps/emqx_auth_redis/src/emqx_auth_redis.app.src.script new file mode 100644 index 000000000..0e14ff23f --- /dev/null +++ b/apps/emqx_auth_redis/src/emqx_auth_redis.app.src.script @@ -0,0 +1,24 @@ +%%-*- mode: erlang -*- +%% .app.src.script + +RemoveLeadingV = + fun(Tag) -> + case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of + nomatch -> + re:replace(Tag, "/", "-", [{return ,list}]); + _ -> + %% if it is a version number prefixed by 'v' or 'e', then remove it + re:replace(Tag, "[v|e]", "", [{return ,list}]) + end + end, + +case os:getenv("EMQX_DEPS_DEFAULT_VSN") of + false -> CONFIG; % env var not defined + [] -> CONFIG; % env var set to empty string + Tag -> + [begin + AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}), + {application, App, AppConf0} + end || Conf = {application, App, AppConf} <- CONFIG] +end. + diff --git a/apps/emqx_auth_redis/src/emqx_auth_redis.appup.src b/apps/emqx_auth_redis/src/emqx_auth_redis.appup.src new file mode 100644 index 000000000..671b9274f --- /dev/null +++ b/apps/emqx_auth_redis/src/emqx_auth_redis.appup.src @@ -0,0 +1,36 @@ +{"4.2.3", + [ + {"4.2.2", [ + {load_module, emqx_auth_redis_cli, brutal_purge, soft_purge, []}, + {load_module, emqx_auth_redis_sup, brutal_purge, soft_purge, []} + ] + }, + {"4.2.1", [ + {load_module, emqx_auth_redis_cli, brutal_purge, soft_purge, []}, + {load_module, emqx_auth_redis_sup, brutal_purge, soft_purge, []} + ] + }, + {"4.2.0", [ + {load_module, emqx_auth_redis_cli, brutal_purge, soft_purge, []}, + {load_module, emqx_auth_redis_sup, brutal_purge, soft_purge, []} + ] + } + ], + [ + {"4.2.2", [ + {load_module, emqx_auth_redis_cli, brutal_purge, soft_purge, []}, + {load_module, emqx_auth_redis_sup, brutal_purge, soft_purge, []} + ] + }, + {"4.2.1", [ + {load_module, emqx_auth_redis_cli, brutal_purge, soft_purge, []}, + {load_module, emqx_auth_redis_sup, brutal_purge, soft_purge, []} + ] + }, + {"4.2.0", [ + {load_module, emqx_auth_redis_cli, brutal_purge, soft_purge, []}, + {load_module, emqx_auth_redis_sup, brutal_purge, soft_purge, []} + ] + } + ] +}. \ No newline at end of file diff --git a/apps/emqx_auth_redis/src/emqx_auth_redis.erl b/apps/emqx_auth_redis/src/emqx_auth_redis.erl new file mode 100644 index 000000000..65f4d9735 --- /dev/null +++ b/apps/emqx_auth_redis/src/emqx_auth_redis.erl @@ -0,0 +1,85 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_redis). + +-include("emqx_auth_redis.hrl"). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-export([ register_metrics/0 + , check/3 + , description/0 + ]). + +-spec(register_metrics() -> ok). +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, ?AUTH_METRICS). + +check(ClientInfo = #{password := Password}, AuthResult, + #{auth_cmd := AuthCmd, + super_cmd := SuperCmd, + hash_type := HashType, + timeout := Timeout, + type := Type, + pool := Pool}) -> + CheckPass = case emqx_auth_redis_cli:q(Pool, Type, AuthCmd, ClientInfo, Timeout) of + {ok, PassHash} when is_binary(PassHash) -> + check_pass({PassHash, Password}, HashType); + {ok, [undefined|_]} -> + {error, not_found}; + {ok, [PassHash]} -> + check_pass({PassHash, Password}, HashType); + {ok, [PassHash, Salt|_]} -> + check_pass({PassHash, Salt, Password}, HashType); + {error, Reason} -> + ?LOG(error, "[Redis] Command: ~p failed: ~p", [AuthCmd, Reason]), + {error, not_found} + end, + case CheckPass of + ok -> + ok = emqx_metrics:inc(?AUTH_METRICS(success)), + IsSuperuser = is_superuser(Pool, Type, SuperCmd, ClientInfo, Timeout), + {stop, AuthResult#{is_superuser => IsSuperuser, + anonymous => false, + auth_result => success}}; + {error, not_found} -> + ok = emqx_metrics:inc(?AUTH_METRICS(ignore)); + {error, ResultCode} -> + ok = emqx_metrics:inc(?AUTH_METRICS(failure)), + ?LOG(error, "[Redis] Auth from redis failed: ~p", [ResultCode]), + {stop, AuthResult#{auth_result => ResultCode, anonymous => false}} + end. + +description() -> "Authentication with Redis". + +-spec(is_superuser(atom(), atom(), undefined|list(), emqx_types:client(), timeout()) -> boolean()). +is_superuser(_Pool, _Type, undefined, _ClientInfo, _Timeout) -> false; +is_superuser(Pool, Type, SuperCmd, ClientInfo, Timeout) -> + case emqx_auth_redis_cli:q(Pool, Type, SuperCmd, ClientInfo, Timeout) of + {ok, undefined} -> false; + {ok, <<"1">>} -> true; + {ok, _Other} -> false; + {error, _Error} -> false + end. + +check_pass(Password, HashType) -> + case emqx_passwd:check_pass(Password, HashType) of + ok -> ok; + {error, _Reason} -> {error, not_authorized} + end. + diff --git a/apps/emqx_auth_redis/src/emqx_auth_redis_app.erl b/apps/emqx_auth_redis/src/emqx_auth_redis_app.erl new file mode 100644 index 000000000..345f9f87d --- /dev/null +++ b/apps/emqx_auth_redis/src/emqx_auth_redis_app.erl @@ -0,0 +1,70 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_redis_app). + +-behaviour(application). + +-emqx_plugin(auth). + +-include("emqx_auth_redis.hrl"). + +-export([ start/2 + , stop/1 + ]). + +start(_StartType, _StartArgs) -> + {ok, Sup} = emqx_auth_redis_sup:start_link(), + if_cmd_enabled(auth_cmd, fun load_auth_hook/1), + if_cmd_enabled(acl_cmd, fun load_acl_hook/1), + {ok, Sup}. + +stop(_State) -> + emqx:unhook('client.authenticate', fun emqx_auth_redis:check/3), + emqx:unhook('client.check_acl', fun emqx_acl_redis:check_acl/5), + %% Ensure stop cluster pool if the server type is cluster + eredis_cluster:stop_pool(?APP). + +load_auth_hook(AuthCmd) -> + SuperCmd = application:get_env(?APP, super_cmd, undefined), + {ok, HashType} = application:get_env(?APP, password_hash), + {ok, Timeout} = application:get_env(?APP, query_timeout), + Type = proplists:get_value(type, application:get_env(?APP, server, [])), + Config = #{auth_cmd => AuthCmd, + super_cmd => SuperCmd, + hash_type => HashType, + timeout => Timeout, + type => Type, + pool => ?APP}, + ok = emqx_auth_redis:register_metrics(), + emqx:hook('client.authenticate', fun emqx_auth_redis:check/3, [Config]). + +load_acl_hook(AclCmd) -> + {ok, Timeout} = application:get_env(?APP, query_timeout), + Type = proplists:get_value(type, application:get_env(?APP, server, [])), + Config = #{acl_cmd => AclCmd, + timeout => Timeout, + type => Type, + pool => ?APP}, + ok = emqx_acl_redis:register_metrics(), + emqx:hook('client.check_acl', fun emqx_acl_redis:check_acl/5, [Config]). + +if_cmd_enabled(Par, Fun) -> + case application:get_env(?APP, Par) of + {ok, Cmd} -> Fun(Cmd); + undefined -> ok + end. + diff --git a/apps/emqx_auth_redis/src/emqx_auth_redis_cli.erl b/apps/emqx_auth_redis/src/emqx_auth_redis_cli.erl new file mode 100644 index 000000000..31cb67505 --- /dev/null +++ b/apps/emqx_auth_redis/src/emqx_auth_redis_cli.erl @@ -0,0 +1,89 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_redis_cli). + +-behaviour(ecpool_worker). + +-include("emqx_auth_redis.hrl"). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-import(proplists, [get_value/2, get_value/3]). + +-export([ connect/1 + , q/5 + ]). + +%%-------------------------------------------------------------------- +%% Redis Connect/Query +%%-------------------------------------------------------------------- + +connect(Opts) -> + Sentinel = get_value(sentinel, Opts), + Host = case Sentinel =:= "" of + true -> get_value(host, Opts); + false -> + eredis_sentinel:start_link(get_value(servers, Opts)), + "sentinel:" ++ Sentinel + end, + case eredis:start_link(Host, + get_value(port, Opts, 6379), + get_value(database, Opts, 0), + get_value(password, Opts, ""), + 3000, + 5000, + get_value(options, Opts, [])) of + {ok, Pid} -> {ok, Pid}; + {error, Reason = {connection_error, _}} -> + ?LOG(error, "[Redis] Can't connect to Redis server: Connection refused."), + {error, Reason}; + {error, Reason = {authentication_error, _}} -> + ?LOG(error, "[Redis] Can't connect to Redis server: Authentication failed."), + {error, Reason}; + {error, Reason} -> + ?LOG(error, "[Redis] Can't connect to Redis server: ~p", [Reason]), + {error, Reason} + end. + +%% Redis Query. +-spec(q(atom(), atom(), string(), emqx_types:credentials(), timeout()) + -> {ok, undefined | binary() | list()} | {error, atom() | binary()}). +q(Pool, Type, CmdStr, Credentials, Timeout) -> + Cmd = string:tokens(replvar(CmdStr, Credentials), " "), + case Type of + cluster -> eredis_cluster:q(Pool, Cmd); + _ -> ecpool:with_client(Pool, fun(C) -> eredis:q(C, Cmd, Timeout) end) + end. + +replvar(Cmd, Credentials = #{cn := CN}) -> + replvar(repl(Cmd, "%C", CN), maps:remove(cn, Credentials)); +replvar(Cmd, Credentials = #{dn := DN}) -> + replvar(repl(Cmd, "%d", DN), maps:remove(dn, Credentials)); +replvar(Cmd, Credentials = #{clientid := ClientId}) -> + replvar(repl(Cmd, "%c", ClientId), maps:remove(clientid, Credentials)); +replvar(Cmd, Credentials = #{username := Username}) -> + replvar(repl(Cmd, "%u", Username), maps:remove(username, Credentials)); +replvar(Cmd, _) -> + Cmd. + +repl(S, _Var, undefined) -> + S; +repl(S, Var, Val) -> + NVal = re:replace(Val, "&", "\\\\&", [global, {return, list}]), + re:replace(S, Var, NVal, [{return, list}]). + diff --git a/apps/emqx_auth_redis/src/emqx_auth_redis_sup.erl b/apps/emqx_auth_redis/src/emqx_auth_redis_sup.erl new file mode 100644 index 000000000..6066a306a --- /dev/null +++ b/apps/emqx_auth_redis/src/emqx_auth_redis_sup.erl @@ -0,0 +1,43 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_redis_sup). + +-behaviour(supervisor). + +-include("emqx_auth_redis.hrl"). + +-export([start_link/0]). + +-export([init/1]). + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init([]) -> + {ok, Server} = application:get_env(?APP, server), + {ok, {{one_for_one, 10, 100}, pool_spec(Server)}}. + +pool_spec(Server) -> + Options = application:get_env(?APP, options, []), + case proplists:get_value(type, Server) of + cluster -> + eredis_cluster:start_pool(?APP, Server ++ Options), + []; + _ -> + [ecpool:pool_spec(?APP, ?APP, emqx_auth_redis_cli, Server ++ Options)] + end. + diff --git a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE.erl b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE.erl new file mode 100644 index 000000000..427806f4e --- /dev/null +++ b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE.erl @@ -0,0 +1,174 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_auth_redis_SUITE). + +-compile(export_all). + +-include_lib("emqx/include/emqx.hrl"). + +-include_lib("common_test/include/ct.hrl"). + +-include_lib("eunit/include/eunit.hrl"). + +-define(APP, emqx_auth_redis). + +-define(POOL(App), ecpool_worker:client(gproc_pool:pick_worker({ecpool, App}))). + +-define(INIT_ACL, [{"mqtt_acl:test1", "topic1", "2"}, + {"mqtt_acl:test2", "topic2", "1"}, + {"mqtt_acl:test3", "topic3", "3"}]). + +-define(INIT_AUTH, [{"mqtt_user:plain", ["password", "plain", "salt", "salt", "is_superuser", "1"]}, + {"mqtt_user:special&symbol", ["password", "plain", "salt", "salt", "is_superuser", "0"]}, + {"mqtt_user:md5", ["password", "1bc29b36f623ba82aaf6724fd3b16718", "salt", "salt", "is_superuser", "0"]}, + {"mqtt_user:sha", ["password", "d8f4590320e1343a915b6394170650a8f35d6926", "salt", "salt", "is_superuser", "0"]}, + {"mqtt_user:sha256", ["password", "5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e", "salt", "salt", "is_superuser", "0"]}, + {"mqtt_user:pbkdf2_password", ["password", "cdedb5281bb2f801565a1122b2563515", "salt", "ATHENA.MIT.EDUraeburn", "is_superuser", "0"]}, + {"mqtt_user:bcrypt_foo", ["password", "$2a$12$sSS8Eg.ovVzaHzi1nUHYK.HbUIOdlQI0iS22Q5rd5z.JVVYH6sfm6", "salt", "$2a$12$sSS8Eg.ovVzaHzi1nUHYK.", "is_superuser", "0"]}, + {"mqtt_user:bcrypt", ["password", "$2y$16$rEVsDarhgHYB0TGnDFJzyu5f.T.Ha9iXMTk9J36NCMWWM7O16qyaK", "salt", "salt", "is_superuser", "0"]}]). + +%%-------------------------------------------------------------------- +%% Setups +%%-------------------------------------------------------------------- + +all() -> + emqx_ct:all(?MODULE). + +init_per_suite(Cfg) -> + emqx_ct_helpers:start_apps([emqx_auth_redis], fun set_special_configs/1), + init_redis_rows(), + Cfg. + +end_per_suite(_Cfg) -> + deinit_redis_rows(), + emqx_ct_helpers:stop_apps([emqx_auth_redis]). + +set_special_configs(emqx) -> + application:set_env(emqx, allow_anonymous, false), + application:set_env(emqx, acl_nomatch, deny), + application:set_env(emqx, acl_file, + emqx_ct_helpers:deps_path(emqx, "test/emqx_SUITE_data/acl.conf")), + application:set_env(emqx, enable_acl_cache, false), + application:set_env(emqx, plugins_loaded_file, + emqx_ct_helpers:deps_path(emqx, "test/emqx_SUITE_data/loaded_plugins")); +set_special_configs(_App) -> + ok. + +init_redis_rows() -> + {ok, Connection} = ?POOL(?APP), + %% Users + [eredis:q(Connection, ["HMSET", Key|FiledValue]) || {Key, FiledValue} <- ?INIT_AUTH], + + %% ACLs + emqx_modules:load_module(emqx_mod_acl_internal, false), + Result = [eredis:q(Connection, ["HSET", Key, Filed, Value]) || {Key, Filed, Value} <- ?INIT_ACL], + ct:pal("redis init result: ~p~n", [Result]). + +deinit_redis_rows() -> + {ok, Connection} = ?POOL(?APP), + AuthKeys = [Key || {Key, _Filed, _Value} <- ?INIT_AUTH], + AclKeys = [Key || {Key, _Value} <- ?INIT_ACL], + eredis:q(Connection, ["DEL" | AuthKeys]), + eredis:q(Connection, ["DEL" | AclKeys]). + +%%-------------------------------------------------------------------- +%% Cases +%%-------------------------------------------------------------------- + +t_check_auth(_) -> + Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external}, + SpecialSymbol = #{clientid => <<"special_symbol">>, username => <<"special&symbol">>, zone => external}, + Md5 = #{clientid => <<"md5">>, username => <<"md5">>, zone => external}, + Sha = #{clientid => <<"sha">>, username => <<"sha">>, zone => external}, + Sha256 = #{clientid => <<"sha256">>, username => <<"sha256">>, zone => external}, + Pbkdf2 = #{clientid => <<"pbkdf2_password">>, username => <<"pbkdf2_password">>, zone => external}, + BcryptFoo = #{clientid => <<"bcrypt_foo">>, username => <<"bcrypt_foo">>, zone => external}, + User1 = #{clientid => <<"bcrypt_foo">>, username => <<"user">>, zone => external}, + User3 = #{clientid => <<"client3">>, zone => external}, + Bcrypt = #{clientid => <<"bcrypt">>, username => <<"bcrypt">>, zone => external}, + {error, _} = emqx_access_control:authenticate(User3#{password => <<>>}), + reload([{password_hash, plain}]), + {ok, #{is_superuser := true}} = emqx_access_control:authenticate(Plain#{password => <<"plain">>}), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(SpecialSymbol#{password => <<"plain">>}), + reload([{password_hash, md5}]), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Md5#{password => <<"md5">>}), + reload([{password_hash, sha}]), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Sha#{password => <<"sha">>}), + reload([{password_hash, sha256}]), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Sha256#{password => <<"sha256">>}), + reload([{password_hash, bcrypt}]), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}), + %%pbkdf2 sha + reload([{password_hash, {pbkdf2, sha, 1, 16}}, {auth_cmd, "HMGET mqtt_user:%u password salt"}]), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(Pbkdf2#{password => <<"password">>}), + reload([{password_hash, {salt, bcrypt}}]), + {ok, #{is_superuser := false}} = emqx_access_control:authenticate(BcryptFoo#{password => <<"foo">>}), + {error,_} = emqx_access_control:authenticate(User1#{password => <<"foo">>}), + {error, _} = emqx_access_control:authenticate(Bcrypt#{password => <<"password">>}). + +t_check_auth_hget(_) -> + {ok, Connection} = ?POOL(?APP), + eredis:q(Connection, ["HSET", "mqtt_user:hset", "password", "hset"]), + eredis:q(Connection, ["HSET", "mqtt_user:hset", "is_superuser", "1"]), + reload([{password_hash, plain}, {auth_cmd, "HGET mqtt_user:%u password"}]), + Hset = #{clientid => <<"hset">>, username => <<"hset">>, zone => external}, + {ok, #{is_superuser := true}} = emqx_access_control:authenticate(Hset#{password => <<"hset">>}). + +t_check_acl(_) -> + User1 = #{zone => external, clientid => <<"client1">>, username => <<"test1">>}, + User2 = #{zone => external, clientid => <<"client2">>, username => <<"test2">>}, + User3 = #{zone => external, clientid => <<"client3">>, username => <<"test3">>}, + User4 = #{zone => external, clientid => <<"client4">>, username => <<"$$user4">>}, + deny = emqx_access_control:check_acl(User1, subscribe, <<"topic1">>), + allow = emqx_access_control:check_acl(User1, publish, <<"topic1">>), + + deny = emqx_access_control:check_acl(User2, publish, <<"topic2">>), + allow = emqx_access_control:check_acl(User2, subscribe, <<"topic2">>), + allow = emqx_access_control:check_acl(User3, publish, <<"topic3">>), + allow = emqx_access_control:check_acl(User3, subscribe, <<"topic3">>), + allow = emqx_access_control:check_acl(User4, publish, <<"a/b/c">>). + +t_acl_super(_) -> + reload([{password_hash, plain}]), + {ok, C} = emqtt:start_link([{host, "localhost"}, + {clientid, <<"simpleClient">>}, + {username, <<"plain">>}, + {password, <<"plain">>}]), + {ok, _} = emqtt:connect(C), + timer:sleep(10), + emqtt:subscribe(C, <<"TopicA">>, qos2), + timer:sleep(1000), + emqtt:publish(C, <<"TopicA">>, <<"Payload">>, qos2), + timer:sleep(1000), + receive + {publish, #{payload := Payload}} -> + ?assertEqual(<<"Payload">>, Payload) + after + 1000 -> + ct:fail({receive_timeout, <<"Payload">>}), + ok + end, + emqtt:disconnect(C). + +%%-------------------------------------------------------------------- +%% Internal funcs +%%-------------------------------------------------------------------- + +reload(Config) when is_list(Config) -> + application:stop(?APP), + [application:set_env(?APP, K, V) || {K, V} <- Config], + application:start(?APP). diff --git a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.crt b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.crt new file mode 100644 index 000000000..b46bef4e5 --- /dev/null +++ b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.crt @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIE5jCCAs4CCQCc1DzEYETfKTANBgkqhkiG9w0BAQsFADA1MRMwEQYDVQQKDApS +ZWRpcyBUZXN0MR4wHAYDVQQDDBVDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMjAx +MDI5MDEzNDE2WhcNMzAxMDI3MDEzNDE2WjA1MRMwEQYDVQQKDApSZWRpcyBUZXN0 +MR4wHAYDVQQDDBVDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQC/RxC/zQ6+ThI2l+LT5tpuvljE7CPca5erahTjv1Pq +mbmHYIVlige9jvZKR/AaaHuhNRT6C4PDpD98TgrhSLSgMMFImoFMSnmFEOVave3O +y1qV9vtoHLMB9hO+t7P98KRi1sCoMdPIE/o5uEGSd4YgWbk3NllAV6me108UniWU +yZMCSEKmV9OpfQ+YfHFolESV92ajdViDbtRBjfDNwD7qb8zgigxIJvBzEnWF4RZl +4+KIiyoJ55AQ3omdEi0QwiRRRONFtB6kRSqjGS8genGnycX1ZNPRB8JeG3ESuFj9 +1WQUD0EMBXFB5agHoZjvtFwxOkUkA4XbcnpKddHGKRt4BAbm+YcizJaT7mRytGWZ +UoTrDWz8/Cc0BlwAfPEk6ogU/sLSZpdxjxwprCNB89UOI+q7ng7CYiFnxY9HHZeg +GCJxYfvpKM/eOT9mSLUug8EGITd0j2cusflO4Q243clPyRbTSSr39Pcpy8rfKApF +HkUuGIpa/qgAbez+lPlIydzpbrTgrnHvL1P6fCYTnHkcgSn8glBIKv3vh4zQd6df +JvcLv3WEka9+lyoCvJ0QH+/ITqrToyWa8g9fR3ajTlyMANesKxQejo80zCwk/0ns +SFKRIJc6vfnUJ12Vdxpmm1LeoJZnCYODNUeeksL1ahHCBGq4M8UJ+ycUM6N4ndWE +6QIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQAg0BaIi7lzrNb7xC42c+GJVrBq8Qf8 +7CBzP8SXYGUavQIYNRrtH8UgTOwaju9vOn3zoY8L59N6e+Icyt+Oh1FENcQMCZ2l +SP79iaY9A/dRV56p6NqNd3VWH+EuRGbQVatLdhJf3l5+W1z3Dum1YXmIn26acawF +GZVqLalgvLqPzPHHWEqz9RnmcvTu3w9YVb4NgbmY4byCb6mB2avt0iWQrY/fZSMe +FvRXurr0jwyIXBncqnXu97sCeccNc+fo3qZC1xxH9iXOIzrRg0ud7VGMTKcNLTTc +GqnbjNT8BC96Qp2Bs8J+JGZa3mT/usKBq2TT/3q6oKevuc23u/a5s1rztnqZgIe5 +RzfevJ79xdva6DMSq/8Yyd3I8hrs3oZKJbAce6ux01RsrCcY2O7gi4dAMoEGumxW +CS9XLchNy7QxQ+J2AKBZXd6AZjvTvloDGz/yC5EbdK/MnLz8oApK5Z8U/huEilFa +AymVWQWpmlX2KxW0nkCperlb7lcbPS+ZuH0+Zd9HOvqr9cpYMrwpF54q4vnzUQkR +Hsxoapv/FBsVoxtcOqrcxwGpYWCsV0VBnv9+1fzzZ83aK7CHDIeGVuKPyjkhHzLy +v7Ljuqg400wH0WB9pyEdK+O3F+xO3zJgf4o0JptOKOFBVVSkZWTrqlDjjbcnXBmh +dwgj2xYeigqHJA== +-----END CERTIFICATE----- diff --git a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.key b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.key new file mode 100644 index 000000000..b615a8c1e --- /dev/null +++ b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKAIBAAKCAgEAv0cQv80Ovk4SNpfi0+babr5YxOwj3GuXq2oU479T6pm5h2CF +ZYoHvY72SkfwGmh7oTUU+guDw6Q/fE4K4Ui0oDDBSJqBTEp5hRDlWr3tzstalfb7 +aByzAfYTvrez/fCkYtbAqDHTyBP6ObhBkneGIFm5NzZZQFepntdPFJ4llMmTAkhC +plfTqX0PmHxxaJRElfdmo3VYg27UQY3wzcA+6m/M4IoMSCbwcxJ1heEWZePiiIsq +CeeQEN6JnRItEMIkUUTjRbQepEUqoxkvIHpxp8nF9WTT0QfCXhtxErhY/dVkFA9B +DAVxQeWoB6GY77RcMTpFJAOF23J6SnXRxikbeAQG5vmHIsyWk+5kcrRlmVKE6w1s +/PwnNAZcAHzxJOqIFP7C0maXcY8cKawjQfPVDiPqu54OwmIhZ8WPRx2XoBgicWH7 +6SjP3jk/Zki1LoPBBiE3dI9nLrH5TuENuN3JT8kW00kq9/T3KcvK3ygKRR5FLhiK +Wv6oAG3s/pT5SMnc6W604K5x7y9T+nwmE5x5HIEp/IJQSCr974eM0HenXyb3C791 +hJGvfpcqArydEB/vyE6q06MlmvIPX0d2o05cjADXrCsUHo6PNMwsJP9J7EhSkSCX +Or351CddlXcaZptS3qCWZwmDgzVHnpLC9WoRwgRquDPFCfsnFDOjeJ3VhOkCAwEA +AQKCAgBF1jSPUtcnNGoB9MKki40FEgpnG7CcMcxWkYy++oQxC59phhwuTo807pWN +2WYYvj0lRrQ59ypMrBNh1zyxtFH+is6HK6I5sJddtiWHVAEXl7ejOWHhSVkyRh4/ +a+MTvGDIlZAR2N9yFZkuqc+HIoyeEyREvFsp2tfbXtFIvdUK1e4Oz0NGaJqnLzoa +epUNkdTYzFN1Ksr+ceCdbq2U8bQG9HrhIIYLcewol3zBPMVoviNfpy/aHenDvvyP +lKtPixKneXdhY7osT/SZSACk4w/MKydTyVRs5WBZ67sFErmrM9YuXMNrGDGZ1bfb +0Wx9WGSwtI258G9XCB0OQqYsq6WTMaEei+z8l0iarZi1l2bz2F89J+IBYM8RqSsa +E30F8AtEG32QJUfK3F6k6N4uLx6JZduJgLyzsSh6q51ghAJ8kD1vUkEeXffCzynp +hzwRHUw5O1jNLEBdKYHpSyszlFX6qbzR1YXypzZs/aehZi5d89eBKN8X/Fnbi9a2 +Q0MqpZ5J/1hH7zadJFibNyuOCP4CNO3Hm18PjyEFRrCMbSF293kY9GoiOlQiwNAT +MqrsyLYgHPCXKXpG/R60lyHEfWKO9sOjyh+mSbv3QfNZS32Fweuo0R/vGYmkmtGn +wpn2IeSmX8ychdQrSemJjwzjUl/EUN0lGRAlHEt4ZDf52vHZ4QKCAQEA9k7b2vpU +g3S3GRCMzhl8GKZloNSbnR/ZHE8b9PVNahp0bQcZj+1yimF35p27VZR662ZBoKLy +/MLyPT+ZyynykwypcTVA5U9CABpSlyZMeezLnFlBXeHHMeoXcBZfFqHeXSsNYbhW +OStf4BGwKf7m/V0P/QL9mNsA/iq2uugC1gHoyp422YUIQQvKkBiFyMl34Zp2URsX +yIwb9aVyg2GogKDtbDPIwW89l3BCBiwalvjR6UotbXh3PQgYbsv62rTJ8AN+E+XH +eSQPnmPR6EXtX2nDuov996qlbja+JQE3SAls4EXLbrLyjSlObjcvkA59r+kZgNIY +g88hv7e9ublfqwKCAQEAxs3d9Zmjh9ByCT6jOUVnRMqw0+lVyVOs0kp7nOCb4HKM +CnupZuJQHQVQt7VhgD7FrALmYwkpt2e/WllN9bPFHRJcsw+SylOqyPil9G4DO5XZ +YPvk6PeQ/c0cbREKhsYNXqj5fWdq5pRd8rE72rK82mhdGQtAAN7NOEW5fo5tqHDK +D079SZmpcgd8Wz9luNpnZRpNhO3ccKV5yf0S1LZOZBbG9t875OVNhxlQY5wwIBXv +8ab13zcFKG21tWvLzz80vgkMIp0A9xh0XznIRnH3NnBZB80Yubg4sIaWvX7bqZ4X +EE9HGeiamw6c6Sm/Lvh/H659ri95l7C9TgAfAA7puwKCAQEAgJ/N0BzJ5ZwdwckS +vs4wL+81QzfDy9nF1zK4tsMjGjWWdxkuECs/lWQw6Q2VtqtDRYqw2uI9YiGrvrBn +7+CH/KKwGZ5ltVoebU9Rsf0eEs3FxnAV4qD1FOvaMX59SaReKulAo7dPz6sG9kxG +YqfqmITwxH+7TwePDSvhINnoITn+B1F38z+1f8JYlcc4lhIfuIChKNmtId2I/E7Z +7iIhjIp9cfPY8qrUzzCgSfjeKdjmRZ2m+3PdUNHZcIK1DWE700r/nARylqBuR5h5 +FYLu4tSokdJpXdyPZ27O/SQValkBslzAT57Da1QW0RegjuoCWMqxtsQAaVTRmvyo +50QW4QKCAQBLJFbn1MmFtRjVO7KwG/Z7fu01O7WsIg9pcLOmSRNB06nw8GrIM3Q6 +c97dgRY4RgGrEXGJL1ZwNyuRd73Kx8cSRPV6zMEb7mHYEnuPluFr7Si7ypnsIF7S +P2umIdHLvSIijFW4u5UhUCTubWUFNZfCKb4+kA0CBzSkN150Yls6Vl9ZR+7emdD9 +A61SQ/Ur2IlKIpX4T3uJrFILMbejZMDefel4OEpIKw+Rp9TFwaxDBGer/AJk+0Pc +0xLiXrsrO2WxCnRmxNcvjjO2Jn33em4JSo+sLi5RTDtJJaXmPAPE6bcn9/8U4OFH +CE/wpVHY7B4ImIhyhQk9d5Ul3U/aUsivAoIBAG8zk3CnFAnii1ENxhPtE8GCinvs +NdsluVtvUgMcA8gNzvqLHLQCoIy/b1wkqxPVsdTq1gZ7+FX9D9LzW9JxrWeEZqVV +jrUQIbls6HZei7i5x0tPwh1shOZiijgY24I6HDX9QRKZ7H7lLw0HfJI9YBI1Hl8E +naOtCuzFiaYEPfbGQACL8/UuoOZaD31JQda2EGYysGRxxJ2ZNPJIphCrwRb/nQBG +7WwCSCzu0peFNhZPVvkWHaFN73Uv/MmgFkp8RZzw9TEENB05wluCZB1TYJAOe65n +HnRWSDvWYR4lzMtq5WASFLC0WrFTiJKRCuKPljjoTptbXsJKyskW/t/+XPM= +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.txt b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.txt new file mode 100644 index 000000000..cf4e2aba5 --- /dev/null +++ b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.txt @@ -0,0 +1 @@ +BFFAA2A065DFA6FC diff --git a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.crt b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.crt new file mode 100644 index 000000000..5eefadf62 --- /dev/null +++ b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID1zCCAb8CCQC/+qKgZd+m/DANBgkqhkiG9w0BAQsFADA1MRMwEQYDVQQKDApS +ZWRpcyBUZXN0MR4wHAYDVQQDDBVDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMjAx +MDI5MDEzNDE2WhcNMjExMDI5MDEzNDE2WjAmMRMwEQYDVQQKDApSZWRpcyBUZXN0 +MQ8wDQYDVQQDDAZTZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDSs3bQ9sYi2AhFuHU75Ryk1HHSgfzA6pQAJilmJdTy0s5vyiWe1HQJaWkMcS5V +GVzGMK+c+OBqtXtDDninL3betg1YPMjSCOjPMOTC1H9K7+effwf7Iwpnw9Zro8mb +TEmMslIYhhcDedzT9Owli4QAgbgTn4l1BYuKX9CLrrKFtnr21miKu3ydViy9q7T1 +pib3eigvAyk7X2fadHFArGEttsXrD6cetPPkSF/1OLWNlqzUKXzhSyrBXzO44Kks +fwR/EpTiES9g4dNOL2wvKS/YE1fNKhiCENrNxTXQo1l0yOdm2+MeyOeHFzRuS0b/ ++uGDFOPPi04KXeO6dQ5olBCPAgMBAAEwDQYJKoZIhvcNAQELBQADggIBADn0E2vG +iQWe8/I7VbBdPhPNupVNcLvew10eIHxY2g5vSruCSVRQTgk8itVMRmDQxbb7gdDW +jnCRbxykxbLjM9iCRljnOCsIcTi7qO7JRl8niV8dtEpPOs9lZxEdNXjIV1iZoWf3 +arBbPQSyQZvTQHG6qbFnyCdMMyyXGGvEPGQDaBiKH+Ko1qeAbCi0zupChYvxmtZ8 +hSTPlMFezDT9bKoNY0pkJSELfokEPU/Pn6Lz/NVbdzmCMjVa/xmF3s31g+DGhz95 +4AyOnCr6o0aydPVVV3pB/BCezNXPUxpp53BG0w/K2f2DnKYCvGvJbqDAaJ8bG/J1 +EFSOmwobdwVxJz3KNubmo1qJ6xOl/YT7yyqPRQRM1SY8nZW+YcoJSZjOe8wJVlob +d0bOwN1C3HQwomyMWes187bEQP6Y36HuEbR1fK8yIOzGsGDKRFAFwQwMgw2M91lr +EJIP5NRD3OZRuiYDiVfVhDZDaNahrAMZUcPCgeCAwc4YG6Gp2sDtdorOl4kIJYWE +BbBZ0Jplq9+g6ciu5ChjAW8iFl0Ae5U24MxPGXnrxiRF4WWxLeZMVLXLDvlPqReD +CHII5ifyvGEt5+RhqtZC/L+HimL+5wQgOlntqhUdLb6yWRz7YW37PFMnUXU3MXe9 +uY7m73ZLluXiLojcZxU2+cx89u5FOJxrYtrj +-----END CERTIFICATE----- diff --git a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.dh b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.dh new file mode 100644 index 000000000..f7dd0569d --- /dev/null +++ b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.dh @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEAo2dgOzTnLK7c8AjkiTXxdmo2MJsyzTlNXUDxLfl2hgwic6benyQ3 +9iL95wKjYg2YpMhzbwux50D+9XeVkRatf1pRi/N9H911f90MO6penzUx/dxfOepN +qoGK/T9xO8e6aFCYOoQjJaZzQYC0HixJVadZd7wRlHkZ3siNKUU5QK68KaN3JE3J +R3yZ9A7MU/TVdwZyVIyoWF2+WJMQW+qaezoqiuVKZXXzzoqbj14ZrtPRmO26vMV/ +bmMuHwPsk9dL7tKnTWEOrs6NVHIQW+RxJuRE9wGa0qqzHAzysEQ8q9QYPRvGo5y+ +XRWosl1bHG4+EmvXsCCs35bcbKToi3NFWwIBAg== +-----END DH PARAMETERS----- diff --git a/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.key b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.key new file mode 100644 index 000000000..b76303f14 --- /dev/null +++ b/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpgIBAAKCAQEA0rN20PbGItgIRbh1O+UcpNRx0oH8wOqUACYpZiXU8tLOb8ol +ntR0CWlpDHEuVRlcxjCvnPjgarV7Qw54py923rYNWDzI0gjozzDkwtR/Su/nn38H ++yMKZ8PWa6PJm0xJjLJSGIYXA3nc0/TsJYuEAIG4E5+JdQWLil/Qi66yhbZ69tZo +irt8nVYsvau09aYm93ooLwMpO19n2nRxQKxhLbbF6w+nHrTz5Ehf9Ti1jZas1Cl8 +4UsqwV8zuOCpLH8EfxKU4hEvYOHTTi9sLykv2BNXzSoYghDazcU10KNZdMjnZtvj +Hsjnhxc0bktG//rhgxTjz4tOCl3junUOaJQQjwIDAQABAoIBAQCP7CJ27nm9B0/v +P+ZkeUWtmaf+IOhjZlieGXMh4SmqjDCSz8QO0BRK8YPeCdmaK27huhPa521ztm9y +CIqFuLg7vKM06KBMR+Wu0TkRlFE3ANR4cC8lbnQHGRB4CjMGL3/16UCGm+FQcIdV +CPHdW4VZS0JPtSQRmS4N4RD0uOocxqGcVbCRqnJoNp1zyXhookgHfZsC3b3cgzC7 +qvI9F1oY4Yg4b9Lw5sNi3JXWtFth8JFOPyImRcE0ngcGZK4iWjiufNKWVeTmSmVy +njMZfj8xKSpfqO3sOTbJMdrH1v5pMrAR/Ed748HheXuL15Ur9n88683hMMATZInn +YzIqNSrBAoGBAO94YBB1hN+jSKw+2FbAhuuM0gWHREmLQuaF2vjeVXL3r6YofFmf ++oJNgoOWXsv4KO2MgKDv4qrz7RohhhQpOFm5PpapSH/di7u6KsbJLYSxv/TEqQFE +NPyGywwNDIkn1wPlnX3LXp26puj2Gtn21Z0trUrpgsDM99BaTBbqTR2xAoGBAOE+ +tw0GHD/6CRPfoBIgVilS/sUJ5VJYTTKo/y6ozovCAq4bt5LkYmAOy6q8paHb58Oc +J890+LEPhelM/ZJDDz9oQFfq5LvuzgNfzDRyIhgDSpghtFrdDxQZP1X1lSdh+MFW +gx0k9h8VuIPksBsIgcmUtyCYitxLFep/0tAA/GI/AoGBAMxexEVntjWSScROgh1P +hBXlAZycO4g0ZK0OEboRLYXHos1AghePM6Ee+0LIAzE6IdvR7DjtYVoagQCrGZ19 +LE1Ojf7QjEIr1kQpdrZeHQ3BERyY9c9R4ZKeiw1G2ar4KEV4Ifeop6AfGrF4z6Oz +R80znVBwhxl6FAhp98QaxCORAoGBAInkc/nEKN09u/rvpzYRl83aol+MDFjZ+ACw +lvBApZnHnw5pp3uE13jI9gRDUv8A+iS1X2XQzULQJwHJgV7eMOJ3dxSbl4Y5zuMf +7YqZ6KdctHjoAVqzBD0gq7Z7DuG6R6hMxx27d/VVvcz43preHV6D7YxF9pSgXv1d +XXi7ccbPAoGBAIeLzCYd+JGufHwbq7oNvSyXJjGMjsAQuErUQ0xXwo7VAyOere2P +Dwk67wq6vsmn38EAs7IkXDgIoTD9z69DNtcjr/3fARYfmDSWyHscRwyUaJ15WQcZ +TCXAPf70Vf0KGBpRkgD+Qnq+lMZ3dr1uINGdalI4AWsXje0dPKpd+W8U +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_bridge_mqtt/.github/workflows/run_test_cases.yaml b/apps/emqx_bridge_mqtt/.github/workflows/run_test_cases.yaml new file mode 100644 index 000000000..3b4fa29ec --- /dev/null +++ b/apps/emqx_bridge_mqtt/.github/workflows/run_test_cases.yaml @@ -0,0 +1,28 @@ +name: Run test cases + +on: [push, pull_request] + +jobs: + run_test_cases: + runs-on: ubuntu-latest + + container: + image: erlang:22.1 + + steps: + - uses: actions/checkout@v1 + - name: run test cases + run: | + make eunit + make ct + make cover + - uses: actions/upload-artifact@v1 + if: always() + with: + name: logs + path: _build/test/logs + - uses: actions/upload-artifact@v1 + with: + name: cover + path: _build/test/cover + diff --git a/apps/emqx_bridge_mqtt/.gitignore b/apps/emqx_bridge_mqtt/.gitignore new file mode 100644 index 000000000..bf9523be5 --- /dev/null +++ b/apps/emqx_bridge_mqtt/.gitignore @@ -0,0 +1,21 @@ +.eunit +deps +*.o +*.beam +*.plt +erl_crash.dump +ebin/*.beam +rel +_build +.concrete/DEV_MODE +.rebar +.erlang.mk +data +ebin +emqx_bridge_mqtt.d +*.rendered +.rebar3/ +*.coverdata +rebar.lock +.DS_Store +Mnesia.*/ \ No newline at end of file diff --git a/apps/emqx_bridge_mqtt/LICENSE b/apps/emqx_bridge_mqtt/LICENSE new file mode 100644 index 000000000..70a976fe3 --- /dev/null +++ b/apps/emqx_bridge_mqtt/LICENSE @@ -0,0 +1,191 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2019, GilbertWong . + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/apps/emqx_bridge_mqtt/README.md b/apps/emqx_bridge_mqtt/README.md new file mode 100644 index 000000000..6656aa36f --- /dev/null +++ b/apps/emqx_bridge_mqtt/README.md @@ -0,0 +1,265 @@ +# EMQ Bridge MQTT + +The concept of **Bridge** means that EMQ X supports forwarding messages +of one of its own topics to another MQTT Broker in some way. + +**Bridge** differs from **Cluster** in that the bridge does not +replicate the topic trie and routing tables and only forwards MQTT +messages based on bridging rules. + +At present, the bridging methods supported by EMQ X are as follows: + +- RPC bridge: RPC Bridge only supports message forwarding and does not + support subscribing to the topic of remote nodes to synchronize + data; +- MQTT Bridge: MQTT Bridge supports both forwarding and data + synchronization through subscription topic. + +These concepts are shown below: + +![bridge](docs/images/bridge.png) + +In addition, the EMQ X message broker supports multi-node bridge mode interconnection + +``` + --------- --------- --------- +Publisher --> | Node1 | --Bridge Forward--> | Node2 | --Bridge Forward--> | Node3 | --> Subscriber + --------- --------- --------- +``` + +In EMQ X, bridge is configured by modifying `etc/emqx.conf`. EMQ X distinguishes between different bridges based on different names. E.g + +``` +## Bridge address: node name for local bridge, host:port for remote. +bridge.mqtt.aws.address = 127.0.0.1:1883 +``` + +This configuration declares a bridge named `aws` and specifies that it is bridged to the MQTT broker of 127.0.0.1:1883 by MQTT mode. + +In case of creating multiple bridges, it is convenient to replicate all configuration items of the first bridge, and modify the bridge name and other configuration items if necessary (such as bridge.$name.address, where $name refers to the name of bridge) + +The next two sections describe how to create a bridge in RPC and MQTT mode respectively and create a forwarding rule that forwards the messages from sensors. Assuming that two EMQ X nodes are running on two hosts: + + +| Name | Node | MQTT Port | +|------|-------------------|-----------| +| emqx1| emqx1@192.168.1.1.| 1883 | +| emqx2| emqx2@192.168.1.2 | 1883 | + + +## EMQ X RPC Bridge Configuration + +The following is the basic configuration of RPC bridging. A simplest RPC bridging only requires the following three items + +``` +## Bridge Address: Use node name (nodename@host) for rpc bridging, and host:port for mqtt connection +bridge.mqtt.emqx2.address = emqx2@192.168.1.2 + +## Forwarding topics of the message +bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/# + +## bridged mountpoint +bridge.mqtt.emqx2.mountpoint = bridge/emqx2/${node}/ +``` + +If the messages received by the local node emqx1 matches the topic `sersor1/#` or `sensor2/#`, these messages will be forwarded to the `sensor1/#` or `sensor2/#` topic of the remote node emqx2. + +`forwards` is used to specify topics. Messages of the in `forwards` specified topics on local node are forwarded to the remote node. + +`mountpoint` is used to add a topic prefix when forwarding a message. To use `mountpoint`, the `forwards` directive must be set. In the above example, a message with the topic `sensor1/hello` received by the local node will be forwarded to the remote node with the topic `bridge/emqx2/emqx1@192.168.1.1/sensor1/hello`. + +Limitations of RPC bridging: + +1. The RPC bridge of emqx can only forward local messages to the remote node, and cannot synchronize the messages of the remote node to the local node; + +2. RPC bridge can only bridge two EMQ X broker together and cannot bridge EMQ X broker to other MQTT brokers. + +## EMQ X MQTT Bridge Configuration + +EMQ X 3.0 officially introduced MQTT bridge, so that EMQ X can bridge any MQTT broker. Because of the characteristics of the MQTT protocol, EMQ X can subscribe to the remote mqtt broker's topic through MQTT bridge, and then synchronize the remote MQTT broker's message to the local. + +EMQ X MQTT bridging principle: Create an MQTT client on the EMQ X broker, and connect this MQTT client to the remote MQTT broker. Therefore, in the MQTT bridge configuration, following fields may be set for the EMQ X to connect to the remote broker as an mqtt client + +``` +## Bridge Address: Use node name for rpc bridging, use host:port for mqtt connection +bridge.mqtt.emqx2.address = 192.168.1.2:1883 + +## Bridged Protocol Version +## Enumeration value: mqttv3 | mqttv4 | mqttv5 +bridge.mqtt.emqx2.proto_ver = mqttv4 + +## mqtt client's clientid +bridge.mqtt.emqx2.clientid = bridge_emq + +## mqtt client's clean_start field +## Note: Some MQTT Brokers need to set the clean_start value as `true` +bridge.mqtt.emqx2.clean_start = true + +## mqtt client's username field +bridge.mqtt.emqx2.username = user + +## mqtt client's password field +bridge.mqtt.emqx2.password = passwd + +## Whether the mqtt client uses ssl to connect to a remote serve or not +bridge.mqtt.emqx2.ssl = off + +## CA Certificate of Client SSL Connection (PEM format) +bridge.mqtt.emqx2.cacertfile = etc/certs/cacert.pem + +## SSL certificate of Client SSL connection +bridge.mqtt.emqx2.certfile = etc/certs/client-cert.pem + +## Key file of Client SSL connection +bridge.mqtt.emqx2.keyfile = etc/certs/client-key.pem + +## SSL encryption +bridge.mqtt.emqx2.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384 + +## TTLS PSK password +## Note 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot be configured at the same time +## +## See 'https://tools.ietf.org/html/rfc4279#section-2'. +## bridge.mqtt.emqx2.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA + +## Client's heartbeat interval +bridge.mqtt.emqx2.keepalive = 60s + +## Supported TLS version +bridge.mqtt.emqx2.tls_versions = tlsv1.2,tlsv1.1,tlsv1 + +## Forwarding topics of the message +bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/# + +## Bridged mountpoint +bridge.mqtt.emqx2.mountpoint = bridge/emqx2/${node}/ + +## Subscription topic for bridging +bridge.mqtt.emqx2.subscription.1.topic = cmd/topic1 + +## Subscription qos for bridging +bridge.mqtt.emqx2.subscription.1.qos = 1 + +## Subscription topic for bridging +bridge.mqtt.emqx2.subscription.2.topic = cmd/topic2 + +## Subscription qos for bridging +bridge.mqtt.emqx2.subscription.2.qos = 1 + +## Bridging reconnection interval +## Default: 30s +bridge.mqtt.emqx2.reconnect_interval = 30s + +## QoS1 message retransmission interval +bridge.mqtt.emqx2.retry_interval = 20s + +## Inflight Size. +bridge.mqtt.emqx2.max_inflight_batches = 32 +``` + +## Bridge Cache Configuration + +The bridge of EMQ X has a message caching mechanism. The caching mechanism is applicable to both RPC bridging and MQTT bridging. When the bridge is disconnected (such as when the network connection is unstable), the messages with a topic specified in `forwards` can be cached to the local message queue. Until the bridge is restored, these messages are re-forwarded to the remote node. The configuration of the cache queue is as follows + +``` +## emqx_bridge internal number of messages used for batch +bridge.mqtt.emqx2.queue.batch_count_limit = 32 + +## emqx_bridge internal number of message bytes used for batch +bridge.mqtt.emqx2.queue.batch_bytes_limit = 1000MB + +## The path for placing replayq queue. If it is not specified, then replayq will run in `mem-only` mode and messages will not be cached on disk. +bridge.mqtt.emqx2.queue.replayq_dir = data/emqx_emqx2_bridge/ + +## Replayq data segment size +bridge.mqtt.emqx2.queue.replayq_seg_bytes = 10MB +``` + +`bridge.mqtt.emqx2.queue.replayq_dir` is a configuration parameter for specifying the path of the bridge storage queue. + +`bridge.mqtt.emqx2.queue.replayq_seg_bytes` is used to specify the size of the largest single file of the message queue that is cached on disk. If the message queue size exceeds the specified value, a new file is created to store the message queue. + +## CLI for EMQ X Bridge MQTT + +CLI for EMQ X Bridge MQTT: + +``` bash +$ cd emqx1/ && ./bin/emqx_ctl bridges +bridges list # List bridges +bridges start # Start a bridge +bridges stop # Stop a bridge +bridges forwards # Show a bridge forward topic +bridges add-forward # Add bridge forward topic +bridges del-forward # Delete bridge forward topic +bridges subscriptions # Show a bridge subscriptions topic +bridges add-subscription # Add bridge subscriptions topic +``` + +List all bridge states + +``` bash +$ ./bin/emqx_ctl bridges list +name: emqx status: Stopped $ ./bin/emqx_ctl bridges list +name: emqx status: Stopped +``` + +Start the specified bridge + +``` bash +$ ./bin/emqx_ctl bridges start emqx +Start bridge successfully. +``` + +Stop the specified bridge + +``` bash +$ ./bin/emqx_ctl bridges stop emqx +Stop bridge successfully. +``` +List the forwarding topics for the specified bridge + +``` bash +$ ./bin/emqx_ctl bridges forwards emqx +topic: topic1/# +topic: topic2/# +``` + +Add a forwarding topic for the specified bridge + +``` bash +$ ./bin/emqx_ctl bridges add-forwards emqx topic3/# +Add-forward topic successfully. +``` + +Delete the forwarding topic for the specified bridge + + +``` bash +$ ./bin/emqx_ctl bridges del-forwards emqx topic3/# +Del-forward topic successfully. +``` + +List subscriptions for the specified bridge + +``` bash +$ ./bin/emqx_ctl bridges subscriptions emqx +topic: cmd/topic1, qos: 1 +topic: cmd/topic2, qos: 1 +``` + +Add a subscription topic for the specified bridge + +``` bash +$ ./bin/emqx_ctl bridges add-subscription emqx cmd/topic3 1 +Add-subscription topic successfully. +``` + +Delete the subscription topic for the specified bridge + +``` bash +$ ./bin/emqx_ctl bridges del-subscription emqx cmd/topic3 +Del-subscription topic successfully. +``` + +Note: In case of creating multiple bridges, it is convenient to replicate all configuration items of the first bridge, and modify the bridge name and other configuration items if necessary. + diff --git a/apps/emqx_bridge_mqtt/docs/guide.rst b/apps/emqx_bridge_mqtt/docs/guide.rst new file mode 100644 index 000000000..73350ca1f --- /dev/null +++ b/apps/emqx_bridge_mqtt/docs/guide.rst @@ -0,0 +1,286 @@ + +EMQ Bridge MQTT +=============== + +The concept of **Bridge** means that EMQ X supports forwarding messages +of one of its own topics to another MQTT Broker in some way. + +**Bridge** differs from **Cluster** in that the bridge does not +replicate the topic trie and routing tables and only forwards MQTT +messages based on bridging rules. + +At present, the bridging methods supported by EMQ X are as follows: + + +* RPC bridge: RPC Bridge only supports message forwarding and does not + support subscribing to the topic of remote nodes to synchronize + data; +* MQTT Bridge: MQTT Bridge supports both forwarding and data + synchronization through subscription topic. + +These concepts are shown below: + + +.. image:: images/bridge.png + :target: images/bridge.png + :alt: bridge + + +In addition, the EMQ X message broker supports multi-node bridge mode interconnection + +.. code-block:: + + --------- --------- --------- + Publisher --> | Node1 | --Bridge Forward--> | Node2 | --Bridge Forward--> | Node3 | --> Subscriber + --------- --------- --------- + +In EMQ X, bridge is configured by modifying ``etc/emqx.conf``. EMQ X distinguishes between different bridges based on different names. E.g + +.. code-block:: + + ## Bridge address: node name for local bridge, host:port for remote. + bridge.mqtt.aws.address = 127.0.0.1:1883 + +This configuration declares a bridge named ``aws`` and specifies that it is bridged to the MQTT broker of 127.0.0.1:1883 by MQTT mode. + +In case of creating multiple bridges, it is convenient to replicate all configuration items of the first bridge, and modify the bridge name and other configuration items if necessary (such as bridge.$name.address, where $name refers to the name of bridge) + +The next two sections describe how to create a bridge in RPC and MQTT mode respectively and create a forwarding rule that forwards the messages from sensors. Assuming that two EMQ X nodes are running on two hosts: + +.. list-table:: + :header-rows: 1 + + * - Name + - Node + - MQTT Port + * - emqx1 + - emqx1@192.168.1.1. + - 1883 + * - emqx2 + - emqx2@192.168.1.2 + - 1883 + + +EMQ X RPC Bridge Configuration +------------------------------ + +The following is the basic configuration of RPC bridging. A simplest RPC bridging only requires the following three items + +.. code-block:: + + ## Bridge Address: Use node name (nodename@host) for rpc bridging, and host:port for mqtt connection + bridge.mqtt.emqx2.address = emqx2@192.168.1.2 + + ## Forwarding topics of the message + bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/# + + ## bridged mountpoint + bridge.mqtt.emqx2.mountpoint = bridge/emqx2/${node}/ + +If the messages received by the local node emqx1 matches the topic ``sersor1/#`` or ``sensor2/#``\ , these messages will be forwarded to the ``sensor1/#`` or ``sensor2/#`` topic of the remote node emqx2. + +``forwards`` is used to specify topics. Messages of the in ``forwards`` specified topics on local node are forwarded to the remote node. + +``mountpoint`` is used to add a topic prefix when forwarding a message. To use ``mountpoint``\ , the ``forwards`` directive must be set. In the above example, a message with the topic ``sensor1/hello`` received by the local node will be forwarded to the remote node with the topic ``bridge/emqx2/emqx1@192.168.1.1/sensor1/hello``. + +Limitations of RPC bridging: + + +#. + The RPC bridge of emqx can only forward local messages to the remote node, and cannot synchronize the messages of the remote node to the local node; + +#. + RPC bridge can only bridge two EMQ X broker together and cannot bridge EMQ X broker to other MQTT brokers. + +EMQ X MQTT Bridge Configuration +------------------------------- + +EMQ X 3.0 officially introduced MQTT bridge, so that EMQ X can bridge any MQTT broker. Because of the characteristics of the MQTT protocol, EMQ X can subscribe to the remote mqtt broker's topic through MQTT bridge, and then synchronize the remote MQTT broker's message to the local. + +EMQ X MQTT bridging principle: Create an MQTT client on the EMQ X broker, and connect this MQTT client to the remote MQTT broker. Therefore, in the MQTT bridge configuration, following fields may be set for the EMQ X to connect to the remote broker as an mqtt client + +.. code-block:: + + ## Bridge Address: Use node name for rpc bridging, use host:port for mqtt connection + bridge.mqtt.emqx2.address = 192.168.1.2:1883 + + ## Bridged Protocol Version + ## Enumeration value: mqttv3 | mqttv4 | mqttv5 + bridge.mqtt.emqx2.proto_ver = mqttv4 + + ## mqtt client's clientid + bridge.mqtt.emqx2.clientid = bridge_emq + + ## mqtt client's clean_start field + ## Note: Some MQTT Brokers need to set the clean_start value as `true` + bridge.mqtt.emqx2.clean_start = true + + ## mqtt client's username field + bridge.mqtt.emqx2.username = user + + ## mqtt client's password field + bridge.mqtt.emqx2.password = passwd + + ## Whether the mqtt client uses ssl to connect to a remote serve or not + bridge.mqtt.emqx2.ssl = off + + ## CA Certificate of Client SSL Connection (PEM format) + bridge.mqtt.emqx2.cacertfile = etc/certs/cacert.pem + + ## SSL certificate of Client SSL connection + bridge.mqtt.emqx2.certfile = etc/certs/client-cert.pem + + ## Key file of Client SSL connection + bridge.mqtt.emqx2.keyfile = etc/certs/client-key.pem + + ## SSL encryption + bridge.mqtt.emqx2.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384 + + ## TTLS PSK password + ## Note 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot be configured at the same time + ## + ## See 'https://tools.ietf.org/html/rfc4279#section-2'. + ## bridge.mqtt.emqx2.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA + + ## Client's heartbeat interval + bridge.mqtt.emqx2.keepalive = 60s + + ## Supported TLS version + bridge.mqtt.emqx2.tls_versions = tlsv1.2,tlsv1.1,tlsv1 + + ## Forwarding topics of the message + bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/# + + ## Bridged mountpoint + bridge.mqtt.emqx2.mountpoint = bridge/emqx2/${node}/ + + ## Subscription topic for bridging + bridge.mqtt.emqx2.subscription.1.topic = cmd/topic1 + + ## Subscription qos for bridging + bridge.mqtt.emqx2.subscription.1.qos = 1 + + ## Subscription topic for bridging + bridge.mqtt.emqx2.subscription.2.topic = cmd/topic2 + + ## Subscription qos for bridging + bridge.mqtt.emqx2.subscription.2.qos = 1 + + ## Bridging reconnection interval + ## Default: 30s + bridge.mqtt.emqx2.reconnect_interval = 30s + + ## QoS1 message retransmission interval + bridge.mqtt.emqx2.retry_interval = 20s + + ## Inflight Size. + bridge.mqtt.emqx2.max_inflight_batches = 32 + +Bridge Cache Configuration +-------------------------- + +The bridge of EMQ X has a message caching mechanism. The caching mechanism is applicable to both RPC bridging and MQTT bridging. When the bridge is disconnected (such as when the network connection is unstable), the messages with a topic specified in ``forwards`` can be cached to the local message queue. Until the bridge is restored, these messages are re-forwarded to the remote node. The configuration of the cache queue is as follows + +.. code-block:: + + ## emqx_bridge internal number of messages used for batch + bridge.mqtt.emqx2.queue.batch_count_limit = 32 + + ## emqx_bridge internal number of message bytes used for batch + bridge.mqtt.emqx2.queue.batch_bytes_limit = 1000MB + + ## The path for placing replayq queue. If it is not specified, then replayq will run in `mem-only` mode and messages will not be cached on disk. + bridge.mqtt.emqx2.queue.replayq_dir = data/emqx_emqx2_bridge/ + + ## Replayq data segment size + bridge.mqtt.emqx2.queue.replayq_seg_bytes = 10MB + +``bridge.mqtt.emqx2.queue.replayq_dir`` is a configuration parameter for specifying the path of the bridge storage queue. + +``bridge.mqtt.emqx2.queue.replayq_seg_bytes`` is used to specify the size of the largest single file of the message queue that is cached on disk. If the message queue size exceeds the specified value, a new file is created to store the message queue. + +CLI for EMQ X Bridge MQTT +------------------------- + +CLI for EMQ X Bridge MQTT: + +.. code-block:: bash + + $ cd emqx1/ && ./bin/emqx_ctl bridges + bridges list # List bridges + bridges start # Start a bridge + bridges stop # Stop a bridge + bridges forwards # Show a bridge forward topic + bridges add-forward # Add bridge forward topic + bridges del-forward # Delete bridge forward topic + bridges subscriptions # Show a bridge subscriptions topic + bridges add-subscription # Add bridge subscriptions topic + +List all bridge states + +.. code-block:: bash + + $ ./bin/emqx_ctl bridges list + name: emqx status: Stopped $ ./bin/emqx_ctl bridges list + name: emqx status: Stopped + +Start the specified bridge + +.. code-block:: bash + + $ ./bin/emqx_ctl bridges start emqx + Start bridge successfully. + +Stop the specified bridge + +.. code-block:: bash + + $ ./bin/emqx_ctl bridges stop emqx + Stop bridge successfully. + +List the forwarding topics for the specified bridge + +.. code-block:: bash + + $ ./bin/emqx_ctl bridges forwards emqx + topic: topic1/# + topic: topic2/# + +Add a forwarding topic for the specified bridge + +.. code-block:: bash + + $ ./bin/emqx_ctl bridges add-forwards emqx topic3/# + Add-forward topic successfully. + +Delete the forwarding topic for the specified bridge + +.. code-block:: bash + + $ ./bin/emqx_ctl bridges del-forwards emqx topic3/# + Del-forward topic successfully. + +List subscriptions for the specified bridge + +.. code-block:: bash + + $ ./bin/emqx_ctl bridges subscriptions emqx + topic: cmd/topic1, qos: 1 + topic: cmd/topic2, qos: 1 + +Add a subscription topic for the specified bridge + +.. code-block:: bash + + $ ./bin/emqx_ctl bridges add-subscription emqx cmd/topic3 1 + Add-subscription topic successfully. + +Delete the subscription topic for the specified bridge + +.. code-block:: bash + + $ ./bin/emqx_ctl bridges del-subscription emqx cmd/topic3 + Del-subscription topic successfully. + +Note: In case of creating multiple bridges, it is convenient to replicate all configuration items of the first bridge, and modify the bridge name and other configuration items if necessary. + diff --git a/apps/emqx_bridge_mqtt/docs/images/bridge.png b/apps/emqx_bridge_mqtt/docs/images/bridge.png new file mode 100644 index 000000000..9bb9c024c Binary files /dev/null and b/apps/emqx_bridge_mqtt/docs/images/bridge.png differ diff --git a/apps/emqx_bridge_mqtt/etc/emqx_bridge_mqtt.conf b/apps/emqx_bridge_mqtt/etc/emqx_bridge_mqtt.conf new file mode 100644 index 000000000..93f0f5579 --- /dev/null +++ b/apps/emqx_bridge_mqtt/etc/emqx_bridge_mqtt.conf @@ -0,0 +1,172 @@ +##==================================================================== +## Configuration for EMQ X MQTT Broker Bridge +##==================================================================== + +##-------------------------------------------------------------------- +## Bridges to aws +##-------------------------------------------------------------------- + +## Bridge address: node name for local bridge, host:port for remote. +## +## Value: String +## Example: emqx@127.0.0.1, 127.0.0.1:1883 +bridge.mqtt.aws.address = 127.0.0.1:1883 + +## Protocol version of the bridge. +## +## Value: Enum +## - mqttv5 +## - mqttv4 +## - mqttv3 +bridge.mqtt.aws.proto_ver = mqttv4 + +## Start type of the bridge. +## +## Value: enum +## manual +## auto +bridge.mqtt.aws.start_type = manual + +## Whether to enable bridge mode for mqtt bridge +## +## This option is prepared for the mqtt broker which does not +## support bridge_mode such as the mqtt-plugin of the rabbitmq +## +## Value: boolean +#bridge.mqtt.aws.bridge_mode = false + +## The ClientId of a remote bridge. +## +## Placeholders: +## ${node}: Node name +## +## Value: String +bridge.mqtt.aws.clientid = bridge_aws + +## The Clean start flag of a remote bridge. +## +## Value: boolean +## Default: true +## +## NOTE: Some IoT platforms require clean_start +## must be set to 'true' +bridge.mqtt.aws.clean_start = true + +## The username for a remote bridge. +## +## Value: String +bridge.mqtt.aws.username = user + +## The password for a remote bridge. +## +## Value: String +bridge.mqtt.aws.password = passwd + +## Topics that need to be forward to AWS IoTHUB +## +## Value: String +## Example: topic1/#,topic2/# +bridge.mqtt.aws.forwards = topic1/#,topic2/# + +## Forward messages to the mountpoint of an AWS IoTHUB +## +## Value: String +bridge.mqtt.aws.forward_mountpoint = bridge/aws/${node}/ + +## Need to subscribe to AWS topics +## +## Value: String +## bridge.mqtt.aws.subscription.1.topic = cmd/topic1 + +## Need to subscribe to AWS topics QoS. +## +## Value: Number +## bridge.mqtt.aws.subscription.1.qos = 1 + +## A mountpoint that receives messages from AWS IoTHUB +## +## Value: String +## bridge.mqtt.aws.receive_mountpoint = receive/aws/ + + +## Bribge to remote server via SSL. +## +## Value: on | off +bridge.mqtt.aws.ssl = off + +## PEM-encoded CA certificates of the bridge. +## +## Value: File +bridge.mqtt.aws.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem + +## Client SSL Certfile of the bridge. +## +## Value: File +bridge.mqtt.aws.certfile = {{ platform_etc_dir }}/certs/client-cert.pem + +## Client SSL Keyfile of the bridge. +## +## Value: File +bridge.mqtt.aws.keyfile = {{ platform_etc_dir }}/certs/client-key.pem + +## SSL Ciphers used by the bridge. +## +## Value: String +bridge.mqtt.aws.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA + +## Ciphers for TLS PSK. +## Note that 'bridge.${BridgeName}.ciphers' and 'bridge.${BridgeName}.psk_ciphers' cannot +## be configured at the same time. +## See 'https://tools.ietf.org/html/rfc4279#section-2'. +#bridge.mqtt.aws.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA + +## Ping interval of a down bridge. +## +## Value: Duration +## Default: 10 seconds +bridge.mqtt.aws.keepalive = 60s + +## TLS versions used by the bridge. +## +## Value: String +bridge.mqtt.aws.tls_versions = tlsv1.2,tlsv1.1,tlsv1 + +## Bridge reconnect time. +## +## Value: Duration +## Default: 30 seconds +bridge.mqtt.aws.reconnect_interval = 30s + +## Retry interval for bridge QoS1 message delivering. +## +## Value: Duration +bridge.mqtt.aws.retry_interval = 20s + +## Publish messages in batches, only RPC Bridge supports +## +## Value: Integer +## default: 32 +bridge.mqtt.aws.batch_size = 32 + +## Inflight size. +## +## Value: Integer +bridge.mqtt.aws.max_inflight_size = 32 + +## Base directory for replayq to store messages on disk +## If this config entry is missing or set to undefined, +## replayq works in a mem-only manner. +## +## Value: String +bridge.mqtt.aws.queue.replayq_dir = {{ platform_data_dir }}/replayq/emqx_aws_bridge/ + +## Replayq segment size +## +## Value: Bytesize +bridge.mqtt.aws.queue.replayq_seg_bytes = 10MB + +## Replayq max total size +## +## Value: Bytesize +bridge.mqtt.aws.queue.max_total_size = 5GB + diff --git a/apps/emqx_bridge_mqtt/include/emqx_bridge_mqtt.hrl b/apps/emqx_bridge_mqtt/include/emqx_bridge_mqtt.hrl new file mode 100644 index 000000000..4bc9ede14 --- /dev/null +++ b/apps/emqx_bridge_mqtt/include/emqx_bridge_mqtt.hrl @@ -0,0 +1,18 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-define(APP, emqx_bridge_mqtt). + diff --git a/apps/emqx_bridge_mqtt/priv/emqx_bridge_mqtt.schema b/apps/emqx_bridge_mqtt/priv/emqx_bridge_mqtt.schema new file mode 100644 index 000000000..9d5f3fe29 --- /dev/null +++ b/apps/emqx_bridge_mqtt/priv/emqx_bridge_mqtt.schema @@ -0,0 +1,242 @@ +%%-*- mode: erlang -*- +%%-------------------------------------------------------------------- +%% Bridges +%%-------------------------------------------------------------------- +{mapping, "bridge.mqtt.$name.address", "emqx_bridge_mqtt.bridges", [ + {datatype, string} +]}. + +{mapping, "bridge.mqtt.$name.proto_ver", "emqx_bridge_mqtt.bridges", [ + {datatype, {enum, [mqttv3, mqttv4, mqttv5]}} +]}. + +{mapping, "bridge.mqtt.$name.bridge_mode", "emqx_bridge_mqtt.bridges", [ + {default, false}, + {datatype, {enum, [true, false]}} +]}. + +{mapping, "bridge.mqtt.$name.start_type", "emqx_bridge_mqtt.bridges", [ + {datatype, {enum, [manual, auto]}}, + {default, auto} +]}. + +{mapping, "bridge.mqtt.$name.clientid", "emqx_bridge_mqtt.bridges", [ + {datatype, string} +]}. + +{mapping, "bridge.mqtt.$name.clean_start", "emqx_bridge_mqtt.bridges", [ + {default, true}, + {datatype, {enum, [true, false]}} +]}. + +{mapping, "bridge.mqtt.$name.username", "emqx_bridge_mqtt.bridges", [ + {datatype, string} +]}. + +{mapping, "bridge.mqtt.$name.password", "emqx_bridge_mqtt.bridges", [ + {datatype, string} +]}. + +{mapping, "bridge.mqtt.$name.forwards", "emqx_bridge_mqtt.bridges", [ + {datatype, string}, + {default, ""} +]}. + +{mapping, "bridge.mqtt.$name.forward_mountpoint", "emqx_bridge_mqtt.bridges", [ + {datatype, string} +]}. + +{mapping, "bridge.mqtt.$name.subscription.$id.topic", "emqx_bridge_mqtt.bridges", [ + {datatype, string} +]}. + +{mapping, "bridge.mqtt.$name.subscription.$id.qos", "emqx_bridge_mqtt.bridges", [ + {datatype, integer} +]}. + +{mapping, "bridge.mqtt.$name.receive_mountpoint", "emqx_bridge_mqtt.bridges", [ + {datatype, string} +]}. + +{mapping, "bridge.mqtt.$name.ssl", "emqx_bridge_mqtt.bridges", [ + {datatype, flag}, + {default, off} +]}. + +{mapping, "bridge.mqtt.$name.cacertfile", "emqx_bridge_mqtt.bridges", [ + {datatype, string} +]}. + +{mapping, "bridge.mqtt.$name.certfile", "emqx_bridge_mqtt.bridges", [ + {datatype, string} +]}. + +{mapping, "bridge.mqtt.$name.keyfile", "emqx_bridge_mqtt.bridges", [ + {datatype, string} +]}. + +{mapping, "bridge.mqtt.$name.ciphers", "emqx_bridge_mqtt.bridges", [ + {datatype, string} +]}. + +{mapping, "bridge.mqtt.$name.psk_ciphers", "emqx_bridge_mqtt.bridges", [ + {datatype, string} +]}. + +{mapping, "bridge.mqtt.$name.keepalive", "emqx_bridge_mqtt.bridges", [ + {default, "10s"}, + {datatype, {duration, s}} +]}. + +{mapping, "bridge.mqtt.$name.tls_versions", "emqx_bridge_mqtt.bridges", [ + {datatype, string}, + {default, "tlsv1,tlsv1.1,tlsv1.2"} +]}. + +{mapping, "bridge.mqtt.$name.reconnect_interval", "emqx_bridge_mqtt.bridges", [ + {default, "30s"}, + {datatype, {duration, ms}} +]}. + +{mapping, "bridge.mqtt.$name.retry_interval", "emqx_bridge_mqtt.bridges", [ + {default, "20s"}, + {datatype, {duration, ms}} +]}. + +{mapping, "bridge.mqtt.$name.max_inflight_size", "emqx_bridge_mqtt.bridges", [ + {default, 0}, + {datatype, integer} + ]}. + +{mapping, "bridge.mqtt.$name.batch_size", "emqx_bridge_mqtt.bridges", [ + {default, 0}, + {datatype, integer} +]}. + +{mapping, "bridge.mqtt.$name.queue.replayq_dir", "emqx_bridge_mqtt.bridges", [ + {datatype, string} +]}. + +{mapping, "bridge.mqtt.$name.queue.replayq_seg_bytes", "emqx_bridge_mqtt.bridges", [ + {datatype, bytesize} +]}. + +{mapping, "bridge.mqtt.$name.queue.max_total_size", "emqx_bridge_mqtt.bridges", [ + {datatype, bytesize} +]}. + +{translation, "emqx_bridge_mqtt.bridges", fun(Conf) -> + + MapPSKCiphers = fun(PSKCiphers) -> + lists:map( + fun("PSK-AES128-CBC-SHA") -> {psk, aes_128_cbc, sha}; + ("PSK-AES256-CBC-SHA") -> {psk, aes_256_cbc, sha}; + ("PSK-3DES-EDE-CBC-SHA") -> {psk, '3des_ede_cbc', sha}; + ("PSK-RC4-SHA") -> {psk, rc4_128, sha} + end, PSKCiphers) + end, + + Split = fun(undefined) -> undefined; (S) -> string:tokens(S, ",") end, + + IsSsl = fun(cacertfile) -> true; + (certfile) -> true; + (keyfile) -> true; + (ciphers) -> true; + (psk_ciphers) -> true; + (tls_versions) -> true; + (_Opt) -> false + end, + + Parse = fun(tls_versions, Vers) -> + [{versions, [list_to_atom(S) || S <- Split(Vers)]}]; + (ciphers, Ciphers) -> + [{ciphers, Split(Ciphers)}]; + (psk_ciphers, Ciphers) -> + [{ciphers, MapPSKCiphers(Split(Ciphers))}, {user_lookup_fun, {fun emqx_psk:lookup/3, <<>>}}]; + (Opt, Val) -> + [{Opt, Val}] + end, + + Merge = fun(forwards, Val, Opts) -> + [{forwards, string:tokens(Val, ",")}|Opts]; + (Opt, Val, Opts) -> + case IsSsl(Opt) of + true -> + SslOpts = Parse(Opt, Val) ++ proplists:get_value(ssl_opts, Opts, []), + lists:ukeymerge(1, [{ssl_opts, SslOpts}], lists:usort(Opts)); + false -> + [{Opt, Val}|Opts] + end + end, + Queue = fun(Name) -> + Configs = cuttlefish_variable:filter_by_prefix("bridge.mqtt." ++ Name ++ ".queue", Conf), + + QOpts = [{list_to_atom(QOpt), QValue}|| {[_, _, _, "queue", QOpt], QValue} <- Configs], + maps:from_list(QOpts) + end, + Subscriptions = fun(Name) -> + Configs = cuttlefish_variable:filter_by_prefix("bridge.mqtt." ++ Name ++ ".subscription", Conf), + lists:zip([Topic || {_, Topic} <- lists:sort([{I, Topic} || {[_, _, _, "subscription", I, "topic"], Topic} <- Configs])], + [QoS || {_, QoS} <- lists:sort([{I, QoS} || {[_, _, _, "subscription", I, "qos"], QoS} <- Configs])]) + end, + IsNodeAddr = fun(Addr) -> + case string:tokens(Addr, "@") of + [_NodeName, _Hostname] -> true; + _ -> false + end + end, + ConnMod = fun(Name) -> + + [AddrConfig] = cuttlefish_variable:filter_by_prefix("bridge.mqtt." ++ Name ++ ".address", Conf), + {_, Addr} = AddrConfig, + + Subs = Subscriptions(Name), + case IsNodeAddr(Addr) of + true when Subs =/= [] -> + error({"subscriptions are not supported when bridging between emqx nodes", Name, Subs}); + true -> + emqx_bridge_rpc; + false -> + emqx_bridge_mqtt + end + end, + + %% to be backward compatible + Translate = + fun Tr(queue, Q, Cfg) -> + NewQ = maps:fold(Tr, #{}, Q), + Cfg#{queue => NewQ}; + Tr(address, Addr0, Cfg) -> + Addr = case IsNodeAddr(Addr0) of + true -> list_to_atom(Addr0); + false -> Addr0 + end, + Cfg#{address => Addr}; + Tr(reconnect_interval, Ms, Cfg) -> + Cfg#{reconnect_delay_ms => Ms}; + Tr(proto_ver, Ver, Cfg) -> + Cfg#{proto_ver => + case Ver of + mqttv3 -> v3; + mqttv4 -> v4; + mqttv5 -> v5; + _ -> v4 + end}; + Tr(Key, Value, Cfg) -> + Cfg#{Key => Value} + end, + C = lists:foldl( + fun({["bridge", "mqtt", Name, Opt], Val}, Acc) -> + %% e.g #{aws => [{OptKey, OptVal}]} + Init = [{list_to_atom(Opt), Val}, + {connect_module, ConnMod(Name)}, + {subscriptions, Subscriptions(Name)}, + {queue, Queue(Name)}], + maps:update_with(list_to_atom(Name), fun(Opts) -> Merge(list_to_atom(Opt), Val, Opts) end, Init, Acc); + (_, Acc) -> Acc + end, #{}, lists:usort(cuttlefish_variable:filter_by_prefix("bridge.mqtt", Conf))), + C1 = maps:map(fun(Bn, Bc) -> + maps:to_list(maps:fold(Translate, #{}, maps:from_list(Bc))) + end, C), + maps:to_list(C1) +end}. diff --git a/apps/emqx_bridge_mqtt/rebar.config b/apps/emqx_bridge_mqtt/rebar.config new file mode 100644 index 000000000..1c9f5cbdd --- /dev/null +++ b/apps/emqx_bridge_mqtt/rebar.config @@ -0,0 +1,32 @@ +{deps, + [{replayq, {git, "https://github.com/emqx/replayq", {tag, "v0.2.0"}}}, + {ecpool, {git, "https://github.com/emqx/ecpool", {tag, "v0.4.2"}}}, + {emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.2.0"}}} + ]}. + +{edoc_opts, [{preprocess, true}]}. +{erl_opts, [warn_unused_vars, + warn_shadow_vars, + warn_unused_import, + warn_obsolete_guard, + debug_info, + {parse_transform}]}. + +{xref_checks, [undefined_function_calls, undefined_functions, + locals_not_used, deprecated_function_calls, + warnings_as_errors, deprecated_functions]}. +{cover_enabled, true}. +{cover_opts, [verbose]}. +{cover_export_enabled, true}. + +{shell, [ + % {config, "config/sys.config"}, + {apps, [emqx, emqx_bridge_mqtt]} +]}. + +{profiles, + [{test, + [{deps, + [{emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}}]} + ]} +]}. diff --git a/apps/emqx_bridge_mqtt/src/emqx_bridge_connect.erl b/apps/emqx_bridge_mqtt/src/emqx_bridge_connect.erl new file mode 100644 index 000000000..5086ca574 --- /dev/null +++ b/apps/emqx_bridge_mqtt/src/emqx_bridge_connect.erl @@ -0,0 +1,74 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_bridge_connect). + +-export([start/2]). + +-export_type([config/0, connection/0]). + +-optional_callbacks([ensure_subscribed/3, ensure_unsubscribed/2]). + +%% map fields depend on implementation +-type(config() :: map()). +-type(connection() :: term()). +-type(batch() :: emqx_protal:batch()). +-type(ack_ref() :: emqx_bridge_worker:ack_ref()). +-type(topic() :: emqx_topic:topic()). +-type(qos() :: emqx_mqtt_types:qos()). + +-include_lib("emqx/include/logger.hrl"). + +-logger_header("[Bridge Connect]"). + +%% establish the connection to remote node/cluster +%% protal worker (the caller process) should be expecting +%% a message {disconnected, conn_ref()} when disconnected. +-callback start(config()) -> {ok, connection()} | {error, any()}. + +%% send to remote node/cluster +%% bridge worker (the caller process) should be expecting +%% a message {batch_ack, reference()} when batch is acknowledged by remote node/cluster +-callback send(connection(), batch()) -> {ok, ack_ref()} | {ok, integer()} | {error, any()}. + +%% called when owner is shutting down. +-callback stop(connection()) -> ok. + +-callback ensure_subscribed(connection(), topic(), qos()) -> ok. + +-callback ensure_unsubscribed(connection(), topic()) -> ok. + +start(Module, Config) -> + case Module:start(Config) of + {ok, Conn} -> + {ok, Conn}; + {error, Reason} -> + Config1 = obfuscate(Config), + ?LOG(error, "Failed to connect with module=~p\n" + "config=~p\nreason:~p", [Module, Config1, Reason]), + {error, Reason} + end. + +obfuscate(Map) -> + maps:fold(fun(K, V, Acc) -> + case is_sensitive(K) of + true -> [{K, '***'} | Acc]; + false -> [{K, V} | Acc] + end + end, [], Map). + +is_sensitive(password) -> true; +is_sensitive(_) -> false. diff --git a/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.app.src b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.app.src new file mode 100644 index 000000000..24ac4eebd --- /dev/null +++ b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.app.src @@ -0,0 +1,14 @@ +{application, emqx_bridge_mqtt, + [{description, "EMQ X Bridge to MQTT Broker"}, + {vsn, "git"}, + {modules, []}, + {registered, []}, + {applications, [kernel,stdlib,replayq,emqtt]}, + {mod, {emqx_bridge_mqtt_app, []}}, + {env, []}, + {licenses, ["Apache-2.0"]}, + {maintainers, ["EMQ X Team "]}, + {links, [{"Homepage", "https://emqx.io/"}, + {"Github", "https://github.com/emqx/emqx-bridge-mqtt"} + ]} + ]}. diff --git a/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.app.src.script b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.app.src.script new file mode 100644 index 000000000..0e14ff23f --- /dev/null +++ b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.app.src.script @@ -0,0 +1,24 @@ +%%-*- mode: erlang -*- +%% .app.src.script + +RemoveLeadingV = + fun(Tag) -> + case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of + nomatch -> + re:replace(Tag, "/", "-", [{return ,list}]); + _ -> + %% if it is a version number prefixed by 'v' or 'e', then remove it + re:replace(Tag, "[v|e]", "", [{return ,list}]) + end + end, + +case os:getenv("EMQX_DEPS_DEFAULT_VSN") of + false -> CONFIG; % env var not defined + [] -> CONFIG; % env var set to empty string + Tag -> + [begin + AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}), + {application, App, AppConf0} + end || Conf = {application, App, AppConf} <- CONFIG] +end. + diff --git a/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.appup.src b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.appup.src new file mode 100644 index 000000000..8264abc25 --- /dev/null +++ b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.appup.src @@ -0,0 +1,32 @@ +%% -*-: erlang -*- + +{"4.2.3", + [ + {"4.2.2", [ + {load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []}, + {apply, {emqx_rule_engine, load_providers, []}} + ]}, + {"4.2.1", [ + {load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []}, + {apply, {emqx_rule_engine, load_providers, []}} + ]}, + {"4.2.0", [ + {load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []}, + {apply, {emqx_rule_engine, load_providers, []}} + ]} + ], + [ + {"4.2.2", [ + {load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []}, + {apply, {emqx_rule_engine, load_providers, []}} + ]}, + {"4.2.1", [ + {load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []}, + {apply, {emqx_rule_engine, load_providers, []}} + ]}, + {"4.2.0", [ + {load_module, emqx_bridge_mqtt_actions, brutal_purge, soft_purge, []}, + {apply, {emqx_rule_engine, load_providers, []}} + ]} + ] +}. diff --git a/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.erl b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.erl new file mode 100644 index 000000000..c189a2ea2 --- /dev/null +++ b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt.erl @@ -0,0 +1,185 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +%% @doc This module implements EMQX Bridge transport layer on top of MQTT protocol + +-module(emqx_bridge_mqtt). + +-behaviour(emqx_bridge_connect). + +%% behaviour callbacks +-export([ start/1 + , send/2 + , stop/1 + ]). + +%% optional behaviour callbacks +-export([ ensure_subscribed/3 + , ensure_unsubscribed/2 + ]). + +-include_lib("emqx/include/logger.hrl"). +-include_lib("emqx/include/emqx_mqtt.hrl"). + +-define(ACK_REF(ClientPid, PktId), {ClientPid, PktId}). + +%% Messages towards ack collector process +-define(REF_IDS(Ref, Ids), {Ref, Ids}). + +%%-------------------------------------------------------------------- +%% emqx_bridge_connect callbacks +%%-------------------------------------------------------------------- + +start(Config = #{address := Address}) -> + Parent = self(), + Mountpoint = maps:get(receive_mountpoint, Config, undefined), + Handlers = make_hdlr(Parent, Mountpoint), + {Host, Port} = case string:tokens(Address, ":") of + [H] -> {H, 1883}; + [H, P] -> {H, list_to_integer(P)} + end, + ClientConfig = Config#{msg_handler => Handlers, + host => Host, + port => Port, + force_ping => true + }, + case emqtt:start_link(replvar(ClientConfig)) of + {ok, Pid} -> + case emqtt:connect(Pid) of + {ok, _} -> + try + subscribe_remote_topics(Pid, maps:get(subscriptions, Config, [])), + {ok, #{client_pid => Pid}} + catch + throw : Reason -> + ok = stop(#{client_pid => Pid}), + {error, Reason} + end; + {error, Reason} -> + ok = stop(#{client_pid => Pid}), + {error, Reason} + end; + {error, Reason} -> + {error, Reason} + end. + +stop(#{client_pid := Pid}) -> + safe_stop(Pid, fun() -> emqtt:stop(Pid) end, 1000), + ok. + +ensure_subscribed(#{client_pid := Pid}, Topic, QoS) when is_pid(Pid) -> + case emqtt:subscribe(Pid, Topic, QoS) of + {ok, _, _} -> ok; + Error -> Error + end; +ensure_subscribed(_Conn, _Topic, _QoS) -> + %% return ok for now, next re-connect should should call start with new topic added to config + ok. + +ensure_unsubscribed(#{client_pid := Pid}, Topic) when is_pid(Pid) -> + case emqtt:unsubscribe(Pid, Topic) of + {ok, _, _} -> ok; + Error -> Error + end; +ensure_unsubscribed(_, _) -> + %% return ok for now, next re-connect should should call start with this topic deleted from config + ok. + +safe_stop(Pid, StopF, Timeout) -> + MRef = monitor(process, Pid), + unlink(Pid), + try + StopF() + catch + _ : _ -> + ok + end, + receive + {'DOWN', MRef, _, _, _} -> + ok + after + Timeout -> + exit(Pid, kill) + end. + +send(Conn, Msgs) -> + send(Conn, Msgs, undefined). +send(_Conn, [], PktId) -> + {ok, PktId}; +send(#{client_pid := ClientPid} = Conn, [Msg | Rest], _PktId) -> + case emqtt:publish(ClientPid, Msg) of + ok -> + Ref = make_ref(), + self() ! {batch_ack, Ref}, + send(Conn, Rest, Ref); + {ok, PktId} -> + send(Conn, Rest, PktId); + {error, Reason} -> + %% NOTE: There is no partial sucess of a batch and recover from the middle + %% only to retry all messages in one batch + {error, Reason} + end. + + +handle_puback(Parent, #{packet_id := PktId, reason_code := RC}) + when RC =:= ?RC_SUCCESS; + RC =:= ?RC_NO_MATCHING_SUBSCRIBERS -> + Parent ! {batch_ack, PktId}, ok; +handle_puback(_Parent, #{packet_id := PktId, reason_code := RC}) -> + ?LOG(warning, "Publish ~p to remote node falied, reason_code: ~p", [PktId, RC]). + +handle_publish(Msg, Mountpoint) -> + emqx_broker:publish(emqx_bridge_msg:to_broker_msg(Msg, Mountpoint)). + +handle_disconnected(Parent, Reason) -> + Parent ! {disconnected, self(), Reason}. + +make_hdlr(Parent, Mountpoint) -> + #{puback => fun(Ack) -> handle_puback(Parent, Ack) end, + publish => fun(Msg) -> handle_publish(Msg, Mountpoint) end, + disconnected => fun(Reason) -> handle_disconnected(Parent, Reason) end + }. + +subscribe_remote_topics(ClientPid, Subscriptions) -> + lists:foreach(fun({Topic, Qos}) -> + case emqtt:subscribe(ClientPid, Topic, Qos) of + {ok, _, _} -> ok; + Error -> throw(Error) + end + end, Subscriptions). + +%%-------------------------------------------------------------------- +%% Internal funcs +%%-------------------------------------------------------------------- + +replvar(Options) -> + replvar([clientid], Options). + +replvar([], Options) -> + Options; +replvar([Key|More], Options) -> + case maps:get(Key, Options, undefined) of + undefined -> + replvar(More, Options); + Val -> + replvar(More, maps:put(Key, feedvar(Key, Val, Options), Options)) + end. + +%% ${node} => node() +feedvar(clientid, ClientId, _) -> + iolist_to_binary(re:replace(ClientId, "\\${node}", atom_to_list(node()))); +feedvar(_, Val, _) -> + Val. diff --git a/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_actions.erl b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_actions.erl new file mode 100644 index 000000000..e46c6d05b --- /dev/null +++ b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_actions.erl @@ -0,0 +1,771 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +%% @doc This module implements EMQX Bridge transport layer on top of MQTT protocol + +-module(emqx_bridge_mqtt_actions). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-import(emqx_rule_utils, [str/1]). + +-export([ on_resource_create/2 + , on_get_resource_status/2 + , on_resource_destroy/2 + ]). + +%% Callbacks of ecpool Worker +-export([connect/1]). + +-export([subscriptions/1]). + +-export([on_action_create_data_to_mqtt_broker/2]). + +-define(RESOURCE_TYPE_MQTT, 'bridge_mqtt'). +-define(RESOURCE_TYPE_MQTT_SUB, 'bridge_mqtt_sub'). +-define(RESOURCE_TYPE_RPC, 'bridge_rpc'). + +-define(RESOURCE_CONFIG_SPEC_MQTT, #{ + address => #{ + order => 1, + type => string, + required => true, + default => <<"127.0.0.1:1883">>, + title => #{en => <<" Broker Address">>, + zh => <<"远程 broker 地址"/utf8>>}, + description => #{en => <<"The MQTT Remote Address">>, + zh => <<"远程 MQTT Broker 的地址"/utf8>>} + }, + pool_size => #{ + order => 2, + type => number, + required => true, + default => 8, + title => #{en => <<"Pool Size">>, + zh => <<"连接池大小"/utf8>>}, + description => #{en => <<"MQTT Connection Pool Size">>, + zh => <<"连接池大小"/utf8>>} + }, + clientid => #{ + order => 3, + type => string, + required => true, + default => <<"client">>, + title => #{en => <<"ClientId">>, + zh => <<"客户端 Id"/utf8>>}, + description => #{en => <<"ClientId for connecting to remote MQTT broker">>, + zh => <<"连接远程 Broker 的 ClientId"/utf8>>} + }, + append => #{ + order => 4, + type => boolean, + required => false, + default => true, + title => #{en => <<"Append GUID">>, + zh => <<"附加 GUID"/utf8>>}, + description => #{en => <<"Append GUID to MQTT ClientId?">>, + zh => <<"是否将GUID附加到 MQTT ClientId 后"/utf8>>} + }, + username => #{ + order => 5, + type => string, + required => false, + default => <<"">>, + title => #{en => <<"Username">>, zh => <<"用户名"/utf8>>}, + description => #{en => <<"Username for connecting to remote MQTT Broker">>, + zh => <<"连接远程 Broker 的用户名"/utf8>>} + }, + password => #{ + order => 6, + type => string, + required => false, + default => <<"">>, + title => #{en => <<"Password">>, + zh => <<"密码"/utf8>>}, + description => #{en => <<"Password for connecting to remote MQTT Broker">>, + zh => <<"连接远程 Broker 的密码"/utf8>>} + }, + mountpoint => #{ + order => 7, + type => string, + required => false, + default => <<"bridge/aws/${node}/">>, + title => #{en => <<"Bridge MountPoint">>, + zh => <<"桥接挂载点"/utf8>>}, + description => #{ + en => <<"MountPoint for bridge topic:
" + "Example: The topic of messages sent to `topic1` on local node" + "will be transformed to `bridge/aws/${node}/topic1`">>, + zh => <<"桥接主题的挂载点:
" + "示例: 本地节点向 `topic1` 发消息,远程桥接节点的主题" + "会变换为 `bridge/aws/${node}/topic1`"/utf8>> + } + }, + disk_cache => #{ + order => 8, + type => string, + required => false, + default => <<"off">>, + enum => [<<"on">>, <<"off">>], + title => #{en => <<"Disk Cache">>, + zh => <<"磁盘缓存"/utf8>>}, + description => #{en => <<"The flag which determines whether messages" + "can be cached on local disk when bridge is" + "disconnected">>, + zh => <<"当桥接断开时用于控制是否将消息缓存到本地磁" + "盘队列上"/utf8>>} + }, + proto_ver => #{ + order => 9, + type => string, + required => false, + default => <<"mqttv4">>, + enum => [<<"mqttv3">>, <<"mqttv4">>, <<"mqttv5">>], + title => #{en => <<"Protocol Version">>, + zh => <<"协议版本"/utf8>>}, + description => #{en => <<"MQTTT Protocol version">>, + zh => <<"MQTT 协议版本"/utf8>>} + }, + keepalive => #{ + order => 10, + type => string, + required => false, + default => <<"60s">> , + title => #{en => <<"Keepalive">>, + zh => <<"心跳间隔"/utf8>>}, + description => #{en => <<"Keepalive">>, + zh => <<"心跳间隔"/utf8>>} + }, + reconnect_interval => #{ + order => 11, + type => string, + required => false, + default => <<"30s">>, + title => #{en => <<"Reconnect Interval">>, + zh => <<"重连间隔"/utf8>>}, + description => #{en => <<"Reconnect interval of bridge:
">>, + zh => <<"重连间隔"/utf8>>} + }, + retry_interval => #{ + order => 12, + type => string, + required => false, + default => <<"20s">>, + title => #{en => <<"Retry interval">>, + zh => <<"重传间隔"/utf8>>}, + description => #{en => <<"Retry interval for bridge QoS1 message delivering">>, + zh => <<"消息重传间隔"/utf8>>} + }, + bridge_mode => #{ + order => 13, + type => boolean, + required => false, + default => false, + title => #{en => <<"Bridge Mode">>, + zh => <<"桥接模式"/utf8>>}, + description => #{en => <<"Bridge mode for MQTT bridge connection">>, + zh => <<"MQTT 连接是否为桥接模式"/utf8>>} + }, + ssl => #{ + order => 14, + type => string, + required => false, + default => <<"off">>, + enum => [<<"on">>, <<"off">>], + title => #{en => <<"Bridge SSL">>, + zh => <<"Bridge SSL"/utf8>>}, + description => #{en => <<"Switch which used to enable ssl connection of the bridge">>, + zh => <<"是否启用 Bridge SSL 连接"/utf8>>} + }, + cacertfile => #{ + order => 15, + type => string, + required => false, + default => <<"etc/certs/cacert.pem">>, + title => #{en => <<"CA certificates">>, + zh => <<"CA 证书"/utf8>>}, + description => #{en => <<"The file path of the CA certificates">>, + zh => <<"CA 证书路径"/utf8>>} + }, + certfile => #{ + order => 16, + type => string, + required => false, + default => <<"etc/certs/client-cert.pem">>, + title => #{en => <<"SSL Certfile">>, + zh => <<"SSL 客户端证书"/utf8>>}, + description => #{en => <<"The file path of the client certfile">>, + zh => <<"客户端证书路径"/utf8>>} + }, + keyfile => #{ + order => 17, + type => string, + required => false, + default => <<"etc/certs/client-key.pem">>, + title => #{en => <<"SSL Keyfile">>, + zh => <<"SSL 密钥文件"/utf8>>}, + description => #{en => <<"The file path of the client keyfile">>, + zh => <<"客户端密钥路径"/utf8>>} + }, + ciphers => #{ + order => 18, + type => string, + required => false, + default => <<"ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,", + "ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,", + "ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,", + "ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,", + "AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,", + "ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,", + "ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,", + "DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,", + "ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,", + "ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,", + "DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA">>, + title => #{en => <<"SSL Ciphers">>, + zh => <<"SSL 加密算法"/utf8>>}, + description => #{en => <<"SSL Ciphers">>, + zh => <<"SSL 加密算法"/utf8>>} + } + }). + + +-define(RESOURCE_CONFIG_SPEC_MQTT_SUB, #{ + address => #{ + order => 1, + type => string, + required => true, + default => <<"127.0.0.1:1883">>, + title => #{en => <<" Broker Address">>, + zh => <<"远程 broker 地址"/utf8>>}, + description => #{en => <<"The MQTT Remote Address">>, + zh => <<"远程 MQTT Broker 的地址"/utf8>>} + }, + pool_size => #{ + order => 2, + type => number, + required => true, + default => 8, + title => #{en => <<"Pool Size">>, + zh => <<"连接池大小"/utf8>>}, + description => #{en => <<"MQTT Connection Pool Size">>, + zh => <<"连接池大小"/utf8>>} + }, + clientid => #{ + order => 3, + type => string, + required => true, + default => <<"client">>, + title => #{en => <<"ClientId">>, + zh => <<"客户端 Id"/utf8>>}, + description => #{en => <<"ClientId for connecting to remote MQTT broker">>, + zh => <<"连接远程 Broker 的 ClientId"/utf8>>} + }, + append => #{ + order => 4, + type => boolean, + required => true, + default => true, + title => #{en => <<"Append GUID">>, + zh => <<"附加 GUID"/utf8>>}, + description => #{en => <<"Append GUID to MQTT ClientId?">>, + zh => <<"是否将GUID附加到 MQTT ClientId 后"/utf8>>} + }, + username => #{ + order => 5, + type => string, + required => false, + default => <<"">>, + title => #{en => <<"Username">>, zh => <<"用户名"/utf8>>}, + description => #{en => <<"Username for connecting to remote MQTT Broker">>, + zh => <<"连接远程 Broker 的用户名"/utf8>>} + }, + password => #{ + order => 6, + type => string, + required => false, + default => <<"">>, + title => #{en => <<"Password">>, + zh => <<"密码"/utf8>>}, + description => #{en => <<"Password for connecting to remote MQTT Broker">>, + zh => <<"连接远程 Broker 的密码"/utf8>>} + }, + subscription_opts => #{ + order => 7, + type => array, + items => #{ + type => object, + schema => #{ + topic => #{ + order => 1, + type => string, + default => <<>>, + title => #{en => <<"MQTT Topic">>, + zh => <<"MQTT 主题"/utf8>>}, + description => #{en => <<"MQTT Topic">>, + zh => <<"MQTT 主题"/utf8>>} + }, + qos => #{ + order => 2, + type => number, + enum => [0, 1, 2], + default => 0, + title => #{en => <<"MQTT Topic QoS">>, + zh => <<"MQTT 服务质量"/utf8>>}, + description => #{en => <<"MQTT Topic QoS">>, + zh => <<"MQTT 服务质量"/utf8>>} + } + } + }, + default => [], + title => #{en => <<"Subscription Opts">>, + zh => <<"订阅选项"/utf8>>}, + description => #{en => <<"Subscription Opts">>, + zh => <<"订阅选项"/utf8>>} + }, + proto_ver => #{ + order => 8, + type => string, + required => false, + default => <<"mqttv4">>, + enum => [<<"mqttv3">>, <<"mqttv4">>, <<"mqttv5">>], + title => #{en => <<"Protocol Version">>, + zh => <<"协议版本"/utf8>>}, + description => #{en => <<"MQTTT Protocol version">>, + zh => <<"MQTT 协议版本"/utf8>>} + }, + keepalive => #{ + order => 9, + type => string, + required => false, + default => <<"60s">> , + title => #{en => <<"Keepalive">>, + zh => <<"心跳间隔"/utf8>>}, + description => #{en => <<"Keepalive">>, + zh => <<"心跳间隔"/utf8>>} + }, + reconnect_interval => #{ + order => 10, + type => string, + required => false, + default => <<"30s">>, + title => #{en => <<"Reconnect Interval">>, + zh => <<"重连间隔"/utf8>>}, + description => #{en => <<"Reconnect interval of bridge">>, + zh => <<"重连间隔"/utf8>>} + }, + ssl => #{ + order => 11, + type => string, + required => false, + default => <<"off">>, + enum => [<<"on">>, <<"off">>], + title => #{en => <<"Bridge SSL">>, + zh => <<"Bridge SSL"/utf8>>}, + description => #{en => <<"Switch which used to enable ssl connection of the bridge">>, + zh => <<"是否启用 Bridge SSL 连接"/utf8>>} + }, + cacertfile => #{ + order => 12, + type => string, + required => false, + default => <<"etc/certs/cacert.pem">>, + title => #{en => <<"CA certificates">>, + zh => <<"CA 证书"/utf8>>}, + description => #{en => <<"The file path of the CA certificates">>, + zh => <<"CA 证书路径"/utf8>>} + }, + certfile => #{ + order => 13, + type => string, + required => false, + default => <<"etc/certs/client-cert.pem">>, + title => #{en => <<"SSL Certfile">>, + zh => <<"SSL 客户端证书"/utf8>>}, + description => #{en => <<"The file path of the client certfile">>, + zh => <<"客户端证书路径"/utf8>>} + }, + keyfile => #{ + order => 14, + type => string, + required => false, + default => <<"etc/certs/client-key.pem">>, + title => #{en => <<"SSL Keyfile">>, + zh => <<"SSL 密钥文件"/utf8>>}, + description => #{en => <<"The file path of the client keyfile">>, + zh => <<"客户端密钥路径"/utf8>>} + }, + ciphers => #{ + order => 15, + type => string, + required => false, + default => <<"ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384">>, + title => #{en => <<"SSL Ciphers">>, + zh => <<"SSL 加密算法"/utf8>>}, + description => #{en => <<"SSL Ciphers">>, + zh => <<"SSL 加密算法"/utf8>>} + } + }). + + +-define(RESOURCE_CONFIG_SPEC_RPC, #{ + address => #{ + order => 1, + type => string, + required => true, + default => <<"emqx2@127.0.0.1">>, + title => #{en => <<"EMQ X Node Name">>, + zh => <<"EMQ X 节点名称"/utf8>>}, + description => #{en => <<"EMQ X Remote Node Name">>, + zh => <<"远程 EMQ X 节点名称 "/utf8>>} + }, + mountpoint => #{ + order => 2, + type => string, + required => false, + default => <<"bridge/emqx/${node}/">>, + title => #{en => <<"Bridge MountPoint">>, + zh => <<"桥接挂载点"/utf8>>}, + description => #{en => <<"MountPoint for bridge topic
" + "Example: The topic of messages sent to `topic1` on local node" + "will be transformed to `bridge/aws/${node}/topic1`">>, + zh => <<"桥接主题的挂载点
" + "示例: 本地节点向 `topic1` 发消息,远程桥接节点的主题" + "会变换为 `bridge/aws/${node}/topic1`"/utf8>>} + }, + pool_size => #{ + order => 3, + type => number, + required => true, + default => 8, + title => #{en => <<"Pool Size">>, + zh => <<"连接池大小"/utf8>>}, + description => #{en => <<"MQTT/RPC Connection Pool Size">>, + zh => <<"连接池大小"/utf8>>} + }, + reconnect_interval => #{ + order => 4, + type => string, + required => false, + default => <<"30s">>, + title => #{en => <<"Reconnect Interval">>, + zh => <<"重连间隔"/utf8>>}, + description => #{en => <<"Reconnect Interval of bridge">>, + zh => <<"重连间隔"/utf8>>} + }, + batch_size => #{ + order => 5, + type => number, + required => false, + default => 32, + title => #{en => <<"Batch Size">>, + zh => <<"批处理大小"/utf8>>}, + description => #{en => <<"Batch Size">>, + zh => <<"批处理大小"/utf8>>} + }, + disk_cache => #{ + order => 6, + type => string, + required => false, + default => <<"off">>, + enum => [<<"on">>, <<"off">>], + title => #{en => <<"Disk Cache">>, + zh => <<"磁盘缓存"/utf8>>}, + description => #{en => <<"The flag which determines whether messages" + "can be cached on local disk when bridge is" + "disconnected">>, + zh => <<"当桥接断开时用于控制是否将消息缓存到本地磁" + "盘队列上"/utf8>>} + } + }). + +-define(ACTION_PARAM_RESOURCE, #{ + type => string, + required => true, + title => #{en => <<"Resource ID">>, zh => <<"资源 ID"/utf8>>}, + description => #{en => <<"Bind a resource to this action">>, + zh => <<"给动作绑定一个资源"/utf8>>} + }). + +-resource_type(#{ + name => ?RESOURCE_TYPE_MQTT, + create => on_resource_create, + status => on_get_resource_status, + destroy => on_resource_destroy, + params => ?RESOURCE_CONFIG_SPEC_MQTT, + title => #{en => <<"MQTT Bridge">>, zh => <<"MQTT Bridge"/utf8>>}, + description => #{en => <<"MQTT Message Bridge">>, zh => <<"MQTT 消息桥接"/utf8>>} + }). + +-resource_type(#{ + name => ?RESOURCE_TYPE_MQTT_SUB, + create => on_resource_create, + status => on_get_resource_status, + destroy => on_resource_destroy, + params => ?RESOURCE_CONFIG_SPEC_MQTT_SUB, + title => #{en => <<"MQTT Subscribe">>, zh => <<"MQTT Subscribe"/utf8>>}, + description => #{en => <<"MQTT Subscribe">>, zh => <<"MQTT 订阅消息"/utf8>>} + }). + +-resource_type(#{ + name => ?RESOURCE_TYPE_RPC, + create => on_resource_create, + status => on_get_resource_status, + destroy => on_resource_destroy, + params => ?RESOURCE_CONFIG_SPEC_RPC, + title => #{en => <<"EMQX Bridge">>, zh => <<"EMQX Bridge"/utf8>>}, + description => #{en => <<"EMQ X RPC Bridge">>, zh => <<"EMQ X RPC 消息桥接"/utf8>>} + }). + +-rule_action(#{ + name => data_to_mqtt_broker, + category => data_forward, + for => 'message.publish', + types => [?RESOURCE_TYPE_MQTT, ?RESOURCE_TYPE_RPC], + create => on_action_create_data_to_mqtt_broker, + params => #{'$resource' => ?ACTION_PARAM_RESOURCE, + forward_topic => #{ + order => 1, + type => string, + required => false, + default => <<"">>, + title => #{en => <<"Forward Topic">>, + zh => <<"转发消息主题"/utf8>>}, + description => #{en => <<"The topic used when forwarding the message. Defaults to the topic of the bridge message if not provided.">>, + zh => <<"转发消息时使用的主题。如果未提供,则默认为桥接消息的主题。"/utf8>>} + }, + payload_tmpl => #{ + order => 2, + type => string, + input => textarea, + required => false, + default => <<"">>, + title => #{en => <<"Payload Template">>, + zh => <<"消息内容模板"/utf8>>}, + description => #{en => <<"The payload template, variable interpolation is supported. If using empty template (default), then the payload will be all the available vars in JSON format">>, + zh => <<"消息内容模板,支持变量。若使用空模板(默认),消息内容为 JSON 格式的所有字段"/utf8>>} + } + }, + title => #{en => <<"Data bridge to MQTT Broker">>, + zh => <<"桥接数据到 MQTT Broker"/utf8>>}, + description => #{en => <<"Bridge Data to MQTT Broker">>, + zh => <<"桥接数据到 MQTT Broker"/utf8>>} + }). + +on_resource_create(ResId, Params) -> + ?LOG(info, "Initiating Resource ~p, ResId: ~p", [?RESOURCE_TYPE_MQTT, ResId]), + {ok, _} = application:ensure_all_started(ecpool), + PoolName = pool_name(ResId), + Options = options(Params, PoolName), + start_resource(ResId, PoolName, Options), + case test_resource_status(PoolName) of + true -> ok; + false -> + on_resource_destroy(ResId, #{<<"pool">> => PoolName}), + error({{?RESOURCE_TYPE_MQTT, ResId}, connection_failed}) + end, + #{<<"pool">> => PoolName}. + +start_resource(ResId, PoolName, Options) -> + case ecpool:start_sup_pool(PoolName, ?MODULE, Options) of + {ok, _} -> + ?LOG(info, "Initiated Resource ~p Successfully, ResId: ~p", [?RESOURCE_TYPE_MQTT, ResId]); + {error, {already_started, _Pid}} -> + on_resource_destroy(ResId, #{<<"pool">> => PoolName}), + start_resource(ResId, PoolName, Options); + {error, Reason} -> + ?LOG(error, "Initiate Resource ~p failed, ResId: ~p, ~p", [?RESOURCE_TYPE_MQTT, ResId, Reason]), + on_resource_destroy(ResId, #{<<"pool">> => PoolName}), + error({{?RESOURCE_TYPE_MQTT, ResId}, create_failed}) + end. + +test_resource_status(PoolName) -> + IsConnected = fun(Worker) -> + case ecpool_worker:client(Worker) of + {ok, Bridge} -> + try emqx_bridge_worker:status(Bridge) of + connected -> true; + _ -> false + catch _Error:_Reason -> + false + end; + {error, _} -> + false + end + end, + Status = [IsConnected(Worker) || {_WorkerName, Worker} <- ecpool:workers(PoolName)], + lists:any(fun(St) -> St =:= true end, Status). + +-spec(on_get_resource_status(ResId::binary(), Params::map()) -> Status::map()). +on_get_resource_status(_ResId, #{<<"pool">> := PoolName}) -> + IsAlive = test_resource_status(PoolName), + #{is_alive => IsAlive}. + +on_resource_destroy(ResId, #{<<"pool">> := PoolName}) -> + ?LOG(info, "Destroying Resource ~p, ResId: ~p", [?RESOURCE_TYPE_MQTT, ResId]), + case ecpool:stop_sup_pool(PoolName) of + ok -> + ?LOG(info, "Destroyed Resource ~p Successfully, ResId: ~p", [?RESOURCE_TYPE_MQTT, ResId]); + {error, Reason} -> + ?LOG(error, "Destroy Resource ~p failed, ResId: ~p, ~p", [?RESOURCE_TYPE_MQTT, ResId, Reason]), + error({{?RESOURCE_TYPE_MQTT, ResId}, destroy_failed}) + end. + +on_action_create_data_to_mqtt_broker(_Id, #{<<"pool">> := PoolName, + <<"forward_topic">> := ForwardTopic, + <<"payload_tmpl">> := PayloadTmpl}) -> + ?LOG(info, "Initiating Action ~p.", [?FUNCTION_NAME]), + PayloadTks = emqx_rule_utils:preproc_tmpl(PayloadTmpl), + TopicTks = case ForwardTopic == <<"">> of + true -> undefined; + false -> emqx_rule_utils:preproc_tmpl(ForwardTopic) + end, + fun(Msg, _Env = #{id := Id, clientid := From, flags := Flags, + topic := Topic, timestamp := TimeStamp, qos := QoS}) -> + Topic1 = case TopicTks =:= undefined of + true -> Topic; + false -> emqx_rule_utils:proc_tmpl(TopicTks, Msg) + end, + BrokerMsg = #message{id = Id, + qos = QoS, + from = From, + flags = Flags, + topic = Topic1, + payload = format_data(PayloadTks, Msg), + timestamp = TimeStamp}, + ecpool:with_client(PoolName, fun(BridgePid) -> + BridgePid ! {deliver, rule_engine, BrokerMsg} + end) + end. + +format_data([], Msg) -> + emqx_json:encode(Msg); + +format_data(Tokens, Msg) -> + emqx_rule_utils:proc_tmpl(Tokens, Msg). + +tls_versions() -> + ['tlsv1.2','tlsv1.1', tlsv1]. + +ciphers(Ciphers) -> + string:tokens(str(Ciphers), ", "). + +subscriptions(Subscriptions) -> + scan_binary(<<"[", Subscriptions/binary, "].">>). + +is_node_addr(Addr0) -> + Addr = binary_to_list(Addr0), + case string:tokens(Addr, "@") of + [_NodeName, _Hostname] -> true; + _ -> false + end. + +scan_binary(Bin) -> + TermString = binary_to_list(Bin), + scan_string(TermString). + +scan_string(TermString) -> + {ok, Tokens, _} = erl_scan:string(TermString), + {ok, Term} = erl_parse:parse_term(Tokens), + Term. + +connect(Options) when is_list(Options) -> + connect(maps:from_list(Options)); +connect(Options = #{disk_cache := DiskCache, ecpool_worker_id := Id, pool_name := Pool}) -> + Options0 = case DiskCache of + true -> + DataDir = filename:join([emqx:get_env(data_dir), replayq, Pool, integer_to_list(Id)]), + QueueOption = #{replayq_dir => DataDir}, + Options#{queue => QueueOption}; + false -> + Options + end, + Options1 = case maps:is_key(append, Options0) of + false -> Options0; + true -> + case maps:get(append, Options0, false) of + true -> + ClientId = lists:concat([str(maps:get(clientid, Options0)), "_", str(emqx_guid:to_hexstr(emqx_guid:gen()))]), + Options0#{clientid => ClientId}; + false -> + Options0 + end + end, + Options2 = maps:without([ecpool_worker_id, pool_name, append], Options1), + emqx_bridge_worker:start_link(name(Pool, Id), Options2). +name(Pool, Id) -> + list_to_atom(atom_to_list(Pool) ++ ":" ++ integer_to_list(Id)). +pool_name(ResId) -> + list_to_atom("bridge_mqtt:" ++ str(ResId)). + +options(Options, PoolName) -> + GetD = fun(Key, Default) -> maps:get(Key, Options, Default) end, + Get = fun(Key) -> GetD(Key, undefined) end, + Address = Get(<<"address">>), + [{max_inflight_batches, 32}, + {forward_mountpoint, str(Get(<<"mountpoint">>))}, + {disk_cache, cuttlefish_flag:parse(str(GetD(<<"disk_cache">>, "off")))}, + {start_type, auto}, + {reconnect_delay_ms, cuttlefish_duration:parse(str(Get(<<"reconnect_interval">>)), ms)}, + {if_record_metrics, false}, + {pool_size, GetD(<<"pool_size">>, 1)}, + {pool_name, PoolName} + ] ++ case is_node_addr(Address) of + true -> + [{address, binary_to_atom(Get(<<"address">>), utf8)}, + {connect_module, emqx_bridge_rpc}, + {batch_size, Get(<<"batch_size">>)}]; + false -> + Subscriptions = format_subscriptions(GetD(<<"subscription_opts">>, [])), + Subscriptions1 = case Get(<<"topic">>) of + undefined -> Subscriptions; + Topic -> + [{subscriptions, [{Topic, Get(<<"qos">>)}]} | Subscriptions] + end, + [{address, binary_to_list(Address)}, + {bridge_mode, GetD(<<"bridge_mode">>, true)}, + {clean_start, true}, + {clientid, str(Get(<<"clientid">>))}, + {append, Get(<<"append">>)}, + {connect_module, emqx_bridge_mqtt}, + {keepalive, cuttlefish_duration:parse(str(Get(<<"keepalive">>)), s)}, + {username, str(Get(<<"username">>))}, + {password, str(Get(<<"password">>))}, + {proto_ver, mqtt_ver(Get(<<"proto_ver">>))}, + {retry_interval, cuttlefish_duration:parse(str(GetD(<<"retry_interval">>, "30s")), ms)}, + {ssl, cuttlefish_flag:parse(str(Get(<<"ssl">>)))}, + {ssl_opts, [{versions, tls_versions()}, + {ciphers, ciphers(Get(<<"ciphers">>))}, + {keyfile, str(Get(<<"keyfile">>))}, + {certfile, str(Get(<<"certfile">>))}, + {cacertfile, str(Get(<<"cacertfile">>))} + ]}] ++ Subscriptions1 + end. + + +mqtt_ver(ProtoVer) -> + case ProtoVer of + <<"mqttv3">> -> v3; + <<"mqttv4">> -> v4; + <<"mqttv5">> -> v5; + _ -> v4 + end. + +format_subscriptions(SubOpts) -> + lists:map(fun(Sub) -> + {maps:get(<<"topic">>, Sub), maps:get(<<"qos">>, Sub)} + end, SubOpts). diff --git a/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_app.erl b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_app.erl new file mode 100644 index 000000000..2c0a0b9a9 --- /dev/null +++ b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_app.erl @@ -0,0 +1,33 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_bridge_mqtt_app). + +-emqx_plugin(bridge). + +-behaviour(application). + +-export([start/2, stop/1]). + +start(_StartType, _StartArgs) -> + emqx_ctl:register_command(bridges, {emqx_bridge_mqtt_cli, cli}, []), + emqx_bridge_worker:register_metrics(), + emqx_bridge_mqtt_sup:start_link(). + +stop(_State) -> + emqx_ctl:unregister_command(bridges), + ok. + diff --git a/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_cli.erl b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_cli.erl new file mode 100644 index 000000000..1ee5a90c6 --- /dev/null +++ b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_cli.erl @@ -0,0 +1,98 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_bridge_mqtt_cli). + +-include("emqx_bridge_mqtt.hrl"). + +-import(lists, [foreach/2]). + +-export([cli/1]). + +cli(["list"]) -> + foreach(fun({Name, State0}) -> + State = case State0 of + connected -> <<"Running">>; + _ -> <<"Stopped">> + end, + emqx_ctl:print("name: ~s status: ~s~n", [Name, State]) + end, emqx_bridge_mqtt_sup:bridges()); + +cli(["start", Name]) -> + emqx_ctl:print("~s.~n", [try emqx_bridge_worker:ensure_started(Name) of + ok -> <<"Start bridge successfully">>; + connected -> <<"Bridge already started">>; + _ -> <<"Start bridge failed">> + catch + _Error:_Reason -> + <<"Start bridge failed">> + end]); + +cli(["stop", Name]) -> + emqx_ctl:print("~s.~n", [try emqx_bridge_worker:ensure_stopped(Name) of + ok -> <<"Stop bridge successfully">>; + _ -> <<"Stop bridge failed">> + catch + _Error:_Reason -> + <<"Stop bridge failed">> + end]); + +cli(["forwards", Name]) -> + foreach(fun(Topic) -> + emqx_ctl:print("topic: ~s~n", [Topic]) + end, emqx_bridge_worker:get_forwards(Name)); + +cli(["add-forward", Name, Topic]) -> + case emqx_bridge_worker:ensure_forward_present(Name, iolist_to_binary(Topic)) of + ok -> emqx_ctl:print("Add-forward topic successfully.~n"); + {error, Reason} -> emqx_ctl:print("Add-forward failed reason: ~p.~n", [Reason]) + end; + +cli(["del-forward", Name, Topic]) -> + case emqx_bridge_worker:ensure_forward_absent(Name, iolist_to_binary(Topic)) of + ok -> emqx_ctl:print("Del-forward topic successfully.~n"); + {error, Reason} -> emqx_ctl:print("Del-forward failed reason: ~p.~n", [Reason]) + end; + +cli(["subscriptions", Name]) -> + foreach(fun({Topic, Qos}) -> + emqx_ctl:print("topic: ~s, qos: ~p~n", [Topic, Qos]) + end, emqx_bridge_worker:get_subscriptions(Name)); + +cli(["add-subscription", Name, Topic, Qos]) -> + case emqx_bridge_worker:ensure_subscription_present(Name, Topic, list_to_integer(Qos)) of + ok -> emqx_ctl:print("Add-subscription topic successfully.~n"); + {error, Reason} -> emqx_ctl:print("Add-subscription failed reason: ~p.~n", [Reason]) + end; + +cli(["del-subscription", Name, Topic]) -> + case emqx_bridge_worker:ensure_subscription_absent(Name, Topic) of + ok -> emqx_ctl:print("Del-subscription topic successfully.~n"); + {error, Reason} -> emqx_ctl:print("Del-subscription failed reason: ~p.~n", [Reason]) + end; + +cli(_) -> + emqx_ctl:usage([{"bridges list", "List bridges"}, + {"bridges start ", "Start a bridge"}, + {"bridges stop ", "Stop a bridge"}, + {"bridges forwards ", "Show a bridge forward topic"}, + {"bridges add-forward ", "Add bridge forward topic"}, + {"bridges del-forward ", "Delete bridge forward topic"}, + {"bridges subscriptions ", "Show a bridge subscriptions topic"}, + {"bridges add-subscription ", "Add bridge subscriptions topic"}, + {"bridges del-subscription ", "Delete bridge subscriptions topic"}]). + + diff --git a/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_sup.erl b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_sup.erl new file mode 100644 index 000000000..92d8e4083 --- /dev/null +++ b/apps/emqx_bridge_mqtt/src/emqx_bridge_mqtt_sup.erl @@ -0,0 +1,84 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_bridge_mqtt_sup). +-behaviour(supervisor). + +-include("emqx_bridge_mqtt.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-logger_header("[Bridge]"). + +%% APIs +-export([ start_link/0 + , start_link/1 + ]). + +-export([ create_bridge/2 + , drop_bridge/1 + , bridges/0 + , is_bridge_exist/1 + ]). + +%% supervisor callbacks +-export([init/1]). + +-define(SUP, ?MODULE). +-define(WORKER_SUP, emqx_bridge_worker_sup). + +start_link() -> start_link(?SUP). + +start_link(Name) -> + supervisor:start_link({local, Name}, ?MODULE, Name). + +init(?SUP) -> + BridgesConf = application:get_env(?APP, bridges, []), + BridgeSpec = lists:map(fun bridge_spec/1, BridgesConf), + SupFlag = #{strategy => one_for_one, + intensity => 100, + period => 10}, + {ok, {SupFlag, BridgeSpec}}. + +bridge_spec({Name, Config}) -> + #{id => Name, + start => {emqx_bridge_worker, start_link, [Name, Config]}, + restart => permanent, + shutdown => 5000, + type => worker, + modules => [emqx_bridge_worker]}. + +-spec(bridges() -> [{node(), map()}]). +bridges() -> + [{Name, emqx_bridge_worker:status(Pid)} || {Name, Pid, _, _} <- supervisor:which_children(?SUP)]. + +-spec(is_bridge_exist(atom() | pid()) -> boolean()). +is_bridge_exist(Id) -> + case supervisor:get_childspec(?SUP, Id) of + {ok, _ChildSpec} -> true; + {error, _Error} -> false + end. + +create_bridge(Id, Config) -> + supervisor:start_child(?SUP, bridge_spec({Id, Config})). + +drop_bridge(Id) -> + case supervisor:terminate_child(?SUP, Id) of + ok -> + supervisor:delete_child(?SUP, Id); + {error, Error} -> + ?LOG(error, "Delete bridge failed, error : ~p", [Error]), + {error, Error} + end. diff --git a/apps/emqx_bridge_mqtt/src/emqx_bridge_msg.erl b/apps/emqx_bridge_mqtt/src/emqx_bridge_msg.erl new file mode 100644 index 000000000..20600282e --- /dev/null +++ b/apps/emqx_bridge_mqtt/src/emqx_bridge_msg.erl @@ -0,0 +1,98 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_bridge_msg). + +-export([ to_binary/1 + , from_binary/1 + , to_export/3 + , to_broker_msgs/1 + , to_broker_msg/1 + , to_broker_msg/2 + , estimate_size/1 + ]). + +-export_type([msg/0]). + +-include_lib("emqx/include/emqx.hrl"). + +-include_lib("emqx_bridge_mqtt/include/emqx_bridge_mqtt.hrl"). +-include_lib("emqtt/include/emqtt.hrl"). + + +-type msg() :: emqx_types:message(). +-type exp_msg() :: emqx_types:message() | #mqtt_msg{}. + +%% @doc Make export format: +%% 1. Mount topic to a prefix +%% 2. Fix QoS to 1 +%% @end +%% Shame that we have to know the callback module here +%% would be great if we can get rid of #mqtt_msg{} record +%% and use #message{} in all places. +-spec to_export(emqx_bridge_rpc | emqx_bridge_worker, + undefined | binary(), msg()) -> exp_msg(). +to_export(emqx_bridge_mqtt, Mountpoint, + #message{topic = Topic, + payload = Payload, + flags = Flags, + qos = QoS + }) -> + Retain = maps:get(retain, Flags, false), + #mqtt_msg{qos = QoS, + retain = Retain, + topic = topic(Mountpoint, Topic), + payload = Payload}; +to_export(_Module, Mountpoint, + #message{topic = Topic} = Msg) -> + Msg#message{topic = topic(Mountpoint, Topic)}. + +%% @doc Make `binary()' in order to make iodata to be persisted on disk. +-spec to_binary(msg()) -> binary(). +to_binary(Msg) -> term_to_binary(Msg). + +%% @doc Unmarshal binary into `msg()'. +-spec from_binary(binary()) -> msg(). +from_binary(Bin) -> binary_to_term(Bin). + +%% @doc Estimate the size of a message. +%% Count only the topic length + payload size +-spec estimate_size(msg()) -> integer(). +estimate_size(#message{topic = Topic, payload = Payload}) -> + size(Topic) + size(Payload). + +%% @doc By message/batch receiver, transform received batch into +%% messages to deliver to local brokers. +to_broker_msgs(Batch) -> lists:map(fun to_broker_msg/1, Batch). + +to_broker_msg(#message{} = Msg) -> + %% internal format from another EMQX node via rpc + Msg; +to_broker_msg(Msg) -> + to_broker_msg(Msg, undefined). +to_broker_msg(#{qos := QoS, dup := Dup, retain := Retain, topic := Topic, + properties := Props, payload := Payload}, Mountpoint) -> + %% published from remote node over a MQTT connection + set_headers(Props, + emqx_message:set_flags(#{dup => Dup, retain => Retain}, + emqx_message:make(bridge, QoS, topic(Mountpoint, Topic), Payload))). + +set_headers(undefined, Msg) -> + Msg; +set_headers(Val, Msg) -> + emqx_message:set_headers(Val, Msg). +topic(undefined, Topic) -> Topic; +topic(Prefix, Topic) -> emqx_topic:prepend(Prefix, Topic). diff --git a/apps/emqx_bridge_mqtt/src/emqx_bridge_rpc.erl b/apps/emqx_bridge_mqtt/src/emqx_bridge_rpc.erl new file mode 100644 index 000000000..92ef0c60a --- /dev/null +++ b/apps/emqx_bridge_mqtt/src/emqx_bridge_rpc.erl @@ -0,0 +1,99 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +%% @doc This module implements EMQX Bridge transport layer based on gen_rpc. + +-module(emqx_bridge_rpc). + +-behaviour(emqx_bridge_connect). + +%% behaviour callbacks +-export([ start/1 + , send/2 + , stop/1 + ]). + +%% Internal exports +-export([ handle_send/1 + , heartbeat/2 + ]). + +-type ack_ref() :: emqx_bridge_worker:ack_ref(). +-type batch() :: emqx_bridge_worker:batch(). + +-define(HEARTBEAT_INTERVAL, timer:seconds(1)). + +-define(RPC, emqx_rpc). + +start(#{address := Remote}) -> + case poke(Remote) of + ok -> + Pid = proc_lib:spawn_link(?MODULE, heartbeat, [self(), Remote]), + {ok, #{client_pid => Pid, address => Remote}}; + Error -> + Error + end. + +stop(#{client_pid := Pid}) when is_pid(Pid) -> + Ref = erlang:monitor(process, Pid), + unlink(Pid), + Pid ! stop, + receive + {'DOWN', Ref, process, Pid, _Reason} -> + ok + after + 1000 -> + exit(Pid, kill) + end, + ok. + +%% @doc Callback for `emqx_bridge_connect' behaviour +-spec send(node(), batch()) -> {ok, ack_ref()} | {error, any()}. +send(#{address := Remote}, Batch) -> + case ?RPC:call(Remote, ?MODULE, handle_send, [Batch]) of + ok -> + Ref = make_ref(), + self() ! {batch_ack, Ref}, + {ok, Ref}; + {badrpc, Reason} -> {error, Reason} + end. + +%% @doc Handle send on receiver side. +-spec handle_send(batch()) -> ok. +handle_send(Batch) -> + lists:foreach(fun(Msg) -> emqx_broker:publish(Msg) end, Batch). + +%% @hidden Heartbeat loop +heartbeat(Parent, RemoteNode) -> + Interval = ?HEARTBEAT_INTERVAL, + receive + stop -> exit(normal) + after + Interval -> + case poke(RemoteNode) of + ok -> + ?MODULE:heartbeat(Parent, RemoteNode); + {error, Reason} -> + Parent ! {disconnected, self(), Reason}, + exit(normal) + end + end. + +poke(Node) -> + case ?RPC:call(Node, erlang, node, []) of + Node -> ok; + {badrpc, Reason} -> {error, Reason} + end. diff --git a/apps/emqx_bridge_mqtt/src/emqx_bridge_worker.erl b/apps/emqx_bridge_mqtt/src/emqx_bridge_worker.erl new file mode 100644 index 000000000..2078fe41a --- /dev/null +++ b/apps/emqx_bridge_mqtt/src/emqx_bridge_worker.erl @@ -0,0 +1,595 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +%% @doc Bridge works in two layers (1) batching layer (2) transport layer +%% The `bridge' batching layer collects local messages in batches and sends over +%% to remote MQTT node/cluster via `connetion' transport layer. +%% In case `REMOTE' is also an EMQX node, `connection' is recommended to be +%% the `gen_rpc' based implementation `emqx_bridge_rpc'. Otherwise `connection' +%% has to be `emqx_bridge_mqtt'. +%% +%% ``` +%% +------+ +--------+ +%% | EMQX | | REMOTE | +%% | | | | +%% | (bridge) <==(connection)==> | | +%% | | | | +%% | | | | +%% +------+ +--------+ +%% ''' +%% +%% +%% This module implements 2 kinds of APIs with regards to batching and +%% messaging protocol. (1) A `gen_statem' based local batch collector; +%% (2) APIs for incoming remote batches/messages. +%% +%% Batch collector state diagram +%% +%% [idle] --(0) --> [connecting] --(2)--> [connected] +%% | ^ | +%% | | | +%% '--(1)---'--------(3)------' +%% +%% (0): auto or manual start +%% (1): retry timeout +%% (2): successfuly connected to remote node/cluster +%% (3): received {disconnected, Reason} OR +%% failed to send to remote node/cluster. +%% +%% NOTE: A bridge worker may subscribe to multiple (including wildcard) +%% local topics, and the underlying `emqx_bridge_connect' may subscribe to +%% multiple remote topics, however, worker/connections are not designed +%% to support automatic load-balancing, i.e. in case it can not keep up +%% with the amount of messages comming in, administrator should split and +%% balance topics between worker/connections manually. +%% +%% NOTES: +%% * Local messages are all normalised to QoS-1 when exporting to remote + +-module(emqx_bridge_worker). +-behaviour(gen_statem). + +%% APIs +-export([ start_link/1 + , start_link/2 + , register_metrics/0 + , stop/1 + ]). + +%% gen_statem callbacks +-export([ terminate/3 + , code_change/4 + , init/1 + , callback_mode/0 + ]). + +%% state functions +-export([ idle/3 + , connected/3 + ]). + +%% management APIs +-export([ ensure_started/1 + , ensure_stopped/1 + , ensure_stopped/2 + , status/1 + ]). + +-export([ get_forwards/1 + , ensure_forward_present/2 + , ensure_forward_absent/2 + ]). + +-export([ get_subscriptions/1 + , ensure_subscription_present/3 + , ensure_subscription_absent/2 + ]). + +-export_type([ config/0 + , batch/0 + , ack_ref/0 + ]). + +-type id() :: atom() | string() | pid(). +-type qos() :: emqx_mqtt_types:qos(). +-type config() :: map(). +-type batch() :: [emqx_bridge_msg:exp_msg()]. +-type ack_ref() :: term(). +-type topic() :: emqx_topic:topic(). + +-include_lib("emqx/include/logger.hrl"). +-include_lib("emqx/include/emqx_mqtt.hrl"). + +-logger_header("[Bridge]"). + +%% same as default in-flight limit for emqtt +-define(DEFAULT_BATCH_SIZE, 32). +-define(DEFAULT_RECONNECT_DELAY_MS, timer:seconds(5)). +-define(DEFAULT_SEG_BYTES, (1 bsl 20)). +-define(DEFAULT_MAX_TOTAL_SIZE, (1 bsl 31)). +-define(NO_BRIDGE_HANDLER, undefined). + +%% @doc Start a bridge worker. Supported configs: +%% start_type: 'manual' (default) or 'auto', when manual, bridge will stay +%% at 'idle' state until a manual call to start it. +%% connect_module: The module which implements emqx_bridge_connect behaviour +%% and work as message batch transport layer +%% reconnect_delay_ms: Delay in milli-seconds for the bridge worker to retry +%% in case of transportation failure. +%% max_inflight_batches: Max number of batches allowed to send-ahead before +%% receiving confirmation from remote node/cluster +%% mountpoint: The topic mount point for messages sent to remote node/cluster +%% `undefined', `<<>>' or `""' to disable +%% forwards: Local topics to subscribe. +%% queue.batch_bytes_limit: Max number of bytes to collect in a batch for each +%% send call towards emqx_bridge_connect +%% queue.batch_count_limit: Max number of messages to collect in a batch for +%% each send call towards emqx_bridge_connect +%% queue.replayq_dir: Directory where replayq should persist messages +%% queue.replayq_seg_bytes: Size in bytes for each replayq segment file +%% +%% Find more connection specific configs in the callback modules +%% of emqx_bridge_connect behaviour. +start_link(Config) when is_list(Config) -> + start_link(maps:from_list(Config)); +start_link(Config) -> + gen_statem:start_link(?MODULE, Config, []). + +start_link(Name, Config) when is_list(Config) -> + start_link(Name, maps:from_list(Config)); +start_link(Name, Config) -> + Name1 = name(Name), + gen_statem:start_link({local, Name1}, ?MODULE, Config#{name => Name1}, []). + +ensure_started(Name) -> + gen_statem:call(name(Name), ensure_started). + +%% @doc Manually stop bridge worker. State idempotency ensured. +ensure_stopped(Id) -> + ensure_stopped(Id, 1000). + +ensure_stopped(Id, Timeout) -> + Pid = case id(Id) of + P when is_pid(P) -> P; + N -> whereis(N) + end, + case Pid of + undefined -> + ok; + _ -> + MRef = monitor(process, Pid), + unlink(Pid), + _ = gen_statem:call(id(Id), ensure_stopped, Timeout), + receive + {'DOWN', MRef, _, _, _} -> + ok + after + Timeout -> + exit(Pid, kill) + end + end. + +stop(Pid) -> gen_statem:stop(Pid). + +status(Pid) when is_pid(Pid) -> + gen_statem:call(Pid, status); +status(Id) -> + gen_statem:call(name(Id), status). + +%% @doc Return all forwards (local subscriptions). +-spec get_forwards(id()) -> [topic()]. +get_forwards(Id) -> gen_statem:call(id(Id), get_forwards, timer:seconds(1000)). + +%% @doc Return all subscriptions (subscription over mqtt connection to remote broker). +-spec get_subscriptions(id()) -> [{emqx_topic:topic(), qos()}]. +get_subscriptions(Id) -> gen_statem:call(id(Id), get_subscriptions). + +%% @doc Add a new forward (local topic subscription). +-spec ensure_forward_present(id(), topic()) -> ok. +ensure_forward_present(Id, Topic) -> + gen_statem:call(id(Id), {ensure_present, forwards, topic(Topic)}). + +%% @doc Ensure a forward topic is deleted. +-spec ensure_forward_absent(id(), topic()) -> ok. +ensure_forward_absent(Id, Topic) -> + gen_statem:call(id(Id), {ensure_absent, forwards, topic(Topic)}). + +%% @doc Ensure subscribed to remote topic. +%% NOTE: only applicable when connection module is emqx_bridge_mqtt +%% return `{error, no_remote_subscription_support}' otherwise. +-spec ensure_subscription_present(id(), topic(), qos()) -> ok | {error, any()}. +ensure_subscription_present(Id, Topic, QoS) -> + gen_statem:call(id(Id), {ensure_present, subscriptions, {topic(Topic), QoS}}). + +%% @doc Ensure unsubscribed from remote topic. +%% NOTE: only applicable when connection module is emqx_bridge_mqtt +-spec ensure_subscription_absent(id(), topic()) -> ok. +ensure_subscription_absent(Id, Topic) -> + gen_statem:call(id(Id), {ensure_absent, subscriptions, topic(Topic)}). + +callback_mode() -> [state_functions]. + +%% @doc Config should be a map(). +init(Config) -> + erlang:process_flag(trap_exit, true), + ConnectModule = maps:get(connect_module, Config), + Subscriptions = maps:get(subscriptions, Config, []), + Forwards = maps:get(forwards, Config, []), + Queue = open_replayq(Config), + State = init_opts(Config), + Topics = [iolist_to_binary(T) || T <- Forwards], + Subs = check_subscriptions(Subscriptions), + ConnectConfig = get_conn_cfg(Config), + ConnectFun = fun(SubsX) -> + emqx_bridge_connect:start(ConnectModule, ConnectConfig#{subscriptions => SubsX}) + end, + self() ! idle, + {ok, idle, State#{connect_module => ConnectModule, + connect_fun => ConnectFun, + forwards => Topics, + subscriptions => Subs, + replayq => Queue + }}. + +init_opts(Config) -> + IfRecordMetrics = maps:get(if_record_metrics, Config, true), + ReconnDelayMs = maps:get(reconnect_delay_ms, Config, ?DEFAULT_RECONNECT_DELAY_MS), + StartType = maps:get(start_type, Config, manual), + BridgeHandler = maps:get(bridge_handler, Config, ?NO_BRIDGE_HANDLER), + Mountpoint = maps:get(forward_mountpoint, Config, undefined), + ReceiveMountpoint = maps:get(receive_mountpoint, Config, undefined), + MaxInflightSize = maps:get(max_inflight_batches, Config, ?DEFAULT_BATCH_SIZE), + BatchSize = maps:get(batch_size, Config, ?DEFAULT_BATCH_SIZE), + Name = maps:get(name, Config, undefined), + #{start_type => StartType, + reconnect_delay_ms => ReconnDelayMs, + batch_size => BatchSize, + mountpoint => format_mountpoint(Mountpoint), + receive_mountpoint => ReceiveMountpoint, + inflight => [], + max_inflight => MaxInflightSize, + connection => undefined, + bridge_handler => BridgeHandler, + if_record_metrics => IfRecordMetrics, + name => Name}. + +open_replayq(Config) -> + QCfg = maps:get(queue, Config, #{}), + Dir = maps:get(replayq_dir, QCfg, undefined), + SegBytes = maps:get(replayq_seg_bytes, QCfg, ?DEFAULT_SEG_BYTES), + MaxTotalSize = maps:get(max_total_size, QCfg, ?DEFAULT_MAX_TOTAL_SIZE), + QueueConfig = case Dir =:= undefined orelse Dir =:= "" of + true -> #{mem_only => true}; + false -> #{dir => Dir, seg_bytes => SegBytes, max_total_size => MaxTotalSize} + end, + replayq:open(QueueConfig#{sizer => fun emqx_bridge_msg:estimate_size/1, + marshaller => fun msg_marshaller/1}). + +check_subscriptions(Subscriptions) -> + lists:map(fun({Topic, QoS}) -> + Topic1 = iolist_to_binary(Topic), + true = emqx_topic:validate({filter, Topic1}), + {Topic1, QoS} + end, Subscriptions). + +get_conn_cfg(Config) -> + maps:without([connect_module, + queue, + reconnect_delay_ms, + forwards, + mountpoint, + name + ], Config). + +code_change(_Vsn, State, Data, _Extra) -> + {ok, State, Data}. + +terminate(_Reason, _StateName, #{replayq := Q} = State) -> + _ = disconnect(State), + _ = replayq:close(Q), + ok. + +%% ensure_started will be deprecated in the future +idle({call, From}, ensure_started, State) -> + case do_connect(State) of + {ok, State1} -> + {next_state, connected, State1, [{reply, From, ok}, {state_timeout, 0, connected}]}; + {error, Reason} -> + {keep_state_and_data, [{reply, From, {error, Reason}}]} + end; +%% @doc Standing by for manual start. +idle(info, idle, #{start_type := manual}) -> + keep_state_and_data; +%% @doc Standing by for auto start. +idle(info, idle, #{start_type := auto} = State) -> + connecting(State); +idle(state_timeout, reconnect, State) -> + connecting(State); + +idle(info, {batch_ack, Ref}, State) -> + case do_ack(State, Ref) of + {ok, NewState} -> + {keep_state, NewState}; + _ -> + keep_state_and_data + end; + +idle(Type, Content, State) -> + common(idle, Type, Content, State). + +connecting(#{reconnect_delay_ms := ReconnectDelayMs} = State) -> + case do_connect(State) of + {ok, State1} -> + {next_state, connected, State1, {state_timeout, 0, connected}}; + _ -> + {keep_state_and_data, {state_timeout, ReconnectDelayMs, reconnect}} + end. + +connected(state_timeout, connected, #{inflight := Inflight} = State) -> + case retry_inflight(State, Inflight) of + {ok, NewState} -> + {keep_state, NewState, {next_event, internal, maybe_send}}; + {error, NewState} -> + {keep_state, NewState} + end; +connected(internal, maybe_send, State) -> + {_, NewState} = pop_and_send(State), + {keep_state, NewState}; + +connected(info, {disconnected, Conn, Reason}, + #{connection := Connection, name := Name, reconnect_delay_ms := ReconnectDelayMs} = State) -> + case Conn =:= maps:get(client_pid, Connection, undefined) of + true -> + ?LOG(info, "Bridge ~p diconnected~nreason=~p", [Name, Reason]), + {next_state, idle, State#{connection => undefined}, {state_timeout, ReconnectDelayMs, reconnect}}; + false -> + keep_state_and_data + end; +connected(info, {batch_ack, Ref}, State) -> + case do_ack(State, Ref) of + {ok, NewState} -> + {keep_state, NewState, {next_event, internal, maybe_send}}; + _ -> + keep_state_and_data + end; +connected(Type, Content, State) -> + common(connected, Type, Content, State). + +%% Common handlers +common(StateName, {call, From}, status, _State) -> + {keep_state_and_data, [{reply, From, StateName}]}; +common(_StateName, {call, From}, ensure_started, _State) -> + {keep_state_and_data, [{reply, From, connected}]}; +common(_StateName, {call, From}, ensure_stopped, _State) -> + {stop_and_reply, {shutdown, manual}, [{reply, From, ok}]}; +common(_StateName, {call, From}, get_forwards, #{forwards := Forwards}) -> + {keep_state_and_data, [{reply, From, Forwards}]}; +common(_StateName, {call, From}, get_subscriptions, #{subscriptions := Subs}) -> + {keep_state_and_data, [{reply, From, Subs}]}; +common(_StateName, {call, From}, {ensure_present, What, Topic}, State) -> + {Result, NewState} = ensure_present(What, Topic, State), + {keep_state, NewState, [{reply, From, Result}]}; +common(_StateName, {call, From}, {ensure_absent, What, Topic}, State) -> + {Result, NewState} = ensure_absent(What, Topic, State), + {keep_state, NewState, [{reply, From, Result}]}; +common(_StateName, info, {deliver, _, Msg}, #{replayq := Q, if_record_metrics := IfRecordMetric} = State) -> + bridges_metrics_inc(IfRecordMetric, 'bridge.mqtt.message_received'), + NewQ = replayq:append(Q, collect([Msg])), + {keep_state, State#{replayq => NewQ}, {next_event, internal, maybe_send}}; +common(_StateName, info, {'EXIT', _, _}, State) -> + {keep_state, State}; +common(StateName, Type, Content, #{name := Name} = State) -> + ?LOG(notice, "Bridge ~p discarded ~p type event at state ~p:~p", + [Name, Type, StateName, Content]), + {keep_state, State}. + +eval_bridge_handler(State = #{bridge_handler := ?NO_BRIDGE_HANDLER}, _Msg) -> + State; +eval_bridge_handler(State = #{bridge_handler := Handler}, Msg) -> + Handler(Msg), + State. + +ensure_present(Key, Topic, State) -> + Topics = maps:get(Key, State), + case is_topic_present(Topic, Topics) of + true -> + {ok, State}; + false -> + R = do_ensure_present(Key, Topic, State), + {R, State#{Key := lists:usort([Topic | Topics])}} + end. + +ensure_absent(Key, Topic, State) -> + Topics = maps:get(Key, State), + case is_topic_present(Topic, Topics) of + true -> + R = do_ensure_absent(Key, Topic, State), + {R, State#{Key := ensure_topic_absent(Topic, Topics)}}; + false -> + {ok, State} + end. + +ensure_topic_absent(_Topic, []) -> []; +ensure_topic_absent(Topic, [{_, _} | _] = L) -> lists:keydelete(Topic, 1, L); +ensure_topic_absent(Topic, L) -> lists:delete(Topic, L). + +is_topic_present({Topic, _QoS}, Topics) -> + is_topic_present(Topic, Topics); +is_topic_present(Topic, Topics) -> + lists:member(Topic, Topics) orelse false =/= lists:keyfind(Topic, 1, Topics). + +do_connect(#{forwards := Forwards, + subscriptions := Subs, + connect_fun := ConnectFun, + name := Name} = State) -> + ok = subscribe_local_topics(Forwards, Name), + case ConnectFun(Subs) of + {ok, Conn} -> + ?LOG(info, "Bridge ~p is connecting......", [Name]), + {ok, eval_bridge_handler(State#{connection => Conn}, connected)}; + {error, Reason} -> + {error, Reason, State} + end. + +do_ensure_present(forwards, Topic, #{name := Name}) -> + subscribe_local_topic(Topic, Name); +do_ensure_present(subscriptions, _Topic, #{connection := undefined}) -> + {error, no_connection}; +do_ensure_present(subscriptions, _Topic, #{connect_module := emqx_bridge_rpc}) -> + {error, no_remote_subscription_support}; +do_ensure_present(subscriptions, {Topic, QoS}, #{connect_module := ConnectModule, + connection := Conn}) -> + ConnectModule:ensure_subscribed(Conn, Topic, QoS). + +do_ensure_absent(forwards, Topic, _) -> + do_unsubscribe(Topic); +do_ensure_absent(subscriptions, _Topic, #{connection := undefined}) -> + {error, no_connection}; +do_ensure_absent(subscriptions, _Topic, #{connect_module := emqx_bridge_rpc}) -> + {error, no_remote_subscription_support}; +do_ensure_absent(subscriptions, Topic, #{connect_module := ConnectModule, + connection := Conn}) -> + ConnectModule:ensure_unsubscribed(Conn, Topic). + +collect(Acc) -> + receive + {deliver, _, Msg} -> + collect([Msg | Acc]) + after + 0 -> + lists:reverse(Acc) + end. + +%% Retry all inflight (previously sent but not acked) batches. +retry_inflight(State, []) -> {ok, State}; +retry_inflight(State, [#{q_ack_ref := QAckRef, batch := Batch} | Inflight]) -> + case do_send(State#{inflight := Inflight}, QAckRef, Batch) of + {ok, State1} -> retry_inflight(State1, Inflight); + {error, State1} -> {error, State1} + end. + +pop_and_send(#{inflight := Inflight, max_inflight := Max } = State) when length(Inflight) >= Max -> + {ok, State}; +pop_and_send(#{replayq := Q, connect_module := Module} = State) -> + case replayq:is_empty(Q) of + true -> + {ok, State}; + false -> + BatchSize = case Module of + emqx_bridge_rpc -> maps:get(batch_size, State); + _ -> 1 + end, + Opts = #{count_limit => BatchSize, bytes_limit => 999999999}, + {Q1, QAckRef, Batch} = replayq:pop(Q, Opts), + do_send(State#{replayq := Q1}, QAckRef, Batch) + end. + +%% Assert non-empty batch because we have a is_empty check earlier. +do_send(#{inflight := Inflight, + connect_module := Module, + connection := Connection, + mountpoint := Mountpoint, + if_record_metrics := IfRecordMetrics} = State, QAckRef, Batch) -> + ExportMsg = fun(Message) -> + bridges_metrics_inc(IfRecordMetrics, 'bridge.mqtt.message_sent'), + emqx_bridge_msg:to_export(Module, Mountpoint, Message) + end, + case Module:send(Connection, [ExportMsg(M) || M <- Batch]) of + {ok, Ref} -> + {ok, State#{inflight := Inflight ++ [#{q_ack_ref => QAckRef, + send_ack_ref => Ref, + batch => Batch}]}}; + {error, Reason} -> + ?LOG(info, "Batch produce failed~p", [Reason]), + {error, State} + end. + + +do_ack(#{inflight := []} = State, Ref) -> + ?LOG(error, "Can't be found from the inflight:~p", [Ref]), + {ok, State}; + +do_ack(#{inflight := [#{send_ack_ref := Ref, + q_ack_ref := QAckRef}| Rest], replayq := Q} = State, Ref) -> + ok = replayq:ack(Q, QAckRef), + {ok, State#{inflight => Rest}}; + +do_ack(#{inflight := [#{q_ack_ref := QAckRef, + batch := Batch}| Rest], replayq := Q} = State, Ref) -> + ok = replayq:ack(Q, QAckRef), + NewQ = replayq:append(Q, Batch), + do_ack(State#{replayq => NewQ, inflight => Rest}, Ref). + +subscribe_local_topics(Topics, Name) -> + lists:foreach(fun(Topic) -> subscribe_local_topic(Topic, Name) end, Topics). + +subscribe_local_topic(Topic, Name) -> + do_subscribe(Topic, Name). + +topic(T) -> iolist_to_binary(T). + +validate(RawTopic) -> + Topic = topic(RawTopic), + try emqx_topic:validate(Topic) of + _Success -> Topic + catch + error:Reason -> + error({bad_topic, Topic, Reason}) + end. + +do_subscribe(RawTopic, Name) -> + TopicFilter = validate(RawTopic), + {Topic, SubOpts} = emqx_topic:parse(TopicFilter, #{qos => ?QOS_1}), + emqx_broker:subscribe(Topic, Name, SubOpts). + +do_unsubscribe(RawTopic) -> + TopicFilter = validate(RawTopic), + {Topic, _SubOpts} = emqx_topic:parse(TopicFilter), + emqx_broker:unsubscribe(Topic). + +disconnect(#{connection := Conn, + connect_module := Module + } = State) when Conn =/= undefined -> + Module:stop(Conn), + State0 = State#{connection => undefined}, + eval_bridge_handler(State0, disconnected); +disconnect(State) -> + eval_bridge_handler(State, disconnected). + +%% Called only when replayq needs to dump it to disk. +msg_marshaller(Bin) when is_binary(Bin) -> emqx_bridge_msg:from_binary(Bin); +msg_marshaller(Msg) -> emqx_bridge_msg:to_binary(Msg). + +format_mountpoint(undefined) -> + undefined; +format_mountpoint(Prefix) -> + binary:replace(iolist_to_binary(Prefix), <<"${node}">>, atom_to_binary(node(), utf8)). + +name(Id) -> list_to_atom(lists:concat([?MODULE, "_", Id])). + +id(Pid) when is_pid(Pid) -> Pid; +id(Name) -> name(Name). + +register_metrics() -> + lists:foreach(fun emqx_metrics:ensure/1, + ['bridge.mqtt.message_sent', + 'bridge.mqtt.message_received' + ]). + +bridges_metrics_inc(true, Metric) -> + emqx_metrics:inc(Metric); +bridges_metrics_inc(_IsRecordMetric, _Metric) -> + ok. diff --git a/apps/emqx_bridge_mqtt/test/emqx_bridge_mqtt_tests.erl b/apps/emqx_bridge_mqtt/test/emqx_bridge_mqtt_tests.erl new file mode 100644 index 000000000..392fa8e17 --- /dev/null +++ b/apps/emqx_bridge_mqtt/test/emqx_bridge_mqtt_tests.erl @@ -0,0 +1,47 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_bridge_mqtt_tests). + +-include_lib("eunit/include/eunit.hrl"). +-include_lib("emqx/include/emqx_mqtt.hrl"). + +send_and_ack_test() -> + %% delegate from gen_rpc to rpc for unit test + meck:new(emqtt, [passthrough, no_history]), + meck:expect(emqtt, start_link, 1, + fun(_) -> + {ok, spawn_link(fun() -> ok end)} + end), + meck:expect(emqtt, connect, 1, {ok, dummy}), + meck:expect(emqtt, stop, 1, + fun(Pid) -> Pid ! stop end), + meck:expect(emqtt, publish, 2, + fun(Client, Msg) -> + Client ! {publish, Msg}, + {ok, Msg} %% as packet id + end), + try + Max = 1, + Batch = lists:seq(1, Max), + {ok, Conn} = emqx_bridge_mqtt:start(#{address => "127.0.0.1:1883"}), + % %% return last packet id as batch reference + {ok, _AckRef} = emqx_bridge_mqtt:send(Conn, Batch), + + ok = emqx_bridge_mqtt:stop(Conn) + after + meck:unload(emqtt) + end. \ No newline at end of file diff --git a/apps/emqx_bridge_mqtt/test/emqx_bridge_rpc_tests.erl b/apps/emqx_bridge_mqtt/test/emqx_bridge_rpc_tests.erl new file mode 100644 index 000000000..f79d12dfb --- /dev/null +++ b/apps/emqx_bridge_mqtt/test/emqx_bridge_rpc_tests.erl @@ -0,0 +1,42 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_bridge_rpc_tests). +-include_lib("eunit/include/eunit.hrl"). + +send_and_ack_test() -> + %% delegate from emqx_rpc to rpc for unit test + meck:new(emqx_rpc, [passthrough, no_history]), + meck:expect(emqx_rpc, call, 4, + fun(Node, Module, Fun, Args) -> + rpc:call(Node, Module, Fun, Args) + end), + meck:expect(emqx_rpc, cast, 4, + fun(Node, Module, Fun, Args) -> + rpc:cast(Node, Module, Fun, Args) + end), + meck:new(emqx_bridge_worker, [passthrough, no_history]), + try + {ok, #{client_pid := Pid, address := Node}} = emqx_bridge_rpc:start(#{address => node()}), + {ok, Ref} = emqx_bridge_rpc:send(#{address => Node}, []), + receive + {batch_ack, Ref} -> + ok + end, + ok = emqx_bridge_rpc:stop( #{client_pid => Pid}) + after + meck:unload(emqx_rpc) + end. diff --git a/apps/emqx_bridge_mqtt/test/emqx_bridge_worker_SUITE.erl b/apps/emqx_bridge_mqtt/test/emqx_bridge_worker_SUITE.erl new file mode 100644 index 000000000..7cbf0987c --- /dev/null +++ b/apps/emqx_bridge_mqtt/test/emqx_bridge_worker_SUITE.erl @@ -0,0 +1,176 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_bridge_worker_SUITE). + +-export([ all/0 + , init_per_suite/1 + , end_per_suite/1]). +-export([ t_rpc/1 + , t_mqtt/1 + , t_mngr/1]). + +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). +-include_lib("emqx/include/emqx_mqtt.hrl"). +-include_lib("emqx/include/emqx.hrl"). + +-define(wait(For, Timeout), emqx_ct_helpers:wait_for(?FUNCTION_NAME, ?LINE, fun() -> For end, Timeout)). + +receive_messages(Count) -> + receive_messages(Count, []). + +receive_messages(0, Msgs) -> + Msgs; +receive_messages(Count, Msgs) -> + receive + {publish, Msg} -> + receive_messages(Count-1, [Msg|Msgs]); + _Other -> + receive_messages(Count, Msgs) + after 1000 -> + Msgs + end. + +all() -> [ t_rpc + , t_mqtt + , t_mngr + ]. + +init_per_suite(Config) -> + case node() of + nonode@nohost -> net_kernel:start(['emqx@127.0.0.1', longnames]); + _ -> ok + end, + ok = application:set_env(gen_rpc, tcp_client_num, 1), + emqx_ct_helpers:start_apps([emqx_bridge_mqtt]), + emqx_logger:set_log_level(error), + [{log_level, error} | Config]. + +end_per_suite(_Config) -> + emqx_ct_helpers:stop_apps([emqx_bridge_mqtt]). + +t_mngr(Config) when is_list(Config) -> + Subs = [{<<"a">>, 1}, {<<"b">>, 2}], + Cfg = #{address => node(), + forwards => [<<"mngr">>], + connect_module => emqx_bridge_rpc, + mountpoint => <<"forwarded">>, + subscriptions => Subs, + start_type => auto}, + Name = ?FUNCTION_NAME, + {ok, Pid} = emqx_bridge_worker:start_link(Name, Cfg), + try + ?assertEqual([<<"mngr">>], emqx_bridge_worker:get_forwards(Name)), + ?assertEqual(ok, emqx_bridge_worker:ensure_forward_present(Name, "mngr")), + ?assertEqual(ok, emqx_bridge_worker:ensure_forward_present(Name, "mngr2")), + ?assertEqual([<<"mngr">>, <<"mngr2">>], emqx_bridge_worker:get_forwards(Pid)), + ?assertEqual(ok, emqx_bridge_worker:ensure_forward_absent(Name, "mngr2")), + ?assertEqual(ok, emqx_bridge_worker:ensure_forward_absent(Name, "mngr3")), + ?assertEqual([<<"mngr">>], emqx_bridge_worker:get_forwards(Pid)), + ?assertEqual({error, no_remote_subscription_support}, + emqx_bridge_worker:ensure_subscription_present(Pid, <<"t">>, 0)), + ?assertEqual({error, no_remote_subscription_support}, + emqx_bridge_worker:ensure_subscription_absent(Pid, <<"t">>)), + ?assertEqual(Subs, emqx_bridge_worker:get_subscriptions(Pid)) + after + ok = emqx_bridge_worker:stop(Pid) + end. + +%% A loopback RPC to local node +t_rpc(Config) when is_list(Config) -> + Cfg = #{address => node(), + forwards => [<<"t_rpc/#">>], + connect_module => emqx_bridge_rpc, + forward_mountpoint => <<"forwarded">>, + start_type => auto}, + {ok, Pid} = emqx_bridge_worker:start_link(?FUNCTION_NAME, Cfg), + ClientId = <<"ClientId">>, + try + {ok, ConnPid} = emqtt:start_link([{clientid, ClientId}]), + {ok, _Props} = emqtt:connect(ConnPid), + {ok, _Props, [1]} = emqtt:subscribe(ConnPid, {<<"forwarded/t_rpc/one">>, ?QOS_1}), + timer:sleep(100), + {ok, _PacketId} = emqtt:publish(ConnPid, <<"t_rpc/one">>, <<"hello">>, ?QOS_1), + timer:sleep(100), + ?assertEqual(1, length(receive_messages(1))), + emqtt:disconnect(ConnPid) + after + ok = emqx_bridge_worker:stop(Pid) + end. + +%% Full data loopback flow explained: +%% mqtt-client ----> local-broker ---(local-subscription)---> +%% bridge(export) --- (mqtt-connection)--> local-broker ---(remote-subscription) --> +%% bridge(import) --> mqtt-client +t_mqtt(Config) when is_list(Config) -> + SendToTopic = <<"t_mqtt/one">>, + SendToTopic2 = <<"t_mqtt/two">>, + SendToTopic3 = <<"t_mqtt/three">>, + Mountpoint = <<"forwarded/${node}/">>, + Cfg = #{address => "127.0.0.1:1883", + forwards => [SendToTopic], + connect_module => emqx_bridge_mqtt, + forward_mountpoint => Mountpoint, + username => "user", + clean_start => true, + clientid => "bridge_aws", + keepalive => 60000, + password => "passwd", + proto_ver => mqttv4, + queue => #{replayq_dir => "data/t_mqtt/", + replayq_seg_bytes => 10000, + batch_bytes_limit => 1000, + batch_count_limit => 10 + }, + reconnect_delay_ms => 1000, + ssl => false, + %% Consume back to forwarded message for verification + %% NOTE: this is a indefenite loopback without mocking emqx_bridge_worker:import_batch/1 + subscriptions => [{SendToTopic2, _QoS = 1}], + receive_mountpoint => <<"receive/aws/">>, + start_type => auto}, + {ok, Pid} = emqx_bridge_worker:start_link(?FUNCTION_NAME, Cfg), + ClientId = <<"client-1">>, + try + ?assertEqual([{SendToTopic2, 1}], emqx_bridge_worker:get_subscriptions(Pid)), + ok = emqx_bridge_worker:ensure_subscription_present(Pid, SendToTopic3, _QoS = 1), + ?assertEqual([{SendToTopic3, 1},{SendToTopic2, 1}], + emqx_bridge_worker:get_subscriptions(Pid)), + {ok, ConnPid} = emqtt:start_link([{clientid, ClientId}]), + {ok, _Props} = emqtt:connect(ConnPid), + emqtt:subscribe(ConnPid, <<"forwarded/+/t_mqtt/one">>, 1), + %% message from a different client, to avoid getting terminated by no-local + Max = 10, + Msgs = lists:seq(1, Max), + lists:foreach(fun(I) -> + {ok, _PacketId} = emqtt:publish(ConnPid, SendToTopic, integer_to_binary(I), ?QOS_1) + end, Msgs), + ?assertEqual(10, length(receive_messages(200))), + + emqtt:subscribe(ConnPid, <<"receive/aws/t_mqtt/two">>, 1), + %% message from a different client, to avoid getting terminated by no-local + Max = 10, + Msgs = lists:seq(1, Max), + lists:foreach(fun(I) -> + {ok, _PacketId} = emqtt:publish(ConnPid, SendToTopic2, integer_to_binary(I), ?QOS_1) + end, Msgs), + ?assertEqual(10, length(receive_messages(200))), + + emqtt:disconnect(ConnPid) + after + ok = emqx_bridge_worker:stop(Pid) + end. diff --git a/apps/emqx_bridge_mqtt/test/emqx_bridge_worker_tests.erl b/apps/emqx_bridge_mqtt/test/emqx_bridge_worker_tests.erl new file mode 100644 index 000000000..1d2ed1b82 --- /dev/null +++ b/apps/emqx_bridge_mqtt/test/emqx_bridge_worker_tests.erl @@ -0,0 +1,172 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_bridge_worker_tests). +-behaviour(emqx_bridge_connect). + +-include_lib("eunit/include/eunit.hrl"). +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/emqx_mqtt.hrl"). + +-define(BRIDGE_NAME, test). +-define(BRIDGE_REG_NAME, emqx_bridge_worker_test). +-define(WAIT(PATTERN, TIMEOUT), + receive + PATTERN -> + ok + after + TIMEOUT -> + error(timeout) + end). + +%% stub callbacks +-export([start/1, send/2, stop/1]). + +start(#{connect_result := Result, test_pid := Pid, test_ref := Ref}) -> + case is_pid(Pid) of + true -> Pid ! {connection_start_attempt, Ref}; + false -> ok + end, + Result. + +send(SendFun, Batch) when is_function(SendFun, 2) -> + SendFun(Batch). + +stop(_Pid) -> ok. + +%% bridge worker should retry connecting remote node indefinitely +% reconnect_test() -> +% emqx_metrics:start_link(), +% emqx_bridge_worker:register_metrics(), +% Ref = make_ref(), +% Config = make_config(Ref, self(), {error, test}), +% {ok, Pid} = emqx_bridge_worker:start_link(?BRIDGE_NAME, Config), +% %% assert name registered +% ?assertEqual(Pid, whereis(?BRIDGE_REG_NAME)), +% ?WAIT({connection_start_attempt, Ref}, 1000), +% %% expect same message again +% ?WAIT({connection_start_attempt, Ref}, 1000), +% ok = emqx_bridge_worker:stop(?BRIDGE_REG_NAME), +% emqx_metrics:stop(), +% ok. + +%% connect first, disconnect, then connect again +disturbance_test() -> + emqx_metrics:start_link(), + emqx_bridge_worker:register_metrics(), + Ref = make_ref(), + TestPid = self(), + Config = make_config(Ref, TestPid, {ok, #{client_pid => TestPid}}), + {ok, Pid} = emqx_bridge_worker:start_link(?BRIDGE_NAME, Config), + ?assertEqual(Pid, whereis(?BRIDGE_REG_NAME)), + ?WAIT({connection_start_attempt, Ref}, 1000), + Pid ! {disconnected, TestPid, test}, + ?WAIT({connection_start_attempt, Ref}, 1000), + emqx_metrics:stop(), + ok = emqx_bridge_worker:stop(?BRIDGE_REG_NAME). + +% % %% buffer should continue taking in messages when disconnected +% buffer_when_disconnected_test_() -> +% {timeout, 10000, fun test_buffer_when_disconnected/0}. + +% test_buffer_when_disconnected() -> +% Ref = make_ref(), +% Nums = lists:seq(1, 100), +% Sender = spawn_link(fun() -> receive {bridge, Pid} -> sender_loop(Pid, Nums, _Interval = 5) end end), +% SenderMref = monitor(process, Sender), +% Receiver = spawn_link(fun() -> receive {bridge, Pid} -> receiver_loop(Pid, Nums, _Interval = 1) end end), +% ReceiverMref = monitor(process, Receiver), +% SendFun = fun(Batch) -> +% BatchRef = make_ref(), +% Receiver ! {batch, BatchRef, Batch}, +% {ok, BatchRef} +% end, +% Config0 = make_config(Ref, false, {ok, #{client_pid => undefined}}), +% Config = Config0#{reconnect_delay_ms => 100}, +% emqx_metrics:start_link(), +% emqx_bridge_worker:register_metrics(), +% {ok, Pid} = emqx_bridge_worker:start_link(?BRIDGE_NAME, Config), +% Sender ! {bridge, Pid}, +% Receiver ! {bridge, Pid}, +% ?assertEqual(Pid, whereis(?BRIDGE_REG_NAME)), +% Pid ! {disconnected, Ref, test}, +% ?WAIT({'DOWN', SenderMref, process, Sender, normal}, 5000), +% ?WAIT({'DOWN', ReceiverMref, process, Receiver, normal}, 1000), +% ok = emqx_bridge_worker:stop(?BRIDGE_REG_NAME), +% emqx_metrics:stop(). + +manual_start_stop_test() -> + emqx_metrics:start_link(), + emqx_bridge_worker:register_metrics(), + Ref = make_ref(), + TestPid = self(), + Config0 = make_config(Ref, TestPid, {ok, #{client_pid => TestPid}}), + Config = Config0#{start_type := manual}, + {ok, Pid} = emqx_bridge_worker:start_link(?BRIDGE_NAME, Config), + %% call ensure_started again should yeld the same result + ok = emqx_bridge_worker:ensure_started(?BRIDGE_NAME), + ?assertEqual(Pid, whereis(?BRIDGE_REG_NAME)), + emqx_bridge_worker:ensure_stopped(unknown), + emqx_bridge_worker:ensure_stopped(Pid), + emqx_bridge_worker:ensure_stopped(?BRIDGE_REG_NAME), + emqx_metrics:stop(). + +%% Feed messages to bridge +sender_loop(_Pid, [], _) -> exit(normal); +sender_loop(Pid, [Num | Rest], Interval) -> + random_sleep(Interval), + Pid ! {deliver, dummy, make_msg(Num)}, + sender_loop(Pid, Rest, Interval). + +%% Feed acknowledgments to bridge +receiver_loop(_Pid, [], _) -> ok; +receiver_loop(Pid, Nums, Interval) -> + receive + {batch, BatchRef, Batch} -> + Rest = match_nums(Batch, Nums), + random_sleep(Interval), + emqx_bridge_worker:handle_ack(Pid, BatchRef), + receiver_loop(Pid, Rest, Interval) + end. + +random_sleep(MaxInterval) -> + case rand:uniform(MaxInterval) - 1 of + 0 -> ok; + T -> timer:sleep(T) + end. + +match_nums([], Rest) -> Rest; +match_nums([#message{payload = P} | Rest], Nums) -> + I = binary_to_integer(P), + case Nums of + [I | NumsLeft] -> match_nums(Rest, NumsLeft); + [J | _] when J > I -> match_nums(Rest, Nums); %% allow retry + _ -> error([{received, I}, {expecting, Nums}]) + end. + +make_config(Ref, TestPid, Result) -> + #{test_pid => TestPid, + test_ref => Ref, + connect_module => ?MODULE, + reconnect_delay_ms => 50, + connect_result => Result, + start_type => auto + }. + +make_msg(I) -> + Payload = integer_to_binary(I), + emqx_message:make(<<"test/topic">>, Payload). + diff --git a/apps/emqx_coap/.github/workflows/run_test_cases.yaml b/apps/emqx_coap/.github/workflows/run_test_cases.yaml new file mode 100644 index 000000000..b8e722570 --- /dev/null +++ b/apps/emqx_coap/.github/workflows/run_test_cases.yaml @@ -0,0 +1,29 @@ +name: Run test cases + +on: [push, pull_request] + +jobs: + run_test_cases: + runs-on: ubuntu-latest + + container: + image: erlang:22.1 + + steps: + - uses: actions/checkout@v1 + - name: run test cases + run: | + make xref + make eunit + make ct + make cover + - uses: actions/upload-artifact@v1 + if: always() + with: + name: logs + path: _build/test/logs + - uses: actions/upload-artifact@v1 + with: + name: cover + path: _build/test/cover + diff --git a/apps/emqx_coap/.gitignore b/apps/emqx_coap/.gitignore new file mode 100644 index 000000000..67eaa0145 --- /dev/null +++ b/apps/emqx_coap/.gitignore @@ -0,0 +1,25 @@ +deps/ +ebin/ +_rel/ +.erlang.mk/ +*.d +*.o +*.exe +data/ +*.iml +.idea/ +logs/ +*.beam +emqx_coap.d +intergration_test/emqx-rel/ +intergration_test/libcoap/ +intergration_test/case*.txt +.DS_Store +_build/ +rebar.lock +rebar3.crashdump +*.swp +erlang.mk +.rebar3/ +etc/emqx_coap.conf.rendered +.tags* diff --git a/apps/emqx_coap/LICENSE b/apps/emqx_coap/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/apps/emqx_coap/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/apps/emqx_coap/README.md b/apps/emqx_coap/README.md new file mode 100644 index 000000000..2da7b9fca --- /dev/null +++ b/apps/emqx_coap/README.md @@ -0,0 +1,254 @@ + +# emqx-coap + +emqx-coap is a CoAP Gateway for EMQ X Broker. It translates CoAP messages into MQTT messages and make it possible to communiate between CoAP clients and MQTT clients. + +### Client Usage Example +libcoap is an excellent coap library which has a simple client tool. It is recommended to use libcoap as a coap client. + +To compile libcoap, do following steps: + +``` +git clone http://github.com/obgm/libcoap +cd libcoap +./autogen.sh +./configure --enable-documentation=no --enable-tests=no +make +``` + +### Publish example: +``` +libcoap/examples/coap-client -m put -e 1234 "coap://127.0.0.1/mqtt/topic1?c=client1&u=tom&p=secret" +``` +- topic name is "topic1", NOT "/topic1" +- client id is client1 +- username is tom +- password is secret +- payload is a text string "1234" + +A mqtt message with topic="topic1", payload="1234" has been published. Any mqtt client or coap client, who has subscribed this topic could receive this message immediately. + +### Subscribe example: + +``` +libcoap/examples/coap-client -m get -s 10 "coap://127.0.0.1/mqtt/topic1?c=client1&u=tom&p=secret" +``` +- topic name is "topic1", NOT "/topic1" +- client id is client1 +- username is tom +- password is secret +- subscribe time is 10 seconds + +And you will get following result if any mqtt client or coap client sent message with text "1234567" to "topic1": + +``` +v:1 t:CON c:GET i:31ae {} [ ] +1234567v:1 t:CON c:GET i:31af {} [ Observe:1, Uri-Path:mqtt, Uri-Path:topic1, Uri-Query:c=client1, Uri-Query:u=tom, Uri-Query:p=secret ] +``` +The output message is not well formatted which hide "1234567" at the head of the 2nd line. + +### Configure + +#### Common + +File: etc/emqx_coap.conf + +```properties + +## The UDP port that CoAP is listening on. +## +## Value: Port +coap.port = 5683 + +## Interval for keepalive, specified in seconds. +## +## Value: Duration +## -s: seconds +## -m: minutes +## -h: hours +coap.keepalive = 120s + +## Whether to enable statistics for CoAP clients. +## +## Value: on | off +coap.enable_stats = off + +``` + +#### DTLS + +emqx_coap enable one-way authentication by default. + +If you want to disable it, comment these lines. + +File: etc/emqx_coap.conf + +```properties + +## The DTLS port that CoAP is listening on. +## +## Value: Port +coap.dtls.port = 5684 + +## Private key file for DTLS +## +## Value: File +coap.dtls.keyfile = {{ platform_etc_dir }}/certs/key.pem + +## Server certificate for DTLS. +## +## Value: File +coap.dtls.certfile = {{ platform_etc_dir }}/certs/cert.pem + +``` + +##### Enable two-way autentication + +For two-way autentication: + +```properties + +## A server only does x509-path validation in mode verify_peer, +## as it then sends a certificate request to the client (this +## message is not sent if the verify option is verify_none). +## You can then also want to specify option fail_if_no_peer_cert. +## More information at: http://erlang.org/doc/man/ssl.html +## +## Value: verify_peer | verify_none +## coap.dtls.verify = verify_peer + +## PEM-encoded CA certificates for DTLS +## +## Value: File +## coap.dtls.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem + +## Used together with {verify, verify_peer} by an SSL server. If set to true, +## the server fails if the client does not have a certificate to send, that is, +## sends an empty certificate. +## +## Value: true | false +## coap.dtls.fail_if_no_peer_cert = false + +``` + +### Load emqx-coap + +```bash +./bin/emqx_ctl plugins load emqx_coap +``` + +CoAP Client Observe Operation (subscribe topic) +----------------------------------------------- +To subscribe any topic, issue following command: + +``` + GET coap://localhost/mqtt/{topicname}?c={clientid}&u={username}&p={password} with OBSERVE=0 +``` + +- "mqtt" in the path is mandatory. +- replace {topicname}, {clientid}, {username} and {password} with your true values. +- {topicname} and {clientid} is mandatory. +- if clientid is absent, a "bad_request" will be returned. +- {topicname} in URI should be percent-encoded to prevent special characters, such as + and #. +- {username} and {password} are optional. +- if {username} and {password} are not correct, an uauthorized error will be returned. +- topic is subscribed with qos1. + +CoAP Client Unobserve Operation (unsubscribe topic) +--------------------------------------------------- +To cancel observation, issue following command: + +``` + GET coap://localhost/mqtt/{topicname}?c={clientid}&u={username}&p={password} with OBSERVE=1 +``` + +- "mqtt" in the path is mandatory. +- replace {topicname}, {clientid}, {username} and {password} with your true values. +- {topicname} and {clientid} is mandatory. +- if clientid is absent, a "bad_request" will be returned. +- {topicname} in URI should be percent-encoded to prevent special characters, such as + and #. +- {username} and {password} are optional. +- if {username} and {password} are not correct, an uauthorized error will be returned. + +CoAP Client Notification Operation (subscribed Message) +------------------------------------------------------- +Server will issue an observe-notification as a subscribed message. + +- Its payload is exactly the mqtt payload. +- payload data type is "application/octet-stream". + +CoAP Client Publish Operation +----------------------------- +Issue a coap put command to do publishment. For example: + +``` + PUT coap://localhost/mqtt/{topicname}?c={clientid}&u={username}&p={password} +``` + +- "mqtt" in the path is mandatory. +- replace {topicname}, {clientid}, {username} and {password} with your true values. +- {topicname} and {clientid} is mandatory. +- if clientid is absent, a "bad_request" will be returned. +- {topicname} in URI should be percent-encoded to prevent special characters, such as + and #. +- {username} and {password} are optional. +- if {username} and {password} are not correct, an uauthorized error will be returned. +- payload could be any binary data. +- payload data type is "application/octet-stream". +- publish message will be sent with qos0. + +CoAP Client Keep Alive +---------------------- +Device should issue a get command periodically, serve as a ping to keep mqtt session online. + +``` + GET coap://localhost/mqtt/{any_topicname}?c={clientid}&u={username}&p={password} +``` + +- "mqtt" in the path is mandatory. +- replace {any_topicname}, {clientid}, {username} and {password} with your true values. +- {any_topicname} is optional, and should be percent-encoded to prevent special characters. +- {clientid} is mandatory. If clientid is absent, a "bad_request" will be returned. +- {username} and {password} are optional. +- if {username} and {password} are not correct, an uauthorized error will be returned. +- coap client should do keepalive work periodically to keep mqtt session online, especially those devices in a NAT network. + + +CoAP Client NOTES +----------------- +emqx-coap gateway does not accept POST and DELETE requests. + +Topics in URI should be percent-encoded, but corresponding uri_path option has percent-encoding converted. Please refer to RFC 7252 section 6.4, "Decomposing URIs into Options": + +> Note that these rules completely resolve any percent-encoding. + +That implies coap client is responsible to convert any percert-encoding into true character while assembling coap packet. + + +ClientId, Username, Password and Topic +-------------------------------------- +ClientId/username/password/topic in the coap URI are the concepts in mqtt. That is to say, emqx-coap is trying to fit coap message into mqtt system, by borrowing the client/username/password/topic from mqtt. + +The Auth/ACL/Hook features in mqtt also applies on coap stuff. For example: +- If username/password is not authorized, coap client will get an uauthorized error. +- If username or clientid is not allowed to published specific topic, coap message will be dropped in fact, although coap client will get an acknoledgement from emqx-coap. +- If a coap message is published, a 'message.publish' hook is able to capture this message as well. + +well-known locations +-------------------- +Discovery always return "," + +For example +``` +libcoap/examples/coap-client -m get "coap://127.0.0.1/.well-known/core" +``` + +License +------- + +Apache License Version 2.0 + +Author +------ + +EMQ X Team. + diff --git a/apps/emqx_coap/TODO b/apps/emqx_coap/TODO new file mode 100644 index 000000000..2af129d6c --- /dev/null +++ b/apps/emqx_coap/TODO @@ -0,0 +1,13 @@ +1. Remove the test/test_mqtt_broker and use emqx-ct-helpers -> Done! + - Enhance all test case + +2. Remove the mqtt adaptor +3. Remove the emqx_coap_ps_topics.erl + + +### Problems + +1. The coap-client of libcoap does not support Fragment DTLS handshake frame + * So, the connection will be established failed, if the 'Server Hello' frame is too big + * Why is the 'Server Hello' too big when enable the 'coap.dtls.cacertfile' option? +2. diff --git a/apps/emqx_coap/docs/rfc7049.pdf b/apps/emqx_coap/docs/rfc7049.pdf new file mode 100644 index 000000000..a16db36ef Binary files /dev/null and b/apps/emqx_coap/docs/rfc7049.pdf differ diff --git a/apps/emqx_coap/docs/rfc7228.pdf b/apps/emqx_coap/docs/rfc7228.pdf new file mode 100644 index 000000000..c9dc1b59f Binary files /dev/null and b/apps/emqx_coap/docs/rfc7228.pdf differ diff --git a/apps/emqx_coap/docs/rfc7252.pdf b/apps/emqx_coap/docs/rfc7252.pdf new file mode 100644 index 000000000..6876fad3e Binary files /dev/null and b/apps/emqx_coap/docs/rfc7252.pdf differ diff --git a/apps/emqx_coap/etc/emqx_coap.conf b/apps/emqx_coap/etc/emqx_coap.conf new file mode 100644 index 000000000..0590a348e --- /dev/null +++ b/apps/emqx_coap/etc/emqx_coap.conf @@ -0,0 +1,82 @@ +##-------------------------------------------------------------------- +## CoAP Gateway +##-------------------------------------------------------------------- + +## The IP and UDP port that CoAP bind with. +## +## Default: 0.0.0.0:5683 +## +## Examples: +## coap.bind.udp.x = 0.0.0.0:5683 | :::5683 | 127.0.0.1:5683 | ::1:5683 +## +coap.bind.udp.1 = 0.0.0.0:5683 +##coap.bind.udp.2 = 0.0.0.0:6683 + +## Whether to enable statistics for CoAP clients. +## +## Value: on | off +coap.enable_stats = off + + +##------------------------------------------------------------------------------ +## DTLS options + +## The DTLS port that CoAP is listening on. +## +## Default: 0.0.0.0:5684 +## +## Examples: +## coap.bind.dtls.x = 0.0.0.0:5684 | :::5684 | 127.0.0.1:5684 | ::1:5684 +## +coap.bind.dtls.1 = 0.0.0.0:5684 +##coap.bind.dtls.2 = 0.0.0.0:6684 + +## A server only does x509-path validation in mode verify_peer, +## as it then sends a certificate request to the client (this +## message is not sent if the verify option is verify_none). +## You can then also want to specify option fail_if_no_peer_cert. +## More information at: http://erlang.org/doc/man/ssl.html +## +## Value: verify_peer | verify_none +## coap.dtls.verify = verify_peer + +## Private key file for DTLS +## +## Value: File +coap.dtls.keyfile = {{ platform_etc_dir }}/certs/key.pem + +## Server certificate for DTLS. +## +## Value: File +coap.dtls.certfile = {{ platform_etc_dir }}/certs/cert.pem + +## PEM-encoded CA certificates for DTLS +## +## Value: File +## coap.dtls.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem + +## Used together with {verify, verify_peer} by an SSL server. If set to true, +## the server fails if the client does not have a certificate to send, that is, +## sends an empty certificate. +## +## Value: true | false +## coap.dtls.fail_if_no_peer_cert = false + +## This is the single most important configuration option of an Erlang SSL +## application. Ciphers (and their ordering) define the way the client and +## server encrypt information over the wire, from the initial Diffie-Helman +## key exchange, the session key encryption ## algorithm and the message +## digest algorithm. Selecting a good cipher suite is critical for the +## application’s data security, confidentiality and performance. +## +## The cipher list above offers: +## +## A good balance between compatibility with older browsers. +## It can get stricter for Machine-To-Machine scenarios. +## Perfect Forward Secrecy. +## No old/insecure encryption and HMAC algorithms +## +## Most of it was copied from Mozilla’s Server Side TLS article +## +## Value: Ciphers +coap.dtls.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA diff --git a/apps/emqx_coap/include/emqx_coap.hrl b/apps/emqx_coap/include/emqx_coap.hrl new file mode 100644 index 000000000..8204dc98c --- /dev/null +++ b/apps/emqx_coap/include/emqx_coap.hrl @@ -0,0 +1,20 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-define(APP, emqx_coap). + +-record(coap_mqtt_auth, {clientid, username, password}). + diff --git a/apps/emqx_coap/intergration_test/Makefile b/apps/emqx_coap/intergration_test/Makefile new file mode 100644 index 000000000..12a2081dd --- /dev/null +++ b/apps/emqx_coap/intergration_test/Makefile @@ -0,0 +1,129 @@ +.PHONY: clean, clean_result, start_broker stop_broker case1 case2 case3 + +RELX_CONF = emqx-rel/relx.config +LIBCOAP_GIT = libcoap/README.md + +all: clean_result $(RELX_CONF) $(LIBCOAP_GIT) start_broker clean_result case1 case2 case3 case4 stop_broker + @echo " " + @echo " test complete" + @echo " " + +clean_result: + -rm -f case*.txt + + +start_broker: + -rm -f emqx-rel/_rel/emqx/log/* + -emqx-rel/_rel/emqx/bin/emqx stop + sleep 1 + emqx-rel/_rel/emqx/bin/emqx start + sleep 1 + emqx-rel/_rel/emqx/bin/emqx_ctl plugins load emqx_coap + +stop_broker: + -emqx-rel/_rel/emqx/bin/emqx stop + +case1: + libcoap/examples/coap-client -m get -s 5 "coap://127.0.0.1/mqtt/topic1?c=client1&u=tom&p=secret" > case1_output.txt & + sleep 1 + libcoap/examples/coap-client -m put -e w123G45 "coap://127.0.0.1/mqtt/topic1?c=client2&u=mike&p=pw12" + sleep 6 + python check_result.py case1 case1_output.txt==w123G45 + +case2: + # subscribe to topic="x/y" + libcoap/examples/coap-client -m get -s 5 "coap://127.0.0.1/mqtt/x%2Fy?c=client3&u=tom&p=secret" > case2_output1.txt & + # subscribe to topic="+/z" + libcoap/examples/coap-client -m get -s 5 "coap://127.0.0.1/mqtt/%2B%2Fz?c=client4&u=mike&p=pw12" > case2_output2.txt & + sleep 1 + # publish to topic="x/y" + libcoap/examples/coap-client -m put -e big9wolf "coap://127.0.0.1/mqtt/x%2Fy?c=client5&u=sun&p=pw3" + # publish to topic="p/z" + libcoap/examples/coap-client -m put -e black2ant "coap://127.0.0.1/mqtt/p%2Fz?c=client5&u=sun&p=pw3" + sleep 6 + python check_result.py case2 case2_output1.txt==big9wolf case2_output1.txt!=black2ant case2_output2.txt!=big9wolf case2_output2.txt==black2ant + +case3: + libcoap/examples/coap-client -m get -T tk12 -s 5 "coap://127.0.0.1/mqtt/a%2Fb?c=client3&u=tom&p=secret" > case3_output1.txt & + libcoap/examples/coap-client -m get -T tk34 -s 5 "coap://127.0.0.1/mqtt/c%2Fd?c=client3&u=tom&p=secret" > case3_output2.txt & + sleep 1 + libcoap/examples/coap-client -m put -e big9wolf "coap://127.0.0.1/mqtt/c%2Fd?c=client5&u=sun&p=pw3" + libcoap/examples/coap-client -m put -e black2ant "coap://127.0.0.1/mqtt/a%2Fb?c=client5&u=sun&p=pw3" + sleep 6 + python check_result.py case3 case3_output1.txt==black2ant case3_output2.txt==big9wolf case3_output2.txt!=black2ant + + + +case4: + # reload emqx_coap, does it work as expected? + sleep 1 + emqx-rel/_rel/emqx/bin/emqx_ctl plugins unload emqx_coap + sleep 1 + emqx-rel/_rel/emqx/bin/emqx_ctl plugins load emqx_coap + sleep 1 + libcoap/examples/coap-client -m get -s 5 "coap://127.0.0.1/mqtt/topic1?c=client1&u=tom&p=secret" > case4_output.txt & + sleep 1 + libcoap/examples/coap-client -m put -e w6J3G45 "coap://127.0.0.1/mqtt/topic1?c=client2&u=mike&p=pw12" + sleep 6 + python check_result.py case4 case4_output.txt==w6J3G45 + + + + +$(RELX_CONF): + git clone https://github.com/emqx/emqx-rel.git + git clone https://github.com/emqx/emq-coap.git + @echo "update emq-coap with this development code" + mv emq-coap emqx_coap + -rm -rf emqx_coap/etc + -rm -rf emqx_coap/include + -rm -rf emqx_coap/priv + -rm -rf emqx_coap/src + -rm -rf emqx_coap/Makefile + cp -rf ../etc emqx_coap/ + cp -rf ../include emqx_coap/ + cp -rf ../priv emqx_coap/ + cp -rf ../src emqx_coap/ + cp -rf ../Makefile emqx_coap/Makefile + -mkdir emqx-rel/deps + mv emqx_coap emqx-rel/deps/ + @echo "start building ..." + make -C emqx-rel -f Makefile + + +coap: $(LIBCOAP_GIT) + @echo "make coap" + +$(LIBCOAP_GIT): + git clone -b v4.1.2 http://github.com/obgm/libcoap + cd libcoap && ./autogen.sh && ./configure --enable-documentation=no --enable-tests=no + make -C libcoap -f Makefile + +r: rebuild_emq + # r short for rebuild_emq + @echo " rebuild complete " + +rebuild_emq: + -emqx-rel/_rel/emqx/bin/emqx stop + -rm -rf emqx-rel/deps/emqx_coap/etc + -rm -rf emqx-rel/deps/emqx_coap/include + -rm -rf emqx-rel/deps/emqx_coap/priv + -rm -rf emqx-rel/deps/emqx_coap/src + -rm -rf emqx-rel/deps/emqx_coap/Makefile + cp -rf ../etc emqx-rel/deps/emqx_coap/ + cp -rf ../include emqx-rel/deps/emqx_coap/ + cp -rf ../priv emqx-rel/deps/emqx_coap/ + cp -rf ../src emqx-rel/deps/emqx_coap/ + cp -rf ../Makefile emqx-rel/deps/emqx_coap/Makefile + make -C emqx-rel -f Makefile + +clean: clean_result + -rm -f client/*.exe + -rm -f client/*.o + -rm -rf emqx-rel + -rm -rf libcoap + +lazy: clean_result start_broker case2 stop_broker + # custom your command here + @echo "you are so lazy" + diff --git a/apps/emqx_coap/intergration_test/README.md b/apps/emqx_coap/intergration_test/README.md new file mode 100644 index 000000000..eb3507923 --- /dev/null +++ b/apps/emqx_coap/intergration_test/README.md @@ -0,0 +1,8 @@ +Integration test for emq-coap +====== + +execute following command +``` +make +``` + diff --git a/apps/emqx_coap/intergration_test/check_result.py b/apps/emqx_coap/intergration_test/check_result.py new file mode 100644 index 000000000..f9baaefae --- /dev/null +++ b/apps/emqx_coap/intergration_test/check_result.py @@ -0,0 +1,52 @@ +import sys + + +def have_string(filename, text): + data = open(filename, "rb").read() + if data.find(text) > 0: + return True + else: + return False + + +def mark(case_number, result, description): + if result: + f = open(case_number+"_PASS.txt", "wb") + f.close() + print("\n\n"+case_number+" PASS\n\n") + else: + f = open(case_number+"_FAIL.txt", "wb") + f.write(description) + f.close() + print("\n\n"+case_number+" FAIL\n\n") + +def parse_condition(condition): + if condition.find("==") > 0: + r = condition.split("==") + return r[0], r[1], True + elif condition.find("!=") > 0: + r = condition.split("!=") + return r[0], r[1], False + else: + print("\ncondition syntax error\n\n\n") + sys.exit("condition syntax error") + + +def main(): + case_number = sys.argv[1] + description = "" + conclustion = True + for condition in sys.argv[2:]: + filename, text, result = parse_condition(condition) + if have_string(filename, text) == result: + pass + else: + conclustion = False + description = description + "\n" + condition + " failed\n" + + mark(case_number, conclustion, description) + + +if __name__ == "__main__": + main() + diff --git a/apps/emqx_coap/priv/emqx_coap.schema b/apps/emqx_coap/priv/emqx_coap.schema new file mode 100644 index 000000000..465979964 --- /dev/null +++ b/apps/emqx_coap/priv/emqx_coap.schema @@ -0,0 +1,93 @@ +%%-*- mode: erlang -*- +%% emqx_coap config mapping +{mapping, "coap.bind.udp.$number", "emqx_coap.bind_udp", [ + {datatype, ip}, + {default, "0.0.0.0:5683"} +]}. + +{mapping, "coap.enable_stats", "emqx_coap.enable_stats", [ + {datatype, flag} +]}. + +{mapping, "coap.bind.dtls.$number", "emqx_coap.bind_dtls", [ + {datatype, ip}, + {default, "0.0.0.0:5684"} +]}. + +{mapping, "coap.dtls.keyfile", "emqx_coap.dtls_opts", [ + {datatype, string} +]}. + +{mapping, "coap.dtls.certfile", "emqx_coap.dtls_opts", [ + {datatype, string} +]}. + +{mapping, "coap.dtls.verify", "emqx_coap.dtls_opts", [ + {default, verify_none}, + {datatype, {enum, [verify_none, verify_peer]}} +]}. + +{mapping, "coap.dtls.cacertfile", "emqx_coap.dtls_opts", [ + {datatype, string} +]}. + +{mapping, "coap.dtls.fail_if_no_peer_cert", "emqx_coap.dtls_opts", [ + {datatype, {enum, [true, false]}} +]}. + +{mapping, "coap.dtls.ciphers", "emqx_coap.dtls_opts", [ + {datatype, string} +]}. + +{translation, "emqx_coap.bind_udp", fun(Conf) -> + Options = cuttlefish_variable:filter_by_prefix("coap.bind.udp", Conf), + lists:map(fun({_, Bind}) -> + {Ip, Port} = cuttlefish_datatypes:from_string(Bind, ip), + Opts = case inet:parse_address(Ip) of + {ok, {_,_,_,_} = Address} -> + [inet, {ip, Address}]; + {ok, {_,_,_,_,_,_,_,_} = Address} -> + [inet6, {ip, Address}] + end, + {Port, Opts} + end, Options) +end}. + +{translation, "emqx_coap.bind_dtls", fun(Conf) -> + Options = cuttlefish_variable:filter_by_prefix("coap.bind.dtls", Conf), + lists:map(fun({_, Bind}) -> + {Ip, Port} = cuttlefish_datatypes:from_string(Bind, ip), + Opts = case inet:parse_address(Ip) of + {ok, {_,_,_,_} = Address} -> + [inet, {ip, Address}]; + {ok, {_,_,_,_,_,_,_,_} = Address} -> + [inet6, {ip, Address}] + end, + {Port, Opts} + end, Options) +end}. + +{translation, "emqx_coap.dtls_opts", fun(Conf) -> + Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end, + + %% Ciphers + SplitFun = fun(undefined) -> undefined; (S) -> string:tokens(S, ",") end, + Ciphers = + case cuttlefish:conf_get("coap.dtls.ciphers", Conf, undefined) of + undefined -> + lists:foldl( + fun(TlsVer, Ciphers) -> + Ciphers ++ ssl:cipher_suites(all, TlsVer) + end, [], ['dtlsv1', 'dtlsv1.2']); + C -> + SplitFun(C) + end, + + Filter([{verify, cuttlefish:conf_get("coap.dtls.verify", Conf, undefined)}, + {keyfile, cuttlefish:conf_get("coap.dtls.keyfile", Conf, undefined)}, + {certfile, cuttlefish:conf_get("coap.dtls.certfile", Conf, undefined)}, + {cacertfile, cuttlefish:conf_get("coap.dtls.cacertfile", Conf, undefined)}, + {fail_if_no_peer_cert, cuttlefish:conf_get("coap.dtls.fail_if_no_peer_cert", Conf, undefined)}, + {ciphers, Ciphers}]) +end}. + diff --git a/apps/emqx_coap/rebar.config b/apps/emqx_coap/rebar.config new file mode 100644 index 000000000..dd1ad613e --- /dev/null +++ b/apps/emqx_coap/rebar.config @@ -0,0 +1,28 @@ +{deps, + [ + {gen_coap, {git, "https://github.com/emqx/gen_coap", {tag, "v0.3.0"}}} + ]}. + +{edoc_opts, [{preprocess, true}]}. +{erl_opts, [warn_unused_vars, + warn_shadow_vars, + warn_unused_import, + warn_obsolete_guard, + debug_info, + {parse_transform}]}. + +{xref_checks, [undefined_function_calls, undefined_functions, + locals_not_used, deprecated_function_calls, + warnings_as_errors, deprecated_functions]}. +{cover_enabled, true}. +{cover_opts, [verbose]}. +{cover_export_enabled, true}. + +{profiles, + [{test, + [{deps, + [{er_coap_client, {git, "https://github.com/emqx/er_coap_client", {tag, "v1.0"}}}, + {emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}} + ]} + ]} + ]}. diff --git a/apps/emqx_coap/src/emqx_coap.app.src b/apps/emqx_coap/src/emqx_coap.app.src new file mode 100644 index 000000000..edbd3223c --- /dev/null +++ b/apps/emqx_coap/src/emqx_coap.app.src @@ -0,0 +1,14 @@ +{application, emqx_coap, + [{description, "EMQ X CoAP Gateway"}, + {vsn, "git"}, + {modules, []}, + {registered, []}, + {applications, [kernel,stdlib,gen_coap]}, + {mod, {emqx_coap_app, []}}, + {env, []}, + {licenses, ["Apache-2.0"]}, + {maintainers, ["EMQ X Team "]}, + {links, [{"Homepage", "https://emqx.io/"}, + {"Github", "https://github.com/emqx/emqx-coap"} + ]} + ]}. diff --git a/apps/emqx_coap/src/emqx_coap.app.src.script b/apps/emqx_coap/src/emqx_coap.app.src.script new file mode 100644 index 000000000..0e14ff23f --- /dev/null +++ b/apps/emqx_coap/src/emqx_coap.app.src.script @@ -0,0 +1,24 @@ +%%-*- mode: erlang -*- +%% .app.src.script + +RemoveLeadingV = + fun(Tag) -> + case re:run(Tag, "^[v|e]?[0-9]\.[0-9]\.([0-9]|(rc|beta|alpha)\.[0-9])", [{capture, none}]) of + nomatch -> + re:replace(Tag, "/", "-", [{return ,list}]); + _ -> + %% if it is a version number prefixed by 'v' or 'e', then remove it + re:replace(Tag, "[v|e]", "", [{return ,list}]) + end + end, + +case os:getenv("EMQX_DEPS_DEFAULT_VSN") of + false -> CONFIG; % env var not defined + [] -> CONFIG; % env var set to empty string + Tag -> + [begin + AppConf0 = lists:keystore(vsn, 1, AppConf, {vsn, RemoveLeadingV(Tag)}), + {application, App, AppConf0} + end || Conf = {application, App, AppConf} <- CONFIG] +end. + diff --git a/apps/emqx_coap/src/emqx_coap_app.erl b/apps/emqx_coap/src/emqx_coap_app.erl new file mode 100644 index 000000000..eaabd356b --- /dev/null +++ b/apps/emqx_coap/src/emqx_coap_app.erl @@ -0,0 +1,40 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_coap_app). + +-behaviour(application). + +-emqx_plugin(protocol). + +-include("emqx_coap.hrl"). + +-export([ start/2 + , stop/1 + ]). + +start(_Type, _Args) -> + {ok, Sup} = emqx_coap_sup:start_link(), + coap_server_registry:add_handler([<<"mqtt">>], emqx_coap_resource, undefined), + coap_server_registry:add_handler([<<"ps">>], emqx_coap_ps_resource, undefined), + emqx_coap_ps_topics:start_link(), + emqx_coap_server:start(application:get_all_env(?APP)), + {ok,Sup}. + +stop(_State) -> + coap_server_registry:remove_handler([<<"mqtt">>], emqx_coap_resource, undefined), + coap_server_registry:remove_handler([<<"ps">>], emqx_coap_ps_resource, undefined), + emqx_coap_server:stop(application:get_all_env(?APP)). diff --git a/apps/emqx_coap/src/emqx_coap_mqtt_adapter.erl b/apps/emqx_coap/src/emqx_coap_mqtt_adapter.erl new file mode 100644 index 000000000..4c508c272 --- /dev/null +++ b/apps/emqx_coap/src/emqx_coap_mqtt_adapter.erl @@ -0,0 +1,357 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_coap_mqtt_adapter). + +-behaviour(gen_server). + +-include("emqx_coap.hrl"). + +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/emqx_mqtt.hrl"). +-include_lib("emqx/include/logger.hrl"). +-include_lib("emqx/include/emqx_mqtt.hrl"). + +-logger_header("[CoAP-Adpter]"). + +%% API. +-export([ subscribe/2 + , unsubscribe/2 + , publish/3 + ]). + +-export([ client_pid/4 + , stop/1 + ]). + +-export([call/2]). + +%% gen_server. +-export([ init/1 + , handle_call/3 + , handle_cast/2 + , handle_info/2 + , terminate/2 + , code_change/3 + ]). + +-record(state, {peername, clientid, username, password, sub_topics = [], connected_at}). + +-define(ALIVE_INTERVAL, 20000). + +-define(CONN_STATS, [recv_pkt, recv_msg, send_pkt, send_msg]). + +-define(SUBOPTS, #{rh => 0, rap => 0, nl => 0, qos => ?QOS_0, is_new => false}). + +%%-------------------------------------------------------------------- +%% API +%%-------------------------------------------------------------------- + +client_pid(undefined, _Username, _Password, _Channel) -> + {error, bad_request}; +client_pid(ClientId, Username, Password, Channel) -> + % check authority + case start(ClientId, Username, Password, Channel) of + {ok, Pid1} -> {ok, Pid1}; + {error, {already_started, Pid2}} -> {ok, Pid2}; + {error, auth_failure} -> {error, auth_failure}; + Other -> {error, Other} + end. + +start(ClientId, Username, Password, Channel) -> + % DO NOT use start_link, since multiple coap_reponsder may have relation with one mqtt adapter, + % one coap_responder crashes should not make mqtt adapter crash too + % And coap_responder is not a system process, it is dangerous to link mqtt adapter to coap_responder + gen_server:start({via, emqx_coap_registry, {ClientId, Username, Password}}, ?MODULE, {ClientId, Username, Password, Channel}, []). + +stop(Pid) -> + gen_server:stop(Pid). + +subscribe(Pid, Topic) -> + gen_server:call(Pid, {subscribe, Topic, self()}). + +unsubscribe(Pid, Topic) -> + gen_server:call(Pid, {unsubscribe, Topic, self()}). + +publish(Pid, Topic, Payload) -> + gen_server:call(Pid, {publish, Topic, Payload}). + +%% For emqx_management plugin +call(Pid, Msg) -> + Pid ! Msg, ok. + +%%-------------------------------------------------------------------- +%% gen_server Callbacks +%%-------------------------------------------------------------------- + +init({ClientId, Username, Password, Channel}) -> + ?LOG(debug, "try to start adapter ClientId=~p, Username=~p, Password=~p, Channel=~p", + [ClientId, Username, Password, Channel]), + State0 = #state{peername = Channel, + clientid = ClientId, + username = Username, + password = Password}, + _ = run_hooks('client.connect', [conninfo(State0)], undefined), + case emqx_access_control:authenticate(clientinfo(State0)) of + {ok, _AuthResult} -> + _ = run_hooks('client.connack', [conninfo(State0), success], undefined), + + State = State0#state{connected_at = erlang:system_time(millisecond)}, + + %% TODO: Evict same clientid on other node?? + + run_hooks('client.connected', [clientinfo(State), conninfo(State)]), + + erlang:send_after(?ALIVE_INTERVAL, self(), check_alive), + emqx_cm:register_channel(ClientId, info(State), stats(State)), + {ok, State}; + {error, Reason} -> + ?LOG(debug, "authentication faild: ~p", [Reason]), + _ = run_hooks('client.connack', [conninfo(State0), not_authorized], undefined), + {stop, {shutdown, Reason}} + end. + +handle_call({subscribe, Topic, CoapPid}, _From, State=#state{sub_topics = TopicList}) -> + NewTopics = proplists:delete(Topic, TopicList), + IsWild = emqx_topic:wildcard(Topic), + chann_subscribe(Topic, State), + {reply, ok, State#state{sub_topics = [{Topic, {IsWild, CoapPid}}|NewTopics]}, hibernate}; + +handle_call({unsubscribe, Topic, _CoapPid}, _From, State=#state{sub_topics = TopicList}) -> + NewTopics = proplists:delete(Topic, TopicList), + chann_unsubscribe(Topic, State), + {reply, ok, State#state{sub_topics = NewTopics}, hibernate}; + +handle_call({publish, Topic, Payload}, _From, State) -> + chann_publish(Topic, Payload, State), + {reply, ok, State}; + +handle_call(info, _From, State) -> + {reply, info(State), State}; + +handle_call(stats, _From, State) -> + {reply, stats(State), State, hibernate}; + +handle_call(kick, _From, State) -> + {stop, {shutdown, kick}, ok, State}; + +handle_call({set_rate_limit, _Rl}, _From, State) -> + ?LOG(error, "set_rate_limit is not support", []), + {reply, ok, State}; + +handle_call(get_rate_limit, _From, State) -> + ?LOG(error, "get_rate_limit is not support", []), + {reply, ok, State}; + +handle_call(Request, _From, State) -> + ?LOG(error, "adapter unexpected call ~p", [Request]), + {reply, ignored, State, hibernate}. + +handle_cast(Msg, State) -> + ?LOG(error, "broker_api unexpected cast ~p", [Msg]), + {noreply, State, hibernate}. + +handle_info({deliver, _Topic, #message{topic = Topic, payload = Payload}}, State = #state{sub_topics = Subscribers}) -> + deliver([{Topic, Payload}], Subscribers), + {noreply, State, hibernate}; + +handle_info(check_alive, State = #state{sub_topics = []}) -> + {stop, {shutdown, check_alive}, State}; +handle_info(check_alive, State) -> + erlang:send_after(?ALIVE_INTERVAL, self(), check_alive), + {noreply, State, hibernate}; + +handle_info({shutdown, Error}, State) -> + {stop, {shutdown, Error}, State}; + +handle_info({shutdown, conflict, {ClientId, NewPid}}, State) -> + ?LOG(warning, "clientid '~s' conflict with ~p", [ClientId, NewPid]), + {stop, {shutdown, conflict}, State}; + +handle_info(kick, State) -> + ?LOG(info, "Kicked", []), + {stop, {shutdown, kick}, State}; + +handle_info(Info, State) -> + ?LOG(error, "adapter unexpected info ~p", [Info]), + {noreply, State, hibernate}. + +terminate(Reason, State = #state{clientid = ClientId, sub_topics = SubTopics}) -> + ?LOG(debug, "unsubscribe ~p while exiting for ~p", [SubTopics, Reason]), + [chann_unsubscribe(Topic, State) || {Topic, _} <- SubTopics], + emqx_cm:unregister_channel(ClientId), + + ConnInfo0 = conninfo(State), + ConnInfo = ConnInfo0#{disconnected_at => erlang:system_time(millisecond)}, + run_hooks('client.disconnected', [clientinfo(State), Reason, ConnInfo]). + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%-------------------------------------------------------------------- +%% Channel adapter functions + +chann_subscribe(Topic, State = #state{clientid = ClientId}) -> + ?LOG(debug, "subscribe Topic=~p", [Topic]), + emqx_broker:subscribe(Topic, ClientId, ?SUBOPTS), + emqx_hooks:run('session.subscribed', [clientinfo(State), Topic, ?SUBOPTS]). + +chann_unsubscribe(Topic, State) -> + ?LOG(debug, "unsubscribe Topic=~p", [Topic]), + Opts = #{rh => 0, rap => 0, nl => 0, qos => 0}, + emqx_broker:unsubscribe(Topic), + emqx_hooks:run('session.unsubscribed', [clientinfo(State), Topic, Opts]). + +chann_publish(Topic, Payload, #state{clientid = ClientId}) -> + ?LOG(debug, "publish Topic=~p, Payload=~p", [Topic, Payload]), + emqx_broker:publish( + emqx_message:set_flag(retain, false, + emqx_message:make(ClientId, ?QOS_0, Topic, Payload))). + +%%-------------------------------------------------------------------- +%% Deliver + +deliver([], _) -> ok; +deliver([Pub | More], Subscribers) -> + ok = do_deliver(Pub, Subscribers), + deliver(More, Subscribers). + +do_deliver({Topic, Payload}, Subscribers) -> + %% handle PUBLISH packet from broker + ?LOG(debug, "deliver message from broker Topic=~p, Payload=~p", [Topic, Payload]), + deliver_to_coap(Topic, Payload, Subscribers), + ok; + +do_deliver(Pkt, _Subscribers) -> + ?LOG(warning, "unknown packet type to deliver, pkt=~p,", [Pkt]), + ok. + +deliver_to_coap(_TopicName, _Payload, []) -> + ok; +deliver_to_coap(TopicName, Payload, [{TopicFilter, {IsWild, CoapPid}}|T]) -> + Matched = case IsWild of + true -> emqx_topic:match(TopicName, TopicFilter); + false -> TopicName =:= TopicFilter + end, + %?LOG(debug, "deliver_to_coap Matched=~p, CoapPid=~p, TopicName=~p, Payload=~p, T=~p", [Matched, CoapPid, TopicName, Payload, T]), + Matched andalso (CoapPid ! {dispatch, TopicName, Payload}), + deliver_to_coap(TopicName, Payload, T). + +%%-------------------------------------------------------------------- +%% Helper funcs + +-compile({inline, [run_hooks/2, run_hooks/3]}). +run_hooks(Name, Args) -> + ok = emqx_metrics:inc(Name), emqx_hooks:run(Name, Args). + +run_hooks(Name, Args, Acc) -> + ok = emqx_metrics:inc(Name), emqx_hooks:run_fold(Name, Args, Acc). + +%%-------------------------------------------------------------------- +%% Info & Stats + +info(State) -> + ChannInfo = chann_info(State), + ChannInfo#{sockinfo => sockinfo(State)}. + +%% copies from emqx_connection:info/1 +sockinfo(#state{peername = Peername}) -> + #{socktype => udp, + peername => Peername, + sockname => {{127,0,0,1}, 5683}, %% FIXME: Sock? + sockstate => running, + active_n => 1 + }. + +%% copies from emqx_channel:info/1 +chann_info(State) -> + #{conninfo => conninfo(State), + conn_state => connected, + clientinfo => clientinfo(State), + session => maps:from_list(session_info(State)), + will_msg => undefined + }. + +conninfo(#state{peername = Peername, + clientid = ClientId, + connected_at = ConnectedAt}) -> + #{socktype => udp, + sockname => {{127,0,0,1}, 5683}, + peername => Peername, + peercert => nossl, %% TODO: dtls + conn_mod => ?MODULE, + proto_name => <<"CoAP">>, + proto_ver => 1, + clean_start => true, + clientid => ClientId, + username => undefined, + conn_props => undefined, + connected => true, + connected_at => ConnectedAt, + keepalive => 0, + receive_maximum => 0, + expiry_interval => 0 + }. + +%% copies from emqx_session:info/1 +session_info(#state{sub_topics = SubTopics, connected_at = ConnectedAt}) -> + Subs = lists:foldl( + fun({Topic, _}, Acc) -> + Acc#{Topic => ?SUBOPTS} + end, #{}, SubTopics), + [{subscriptions, Subs}, + {upgrade_qos, false}, + {retry_interval, 0}, + {await_rel_timeout, 0}, + {created_at, ConnectedAt} + ]. + +%% The stats keys copied from emqx_connection:stats/1 +stats(#state{sub_topics = SubTopics}) -> + SockStats = [{recv_oct,0}, {recv_cnt,0}, {send_oct,0}, {send_cnt,0}, {send_pend,0}], + ConnStats = emqx_pd:get_counters(?CONN_STATS), + ChanStats = [{subscriptions_cnt, length(SubTopics)}, + {subscriptions_max, length(SubTopics)}, + {inflight_cnt, 0}, + {inflight_max, 0}, + {mqueue_len, 0}, + {mqueue_max, 0}, + {mqueue_dropped, 0}, + {next_pkt_id, 0}, + {awaiting_rel_cnt, 0}, + {awaiting_rel_max, 0} + ], + ProcStats = emqx_misc:proc_stats(), + lists:append([SockStats, ConnStats, ChanStats, ProcStats]). + +clientinfo(#state{peername = {PeerHost, _}, + clientid = ClientId, + username = Username, + password = Password}) -> + #{zone => undefined, + protocol => coap, + peerhost => PeerHost, + sockport => 5683, %% FIXME: + clientid => ClientId, + username => Username, + password => Password, + peercert => nossl, + is_bridge => false, + is_superuser => false, + mountpoint => undefined, + ws_cookie => undefined + }. + diff --git a/apps/emqx_coap/src/emqx_coap_ps_resource.erl b/apps/emqx_coap/src/emqx_coap_ps_resource.erl new file mode 100644 index 000000000..09d291bf5 --- /dev/null +++ b/apps/emqx_coap/src/emqx_coap_ps_resource.erl @@ -0,0 +1,318 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_coap_ps_resource). + +-behaviour(coap_resource). + +-include("emqx_coap.hrl"). +-include_lib("gen_coap/include/coap.hrl"). +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/emqx_mqtt.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-logger_header("[CoAP-PS-RES]"). + +-export([ coap_discover/2 + , coap_get/5 + , coap_post/4 + , coap_put/4 + , coap_delete/3 + , coap_observe/5 + , coap_unobserve/1 + , handle_info/2 + , coap_ack/2 + ]). + +-ifdef(TEST). +-export([topic/1]). +-endif. + +-define(PS_PREFIX, [<<"ps">>]). + +%%-------------------------------------------------------------------- +%% Resource Callbacks +%%-------------------------------------------------------------------- +coap_discover(_Prefix, _Args) -> + [{absolute, [<<"ps">>], []}]. + +coap_get(ChId, ?PS_PREFIX, TopicPath, Query, Content=#coap_content{format = Format}) when TopicPath =/= [] -> + Topic = topic(TopicPath), + ?LOG(debug, "coap_get() Topic=~p, Query=~p~n", [Topic, Query]), + #coap_mqtt_auth{clientid = Clientid, username = Usr, password = Passwd} = get_auth(Query), + case emqx_coap_mqtt_adapter:client_pid(Clientid, Usr, Passwd, ChId) of + {ok, Pid} -> + put(mqtt_client_pid, Pid), + case Format of + <<"application/link-format">> -> + Content; + _Other -> + %% READ the topic info + read_last_publish_message(emqx_topic:wildcard(Topic), Topic, Content) + end; + {error, auth_failure} -> + put(mqtt_client_pid, undefined), + {error, uauthorized}; + {error, bad_request} -> + put(mqtt_client_pid, undefined), + {error, bad_request}; + {error, _Other} -> + put(mqtt_client_pid, undefined), + {error, internal_server_error} + end; +coap_get(ChId, Prefix, TopicPath, Query, _Content) -> + ?LOG(error, "ignore bad get request ChId=~p, Prefix=~p, TopicPath=~p, Query=~p", [ChId, Prefix, TopicPath, Query]), + {error, bad_request}. + +coap_post(_ChId, ?PS_PREFIX, TopicPath, #coap_content{format = Format, payload = Payload, max_age = MaxAge}) when TopicPath =/= [] -> + Topic = topic(TopicPath), + ?LOG(debug, "coap_post() Topic=~p, MaxAge=~p, Format=~p~n", [Topic, MaxAge, Format]), + case Format of + %% We treat ct of "application/link-format" as CREATE message + <<"application/link-format">> -> + handle_received_create(Topic, MaxAge, Payload); + %% We treat ct of other values as PUBLISH message + Other -> + ?LOG(debug, "coap_post() receive payload format=~p, will process as PUBLISH~n", [Format]), + handle_received_publish(Topic, MaxAge, Other, Payload) + end; + +coap_post(_ChId, _Prefix, _TopicPath, _Content) -> + {error, method_not_allowed}. + +coap_put(_ChId, ?PS_PREFIX, TopicPath, #coap_content{max_age = MaxAge, format = Format, payload = Payload}) when TopicPath =/= [] -> + Topic = topic(TopicPath), + ?LOG(debug, "put message, Topic=~p, Payload=~p~n", [Topic, Payload]), + handle_received_publish(Topic, MaxAge, Format, Payload); + +coap_put(_ChId, Prefix, TopicPath, Content) -> + ?LOG(error, "put has error, Prefix=~p, TopicPath=~p, Content=~p", [Prefix, TopicPath, Content]), + {error, bad_request}. + +coap_delete(_ChId, ?PS_PREFIX, TopicPath) -> + delete_topic_info(topic(TopicPath)); + +coap_delete(_ChId, _Prefix, _TopicPath) -> + {error, method_not_allowed}. + +coap_observe(ChId, ?PS_PREFIX, TopicPath, Ack, Content) when TopicPath =/= [] -> + Topic = topic(TopicPath), + ?LOG(debug, "observe Topic=~p, Ack=~p,Content=~p", [Topic, Ack, Content]), + Pid = get(mqtt_client_pid), + emqx_coap_mqtt_adapter:subscribe(Pid, Topic), + Code = case emqx_coap_ps_topics:is_topic_timeout(Topic) of + true -> + nocontent; + false-> + content + end, + {ok, {state, ChId, ?PS_PREFIX, [Topic]}, Code, Content}; + +coap_observe(ChId, Prefix, TopicPath, Ack, _Content) -> + ?LOG(error, "unknown observe request ChId=~p, Prefix=~p, TopicPath=~p, Ack=~p", [ChId, Prefix, TopicPath, Ack]), + {error, bad_request}. + +coap_unobserve({state, _ChId, ?PS_PREFIX, TopicPath}) when TopicPath =/= [] -> + Topic = topic(TopicPath), + ?LOG(debug, "unobserve ~p", [Topic]), + Pid = get(mqtt_client_pid), + emqx_coap_mqtt_adapter:unsubscribe(Pid, Topic), + ok; +coap_unobserve({state, ChId, Prefix, TopicPath}) -> + ?LOG(error, "ignore unknown unobserve request ChId=~p, Prefix=~p, TopicPath=~p", [ChId, Prefix, TopicPath]), + ok. + +handle_info({dispatch, Topic, Payload}, State) -> + ?LOG(debug, "dispatch Topic=~p, Payload=~p", [Topic, Payload]), + {ok, Ret} = emqx_coap_ps_topics:reset_topic_info(Topic, Payload), + ?LOG(debug, "Updated publish info of topic=~p, the Ret is ~p", [Topic, Ret]), + {notify, [], #coap_content{format = <<"application/octet-stream">>, payload = Payload}, State}; +handle_info(Message, State) -> + ?LOG(error, "Unknown Message ~p", [Message]), + {noreply, State}. + +coap_ack(_Ref, State) -> {ok, State}. + + +%%-------------------------------------------------------------------- +%% Internal Functions +%%-------------------------------------------------------------------- +get_auth(Query) -> + get_auth(Query, #coap_mqtt_auth{}). + +get_auth([], Auth=#coap_mqtt_auth{}) -> + Auth; +get_auth([<<$c, $=, Rest/binary>>|T], Auth=#coap_mqtt_auth{}) -> + get_auth(T, Auth#coap_mqtt_auth{clientid = Rest}); +get_auth([<<$u, $=, Rest/binary>>|T], Auth=#coap_mqtt_auth{}) -> + get_auth(T, Auth#coap_mqtt_auth{username = Rest}); +get_auth([<<$p, $=, Rest/binary>>|T], Auth=#coap_mqtt_auth{}) -> + get_auth(T, Auth#coap_mqtt_auth{password = Rest}); +get_auth([Param|T], Auth=#coap_mqtt_auth{}) -> + ?LOG(error, "ignore unknown parameter ~p", [Param]), + get_auth(T, Auth). + +add_topic_info(publish, Topic, MaxAge, Format, Payload) when is_binary(Topic), Topic =/= <<>> -> + case emqx_coap_ps_topics:lookup_topic_info(Topic) of + [{_, StoredMaxAge, StoredCT, _, _}] -> + ?LOG(debug, "publish topic=~p already exists, need reset the topic info", [Topic]), + %% check whether the ct value stored matches the ct option in this POST message + case Format =:= StoredCT of + true -> + {ok, Ret} = + case StoredMaxAge =:= MaxAge of + true -> + emqx_coap_ps_topics:reset_topic_info(Topic, Payload); + false -> + emqx_coap_ps_topics:reset_topic_info(Topic, MaxAge, Payload) + end, + {changed, Ret}; + false -> + ?LOG(debug, "ct values of topic=~p do not match, stored ct=~p, new ct=~p, ignore the PUBLISH", [Topic, StoredCT, Format]), + {changed, false} + end; + [] -> + ?LOG(debug, "publish topic=~p will be created", [Topic]), + {ok, Ret} = emqx_coap_ps_topics:add_topic_info(Topic, MaxAge, Format, Payload), + {created, Ret} + end; + +add_topic_info(create, Topic, MaxAge, Format, _Payload) when is_binary(Topic), Topic =/= <<>> -> + case emqx_coap_ps_topics:is_topic_existed(Topic) of + true -> + %% Whether we should support CREATE to an existed topic is TBD!! + ?LOG(debug, "create topic=~p already exists, need reset the topic info", [Topic]), + {ok, Ret} = emqx_coap_ps_topics:reset_topic_info(Topic, MaxAge, Format, <<>>); + false -> + ?LOG(debug, "create topic=~p will be created", [Topic]), + {ok, Ret} = emqx_coap_ps_topics:add_topic_info(Topic, MaxAge, Format, <<>>) + end, + {created, Ret}; + +add_topic_info(_, Topic, _MaxAge, _Format, _Payload) -> + ?LOG(debug, "create topic=~p info failed", [Topic]), + {badarg, false}. + +concatenate_location_path(List = [TopicPart1, TopicPart2, TopicPart3]) when is_binary(TopicPart1), is_binary(TopicPart2), is_binary(TopicPart3) -> + list_to_binary(lists:foldl( fun (Element, AccIn) when Element =/= <<>> -> + AccIn ++ "/" ++ binary_to_list(Element); + (_Element, AccIn) -> + AccIn + end, [], List)). + +format_string_to_int(<<"application/octet-stream">>) -> + <<"42">>; +format_string_to_int(<<"application/exi">>) -> + <<"47">>; +format_string_to_int(<<"application/json">>) -> + <<"50">>. + +handle_received_publish(Topic, MaxAge, Format, Payload) -> + case add_topic_info(publish, Topic, MaxAge, format_string_to_int(Format), Payload) of + {Ret ,true} -> + Pid = get(mqtt_client_pid), + emqx_coap_mqtt_adapter:publish(Pid, topic(Topic), Payload), + Content = case Ret of + changed -> + #coap_content{}; + created -> + LocPath = concatenate_location_path([<<"ps">>, Topic, <<>>]), + #coap_content{location_path = [LocPath]} + end, + {ok, Ret, Content}; + {_, false} -> + ?LOG(debug, "add_topic_info failed, will return bad_request", []), + {error, bad_request} + end. + +handle_received_create(TopicPrefix, MaxAge, Payload) -> + case core_link:decode(Payload) of + [{rootless, [Topic], [{ct, CT}]}] when is_binary(Topic), Topic =/= <<>> -> + TrueTopic = http_uri:decode(Topic), + ?LOG(debug, "decoded link-format payload, the Topic=~p, CT=~p~n", [TrueTopic, CT]), + LocPath = concatenate_location_path([<<"ps">>, TopicPrefix, TrueTopic]), + FullTopic = binary:part(LocPath, 4, byte_size(LocPath)-4), + ?LOG(debug, "the location path is ~p, the full topic is ~p~n", [LocPath, FullTopic]), + case add_topic_info(create, FullTopic, MaxAge, CT, <<>>) of + {_, true} -> + ?LOG(debug, "create topic info successfully, will return LocPath=~p", [LocPath]), + {ok, created, #coap_content{location_path = [LocPath]}}; + {_, false} -> + ?LOG(debug, "create topic info failed, will return bad_request", []), + {error, bad_request} + end; + Other -> + ?LOG(debug, "post with bad payload of link-format ~p, will return bad_request", [Other]), + {error, bad_request} + end. + +%% When topic is timeout, server should return nocontent here, +%% but gen_coap only receive return value of #coap_content from coap_get, so temporarily we can't give the Code 2.07 {ok, nocontent} out.TBC!!! +return_resource(Topic, Payload, MaxAge, TimeStamp, Content) -> + TimeElapsed = trunc((erlang:system_time(millisecond) - TimeStamp) / 1000), + case TimeElapsed < MaxAge of + true -> + LeftTime = (MaxAge - TimeElapsed), + ?LOG(debug, "topic=~p has max age left time is ~p", [Topic, LeftTime]), + Content#coap_content{max_age = LeftTime, payload = Payload}; + false -> + ?LOG(debug, "topic=~p has been timeout, will return empty content", [Topic]), + #coap_content{} + end. + +read_last_publish_message(false, Topic, Content=#coap_content{format = QueryFormat}) when is_binary(QueryFormat)-> + ?LOG(debug, "the QueryFormat=~p", [QueryFormat]), + case emqx_coap_ps_topics:lookup_topic_info(Topic) of + [] -> + {error, not_found}; + [{_, MaxAge, CT, Payload, TimeStamp}] -> + case CT =:= format_string_to_int(QueryFormat) of + true -> + return_resource(Topic, Payload, MaxAge, TimeStamp, Content); + false -> + ?LOG(debug, "format value does not match, the queried format=~p, the stored format=~p", [QueryFormat, CT]), + {error, bad_request} + end + end; + +read_last_publish_message(false, Topic, Content) -> + case emqx_coap_ps_topics:lookup_topic_info(Topic) of + [] -> + {error, not_found}; + [{_, MaxAge, _, Payload, TimeStamp}] -> + return_resource(Topic, Payload, MaxAge, TimeStamp, Content) + end; + +read_last_publish_message(true, Topic, _Content) -> + ?LOG(debug, "the topic=~p is illegal wildcard topic", [Topic]), + {error, bad_request}. + +delete_topic_info(Topic) -> + case emqx_coap_ps_topics:lookup_topic_info(Topic) of + [] -> + {error, not_found}; + [{_, _, _, _, _}] -> + emqx_coap_ps_topics:delete_sub_topics(Topic) + end. + +topic(Topic) when is_binary(Topic) -> Topic; +topic([]) -> <<>>; +topic([Path | TopicPath]) -> + case topic(TopicPath) of + <<>> -> Path; + RemTopic -> + <> + end. diff --git a/apps/emqx_coap/src/emqx_coap_ps_topics.erl b/apps/emqx_coap/src/emqx_coap_ps_topics.erl new file mode 100644 index 000000000..30e9d2623 --- /dev/null +++ b/apps/emqx_coap/src/emqx_coap_ps_topics.erl @@ -0,0 +1,185 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_coap_ps_topics). + +-behaviour(gen_server). + +-include("emqx_coap.hrl"). +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/emqx_mqtt.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-logger_header("[CoAP-PS-TOPICS]"). + +-export([ start_link/0 + , stop/1 + ]). + +-export([ add_topic_info/4 + , delete_topic_info/1 + , delete_sub_topics/1 + , is_topic_existed/1 + , is_topic_timeout/1 + , reset_topic_info/2 + , reset_topic_info/3 + , reset_topic_info/4 + , lookup_topic_info/1 + , lookup_topic_payload/1 + ]). + +%% gen_server. +-export([ init/1 + , handle_call/3 + , handle_cast/2 + , handle_info/2 + , terminate/2 + , code_change/3 + ]). + +-record(state, {}). + +-define(COAP_TOPIC_TABLE, coap_topic). + +%%-------------------------------------------------------------------- +%% API +%%-------------------------------------------------------------------- + +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +stop(Pid) -> + gen_server:stop(Pid). + +add_topic_info(Topic, MaxAge, CT, Payload) when is_binary(Topic), is_integer(MaxAge), is_binary(CT), is_binary(Payload) -> + gen_server:call(?MODULE, {add_topic, {Topic, MaxAge, CT, Payload}}). + +delete_topic_info(Topic) when is_binary(Topic) -> + gen_server:call(?MODULE, {remove_topic, Topic}). + +delete_sub_topics(Topic) when is_binary(Topic) -> + gen_server:cast(?MODULE, {remove_sub_topics, Topic}). + +reset_topic_info(Topic, Payload) -> + gen_server:call(?MODULE, {reset_topic, {Topic, Payload}}). + +reset_topic_info(Topic, MaxAge, Payload) -> + gen_server:call(?MODULE, {reset_topic, {Topic, MaxAge, Payload}}). + +reset_topic_info(Topic, MaxAge, CT, Payload) -> + gen_server:call(?MODULE, {reset_topic, {Topic, MaxAge, CT, Payload}}). + +is_topic_existed(Topic) -> + ets:member(?COAP_TOPIC_TABLE, Topic). + +is_topic_timeout(Topic) when is_binary(Topic) -> + [{Topic, MaxAge, _, _, TimeStamp}] = ets:lookup(?COAP_TOPIC_TABLE, Topic), + %% MaxAge: x seconds + MaxAge < ((erlang:system_time(millisecond) - TimeStamp) / 1000). + +lookup_topic_info(Topic) -> + ets:lookup(?COAP_TOPIC_TABLE, Topic). + +lookup_topic_payload(Topic) -> + try ets:lookup_element(?COAP_TOPIC_TABLE, Topic, 4) + catch + error:badarg -> undefined + end. + +%%-------------------------------------------------------------------- +%% gen_server callbacks +%%-------------------------------------------------------------------- + +init([]) -> + ets:new(?COAP_TOPIC_TABLE, [set, named_table, protected]), + ?LOG(debug, "Create the coap_topic table", []), + {ok, #state{}}. + +handle_call({add_topic, {Topic, MaxAge, CT, Payload}}, _From, State) -> + Ret = create_table_element(Topic, MaxAge, CT, Payload), + {reply, {ok, Ret}, State, hibernate}; + +handle_call({reset_topic, {Topic, Payload}}, _From, State) -> + Ret = update_table_element(Topic, Payload), + {reply, {ok, Ret}, State, hibernate}; + +handle_call({reset_topic, {Topic, MaxAge, Payload}}, _From, State) -> + Ret = update_table_element(Topic, MaxAge, Payload), + {reply, {ok, Ret}, State, hibernate}; + +handle_call({reset_topic, {Topic, MaxAge, CT, Payload}}, _From, State) -> + Ret = update_table_element(Topic, MaxAge, CT, Payload), + {reply, {ok, Ret}, State, hibernate}; + +handle_call({remove_topic, {Topic, _Content}}, _From, State) -> + ets:delete(?COAP_TOPIC_TABLE, Topic), + ?LOG(debug, "Remove topic ~p in the coap_topic table", [Topic]), + {reply, ok, State, hibernate}; + +handle_call(Request, _From, State) -> + ?LOG(error, "adapter unexpected call ~p", [Request]), + {reply, ignored, State, hibernate}. + +handle_cast({remove_sub_topics, TopicPrefix}, State) -> + DeletedTopicNum = ets:foldl(fun ({Topic, _, _, _, _}, AccIn) -> + case binary:match(Topic, TopicPrefix) =/= nomatch of + true -> + ?LOG(debug, "Remove topic ~p in the coap_topic table", [Topic]), + ets:delete(?COAP_TOPIC_TABLE, Topic), + AccIn + 1; + false -> + AccIn + end + end, 0, ?COAP_TOPIC_TABLE), + ?LOG(debug, "Remove number of ~p topics with prefix=~p in the coap_topic table", [DeletedTopicNum, TopicPrefix]), + {noreply, State, hibernate}; + +handle_cast(Msg, State) -> + ?LOG(error, "broker_api unexpected cast ~p", [Msg]), + {noreply, State, hibernate}. + +handle_info(Info, State) -> + ?LOG(error, "adapter unexpected info ~p", [Info]), + {noreply, State, hibernate}. + +terminate(Reason, #state{}) -> + ets:delete(?COAP_TOPIC_TABLE), + ?LOG(error, "the ~p terminate for reason ~p", [?MODULE, Reason]), + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + + +%%-------------------------------------------------------------------- +%% Internal Functions +%%-------------------------------------------------------------------- +create_table_element(Topic, MaxAge, CT, Payload) -> + TopicInfo = {Topic, MaxAge, CT, Payload, erlang:system_time(millisecond)}, + ?LOG(debug, "Insert ~p in the coap_topic table", [TopicInfo]), + ets:insert_new(?COAP_TOPIC_TABLE, TopicInfo). + +update_table_element(Topic, Payload) -> + ?LOG(debug, "Update the topic=~p only with Payload", [Topic]), + ets:update_element(?COAP_TOPIC_TABLE, Topic, [{4, Payload}, {5, erlang:system_time(millisecond)}]). + +update_table_element(Topic, MaxAge, Payload) -> + ?LOG(debug, "Update the topic=~p info of MaxAge=~p and Payload", [Topic, MaxAge]), + ets:update_element(?COAP_TOPIC_TABLE, Topic, [{2, MaxAge}, {4, Payload}, {5, erlang:system_time(millisecond)}]). + +update_table_element(Topic, MaxAge, CT, <<>>) -> + ?LOG(debug, "Update the topic=~p info of MaxAge=~p, CT=~p, payload=<<>>", [Topic, MaxAge, CT]), + ets:update_element(?COAP_TOPIC_TABLE, Topic, [{2, MaxAge}, {3, CT}, {5, erlang:system_time(millisecond)}]). diff --git a/apps/emqx_coap/src/emqx_coap_registry.erl b/apps/emqx_coap/src/emqx_coap_registry.erl new file mode 100644 index 000000000..369bf2787 --- /dev/null +++ b/apps/emqx_coap/src/emqx_coap_registry.erl @@ -0,0 +1,154 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_coap_registry). + +-author("Feng Lee "). + +-include("emqx_coap.hrl"). +-include_lib("emqx/include/logger.hrl"). + +-logger_header("[CoAP-Registry]"). + +-behaviour(gen_server). + +%% API. +-export([ start_link/0 + , register_name/2 + , unregister_name/1 + , whereis_name/1 + , send/2 + , stop/0 + ]). + +%% gen_server. +-export([ init/1 + , handle_call/3 + , handle_cast/2 + , handle_info/2 + , terminate/2 + , code_change/3 + ]). + +-record(state, {}). + +-define(RESPONSE_TAB, coap_response_process). +-define(RESPONSE_REF_TAB, coap_response_process_ref). + +%% ------------------------------------------------------------------ +%% API Function Definitions +%% ------------------------------------------------------------------ + + +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +register_name(Name, Pid) -> + gen_server:call(?MODULE, {register_name, Name, Pid}). + +unregister_name(Name) -> + gen_server:call(?MODULE, {unregister_name, Name}). + +whereis_name(Name) -> + case ets:lookup(?RESPONSE_TAB, Name) of + [] -> undefined; + [{Name, Pid, _MRef}] -> Pid + end. + +send(Name, Msg) -> + case whereis_name(Name) of + undefined -> + exit({badarg, {Name, Msg}}); + Pid when is_pid(Pid) -> + Pid ! Msg, + Pid + end. + +stop() -> + gen_server:stop(?MODULE). + + +%% ------------------------------------------------------------------ +%% gen_server Function Definitions +%% ------------------------------------------------------------------ + +init([]) -> + ets:new(?RESPONSE_TAB, [set, named_table, protected]), + ets:new(?RESPONSE_REF_TAB, [set, named_table, protected]), + {ok, #state{}}. + +handle_call({register_name, Name, Pid}, _From, State) -> + case ets:member(?RESPONSE_TAB, Name) of + false -> + MRef = monitor_client(Pid), + ets:insert(?RESPONSE_TAB, {Name, Pid, MRef}), + ets:insert(?RESPONSE_REF_TAB, {MRef, Name, Pid}), + {reply, yes, State}; + true -> {reply, no, State} + end; + +handle_call({unregister_name, Name}, _From, State) -> + case ets:lookup(?RESPONSE_TAB, Name) of + [] -> + ok; + [{Name, _Pid, MRef}] -> + erase_monitor(MRef), + ets:delete(?RESPONSE_TAB, Name), + ets:delete(?RESPONSE_REF_TAB, MRef) + end, + {reply, ok, State}; + +handle_call(_Request, _From, State) -> + {reply, ignored, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + + +handle_info({'DOWN', MRef, process, DownPid, _Reason}, State) -> + case ets:lookup(?RESPONSE_REF_TAB, MRef) of + [{MRef, Name, _Pid}] -> + ets:delete(?RESPONSE_TAB, Name), + ets:delete(?RESPONSE_REF_TAB, MRef), + erase_monitor(MRef); + [] -> + ?LOG(error, "MRef of client ~p not found", [DownPid]) + end, + {noreply, State}; + + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ets:delete(?RESPONSE_TAB), + ets:delete(?RESPONSE_REF_TAB), + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + + + +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- + +monitor_client(Pid) -> + erlang:monitor(process, Pid). + +erase_monitor(MRef) -> + catch erlang:demonitor(MRef, [flush]). diff --git a/apps/emqx_coap/src/emqx_coap_resource.erl b/apps/emqx_coap/src/emqx_coap_resource.erl new file mode 100644 index 000000000..e11788a04 --- /dev/null +++ b/apps/emqx_coap/src/emqx_coap_resource.erl @@ -0,0 +1,136 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_coap_resource). + +-behaviour(coap_resource). + +-include("emqx_coap.hrl"). +-include_lib("emqx/include/emqx.hrl"). +-include_lib("emqx/include/logger.hrl"). +-include_lib("emqx/include/emqx_mqtt.hrl"). +-include_lib("gen_coap/include/coap.hrl"). + +-logger_header("[CoAP-RES]"). + +-export([ coap_discover/2 + , coap_get/5 + , coap_post/4 + , coap_put/4 + , coap_delete/3 + , coap_observe/5 + , coap_unobserve/1 + , handle_info/2 + , coap_ack/2 + ]). + +-ifdef(TEST). +-export([topic/1]). +-endif. + +-define(MQTT_PREFIX, [<<"mqtt">>]). + +% resource operations +coap_discover(_Prefix, _Args) -> + [{absolute, [<<"mqtt">>], []}]. + +coap_get(ChId, ?MQTT_PREFIX, Path, Query, _Content) -> + ?LOG(debug, "coap_get() Path=~p, Query=~p~n", [Path, Query]), + #coap_mqtt_auth{clientid = Clientid, username = Usr, password = Passwd} = get_auth(Query), + case emqx_coap_mqtt_adapter:client_pid(Clientid, Usr, Passwd, ChId) of + {ok, Pid} -> + put(mqtt_client_pid, Pid), + #coap_content{}; + {error, auth_failure} -> + put(mqtt_client_pid, undefined), + {error, unauthorized}; + {error, bad_request} -> + put(mqtt_client_pid, undefined), + {error, bad_request}; + {error, _Other} -> + put(mqtt_client_pid, undefined), + {error, internal_server_error} + end; +coap_get(ChId, Prefix, Path, Query, _Content) -> + ?LOG(error, "ignore bad get request ChId=~p, Prefix=~p, Path=~p, Query=~p", [ChId, Prefix, Path, Query]), + {error, bad_request}. + +coap_post(_ChId, _Prefix, _Topic, _Content) -> + {error, method_not_allowed}. + +coap_put(_ChId, ?MQTT_PREFIX, Topic, #coap_content{payload = Payload}) when Topic =/= [] -> + ?LOG(debug, "put message, Topic=~p, Payload=~p~n", [Topic, Payload]), + Pid = get(mqtt_client_pid), + emqx_coap_mqtt_adapter:publish(Pid, topic(Topic), Payload), + ok; +coap_put(_ChId, Prefix, Topic, Content) -> + ?LOG(error, "put has error, Prefix=~p, Topic=~p, Content=~p", [Prefix, Topic, Content]), + {error, bad_request}. + +coap_delete(_ChId, _Prefix, _Topic) -> + {error, method_not_allowed}. + +coap_observe(ChId, ?MQTT_PREFIX, Topic, Ack, Content) when Topic =/= [] -> + TrueTopic = topic(Topic), + ?LOG(debug, "observe Topic=~p, Ack=~p", [TrueTopic, Ack]), + Pid = get(mqtt_client_pid), + emqx_coap_mqtt_adapter:subscribe(Pid, TrueTopic), + {ok, {state, ChId, ?MQTT_PREFIX, [TrueTopic]}, content, Content}; +coap_observe(ChId, Prefix, Topic, Ack, _Content) -> + ?LOG(error, "unknown observe request ChId=~p, Prefix=~p, Topic=~p, Ack=~p", [ChId, Prefix, Topic, Ack]), + {error, bad_request}. + +coap_unobserve({state, _ChId, ?MQTT_PREFIX, Topic}) when Topic =/= [] -> + ?LOG(debug, "unobserve ~p", [Topic]), + Pid = get(mqtt_client_pid), + emqx_coap_mqtt_adapter:unsubscribe(Pid, topic(Topic)), + ok; +coap_unobserve({state, ChId, Prefix, Topic}) -> + ?LOG(error, "ignore unknown unobserve request ChId=~p, Prefix=~p, Topic=~p", [ChId, Prefix, Topic]), + ok. + +handle_info({dispatch, Topic, Payload}, State) -> + ?LOG(debug, "dispatch Topic=~p, Payload=~p", [Topic, Payload]), + {notify, [], #coap_content{format = <<"application/octet-stream">>, payload = Payload}, State}; +handle_info(Message, State) -> + emqx_coap_mqtt_adapter:handle_info(Message, State). + +coap_ack(_Ref, State) -> {ok, State}. + +get_auth(Query) -> + get_auth(Query, #coap_mqtt_auth{}). + +get_auth([], Auth=#coap_mqtt_auth{}) -> + Auth; +get_auth([<<$c, $=, Rest/binary>>|T], Auth=#coap_mqtt_auth{}) -> + get_auth(T, Auth#coap_mqtt_auth{clientid = Rest}); +get_auth([<<$u, $=, Rest/binary>>|T], Auth=#coap_mqtt_auth{}) -> + get_auth(T, Auth#coap_mqtt_auth{username = Rest}); +get_auth([<<$p, $=, Rest/binary>>|T], Auth=#coap_mqtt_auth{}) -> + get_auth(T, Auth#coap_mqtt_auth{password = Rest}); +get_auth([Param|T], Auth=#coap_mqtt_auth{}) -> + ?LOG(error, "ignore unknown parameter ~p", [Param]), + get_auth(T, Auth). + +topic(Topic) when is_binary(Topic) -> Topic; +topic([]) -> <<>>; +topic([Path | TopicPath]) -> + case topic(TopicPath) of + <<>> -> Path; + RemTopic -> + <> + end. + diff --git a/apps/emqx_coap/src/emqx_coap_server.erl b/apps/emqx_coap/src/emqx_coap_server.erl new file mode 100644 index 000000000..d3ed1a96e --- /dev/null +++ b/apps/emqx_coap/src/emqx_coap_server.erl @@ -0,0 +1,100 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_coap_server). + +-include("emqx_coap.hrl"). + +-export([ start/1 + , stop/1 + ]). + +%%-------------------------------------------------------------------- +%% APIs +%%-------------------------------------------------------------------- + +start(Envs) -> + {ok, _} = application:ensure_all_started(gen_coap), + start_listeners(Envs). + +stop(Envs) -> + stop_listeners(Envs). + +%%-------------------------------------------------------------------- +%% Internal funcs +%%-------------------------------------------------------------------- + +start_listeners(Envs) -> + lists:foreach(fun start_listener/1, listeners_confs(Envs)). + +stop_listeners(Envs) -> + lists:foreach(fun stop_listener/1, listeners_confs(Envs)). + +start_listener({Proto, ListenOn, Opts}) -> + case start_listener(Proto, ListenOn, Opts) of + {ok, _Pid} -> + io:format("Start coap:~s listener on ~s successfully.~n", + [Proto, format(ListenOn)]); + {error, Reason} -> + io:format(standard_error, "Failed to start coap:~s listener on ~s - ~0p~n!", + [Proto, format(ListenOn), Reason]), + error(Reason) + end. + +start_listener(udp, ListenOn, Opts) -> + coap_server:start_udp('coap:udp', ListenOn, Opts); +start_listener(dtls, ListenOn, Opts) -> + coap_server:start_dtls('coap:dtls', ListenOn, Opts). + +stop_listener({Proto, ListenOn, _Opts}) -> + Ret = stop_listener(Proto, ListenOn), + case Ret of + ok -> io:format("Stop coap:~s listener on ~s successfully.~n", + [Proto, format(ListenOn)]); + {error, Reason} -> + io:format(standard_error, "Failed to stop coap:~s listener on ~s - ~p~n.", + [Proto, format(ListenOn), Reason]) + end, + Ret. + +stop_listener(udp, ListenOn) -> + coap_server:stop_udp('coap:udp', ListenOn); +stop_listener(dtls, ListenOn) -> + coap_server:stop_dtls('coap:dtls', ListenOn). + +%% XXX: It is a temporary func to convert conf format for esockd +listeners_confs(Envs) -> + listeners_confs(udp, Envs) ++ listeners_confs(dtls, Envs). + +listeners_confs(udp, Envs) -> + Udps = proplists:get_value(bind_udp, Envs, []), + [{udp, Port, [{udp_options, InetOpts}]} || {Port, InetOpts} <- Udps]; + +listeners_confs(dtls, Envs) -> + case proplists:get_value(dtls_opts, Envs, []) of + [] -> []; + DtlsOpts -> + BindDtls = proplists:get_value(bind_dtls, Envs, []), + [{dtls, Port, [{dtls_options, InetOpts ++ DtlsOpts}]} || {Port, InetOpts} <- BindDtls] + end. + +format(Port) when is_integer(Port) -> + io_lib:format("0.0.0.0:~w", [Port]); +format({Addr, Port}) when is_list(Addr) -> + io_lib:format("~s:~w", [Addr, Port]); +format({Addr, Port}) when is_tuple(Addr) -> + io_lib:format("~s:~w", [inet:ntoa(Addr), Port]). + diff --git a/apps/emqx_coap/src/emqx_coap_sup.erl b/apps/emqx_coap/src/emqx_coap_sup.erl new file mode 100644 index 000000000..a3a0fdc53 --- /dev/null +++ b/apps/emqx_coap/src/emqx_coap_sup.erl @@ -0,0 +1,42 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_coap_sup). + +-behaviour(supervisor). + +-export([ start_link/0 + , init/1 + ]). + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init(_Args) -> + Registry = #{id => emqx_coap_registry, + start => {emqx_coap_registry, start_link, []}, + restart => permanent, + shutdown => 5000, + type => worker, + modules => [emqx_coap_registry]}, + PsTopics = #{id => emqx_coap_ps_topics, + start => {emqx_coap_ps_topics, start_link, []}, + restart => permanent, + shutdown => 5000, + type => worker, + modules => [emqx_coap_ps_topics]}, + {ok, {{one_for_all, 10, 3600}, [Registry, PsTopics]}}. + diff --git a/apps/emqx_coap/src/emqx_coap_timer.erl b/apps/emqx_coap/src/emqx_coap_timer.erl new file mode 100644 index 000000000..3924ba239 --- /dev/null +++ b/apps/emqx_coap/src/emqx_coap_timer.erl @@ -0,0 +1,59 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_coap_timer). + +-include("emqx_coap.hrl"). + +-export([ cancel_timer/1 + , start_timer/2 + , restart_timer/1 + , kick_timer/1 + , is_timeout/1 + , get_timer_length/1 + ]). + +-record(timer_state, {interval, kickme, tref, message}). + +-define(LOG(Level, Format, Args), + emqx_logger:Level("CoAP-Timer: " ++ Format, Args)). + +cancel_timer(#timer_state{tref = TRef}) when is_reference(TRef) -> + catch erlang:cancel_timer(TRef), + ok; +cancel_timer(_) -> + ok. + +kick_timer(State=#timer_state{kickme = false}) -> + State#timer_state{kickme = true}; +kick_timer(State=#timer_state{kickme = true}) -> + State. + +start_timer(Sec, Msg) -> + ?LOG(debug, "emqx_coap_timer:start_timer ~p", [Sec]), + TRef = erlang:send_after(timer:seconds(Sec), self(), Msg), + #timer_state{interval = Sec, kickme = false, tref = TRef, message = Msg}. + +restart_timer(State=#timer_state{interval = Sec, message = Msg}) -> + ?LOG(debug, "emqx_coap_timer:restart_timer ~p", [Sec]), + TRef = erlang:send_after(timer:seconds(Sec), self(), Msg), + State#timer_state{kickme = false, tref = TRef}. + +is_timeout(#timer_state{kickme = Bool}) -> + not Bool. + +get_timer_length(#timer_state{interval = Interval}) -> + Interval. diff --git a/apps/emqx_coap/test/emqx_coap_SUITE.erl b/apps/emqx_coap/test/emqx_coap_SUITE.erl new file mode 100644 index 000000000..866e2e203 --- /dev/null +++ b/apps/emqx_coap/test/emqx_coap_SUITE.erl @@ -0,0 +1,240 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_coap_SUITE). + +-compile(export_all). +-compile(nowarn_export_all). + +-include_lib("gen_coap/include/coap.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("emqx/include/emqx.hrl"). + +-define(LOGT(Format, Args), ct:pal(Format, Args)). + +all() -> emqx_ct:all(?MODULE). + +init_per_suite(Config) -> + emqx_ct_helpers:start_apps([emqx_coap], fun set_sepecial_cfg/1), + Config. + +set_sepecial_cfg(emqx_coap) -> + Opts = application:get_env(emqx_coap, dtls_opts,[]), + Opts2 = [{keyfile, emqx_ct_helpers:deps_path(emqx, "etc/certs/key.pem")}, + {certfile, emqx_ct_helpers:deps_path(emqx, "etc/certs/cert.pem")}], + application:set_env(emqx_coap, dtls_opts, emqx_misc:merge_opts(Opts, Opts2)), + application:set_env(emqx_coap, enable_stats, true); +set_sepecial_cfg(_) -> + ok. + +end_per_suite(Config) -> + emqx_ct_helpers:stop_apps([emqx_coap]), + Config. + +%%-------------------------------------------------------------------- +%% Test Cases +%%-------------------------------------------------------------------- + +t_publish(_Config) -> + Topic = <<"abc">>, Payload = <<"123">>, + TopicStr = binary_to_list(Topic), + URI = "coap://127.0.0.1/mqtt/"++TopicStr++"?c=client1&u=tom&p=secret", + + %% Sub topic first + emqx:subscribe(Topic), + + Reply = er_coap_client:request(put, URI, #coap_content{format = <<"application/octet-stream">>, payload = Payload}), + {ok, changed, _} = Reply, + + receive + {deliver, Topic, Msg} -> + ?assertEqual(Topic, Msg#message.topic), + ?assertEqual(Payload, Msg#message.payload) + after + 500 -> + ?assert(false) + end. + +t_observe(_Config) -> + Topic = <<"abc">>, TopicStr = binary_to_list(Topic), + Payload = <<"123">>, + Uri = "coap://127.0.0.1/mqtt/"++TopicStr++"?c=client1&u=tom&p=secret", + {ok, Pid, N, Code, Content} = er_coap_observer:observe(Uri), + ?LOGT("observer Pid=~p, N=~p, Code=~p, Content=~p", [Pid, N, Code, Content]), + + [SubPid] = emqx:subscribers(Topic), + ?assert(is_pid(SubPid)), + + %% Publish a message + emqx:publish(emqx_message:make(Topic, Payload)), + + Notif = receive_notification(), + ?LOGT("observer get Notif=~p", [Notif]), + {coap_notify, _, _, {ok,content}, #coap_content{payload = PayloadRecv}} = Notif, + ?_assertEqual(Payload, PayloadRecv), + + er_coap_observer:stop(Pid), + timer:sleep(100), + + [] = emqx:subscribers(Topic). + +t_observe_wildcard(_Config) -> + Topic = <<"+/b">>, TopicStr = http_uri:encode(binary_to_list(Topic)), + Payload = <<"123">>, + Uri = "coap://127.0.0.1/mqtt/"++TopicStr++"?c=client1&u=tom&p=secret", + {ok, Pid, N, Code, Content} = er_coap_observer:observe(Uri), + ?LOGT("observer Uri=~p, Pid=~p, N=~p, Code=~p, Content=~p", [Uri, Pid, N, Code, Content]), + + [SubPid] = emqx:subscribers(Topic), + ?assert(is_pid(SubPid)), + + %% Publish a message + emqx:publish(emqx_message:make(Topic, Payload)), + + Notif = receive_notification(), + ?LOGT("observer get Notif=~p", [Notif]), + {coap_notify, _, _, {ok,content}, #coap_content{payload = PayloadRecv}} = Notif, + ?_assertEqual(Payload, PayloadRecv), + + er_coap_observer:stop(Pid), + timer:sleep(100), + + [] = emqx:subscribers(Topic). + +t_observe_pub(_Config) -> + Topic = <<"+/b">>, TopicStr = http_uri:encode(binary_to_list(Topic)), + Uri = "coap://127.0.0.1/mqtt/"++TopicStr++"?c=client1&u=tom&p=secret", + {ok, Pid, N, Code, Content} = er_coap_observer:observe(Uri), + ?LOGT("observer Pid=~p, N=~p, Code=~p, Content=~p", [Pid, N, Code, Content]), + + [SubPid] = emqx:subscribers(Topic), + ?assert(is_pid(SubPid)), + + Topic2 = <<"a/b">>, Payload2 = <<"UFO">>, + TopicStr2 = http_uri:encode(binary_to_list(Topic2)), + URI2 = "coap://127.0.0.1/mqtt/"++TopicStr2++"?c=client1&u=tom&p=secret", + + Reply2 = er_coap_client:request(put, URI2, #coap_content{format = <<"application/octet-stream">>, payload = Payload2}), + {ok,changed, _} = Reply2, + + Notif2 = receive_notification(), + ?LOGT("observer get Notif2=~p", [Notif2]), + {coap_notify, _, _, {ok,content}, #coap_content{payload = PayloadRecv2}} = Notif2, + ?_assertEqual(Payload2, PayloadRecv2), + + Topic3 = <<"j/b">>, Payload3 = <<"ET629">>, + TopicStr3 = http_uri:encode(binary_to_list(Topic3)), + URI3 = "coap://127.0.0.1/mqtt/"++TopicStr3++"?c=client2&u=mike&p=guess", + Reply3 = er_coap_client:request(put, URI3, #coap_content{format = <<"application/octet-stream">>, payload = Payload3}), + {ok,changed, _} = Reply3, + + Notif3 = receive_notification(), + ?LOGT("observer get Notif3=~p", [Notif3]), + {coap_notify, _, _, {ok,content}, #coap_content{payload = PayloadRecv3}} = Notif3, + ?_assertEqual(Payload3, PayloadRecv3), + + er_coap_observer:stop(Pid). + +t_one_clientid_sub_2_topics(_Config) -> + Topic1 = <<"abc">>, TopicStr1 = binary_to_list(Topic1), + Payload1 = <<"123">>, + Uri1 = "coap://127.0.0.1/mqtt/"++TopicStr1++"?c=client1&u=tom&p=secret", + {ok, Pid1, N1, Code1, Content1} = er_coap_observer:observe(Uri1), + ?LOGT("observer 1 Pid=~p, N=~p, Code=~p, Content=~p", [Pid1, N1, Code1, Content1]), + + [SubPid] = emqx:subscribers(Topic1), + ?assert(is_pid(SubPid)), + + Topic2 = <<"x/y">>, TopicStr2 = http_uri:encode(binary_to_list(Topic2)), + Payload2 = <<"456">>, + Uri2 = "coap://127.0.0.1/mqtt/"++TopicStr2++"?c=client1&u=tom&p=secret", + {ok, Pid2, N2, Code2, Content2} = er_coap_observer:observe(Uri2), + ?LOGT("observer 2 Pid=~p, N=~p, Code=~p, Content=~p", [Pid2, N2, Code2, Content2]), + + [SubPid] = emqx:subscribers(Topic2), + ?assert(is_pid(SubPid)), + + emqx:publish(emqx_message:make(Topic1, Payload1)), + + Notif1 = receive_notification(), + ?LOGT("observer 1 get Notif=~p", [Notif1]), + {coap_notify, _, _, {ok,content}, #coap_content{payload = PayloadRecv1}} = Notif1, + ?_assertEqual(Payload1, PayloadRecv1), + + emqx:publish(emqx_message:make(Topic2, Payload2)), + + Notif2 = receive_notification(), + ?LOGT("observer 2 get Notif=~p", [Notif2]), + {coap_notify, _, _, {ok,content}, #coap_content{payload = PayloadRecv2}} = Notif2, + ?_assertEqual(Payload2, PayloadRecv2), + + er_coap_observer:stop(Pid1), + er_coap_observer:stop(Pid2). + +t_invalid_parameter(_Config) -> + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %% "cid=client2" is invaid + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Topic3 = <<"a/b">>, Payload3 = <<"ET629">>, + TopicStr3 = http_uri:encode(binary_to_list(Topic3)), + URI3 = "coap://127.0.0.1/mqtt/"++TopicStr3++"?cid=client2&u=tom&p=simple", + Reply3 = er_coap_client:request(put, URI3, #coap_content{format = <<"application/octet-stream">>, payload = Payload3}), + ?assertMatch({error,bad_request}, Reply3), + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %% "what=hello" is invaid + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + URI4 = "coap://127.0.0.1/mqtt/"++TopicStr3++"?what=hello", + Reply4 = er_coap_client:request(put, URI4, #coap_content{format = <<"application/octet-stream">>, payload = Payload3}), + ?assertMatch({error, bad_request}, Reply4). + +t_invalid_topic(_Config) -> + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %% "a/b" is a valid topic string + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Topic3 = <<"a/b">>, Payload3 = <<"ET629">>, + TopicStr3 = binary_to_list(Topic3), + URI3 = "coap://127.0.0.1/mqtt/"++TopicStr3++"?c=client2&u=tom&p=simple", + Reply3 = er_coap_client:request(put, URI3, #coap_content{format = <<"application/octet-stream">>, payload = Payload3}), + ?assertMatch({ok,changed,_Content}, Reply3), + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %% "+?#" is invaid topic string + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + URI4 = "coap://127.0.0.1/mqtt/"++"+?#"++"?what=hello", + Reply4 = er_coap_client:request(put, URI4, #coap_content{format = <<"application/octet-stream">>, payload = Payload3}), + ?assertMatch({error,bad_request}, Reply4). + +t_stats(_) -> + ok. + +t_auth_failure(_) -> + ok. + +t_qos_supprot(_) -> + ok. + +%%-------------------------------------------------------------------- +%% Helpers + +receive_notification() -> + receive + {coap_notify, Pid, N2, Code2, Content2} -> + {coap_notify, Pid, N2, Code2, Content2} + after 2000 -> + receive_notification_timeout + end. + diff --git a/apps/emqx_coap/test/emqx_coap_ps_SUITE.erl b/apps/emqx_coap/test/emqx_coap_ps_SUITE.erl new file mode 100644 index 000000000..0a1cc3860 --- /dev/null +++ b/apps/emqx_coap/test/emqx_coap_ps_SUITE.erl @@ -0,0 +1,677 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_coap_ps_SUITE). + +-compile(export_all). +-compile(nowarn_export_all). + +-include_lib("gen_coap/include/coap.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("emqx/include/emqx.hrl"). + +-define(LOGT(Format, Args), ct:pal(Format, Args)). + +all() -> emqx_ct:all(?MODULE). + +init_per_suite(Config) -> + emqx_ct_helpers:start_apps([emqx_coap], fun set_sepecial_cfg/1), + Config. + +set_sepecial_cfg(emqx_coap) -> + application:set_env(emqx_coap, enable_stats, true); +set_sepecial_cfg(_) -> + ok. + +end_per_suite(Config) -> + emqx_ct_helpers:stop_apps([emqx_coap]), + Config. + +%%-------------------------------------------------------------------- +%% Test Cases +%%-------------------------------------------------------------------- + +t_update_max_age(_Config) -> + TopicInPayload = <<"topic1">>, + Payload = <<";ct=42">>, + Payload1 = <<";ct=50">>, + URI = "coap://127.0.0.1/ps/"++"?c=client1&u=tom&p=secret", + URI2 = "coap://127.0.0.1/ps/topic1"++"?c=client1&u=tom&p=secret", + Reply = er_coap_client:request(post, URI, #coap_content{format = <<"application/link-format">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = LocPath}} = Reply, + ?assertEqual([<<"/ps/topic1">>] ,LocPath), + TopicInfo = [{TopicInPayload, MaxAge1, CT1, _ResPayload, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(TopicInPayload), + ?LOGT("lookup topic info=~p", [TopicInfo]), + ?assertEqual(60, MaxAge1), + ?assertEqual(<<"42">>, CT1), + + timer:sleep(50), + + %% post to create the same topic but with different max age and ct value in payload + Reply1 = er_coap_client:request(post, URI, #coap_content{max_age = 70, format = <<"application/link-format">>, payload = Payload1}), + {ok,created, #coap_content{location_path = LocPath}} = Reply1, + ?assertEqual([<<"/ps/topic1">>] ,LocPath), + [{TopicInPayload, MaxAge2, CT2, _ResPayload, _TimeStamp1}] = emqx_coap_ps_topics:lookup_topic_info(TopicInPayload), + ?assertEqual(70, MaxAge2), + ?assertEqual(<<"50">>, CT2), + + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, URI2). + +t_create_subtopic(_Config) -> + TopicInPayload = <<"topic1">>, + TopicInPayloadStr = "topic1", + Payload = <<";ct=42">>, + URI = "coap://127.0.0.1/ps/"++"?c=client1&u=tom&p=secret", + RealURI = "coap://127.0.0.1/ps/topic1"++"?c=client1&u=tom&p=secret", + + Reply = er_coap_client:request(post, URI, #coap_content{format = <<"application/link-format">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = LocPath}} = Reply, + ?assertEqual([<<"/ps/topic1">>] ,LocPath), + TopicInfo = [{TopicInPayload, MaxAge1, CT1, _ResPayload, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(TopicInPayload), + ?LOGT("lookup topic info=~p", [TopicInfo]), + ?assertEqual(60, MaxAge1), + ?assertEqual(<<"42">>, CT1), + + timer:sleep(50), + + %% post to create the a sub topic + SubPayload = <<";ct=42">>, + SubTopicInPayloadStr = "subtopic", + SubURI = "coap://127.0.0.1/ps/"++TopicInPayloadStr++"?c=client1&u=tom&p=secret", + SubRealURI = "coap://127.0.0.1/ps/"++TopicInPayloadStr++"/"++SubTopicInPayloadStr++"?c=client1&u=tom&p=secret", + FullTopic = list_to_binary(TopicInPayloadStr++"/"++SubTopicInPayloadStr), + Reply1 = er_coap_client:request(post, SubURI, #coap_content{format = <<"application/link-format">>, payload = SubPayload}), + ?LOGT("Reply =~p", [Reply1]), + {ok,created, #coap_content{location_path = LocPath1}} = Reply1, + ?assertEqual([<<"/ps/topic1/subtopic">>] ,LocPath1), + [{FullTopic, MaxAge2, CT2, _ResPayload, _}] = emqx_coap_ps_topics:lookup_topic_info(FullTopic), + ?assertEqual(60, MaxAge2), + ?assertEqual(<<"42">>, CT2), + + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, SubRealURI), + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, RealURI). + +t_over_max_age(_Config) -> + TopicInPayload = <<"topic1">>, + Payload = <<";ct=42">>, + URI = "coap://127.0.0.1/ps/"++"?c=client1&u=tom&p=secret", + Reply = er_coap_client:request(post, URI, #coap_content{max_age = 2, format = <<"application/link-format">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = LocPath}} = Reply, + ?assertEqual([<<"/ps/topic1">>] ,LocPath), + TopicInfo = [{TopicInPayload, MaxAge1, CT1, _ResPayload, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(TopicInPayload), + ?LOGT("lookup topic info=~p", [TopicInfo]), + ?assertEqual(2, MaxAge1), + ?assertEqual(<<"42">>, CT1), + + timer:sleep(3000), + ?assertEqual(true, emqx_coap_ps_topics:is_topic_timeout(TopicInPayload)). + +t_refreash_max_age(_Config) -> + TopicInPayload = <<"topic1">>, + Payload = <<";ct=42">>, + Payload1 = <<";ct=50">>, + URI = "coap://127.0.0.1/ps/"++"?c=client1&u=tom&p=secret", + RealURI = "coap://127.0.0.1/ps/topic1"++"?c=client1&u=tom&p=secret", + Reply = er_coap_client:request(post, URI, #coap_content{max_age = 5, format = <<"application/link-format">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = LocPath}} = Reply, + ?assertEqual([<<"/ps/topic1">>] ,LocPath), + TopicInfo = [{TopicInPayload, MaxAge1, CT1, _ResPayload, TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(TopicInPayload), + ?LOGT("lookup topic info=~p", [TopicInfo]), + ?LOGT("TimeStamp=~p", [TimeStamp]), + ?assertEqual(5, MaxAge1), + ?assertEqual(<<"42">>, CT1), + + timer:sleep(3000), + + %% post to create the same topic, the max age timer will be restarted with the new max age value + Reply1 = er_coap_client:request(post, URI, #coap_content{max_age = 5, format = <<"application/link-format">>, payload = Payload1}), + {ok,created, #coap_content{location_path = LocPath}} = Reply1, + ?assertEqual([<<"/ps/topic1">>] ,LocPath), + [{TopicInPayload, MaxAge2, CT2, _ResPayload, TimeStamp1}] = emqx_coap_ps_topics:lookup_topic_info(TopicInPayload), + ?LOGT("TimeStamp1=~p", [TimeStamp1]), + ?assertEqual(5, MaxAge2), + ?assertEqual(<<"50">>, CT2), + + timer:sleep(3000), + ?assertEqual(false, emqx_coap_ps_topics:is_topic_timeout(TopicInPayload)), + + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, RealURI). + +t_case01_publish_post(_Config) -> + timer:sleep(100), + MainTopic = <<"maintopic">>, + TopicInPayload = <<"topic1">>, + Payload = <<";ct=42">>, + MainTopicStr = binary_to_list(MainTopic), + + %% post to create topic maintopic/topic1 + URI1 = "coap://127.0.0.1/ps/"++MainTopicStr++"?c=client1&u=tom&p=secret", + FullTopic = list_to_binary(MainTopicStr++"/"++binary_to_list(TopicInPayload)), + Reply1 = er_coap_client:request(post, URI1, #coap_content{format = <<"application/link-format">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply1]), + {ok,created, #coap_content{location_path = LocPath1}} = Reply1, + ?assertEqual([<<"/ps/maintopic/topic1">>] ,LocPath1), + [{FullTopic, MaxAge, CT2, <<>>, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(FullTopic), + ?assertEqual(60, MaxAge), + ?assertEqual(<<"42">>, CT2), + + %% post to publish message to topic maintopic/topic1 + FullTopicStr = http_uri:encode(binary_to_list(FullTopic)), + URI2 = "coap://127.0.0.1/ps/"++FullTopicStr++"?c=client1&u=tom&p=secret", + PubPayload = <<"PUBLISH">>, + + %% Sub topic first + emqx:subscribe(FullTopic), + + Reply2 = er_coap_client:request(post, URI2, #coap_content{format = <<"application/octet-stream">>, payload = PubPayload}), + ?LOGT("Reply =~p", [Reply2]), + {ok,changed, _} = Reply2, + TopicInfo = [{FullTopic, MaxAge, CT2, PubPayload, _TimeStamp1}] = emqx_coap_ps_topics:lookup_topic_info(FullTopic), + ?LOGT("the topic info =~p", [TopicInfo]), + + assert_recv(FullTopic, PubPayload), + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, URI2). + +t_case02_publish_post(_Config) -> + Topic = <<"topic1">>, + TopicStr = binary_to_list(Topic), + Payload = <<"payload">>, + + %% Sub topic first + emqx:subscribe(Topic), + + %% post to publish a new topic "topic1", and the topic is created + URI = "coap://127.0.0.1/ps/"++TopicStr++"?c=client1&u=tom&p=secret", + Reply = er_coap_client:request(post, URI, #coap_content{format = <<"application/octet-stream">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = LocPath}} = Reply, + ?assertEqual([<<"/ps/topic1">>] ,LocPath), + [{Topic, MaxAge, CT, Payload, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(Topic), + ?assertEqual(60, MaxAge), + ?assertEqual(<<"42">>, CT), + + assert_recv(Topic, Payload), + + %% post to publish a new message to the same topic "topic1" with different payload + NewPayload = <<"newpayload">>, + Reply1 = er_coap_client:request(post, URI, #coap_content{format = <<"application/octet-stream">>, payload = NewPayload}), + ?LOGT("Reply =~p", [Reply1]), + {ok,changed, _} = Reply1, + [{Topic, MaxAge, CT, NewPayload, _TimeStamp1}] = emqx_coap_ps_topics:lookup_topic_info(Topic), + + assert_recv(Topic, NewPayload), + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, URI). + +t_case03_publish_post(_Config) -> + Topic = <<"topic1">>, + TopicStr = binary_to_list(Topic), + Payload = <<"payload">>, + + %% Sub topic first + emqx:subscribe(Topic), + + %% post to publish a new topic "topic1", and the topic is created + URI = "coap://127.0.0.1/ps/"++TopicStr++"?c=client1&u=tom&p=secret", + Reply = er_coap_client:request(post, URI, #coap_content{format = <<"application/octet-stream">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = LocPath}} = Reply, + ?assertEqual([<<"/ps/topic1">>] ,LocPath), + [{Topic, MaxAge, CT, Payload, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(Topic), + ?assertEqual(60, MaxAge), + ?assertEqual(<<"42">>, CT), + + assert_recv(Topic, Payload), + + %% post to publish a new message to the same topic "topic1", but the ct is not same as created + NewPayload = <<"newpayload">>, + Reply1 = er_coap_client:request(post, URI, #coap_content{format = <<"application/exi">>, payload = NewPayload}), + ?LOGT("Reply =~p", [Reply1]), + ?assertEqual({error,bad_request}, Reply1), + + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, URI). + +t_case04_publish_post(_Config) -> + Topic = <<"topic1">>, + TopicStr = binary_to_list(Topic), + Payload = <<"payload">>, + + %% post to publish a new topic "topic1", and the topic is created + URI = "coap://127.0.0.1/ps/"++TopicStr++"?c=client1&u=tom&p=secret", + Reply = er_coap_client:request(post, URI, #coap_content{max_age = 5, format = <<"application/octet-stream">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = LocPath}} = Reply, + ?assertEqual([<<"/ps/topic1">>] ,LocPath), + [{Topic, MaxAge, CT, Payload, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(Topic), + ?assertEqual(5, MaxAge), + ?assertEqual(<<"42">>, CT), + + %% after max age timeout, the topic still exists but the status is timeout + timer:sleep(6000), + ?assertEqual(true, emqx_coap_ps_topics:is_topic_timeout(Topic)), + + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, URI). + +t_case01_publish_put(_Config) -> + MainTopic = <<"maintopic">>, + TopicInPayload = <<"topic1">>, + Payload = <<";ct=42">>, + MainTopicStr = binary_to_list(MainTopic), + + %% post to create topic maintopic/topic1 + URI1 = "coap://127.0.0.1/ps/"++MainTopicStr++"?c=client1&u=tom&p=secret", + FullTopic = list_to_binary(MainTopicStr++"/"++binary_to_list(TopicInPayload)), + Reply1 = er_coap_client:request(post, URI1, #coap_content{format = <<"application/link-format">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply1]), + {ok,created, #coap_content{location_path = LocPath1}} = Reply1, + ?assertEqual([<<"/ps/maintopic/topic1">>] ,LocPath1), + [{FullTopic, MaxAge, CT2, <<>>, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(FullTopic), + ?assertEqual(60, MaxAge), + ?assertEqual(<<"42">>, CT2), + + %% put to publish message to topic maintopic/topic1 + FullTopicStr = http_uri:encode(binary_to_list(FullTopic)), + URI2 = "coap://127.0.0.1/ps/"++FullTopicStr++"?c=client1&u=tom&p=secret", + PubPayload = <<"PUBLISH">>, + + %% Sub topic first + emqx:subscribe(FullTopic), + + Reply2 = er_coap_client:request(put, URI2, #coap_content{format = <<"application/octet-stream">>, payload = PubPayload}), + ?LOGT("Reply =~p", [Reply2]), + {ok,changed, _} = Reply2, + [{FullTopic, MaxAge, CT2, PubPayload, _TimeStamp1}] = emqx_coap_ps_topics:lookup_topic_info(FullTopic), + + assert_recv(FullTopic, PubPayload), + + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, URI2). + +t_case02_publish_put(_Config) -> + Topic = <<"topic1">>, + TopicStr = binary_to_list(Topic), + Payload = <<"payload">>, + + %% Sub topic first + emqx:subscribe(Topic), + + %% put to publish a new topic "topic1", and the topic is created + URI = "coap://127.0.0.1/ps/"++TopicStr++"?c=client1&u=tom&p=secret", + Reply = er_coap_client:request(put, URI, #coap_content{format = <<"application/octet-stream">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = LocPath}} = Reply, + ?assertEqual([<<"/ps/topic1">>] ,LocPath), + [{Topic, MaxAge, CT, Payload, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(Topic), + ?assertEqual(60, MaxAge), + ?assertEqual(<<"42">>, CT), + + assert_recv(Topic, Payload), + + %% put to publish a new message to the same topic "topic1" with different payload + NewPayload = <<"newpayload">>, + Reply1 = er_coap_client:request(put, URI, #coap_content{format = <<"application/octet-stream">>, payload = NewPayload}), + ?LOGT("Reply =~p", [Reply1]), + {ok,changed, _} = Reply1, + [{Topic, MaxAge, CT, NewPayload, _TimeStamp1}] = emqx_coap_ps_topics:lookup_topic_info(Topic), + + assert_recv(Topic, NewPayload), + + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, URI). + +t_case03_publish_put(_Config) -> + Topic = <<"topic1">>, + TopicStr = binary_to_list(Topic), + Payload = <<"payload">>, + + %% Sub topic first + emqx:subscribe(Topic), + + %% put to publish a new topic "topic1", and the topic is created + URI = "coap://127.0.0.1/ps/"++TopicStr++"?c=client1&u=tom&p=secret", + Reply = er_coap_client:request(put, URI, #coap_content{format = <<"application/octet-stream">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = LocPath}} = Reply, + ?assertEqual([<<"/ps/topic1">>] ,LocPath), + [{Topic, MaxAge, CT, Payload, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(Topic), + ?assertEqual(60, MaxAge), + ?assertEqual(<<"42">>, CT), + + assert_recv(Topic, Payload), + + %% put to publish a new message to the same topic "topic1", but the ct is not same as created + NewPayload = <<"newpayload">>, + Reply1 = er_coap_client:request(put, URI, #coap_content{format = <<"application/exi">>, payload = NewPayload}), + ?LOGT("Reply =~p", [Reply1]), + ?assertEqual({error,bad_request}, Reply1), + + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, URI). + +t_case04_publish_put(_Config) -> + Topic = <<"topic1">>, + TopicStr = binary_to_list(Topic), + Payload = <<"payload">>, + + %% put to publish a new topic "topic1", and the topic is created + URI = "coap://127.0.0.1/ps/"++TopicStr++"?c=client1&u=tom&p=secret", + Reply = er_coap_client:request(put, URI, #coap_content{max_age = 5, format = <<"application/octet-stream">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = LocPath}} = Reply, + ?assertEqual([<<"/ps/topic1">>] ,LocPath), + [{Topic, MaxAge, CT, Payload, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(Topic), + ?assertEqual(5, MaxAge), + ?assertEqual(<<"42">>, CT), + + %% after max age timeout, no publish message to the same topic, the topic info will be deleted + %%%%%%%%%%%%%%%%%%%%%%%%%% + % but there is one thing to do is we don't count in the publish message received from emqx(from other node).TBD!!!!!!!!!!!!! + %%%%%%%%%%%%%%%%%%%%%%%%%% + timer:sleep(6000), + ?assertEqual(true, emqx_coap_ps_topics:is_topic_timeout(Topic)), + + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, URI). + +t_case01_subscribe(_Config) -> + Topic = <<"topic1">>, + Payload1 = <<";ct=42">>, + timer:sleep(100), + + %% First post to create a topic "topic1" + Uri = "coap://127.0.0.1/ps/"++"?c=client1&u=tom&p=secret", + Reply = er_coap_client:request(post, Uri, #coap_content{format = <<"application/link-format">>, payload = Payload1}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = [LocPath]}} = Reply, + ?assertEqual(<<"/ps/topic1">> ,LocPath), + TopicInfo = [{Topic, MaxAge1, CT1, _ResPayload, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(Topic), + ?LOGT("lookup topic info=~p", [TopicInfo]), + ?assertEqual(60, MaxAge1), + ?assertEqual(<<"42">>, CT1), + + %% Subscribe the topic + Uri1 = "coap://127.0.0.1"++binary_to_list(LocPath)++"?c=client1&u=tom&p=secret", + {ok, Pid, N, Code, Content} = er_coap_observer:observe(Uri1), + ?LOGT("observer Pid=~p, N=~p, Code=~p, Content=~p", [Pid, N, Code, Content]), + + [SubPid] = emqx:subscribers(Topic), + ?assert(is_pid(SubPid)), + + %% Publish a message + Payload = <<"123">>, + emqx:publish(emqx_message:make(Topic, Payload)), + + Notif = receive_notification(), + ?LOGT("observer get Notif=~p", [Notif]), + {coap_notify, _, _, {ok,content}, #coap_content{payload = PayloadRecv}} = Notif, + + ?_assertEqual(Payload, PayloadRecv), + + %% GET to read the publish message of the topic + Reply1 = er_coap_client:request(get, Uri1), + ?LOGT("Reply=~p", [Reply1]), + {ok,content, #coap_content{max_age = MaxAgeLeft,payload = <<"123">>}} = Reply1, + ?_assertEqual(true, MaxAgeLeft<60), + + er_coap_observer:stop(Pid), + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, Uri1). + +t_case02_subscribe(_Config) -> + Topic = <<"a/b">>, + TopicStr = binary_to_list(Topic), + PercentEncodedTopic = http_uri:encode(TopicStr), + Payload = <<"payload">>, + + %% post to publish a new topic "a/b", and the topic is created + URI = "coap://127.0.0.1/ps/"++PercentEncodedTopic++"?c=client1&u=tom&p=secret", + Reply = er_coap_client:request(post, URI, #coap_content{max_age = 5, format = <<"application/octet-stream">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = LocPath}} = Reply, + ?assertEqual([<<"/ps/a/b">>] ,LocPath), + [{Topic, MaxAge, CT, Payload, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(Topic), + ?assertEqual(5, MaxAge), + ?assertEqual(<<"42">>, CT), + + %% Wait for the max age of the timer expires + timer:sleep(6000), + ?assertEqual(true, emqx_coap_ps_topics:is_topic_timeout(Topic)), + + %% Subscribe to the timeout topic "a/b", still successfully,got {ok, nocontent} Method + Uri = "coap://127.0.0.1/ps/"++PercentEncodedTopic++"?c=client1&u=tom&p=secret", + Reply1 = {ok, Pid, _N, nocontent, _} = er_coap_observer:observe(Uri), + ?LOGT("Subscribe Reply=~p", [Reply1]), + + [SubPid] = emqx:subscribers(Topic), + ?assert(is_pid(SubPid)), + + %% put to publish to topic "a/b" + Reply2 = er_coap_client:request(put, URI, #coap_content{format = <<"application/octet-stream">>, payload = Payload}), + {ok,changed, #coap_content{}} = Reply2, + [{Topic, MaxAge1, CT, Payload, TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(Topic), + ?assertEqual(60, MaxAge1), + ?assertEqual(<<"42">>, CT), + ?assertEqual(false, TimeStamp =:= timeout), + + %% Publish a message + emqx:publish(emqx_message:make(Topic, Payload)), + + Notif = receive_notification(), + ?LOGT("observer get Notif=~p", [Notif]), + {coap_notify, _, _, {ok,content}, #coap_content{payload = Payload}} = Notif, + + er_coap_observer:stop(Pid), + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, URI). + +t_case03_subscribe(_Config) -> + %% Subscribe to the unexisted topic "a/b", got not_found + Topic = <<"a/b">>, + TopicStr = binary_to_list(Topic), + PercentEncodedTopic = http_uri:encode(TopicStr), + Uri = "coap://127.0.0.1/ps/"++PercentEncodedTopic++"?c=client1&u=tom&p=secret", + {error, not_found} = er_coap_observer:observe(Uri), + + [] = emqx:subscribers(Topic). + +t_case04_subscribe(_Config) -> + %% Subscribe to the wildcad topic "+/b", got bad_request + Topic = <<"+/b">>, + TopicStr = binary_to_list(Topic), + PercentEncodedTopic = http_uri:encode(TopicStr), + Uri = "coap://127.0.0.1/ps/"++PercentEncodedTopic++"?c=client1&u=tom&p=secret", + {error, bad_request} = er_coap_observer:observe(Uri), + + [] = emqx:subscribers(Topic). + +t_case01_read(_Config) -> + Topic = <<"topic1">>, + TopicStr = binary_to_list(Topic), + Payload = <<"PubPayload">>, + timer:sleep(100), + + %% First post to create a topic "topic1" + Uri = "coap://127.0.0.1/ps/"++TopicStr++"?c=client1&u=tom&p=secret", + Reply = er_coap_client:request(post, Uri, #coap_content{format = <<"application/octet-stream">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = [LocPath]}} = Reply, + ?assertEqual(<<"/ps/topic1">> ,LocPath), + TopicInfo = [{Topic, MaxAge1, CT1, _ResPayload, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(Topic), + ?LOGT("lookup topic info=~p", [TopicInfo]), + ?assertEqual(60, MaxAge1), + ?assertEqual(<<"42">>, CT1), + + %% GET to read the publish message of the topic + Reply1 = er_coap_client:request(get, Uri), + ?LOGT("Reply=~p", [Reply1]), + {ok,content, #coap_content{max_age = MaxAgeLeft,payload = Payload}} = Reply1, + ?_assertEqual(true, MaxAgeLeft<60), + + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, Uri). + +t_case02_read(_Config) -> + Topic = <<"topic1">>, + TopicStr = binary_to_list(Topic), + Payload = <<"PubPayload">>, + timer:sleep(100), + + %% First post to publish a topic "topic1" + Uri = "coap://127.0.0.1/ps/"++TopicStr++"?c=client1&u=tom&p=secret", + Reply = er_coap_client:request(post, Uri, #coap_content{format = <<"application/octet-stream">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = [LocPath]}} = Reply, + ?assertEqual(<<"/ps/topic1">> ,LocPath), + TopicInfo = [{Topic, MaxAge1, CT1, _ResPayload, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(Topic), + ?LOGT("lookup topic info=~p", [TopicInfo]), + ?assertEqual(60, MaxAge1), + ?assertEqual(<<"42">>, CT1), + + %% GET to read the publish message of unmatched format, got bad_request + Reply1 = er_coap_client:request(get, Uri, #coap_content{format = <<"application/json">>}), + ?LOGT("Reply=~p", [Reply1]), + {error, bad_request} = Reply1, + + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, Uri). + +t_case03_read(_Config) -> + Topic = <<"topic1">>, + TopicStr = binary_to_list(Topic), + Uri = "coap://127.0.0.1/ps/"++TopicStr++"?c=client1&u=tom&p=secret", + timer:sleep(100), + + %% GET to read the nexisted topic "topic1", got not_found + Reply = er_coap_client:request(get, Uri), + ?LOGT("Reply=~p", [Reply]), + {error, not_found} = Reply. + +t_case04_read(_Config) -> + Topic = <<"topic1">>, + TopicStr = binary_to_list(Topic), + Payload = <<"PubPayload">>, + timer:sleep(100), + + %% First post to publish a topic "topic1" + Uri = "coap://127.0.0.1/ps/"++TopicStr++"?c=client1&u=tom&p=secret", + Reply = er_coap_client:request(post, Uri, #coap_content{format = <<"application/octet-stream">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = [LocPath]}} = Reply, + ?assertEqual(<<"/ps/topic1">> ,LocPath), + TopicInfo = [{Topic, MaxAge1, CT1, _ResPayload, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(Topic), + ?LOGT("lookup topic info=~p", [TopicInfo]), + ?assertEqual(60, MaxAge1), + ?assertEqual(<<"42">>, CT1), + + %% GET to read the publish message of wildcard topic, got bad_request + WildTopic = binary_to_list(<<"+/topic1">>), + Uri1 = "coap://127.0.0.1/ps/"++WildTopic++"?c=client1&u=tom&p=secret", + Reply1 = er_coap_client:request(get, Uri1, #coap_content{format = <<"application/json">>}), + ?LOGT("Reply=~p", [Reply1]), + {error, bad_request} = Reply1, + + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, Uri). + +t_case05_read(_Config) -> + Topic = <<"a/b">>, + TopicStr = binary_to_list(Topic), + PercentEncodedTopic = http_uri:encode(TopicStr), + Payload = <<"payload">>, + + %% post to publish a new topic "a/b", and the topic is created + URI = "coap://127.0.0.1/ps/"++PercentEncodedTopic++"?c=client1&u=tom&p=secret", + Reply = er_coap_client:request(post, URI, #coap_content{max_age = 5, format = <<"application/octet-stream">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = LocPath}} = Reply, + ?assertEqual([<<"/ps/a/b">>] ,LocPath), + [{Topic, MaxAge, CT, Payload, _TimeStamp}] = emqx_coap_ps_topics:lookup_topic_info(Topic), + ?assertEqual(5, MaxAge), + ?assertEqual(<<"42">>, CT), + + %% Wait for the max age of the timer expires + timer:sleep(6000), + ?assertEqual(true, emqx_coap_ps_topics:is_topic_timeout(Topic)), + + %% GET to read the expired publish message, supposed to get {ok, nocontent}, but now got {ok, content} + Reply1 = er_coap_client:request(get, URI), + ?LOGT("Reply=~p", [Reply1]), + {ok, content, #coap_content{payload = <<>>}}= Reply1, + + {ok, deleted, #coap_content{}} = er_coap_client:request(delete, URI). + +t_case01_delete(_Config) -> + TopicInPayload = <<"a/b">>, + TopicStr = binary_to_list(TopicInPayload), + PercentEncodedTopic = http_uri:encode(TopicStr), + Payload = list_to_binary("<"++PercentEncodedTopic++">;ct=42"), + URI = "coap://127.0.0.1/ps/"++"?c=client1&u=tom&p=secret", + + %% Client post to CREATE topic "a/b" + Reply = er_coap_client:request(post, URI, #coap_content{format = <<"application/link-format">>, payload = Payload}), + ?LOGT("Reply =~p", [Reply]), + {ok,created, #coap_content{location_path = LocPath}} = Reply, + ?assertEqual([<<"/ps/a/b">>] ,LocPath), + + %% Client post to CREATE topic "a/b/c" + TopicInPayload1 = <<"a/b/c">>, + PercentEncodedTopic1 = http_uri:encode(binary_to_list(TopicInPayload1)), + Payload1 = list_to_binary("<"++PercentEncodedTopic1++">;ct=42"), + Reply1 = er_coap_client:request(post, URI, #coap_content{format = <<"application/link-format">>, payload = Payload1}), + ?LOGT("Reply =~p", [Reply1]), + {ok,created, #coap_content{location_path = LocPath1}} = Reply1, + ?assertEqual([<<"/ps/a/b/c">>] ,LocPath1), + + timer:sleep(50), + + %% DELETE the topic "a/b" + UriD = "coap://127.0.0.1/ps/"++PercentEncodedTopic++"?c=client1&u=tom&p=secret", + ReplyD = er_coap_client:request(delete, UriD), + ?LOGT("Reply=~p", [Reply1]), + {ok, deleted, #coap_content{}}= ReplyD, + + ?assertEqual(false, emqx_coap_ps_topics:is_topic_existed(TopicInPayload)), + ?assertEqual(false, emqx_coap_ps_topics:is_topic_existed(TopicInPayload1)). + +t_case02_delete(_Config) -> + TopicInPayload = <<"a/b">>, + TopicStr = binary_to_list(TopicInPayload), + PercentEncodedTopic = http_uri:encode(TopicStr), + + %% DELETE the unexisted topic "a/b" + Uri1 = "coap://127.0.0.1/ps/"++PercentEncodedTopic++"?c=client1&u=tom&p=secret", + Reply1 = er_coap_client:request(delete, Uri1), + ?LOGT("Reply=~p", [Reply1]), + {error, not_found} = Reply1. + +t_case13_emit_stats_test(_Config) -> + ok. + +%%-------------------------------------------------------------------- +%% Internal functions + +receive_notification() -> + receive + {coap_notify, Pid, N2, Code2, Content2} -> + {coap_notify, Pid, N2, Code2, Content2} + after 2000 -> + receive_notification_timeout + end. + +assert_recv(Topic, Payload) -> + receive + {deliver, _, Msg} -> + ?assertEqual(Topic, Msg#message.topic), + ?assertEqual(Payload, Msg#message.payload) + after + 500 -> + ?assert(false) + end. + diff --git a/apps/emqx_dashboard/.github/workflows/run_test_cases.yaml b/apps/emqx_dashboard/.github/workflows/run_test_cases.yaml new file mode 100644 index 000000000..b8e722570 --- /dev/null +++ b/apps/emqx_dashboard/.github/workflows/run_test_cases.yaml @@ -0,0 +1,29 @@ +name: Run test cases + +on: [push, pull_request] + +jobs: + run_test_cases: + runs-on: ubuntu-latest + + container: + image: erlang:22.1 + + steps: + - uses: actions/checkout@v1 + - name: run test cases + run: | + make xref + make eunit + make ct + make cover + - uses: actions/upload-artifact@v1 + if: always() + with: + name: logs + path: _build/test/logs + - uses: actions/upload-artifact@v1 + with: + name: cover + path: _build/test/cover + diff --git a/apps/emqx_dashboard/.gitignore b/apps/emqx_dashboard/.gitignore new file mode 100644 index 000000000..d19e1b7d5 --- /dev/null +++ b/apps/emqx_dashboard/.gitignore @@ -0,0 +1,25 @@ +.eunit +deps +*.o +*.beam +*.plt +erl_crash.dump +ebin +rel/example_project +.concrete/DEV_MODE +.rebar +.erlang.mk/ +ct.coverdata +logs/ +test/ct.cover.spec +data/ +.DS_Store +emqx_dashboard.d +cover/ +eunit.coverdata +.DS_Store +erlang.mk +rebar.lock +_build/ +*.conf.rendered +.rebar3/ diff --git a/apps/emqx_dashboard/LICENSE b/apps/emqx_dashboard/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/apps/emqx_dashboard/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/apps/emqx_dashboard/README.md b/apps/emqx_dashboard/README.md new file mode 100644 index 000000000..e9e50a7c9 --- /dev/null +++ b/apps/emqx_dashboard/README.md @@ -0,0 +1,88 @@ + +emqx-dashboard +============== + +Dashboard for the EMQ X Broker. + +REST API +-------- + +The prefix of REST API is '/api/v4/'. + +Method | Path | Description +-------|---------------------------------------|------------------------------------ +GET | /nodes/ | A list of nodes in the cluster +GET | /nodes/:node | Lookup a node in the cluster +GET | /brokers/ | A list of brokers in the cluster +GET | /brokers/:node | Get broker info of a node +GET | /metrics/ | A list of metrics of all nodes in the cluster +GET | /nodes/:node/metrics/ | A list of metrics of a node +GET | /stats/ | A list of stats of all nodes in the cluster +GET | /nodes/:node/stats/ | A list of stats of a node +GET | /nodes/:node/clients/ | A list of clients on a node +GET | /listeners/ | A list of listeners in the cluster +GET | /nodes/:node/listeners | A list of listeners on the node +GET | /nodes/:node/sessions/ | A list of sessions on a node +GET | /subscriptions/:clientid | A list of subscriptions of a client +GET | /nodes/:node/subscriptions/:clientid | A list of subscriptions of a client on the node +GET | /nodes/:node/subscriptions/ | A list of subscriptions on a node +PUT | /clients/:clientid/clean_acl_cache | Clean ACL cache of a client +GET | /configs/ | Get all configs +GET | /nodes/:node/configs/ | Get all configs of a node +GET | /nodes/:node/plugin_configs/:plugin | Get configurations of a plugin on the node +DELETE | /clients/:clientid | Kick out a client +GET | /alarms/:node | List alarms of a node +GET | /alarms/ | List all alarms +GET | /plugins/ | List all plugins in the cluster +GET | /nodes/:node/plugins/ | List all plugins on a node +GET | /routes/ | List routes +POST | /nodes/:node/plugins/:plugin/load | Load a plugin +GET | /clients/:clientid | Lookup a client in the cluster +GET | nodes/:node/clients/:clientid | Lookup a client on node +GET | nodes/:node/sessions/:clientid | Lookup a session in the cluster +GET | nodes/:node/sessions/:clientid | Lookup a session on the node +POST | /mqtt/publish | Publish a MQTT message +POST | /mqtt/subscribe | Subscribe a topic +POST | /nodes/:node/plugins/:plugin/unload | Unload a plugin +POST | /mqtt/unsubscribe | Unsubscribe a topic +PUT | /configs/:app | Update config of an application in the cluster +PUT | /nodes/:node/configs/:app | Update config of an application on a node +PUT | /nodes/:node/plugin_configs/:plugin | Update configurations of a plugin on the node + +Build +----- + +make && make ct + +Configurtion +------------ + +``` +dashboard.listener = 18083 + +dashboard.listener.acceptors = 2 + +dashboard.listener.max_clients = 512 +``` + +Load Plugin +----------- + +``` +./bin/emqx_ctl plugins load emqx_dashboard +``` + +Login +----- + +URL: http://host:18083 + +Username: admin + +Password: public + +License +------- + +Apache License Version 2.0 + diff --git a/apps/emqx_dashboard/etc/emqx_dashboard.conf b/apps/emqx_dashboard/etc/emqx_dashboard.conf new file mode 100644 index 000000000..7c2125b4c --- /dev/null +++ b/apps/emqx_dashboard/etc/emqx_dashboard.conf @@ -0,0 +1,129 @@ +##-------------------------------------------------------------------- +## EMQ X Dashboard +##-------------------------------------------------------------------- + +## Default user's login name. +## +## Value: String +dashboard.default_user.login = admin + +## Default user's password. +## +## Value: String +dashboard.default_user.password = public + +##-------------------------------------------------------------------- +## HTTP Listener + +## The port that the Dashboard HTTP listener will bind. +## +## Value: Port +## +## Examples: 18083 +dashboard.listener.http = 18083 + +## The acceptor pool for external Dashboard HTTP listener. +## +## Value: Number +dashboard.listener.http.acceptors = 4 + +## Maximum number of concurrent Dashboard HTTP connections. +## +## Value: Number +dashboard.listener.http.max_clients = 512 + +## Set up the socket for IPv6. +## +## Value: false | true +dashboard.listener.http.inet6 = false + +## Listen on IPv4 and IPv6 (false) or only on IPv6 (true). Use with inet6. +## +## Value: false | true +dashboard.listener.http.ipv6_v6only = false + +##-------------------------------------------------------------------- +## HTTPS Listener + +## The port that the Dashboard HTTPS listener will bind. +## +## Value: Port +## +## Examples: 18084 +## dashboard.listener.https = 18084 + +## The acceptor pool for external Dashboard HTTPS listener. +## +## Value: Number +## dashboard.listener.https.acceptors = 2 + +## Maximum number of concurrent Dashboard HTTPS connections. +## +## Value: Number +## dashboard.listener.https.max_clients = 512 + +## Set up the socket for IPv6. +## +## Value: false | true +## dashboard.listener.https.inet6 = false + +## Listen on IPv4 and IPv6 (false) or only on IPv6 (true). Use with inet6. +## +## Value: false | true +## dashboard.listener.https.ipv6_v6only = false + +## Path to the file containing the user's private PEM-encoded key. +## +## Value: File +## dashboard.listener.https.keyfile = etc/certs/key.pem + +## Path to a file containing the user certificate. +## +## Value: File +## dashboard.listener.https.certfile = etc/certs/cert.pem + +## Path to the file containing PEM-encoded CA certificates. +## +## Value: File +## dashboard.listener.https.cacertfile = etc/certs/cacert.pem + +## See: 'listener.ssl..dhfile' in emq.conf +## +## Value: File +## dashboard.listener.https.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem + +## See: 'listener.ssl..vefify' in emq.conf +## +## Value: vefify_peer | verify_none +## dashboard.listener.https.verify = verify_peer + +## See: 'listener.ssl..fail_if_no_peer_cert' in emq.conf +## +## Value: false | true +## dashboard.listener.https.fail_if_no_peer_cert = true + +## TLS versions only to protect from POODLE attack. +## +## Value: String, seperated by ',' +## dashboard.listener.https.tls_versions = tlsv1.2,tlsv1.1,tlsv1 + +## See: 'listener.ssl..ciphers' in emq.conf +## +## Value: Ciphers +## dashboard.listener.https.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA + +## See: 'listener.ssl..secure_renegotiate' in emq.conf +## +## Value: on | off +## dashboard.listener.https.secure_renegotiate = off + +## See: 'listener.ssl..reuse_sessions' in emq.conf +## +## Value: on | off +## dashboard.listener.https.reuse_sessions = on + +## See: 'listener.ssl..honor_cipher_order' in emq.conf +## +## Value: on | off +## dashboard.listener.https.honor_cipher_order = on + diff --git a/apps/emqx_dashboard/include/emqx_dashboard.hrl b/apps/emqx_dashboard/include/emqx_dashboard.hrl new file mode 100644 index 000000000..73b64de77 --- /dev/null +++ b/apps/emqx_dashboard/include/emqx_dashboard.hrl @@ -0,0 +1,21 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-record(mqtt_admin, {username, password, tags}). + +-type(mqtt_admin() :: #mqtt_admin{}). + +-define(EMPTY_KEY(Key), ((Key == undefined) orelse (Key == <<>>))). diff --git a/apps/emqx_dashboard/priv/emqx_dashboard.schema b/apps/emqx_dashboard/priv/emqx_dashboard.schema new file mode 100644 index 000000000..fcc8f3489 --- /dev/null +++ b/apps/emqx_dashboard/priv/emqx_dashboard.schema @@ -0,0 +1,151 @@ +%%-*- mode: erlang -*- +%% emqx_dashboard config mapping + +{mapping, "dashboard.default_user.login", "emqx_dashboard.default_user_username", [ + {datatype, string} +]}. + +{mapping, "dashboard.default_user.password", "emqx_dashboard.default_user_passwd", [ + {datatype, string} +]}. + +{mapping, "dashboard.listener.http", "emqx_dashboard.listeners", [ + {datatype, integer} +]}. + +{mapping, "dashboard.listener.http.acceptors", "emqx_dashboard.listeners", [ + {default, 4}, + {datatype, integer} +]}. + +{mapping, "dashboard.listener.http.max_clients", "emqx_dashboard.listeners", [ + {default, 512}, + {datatype, integer} +]}. + +{mapping, "dashboard.listener.http.access.$id", "emqx_dashboard.listeners", [ + {datatype, string} +]}. + +{mapping, "dashboard.listener.http.inet6", "emqx_dashboard.listeners", [ + {default, false}, + {datatype, {enum, [true, false]}} +]}. + +{mapping, "dashboard.listener.http.ipv6_v6only", "emqx_dashboard.listeners", [ + {default, false}, + {datatype, {enum, [true, false]}} +]}. + +{mapping, "dashboard.listener.https", "emqx_dashboard.listeners", [ + {datatype, integer} +]}. + +{mapping, "dashboard.listener.https.acceptors", "emqx_dashboard.listeners", [ + {default, 8}, + {datatype, integer} +]}. + +{mapping, "dashboard.listener.https.max_clients", "emqx_dashboard.listeners", [ + {default, 64}, + {datatype, integer} +]}. + +{mapping, "dashboard.listener.https.inet6", "emqx_dashboard.listeners", [ + {default, false}, + {datatype, {enum, [true, false]}} +]}. + +{mapping, "dashboard.listener.https.ipv6_v6only", "emqx_dashboard.listeners", [ + {default, false}, + {datatype, {enum, [true, false]}} +]}. + +{mapping, "dashboard.listener.https.tls_versions", "emqx_dashboard.listeners", [ + {datatype, string} +]}. + +{mapping, "dashboard.listener.https.dhfile", "emqx_dashboard.listeners", [ + {datatype, string} +]}. + +{mapping, "dashboard.listener.https.keyfile", "emqx_dashboard.listeners", [ + {datatype, string} +]}. + +{mapping, "dashboard.listener.https.certfile", "emqx_dashboard.listeners", [ + {datatype, string} +]}. + +{mapping, "dashboard.listener.https.cacertfile", "emqx_dashboard.listeners", [ + {datatype, string} +]}. + +{mapping, "dashboard.listener.https.verify", "emqx_dashboard.listeners", [ + {datatype, string} +]}. + +{mapping, "dashboard.listener.https.fail_if_no_peer_cert", "emqx_dashboard.listeners", [ + {datatype, {enum, [true, false]}} +]}. + +{mapping, "dashboard.listener.https.ciphers", "emqx_dashboard.listeners", [ + {datatype, string} +]}. + +{mapping, "dashboard.listener.https.secure_renegotiate", "emqx_dashboard.listeners", [ + {datatype, flag} +]}. + +{mapping, "dashboard.listener.https.reuse_sessions", "emqx_dashboard.listeners", [ + {default, on}, + {datatype, flag} +]}. + +{mapping, "dashboard.listener.https.honor_cipher_order", "emqx_dashboard.listeners", [ + {datatype, flag} +]}. + +{translation, "emqx_dashboard.listeners", fun(Conf) -> + Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end, + LisOpts = fun(Prefix) -> + Filter([{num_acceptors, cuttlefish:conf_get(Prefix ++ ".acceptors", Conf)}, + {max_connections, cuttlefish:conf_get(Prefix ++ ".max_clients", Conf)}, + {inet6, cuttlefish:conf_get(Prefix ++ ".inet6", Conf)}, + {ipv6_v6only, cuttlefish:conf_get(Prefix ++ ".ipv6_v6only", Conf)}]) + end, + + SplitFun = fun(undefined) -> undefined; (S) -> string:tokens(S, ",") end, + + SslOpts = fun(Prefix) -> + Versions = case SplitFun(cuttlefish:conf_get(Prefix ++ ".tls_versions", Conf, undefined)) of + undefined -> undefined; + L -> [list_to_atom(V) || V <- L] + end, + Filter([{versions, Versions}, + {ciphers, SplitFun(cuttlefish:conf_get(Prefix ++ ".ciphers", Conf, undefined))}, + {dhfile, cuttlefish:conf_get(Prefix ++ ".dhfile", Conf, undefined)}, + {keyfile, cuttlefish:conf_get(Prefix ++ ".keyfile", Conf, undefined)}, + {certfile, cuttlefish:conf_get(Prefix ++ ".certfile", Conf, undefined)}, + {cacertfile, cuttlefish:conf_get(Prefix ++ ".cacertfile", Conf, undefined)}, + {verify, cuttlefish:conf_get(Prefix ++ ".verify", Conf, undefined)}, + {fail_if_no_peer_cert, cuttlefish:conf_get(Prefix ++ ".fail_if_no_peer_cert", Conf, undefined)}, + {secure_renegotiate, cuttlefish:conf_get(Prefix ++ ".secure_renegotiate", Conf, undefined)}, + {reuse_sessions, cuttlefish:conf_get(Prefix ++ ".reuse_sessions", Conf, undefined)}, + {honor_cipher_order, cuttlefish:conf_get(Prefix ++ ".honor_cipher_order", Conf, undefined)}]) + end, + lists:append( + lists:map( + fun(Proto) -> + Prefix = "dashboard.listener." ++ atom_to_list(Proto), + case cuttlefish:conf_get(Prefix, Conf, undefined) of + undefined -> []; + Port -> + [{Proto, Port, case Proto of + http -> LisOpts(Prefix); + https -> LisOpts(Prefix) ++ SslOpts(Prefix) + end}] + end + end, [http, https])) +end}. + diff --git a/apps/emqx_dashboard/priv/www/index.html b/apps/emqx_dashboard/priv/www/index.html new file mode 100644 index 000000000..4f43f0708 --- /dev/null +++ b/apps/emqx_dashboard/priv/www/index.html @@ -0,0 +1,3 @@ +Dashboard
\ No newline at end of file diff --git a/apps/emqx_dashboard/priv/www/static/css/app.1495f134b9420661c25a03fc6be1c155.css b/apps/emqx_dashboard/priv/www/static/css/app.1495f134b9420661c25a03fc6be1c155.css new file mode 100644 index 000000000..ab753d5bb --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/css/app.1495f134b9420661c25a03fc6be1c155.css @@ -0,0 +1 @@ +.login-view{width:100%;height:100%;background-color:#181818}.login-view .el-card{width:420px;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.login-view .el-card .el-card__header{font-size:16px}.login-view .error input{border:2px solid #e0b4b4}.login-view .login-footer{margin-top:40px;display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between;-ms-flex-align:center;align-items:center}.home-view{box-sizing:border-box;min-height:100%}.home-view .home-content{margin:0 32px 0 232px;padding:60px 0 20px}.left-bar{position:fixed;top:0;bottom:0;width:200px;z-index:1002;overflow-y:auto;overflow-x:hidden;padding-top:140px;padding-bottom:45px;background-color:#242327;box-shadow:0 0 15px #0b0b0b}.left-bar .iconfont{margin-right:8px;font-size:18px}.left-bar .bar-title{position:fixed;top:0;z-index:1003;width:200px;border-bottom:1px solid #2b2c30;color:#fff!important;background-color:#242327;text-align:center;padding:24px 0}.left-bar .bar-title h3{font-size:18px;margin:0;color:#fff}.left-bar .bar-title img{width:60px;height:60px;margin-bottom:8px}.left-bar .bar-footer{position:fixed;bottom:0;width:180px;z-index:1003;padding-left:20px;height:47px;line-height:47px;background:inherit;border-top:1px solid #2b2c30;color:#fff!important}.left-bar .bar-footer span{font-size:16px;font-weight:bolder;margin-right:12px;vertical-align:middle}.left-bar .bar-footer a{font-weight:700}.left-bar .bar-footer a img{width:20px;height:20px;border:none;vertical-align:middle}.left-bar .el-menu{width:200px;min-height:100%;border-right:none!important}.left-bar .el-submenu__title{padding-left:27px!important;height:40px;line-height:40px}.left-bar .el-submenu .el-menu-item{width:157px!important;min-width:0;margin-left:11px}.left-bar .el-menu-item{height:36px!important;line-height:36px!important;background-color:#8f8e8e;color:#929299!important;border-radius:4px;width:185px;margin-left:7px;margin-bottom:4px;margin-top:4px;transition:border-color .3s,background-color .3s,color .3s,box-shadow .3s}.left-bar .el-menu-item:hover{color:#fff!important;background-color:#393a3e!important}.left-bar .el-menu-item:hover i{color:#fff!important}.left-bar .el-menu-item.is-active{color:#fff!important;background-color:#00b173!important;box-shadow:0 0 5px 0 #02d48a}.left-bar .el-menu-item.is-active i{color:#fff!important}.left-bar .last-item{margin-bottom:72px}.left-bar .menu-dot .el-badge__content.is-fixed.is-dot{top:17px;right:0}.left-bar .submenu-dot .el-badge__content.is-fixed.is-dot{right:3px;top:14px}.topbar{height:60px;line-height:60px;padding-left:110px;position:fixed;top:0;left:0;right:0;z-index:4}.topbar .top-area{background-color:#242327;height:100%;padding:0 32px;text-align:right}.topbar .top-area .topbar-right .help-link{display:inline;line-height:32px}.topbar .top-area .topbar-right .help-link .link{display:inline-block;color:#82858f;padding:0 20px;border-right:1px solid #2b2c30;position:relative;top:3px}.topbar .top-area .topbar-right .help-link .link .icon-bangzhu{font-size:20px}.topbar .top-area .topbar-right .help-link a.active{color:#34c388}.topbar .top-area .topbar-right .el-button{border-radius:40px;border-width:2px;margin-left:20px;font-size:14px;font-weight:400;line-height:15px;background:transparent}.topbar .top-area .topbar-right .el-button.enterprise-btn{color:#34c388;border-color:#34c388}.topbar .top-area .topbar-right .el-button.enterprise-btn .icon-arrow{position:relative;top:1px}.topbar .top-area .topbar-right .el-button.github-btn{color:#adafb4;border-color:#adafb4}.topbar .top-area .topbar-right .el-button .iconfont{margin-left:5px}.topbar .top-area .topbar-right .el-button--medium{padding:9px 20px}.overview-view *{padding:0;margin:0;box-sizing:border-box}.overview-view .el-select .el-input__inner{padding-left:10px}.overview-view .card-box{position:relative;margin-top:74px}.overview-view .card-box .card-title{position:absolute;height:24px;line-height:24px;width:100%;top:-34px;left:0;font-size:16px}.overview-view .card-box .el-table{margin-top:0}.overview-view .card-box .el-table .caret-wrapper{left:-8px}.overview-view .card-box .el-table--medium td,.overview-view .card-box .stats-table.el-table--medium th{padding:4px 0}@media screen and (max-width:1280px){.overview-view .card-box .el-col-6{width:50%!important;margin-top:10px!important}}@media screen and (max-width:740px){.overview-view .card-box .el-col-6{width:100%!important;margin-top:10px!important}}.overview-view .card-box .broker-card.el-row{overflow-x:auto}.overview-view .card-box .broker-card .el-col .card-item{height:90px;min-width:250px;line-height:90px;padding:18px 20px 0;border-radius:4px}.overview-view .card-box .broker-card .el-col .card-item .icon{float:left;width:54px;height:54px;line-height:50px;text-align:center;border:2px solid;border-radius:50%}.overview-view .card-box .broker-card .el-col .card-item .icon i{font-size:26px}.overview-view .card-box .broker-card .el-col .card-item .desc{line-height:normal;float:right;text-align:right;height:70px}.overview-view .card-box .broker-card .el-col .card-item .desc h3{font-size:14px;font-weight:700}.overview-view .card-box .broker-card .el-col .card-item .desc p{margin-top:12px;max-width:150px}.overview-view span{line-height:10px}.overview-view .box-card,.overview-view .el-row{margin-top:20px}.overview-view .iconfont{position:relative}.data-view .el-table{margin-top:24px}.data-view .el-row{margin-top:20px}.data-view .el-input{width:240px}.data-view .search-btn{margin-left:8px}.data-view .el-breadcrumb{margin-top:10px;margin-bottom:20px}.data-view .el-pagination .el-select .el-input .el-input__inner{height:22px}.data-view .el-select .el-input .el-select__caret{line-height:12px}.data-view .search-card{margin-top:24px;border:none}.data-view .search-card .el-card__body{padding:5px 12px}.data-view .search-card .el-input,.data-view .search-card .el-select{width:100%}.data-view .search-card .el-input--medium .el-input__inner{height:32px!important}.data-view .search-card .col-share{position:absolute;bottom:-13px}.data-view .search-card .col-oper{float:right;position:relative;top:1px;margin-bottom:10px}.data-view .search-card .col-oper .show-more{margin:0 10px;font-size:12px}.data-view .search-card .form-item-row{margin-top:0}.data-view .search-card .form-item-row .el-select.comparator .el-input--medium .el-input__inner,.data-view .search-card .form-item-row .el-select.match .el-input--medium .el-input__inner{border-radius:4px 0 0 4px}.data-view .search-card .form-item-row .el-input__inner{border-radius:0 4px 4px 0}.data-view .search-card .el-select .el-input .el-select__caret{line-height:36px}.data-view .custom-pagination{margin-top:10px}.data-view .custom-pagination a{transition:all .3s ease;color:#fff;margin-right:10px;background:#42d885;display:inline-block;border-radius:4px;padding:5px 8px}.data-view .custom-pagination a:hover{color:#fff}.data-view .custom-pagination a.disabled{transition:all .3s ease;color:#606266;background:transparent;cursor:not-allowed}.clients-view .client-oper{float:right;margin-top:-32px;color:#adafb4}.clients-view .client-oper .connect-btn{border:1px solid;background:transparent;margin-left:20px;min-width:80px;font-size:14px;font-weight:400}.clients-view .client-oper .connect-btn.disconnected{border-color:#ff6d6d;color:#ff6d6d}.clients-view .client-oper .connect-btn.connected{border-color:#adafb4;color:#adafb4}.clients-view .client-oper .connect-btn:hover{background:transparent!important}.clients-view .el-card.tabs-card{border-radius:0 0 4px 4px}.clients-view .el-card .el-card__body{padding:10px 36px}.clients-view .card-subtitle{font-size:16px;margin:24px 0}.clients-basic .clients-basic-form .form-item-desc{color:#5f6067;margin-left:20px;font-size:14px}.clients-basic .clients-basic-form .el-form-item__content{color:#f8f8f8}.clients-basic .clients-basic-form .el-form-item{margin-bottom:12px}.clients-basic .view-more{margin:24px 0;font-size:14px}.clients-subscriptions .oper-btn-group{text-align:right;margin:24px 0}.clients-subscriptions .client-sub-table{margin-bottom:24px}.clients-subscriptions .el-select--public{width:100%}.clients-subscriptions .el-select--public .el-input__inner{height:32px!important;line-height:32px!important}.rules-view .el-table{margin-top:24px}.rules-view .status-wrapper{padding:0;list-style-type:none}.rules-view .status-wrapper .status-item{padding:2px 6px}.rules-view .status-wrapper .status-item>span{margin-right:12px}.rules-view span[type=info]{padding-right:20px}.rules-view span[type=info]>span{margin-left:6px;color:#333;font-weight:600;border-bottom:1px dashed #d8d8d8}.rules-view span[type=info]>span:hover{font-weight:800}.rule-actions .status-wrapper .status-item,.rule-actions .status-wrapper .title{margin-bottom:10px}.rule-actions .status-wrapper .key{width:120px;display:inline-block}.rule-actions .action-item{margin:2px auto}.rule-actions .action-card{font-size:14px;margin-bottom:24px}.rule-actions .action-card .action-body,.rule-actions .action-card .action-footer{padding:20px}.rule-actions .action-card .filed-item{margin-bottom:16px}.rule-actions .action-card .filed-item:last-child{margin-bottom:0}.rule-actions .action-card .filed-item .title{margin-right:10px}.rule-actions .action-card .action-oper{text-align:right;position:relative}.rule-actions .action-card .action-oper .delete-btn{margin-bottom:40px}.rule-actions .action-card .action-oper .fallbacks{position:absolute;right:0;bottom:0}.rule-actions .action-card .action-oper .fallbacks .el-button [class*=el-icon-]+span{margin-left:0}.rule-actions .action-card:last-child{margin-bottom:0}.rule-actions .el-table__expanded-cell{padding:6px;font-size:12px}.action-dialog .action-tips{padding:20px auto;font-size:13px}.action-dialog .resource-item .el-form-item__label{width:100%;text-align:left;padding-right:0}.action-dialog .el-form-item__content{clear:both}.action-dialog .el-select{width:100%}.resource-dialog .el-form{padding:20px}.resource-dialog .el-input--medium .el-input__inner{height:35px;line-height:35px}.resource-dialog .block__title{padding-left:10px;margin-left:10px;border-left:4px solid #34c388;margin-bottom:20px}.resource-dialog .el-select{width:100%}.resource-dialog .divide{margin:25px auto;border-bottom:1px solid #d8d8d8;clear:both}.resource-dialog .el-form-item__content{clear:both}.rule-create .page-title .el-breadcrumb{text-transform:none}.rule-create .el-card{margin-top:24px;min-height:150px;padding:20px;overflow:visible}.rule-create .el-card .config-null{text-align:center;margin:20px auto}.rule-create .toggle-btn{cursor:pointer;margin-top:4px;width:auto}.rule-create .show-guess{line-height:1.4}.rule-create .show-guess p{font-size:13px;margin-bottom:4px}.rule-create .show-guess p .notice{color:#ff6d6d}.rule-create .form-block--wrapper{margin-bottom:50px;padding-bottom:24px}.rule-create .form-block--wrapper .form-block__title{margin-bottom:30px;padding-left:10px;border-left:4px solid #34c388}.rule-create .form-block--wrapper .form-block__title .form-block__title-tips{font-size:12px;display:inline-block;margin-left:4px}.rule-create .form-block--wrapper .form-block__body{padding-left:20px}.rule-create .sql-tips{padding:20px 0;border-radius:4px;font-size:15px;max-height:480px}.rule-create .sql-tips .title{padding:0 20px 12px}.rule-create .sql-tips .el-scrollbar__wrap{overflow-x:hidden}.rule-create .sql-tips .doc-wrapper{max-height:400px;padding:0 20px}.rule-create .sql-tips p{font-size:13px;margin-bottom:4px}.rule-create .sql-tips li{font-size:13px;margin:8px 12px}.rule-create .sql-tips code,.rule-create .sql-tips span{font-size:12px;margin-bottom:12px}.rule-create .code{line-height:1.4;padding:6px;border-radius:4px;margin-bottom:12px}.rule-create .code.text{line-height:1.8}.rule-create .test-btn{margin-top:12px}.code-sql{line-height:18px!important;margin-bottom:44px}.code-sql.rawsql .el-form-item__content{height:420px}.code-sql.payload .el-form-item__content{height:200px}.code-sql .el-form-item__content{line-height:18px!important}.code-sql .payload-type{width:100%;text-align:right;padding:2px 4px;margin-bottom:12px}.code-sql .payload-type .el-radio__label{font-size:13px}.code-sql.is-error .code-sql__editor,.code-sql.is-error .monaco-container{border-color:#ff6d6d}#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.monaco-view{height:100%;position:relative}.monaco-editor .accessibilityHelpWidget{padding:10px;vertical-align:middle;overflow:scroll}.monaco-aria-container{position:absolute;left:-999em}.monaco-editor .bracket-match{box-sizing:border-box}.monaco-menu .monaco-action-bar.vertical .action-label.hover{background-color:#eee}.monaco-editor .monaco-editor-overlaymessage{padding-bottom:8px}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.monaco-editor .monaco-editor-overlaymessage.fadeIn{animation:fadeIn .15s ease-out}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}.monaco-editor .monaco-editor-overlaymessage.fadeOut{animation:fadeOut .1s ease-out}.monaco-editor .monaco-editor-overlaymessage .message{padding:1px 4px}.monaco-editor .monaco-editor-overlaymessage .anchor{width:0!important;height:0!important;border:8px solid transparent;z-index:1000;position:absolute}.monaco-editor .lightbulb-glyph{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;height:16px;width:20px;padding-left:2px}.monaco-editor .lightbulb-glyph:hover{cursor:pointer}.monaco-editor.vs .lightbulb-glyph{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMS42NzA4IDguNjU4MDZDMTEuMzMxOSA4Ljk5MTYgMTEuMDcxNiA5LjM2Mjc4IDEwLjg4ODYgOS43NzE3MkMxMC43MTA1IDEwLjE3OTIgMTAuNjIxIDEwLjYyMTkgMTAuNjIxIDExLjEwMDlWMTIuNzAxMkMxMC42MjEgMTIuODgwNyAxMC41ODcyIDEzLjA1MDMgMTAuNTE4OSAxMy4yMDkxQzEwLjQ1MTMgMTMuMzY2MSAxMC4zNTg2IDEzLjUwMzggMTAuMjQwNyAxMy42MjEzQzEwLjEyMjggMTMuNzM4OCA5Ljk4NDY0IDEzLjgzMTEgOS44MjcyMyAxMy44OTg0QzkuNjY4MDYgMTMuOTY2MyA5LjQ5ODA2IDE0IDkuMzE4MjMgMTRINy43MTIwNUM3LjUzMjIzIDE0IDcuMzYyMjMgMTMuOTY2MyA3LjIwMzA2IDEzLjg5ODRDNy4wNDU2NCAxMy44MzExIDYuOTA3NTMgMTMuNzM4OCA2Ljc4OTYxIDEzLjYyMTNDNi42NzE2OCAxMy41MDM4IDYuNTc4OTUgMTMuMzY2MSA2LjUxMTQxIDEzLjIwOTFDNi40NDMxMSAxMy4wNTAzIDYuNDA5MjcgMTIuODgwNyA2LjQwOTI3IDEyLjcwMTJWMTEuMTAwOUM2LjQwOTI3IDEwLjYyMiA2LjMxNzcyIDEwLjE3OTUgNi4xMzU1MyA5Ljc3MjA5QzUuOTU2ODMgOS4zNjMzNiA1LjY5ODMyIDguOTkxNTYgNS4zNTk1MyA4LjY1ODA2QzQuOTI0NjggOC4yMjkwMyA0LjU4ODk2IDcuNzUwMDMgNC4zNTM2MSA3LjIyMTM0QzQuMTE3NTYgNi42OTEwNyA0IDYuMTE2NzIgNCA1LjQ5OTUzQzQgNS4wODY2NCA0LjA1MzQyIDQuNjg4MDIgNC4xNjA0OCA0LjMwMzk3QzQuMjY3MjggMy45MjA4OSA0LjQxOTA3IDMuNTYyODYgNC42MTU5NSAzLjIzMDE4QzQuODEyNTcgMi44OTM3NyA1LjA0Nzc3IDIuNTg5MTEgNS4zMjE0NiAyLjMxNjQxQzUuNTk1MDMgMi4wNDM4MyA1Ljg5ODU4IDEuODA5NTMgNi4yMzE5NSAxLjYxMzY0QzYuNTY5NzkgMS40MTc2NCA2LjkzMTQ2IDEuMjY2MiA3LjMxNTc4IDEuMTU5ODNDNy43MDEwNiAxLjA1MzIgOC4xMDA5NCAxIDguNTE1MTQgMUM4LjkyOTM0IDEgOS4zMjkyMyAxLjA1MzIgOS43MTQ1MSAxLjE1OTgzQzEwLjA5ODggMS4yNjYyIDEwLjQ1OCAxLjQxNzM5IDEwLjc5MTggMS42MTM1MUMxMS4xMjk0IDEuODA5MzggMTEuNDM1MSAyLjA0MzcgMTEuNzA4OCAyLjMxNjQxQzExLjk4MjUgMi41ODkxIDEyLjIxNzcgMi44OTM3NiAxMi40MTQzIDMuMjMwMTZDMTIuNjExMiAzLjU2Mjg1IDEyLjc2MyAzLjkyMDg4IDEyLjg2OTggNC4zMDM5N0MxMi45NzY5IDQuNjg4MDIgMTMuMDMwMyA1LjA4NjY0IDEzLjAzMDMgNS40OTk1M0MxMy4wMzAzIDYuMTE2NzIgMTIuOTEyNyA2LjY5MTA3IDEyLjY3NjcgNy4yMjEzNEMxMi40NDEzIDcuNzUwMDMgMTIuMTA1NiA4LjIyOTAzIDExLjY3MDggOC42NTgwNlpNOS42MjE2MiAxMC41SDcuNDA4NjdWMTIuNzAxMkM3LjQwODY3IDEyLjc4MjMgNy40MzcyIDEyLjg1MTIgNy40OTg4OCAxMi45MTI3QzcuNTYwNTggMTIuOTc0MSA3LjYzMDA3IDEzLjAwMjggNy43MTIwNSAxMy4wMDI4SDkuMzE4MjNDOS40MDAyMiAxMy4wMDI4IDkuNDY5NzEgMTIuOTc0MSA5LjUzMTQgMTIuOTEyN0M5LjU5MzA5IDEyLjg1MTIgOS42MjE2MiAxMi43ODIzIDkuNjIxNjIgMTIuNzAxMlYxMC41WiIgZmlsbD0iI0ZGQ0MwMCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTExLjY3MDggOC42NTgwNkMxMS4zMzE5IDguOTkxNiAxMS4wNzE2IDkuMzYyNzggMTAuODg4NiA5Ljc3MTcyQzEwLjcxMDUgMTAuMTc5MiAxMC42MjEgMTAuNjIxOSAxMC42MjEgMTEuMTAwOVYxMi43MDEyQzEwLjYyMSAxMi44ODA3IDEwLjU4NzIgMTMuMDUwMyAxMC41MTg5IDEzLjIwOTFDMTAuNDUxMyAxMy4zNjYxIDEwLjM1ODYgMTMuNTAzOCAxMC4yNDA3IDEzLjYyMTNDMTAuMTIyOCAxMy43Mzg4IDkuOTg0NjQgMTMuODMxMSA5LjgyNzIzIDEzLjg5ODRDOS42NjgwNiAxMy45NjYzIDkuNDk4MDYgMTQgOS4zMTgyMyAxNEg3LjcxMjA1QzcuNTMyMjMgMTQgNy4zNjIyMyAxMy45NjYzIDcuMjAzMDYgMTMuODk4NEM3LjA0NTY0IDEzLjgzMTEgNi45MDc1MyAxMy43Mzg4IDYuNzg5NjEgMTMuNjIxM0M2LjY3MTY4IDEzLjUwMzggNi41Nzg5NSAxMy4zNjYxIDYuNTExNDEgMTMuMjA5MUM2LjQ0MzExIDEzLjA1MDMgNi40MDkyNyAxMi44ODA3IDYuNDA5MjcgMTIuNzAxMlYxMS4xMDA5QzYuNDA5MjcgMTAuNjIyIDYuMzE3NzIgMTAuMTc5NSA2LjEzNTUzIDkuNzcyMDlDNS45NTY4MyA5LjM2MzM2IDUuNjk4MzIgOC45OTE1NiA1LjM1OTUzIDguNjU4MDZDNC45MjQ2OCA4LjIyOTAzIDQuNTg4OTYgNy43NTAwMyA0LjM1MzYxIDcuMjIxMzRDNC4xMTc1NiA2LjY5MTA3IDQgNi4xMTY3MiA0IDUuNDk5NTNDNCA1LjA4NjY0IDQuMDUzNDIgNC42ODgwMiA0LjE2MDQ4IDQuMzAzOTdDNC4yNjcyOCAzLjkyMDg5IDQuNDE5MDcgMy41NjI4NiA0LjYxNTk1IDMuMjMwMThDNC44MTI1NyAyLjg5Mzc3IDUuMDQ3NzcgMi41ODkxMSA1LjMyMTQ2IDIuMzE2NDFDNS41OTUwMyAyLjA0MzgzIDUuODk4NTggMS44MDk1MyA2LjIzMTk1IDEuNjEzNjRDNi41Njk3OSAxLjQxNzY0IDYuOTMxNDYgMS4yNjYyIDcuMzE1NzggMS4xNTk4M0M3LjcwMTA2IDEuMDUzMiA4LjEwMDk0IDEgOC41MTUxNCAxQzguOTI5MzQgMSA5LjMyOTIzIDEuMDUzMiA5LjcxNDUxIDEuMTU5ODNDMTAuMDk4OCAxLjI2NjIgMTAuNDU4IDEuNDE3MzkgMTAuNzkxOCAxLjYxMzUxQzExLjEyOTQgMS44MDkzOCAxMS40MzUxIDIuMDQzNyAxMS43MDg4IDIuMzE2NDFDMTEuOTgyNSAyLjU4OTEgMTIuMjE3NyAyLjg5Mzc2IDEyLjQxNDMgMy4yMzAxNkMxMi42MTEyIDMuNTYyODUgMTIuNzYzIDMuOTIwODggMTIuODY5OCA0LjMwMzk3QzEyLjk3NjkgNC42ODgwMiAxMy4wMzAzIDUuMDg2NjQgMTMuMDMwMyA1LjQ5OTUzQzEzLjAzMDMgNi4xMTY3MiAxMi45MTI3IDYuNjkxMDcgMTIuNjc2NyA3LjIyMTM0QzEyLjQ0MTMgNy43NTAwMyAxMi4xMDU2IDguMjI5MDMgMTEuNjcwOCA4LjY1ODA2Wk05LjYyMTYyIDEwLjVINy40MDg2N1YxMi43MDEyQzcuNDA4NjcgMTIuNzgyMyA3LjQzNzIgMTIuODUxMiA3LjQ5ODg4IDEyLjkxMjdDNy41NjA1OCAxMi45NzQxIDcuNjMwMDcgMTMuMDAyOCA3LjcxMjA1IDEzLjAwMjhIOS4zMTgyM0M5LjQwMDIyIDEzLjAwMjggOS40Njk3MSAxMi45NzQxIDkuNTMxNCAxMi45MTI3QzkuNTkzMDkgMTIuODUxMiA5LjYyMTYyIDEyLjc4MjMgOS42MjE2MiAxMi43MDEyVjEwLjVaIiBmaWxsPSIjRERCMTAwIi8+Cjwvc3ZnPgo=") 50% no-repeat}.monaco-editor.vs .lightbulb-glyph.autofixable{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMiA5QzEwLjMzMDQgOSA5IDEwLjMzMDQgOSAxMkM5IDEzLjY2OTYgMTAuMzMwNCAxNSAxMiAxNUMxMy42Njk2IDE1IDE1IDEzLjY2OTYgMTUgMTJDMTUgMTAuMzMwNCAxMy42Njk2IDkgMTIgOVpNMTEuMjAyOCAxMi40NzEyTDEwLjcwNCAxNEwxMiAxMy4wMjRMMTMuMzA1NCAxNEwxMi43OTcyIDEyLjQ3MTJMMTQgMTEuNjM5NEgxMi41MzYxTDEyIDEwTDExLjQ3MzIgMTEuNjM5NEgxMEwxMS4yMDI4IDEyLjQ3MTJaIiBmaWxsPSIjMDA3QUNDIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTEuMTcwOCA4LjA4NDc0QzkuODUwODEgOC4zNTkxMSA4Ljc3Njg3IDkuMjc2ODQgOC4yODY5NiAxMC41SDYuNDA4NjdWMTIuNzAxMkM2LjQwODY3IDEyLjc4MjMgNi40MzcyIDEyLjg1MTIgNi40OTg4OCAxMi45MTI3QzYuNTYwNTggMTIuOTc0MSA2LjYzMDA3IDEzLjAwMjggNi43MTIwNSAxMy4wMDI4SDguMTI0ODdDOC4yMTM2NCAxMy4zNTEzIDguMzQ3NzMgMTMuNjgwOSA4LjUyMDU5IDEzLjk4NTFDOC40NTQ2MiAxMy45OTUxIDguMzg3MTUgMTQgOC4zMTgyMyAxNEg2LjcxMjA1QzYuNTMyMjMgMTQgNi4zNjIyMyAxMy45NjYzIDYuMjAzMDYgMTMuODk4NEM2LjA0NTY0IDEzLjgzMTEgNS45MDc1MyAxMy43Mzg4IDUuNzg5NjEgMTMuNjIxM0M1LjY3MTY4IDEzLjUwMzggNS41Nzg5NSAxMy4zNjYxIDUuNTExNDEgMTMuMjA5MUM1LjQ0MzExIDEzLjA1MDMgNS40MDkyNyAxMi44ODA3IDUuNDA5MjcgMTIuNzAxMlYxMS4xMDA5QzUuNDA5MjcgMTAuNjIyIDUuMzE3NzIgMTAuMTc5NSA1LjEzNTUzIDkuNzcyMDlDNC45NTY4MyA5LjM2MzM2IDQuNjk4MzIgOC45OTE1NiA0LjM1OTUzIDguNjU4MDZDMy45MjQ2OCA4LjIyOTAzIDMuNTg4OTYgNy43NTAwMyAzLjM1MzYxIDcuMjIxMzRDMy4xMTc1NiA2LjY5MTA3IDMgNi4xMTY3MiAzIDUuNDk5NTNDMyA1LjA4NjY0IDMuMDUzNDIgNC42ODgwMiAzLjE2MDQ4IDQuMzAzOTdDMy4yNjcyOCAzLjkyMDg5IDMuNDE5MDcgMy41NjI4NiAzLjYxNTk1IDMuMjMwMThDMy44MTI1NyAyLjg5Mzc3IDQuMDQ3NzcgMi41ODkxMSA0LjMyMTQ2IDIuMzE2NDFDNC41OTUwMyAyLjA0MzgzIDQuODk4NTggMS44MDk1MyA1LjIzMTk1IDEuNjEzNjRDNS41Njk3OSAxLjQxNzY0IDUuOTMxNDYgMS4yNjYyIDYuMzE1NzggMS4xNTk4M0M2LjcwMTA2IDEuMDUzMiA3LjEwMDk0IDEgNy41MTUxNCAxQzcuOTI5MzQgMSA4LjMyOTIzIDEuMDUzMiA4LjcxNDUxIDEuMTU5ODNDOS4wOTg4MyAxLjI2NjIgOS40NTgwMyAxLjQxNzM5IDkuNzkxODMgMS42MTM1MUMxMC4xMjk0IDEuODA5MzggMTAuNDM1MSAyLjA0MzcgMTAuNzA4OCAyLjMxNjQxQzEwLjk4MjUgMi41ODkxIDExLjIxNzcgMi44OTM3NiAxMS40MTQzIDMuMjMwMTZDMTEuNjExMiAzLjU2Mjg1IDExLjc2MyAzLjkyMDg4IDExLjg2OTggNC4zMDM5N0MxMS45NzY5IDQuNjg4MDIgMTIuMDMwMyA1LjA4NjY0IDEyLjAzMDMgNS40OTk1M0MxMi4wMzAzIDYuMTE2NzIgMTEuOTEyNyA2LjY5MTA3IDExLjY3NjcgNy4yMjEzNEMxMS41NDEyIDcuNTI1NjIgMTEuMzcyNSA3LjgxMzQ0IDExLjE3MDggOC4wODQ3NFoiIGZpbGw9IiMwMDdBQ0MiLz4KPC9zdmc+Cg==") 50% no-repeat}.monaco-editor.hc-black .lightbulb-glyph,.monaco-editor.vs-dark .lightbulb-glyph{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMS42NzA4IDguNjU4MDZDMTEuMzMxOSA4Ljk5MTYgMTEuMDcxNiA5LjM2Mjc4IDEwLjg4ODYgOS43NzE3MkMxMC43MTA1IDEwLjE3OTIgMTAuNjIxIDEwLjYyMTkgMTAuNjIxIDExLjEwMDlWMTIuNzAxMkMxMC42MjEgMTIuODgwNyAxMC41ODcyIDEzLjA1MDMgMTAuNTE4OSAxMy4yMDkxQzEwLjQ1MTMgMTMuMzY2MSAxMC4zNTg2IDEzLjUwMzggMTAuMjQwNyAxMy42MjEzQzEwLjEyMjggMTMuNzM4OCA5Ljk4NDY0IDEzLjgzMTEgOS44MjcyMyAxMy44OTg0QzkuNjY4MDYgMTMuOTY2MyA5LjQ5ODA2IDE0IDkuMzE4MjMgMTRINy43MTIwNUM3LjUzMjIzIDE0IDcuMzYyMjMgMTMuOTY2MyA3LjIwMzA2IDEzLjg5ODRDNy4wNDU2NCAxMy44MzExIDYuOTA3NTMgMTMuNzM4OCA2Ljc4OTYxIDEzLjYyMTNDNi42NzE2OCAxMy41MDM4IDYuNTc4OTUgMTMuMzY2MSA2LjUxMTQxIDEzLjIwOTFDNi40NDMxMSAxMy4wNTAzIDYuNDA5MjcgMTIuODgwNyA2LjQwOTI3IDEyLjcwMTJWMTEuMTAwOUM2LjQwOTI3IDEwLjYyMiA2LjMxNzcyIDEwLjE3OTUgNi4xMzU1MyA5Ljc3MjA5QzUuOTU2ODMgOS4zNjMzNiA1LjY5ODMyIDguOTkxNTYgNS4zNTk1MyA4LjY1ODA2QzQuOTI0NjggOC4yMjkwMyA0LjU4ODk2IDcuNzUwMDMgNC4zNTM2MSA3LjIyMTM0QzQuMTE3NTYgNi42OTEwNyA0IDYuMTE2NzIgNCA1LjQ5OTUzQzQgNS4wODY2NCA0LjA1MzQyIDQuNjg4MDIgNC4xNjA0OCA0LjMwMzk3QzQuMjY3MjggMy45MjA4OSA0LjQxOTA3IDMuNTYyODYgNC42MTU5NSAzLjIzMDE4QzQuODEyNTcgMi44OTM3NyA1LjA0Nzc3IDIuNTg5MTEgNS4zMjE0NiAyLjMxNjQxQzUuNTk1MDMgMi4wNDM4MyA1Ljg5ODU4IDEuODA5NTMgNi4yMzE5NSAxLjYxMzY0QzYuNTY5NzkgMS40MTc2NCA2LjkzMTQ2IDEuMjY2MiA3LjMxNTc4IDEuMTU5ODNDNy43MDEwNiAxLjA1MzIgOC4xMDA5NCAxIDguNTE1MTQgMUM4LjkyOTM0IDEgOS4zMjkyMyAxLjA1MzIgOS43MTQ1MSAxLjE1OTgzQzEwLjA5ODggMS4yNjYyIDEwLjQ1OCAxLjQxNzM5IDEwLjc5MTggMS42MTM1MUMxMS4xMjk0IDEuODA5MzggMTEuNDM1MSAyLjA0MzcgMTEuNzA4OCAyLjMxNjQxQzExLjk4MjUgMi41ODkxIDEyLjIxNzcgMi44OTM3NiAxMi40MTQzIDMuMjMwMTZDMTIuNjExMiAzLjU2Mjg1IDEyLjc2MyAzLjkyMDg4IDEyLjg2OTggNC4zMDM5N0MxMi45NzY5IDQuNjg4MDIgMTMuMDMwMyA1LjA4NjY0IDEzLjAzMDMgNS40OTk1M0MxMy4wMzAzIDYuMTE2NzIgMTIuOTEyNyA2LjY5MTA3IDEyLjY3NjcgNy4yMjEzNEMxMi40NDEzIDcuNzUwMDMgMTIuMTA1NiA4LjIyOTAzIDExLjY3MDggOC42NTgwNlpNOS42MjE2MiAxMC41SDcuNDA4NjdWMTIuNzAxMkM3LjQwODY3IDEyLjc4MjMgNy40MzcyIDEyLjg1MTIgNy40OTg4OCAxMi45MTI3QzcuNTYwNTggMTIuOTc0MSA3LjYzMDA3IDEzLjAwMjggNy43MTIwNSAxMy4wMDI4SDkuMzE4MjNDOS40MDAyMiAxMy4wMDI4IDkuNDY5NzEgMTIuOTc0MSA5LjUzMTQgMTIuOTEyN0M5LjU5MzA5IDEyLjg1MTIgOS42MjE2MiAxMi43ODIzIDkuNjIxNjIgMTIuNzAxMlYxMC41WiIgZmlsbD0iI0ZGQ0MwMCIvPgo8L3N2Zz4K") 50% no-repeat}.monaco-editor.hc-black .lightbulb-glyph.autofixable,.monaco-editor.vs-dark .lightbulb-glyph.autofixable{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMiA5QzEwLjMzMDQgOSA5IDEwLjMzMDQgOSAxMkM5IDEzLjY2OTYgMTAuMzMwNCAxNSAxMiAxNUMxMy42Njk2IDE1IDE1IDEzLjY2OTYgMTUgMTJDMTUgMTAuMzMwNCAxMy42Njk2IDkgMTIgOVpNMTEuMjAyOCAxMi40NzEyTDEwLjcwNCAxNEwxMiAxMy4wMjRMMTMuMzA1NCAxNEwxMi43OTcyIDEyLjQ3MTJMMTQgMTEuNjM5NEgxMi41MzYxTDEyIDEwTDExLjQ3MzIgMTEuNjM5NEgxMEwxMS4yMDI4IDEyLjQ3MTJaIiBmaWxsPSIjNzVCRUZGIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTEuMTcwOCA4LjA4NDc0QzkuODUwODEgOC4zNTkxMSA4Ljc3Njg3IDkuMjc2ODQgOC4yODY5NiAxMC41SDYuNDA4NjdWMTIuNzAxMkM2LjQwODY3IDEyLjc4MjMgNi40MzcyIDEyLjg1MTIgNi40OTg4OCAxMi45MTI3QzYuNTYwNTggMTIuOTc0MSA2LjYzMDA3IDEzLjAwMjggNi43MTIwNSAxMy4wMDI4SDguMTI0ODdDOC4yMTM2NCAxMy4zNTEzIDguMzQ3NzMgMTMuNjgwOSA4LjUyMDU5IDEzLjk4NTFDOC40NTQ2MiAxMy45OTUxIDguMzg3MTUgMTQgOC4zMTgyMyAxNEg2LjcxMjA1QzYuNTMyMjMgMTQgNi4zNjIyMyAxMy45NjYzIDYuMjAzMDYgMTMuODk4NEM2LjA0NTY0IDEzLjgzMTEgNS45MDc1MyAxMy43Mzg4IDUuNzg5NjEgMTMuNjIxM0M1LjY3MTY4IDEzLjUwMzggNS41Nzg5NSAxMy4zNjYxIDUuNTExNDEgMTMuMjA5MUM1LjQ0MzExIDEzLjA1MDMgNS40MDkyNyAxMi44ODA3IDUuNDA5MjcgMTIuNzAxMlYxMS4xMDA5QzUuNDA5MjcgMTAuNjIyIDUuMzE3NzIgMTAuMTc5NSA1LjEzNTUzIDkuNzcyMDlDNC45NTY4MyA5LjM2MzM2IDQuNjk4MzIgOC45OTE1NiA0LjM1OTUzIDguNjU4MDZDMy45MjQ2OCA4LjIyOTAzIDMuNTg4OTYgNy43NTAwMyAzLjM1MzYxIDcuMjIxMzRDMy4xMTc1NiA2LjY5MTA3IDMgNi4xMTY3MiAzIDUuNDk5NTNDMyA1LjA4NjY0IDMuMDUzNDIgNC42ODgwMiAzLjE2MDQ4IDQuMzAzOTdDMy4yNjcyOCAzLjkyMDg5IDMuNDE5MDcgMy41NjI4NiAzLjYxNTk1IDMuMjMwMThDMy44MTI1NyAyLjg5Mzc3IDQuMDQ3NzcgMi41ODkxMSA0LjMyMTQ2IDIuMzE2NDFDNC41OTUwMyAyLjA0MzgzIDQuODk4NTggMS44MDk1MyA1LjIzMTk1IDEuNjEzNjRDNS41Njk3OSAxLjQxNzY0IDUuOTMxNDYgMS4yNjYyIDYuMzE1NzggMS4xNTk4M0M2LjcwMTA2IDEuMDUzMiA3LjEwMDk0IDEgNy41MTUxNCAxQzcuOTI5MzQgMSA4LjMyOTIzIDEuMDUzMiA4LjcxNDUxIDEuMTU5ODNDOS4wOTg4MyAxLjI2NjIgOS40NTgwMyAxLjQxNzM5IDkuNzkxODMgMS42MTM1MUMxMC4xMjk0IDEuODA5MzggMTAuNDM1MSAyLjA0MzcgMTAuNzA4OCAyLjMxNjQxQzEwLjk4MjUgMi41ODkxIDExLjIxNzcgMi44OTM3NiAxMS40MTQzIDMuMjMwMTZDMTEuNjExMiAzLjU2Mjg1IDExLjc2MyAzLjkyMDg4IDExLjg2OTggNC4zMDM5N0MxMS45NzY5IDQuNjg4MDIgMTIuMDMwMyA1LjA4NjY0IDEyLjAzMDMgNS40OTk1M0MxMi4wMzAzIDYuMTE2NzIgMTEuOTEyNyA2LjY5MTA3IDExLjY3NjcgNy4yMjEzNEMxMS41NDEyIDcuNTI1NjIgMTEuMzcyNSA3LjgxMzQ0IDExLjE3MDggOC4wODQ3NFoiIGZpbGw9IiM3NUJFRkYiLz4KPC9zdmc+Cg==") 50% no-repeat}.monaco-editor .codelens-decoration{overflow:hidden;display:inline-block;text-overflow:ellipsis}.monaco-editor .codelens-decoration>a,.monaco-editor .codelens-decoration>span{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;white-space:nowrap;vertical-align:sub}.monaco-editor .codelens-decoration>a{text-decoration:none}.monaco-editor .codelens-decoration>a:hover{text-decoration:underline;cursor:pointer}.monaco-editor .codelens-decoration.invisible-cl{opacity:0}@keyframes fadein{0%{opacity:0;visibility:visible}to{opacity:1}}.monaco-editor .codelens-decoration.fadein{animation:fadein .1s linear}.monaco-action-bar{text-align:right;overflow:hidden;white-space:nowrap}.monaco-action-bar .actions-container{display:-ms-flexbox;display:flex;margin:0 auto;padding:0;width:100%;-ms-flex-pack:end;justify-content:flex-end}.monaco-action-bar.vertical .actions-container{display:inline-block}.monaco-action-bar.reverse .actions-container{-ms-flex-direction:row-reverse;flex-direction:row-reverse}.monaco-action-bar .action-item{cursor:pointer;display:inline-block;transition:transform 50ms ease;position:relative}.monaco-action-bar .action-item.disabled{cursor:default}.monaco-action-bar.animated .action-item.active{transform:scale(1.272019649)}.monaco-action-bar .action-item .icon{display:inline-block}.monaco-action-bar .action-label{font-size:11px;margin-right:4px}.monaco-action-bar .action-label.octicon{font-size:15px;line-height:35px;text-align:center}.monaco-action-bar .action-item.disabled .action-label,.monaco-action-bar .action-item.disabled .action-label:hover{opacity:.4}.monaco-action-bar.vertical{text-align:left}.monaco-action-bar.vertical .action-item{display:block}.monaco-action-bar.vertical .action-label.separator{display:block;border-bottom:1px solid #bbb;padding-top:1px;margin-left:.8em;margin-right:.8em}.monaco-action-bar.animated.vertical .action-item.active{transform:translate(5px)}.secondary-actions .monaco-action-bar .action-label{margin-left:6px}.monaco-action-bar .action-item.select-container{overflow:hidden;-ms-flex:1;flex:1;max-width:170px;min-width:60px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.monaco-editor.vs .dnd-target{border-right:2px dotted #000;color:#fff}.monaco-editor.vs-dark .dnd-target{border-right:2px dotted #aeafad;color:#51504f}.monaco-editor.hc-black .dnd-target{border-right:2px dotted #fff;color:#000}.monaco-editor.hc-black.mac.mouse-default .view-lines,.monaco-editor.mouse-default .view-lines,.monaco-editor.vs-dark.mac.mouse-default .view-lines{cursor:default}.monaco-editor.hc-black.mac.mouse-copy .view-lines,.monaco-editor.mouse-copy .view-lines,.monaco-editor.vs-dark.mac.mouse-copy .view-lines{cursor:copy}.monaco-custom-checkbox{margin-left:2px;float:left;cursor:pointer;overflow:hidden;opacity:.7;width:20px;height:20px;border:1px solid transparent;padding:1px;-o-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-o-user-select:none;-ms-user-select:none;user-select:none}.monaco-custom-checkbox.checked,.monaco-custom-checkbox:hover{opacity:1}.hc-black .monaco-custom-checkbox,.hc-black .monaco-custom-checkbox:hover{background:none}.monaco-custom-checkbox.monaco-simple-checkbox{height:18px;width:18px;border:1px solid transparent;border-radius:3px;margin-right:9px;margin-left:0;padding:0;opacity:1;background-size:16px!important}.monaco-custom-checkbox.monaco-simple-checkbox.checked{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNSAzLjc2MzQ1TDUuODA2ODcgMTEuOTM1MUw1LjA4NTg0IDExLjg5MjdMMSA3LjI5NjE0TDEuNzYzNDUgNi42MTc1Mkw1LjUwOTk3IDEwLjgzMjRMMTQuMzIxNCAzTDE1IDMuNzYzNDVaIiBmaWxsPSIjNDI0MjQyIi8+Cjwvc3ZnPgo=") 50% no-repeat;background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNSAzLjc2MzQ1TDUuODA2ODcgMTEuOTM1MUw1LjA4NTg0IDExLjg5MjdMMSA3LjI5NjE0TDEuNzYzNDUgNi42MTc1Mkw1LjUwOTk3IDEwLjgzMjRMMTQuMzIxNCAzTDE1IDMuNzYzNDVaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=") 50% no-repeat}.vs .monaco-custom-checkbox.monaco-case-sensitive{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03LjQ5NSA5LjA1Mkw4LjM4NiAxMS40MDJIOS40NzdMNi4yMzcgM0g1LjIxN0wyIDExLjQwMkgzLjA5NUwzLjkzMyA5LjA1Mkg3LjQ5NVpNNS44MTEgNC40NTNMNS44NTUgNC41ODhMNy4xNzMgOC4xNjJINC4yNTVMNS41NjIgNC41ODhMNS42MDYgNC40NTNMNS42NDQgNC4yOTdMNS42NzYgNC4xNDVMNS42OTcgNC4wMTlINS43Mkw1Ljc0NCA0LjE0NUw1Ljc3MyA0LjI5N0w1LjgxMSA0LjQ1M1pNMTMuNzk1IDEwLjQ2NFYxMS40SDE0Ljc1NVY3LjQ5OEMxNC43NTUgNi43NzkgMTQuNTc1IDYuMjI2IDE0LjIxNiA1LjgzN0MxMy44NTcgNS40NDggMTMuMzI3IDUuMjU0IDEyLjYyOCA1LjI1NEMxMi40MjkgNS4yNTQgMTIuMjI3IDUuMjczIDEyLjAyMiA1LjMxQzExLjgxNyA1LjM0NyAxMS42MjIgNS4zOTQgMTEuNDM5IDUuNDUxQzExLjI1NiA1LjUwOCAxMS4wOTEgNS41NjkgMTAuOTQ0IDUuNjM2QzEwLjc5NyA1LjcwMyAxMC42ODMgNS43NjUgMTAuNjAxIDUuODI0VjYuODA4QzEwLjg2NyA2LjU3OCAxMS4xNjcgNi4zOTcgMTEuNTA1IDYuMjY4QzExLjg0MyA2LjEzOSAxMi4xOTQgNi4wNzUgMTIuNTU3IDYuMDc1QzEyLjc0NSA2LjA3NSAxMi45MTUgNi4xMDMgMTMuMDcgNi4xNkMxMy4yMjUgNi4yMTcgMTMuMzU3IDYuMzA2IDEzLjQ2NiA2LjQyN0MxMy41NzUgNi41NDggMTMuNjU5IDYuNzA2IDEzLjcxOCA2Ljg5OUMxMy43NzcgNy4wOTIgMTMuODA2IDcuMzI2IDEzLjgwNiA3LjU5OUwxMS45OTUgNy44NTFDMTEuNjUxIDcuODk4IDExLjM1NSA3Ljk3NyAxMS4xMDcgOC4wODhDMTAuODU5IDguMTk5IDEwLjY1NCA4LjMzOSAxMC40OTIgOC41MDdDMTAuMzMgOC42NzUgMTAuMjEgOC44NjggMTAuMTMyIDkuMDg3QzEwLjA1NCA5LjMwNiAxMC4wMTUgOS41NDYgMTAuMDE1IDkuODA4QzEwLjAxNSAxMC4wNTQgMTAuMDU3IDEwLjI4MyAxMC4xMzkgMTAuNDk2QzEwLjIyMSAxMC43MDkgMTAuMzQyIDEwLjg5MyAxMC41MDIgMTEuMDQ3QzEwLjY2MiAxMS4yMDEgMTAuODYyIDExLjMyMyAxMS4xIDExLjQxM0MxMS4zMzggMTEuNTAzIDExLjYxMyAxMS41NDggMTEuOTI2IDExLjU0OEMxMi4zMjggMTEuNTQ4IDEyLjY4NiAxMS40NTYgMTMuMDAxIDExLjI3QzEzLjMxNiAxMS4wODQgMTMuNTczIDEwLjgxNiAxMy43NzIgMTAuNDY0SDEzLjc5NVpNMTEuNjY3IDguNzIxQzExLjg0MyA4LjY1NyAxMi4wNjggOC42MDcgMTIuMzQxIDguNTcyTDEzLjgwNiA4LjM2N1Y4Ljk3NkMxMy44MDYgOS4yMjIgMTMuNzY1IDkuNDUxIDEzLjY4MyA5LjY2NEMxMy42MDEgOS44NzcgMTMuNDg2IDEwLjA2MyAxMy4zNCAxMC4yMjFDMTMuMTk0IDEwLjM3OSAxMy4wMTkgMTAuNTAzIDEyLjgxNiAxMC41OTNDMTIuNjEzIDEwLjY4MyAxMi4zOSAxMC43MjggMTIuMTQ4IDEwLjcyOEMxMS45NjEgMTAuNzI4IDExLjc5NSAxMC43MDMgMTEuNjUzIDEwLjY1MkMxMS41MTEgMTAuNjAxIDExLjM5MiAxMC41MyAxMS4yOTYgMTAuNDQxQzExLjIgMTAuMzUyIDExLjEyNyAxMC4yNDcgMTEuMDc2IDEwLjEyNUMxMS4wMjUgMTAuMDAzIDExIDkuODczIDExIDkuNzMyQzExIDkuNTY4IDExLjAxOCA5LjQyMSAxMS4wNTUgOS4yOTJDMTEuMDkyIDkuMTYzIDExLjE2IDkuMDUxIDExLjI1NyA4Ljk1OEMxMS4zNTQgOC44NjUgMTEuNDkxIDguNzg1IDExLjY2NyA4LjcyMVoiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==") 50% no-repeat}.vs-dark .monaco-custom-checkbox.monaco-case-sensitive{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03LjQ5NSA5LjA1Mkw4LjM4NiAxMS40MDJIOS40NzdMNi4yMzcgM0g1LjIxN0wyIDExLjQwMkgzLjA5NUwzLjkzMyA5LjA1Mkg3LjQ5NVpNNS44MTEgNC40NTNMNS44NTUgNC41ODhMNy4xNzMgOC4xNjJINC4yNTVMNS41NjIgNC41ODhMNS42MDYgNC40NTNMNS42NDQgNC4yOTdMNS42NzYgNC4xNDVMNS42OTcgNC4wMTlINS43Mkw1Ljc0NCA0LjE0NUw1Ljc3MyA0LjI5N0w1LjgxMSA0LjQ1M1pNMTMuNzk1IDEwLjQ2NFYxMS40SDE0Ljc1NVY3LjQ5OEMxNC43NTUgNi43NzkgMTQuNTc1IDYuMjI2IDE0LjIxNiA1LjgzN0MxMy44NTcgNS40NDggMTMuMzI3IDUuMjU0IDEyLjYyOCA1LjI1NEMxMi40MjkgNS4yNTQgMTIuMjI3IDUuMjczIDEyLjAyMiA1LjMxQzExLjgxNyA1LjM0NyAxMS42MjIgNS4zOTQgMTEuNDM5IDUuNDUxQzExLjI1NiA1LjUwOCAxMS4wOTEgNS41NjkgMTAuOTQ0IDUuNjM2QzEwLjc5NyA1LjcwMyAxMC42ODMgNS43NjUgMTAuNjAxIDUuODI0VjYuODA4QzEwLjg2NyA2LjU3OCAxMS4xNjcgNi4zOTcgMTEuNTA1IDYuMjY4QzExLjg0MyA2LjEzOSAxMi4xOTQgNi4wNzUgMTIuNTU3IDYuMDc1QzEyLjc0NSA2LjA3NSAxMi45MTUgNi4xMDMgMTMuMDcgNi4xNkMxMy4yMjUgNi4yMTcgMTMuMzU3IDYuMzA2IDEzLjQ2NiA2LjQyN0MxMy41NzUgNi41NDggMTMuNjU5IDYuNzA2IDEzLjcxOCA2Ljg5OUMxMy43NzcgNy4wOTIgMTMuODA2IDcuMzI2IDEzLjgwNiA3LjU5OUwxMS45OTUgNy44NTFDMTEuNjUxIDcuODk4IDExLjM1NSA3Ljk3NyAxMS4xMDcgOC4wODhDMTAuODU5IDguMTk5IDEwLjY1NCA4LjMzOSAxMC40OTIgOC41MDdDMTAuMzMgOC42NzUgMTAuMjEgOC44NjggMTAuMTMyIDkuMDg3QzEwLjA1NCA5LjMwNiAxMC4wMTUgOS41NDYgMTAuMDE1IDkuODA4QzEwLjAxNSAxMC4wNTQgMTAuMDU3IDEwLjI4MyAxMC4xMzkgMTAuNDk2QzEwLjIyMSAxMC43MDkgMTAuMzQyIDEwLjg5MyAxMC41MDIgMTEuMDQ3QzEwLjY2MiAxMS4yMDEgMTAuODYyIDExLjMyMyAxMS4xIDExLjQxM0MxMS4zMzggMTEuNTAzIDExLjYxMyAxMS41NDggMTEuOTI2IDExLjU0OEMxMi4zMjggMTEuNTQ4IDEyLjY4NiAxMS40NTYgMTMuMDAxIDExLjI3QzEzLjMxNiAxMS4wODQgMTMuNTczIDEwLjgxNiAxMy43NzIgMTAuNDY0SDEzLjc5NVpNMTEuNjY3IDguNzIxQzExLjg0MyA4LjY1NyAxMi4wNjggOC42MDcgMTIuMzQxIDguNTcyTDEzLjgwNiA4LjM2N1Y4Ljk3NkMxMy44MDYgOS4yMjIgMTMuNzY1IDkuNDUxIDEzLjY4MyA5LjY2NEMxMy42MDEgOS44NzcgMTMuNDg2IDEwLjA2MyAxMy4zNCAxMC4yMjFDMTMuMTk0IDEwLjM3OSAxMy4wMTkgMTAuNTAzIDEyLjgxNiAxMC41OTNDMTIuNjEzIDEwLjY4MyAxMi4zOSAxMC43MjggMTIuMTQ4IDEwLjcyOEMxMS45NjEgMTAuNzI4IDExLjc5NSAxMC43MDMgMTEuNjUzIDEwLjY1MkMxMS41MTEgMTAuNjAxIDExLjM5MiAxMC41MyAxMS4yOTYgMTAuNDQxQzExLjIgMTAuMzUyIDExLjEyNyAxMC4yNDcgMTEuMDc2IDEwLjEyNUMxMS4wMjUgMTAuMDAzIDExIDkuODczIDExIDkuNzMyQzExIDkuNTY4IDExLjAxOCA5LjQyMSAxMS4wNTUgOS4yOTJDMTEuMDkyIDkuMTYzIDExLjE2IDkuMDUxIDExLjI1NyA4Ljk1OEMxMS4zNTQgOC44NjUgMTEuNDkxIDguNzg1IDExLjY2NyA4LjcyMVoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==") 50% no-repeat}.hc-black .monaco-custom-checkbox.monaco-case-sensitive,.hc-black .monaco-custom-checkbox.monaco-case-sensitive:hover{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03LjQ5NSA5LjA1Mkw4LjM4NiAxMS40MDJIOS40NzdMNi4yMzcgM0g1LjIxN0wyIDExLjQwMkgzLjA5NUwzLjkzMyA5LjA1Mkg3LjQ5NVpNNS44MTEgNC40NTNMNS44NTUgNC41ODhMNy4xNzMgOC4xNjJINC4yNTVMNS41NjIgNC41ODhMNS42MDYgNC40NTNMNS42NDQgNC4yOTdMNS42NzYgNC4xNDVMNS42OTcgNC4wMTlINS43Mkw1Ljc0NCA0LjE0NUw1Ljc3MyA0LjI5N0w1LjgxMSA0LjQ1M1pNMTMuNzk1IDEwLjQ2NFYxMS40SDE0Ljc1NVY3LjQ5OEMxNC43NTUgNi43NzkgMTQuNTc1IDYuMjI2IDE0LjIxNiA1LjgzN0MxMy44NTcgNS40NDggMTMuMzI3IDUuMjU0IDEyLjYyOCA1LjI1NEMxMi40MjkgNS4yNTQgMTIuMjI3IDUuMjczIDEyLjAyMiA1LjMxQzExLjgxNyA1LjM0NyAxMS42MjIgNS4zOTQgMTEuNDM5IDUuNDUxQzExLjI1NiA1LjUwOCAxMS4wOTEgNS41NjkgMTAuOTQ0IDUuNjM2QzEwLjc5NyA1LjcwMyAxMC42ODMgNS43NjUgMTAuNjAxIDUuODI0VjYuODA4QzEwLjg2NyA2LjU3OCAxMS4xNjcgNi4zOTcgMTEuNTA1IDYuMjY4QzExLjg0MyA2LjEzOSAxMi4xOTQgNi4wNzUgMTIuNTU3IDYuMDc1QzEyLjc0NSA2LjA3NSAxMi45MTUgNi4xMDMgMTMuMDcgNi4xNkMxMy4yMjUgNi4yMTcgMTMuMzU3IDYuMzA2IDEzLjQ2NiA2LjQyN0MxMy41NzUgNi41NDggMTMuNjU5IDYuNzA2IDEzLjcxOCA2Ljg5OUMxMy43NzcgNy4wOTIgMTMuODA2IDcuMzI2IDEzLjgwNiA3LjU5OUwxMS45OTUgNy44NTFDMTEuNjUxIDcuODk4IDExLjM1NSA3Ljk3NyAxMS4xMDcgOC4wODhDMTAuODU5IDguMTk5IDEwLjY1NCA4LjMzOSAxMC40OTIgOC41MDdDMTAuMzMgOC42NzUgMTAuMjEgOC44NjggMTAuMTMyIDkuMDg3QzEwLjA1NCA5LjMwNiAxMC4wMTUgOS41NDYgMTAuMDE1IDkuODA4QzEwLjAxNSAxMC4wNTQgMTAuMDU3IDEwLjI4MyAxMC4xMzkgMTAuNDk2QzEwLjIyMSAxMC43MDkgMTAuMzQyIDEwLjg5MyAxMC41MDIgMTEuMDQ3QzEwLjY2MiAxMS4yMDEgMTAuODYyIDExLjMyMyAxMS4xIDExLjQxM0MxMS4zMzggMTEuNTAzIDExLjYxMyAxMS41NDggMTEuOTI2IDExLjU0OEMxMi4zMjggMTEuNTQ4IDEyLjY4NiAxMS40NTYgMTMuMDAxIDExLjI3QzEzLjMxNiAxMS4wODQgMTMuNTczIDEwLjgxNiAxMy43NzIgMTAuNDY0SDEzLjc5NVpNMTEuNjY3IDguNzIxQzExLjg0MyA4LjY1NyAxMi4wNjggOC42MDcgMTIuMzQxIDguNTcyTDEzLjgwNiA4LjM2N1Y4Ljk3NkMxMy44MDYgOS4yMjIgMTMuNzY1IDkuNDUxIDEzLjY4MyA5LjY2NEMxMy42MDEgOS44NzcgMTMuNDg2IDEwLjA2MyAxMy4zNCAxMC4yMjFDMTMuMTk0IDEwLjM3OSAxMy4wMTkgMTAuNTAzIDEyLjgxNiAxMC41OTNDMTIuNjEzIDEwLjY4MyAxMi4zOSAxMC43MjggMTIuMTQ4IDEwLjcyOEMxMS45NjEgMTAuNzI4IDExLjc5NSAxMC43MDMgMTEuNjUzIDEwLjY1MkMxMS41MTEgMTAuNjAxIDExLjM5MiAxMC41MyAxMS4yOTYgMTAuNDQxQzExLjIgMTAuMzUyIDExLjEyNyAxMC4yNDcgMTEuMDc2IDEwLjEyNUMxMS4wMjUgMTAuMDAzIDExIDkuODczIDExIDkuNzMyQzExIDkuNTY4IDExLjAxOCA5LjQyMSAxMS4wNTUgOS4yOTJDMTEuMDkyIDkuMTYzIDExLjE2IDkuMDUxIDExLjI1NyA4Ljk1OEMxMS4zNTQgOC44NjUgMTEuNDkxIDguNzg1IDExLjY2NyA4LjcyMVoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=") 50% no-repeat}.vs .monaco-custom-checkbox.monaco-preserve-case{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTguNTM0MzcgMTIuNDY3M0g3LjQzNjFMNi41Mzg1OSAxMC4wOTM2SDIuOTQ4NTRMMi4xMDQxOCAxMi40NjczSDFMNC4yNDc1NyA0SDUuMjc0OTlMOC41MzQzNyAxMi40NjczWk02LjIxMzgzIDkuMjAyMDJMNC44ODUyOCA1LjU5NDI2QzQuODQxOTggNS40NzYxNyA0Ljc5ODY4IDUuMjg3MjIgNC43NTUzOCA1LjAyNzQxSDQuNzMxNzZDNC42OTIzOSA1LjI2NzU0IDQuNjQ3MTMgNS40NTY0OSA0LjU5NTk1IDUuNTk0MjZMMy4yNzkyMSA5LjIwMjAySDYuMjEzODNaIiBmaWxsPSIjNDI0MjQyIi8+CjxwYXRoIGQ9Ik05Ljc4NjE3IDEyLjQ2NzNWNEgxMi4xOTUzQzEyLjkyNzUgNCAxMy41MDgxIDQuMTc5MTEgMTMuOTM3MiA0LjUzNzMzQzE0LjM2NjIgNC44OTU1NCAxNC41ODA4IDUuMzYyMDEgMTQuNTgwOCA1LjkzNjc0QzE0LjU4MDggNi40MTY5OCAxNC40NTA5IDYuODM0MjUgMTQuMTkxMSA3LjE4ODUzQzEzLjkzMTMgNy41NDI4MSAxMy41NzMgNy43OTQ3NCAxMy4xMTY0IDcuOTQ0MzNWNy45Njc5NUMxMy42ODcyIDguMDM0ODcgMTQuMTQzOCA4LjI1MTM3IDE0LjQ4NjMgOC42MTc0NkMxNC44Mjg4IDguOTc5NjEgMTUgOS40NTE5OSAxNSAxMC4wMzQ2QzE1IDEwLjc1ODkgMTQuNzQwMiAxMS4zNDU0IDE0LjIyMDYgMTEuNzk0MkMxMy43MDEgMTIuMjQyOSAxMy4wNDU2IDEyLjQ2NzMgMTIuMjU0MyAxMi40NjczSDkuNzg2MTdaTTEwLjc3ODIgNC44OTc1MVY3LjYzMTM4SDExLjc5MzhDMTIuMzM3IDcuNjMxMzggMTIuNzY0MSA3LjUwMTQ4IDEzLjA3NTEgNy4yNDE2N0MxMy4zODYxIDYuOTc3OTMgMTMuNTQxNSA2LjYwNzkgMTMuNTQxNSA2LjEzMTU5QzEzLjU0MTUgNS4zMDg4NyAxMy4wMDAzIDQuODk3NTEgMTEuOTE3OCA0Ljg5NzUxSDEwLjc3ODJaTTEwLjc3ODIgOC41MjI5OVYxMS41Njk4SDEyLjEyNDRDMTIuNzA3IDExLjU2OTggMTMuMTU3NyAxMS40MzIgMTMuNDc2NiAxMS4xNTY1QzEzLjc5OTQgMTAuODgwOSAxMy45NjA4IDEwLjUwMyAxMy45NjA4IDEwLjAyMjhDMTMuOTYwOCA5LjAyMjkyIDEzLjI3OTggOC41MjI5OSAxMS45MTc4IDguNTIyOTlIMTAuNzc4MloiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==") 50% no-repeat}.vs-dark .monaco-custom-checkbox.monaco-preserve-case{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTguNTM0MzcgMTIuNDY3M0g3LjQzNjFMNi41Mzg1OSAxMC4wOTM2SDIuOTQ4NTRMMi4xMDQxOCAxMi40NjczSDFMNC4yNDc1NyA0SDUuMjc0OTlMOC41MzQzNyAxMi40NjczWk02LjIxMzgzIDkuMjAyMDJMNC44ODUyOCA1LjU5NDI2QzQuODQxOTggNS40NzYxNyA0Ljc5ODY4IDUuMjg3MjIgNC43NTUzOCA1LjAyNzQxSDQuNzMxNzZDNC42OTIzOSA1LjI2NzU0IDQuNjQ3MTMgNS40NTY0OSA0LjU5NTk1IDUuNTk0MjZMMy4yNzkyMSA5LjIwMjAySDYuMjEzODNaIiBmaWxsPSIjQzVDNUM1Ii8+CjxwYXRoIGQ9Ik05Ljc4NjE3IDEyLjQ2NzNWNEgxMi4xOTUzQzEyLjkyNzUgNCAxMy41MDgxIDQuMTc5MTEgMTMuOTM3MiA0LjUzNzMzQzE0LjM2NjIgNC44OTU1NCAxNC41ODA4IDUuMzYyMDEgMTQuNTgwOCA1LjkzNjc0QzE0LjU4MDggNi40MTY5OCAxNC40NTA5IDYuODM0MjUgMTQuMTkxMSA3LjE4ODUzQzEzLjkzMTMgNy41NDI4MSAxMy41NzMgNy43OTQ3NCAxMy4xMTY0IDcuOTQ0MzNWNy45Njc5NUMxMy42ODcyIDguMDM0ODcgMTQuMTQzOCA4LjI1MTM3IDE0LjQ4NjMgOC42MTc0NkMxNC44Mjg4IDguOTc5NjEgMTUgOS40NTE5OSAxNSAxMC4wMzQ2QzE1IDEwLjc1ODkgMTQuNzQwMiAxMS4zNDU0IDE0LjIyMDYgMTEuNzk0MkMxMy43MDEgMTIuMjQyOSAxMy4wNDU2IDEyLjQ2NzMgMTIuMjU0MyAxMi40NjczSDkuNzg2MTdaTTEwLjc3ODIgNC44OTc1MVY3LjYzMTM4SDExLjc5MzhDMTIuMzM3IDcuNjMxMzggMTIuNzY0MSA3LjUwMTQ4IDEzLjA3NTEgNy4yNDE2N0MxMy4zODYxIDYuOTc3OTMgMTMuNTQxNSA2LjYwNzkgMTMuNTQxNSA2LjEzMTU5QzEzLjU0MTUgNS4zMDg4NyAxMy4wMDAzIDQuODk3NTEgMTEuOTE3OCA0Ljg5NzUxSDEwLjc3ODJaTTEwLjc3ODIgOC41MjI5OVYxMS41Njk4SDEyLjEyNDRDMTIuNzA3IDExLjU2OTggMTMuMTU3NyAxMS40MzIgMTMuNDc2NiAxMS4xNTY1QzEzLjc5OTQgMTAuODgwOSAxMy45NjA4IDEwLjUwMyAxMy45NjA4IDEwLjAyMjhDMTMuOTYwOCA5LjAyMjkyIDEzLjI3OTggOC41MjI5OSAxMS45MTc4IDguNTIyOTlIMTAuNzc4MloiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==") 50% no-repeat}.hc-black .monaco-custom-checkbox.monaco-preserve-case,.hc-black .monaco-custom-checkbox.monaco-preserve-case:hover{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTguNTM0MzcgMTIuNDY3M0g3LjQzNjFMNi41Mzg1OSAxMC4wOTM2SDIuOTQ4NTRMMi4xMDQxOCAxMi40NjczSDFMNC4yNDc1NyA0SDUuMjc0OTlMOC41MzQzNyAxMi40NjczWk02LjIxMzgzIDkuMjAyMDJMNC44ODUyOCA1LjU5NDI2QzQuODQxOTggNS40NzYxNyA0Ljc5ODY4IDUuMjg3MjIgNC43NTUzOCA1LjAyNzQxSDQuNzMxNzZDNC42OTIzOSA1LjI2NzU0IDQuNjQ3MTMgNS40NTY0OSA0LjU5NTk1IDUuNTk0MjZMMy4yNzkyMSA5LjIwMjAySDYuMjEzODNaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNOS43ODYxNyAxMi40NjczVjRIMTIuMTk1M0MxMi45Mjc1IDQgMTMuNTA4MSA0LjE3OTExIDEzLjkzNzIgNC41MzczM0MxNC4zNjYyIDQuODk1NTQgMTQuNTgwOCA1LjM2MjAxIDE0LjU4MDggNS45MzY3NEMxNC41ODA4IDYuNDE2OTggMTQuNDUwOSA2LjgzNDI1IDE0LjE5MTEgNy4xODg1M0MxMy45MzEzIDcuNTQyODEgMTMuNTczIDcuNzk0NzQgMTMuMTE2NCA3Ljk0NDMzVjcuOTY3OTVDMTMuNjg3MiA4LjAzNDg3IDE0LjE0MzggOC4yNTEzNyAxNC40ODYzIDguNjE3NDZDMTQuODI4OCA4Ljk3OTYxIDE1IDkuNDUxOTkgMTUgMTAuMDM0NkMxNSAxMC43NTg5IDE0Ljc0MDIgMTEuMzQ1NCAxNC4yMjA2IDExLjc5NDJDMTMuNzAxIDEyLjI0MjkgMTMuMDQ1NiAxMi40NjczIDEyLjI1NDMgMTIuNDY3M0g5Ljc4NjE3Wk0xMC43NzgyIDQuODk3NTFWNy42MzEzOEgxMS43OTM4QzEyLjMzNyA3LjYzMTM4IDEyLjc2NDEgNy41MDE0OCAxMy4wNzUxIDcuMjQxNjdDMTMuMzg2MSA2Ljk3NzkzIDEzLjU0MTUgNi42MDc5IDEzLjU0MTUgNi4xMzE1OUMxMy41NDE1IDUuMzA4ODcgMTMuMDAwMyA0Ljg5NzUxIDExLjkxNzggNC44OTc1MUgxMC43NzgyWk0xMC43NzgyIDguNTIyOTlWMTEuNTY5OEgxMi4xMjQ0QzEyLjcwNyAxMS41Njk4IDEzLjE1NzcgMTEuNDMyIDEzLjQ3NjYgMTEuMTU2NUMxMy43OTk0IDEwLjg4MDkgMTMuOTYwOCAxMC41MDMgMTMuOTYwOCAxMC4wMjI4QzEzLjk2MDggOS4wMjI5MiAxMy4yNzk4IDguNTIyOTkgMTEuOTE3OCA4LjUyMjk5SDEwLjc3ODJaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K") 50% no-repeat}.vs .monaco-custom-checkbox.monaco-whole-word{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xIDJIMTVWM0gxVjJaTTE0IDRIMTNWMTJIMTRWNFpNMTEuMjcyIDguMzg3QzExLjE5NCA4LjA4OCAxMS4wNzMgNy44MjUgMTAuOTEyIDcuNjAxQzEwLjc1MSA3LjM3NyAxMC41NDcgNy4yIDEwLjMwMyA3LjA3MUMxMC4wNTkgNi45NDIgOS43NjkgNi44NzggOS40MzcgNi44NzhDOS4yMzkgNi44NzggOS4wNTcgNi45MDIgOC44OSA2Ljk1MUM4LjcyNSA3IDguNTc0IDcuMDY4IDguNDM3IDcuMTU2QzguMzAxIDcuMjQ0IDguMTggNy4zNSA4LjA3MiA3LjQ3NEw3Ljg5MyA3LjczMlY0LjU3OEg3VjEySDcuODkzVjExLjQyNUw4LjAxOSAxMS42QzguMTA2IDExLjcwMiA4LjIwOCAxMS43OSA4LjMyMyAxMS44NjlDOC40NCAxMS45NDcgOC41NzIgMTIuMDA5IDguNzIxIDEyLjA1NUM4Ljg3IDEyLjEwMSA5LjAzNSAxMi4xMjMgOS4yMTkgMTIuMTIzQzkuNTcyIDEyLjEyMyA5Ljg4NSAxMi4wNTIgMTAuMTU2IDExLjkxMUMxMC40MjggMTEuNzY4IDEwLjY1NSAxMS41NzMgMTAuODM4IDExLjMyNUMxMS4wMjEgMTEuMDc1IDExLjE1OSAxMC43ODIgMTEuMjUyIDEwLjQ0NkMxMS4zNDUgMTAuMTA4IDExLjM5MiA5Ljc0MyAxMS4zOTIgOS4zNDlDMTEuMzkxIDkuMDA3IDExLjM1MiA4LjY4NiAxMS4yNzIgOC4zODdaTTkuNzkzIDcuNzhDOS45NDQgNy44NTEgMTAuMDc1IDcuOTU2IDEwLjE4MyA4LjA5NEMxMC4yOTIgOC4yMzQgMTAuMzc3IDguNDA3IDEwLjQzOCA4LjYxMUMxMC40ODkgOC43ODUgMTAuNTIgOC45ODIgMTAuNTI3IDkuMTk4TDEwLjUyIDkuMzIzQzEwLjUyIDkuNjUgMTAuNDg3IDkuOTQzIDEwLjQyIDEwLjE5MkMxMC4zNTMgMTAuNDM4IDEwLjI1OSAxMC42NDUgMTAuMTQyIDEwLjgwNkMxMC4wMjUgMTAuOTY4IDkuODgyIDExLjA5MSA5LjcyMSAxMS4xNzJDOS4zOTkgMTEuMzM0IDguOTYxIDExLjMzOCA4LjY1MiAxMS4xODdDOC40OTkgMTEuMTEyIDguMzY2IDExLjAxMiA4LjI1OSAxMC44OTFDOC4xNzQgMTAuNzk1IDguMTAzIDEwLjY3NSA4LjA0MSAxMC41MjRDOC4wNDEgMTAuNTI0IDcuODYyIDEwLjA3NyA3Ljg2MiA5LjU3N0M3Ljg2MiA5LjA3NyA4LjA0MSA4LjU3NSA4LjA0MSA4LjU3NUM4LjEwMyA4LjM5OCA4LjE3NyA4LjI1NyA4LjI2NSA4LjE0NUM4LjM3OSA4LjAwMiA4LjUyMSA3Ljg4NiA4LjY4OSA3LjhDOC44NTcgNy43MTQgOS4wNTQgNy42NzEgOS4yNzYgNy42NzFDOS40NjYgNy42NzEgOS42NCA3LjcwOCA5Ljc5MyA3Ljc4Wk0xNSAxM0gxVjE0SDE1VjEzWk0yLjgxMyAxMEwyLjA4NSAxMi4wMzFIMUwxLjAyNSAxMS45NTlMMy40NjYgNC44NzMwNUg0LjQwN0w2Ljg5MiAxMi4wMzFINS44MUw1LjAzMiAxMEgyLjgxM1pNMy45MzQgNi40MjIwNUgzLjkxMkwzLjAwNyA5LjE3NTA1SDQuODQ4TDMuOTM0IDYuNDIyMDVaIiBmaWxsPSIjNDI0MjQyIi8+Cjwvc3ZnPgo=") 50% no-repeat}.vs-dark .monaco-custom-checkbox.monaco-whole-word{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xIDJIMTVWM0gxVjJaTTE0IDRIMTNWMTJIMTRWNFpNMTEuMjcyIDguMzg3QzExLjE5NCA4LjA4OCAxMS4wNzMgNy44MjUgMTAuOTEyIDcuNjAxQzEwLjc1MSA3LjM3NyAxMC41NDcgNy4yIDEwLjMwMyA3LjA3MUMxMC4wNTkgNi45NDIgOS43NjkgNi44NzggOS40MzcgNi44NzhDOS4yMzkgNi44NzggOS4wNTcgNi45MDIgOC44OSA2Ljk1MUM4LjcyNSA3IDguNTc0IDcuMDY4IDguNDM3IDcuMTU2QzguMzAxIDcuMjQ0IDguMTggNy4zNSA4LjA3MiA3LjQ3NEw3Ljg5MyA3LjczMlY0LjU3OEg3VjEySDcuODkzVjExLjQyNUw4LjAxOSAxMS42QzguMTA2IDExLjcwMiA4LjIwOCAxMS43OSA4LjMyMyAxMS44NjlDOC40NCAxMS45NDcgOC41NzIgMTIuMDA5IDguNzIxIDEyLjA1NUM4Ljg3IDEyLjEwMSA5LjAzNSAxMi4xMjMgOS4yMTkgMTIuMTIzQzkuNTcyIDEyLjEyMyA5Ljg4NSAxMi4wNTIgMTAuMTU2IDExLjkxMUMxMC40MjggMTEuNzY4IDEwLjY1NSAxMS41NzMgMTAuODM4IDExLjMyNUMxMS4wMjEgMTEuMDc1IDExLjE1OSAxMC43ODIgMTEuMjUyIDEwLjQ0NkMxMS4zNDUgMTAuMTA4IDExLjM5MiA5Ljc0MyAxMS4zOTIgOS4zNDlDMTEuMzkxIDkuMDA3IDExLjM1MiA4LjY4NiAxMS4yNzIgOC4zODdaTTkuNzkzIDcuNzhDOS45NDQgNy44NTEgMTAuMDc1IDcuOTU2IDEwLjE4MyA4LjA5NEMxMC4yOTIgOC4yMzQgMTAuMzc3IDguNDA3IDEwLjQzOCA4LjYxMUMxMC40ODkgOC43ODUgMTAuNTIgOC45ODIgMTAuNTI3IDkuMTk4TDEwLjUyIDkuMzIzQzEwLjUyIDkuNjUgMTAuNDg3IDkuOTQzIDEwLjQyIDEwLjE5MkMxMC4zNTMgMTAuNDM4IDEwLjI1OSAxMC42NDUgMTAuMTQyIDEwLjgwNkMxMC4wMjUgMTAuOTY4IDkuODgyIDExLjA5MSA5LjcyMSAxMS4xNzJDOS4zOTkgMTEuMzM0IDguOTYxIDExLjMzOCA4LjY1MiAxMS4xODdDOC40OTkgMTEuMTEyIDguMzY2IDExLjAxMiA4LjI1OSAxMC44OTFDOC4xNzQgMTAuNzk1IDguMTAzIDEwLjY3NSA4LjA0MSAxMC41MjRDOC4wNDEgMTAuNTI0IDcuODYyIDEwLjA3NyA3Ljg2MiA5LjU3N0M3Ljg2MiA5LjA3NyA4LjA0MSA4LjU3NSA4LjA0MSA4LjU3NUM4LjEwMyA4LjM5OCA4LjE3NyA4LjI1NyA4LjI2NSA4LjE0NUM4LjM3OSA4LjAwMiA4LjUyMSA3Ljg4NiA4LjY4OSA3LjhDOC44NTcgNy43MTQgOS4wNTQgNy42NzEgOS4yNzYgNy42NzFDOS40NjYgNy42NzEgOS42NCA3LjcwOCA5Ljc5MyA3Ljc4Wk0xNSAxM0gxVjE0SDE1VjEzWk0yLjgxMyAxMEwyLjA4NSAxMi4wMzFIMUwxLjAyNSAxMS45NTlMMy40NjYgNC44NzMwNUg0LjQwN0w2Ljg5MiAxMi4wMzFINS44MUw1LjAzMiAxMEgyLjgxM1pNMy45MzQgNi40MjIwNUgzLjkxMkwzLjAwNyA5LjE3NTA1SDQuODQ4TDMuOTM0IDYuNDIyMDVaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=") 50% no-repeat}.hc-black .monaco-custom-checkbox.monaco-whole-word,.hc-black .monaco-custom-checkbox.monaco-whole-word:hover{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xIDJIMTVWM0gxVjJaTTE0IDRIMTNWMTJIMTRWNFpNMTEuMjcyIDguMzg3QzExLjE5NCA4LjA4OCAxMS4wNzMgNy44MjUgMTAuOTEyIDcuNjAxQzEwLjc1MSA3LjM3NyAxMC41NDcgNy4yIDEwLjMwMyA3LjA3MUMxMC4wNTkgNi45NDIgOS43NjkgNi44NzggOS40MzcgNi44NzhDOS4yMzkgNi44NzggOS4wNTcgNi45MDIgOC44OSA2Ljk1MUM4LjcyNSA3IDguNTc0IDcuMDY4IDguNDM3IDcuMTU2QzguMzAxIDcuMjQ0IDguMTggNy4zNSA4LjA3MiA3LjQ3NEw3Ljg5MyA3LjczMlY0LjU3OEg3VjEySDcuODkzVjExLjQyNUw4LjAxOSAxMS42QzguMTA2IDExLjcwMiA4LjIwOCAxMS43OSA4LjMyMyAxMS44NjlDOC40NCAxMS45NDcgOC41NzIgMTIuMDA5IDguNzIxIDEyLjA1NUM4Ljg3IDEyLjEwMSA5LjAzNSAxMi4xMjMgOS4yMTkgMTIuMTIzQzkuNTcyIDEyLjEyMyA5Ljg4NSAxMi4wNTIgMTAuMTU2IDExLjkxMUMxMC40MjggMTEuNzY4IDEwLjY1NSAxMS41NzMgMTAuODM4IDExLjMyNUMxMS4wMjEgMTEuMDc1IDExLjE1OSAxMC43ODIgMTEuMjUyIDEwLjQ0NkMxMS4zNDUgMTAuMTA4IDExLjM5MiA5Ljc0MyAxMS4zOTIgOS4zNDlDMTEuMzkxIDkuMDA3IDExLjM1MiA4LjY4NiAxMS4yNzIgOC4zODdaTTkuNzkzIDcuNzhDOS45NDQgNy44NTEgMTAuMDc1IDcuOTU2IDEwLjE4MyA4LjA5NEMxMC4yOTIgOC4yMzQgMTAuMzc3IDguNDA3IDEwLjQzOCA4LjYxMUMxMC40ODkgOC43ODUgMTAuNTIgOC45ODIgMTAuNTI3IDkuMTk4TDEwLjUyIDkuMzIzQzEwLjUyIDkuNjUgMTAuNDg3IDkuOTQzIDEwLjQyIDEwLjE5MkMxMC4zNTMgMTAuNDM4IDEwLjI1OSAxMC42NDUgMTAuMTQyIDEwLjgwNkMxMC4wMjUgMTAuOTY4IDkuODgyIDExLjA5MSA5LjcyMSAxMS4xNzJDOS4zOTkgMTEuMzM0IDguOTYxIDExLjMzOCA4LjY1MiAxMS4xODdDOC40OTkgMTEuMTEyIDguMzY2IDExLjAxMiA4LjI1OSAxMC44OTFDOC4xNzQgMTAuNzk1IDguMTAzIDEwLjY3NSA4LjA0MSAxMC41MjRDOC4wNDEgMTAuNTI0IDcuODYyIDEwLjA3NyA3Ljg2MiA5LjU3N0M3Ljg2MiA5LjA3NyA4LjA0MSA4LjU3NSA4LjA0MSA4LjU3NUM4LjEwMyA4LjM5OCA4LjE3NyA4LjI1NyA4LjI2NSA4LjE0NUM4LjM3OSA4LjAwMiA4LjUyMSA3Ljg4NiA4LjY4OSA3LjhDOC44NTcgNy43MTQgOS4wNTQgNy42NzEgOS4yNzYgNy42NzFDOS40NjYgNy42NzEgOS42NCA3LjcwOCA5Ljc5MyA3Ljc4Wk0xNSAxM0gxVjE0SDE1VjEzWk0yLjgxMyAxMEwyLjA4NSAxMi4wMzFIMUwxLjAyNSAxMS45NTlMMy40NjYgNC44NzMwNUg0LjQwN0w2Ljg5MiAxMi4wMzFINS44MUw1LjAzMiAxMEgyLjgxM1pNMy45MzQgNi40MjIwNUgzLjkxMkwzLjAwNyA5LjE3NTA1SDQuODQ4TDMuOTM0IDYuNDIyMDVaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K") 50% no-repeat}.vs .monaco-custom-checkbox.monaco-regex{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC4wMTIyIDJIMTAuOTg3OVY1LjExMzQ2TDEzLjU0ODkgMy41NTYwOUwxNC4wMzQgNC40NDA5NUwxMS40NzAyIDZMMTQuMDM0IDcuNTU5MDVMMTMuNTQ4OSA4LjQ0MzkxTDEwLjk4NzkgNi44ODY1NFYxMEgxMC4wMTIyVjYuODg2NTRMNy40NTExNCA4LjQ0MzkxTDYuOTY2MDYgNy41NTkwNUw5LjUyOTkgNkw2Ljk2NjA2IDQuNDQwOTVMNy40NTExNCAzLjU1NjA5TDEwLjAxMjIgNS4xMTM0NlYyWk0yIDEwSDZWMTRIMlYxMFoiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==") 50% no-repeat}.vs-dark .monaco-custom-checkbox.monaco-regex{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC4wMTIyIDJIMTAuOTg3OVY1LjExMzQ2TDEzLjU0ODkgMy41NTYwOUwxNC4wMzQgNC40NDA5NUwxMS40NzAyIDZMMTQuMDM0IDcuNTU5MDVMMTMuNTQ4OSA4LjQ0MzkxTDEwLjk4NzkgNi44ODY1NFYxMEgxMC4wMTIyVjYuODg2NTRMNy40NTExNCA4LjQ0MzkxTDYuOTY2MDYgNy41NTkwNUw5LjUyOTkgNkw2Ljk2NjA2IDQuNDQwOTVMNy40NTExNCAzLjU1NjA5TDEwLjAxMjIgNS4xMTM0NlYyWk0yIDEwSDZWMTRIMlYxMFoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==") 50% no-repeat}.hc-black .monaco-custom-checkbox.monaco-regex,.hc-black .monaco-custom-checkbox.monaco-regex:hover{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC4wMTIyIDJIMTAuOTg3OVY1LjExMzQ2TDEzLjU0ODkgMy41NTYwOUwxNC4wMzQgNC40NDA5NUwxMS40NzAyIDZMMTQuMDM0IDcuNTU5MDVMMTMuNTQ4OSA4LjQ0MzkxTDEwLjk4NzkgNi44ODY1NFYxMEgxMC4wMTIyVjYuODg2NTRMNy40NTExNCA4LjQ0MzkxTDYuOTY2MDYgNy41NTkwNUw5LjUyOTkgNkw2Ljk2NjA2IDQuNDQwOTVMNy40NTExNCAzLjU1NjA5TDEwLjAxMjIgNS4xMTM0NlYyWk0yIDEwSDZWMTRIMlYxMFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=") 50% no-repeat}.monaco-checkbox .label{width:12px;height:12px;border:1px solid #000;background-color:transparent;display:inline-block}.monaco-checkbox .checkbox{position:absolute;overflow:hidden;clip:rect(0 0 0 0);height:1px;width:1px;margin:-1px;padding:0;border:0}.monaco-checkbox .checkbox:checked+.label{background-color:#000}.monaco-editor .find-widget{position:absolute;z-index:10;top:-44px;height:33px;overflow:hidden;line-height:19px;transition:top .2s linear;padding:0 4px;box-sizing:border-box}.monaco-editor .find-widget.hiddenEditor{display:none}.monaco-editor .find-widget.replaceToggled{top:-74px}.monaco-editor .find-widget.replaceToggled>.replace-part{display:-ms-flexbox;display:flex;display:-webkit-flex}.monaco-editor .find-widget.replaceToggled.visible,.monaco-editor .find-widget.visible{top:0}.monaco-editor .find-widget.multipleline{top:unset;bottom:10px}.monaco-editor .find-widget.multipleline.replaceToggled.visible,.monaco-editor .find-widget.multipleline.visible{top:0;bottom:unset}.monaco-editor .find-widget .monaco-inputbox.synthetic-focus{outline:1px solid -webkit-focus-ring-color;outline-offset:-1px}.monaco-editor .find-widget .monaco-inputbox .input{background-color:transparent;min-height:0}.monaco-editor .find-widget .monaco-findInput .input{font-size:13px}.monaco-editor .find-widget>.find-part,.monaco-editor .find-widget>.replace-part{margin:4px 0 0 17px;font-size:12px;display:-ms-flexbox;display:flex;display:-webkit-flex}.monaco-editor .find-widget>.find-part .monaco-inputbox,.monaco-editor .find-widget>.replace-part .monaco-inputbox{min-height:25px}.monaco-editor .find-widget>.replace-part .monaco-inputbox>.wrapper>.mirror{padding-right:22px}.monaco-editor .find-widget>.find-part .monaco-inputbox>.wrapper>.input,.monaco-editor .find-widget>.find-part .monaco-inputbox>.wrapper>.mirror,.monaco-editor .find-widget>.replace-part .monaco-inputbox>.wrapper>.input,.monaco-editor .find-widget>.replace-part .monaco-inputbox>.wrapper>.mirror{padding-top:2px;padding-bottom:2px}.monaco-editor .find-widget>.find-part .find-actions,.monaco-editor .find-widget>.replace-part .replace-actions{height:25px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.monaco-editor .find-widget .monaco-findInput{vertical-align:middle;display:-ms-flexbox;display:flex;display:-webkit-flex;-ms-flex:1;flex:1}.monaco-editor .find-widget .monaco-findInput .monaco-scrollable-element{width:100%}.monaco-editor .find-widget .monaco-findInput .monaco-scrollable-element .scrollbar.vertical{opacity:0}.monaco-editor .find-widget .matchesCount{margin:0 0 0 3px;padding:2px 0 0 2px;height:25px;vertical-align:middle;box-sizing:border-box;text-align:center;line-height:23px}.monaco-editor .find-widget .button,.monaco-editor .find-widget .matchesCount{display:-ms-flexbox;display:flex;display:-webkit-flex;-ms-flex:initial;flex:initial}.monaco-editor .find-widget .button{min-width:20px;width:20px;height:20px;margin-left:3px;background-position:50%;background-repeat:no-repeat;cursor:pointer}.monaco-editor .find-widget .button:not(.disabled):hover{background-color:rgba(0,0,0,.1)}.monaco-editor .find-widget .button.left{margin-left:0;margin-right:3px}.monaco-editor .find-widget .button.wide{width:auto;padding:1px 6px;top:-1px}.monaco-editor .find-widget .button.toggle{position:absolute;top:0;left:0;width:18px;height:100%;-o-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.monaco-editor .find-widget .button.toggle.disabled{display:none}.monaco-editor .find-widget .previous{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMy44NTM1IDYuMjkyODRMOC44NTM1NiAxLjI5Mjg1SDguMTQ2NDVMMy4xNDY0NSA2LjI5Mjg0TDMuODUzNTYgNi45OTk5NUw4IDIuODUzNTFWMTUuMDYwNkg5VjIuODUzNTFMMTMuMTQ2NCA2Ljk5OTk1TDEzLjg1MzUgNi4yOTI4NFoiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==")}.monaco-editor .find-widget .next{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zLjE0NjQ2IDkuNzY3ODNMOC4xNDY0NCAxNC43Njc4TDguODUzNTUgMTQuNzY3OEwxMy44NTM1IDkuNzY3ODNMMTMuMTQ2NCA5LjA2MDcyTDkgMTMuMjA3Mkw5IDEuMDAwMDZMOCAxLjAwMDA2TDggMTMuMjA3MkwzLjg1MzU2IDkuMDYwNzJMMy4xNDY0NiA5Ljc2NzgzWiIgZmlsbD0iIzQyNDI0MiIvPgo8L3N2Zz4K")}.monaco-editor .find-widget .disabled{opacity:.3;cursor:default}.monaco-editor .find-widget .monaco-checkbox{width:20px;height:20px;display:inline-block;vertical-align:middle;margin-left:3px}.monaco-editor .find-widget .monaco-checkbox .label{content:"";display:inline-block;background-repeat:no-repeat;background-position:50%;background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEgMTJMMSAxMUgxMFYxMkgxWk0xIDdIMTVWOEgxTDEgN1pNMTIgM1Y0SDFMMSAzSDEyWiIgZmlsbD0iIzQyNDI0MiIvPgo8L3N2Zz4K");width:20px;height:20px;border:none}.monaco-editor .find-widget .monaco-checkbox .checkbox:disabled+.label{opacity:.3;cursor:default}.monaco-editor .find-widget .monaco-checkbox .checkbox:not(:disabled)+.label{cursor:pointer}.monaco-editor .find-widget .monaco-checkbox .checkbox:not(:disabled):hover:before+.label{background-color:#ddd}.monaco-editor .find-widget .monaco-checkbox .checkbox:checked+.label{background-color:hsla(0,0%,39%,.2)}.monaco-editor .find-widget .close-fw{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04IDguNzA3MTRMMTEuNjQ2NCAxMi4zNTM2TDEyLjM1MzYgMTEuNjQ2NUw4LjcwNzExIDguMDAwMDRMMTIuMzUzNiA0LjM1MzU5TDExLjY0NjQgMy42NDY0OEw4IDcuMjkyOTNMNC4zNTM1NSAzLjY0NjQ4TDMuNjQ2NDUgNC4zNTM1OUw3LjI5Mjg5IDguMDAwMDRMMy42NDY0NSAxMS42NDY1TDQuMzUzNTUgMTIuMzUzNkw4IDguNzA3MTRaIiBmaWxsPSIjNDI0MjQyIi8+Cjwvc3ZnPgo=")}.monaco-editor .find-widget .expand{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03Ljk3NjAzIDEwLjA3MTlMMTIuMzMzMyA1LjcxNDZMMTIuOTUyMSA2LjMzMzMyTDguMjg1MzkgMTFMNy42NjY2NyAxMUwzIDYuMzMzMzJMMy42MTg3MiA1LjcxNDZMNy45NzYwMyAxMC4wNzE5WiIgZmlsbD0iIzQyNDI0MiIvPgo8L3N2Zz4K")}.monaco-editor .find-widget .collapse{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC4wNzE5IDguMDAwMDVMNS43MTQ2MSAxMi4zNTc0TDYuMzMzMzMgMTIuOTc2MUwxMSA4LjMwOTQxVjcuNjkwNjlMNi4zMzMzMyAzLjAyNDAyTDUuNzE0NjEgMy42NDI3NEwxMC4wNzE5IDguMDAwMDVaIiBmaWxsPSIjNDI0MjQyIi8+Cjwvc3ZnPgo=")}.monaco-editor .find-widget .replace{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zLjIyMSAzLjczOUw1LjQ4MiA2LjAwOEw3LjcgMy43ODRMNyAzLjA4NEw1Ljk4OCA0LjA5MUw1Ljk4IDIuNDkxQzUuOTc5MDkgMi4zNTU2NyA2LjAzMDY4IDIuMjI1MjUgNi4xMjM5MiAyLjEyNzE2QzYuMjE3MTYgMi4wMjkwOCA2LjM0NDggMS45NzA5NSA2LjQ4IDEuOTY1SDhWMUg2LjQ4QzYuMjg0OTYgMS4wMDAyNiA2LjA5MTg5IDEuMDM5MDIgNS45MTE4NiAxLjExNDA1QzUuNzMxODMgMS4xODkwOCA1LjU2ODM4IDEuMjk4OTIgNS40MzA4OCAxLjQzNzI1QzUuMjkzMzggMS41NzU1OCA1LjE4NDU1IDEuNzM5NjkgNS4xMTA2MSAxLjkyMDE4QzUuMDM2NjcgMi4xMDA2NiA0Ljk5OTA4IDIuMjkzOTYgNSAyLjQ4OVY0LjFMMy45MjcgMy4wMzNMMy4yMjEgMy43MzlaTTkuODkwMTQgNS41MzI3N0g5LjkwMTQxQzEwLjA4MzYgNS44NDQyNiAxMC4zNTIxIDYgMTAuNzA3IDZDMTEuMDk5NSA2IDExLjQxMzEgNS44MzIzNiAxMS42NDc5IDUuNDk3MDhDMTEuODgyNiA1LjE2MTggMTIgNC43MTcyOCAxMiA0LjE2MzUzQzEyIDMuNjUzMDQgMTEuODk5NSAzLjI1MDcgMTEuNjk4NiAyLjk1NjUyQzExLjQ5NzcgMi42NjIzNCAxMS4yMTEzIDIuNTE1MjUgMTAuODM5NCAyLjUxNTI1QzEwLjQzMzggMi41MTUyNSAxMC4xMjExIDIuNzA4ODUgOS45MDE0MSAzLjA5NjA0SDkuODkwMTRWMUg5VjUuOTE4ODhIOS44OTAxNFY1LjUzMjc3Wk05Ljg3NjA2IDQuNDcxNzdWNC4xMzEwOEM5Ljg3NjA2IDMuODg0NDkgOS45MzQyNyAzLjY4NDQgMTAuMDUwNyAzLjUzMDgyQzEwLjE2OSAzLjM3NzI0IDEwLjMxNzQgMy4zMDA0NSAxMC40OTU4IDMuMzAwNDVDMTAuNjg1NCAzLjMwMDQ1IDEwLjgzMSAzLjM3ODMzIDEwLjkzMjQgMy41MzQwN0MxMS4wMzU3IDMuNjg3NjUgMTEuMDg3MyAzLjkwMTggMTEuMDg3MyA0LjE3NjUxQzExLjA4NzMgNC41MDc0NiAxMS4wMzEgNC43NjM3OSAxMC45MTgzIDQuOTQ1NDlDMTAuODA3NSA1LjEyNTAzIDEwLjY1MDcgNS4yMTQ4IDEwLjQ0NzkgNS4yMTQ4QzEwLjI4MDggNS4yMTQ4IDEwLjE0MzcgNS4xNDQ0OSAxMC4wMzY2IDUuMDAzODlDOS45Mjk1OCA0Ljg2MzI5IDkuODc2MDYgNC42ODU5MiA5Ljg3NjA2IDQuNDcxNzdaTTkgMTIuNzY5MUM4Ljc0NDMyIDEyLjkyMyA4LjM3NTE1IDEzIDcuODkyNDcgMTNDNy4zMjg1NSAxMyA2Ljg3MjE2IDEyLjgyMjUgNi41MjMzIDEyLjQ2NzRDNi4xNzQ0MyAxMi4xMTI0IDYgMTEuNjU0MyA2IDExLjA5MzFDNiAxMC40NDUxIDYuMTg2MzggOS45MzQ4NCA2LjU1OTE0IDkuNTYyNEM2LjkzNDI5IDkuMTg3NDcgNy40MzQ4OSA5LjAwMDAxIDguMDYwOTMgOS4wMDAwMUM4LjQ5MzQzIDkuMDAwMDEgOC44MDY0NSA5LjA1OTYgOSA5LjE3ODc4VjEwLjE3NjlDOC43NjM0NCA5Ljk5MzE5IDguNDk5NCA5LjkwMTMyIDguMjA3ODkgOS45MDEzMkM3Ljg4MjkyIDkuOTAxMzIgNy42MjQ4NSAxMC4wMDA2IDcuNDMzNjkgMTAuMTk5M0M3LjI0NDkyIDEwLjM5NTQgNy4xNTA1NCAxMC42NjczIDcuMTUwNTQgMTEuMDE0OUM3LjE1MDU0IDExLjM1MjYgNy4yNDEzNCAxMS42MTgzIDcuNDIyOTQgMTEuODExOUM3LjYwNDU0IDEyLjAwMzEgNy44NTQyNCAxMi4wOTg3IDguMTcyMDQgMTIuMDk4N0M4LjQ1NCAxMi4wOTg3IDguNzI5OTkgMTIuMDA2OCA5IDExLjgyMzFWMTIuNzY5MVpNNCA3TDMgOFYxNEw0IDE1SDExTDEyIDE0VjhMMTEgN0g0Wk00IDhINUgxMEgxMVY5VjEzVjE0SDEwSDVINFYxM1Y5VjhaIiBmaWxsPSIjNDI0MjQyIi8+Cjwvc3ZnPgo=")}.monaco-editor .find-widget .replace-all{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMS42MDA5IDIuNjc2ODNDMTEuNzQ3NCAyLjM2NzA4IDExLjk1NTkgMi4yMTIyIDEyLjIyNjMgMi4yMTIyQzEyLjQ3NDIgMi4yMTIyIDEyLjY2NTEgMi4zMjk4NyAxMi43OTkxIDIuNTY1MjJDMTIuOTMzIDIuODAwNTYgMTMgMy4xMjI0MyAxMyAzLjUzMDgyQzEzIDMuOTczODMgMTIuOTIxOCA0LjMyOTQ0IDEyLjc2NTMgNC41OTc2NkMxMi42MDg4IDQuODY1ODkgMTIuMzk5NyA1IDEyLjEzOCA1QzExLjkwMTQgNSAxMS43MjI0IDQuODc1NDEgMTEuNjAwOSA0LjYyNjIySDExLjU5MzRWNC45MzUxMUgxMVYxSDExLjU5MzRWMi42NzY4M0gxMS42MDA5Wk0xMS41ODQgMy43Nzc0MkMxMS41ODQgMy45NDg3MyAxMS42MTk3IDQuMDkwNjMgMTEuNjkxMSA0LjIwMzExQzExLjc2MjQgNC4zMTU2IDExLjg1MzggNC4zNzE4NCAxMS45NjUzIDQuMzcxODRDMTIuMTAwNSA0LjM3MTg0IDEyLjIwNSA0LjMwMDAyIDEyLjI3ODkgNC4xNTYzOUMxMi4zNTQgNC4wMTEwMyAxMi4zOTE1IDMuODA1OTcgMTIuMzkxNSAzLjU0MTIxQzEyLjM5MTUgMy4zMjE0NCAxMi4zNTcxIDMuMTUwMTIgMTIuMjg4MyAzLjAyNzI2QzEyLjIyMDcgMi45MDI2NiAxMi4xMjM2IDIuODQwMzYgMTEuOTk3MiAyLjg0MDM2QzExLjg3ODIgMi44NDAzNiAxMS43NzkzIDIuOTAxOCAxMS43MDA1IDMuMDI0NjZDMTEuNjIyOCAzLjE0NzUyIDExLjU4NCAzLjMwNzU5IDExLjU4NCAzLjUwNDg3VjMuNzc3NDJaTTQuMTE5NjkgNy42OTVMMiA1LjU2NzgxTDIuNjYxODggNC45MDU5NEwzLjY2NzgxIDUuOTA2MjVWNC4zOTU5NEMzLjY2Njk1IDQuMjEzMDkgMy43MDIxOSA0LjAzMTg3IDMuNzcxNSAzLjg2MjY2QzMuODQwODIgMy42OTM0NiAzLjk0Mjg2IDMuNTM5NjEgNC4wNzE3NiAzLjQwOTkyQzQuMjAwNjYgMy4yODAyMyA0LjM1MzkgMy4xNzcyNyA0LjUyMjY4IDMuMTA2OTJDNC42OTE0NiAzLjAzNjU4IDQuODcyNDYgMy4wMDAyNCA1LjA1NTMxIDNINy4zOTkwNlYzLjkwNDY5SDUuMDU1MzFDNC45Mjg1NiAzLjkxMDI2IDQuODA4OSAzLjk2NDc2IDQuNzIxNDkgNC4wNTY3MkM0LjYzNDA4IDQuMTQ4NjggNC41ODU3MSA0LjI3MDk0IDQuNTg2NTYgNC4zOTc4MUw0LjU5NDA2IDUuODk3ODFMNS41NDI4MSA0Ljk1Mzc1TDYuMTk5MDYgNS42MUw0LjExOTY5IDcuNjk1Wk05LjM1NTYgNC45MzAxN0gxMFYzLjIyMDY3QzEwIDIuNDA2ODkgOS42ODUzNCAyIDkuMDU2MDMgMkM4LjkyMDk4IDIgOC43NzA4MyAyLjAyNDIxIDguNjA1NiAyLjA3MjYzQzguNDQxODEgMi4xMjEwNCA4LjMxMjUgMi4xNzY5MSA4LjIxNzY3IDIuMjQwMjJWMi45MDUwM0M4LjQ1NDc0IDIuNzAyMDUgOC43MDQ3NCAyLjYwMDU2IDguOTY3NjcgMi42MDA1NkM5LjIyOTE3IDIuNjAwNTYgOS4zNTk5MSAyLjc1Njk4IDkuMzU5OTEgMy4wNjk4M0w4Ljc2MDc4IDMuMTczMThDOC4yNTM1OSAzLjI1ODg1IDggMy41NzkxNCA4IDQuMTM0MDhDOCA0LjM5NjY1IDguMDYxMDYgNC42MDcwOCA4LjE4MzE5IDQuNzY1MzZDOC4zMDY3NSA0LjkyMTc5IDguNDc1NTcgNSA4LjY4OTY2IDVDOC45Nzk4OSA1IDkuMTk4OTkgNC44Mzk4NSA5LjM0Njk4IDQuNTE5NTVIOS4zNTU2VjQuOTMwMTdaTTkuMzU5OTEgMy41NzU0MlYzLjc2ODE2QzkuMzU5OTEgMy45NDMyIDkuMzE5NjggNC4wODg0NSA5LjIzOTIyIDQuMjAzOTFDOS4xNTg3NiA0LjMxNzUgOS4wNTQ2IDQuMzc0MyA4LjkyNjcyIDQuMzc0M0M4LjgzNDc3IDQuMzc0MyA4Ljc2MTQ5IDQuMzQyNjQgOC43MDY5IDQuMjc5MzNDOC42NTM3NCA0LjIxNDE1IDguNjI3MTYgNC4xMzEyOCA4LjYyNzE2IDQuMDMwNzNDOC42MjcxNiAzLjgwOTEyIDguNzM3NzkgMy42Nzk3IDguOTU5MDUgMy42NDI0Nkw5LjM1OTkxIDMuNTc1NDJaTTcgMTIuOTMwMkg2LjM1NTZWMTIuNTE5Nkg2LjM0Njk4QzYuMTk4OTkgMTIuODM5OSA1Ljk3OTg5IDEzIDUuNjg5NjYgMTNDNS40NzU1NyAxMyA1LjMwNjc1IDEyLjkyMTggNS4xODMxOSAxMi43NjU0QzUuMDYxMDYgMTIuNjA3MSA1IDEyLjM5NjYgNSAxMi4xMzQxQzUgMTEuNTc5MSA1LjI1MzU5IDExLjI1ODggNS43NjA3OCAxMS4xNzMyTDYuMzU5OTEgMTEuMDY5OEM2LjM1OTkxIDEwLjc1NyA2LjIyOTE3IDEwLjYwMDYgNS45Njc2NyAxMC42MDA2QzUuNzA0NzQgMTAuNjAwNiA1LjQ1NDc0IDEwLjcwMiA1LjIxNzY3IDEwLjkwNVYxMC4yNDAyQzUuMzEyNSAxMC4xNzY5IDUuNDQxODEgMTAuMTIxIDUuNjA1NiAxMC4wNzI2QzUuNzcwODMgMTAuMDI0MiA1LjkyMDk4IDEwIDYuMDU2MDMgMTBDNi42ODUzNCAxMCA3IDEwLjQwNjkgNyAxMS4yMjA3VjEyLjkzMDJaTTYuMzU5OTEgMTEuNzY4MlYxMS41NzU0TDUuOTU5MDUgMTEuNjQyNUM1LjczNzc5IDExLjY3OTcgNS42MjcxNiAxMS44MDkxIDUuNjI3MTYgMTIuMDMwN0M1LjYyNzE2IDEyLjEzMTMgNS42NTM3NCAxMi4yMTQyIDUuNzA2OSAxMi4yNzkzQzUuNzYxNDkgMTIuMzQyNiA1LjgzNDc3IDEyLjM3NDMgNS45MjY3MiAxMi4zNzQzQzYuMDU0NiAxMi4zNzQzIDYuMTU4NzYgMTIuMzE3NSA2LjIzOTIyIDEyLjIwMzlDNi4zMTk2OCAxMi4wODg1IDYuMzU5OTEgMTEuOTQzMiA2LjM1OTkxIDExLjc2ODJaTTkuMjYxNjUgMTNDOS41ODM0MyAxMyA5LjgyOTU1IDEyLjk0MjMgMTAgMTIuODI2OFYxMi4xMTczQzkuODE5OTkgMTIuMjU1MSA5LjYzNiAxMi4zMjQgOS40NDgwMyAxMi4zMjRDOS4yMzYxNiAxMi4zMjQgOS4wNjk2OSAxMi4yNTIzIDguOTQ4NjMgMTIuMTA4OUM4LjgyNzU2IDExLjk2MzcgOC43NjcwMiAxMS43NjQ0IDguNzY3MDIgMTEuNTExMkM4Ljc2NzAyIDExLjI1MDUgOC44Mjk5NSAxMS4wNDY2IDguOTU1NzkgMTAuODk5NEM5LjA4MzIzIDEwLjc1MDUgOS4yNTUyOCAxMC42NzYgOS40NzE5MiAxMC42NzZDOS42NjYyNyAxMC42NzYgOS44NDIyOSAxMC43NDQ5IDEwIDEwLjg4MjdWMTAuMTM0MUM5Ljg3MDk3IDEwLjA0NDcgOS42NjIyOSAxMCA5LjM3Mzk1IDEwQzguOTU2NTkgMTAgOC42MjI4NiAxMC4xNDA2IDguMzcyNzYgMTAuNDIxOEM4LjEyNDI1IDEwLjcwMTEgOCAxMS4wODM4IDggMTEuNTY5OEM4IDExLjk5MDcgOC4xMTYyOSAxMi4zMzQzIDguMzQ4ODcgMTIuNjAwNkM4LjU4MTQ0IDEyLjg2NjkgOC44ODU3IDEzIDkuMjYxNjUgMTNaTTIgOUwzIDhIMTJMMTMgOVYxNEwxMiAxNUgzTDIgMTRWOVpNMyA5VjE0SDEyVjlIM1pNNiA3TDcgNkgxNEwxNSA3VjEyTDE0IDEzVjEyVjdIN0g2WiIgZmlsbD0iIzQyNDI0MiIvPgo8L3N2Zz4K")}.monaco-editor .find-widget>.replace-part{display:none}.monaco-editor .find-widget>.replace-part>.monaco-findInput{position:relative;display:-ms-flexbox;display:flex;display:-webkit-flex;vertical-align:middle;-ms-flex:auto;flex:auto;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.monaco-editor .find-widget>.replace-part>.monaco-findInput>.controls{position:absolute;top:3px;right:2px}.monaco-editor .find-widget.reduced-find-widget .matchesCount,.monaco-editor .find-widget.reduced-find-widget .monaco-checkbox{display:none}.monaco-editor .find-widget.narrow-find-widget{max-width:257px!important}.monaco-editor .find-widget.collapsed-find-widget{max-width:170px!important}.monaco-editor .find-widget.collapsed-find-widget .button.next,.monaco-editor .find-widget.collapsed-find-widget .button.previous,.monaco-editor .find-widget.collapsed-find-widget .button.replace,.monaco-editor .find-widget.collapsed-find-widget .button.replace-all,.monaco-editor .find-widget.collapsed-find-widget>.find-part .monaco-findInput .controls{display:none}.monaco-editor .findMatch{-webkit-animation-duration:0;-webkit-animation-name:inherit!important;-moz-animation-duration:0;-moz-animation-name:inherit!important;-ms-animation-duration:0;-ms-animation-name:inherit!important;animation-duration:0;animation-name:inherit!important}.monaco-editor .find-widget .monaco-sash{width:2px!important;margin-left:-4px}.monaco-editor.hc-black .find-widget .previous,.monaco-editor.vs-dark .find-widget .previous{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMy44NTM1IDYuMjkyODRMOC44NTM1NiAxLjI5Mjg1SDguMTQ2NDVMMy4xNDY0NSA2LjI5Mjg0TDMuODUzNTYgNi45OTk5NUw4IDIuODUzNTFWMTUuMDYwNkg5VjIuODUzNTFMMTMuMTQ2NCA2Ljk5OTk1TDEzLjg1MzUgNi4yOTI4NFoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==")}.monaco-editor.hc-black .find-widget .next,.monaco-editor.vs-dark .find-widget .next{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zLjE0NjQ2IDkuNzY3ODNMOC4xNDY0NCAxNC43Njc4TDguODUzNTUgMTQuNzY3OEwxMy44NTM1IDkuNzY3ODNMMTMuMTQ2NCA5LjA2MDcyTDkgMTMuMjA3Mkw5IDEuMDAwMDZMOCAxLjAwMDA2TDggMTMuMjA3MkwzLjg1MzU2IDkuMDYwNzJMMy4xNDY0NiA5Ljc2NzgzWiIgZmlsbD0iI0M1QzVDNSIvPgo8L3N2Zz4K")}.monaco-editor.hc-black .find-widget .monaco-checkbox .label,.monaco-editor.vs-dark .find-widget .monaco-checkbox .label{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEgMTJMMSAxMUgxMFYxMkgxWk0xIDdIMTVWOEgxTDEgN1pNMTIgM1Y0SDFMMSAzSDEyWiIgZmlsbD0iI0M1QzVDNSIvPgo8L3N2Zz4K")}.monaco-editor.vs-dark .find-widget .monaco-checkbox .checkbox:not(:disabled):hover:before+.label{background-color:hsla(0,0%,100%,.1)}.monaco-editor.hc-black .find-widget .close-fw,.monaco-editor.vs-dark .find-widget .close-fw{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04IDguNzA3MTRMMTEuNjQ2NCAxMi4zNTM2TDEyLjM1MzYgMTEuNjQ2NUw4LjcwNzExIDguMDAwMDRMMTIuMzUzNiA0LjM1MzU5TDExLjY0NjQgMy42NDY0OEw4IDcuMjkyOTNMNC4zNTM1NSAzLjY0NjQ4TDMuNjQ2NDUgNC4zNTM1OUw3LjI5Mjg5IDguMDAwMDRMMy42NDY0NSAxMS42NDY1TDQuMzUzNTUgMTIuMzUzNkw4IDguNzA3MTRaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=")}.monaco-editor.hc-black .find-widget .replace,.monaco-editor.vs-dark .find-widget .replace{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zLjIyMSAzLjczOUw1LjQ4MiA2LjAwOEw3LjcgMy43ODRMNyAzLjA4NEw1Ljk4OCA0LjA5MUw1Ljk4IDIuNDkxQzUuOTc5MDkgMi4zNTU2NyA2LjAzMDY4IDIuMjI1MjUgNi4xMjM5MiAyLjEyNzE2QzYuMjE3MTYgMi4wMjkwOCA2LjM0NDggMS45NzA5NSA2LjQ4IDEuOTY1SDhWMUg2LjQ4QzYuMjg0OTYgMS4wMDAyNiA2LjA5MTg5IDEuMDM5MDIgNS45MTE4NiAxLjExNDA1QzUuNzMxODMgMS4xODkwOCA1LjU2ODM4IDEuMjk4OTIgNS40MzA4OCAxLjQzNzI1QzUuMjkzMzggMS41NzU1OCA1LjE4NDU1IDEuNzM5NjkgNS4xMTA2MSAxLjkyMDE4QzUuMDM2NjcgMi4xMDA2NiA0Ljk5OTA4IDIuMjkzOTYgNSAyLjQ4OVY0LjFMMy45MjcgMy4wMzNMMy4yMjEgMy43MzlaTTkuODkwMTQgNS41MzI3N0g5LjkwMTQxQzEwLjA4MzYgNS44NDQyNiAxMC4zNTIxIDYgMTAuNzA3IDZDMTEuMDk5NSA2IDExLjQxMzEgNS44MzIzNiAxMS42NDc5IDUuNDk3MDhDMTEuODgyNiA1LjE2MTggMTIgNC43MTcyOCAxMiA0LjE2MzUzQzEyIDMuNjUzMDQgMTEuODk5NSAzLjI1MDcgMTEuNjk4NiAyLjk1NjUyQzExLjQ5NzcgMi42NjIzNCAxMS4yMTEzIDIuNTE1MjUgMTAuODM5NCAyLjUxNTI1QzEwLjQzMzggMi41MTUyNSAxMC4xMjExIDIuNzA4ODUgOS45MDE0MSAzLjA5NjA0SDkuODkwMTRWMUg5VjUuOTE4ODhIOS44OTAxNFY1LjUzMjc3Wk05Ljg3NjA2IDQuNDcxNzdWNC4xMzEwOEM5Ljg3NjA2IDMuODg0NDkgOS45MzQyNyAzLjY4NDQgMTAuMDUwNyAzLjUzMDgyQzEwLjE2OSAzLjM3NzI0IDEwLjMxNzQgMy4zMDA0NSAxMC40OTU4IDMuMzAwNDVDMTAuNjg1NCAzLjMwMDQ1IDEwLjgzMSAzLjM3ODMzIDEwLjkzMjQgMy41MzQwN0MxMS4wMzU3IDMuNjg3NjUgMTEuMDg3MyAzLjkwMTggMTEuMDg3MyA0LjE3NjUxQzExLjA4NzMgNC41MDc0NiAxMS4wMzEgNC43NjM3OSAxMC45MTgzIDQuOTQ1NDlDMTAuODA3NSA1LjEyNTAzIDEwLjY1MDcgNS4yMTQ4IDEwLjQ0NzkgNS4yMTQ4QzEwLjI4MDggNS4yMTQ4IDEwLjE0MzcgNS4xNDQ0OSAxMC4wMzY2IDUuMDAzODlDOS45Mjk1OCA0Ljg2MzI5IDkuODc2MDYgNC42ODU5MiA5Ljg3NjA2IDQuNDcxNzdaTTkgMTIuNzY5MUM4Ljc0NDMzIDEyLjkyMyA4LjM3NTE1IDEzIDcuODkyNDcgMTNDNy4zMjg1NSAxMyA2Ljg3MjE2IDEyLjgyMjUgNi41MjMzIDEyLjQ2NzRDNi4xNzQ0MyAxMi4xMTI0IDYgMTEuNjU0MyA2IDExLjA5MzFDNiAxMC40NDUxIDYuMTg2MzggOS45MzQ4NCA2LjU1OTE0IDkuNTYyNEM2LjkzNDI5IDkuMTg3NDcgNy40MzQ4OSA5LjAwMDAxIDguMDYwOTMgOS4wMDAwMUM4LjQ5MzQzIDkuMDAwMDEgOC44MDY0NSA5LjA1OTYgOSA5LjE3ODc4VjEwLjE3NjlDOC43NjM0NCA5Ljk5MzE5IDguNDk5NCA5LjkwMTMyIDguMjA3ODkgOS45MDEzMkM3Ljg4MjkyIDkuOTAxMzIgNy42MjQ4NSAxMC4wMDA2IDcuNDMzNjkgMTAuMTk5M0M3LjI0NDkyIDEwLjM5NTQgNy4xNTA1NCAxMC42NjczIDcuMTUwNTQgMTEuMDE0OUM3LjE1MDU0IDExLjM1MjYgNy4yNDEzNCAxMS42MTgzIDcuNDIyOTQgMTEuODExOUM3LjYwNDU0IDEyLjAwMzEgNy44NTQyNCAxMi4wOTg3IDguMTcyMDQgMTIuMDk4N0M4LjQ1NCAxMi4wOTg3IDguNzI5OTkgMTIuMDA2OCA5IDExLjgyMzFWMTIuNzY5MVpNNCA3TDMgOFYxNEw0IDE1SDExTDEyIDE0VjhMMTEgN0g0Wk00IDhINUgxMEgxMVY5VjEzVjE0SDEwSDVINFYxM1Y5VjhaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=")}.monaco-editor.hc-black .find-widget .replace-all,.monaco-editor.vs-dark .find-widget .replace-all{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMS42MDA5IDIuNjc2ODNDMTEuNzQ3NCAyLjM2NzA4IDExLjk1NTkgMi4yMTIyIDEyLjIyNjMgMi4yMTIyQzEyLjQ3NDIgMi4yMTIyIDEyLjY2NTEgMi4zMjk4NyAxMi43OTkxIDIuNTY1MjJDMTIuOTMzIDIuODAwNTYgMTMgMy4xMjI0MyAxMyAzLjUzMDgyQzEzIDMuOTczODMgMTIuOTIxOCA0LjMyOTQ0IDEyLjc2NTMgNC41OTc2NkMxMi42MDg4IDQuODY1ODkgMTIuMzk5NyA1IDEyLjEzOCA1QzExLjkwMTQgNSAxMS43MjI0IDQuODc1NDEgMTEuNjAwOSA0LjYyNjIySDExLjU5MzRWNC45MzUxMUgxMVYxSDExLjU5MzRWMi42NzY4M0gxMS42MDA5Wk0xMS41ODQgMy43Nzc0MkMxMS41ODQgMy45NDg3MyAxMS42MTk3IDQuMDkwNjMgMTEuNjkxMSA0LjIwMzExQzExLjc2MjQgNC4zMTU2IDExLjg1MzggNC4zNzE4NCAxMS45NjUzIDQuMzcxODRDMTIuMTAwNSA0LjM3MTg0IDEyLjIwNSA0LjMwMDAyIDEyLjI3ODkgNC4xNTYzOUMxMi4zNTQgNC4wMTEwMyAxMi4zOTE1IDMuODA1OTcgMTIuMzkxNSAzLjU0MTIxQzEyLjM5MTUgMy4zMjE0NCAxMi4zNTcxIDMuMTUwMTIgMTIuMjg4MyAzLjAyNzI2QzEyLjIyMDcgMi45MDI2NiAxMi4xMjM2IDIuODQwMzYgMTEuOTk3MiAyLjg0MDM2QzExLjg3ODIgMi44NDAzNiAxMS43NzkzIDIuOTAxOCAxMS43MDA1IDMuMDI0NjZDMTEuNjIyOCAzLjE0NzUyIDExLjU4NCAzLjMwNzU5IDExLjU4NCAzLjUwNDg3VjMuNzc3NDJaTTQuMTE5NjkgNy42OTVMMiA1LjU2NzgxTDIuNjYxODggNC45MDU5NEwzLjY2NzgxIDUuOTA2MjVWNC4zOTU5NEMzLjY2Njk1IDQuMjEzMDkgMy43MDIxOSA0LjAzMTg3IDMuNzcxNSAzLjg2MjY2QzMuODQwODIgMy42OTM0NiAzLjk0Mjg2IDMuNTM5NjEgNC4wNzE3NiAzLjQwOTkyQzQuMjAwNjYgMy4yODAyMyA0LjM1MzkgMy4xNzcyNyA0LjUyMjY4IDMuMTA2OTJDNC42OTE0NiAzLjAzNjU4IDQuODcyNDYgMy4wMDAyNCA1LjA1NTMxIDNINy4zOTkwNlYzLjkwNDY5SDUuMDU1MzFDNC45Mjg1NiAzLjkxMDI2IDQuODA4OSAzLjk2NDc2IDQuNzIxNDkgNC4wNTY3MkM0LjYzNDA4IDQuMTQ4NjggNC41ODU3MSA0LjI3MDk0IDQuNTg2NTYgNC4zOTc4MUw0LjU5NDA2IDUuODk3ODFMNS41NDI4MSA0Ljk1Mzc1TDYuMTk5MDYgNS42MUw0LjExOTY5IDcuNjk1Wk05LjM1NTYgNC45MzAxN0gxMFYzLjIyMDY3QzEwIDIuNDA2ODkgOS42ODUzNCAyIDkuMDU2MDMgMkM4LjkyMDk4IDIgOC43NzA4MyAyLjAyNDIxIDguNjA1NiAyLjA3MjYzQzguNDQxODEgMi4xMjEwNCA4LjMxMjUgMi4xNzY5MSA4LjIxNzY3IDIuMjQwMjJWMi45MDUwM0M4LjQ1NDc0IDIuNzAyMDUgOC43MDQ3NCAyLjYwMDU2IDguOTY3NjcgMi42MDA1NkM5LjIyOTE3IDIuNjAwNTYgOS4zNTk5MSAyLjc1Njk4IDkuMzU5OTEgMy4wNjk4M0w4Ljc2MDc4IDMuMTczMThDOC4yNTM1OSAzLjI1ODg1IDggMy41NzkxNCA4IDQuMTM0MDhDOCA0LjM5NjY1IDguMDYxMDYgNC42MDcwOCA4LjE4MzE5IDQuNzY1MzZDOC4zMDY3NSA0LjkyMTc5IDguNDc1NTcgNSA4LjY4OTY2IDVDOC45Nzk4OSA1IDkuMTk4OTkgNC44Mzk4NSA5LjM0Njk4IDQuNTE5NTVIOS4zNTU2VjQuOTMwMTdaTTkuMzU5OTEgMy41NzU0MlYzLjc2ODE2QzkuMzU5OTEgMy45NDMyIDkuMzE5NjggNC4wODg0NSA5LjIzOTIyIDQuMjAzOTFDOS4xNTg3NiA0LjMxNzUgOS4wNTQ2IDQuMzc0MyA4LjkyNjcyIDQuMzc0M0M4LjgzNDc3IDQuMzc0MyA4Ljc2MTQ5IDQuMzQyNjQgOC43MDY5IDQuMjc5MzNDOC42NTM3NCA0LjIxNDE1IDguNjI3MTYgNC4xMzEyOCA4LjYyNzE2IDQuMDMwNzNDOC42MjcxNiAzLjgwOTEyIDguNzM3NzkgMy42Nzk3IDguOTU5MDUgMy42NDI0Nkw5LjM1OTkxIDMuNTc1NDJaTTcgMTIuOTMwMkg2LjM1NTZWMTIuNTE5Nkg2LjM0Njk4QzYuMTk4OTkgMTIuODM5OSA1Ljk3OTg5IDEzIDUuNjg5NjYgMTNDNS40NzU1NyAxMyA1LjMwNjc1IDEyLjkyMTggNS4xODMxOSAxMi43NjU0QzUuMDYxMDYgMTIuNjA3MSA1IDEyLjM5NjYgNSAxMi4xMzQxQzUgMTEuNTc5MSA1LjI1MzU5IDExLjI1ODggNS43NjA3OCAxMS4xNzMyTDYuMzU5OTEgMTEuMDY5OEM2LjM1OTkxIDEwLjc1NyA2LjIyOTE3IDEwLjYwMDYgNS45Njc2NyAxMC42MDA2QzUuNzA0NzQgMTAuNjAwNiA1LjQ1NDc0IDEwLjcwMiA1LjIxNzY3IDEwLjkwNVYxMC4yNDAyQzUuMzEyNSAxMC4xNzY5IDUuNDQxODEgMTAuMTIxIDUuNjA1NiAxMC4wNzI2QzUuNzcwODMgMTAuMDI0MiA1LjkyMDk4IDEwIDYuMDU2MDMgMTBDNi42ODUzNCAxMCA3IDEwLjQwNjkgNyAxMS4yMjA3VjEyLjkzMDJaTTYuMzU5OTEgMTEuNzY4MlYxMS41NzU0TDUuOTU5MDUgMTEuNjQyNUM1LjczNzc5IDExLjY3OTcgNS42MjcxNiAxMS44MDkxIDUuNjI3MTYgMTIuMDMwN0M1LjYyNzE2IDEyLjEzMTMgNS42NTM3NCAxMi4yMTQyIDUuNzA2OSAxMi4yNzkzQzUuNzYxNDkgMTIuMzQyNiA1LjgzNDc3IDEyLjM3NDMgNS45MjY3MiAxMi4zNzQzQzYuMDU0NiAxMi4zNzQzIDYuMTU4NzYgMTIuMzE3NSA2LjIzOTIyIDEyLjIwMzlDNi4zMTk2OCAxMi4wODg1IDYuMzU5OTEgMTEuOTQzMiA2LjM1OTkxIDExLjc2ODJaTTkuMjYxNjUgMTNDOS41ODM0MyAxMyA5LjgyOTU1IDEyLjk0MjMgMTAgMTIuODI2OFYxMi4xMTczQzkuODE5OTkgMTIuMjU1MSA5LjYzNiAxMi4zMjQgOS40NDgwMyAxMi4zMjRDOS4yMzYxNiAxMi4zMjQgOS4wNjk2OSAxMi4yNTIzIDguOTQ4NjMgMTIuMTA4OUM4LjgyNzU2IDExLjk2MzcgOC43NjcwMiAxMS43NjQ0IDguNzY3MDIgMTEuNTExMkM4Ljc2NzAyIDExLjI1MDUgOC44Mjk5NSAxMS4wNDY2IDguOTU1NzkgMTAuODk5NEM5LjA4MzIzIDEwLjc1MDUgOS4yNTUyOCAxMC42NzYgOS40NzE5MiAxMC42NzZDOS42NjYyNyAxMC42NzYgOS44NDIyOSAxMC43NDQ5IDEwIDEwLjg4MjdWMTAuMTM0MUM5Ljg3MDk3IDEwLjA0NDcgOS42NjIyOSAxMCA5LjM3Mzk1IDEwQzguOTU2NTkgMTAgOC42MjI4NiAxMC4xNDA2IDguMzcyNzYgMTAuNDIxOEM4LjEyNDI1IDEwLjcwMTEgOCAxMS4wODM4IDggMTEuNTY5OEM4IDExLjk5MDcgOC4xMTYyOSAxMi4zMzQzIDguMzQ4ODcgMTIuNjAwNkM4LjU4MTQ0IDEyLjg2NjkgOC44ODU3IDEzIDkuMjYxNjUgMTNaTTIgOUwzIDhIMTJMMTMgOVYxNEwxMiAxNUgzTDIgMTRWOVpNMyA5VjE0SDEyVjlIM1pNNiA3TDcgNkgxNEwxNSA3VjEyTDE0IDEzVjEyVjdIN0g2WiIgZmlsbD0iI0M1QzVDNSIvPgo8L3N2Zz4K")}.monaco-editor.hc-black .find-widget .expand,.monaco-editor.vs-dark .find-widget .expand{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03Ljk3NjAzIDEwLjA3MTlMMTIuMzMzMyA1LjcxNDZMMTIuOTUyMSA2LjMzMzMyTDguMjg1MzkgMTFMNy42NjY2NyAxMUwzIDYuMzMzMzJMMy42MTg3MiA1LjcxNDZMNy45NzYwMyAxMC4wNzE5WiIgZmlsbD0iI0M1QzVDNSIvPgo8L3N2Zz4K")}.monaco-editor.hc-black .find-widget .collapse,.monaco-editor.vs-dark .find-widget .collapse{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC4wNzE5IDguMDAwMDVMNS43MTQ2MSAxMi4zNTc0TDYuMzMzMzMgMTIuOTc2MUwxMSA4LjMwOTQxVjcuNjkwNjlMNi4zMzMzMyAzLjAyNDAyTDUuNzE0NjEgMy42NDI3NEwxMC4wNzE5IDguMDAwMDVaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=")}.monaco-editor.hc-black .find-widget .button:not(.disabled):hover,.monaco-editor.vs-dark .find-widget .button:not(.disabled):hover{background-color:hsla(0,0%,100%,.1)}.monaco-editor.hc-black .find-widget .button:before{position:relative;top:1px;left:2px}.monaco-editor.hc-black .find-widget .monaco-checkbox .checkbox:checked+.label{background-color:hsla(0,0%,100%,.1)}.monaco-sash{position:absolute;z-index:35;-ms-touch-action:none;touch-action:none}.monaco-sash.disabled{pointer-events:none}.monaco-sash.vertical{cursor:ew-resize;top:0;width:4px;height:100%}.monaco-sash.mac.vertical{cursor:col-resize}.monaco-sash.vertical.minimum{cursor:e-resize}.monaco-sash.vertical.maximum{cursor:w-resize}.monaco-sash.horizontal{cursor:ns-resize;left:0;width:100%;height:4px}.monaco-sash.mac.horizontal{cursor:row-resize}.monaco-sash.horizontal.minimum{cursor:s-resize}.monaco-sash.horizontal.maximum{cursor:n-resize}.monaco-sash:not(.disabled).orthogonal-end:after,.monaco-sash:not(.disabled).orthogonal-start:before{content:" ";height:8px;width:8px;z-index:100;display:block;cursor:all-scroll;position:absolute}.monaco-sash.orthogonal-start.vertical:before{left:-2px;top:-4px}.monaco-sash.orthogonal-end.vertical:after{left:-2px;bottom:-4px}.monaco-sash.orthogonal-start.horizontal:before{top:-2px;left:-4px}.monaco-sash.orthogonal-end.horizontal:after{top:-2px;right:-4px}.monaco-sash.disabled{cursor:default!important;pointer-events:none!important}.monaco-sash.touch.vertical{width:20px}.monaco-sash.touch.horizontal{height:20px}.monaco-sash.debug{background:cyan}.monaco-sash.debug.disabled{background:rgba(0,255,255,.2)}.monaco-sash.debug:not(.disabled).orthogonal-end:after,.monaco-sash.debug:not(.disabled).orthogonal-start:before{background:red}.monaco-findInput{position:relative}.monaco-findInput .monaco-inputbox{font-size:13px;width:100%}.monaco-findInput>.controls{position:absolute;top:3px;right:2px}.vs .monaco-findInput.disabled{background-color:#e1e1e1}.vs-dark .monaco-findInput.disabled{background-color:#333}.monaco-findInput.highlight-0 .controls{animation:monaco-findInput-highlight-0 .1s linear 0s}.monaco-findInput.highlight-1 .controls{animation:monaco-findInput-highlight-1 .1s linear 0s}.hc-black .monaco-findInput.highlight-0 .controls,.vs-dark .monaco-findInput.highlight-0 .controls{animation:monaco-findInput-highlight-dark-0 .1s linear 0s}.hc-black .monaco-findInput.highlight-1 .controls,.vs-dark .monaco-findInput.highlight-1 .controls{animation:monaco-findInput-highlight-dark-1 .1s linear 0s}@keyframes monaco-findInput-highlight-0{0%{background:rgba(253,255,0,.8)}to{background:transparent}}@keyframes monaco-findInput-highlight-1{0%{background:rgba(253,255,0,.8)}99%{background:transparent}}@keyframes monaco-findInput-highlight-dark-0{0%{background:hsla(0,0%,100%,.44)}to{background:transparent}}@keyframes monaco-findInput-highlight-dark-1{0%{background:hsla(0,0%,100%,.44)}99%{background:transparent}}.monaco-inputbox{position:relative;display:block;padding:0;-o-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;line-height:auto!important;font-size:inherit}.monaco-inputbox.idle{border:1px solid transparent}.monaco-inputbox>.wrapper>.input,.monaco-inputbox>.wrapper>.mirror{padding:4px}.monaco-inputbox>.wrapper{position:relative;width:100%;height:100%}.monaco-inputbox>.wrapper>.input{display:inline-block;-o-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;width:100%;height:100%;line-height:inherit;border:none;font-family:inherit;font-size:inherit;resize:none;color:inherit}.monaco-inputbox>.wrapper>input{text-overflow:ellipsis}.monaco-inputbox>.wrapper>textarea.input{display:block;-ms-overflow-style:none;overflow:-moz-scrollbars-none;scrollbar-width:none;outline:none}.monaco-inputbox>.wrapper>textarea.input::-webkit-scrollbar{display:none}.monaco-inputbox>.wrapper>.mirror{position:absolute;display:inline-block;width:100%;top:0;left:0;-o-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;white-space:pre-wrap;visibility:hidden;word-wrap:break-word}.monaco-inputbox-container{text-align:right}.monaco-inputbox-container .monaco-inputbox-message{display:inline-block;overflow:hidden;text-align:left;width:100%;-o-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;padding:.4em;font-size:12px;line-height:17px;min-height:34px;margin-top:-1px;word-wrap:break-word}.monaco-inputbox .monaco-action-bar{position:absolute;right:2px;top:4px}.monaco-inputbox .monaco-action-bar .action-item{margin-left:2px}.monaco-inputbox .monaco-action-bar .action-item .icon{background-repeat:no-repeat;width:16px;height:16px}.monaco-scrollable-element>.scrollbar>.up-arrow{background:url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTEgMTEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTkuNDgwNDYsOC45NjE1bDEuMjYsLTEuMjZsLTUuMDQsLTUuMDRsLTUuNDYsNS4wNGwxLjI2LDEuMjZsNC4yLC0zLjc4bDMuNzgsMy43OHoiIGZpbGw9IiM0MjQyNDIiLz48L3N2Zz4=");cursor:pointer}.monaco-scrollable-element>.scrollbar>.down-arrow{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMSAxMSI+PHBhdGggdHJhbnNmb3JtPSJyb3RhdGUoLTE4MCA1LjQ5MDQ1OTkxODk3NTgzLDUuODExNTAwMDcyNDc5MjQ4KSIgZmlsbD0iIzQyNDI0MiIgZD0ibTkuNDgwNDYsOC45NjE1bDEuMjYsLTEuMjZsLTUuMDQsLTUuMDRsLTUuNDYsNS4wNGwxLjI2LDEuMjZsNC4yLC0zLjc4bDMuNzgsMy43OHoiLz48L3N2Zz4=");cursor:pointer}.monaco-scrollable-element>.scrollbar>.left-arrow{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMSAxMSI+PHBhdGggdHJhbnNmb3JtPSJyb3RhdGUoLTkwIDUuNDkwNDU5OTE4OTc1ODMxLDUuNDMxMzgyMTc5MjYwMjU0KSIgZmlsbD0iIzQyNDI0MiIgZD0ibTkuNDgwNDYsOC41ODEzOGwxLjI2LC0xLjI2bC01LjA0LC01LjA0bC01LjQ2LDUuMDRsMS4yNiwxLjI2bDQuMiwtMy43OGwzLjc4LDMuNzh6Ii8+PC9zdmc+");cursor:pointer}.monaco-scrollable-element>.scrollbar>.right-arrow{background:url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTEgMTEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggdHJhbnNmb3JtPSJyb3RhdGUoOTAgNS42MTcxNjUwODg2NTM1NjQ1LDUuNTU4MDg5NzMzMTIzNzgpICIgZmlsbD0iIzQyNDI0MiIgZD0ibTkuNjA3MTcsOC43MDgwOWwxLjI2LC0xLjI2bC01LjA0LC01LjA0bC01LjQ2LDUuMDRsMS4yNiwxLjI2bDQuMiwtMy43OGwzLjc4LDMuNzh6Ii8+PC9zdmc+");cursor:pointer}.hc-black .monaco-scrollable-element>.scrollbar>.up-arrow,.vs-dark .monaco-scrollable-element>.scrollbar>.up-arrow{background:url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTEgMTEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTkuNDgwNDYsOC45NjE1bDEuMjYsLTEuMjZsLTUuMDQsLTUuMDRsLTUuNDYsNS4wNGwxLjI2LDEuMjZsNC4yLC0zLjc4bDMuNzgsMy43OHoiIGZpbGw9IiNFOEU4RTgiLz48L3N2Zz4=")}.hc-black .monaco-scrollable-element>.scrollbar>.down-arrow,.vs-dark .monaco-scrollable-element>.scrollbar>.down-arrow{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMSAxMSI+PHBhdGggdHJhbnNmb3JtPSJyb3RhdGUoLTE4MCA1LjQ5MDQ1OTkxODk3NTgzLDUuODExNTAwMDcyNDc5MjQ4KSIgZmlsbD0iI0U4RThFOCIgZD0ibTkuNDgwNDYsOC45NjE1bDEuMjYsLTEuMjZsLTUuMDQsLTUuMDRsLTUuNDYsNS4wNGwxLjI2LDEuMjZsNC4yLC0zLjc4bDMuNzgsMy43OHoiLz48L3N2Zz4=")}.hc-black .monaco-scrollable-element>.scrollbar>.left-arrow,.vs-dark .monaco-scrollable-element>.scrollbar>.left-arrow{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMSAxMSI+PHBhdGggdHJhbnNmb3JtPSJyb3RhdGUoLTkwIDUuNDkwNDU5OTE4OTc1ODMxLDUuNDMxMzgyMTc5MjYwMjU0KSIgZmlsbD0iI0U4RThFOCIgZD0ibTkuNDgwNDYsOC41ODEzOGwxLjI2LC0xLjI2bC01LjA0LC01LjA0bC01LjQ2LDUuMDRsMS4yNiwxLjI2bDQuMiwtMy43OGwzLjc4LDMuNzh6Ii8+PC9zdmc+")}.hc-black .monaco-scrollable-element>.scrollbar>.right-arrow,.vs-dark .monaco-scrollable-element>.scrollbar>.right-arrow{background:url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTEgMTEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggdHJhbnNmb3JtPSJyb3RhdGUoOTAgNS42MTcxNjUwODg2NTM1NjQ1LDUuNTU4MDg5NzMzMTIzNzgpICIgZmlsbD0iI0U4RThFOCIgZD0ibTkuNjA3MTcsOC43MDgwOWwxLjI2LC0xLjI2bC01LjA0LC01LjA0bC01LjQ2LDUuMDRsMS4yNiwxLjI2bDQuMiwtMy43OGwzLjc4LDMuNzh6Ii8+PC9zdmc+")}.monaco-scrollable-element>.visible{opacity:1;background:transparent;transition:opacity .1s linear}.monaco-scrollable-element>.invisible{opacity:0;pointer-events:none}.monaco-scrollable-element>.invisible.fade{transition:opacity .8s linear}.monaco-scrollable-element>.shadow{position:absolute;display:none}.monaco-scrollable-element>.shadow.top{display:block;top:0;left:3px;height:3px;width:100%;box-shadow:inset 0 6px 6px -6px #ddd}.monaco-scrollable-element>.shadow.left{display:block;top:3px;left:0;height:100%;width:3px;box-shadow:inset 6px 0 6px -6px #ddd}.monaco-scrollable-element>.shadow.top-left-corner{display:block;top:0;left:0;height:3px;width:3px}.monaco-scrollable-element>.shadow.top.left{box-shadow:inset 6px 6px 6px -6px #ddd}.vs .monaco-scrollable-element>.scrollbar>.slider{background:hsla(0,0%,39%,.4)}.vs-dark .monaco-scrollable-element>.scrollbar>.slider{background:hsla(0,0%,47%,.4)}.hc-black .monaco-scrollable-element>.scrollbar>.slider{background:rgba(111,195,223,.6)}.monaco-scrollable-element>.scrollbar>.slider:hover{background:hsla(0,0%,39%,.7)}.hc-black .monaco-scrollable-element>.scrollbar>.slider:hover{background:rgba(111,195,223,.8)}.monaco-scrollable-element>.scrollbar>.slider.active{background:rgba(0,0,0,.6)}.vs-dark .monaco-scrollable-element>.scrollbar>.slider.active{background:hsla(0,0%,75%,.4)}.hc-black .monaco-scrollable-element>.scrollbar>.slider.active{background:#6fc3df}.vs-dark .monaco-scrollable-element .shadow.top{box-shadow:none}.vs-dark .monaco-scrollable-element .shadow.left{box-shadow:inset 6px 0 6px -6px #000}.vs-dark .monaco-scrollable-element .shadow.top.left{box-shadow:inset 6px 6px 6px -6px #000}.hc-black .monaco-scrollable-element .shadow.left,.hc-black .monaco-scrollable-element .shadow.top,.hc-black .monaco-scrollable-element .shadow.top.left{box-shadow:none}.monaco-editor .margin-view-overlays .folding{cursor:pointer;background-repeat:no-repeat;background-origin:border-box;background-position:calc(50% + 2px) 50%;background-size:auto calc(100% - 3px);opacity:0;transition:opacity .5s;background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03Ljk3NjAzIDEwLjA3MTlMMTIuMzMzMyA1LjcxNDYxTDEyLjk1MjEgNi4zMzMzM0w4LjI4NTM5IDExTDcuNjY2NjcgMTFMMyA2LjMzMzMzTDMuNjE4NzIgNS43MTQ2MUw3Ljk3NjAzIDEwLjA3MTlaIiBmaWxsPSIjNDI0MjQyIi8+Cjwvc3ZnPgo=")}.monaco-editor.hc-black .margin-view-overlays .folding,.monaco-editor.vs-dark .margin-view-overlays .folding{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03Ljk3NjAzIDEwLjA3MTlMMTIuMzMzMyA1LjcxNDYxTDEyLjk1MjEgNi4zMzMzM0w4LjI4NTM5IDExTDcuNjY2NjcgMTFMMyA2LjMzMzMzTDMuNjE4NzIgNS43MTQ2MUw3Ljk3NjAzIDEwLjA3MTlaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=")}.monaco-editor.hc-black .margin-view-overlays .folding{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03Ljk3NjAzIDEwLjA3MTlMMTIuMzMzMyA1LjcxNDYxTDEyLjk1MjEgNi4zMzMzM0w4LjI4NTM5IDExTDcuNjY2NjcgMTFMMyA2LjMzMzMzTDMuNjE4NzIgNS43MTQ2MUw3Ljk3NjAzIDEwLjA3MTlaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K")}.monaco-editor .margin-view-overlays .folding.alwaysShowFoldIcons,.monaco-editor .margin-view-overlays:hover .folding{opacity:1}.monaco-editor .margin-view-overlays .folding.collapsed{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC4wNzE5IDcuOTk5OTlMNS43MTQ2IDEyLjM1NzNMNi4zMzMzMiAxMi45NzZMMTEgOC4zMDkzNVY3LjY5MDYzTDYuMzMzMzIgMy4wMjM5Nkw1LjcxNDYgMy42NDI2OEwxMC4wNzE5IDcuOTk5OTlaIiBmaWxsPSIjNDI0MjQyIi8+Cjwvc3ZnPgo=");opacity:1}.monaco-editor.vs-dark .margin-view-overlays .folding.collapsed{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC4wNzE5IDcuOTk5OTlMNS43MTQ2IDEyLjM1NzNMNi4zMzMzMiAxMi45NzZMMTEgOC4zMDkzNVY3LjY5MDY0TDYuMzMzMzIgMy4wMjM5N0w1LjcxNDYgMy42NDI2OUwxMC4wNzE5IDcuOTk5OTlaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=")}.monaco-editor.hc-black .margin-view-overlays .folding.collapsed{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC4wNzE5IDcuOTk5OTlMNS43MTQ2IDEyLjM1NzNMNi4zMzMzMiAxMi45NzZMMTEgOC4zMDkzNVY3LjY5MDYzTDYuMzMzMzIgMy4wMjM5Nkw1LjcxNDYgMy42NDI2OEwxMC4wNzE5IDcuOTk5OTlaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K")}.monaco-editor .inline-folded:after{color:grey;margin:.1em .2em 0;content:"\22EF";display:inline;line-height:1em;cursor:pointer}.monaco-editor .peekview-widget .head{-o-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;display:-ms-flexbox;display:flex}.monaco-editor .peekview-widget .head .peekview-title{display:inline-block;font-size:13px;margin-left:20px;cursor:pointer}.monaco-editor .peekview-widget .head .peekview-title .dirname:not(:empty){font-size:.9em;margin-left:.5em}.monaco-editor .peekview-widget .head .peekview-actions{-ms-flex:1;flex:1;text-align:right;padding-right:2px}.monaco-editor .peekview-widget .head .peekview-actions>.monaco-action-bar{display:inline-block}.monaco-editor .peekview-widget .head .peekview-actions>.monaco-action-bar,.monaco-editor .peekview-widget .head .peekview-actions>.monaco-action-bar>.actions-container{height:100%}.monaco-editor .peekview-widget .head .peekview-actions>.monaco-action-bar .action-item{margin-left:4px}.monaco-editor .peekview-widget .head .peekview-actions>.monaco-action-bar .action-label{width:16px;height:100%;margin:0;line-height:inherit;background-repeat:no-repeat;background-position:50%}.monaco-editor .peekview-widget .head .peekview-actions>.monaco-action-bar .action-label.octicon{margin:0}.monaco-editor .peekview-widget .head .peekview-actions .action-label.icon.close-peekview-action{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04LjAwMDAxIDguNzA3MDhMMTEuNjQ2NSAxMi4zNTM1TDEyLjM1MzYgMTEuNjQ2NEw4LjcwNzExIDcuOTk5OThMMTIuMzUzNiA0LjM1MzUzTDExLjY0NjUgMy42NDY0Mkw4LjAwMDAxIDcuMjkyODdMNC4zNTM1NiAzLjY0NjQyTDMuNjQ2NDUgNC4zNTM1M0w3LjI5MjkgNy45OTk5OEwzLjY0NjQ1IDExLjY0NjRMNC4zNTM1NiAxMi4zNTM1TDguMDAwMDEgOC43MDcwOFoiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==") 50% no-repeat}.monaco-editor .peekview-widget>.body{border-top:1px solid;position:relative}.monaco-editor.hc-black .peekview-widget .head .peekview-actions .action-label.icon.close-peekview-action,.monaco-editor.vs-dark .peekview-widget .head .peekview-actions .action-label.icon.close-peekview-action{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04LjAwMDAxIDguNzA3MDhMMTEuNjQ2NSAxMi4zNTM1TDEyLjM1MzYgMTEuNjQ2NEw4LjcwNzExIDcuOTk5OThMMTIuMzUzNiA0LjM1MzUzTDExLjY0NjUgMy42NDY0Mkw4LjAwMDAxIDcuMjkyODdMNC4zNTM1NiAzLjY0NjQyTDMuNjQ2NDUgNC4zNTM1M0w3LjI5MjkgNy45OTk5OEwzLjY0NjQ1IDExLjY0NjRMNC4zNTM1NiAxMi4zNTM1TDguMDAwMDEgOC43MDcwOFoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==") 50% no-repeat}.monaco-editor .peekview-widget .peekview-actions .icon.chevron-up{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMy44NTM1IDYuMjkyOUw4Ljg1MzU2IDEuMjkyOTFIOC4xNDY0NUwzLjE0NjQ1IDYuMjkyOUwzLjg1MzU2IDcuMDAwMDFMOCAyLjg1MzU3VjE1LjA2MDdIOVYyLjg1MzU3TDEzLjE0NjQgNy4wMDAwMUwxMy44NTM1IDYuMjkyOVoiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==") 50% no-repeat}.hc-black .monaco-editor .peekview-widget .peekview-actions .icon.chevron-up,.vs-dark .monaco-editor .peekview-widget .peekview-actions .icon.chevron-up{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMy44NTM1IDYuMjkyOUw4Ljg1MzU2IDEuMjkyOTFIOC4xNDY0NUwzLjE0NjQ1IDYuMjkyOUwzLjg1MzU2IDcuMDAwMDFMOCAyLjg1MzU3VjE1LjA2MDdIOVYyLjg1MzU3TDEzLjE0NjQgNy4wMDAwMUwxMy44NTM1IDYuMjkyOVoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==") 50% no-repeat}.monaco-editor .peekview-widget .peekview-actions .icon.chevron-down{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zLjE0NjQ2IDkuNzY3NzdMOC4xNDY0NCAxNC43Njc4TDguODUzNTUgMTQuNzY3OEwxMy44NTM1IDkuNzY3NzdMMTMuMTQ2NCA5LjA2MDY2TDkgMTMuMjA3MUw5IDFMOCAwLjk5OTk5OUw4IDEzLjIwNzFMMy44NTM1NiA5LjA2MDY2TDMuMTQ2NDYgOS43Njc3N1oiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==") 50% no-repeat}.hc-black .monaco-editor .peekview-widget .peekview-actions .icon.chevron-down,.vs-dark .monaco-editor .peekview-widget .peekview-actions .icon.chevron-down{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zLjE0NjQ2IDkuNzY3NzdMOC4xNDY0NCAxNC43Njc4TDguODUzNTUgMTQuNzY3OEwxMy44NTM1IDkuNzY3NzdMMTMuMTQ2NCA5LjA2MDY2TDkgMTMuMjA3MUw5IDFMOCAwLjk5OTk5OUw4IDEzLjIwNzFMMy44NTM1NiA5LjA2MDY2TDMuMTQ2NDYgOS43Njc3N1oiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==") 50% no-repeat}::-ms-clear{display:none}.monaco-editor .editor-widget input{color:inherit}.monaco-editor{position:relative;overflow:visible;-webkit-text-size-adjust:100%;-webkit-font-feature-settings:"liga" off,"calt" off;font-feature-settings:"liga" off,"calt" off}.monaco-editor.enable-ligatures{-webkit-font-feature-settings:"liga" on,"calt" on;font-feature-settings:"liga" on,"calt" on}.monaco-editor .overflow-guard{position:relative;overflow:hidden}.monaco-editor .view-overlays{position:absolute;top:0}.monaco-editor .vs-whitespace{display:inline-block}.monaco-editor .inputarea{min-width:0;min-height:0;margin:0;padding:0;position:absolute;outline:none!important;resize:none;border:none;overflow:hidden;color:transparent;background-color:transparent}.monaco-editor .inputarea.ime-input{z-index:10}.monaco-editor .margin-view-overlays .line-numbers{position:absolute;text-align:right;display:inline-block;vertical-align:middle;box-sizing:border-box;cursor:default;height:100%}.monaco-editor .relative-current-line-number{text-align:left;display:inline-block;width:100%}.monaco-editor .margin-view-overlays .line-numbers{cursor:-webkit-image-set(url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNSIgaGVpZ2h0PSIyNSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCAxNSAyNSIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTUgMjU7Ij48cG9seWdvbiBzdHlsZT0iZmlsbDojRkZGRkZGO3N0cm9rZTojMDAwMDAwIiBwb2ludHM9IjE0LjUsMS4yIDEuOSwxMy44IDcsMTMuOCAzLjIsMjEuNSA2LjMsMjIuNSAxMC4xLDE0LjkgMTQuNSwxOCIvPjwvc3ZnPg==") 1x,url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMzAiIGhlaWdodD0iNTAiIHZpZXdCb3g9IjAgMCAzMCA1MCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzAgNTA7Ij48cG9seWdvbiBzdHlsZT0iZmlsbDojRkZGRkZGO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyOyIgcG9pbnRzPSIyOSwyLjQgMy44LDI3LjYgMTQsMjcuNiA2LjQsNDMgMTIuNiw0NSAyMC4yLDI5LjggMjksMzYiLz48L3N2Zz4K") 2x) 30 0,default}.monaco-editor.mac .margin-view-overlays .line-numbers{cursor:-webkit-image-set(url("data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTMgMTkiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDEzIDE5OyIgd2lkdGg9IjEzIiBoZWlnaHQ9IjE5Ij48c3R5bGUgdHlwZT0idGV4dC9jc3MiPi5zdDB7c3Ryb2tlOiNGRkZGRkY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fTwvc3R5bGU+PHRpdGxlPmZsaXBwZWQtY3Vyc29yLW1hYzwvdGl0bGU+PHBhdGggY2xhc3M9InN0MCIgZD0iTTUuMywxNi42bDEuNi00LjdIMi4xTDEyLjUsMS4ydjE0LjRMOS43LDEzbC0xLjYsNC42Yy0wLjIsMC41LTAuOCwwLjgtMS4zLDAuNkw2LDE3LjkgQzUuNCwxNy43LDUuMSwxNy4yLDUuMywxNi42eiIvPjwvc3ZnPgo=") 1x,url("data:image/svg+xml;base64,CjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmlld0JveD0iMCAwIDI2IDM4IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAyNiAzODsiIHdpZHRoPSIyNiIgaGVpZ2h0PSIzOCI+PHN0eWxlIHR5cGU9InRleHQvY3NzIj4uc3Qwe3N0cm9rZTojRkZGRkZGO3N0cm9rZS1taXRlcmxpbWl0OjEwO308L3N0eWxlPgk8dGl0bGU+ZmxpcHBlZC1jdXJzb3ItbWFjPC90aXRsZT48cGF0aCBjbGFzcz0ic3QwIiBkPSJNMTAuNiwzMy4ybDMuMi05LjRINC4yTDI1LDIuNHYyOC44TDE5LjQsMjZsLTMuMiw5LjJjLTAuNCwxLTEuNiwxLjYtMi42LDEuMkwxMiwzNS44IEMxMC44LDM1LjQsMTAuMiwzNC40LDEwLjYsMzMuMnoiLz48L3N2Zz4K") 2x) 24 3,default}.monaco-editor .margin-view-overlays .line-numbers.lh-odd{margin-top:1px}.monaco-editor .margin-view-overlays .current-line,.monaco-editor .view-overlays .current-line{display:block;position:absolute;left:0;top:0;box-sizing:border-box}.monaco-editor .margin-view-overlays .current-line.current-line-margin.current-line-margin-both{border-right:0}.monaco-editor .lines-content .cdr{position:absolute}.monaco-editor .glyph-margin{position:absolute;top:0}.monaco-editor .lines-content .cigr,.monaco-editor .lines-content .cigra,.monaco-editor .margin-view-overlays .cgmr{position:absolute}.monaco-editor.no-user-select .lines-content,.monaco-editor.no-user-select .view-line,.monaco-editor.no-user-select .view-lines{-webkit-user-select:none;-ms-user-select:none;-moz-user-select:none;-o-user-select:none;user-select:none}.monaco-editor .view-lines{cursor:text;white-space:nowrap}.monaco-editor.hc-black.mac .view-lines,.monaco-editor.vs-dark.mac .view-lines{cursor:-webkit-image-set(url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAL0lEQVQoz2NgCD3x//9/BhBYBWdhgFVAiVW4JBFKGIa4AqD0//9D3pt4I4tAdAMAHTQ/j5Zom30AAAAASUVORK5CYII=) 1x,url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAz0lEQVRIx2NgYGBY/R8I/vx5eelX3n82IJ9FxGf6tksvf/8FiTMQAcAGQMDvSwu09abffY8QYSAScNk45G198eX//yev73/4///701eh//kZSARckrNBRvz//+8+6ZohwCzjGNjdgQxkAg7B9WADeBjIBqtJCbhRA0YNoIkBSNmaPEMoNmA0FkYNoFKhapJ6FGyAH3nauaSmPfwI0v/3OukVi0CIZ+F25KrtYcx/CTIy0e+rC7R1Z4KMICVTQQ14feVXIbR695u14+Ir4gwAAD49E54wc1kWAAAAAElFTkSuQmCC) 2x) 5 8,text}.monaco-editor .view-line{position:absolute;width:100%}.monaco-editor .lines-decorations{position:absolute;top:0;background:#fff}.monaco-editor .margin-view-overlays .cldr{position:absolute;height:100%}.monaco-editor .margin-view-overlays .cmdr{position:absolute;left:0;width:100%;height:100%}.monaco-editor .minimap.slider-mouseover .minimap-slider{opacity:0;transition:opacity .1s linear}.monaco-editor .minimap.slider-mouseover .minimap-slider.active,.monaco-editor .minimap.slider-mouseover:hover .minimap-slider{opacity:1}.monaco-editor .minimap-shadow-hidden{position:absolute;width:0}.monaco-editor .minimap-shadow-visible{position:absolute;left:-6px;width:6px}.monaco-editor .overlayWidgets{position:absolute;top:0;left:0}.monaco-editor .view-ruler{position:absolute;top:0}.monaco-editor .scroll-decoration{position:absolute;top:0;left:0;height:6px}.monaco-editor .lines-content .cslr{position:absolute}.monaco-editor .top-left-radius{border-top-left-radius:3px}.monaco-editor .bottom-left-radius{border-bottom-left-radius:3px}.monaco-editor .top-right-radius{border-top-right-radius:3px}.monaco-editor .bottom-right-radius{border-bottom-right-radius:3px}.monaco-editor.hc-black .top-left-radius{border-top-left-radius:0}.monaco-editor.hc-black .bottom-left-radius{border-bottom-left-radius:0}.monaco-editor.hc-black .top-right-radius{border-top-right-radius:0}.monaco-editor.hc-black .bottom-right-radius{border-bottom-right-radius:0}.monaco-editor .cursors-layer{position:absolute;top:0}.monaco-editor .cursors-layer>.cursor{position:absolute;cursor:text;overflow:hidden}.monaco-editor .cursors-layer.cursor-smooth-caret-animation>.cursor{transition:80ms}.monaco-editor .cursors-layer.cursor-block-outline-style>.cursor{box-sizing:border-box;background:transparent!important;border-style:solid;border-width:1px}.monaco-editor .cursors-layer.cursor-underline-style>.cursor{border-bottom-width:2px;border-bottom-style:solid;background:transparent!important;box-sizing:border-box}.monaco-editor .cursors-layer.cursor-underline-thin-style>.cursor{border-bottom-width:1px;border-bottom-style:solid;background:transparent!important;box-sizing:border-box}@keyframes monaco-cursor-smooth{0%,20%{opacity:1}60%,to{opacity:0}}@keyframes monaco-cursor-phase{0%,20%{opacity:1}90%,to{opacity:0}}@keyframes monaco-cursor-expand{0%,20%{transform:scaleY(1)}80%,to{transform:scaleY(0)}}.cursor-smooth{animation:monaco-cursor-smooth .5s ease-in-out 0s 20 alternate}.cursor-phase{animation:monaco-cursor-phase .5s ease-in-out 0s 20 alternate}.cursor-expand>.cursor{animation:monaco-cursor-expand .5s ease-in-out 0s 20 alternate}.monaco-editor .zone-widget{position:absolute;z-index:10}.monaco-editor .zone-widget .zone-widget-container{border-top-style:solid;border-bottom-style:solid;border-top-width:0;border-bottom-width:0;position:relative}.monaco-editor .zone-widget .zone-widget-container.reference-zone-widget{border-top-width:1px;border-bottom-width:1px}.monaco-editor .reference-zone-widget .inline{display:inline-block;vertical-align:top}.monaco-editor .reference-zone-widget .messages{height:100%;width:100%;text-align:center;padding:3em 0}.monaco-editor .reference-zone-widget .ref-tree{line-height:23px}.monaco-editor .reference-zone-widget .ref-tree .reference{text-overflow:ellipsis;overflow:hidden}.monaco-editor .reference-zone-widget .ref-tree .reference-file{display:-ms-inline-flexbox;display:inline-flex;width:100%;height:100%}.monaco-editor .reference-zone-widget .ref-tree .monaco-list:focus .selected .reference-file{color:inherit!important}.monaco-editor .reference-zone-widget .ref-tree .reference-file .count{margin-right:12px;margin-left:auto}.monaco-editor.hc-black .reference-zone-widget .ref-tree .reference-file{font-weight:700}.monaco-icon-label{display:-ms-flexbox;display:flex;overflow:hidden;text-overflow:ellipsis}.monaco-icon-label:before{background-size:16px;background-position:0;background-repeat:no-repeat;padding-right:6px;width:16px;height:22px;display:inline-block;-webkit-font-smoothing:antialiased;vertical-align:top;-ms-flex-negative:0;flex-shrink:0}.monaco-icon-label>.monaco-icon-label-description-container{overflow:hidden;text-overflow:ellipsis}.monaco-icon-label>.monaco-icon-label-description-container>.label-name{color:inherit;white-space:pre}.monaco-icon-label>.monaco-icon-label-description-container>.label-description{opacity:.7;margin-left:.5em;font-size:.9em;white-space:pre}.monaco-icon-label.italic>.monaco-icon-label-description-container>.label-description,.monaco-icon-label.italic>.monaco-icon-label-description-container>.label-name{font-style:italic}.monaco-icon-label:after{opacity:.75;font-size:90%;font-weight:600;padding:0 12px 0 5px;margin-left:auto;text-align:center}.monaco-list:focus .selected .monaco-icon-label,.monaco-list:focus .selected .monaco-icon-label:after,.monaco-tree.focused .selected .monaco-icon-label,.monaco-tree.focused .selected .monaco-icon-label:after{color:inherit!important}.monaco-list-row.focused.selected .label-description,.monaco-list-row.selected .label-description,.monaco-tree-row.focused.selected .label-description,.monaco-tree-row.selected .label-description{opacity:.8}.monaco-count-badge{padding:3px 5px;border-radius:11px;font-size:11px;min-width:18px;min-height:18px;line-height:11px;font-weight:400;text-align:center;display:inline-block;box-sizing:border-box}.monaco-list{position:relative;height:100%;width:100%;white-space:nowrap}.monaco-list.mouse-support{-webkit-user-select:none;-moz-user-select:-moz-none;-ms-user-select:none;-o-user-select:none;user-select:none}.monaco-list>.monaco-scrollable-element{height:100%}.monaco-list-rows{position:relative;width:100%;height:100%}.monaco-list.horizontal-scrolling .monaco-list-rows{width:auto;min-width:100%}.monaco-list-row{position:absolute;-o-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;overflow:hidden;width:100%}.monaco-list.mouse-support .monaco-list-row{cursor:pointer;-ms-touch-action:none;touch-action:none}.monaco-list-row.scrolling{display:none!important}.monaco-list.element-focused,.monaco-list.selection-multiple,.monaco-list.selection-single{outline:0!important}.monaco-drag-image{display:inline-block;padding:1px 7px;border-radius:10px;font-size:12px;position:absolute}.monaco-list-type-filter{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;position:absolute;border-radius:2px;padding:0 3px;max-width:calc(100% - 10px);text-overflow:ellipsis;overflow:hidden;text-align:right;box-sizing:border-box;cursor:all-scroll;font-size:13px;line-height:18px;height:20px;z-index:1;top:4px}.monaco-list-type-filter.dragging{transition:top .2s,left .2s}.monaco-list-type-filter.ne{right:4px}.monaco-list-type-filter.nw{left:4px}.monaco-list-type-filter>.controls{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;box-sizing:border-box;transition:width .2s;width:0}.monaco-list-type-filter.dragging>.controls,.monaco-list-type-filter:hover>.controls{width:36px}.monaco-list-type-filter>.controls>*{box-sizing:border-box;width:16px;height:16px;margin:0 0 0 2px;-ms-flex-negative:0;flex-shrink:0}.monaco-list-type-filter>.controls>.filter{-webkit-appearance:none;width:16px;height:16px;background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEgMTJMMSAxMUgxMFYxMkgxWk0xIDdIMTVWOEgxTDEgN1pNMTIgM1Y0SDFMMSAzSDEyWiIgZmlsbD0iIzQyNDI0MiIvPgo8L3N2Zz4K");background-position:50% 50%;cursor:pointer}.monaco-list-type-filter>.controls>.filter:checked{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYgMTJWMTFIMTBWMTJINlpNNCA3SDEyVjhINFY3Wk0xNCAzVjRIMlYzSDE0WiIgZmlsbD0iIzQyNDI0MiIvPgo8L3N2Zz4K")}.vs-dark .monaco-list-type-filter>.controls>.filter{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEgMTJMMSAxMUgxMFYxMkgxWk0xIDdIMTVWOEgxTDEgN1pNMTIgM1Y0SDFMMSAzSDEyWiIgZmlsbD0iI0M1QzVDNSIvPgo8L3N2Zz4K")}.vs-dark .monaco-list-type-filter>.controls>.filter:checked{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYgMTJWMTFIMTBWMTJINlpNNCA3SDEyVjhINFY3Wk0xNCAzVjRIMlYzSDE0WiIgZmlsbD0iI0M1QzVDNSIvPgo8L3N2Zz4K")}.hc-black .monaco-list-type-filter>.controls>.filter{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEgMTJMMSAxMUgxMFYxMkgxWk0xIDdIMTVWOEgxTDEgN1pNMTIgM1Y0SDFMMSAzSDEyWiIgZmlsbD0iI0M1QzVDNSIvPgo8L3N2Zz4K")}.hc-black .monaco-list-type-filter>.controls>.filter:checked{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYgMTJWMTFIMTBWMTJINlpNNCA3SDEyVjhINFY3Wk0xNCAzVjRIMlYzSDE0WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg==")}.monaco-list-type-filter>.controls>.clear{border:none;background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04LjAwMDAxIDguNzA3MTRMMTEuNjQ2NSAxMi4zNTM2TDEyLjM1MzYgMTEuNjQ2NUw4LjcwNzExIDguMDAwMDRMMTIuMzUzNiA0LjM1MzU5TDExLjY0NjUgMy42NDY0OEw4LjAwMDAxIDcuMjkyOTNMNC4zNTM1NiAzLjY0NjQ4TDMuNjQ2NDUgNC4zNTM1OUw3LjI5MjkgOC4wMDAwNEwzLjY0NjQ1IDExLjY0NjVMNC4zNTM1NiAxMi4zNTM2TDguMDAwMDEgOC43MDcxNFoiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==");cursor:pointer}.hc-black .monaco-list-type-filter>.controls>.clear,.vs-dark .monaco-list-type-filter>.controls>.clear{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04LjAwMDAxIDguNzA3MTRMMTEuNjQ2NSAxMi4zNTM2TDEyLjM1MzYgMTEuNjQ2NUw4LjcwNzExIDguMDAwMDRMMTIuMzUzNiA0LjM1MzU5TDExLjY0NjUgMy42NDY0OEw4LjAwMDAxIDcuMjkyOTNMNC4zNTM1NiAzLjY0NjQ4TDMuNjQ2NDUgNC4zNTM1OUw3LjI5MjkgOC4wMDAwNEwzLjY0NjQ1IDExLjY0NjVMNC4zNTM1NiAxMi4zNTM2TDguMDAwMDEgOC43MDcxNFoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==")}.monaco-list-type-filter-message{position:absolute;box-sizing:border-box;width:100%;height:100%;top:0;left:0;padding:40px 1em 1em;text-align:center;white-space:normal;opacity:.7;pointer-events:none}.monaco-list-type-filter-message:empty{display:none}.monaco-list-type-filter{cursor:grab}.monaco-list-type-filter.dragging{cursor:grabbing}.monaco-tl-row{display:-ms-flexbox;display:flex;height:100%;-ms-flex-align:center;align-items:center;position:relative}.monaco-tl-indent{height:100%;position:absolute;top:0;left:18px;pointer-events:none}.hide-arrows .monaco-tl-indent{left:12px}.monaco-tl-indent>.indent-guide{display:inline-block;box-sizing:border-box;height:100%;border-left:1px solid transparent;transition:border-color .1s linear}.monaco-tl-contents,.monaco-tl-twistie{height:100%}.monaco-tl-twistie{font-size:10px;text-align:right;margin-right:6px;-ms-flex-negative:0;flex-shrink:0;width:16px}.monaco-tl-contents{-ms-flex:1;flex:1;overflow:hidden}.monaco-tl-twistie.collapsible{background-size:16px;background-position:3px 50%;background-repeat:no-repeat;background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03Ljk3NjAzIDEwLjA3MTlMMTIuMzMzMyA1LjcxNDYxTDEyLjk1MjEgNi4zMzMzM0w4LjI4NTM5IDExTDcuNjY2NjcgMTFMMyA2LjMzMzMzTDMuNjE4NzIgNS43MTQ2MUw3Ljk3NjAzIDEwLjA3MTlaIiBmaWxsPSIjNDI0MjQyIi8+Cjwvc3ZnPgo=")}.monaco-tl-twistie.collapsible.collapsed:not(.loading){display:inline-block;background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC4wNzE5IDcuOTk5OTlMNS43MTQ2MSAxMi4zNTczTDYuMzMzMzMgMTIuOTc2TDExIDguMzA5MzVWNy42OTA2M0w2LjMzMzMzIDMuMDIzOTZMNS43MTQ2MSAzLjY0MjY4TDEwLjA3MTkgNy45OTk5OVoiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==")}.vs-dark .monaco-tl-twistie.collapsible:not(.loading){background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03Ljk3NjAzIDEwLjA3MTlMMTIuMzMzMyA1LjcxNDYxTDEyLjk1MjEgNi4zMzMzM0w4LjI4NTM5IDExTDcuNjY2NjcgMTFMMyA2LjMzMzMzTDMuNjE4NzIgNS43MTQ2MUw3Ljk3NjAzIDEwLjA3MTlaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=")}.vs-dark .monaco-tl-twistie.collapsible.collapsed:not(.loading){background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC4wNzE5IDcuOTk5OTlMNS43MTQ2MSAxMi4zNTczTDYuMzMzMzMgMTIuOTc2TDExIDguMzA5MzVWNy42OTA2NEw2LjMzMzMzIDMuMDIzOTdMNS43MTQ2MSAzLjY0MjY5TDEwLjA3MTkgNy45OTk5OVoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==")}.hc-black .monaco-tl-twistie.collapsible:not(.loading){background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03Ljk3NjAzIDEwLjA3MTlMMTIuMzMzMyA1LjcxNDYxTDEyLjk1MjEgNi4zMzMzM0w4LjI4NTM5IDExTDcuNjY2NjcgMTFMMyA2LjMzMzMzTDMuNjE4NzIgNS43MTQ2MUw3Ljk3NjAzIDEwLjA3MTlaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K")}.hc-black .monaco-tl-twistie.collapsible.collapsed:not(.loading){background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC4wNzE5IDcuOTk5OTlMNS43MTQ2MSAxMi4zNTczTDYuMzMzMzMgMTIuOTc2TDExIDguMzA5MzVWNy42OTA2M0w2LjMzMzMzIDMuMDIzOTZMNS43MTQ2MSAzLjY0MjY4TDEwLjA3MTkgNy45OTk5OVoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=")}.monaco-tl-twistie.loading{background-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBzdGFuZGFsb25lPSdubycgPz4KPHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZlcnNpb249JzEuMScgd2lkdGg9JzEwcHgnIGhlaWdodD0nMTBweCc+Cgk8c3R5bGU+CiAgICBjaXJjbGUgewogICAgICBhbmltYXRpb246IGJhbGwgMC42cyBsaW5lYXIgaW5maW5pdGU7CiAgICB9CgogICAgY2lyY2xlOm50aC1jaGlsZCgyKSB7IGFuaW1hdGlvbi1kZWxheTogMC4wNzVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDMpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjE1czsgfQogICAgY2lyY2xlOm50aC1jaGlsZCg0KSB7IGFuaW1hdGlvbi1kZWxheTogMC4yMjVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDUpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjNzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDYpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjM3NXM7IH0KICAgIGNpcmNsZTpudGgtY2hpbGQoNykgeyBhbmltYXRpb24tZGVsYXk6IDAuNDVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDgpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjUyNXM7IH0KCiAgICBAa2V5ZnJhbWVzIGJhbGwgewogICAgICBmcm9tIHsgb3BhY2l0eTogMTsgfQogICAgICB0byB7IG9wYWNpdHk6IDAuMzsgfQogICAgfQoJPC9zdHlsZT4KCTxnPgoJCTxjaXJjbGUgY3g9JzUnIGN5PScxJyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJCTxjaXJjbGUgY3g9JzcuODI4NCcgY3k9JzIuMTcxNicgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCQk8Y2lyY2xlIGN4PSc5JyBjeT0nNScgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCQk8Y2lyY2xlIGN4PSc3LjgyODQnIGN5PSc3LjgyODQnIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+CgkJPGNpcmNsZSBjeD0nNScgY3k9JzknIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+CgkJPGNpcmNsZSBjeD0nMi4xNzE2JyBjeT0nNy44Mjg0JyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJCTxjaXJjbGUgY3g9JzEnIGN5PSc1JyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJCTxjaXJjbGUgY3g9JzIuMTcxNicgY3k9JzIuMTcxNicgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCTwvZz4KPC9zdmc+Cg==");background-position:0}.vs-dark .monaco-tl-twistie.loading{background-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBzdGFuZGFsb25lPSdubycgPz4KPHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZlcnNpb249JzEuMScgd2lkdGg9JzEwcHgnIGhlaWdodD0nMTBweCc+Cgk8c3R5bGU+CiAgICBjaXJjbGUgewogICAgICBhbmltYXRpb246IGJhbGwgMC42cyBsaW5lYXIgaW5maW5pdGU7CiAgICB9CgogICAgY2lyY2xlOm50aC1jaGlsZCgyKSB7IGFuaW1hdGlvbi1kZWxheTogMC4wNzVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDMpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjE1czsgfQogICAgY2lyY2xlOm50aC1jaGlsZCg0KSB7IGFuaW1hdGlvbi1kZWxheTogMC4yMjVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDUpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjNzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDYpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjM3NXM7IH0KICAgIGNpcmNsZTpudGgtY2hpbGQoNykgeyBhbmltYXRpb24tZGVsYXk6IDAuNDVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDgpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjUyNXM7IH0KCiAgICBAa2V5ZnJhbWVzIGJhbGwgewogICAgICBmcm9tIHsgb3BhY2l0eTogMTsgfQogICAgICB0byB7IG9wYWNpdHk6IDAuMzsgfQogICAgfQoJPC9zdHlsZT4KCTxnIHN0eWxlPSJmaWxsOmdyZXk7Ij4KCQk8Y2lyY2xlIGN4PSc1JyBjeT0nMScgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCQk8Y2lyY2xlIGN4PSc3LjgyODQnIGN5PScyLjE3MTYnIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+CgkJPGNpcmNsZSBjeD0nOScgY3k9JzUnIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+CgkJPGNpcmNsZSBjeD0nNy44Mjg0JyBjeT0nNy44Mjg0JyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJCTxjaXJjbGUgY3g9JzUnIGN5PSc5JyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJCTxjaXJjbGUgY3g9JzIuMTcxNicgY3k9JzcuODI4NCcgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCQk8Y2lyY2xlIGN4PScxJyBjeT0nNScgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCQk8Y2lyY2xlIGN4PScyLjE3MTYnIGN5PScyLjE3MTYnIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+Cgk8L2c+Cjwvc3ZnPgo=")}.hc-black .monaco-tl-twistie.loading{background-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBzdGFuZGFsb25lPSdubycgPz4KPHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZlcnNpb249JzEuMScgd2lkdGg9JzEwcHgnIGhlaWdodD0nMTBweCc+Cgk8c3R5bGU+CiAgICBjaXJjbGUgewogICAgICBhbmltYXRpb246IGJhbGwgMC42cyBsaW5lYXIgaW5maW5pdGU7CiAgICB9CgogICAgY2lyY2xlOm50aC1jaGlsZCgyKSB7IGFuaW1hdGlvbi1kZWxheTogMC4wNzVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDMpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjE1czsgfQogICAgY2lyY2xlOm50aC1jaGlsZCg0KSB7IGFuaW1hdGlvbi1kZWxheTogMC4yMjVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDUpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjNzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDYpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjM3NXM7IH0KICAgIGNpcmNsZTpudGgtY2hpbGQoNykgeyBhbmltYXRpb24tZGVsYXk6IDAuNDVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDgpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjUyNXM7IH0KCiAgICBAa2V5ZnJhbWVzIGJhbGwgewogICAgICBmcm9tIHsgb3BhY2l0eTogMTsgfQogICAgICB0byB7IG9wYWNpdHk6IDAuMzsgfQogICAgfQoJPC9zdHlsZT4KCTxnIHN0eWxlPSJmaWxsOndoaXRlOyI+CgkJPGNpcmNsZSBjeD0nNScgY3k9JzEnIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+CgkJPGNpcmNsZSBjeD0nNy44Mjg0JyBjeT0nMi4xNzE2JyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJCTxjaXJjbGUgY3g9JzknIGN5PSc1JyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJCTxjaXJjbGUgY3g9JzcuODI4NCcgY3k9JzcuODI4NCcgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCQk8Y2lyY2xlIGN4PSc1JyBjeT0nOScgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCQk8Y2lyY2xlIGN4PScyLjE3MTYnIGN5PSc3LjgyODQnIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+CgkJPGNpcmNsZSBjeD0nMScgY3k9JzUnIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+CgkJPGNpcmNsZSBjeD0nMi4xNzE2JyBjeT0nMi4xNzE2JyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJPC9nPgo8L3N2Zz4K")}.monaco-split-view2{position:relative;width:100%;height:100%}.monaco-split-view2>.sash-container{position:absolute;width:100%;height:100%;pointer-events:none}.monaco-split-view2>.sash-container>.monaco-sash{pointer-events:auto}.monaco-split-view2>.split-view-container{display:-ms-flexbox;display:flex;width:100%;height:100%;white-space:nowrap}.monaco-split-view2.vertical>.split-view-container{-ms-flex-direction:column;flex-direction:column}.monaco-split-view2.horizontal>.split-view-container{-ms-flex-direction:row;flex-direction:row}.monaco-split-view2>.split-view-container>.split-view-view{white-space:normal;-ms-flex:none;flex:none;position:relative}.monaco-split-view2>.split-view-container>.split-view-view:not(.visible){display:none}.monaco-split-view2.vertical>.split-view-container>.split-view-view{width:100%}.monaco-split-view2.horizontal>.split-view-container>.split-view-view{height:100%;display:inline-block}.monaco-split-view2.separator-border>.split-view-container>.split-view-view:not(:first-child):before{content:" ";position:absolute;top:0;left:0;z-index:5;pointer-events:none;background-color:var(--separator-border)}.monaco-split-view2.separator-border.horizontal>.split-view-container>.split-view-view:not(:first-child):before{height:100%;width:1px}.monaco-split-view2.separator-border.vertical>.split-view-container>.split-view-view:not(:first-child):before{height:1px;width:100%}.monaco-editor .goto-definition-link{text-decoration:underline;cursor:pointer}.monaco-editor .peekview-widget .head .peekview-title .severity-icon{display:inline-block;vertical-align:text-top;margin-right:4px}.monaco-editor .marker-widget{text-overflow:ellipsis;white-space:nowrap}.monaco-editor .marker-widget>.stale{opacity:.6;font-style:italic}.monaco-editor .marker-widget .title{display:inline-block;padding-right:5px}.monaco-editor .marker-widget .descriptioncontainer{position:absolute;white-space:pre;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;padding:8px 12px 0 20px}.monaco-editor .marker-widget .descriptioncontainer .message{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.monaco-editor .marker-widget .descriptioncontainer .message .details{padding-left:6px}.monaco-editor .marker-widget .descriptioncontainer .message .code,.monaco-editor .marker-widget .descriptioncontainer .message .source{opacity:.6}.monaco-editor .marker-widget .descriptioncontainer .filename{cursor:pointer}.monaco-keybinding{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;line-height:10px}.monaco-keybinding>.monaco-keybinding-key{display:inline-block;border:1px solid hsla(0,0%,80%,.4);border-bottom-color:hsla(0,0%,73%,.4);border-radius:3px;box-shadow:inset 0 -1px 0 hsla(0,0%,73%,.4);background-color:hsla(0,0%,87%,.4);vertical-align:middle;color:#555;font-size:11px;padding:3px 5px;margin:0 2px}.monaco-keybinding>.monaco-keybinding-key:first-child{margin-left:0}.monaco-keybinding>.monaco-keybinding-key:last-child{margin-right:0}.hc-black .monaco-keybinding>.monaco-keybinding-key,.vs-dark .monaco-keybinding>.monaco-keybinding-key{background-color:hsla(0,0%,50%,.17);color:#ccc;border:1px solid rgba(51,51,51,.6);border-bottom-color:rgba(68,68,68,.6);box-shadow:inset 0 -1px 0 rgba(68,68,68,.6)}.monaco-keybinding>.monaco-keybinding-key-separator{display:inline-block}.monaco-keybinding>.monaco-keybinding-key-chord-separator{width:6px}.monaco-quick-open-widget .monaco-list .monaco-list-row .monaco-highlighted-label .highlight,.monaco-quick-open-widget .monaco-tree .monaco-tree-row .monaco-highlighted-label .highlight{color:#0066bf}.vs-dark .monaco-quick-open-widget .monaco-list .monaco-list-row .monaco-highlighted-label .highlight,.vs-dark .monaco-quick-open-widget .monaco-tree .monaco-tree-row .monaco-highlighted-label .highlight{color:#0097fb}.hc-black .monaco-quick-open-widget .monaco-list .monaco-list-row .monaco-highlighted-label .highlight,.hc-black .monaco-quick-open-widget .monaco-tree .monaco-tree-row .monaco-highlighted-label .highlight{color:#f38518}.monaco-quick-open-widget{position:absolute;width:600px;z-index:2000;padding-bottom:6px;left:50%;margin-left:-300px}.monaco-quick-open-widget .monaco-progress-container{position:absolute;left:0;top:38px;z-index:1;height:2px}.monaco-quick-open-widget .monaco-progress-container .progress-bit{height:2px}.monaco-quick-open-widget .quick-open-input{width:588px;border:none;margin:6px}.monaco-quick-open-widget .quick-open-input .monaco-inputbox{width:100%;height:25px}.monaco-quick-open-widget .quick-open-result-count{position:absolute;left:-10000px}.monaco-quick-open-widget .quick-open-tree{line-height:22px}.monaco-quick-open-widget .quick-open-tree .monaco-tree-row>.content>.sub-content{overflow:hidden}.monaco-quick-open-widget.content-changing .quick-open-tree .monaco-scrollable-element .slider{display:none}.monaco-quick-open-widget .quick-open-tree .quick-open-entry{overflow:hidden;text-overflow:ellipsis;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;height:100%}.monaco-quick-open-widget .quick-open-tree .quick-open-entry>.quick-open-row{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon{overflow:hidden;width:16px;height:16px;margin-right:4px;display:inline-block;vertical-align:middle;-ms-flex-negative:0;flex-shrink:0}.monaco-quick-open-widget .quick-open-tree .monaco-icon-label,.monaco-quick-open-widget .quick-open-tree .monaco-icon-label .monaco-icon-label-description-container{-ms-flex:1;flex:1}.monaco-quick-open-widget .quick-open-tree .quick-open-entry .monaco-highlighted-label span{opacity:1}.monaco-quick-open-widget .quick-open-tree .quick-open-entry-meta{opacity:.7;line-height:normal}.monaco-quick-open-widget .quick-open-tree .content.has-group-label .quick-open-entry-keybinding{margin-right:8px}.monaco-quick-open-widget .quick-open-tree .quick-open-entry-keybinding .monaco-keybinding-key{vertical-align:text-bottom}.monaco-quick-open-widget .quick-open-tree .results-group{margin-right:18px}.monaco-quick-open-widget .quick-open-tree .focused .monaco-tree-row.focused>.content.has-actions>.results-group,.monaco-quick-open-widget .quick-open-tree .monaco-tree-row.focused>.content.has-actions>.results-group,.monaco-quick-open-widget .quick-open-tree .monaco-tree-row:hover:not(.highlighted)>.content.has-actions>.results-group{margin-right:0}.monaco-quick-open-widget .quick-open-tree .results-group-separator{border-top-width:1px;border-top-style:solid;box-sizing:border-box;margin-left:-11px;padding-left:11px}.monaco-tree .monaco-tree-row>.content.actions{position:relative;display:-ms-flexbox;display:flex}.monaco-tree .monaco-tree-row>.content.actions>.sub-content{-ms-flex:1;flex:1}.monaco-tree .monaco-tree-row>.content.actions .action-item{margin:0}.monaco-tree .monaco-tree-row>.content.actions>.primary-action-bar{line-height:22px;display:none;padding:0 .8em 0 .4em}.monaco-tree .monaco-tree-row.focused>.content.has-actions>.primary-action-bar{width:0;display:block}.monaco-tree.focused .monaco-tree-row.focused>.content.has-actions>.primary-action-bar,.monaco-tree .monaco-tree-row:hover:not(.highlighted)>.content.has-actions>.primary-action-bar,.monaco-tree .monaco-tree-row>.content.has-actions.more>.primary-action-bar{width:inherit;display:block}.monaco-tree .monaco-tree-row>.content.actions>.primary-action-bar .action-label{margin-right:.4em;margin-top:4px;background-repeat:no-repeat;width:16px;height:16px}.monaco-quick-open-widget .quick-open-tree .monaco-highlighted-label .highlight{font-weight:700}.monaco-tree{height:100%;width:100%;white-space:nowrap;-webkit-user-select:none;-moz-user-select:-moz-none;-ms-user-select:none;-o-user-select:none;user-select:none;position:relative}.monaco-tree>.monaco-scrollable-element{height:100%}.monaco-tree>.monaco-scrollable-element>.monaco-tree-wrapper{height:100%;width:100%;position:relative}.monaco-tree .monaco-tree-rows{position:absolute;width:100%;height:100%}.monaco-tree .monaco-tree-rows>.monaco-tree-row{-o-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;cursor:pointer;overflow:hidden;width:100%;-ms-touch-action:none;touch-action:none}.monaco-tree .monaco-tree-rows>.monaco-tree-row>.content{position:relative;height:100%}.monaco-tree-drag-image{display:inline-block;padding:1px 7px;border-radius:10px;font-size:12px;position:absolute}.monaco-tree .monaco-tree-rows>.monaco-tree-row.scrolling{display:none}.monaco-tree .monaco-tree-rows.show-twisties>.monaco-tree-row.has-children>.content:before{content:" ";position:absolute;display:block;background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC4wNzE5IDcuOTk5OTlMNS43MTQ2MSAxMi4zNTczTDYuMzMzMzMgMTIuOTc2TDExIDguMzA5MzVWNy42OTA2M0w2LjMzMzMzIDMuMDIzOTZMNS43MTQ2MSAzLjY0MjY4TDEwLjA3MTkgNy45OTk5OVoiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==") 50% 50% no-repeat;width:16px;height:100%;top:0;left:-16px}.monaco-tree .monaco-tree-rows.show-twisties>.monaco-tree-row.expanded>.content:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03Ljk3NjAyIDEwLjA3MTlMMTIuMzMzMyA1LjcxNDYxTDEyLjk1MiA2LjMzMzMzTDguMjg1MzggMTFMNy42NjY2NiAxMUwzIDYuMzMzMzNMMy42MTg3MiA1LjcxNDYxTDcuOTc2MDIgMTAuMDcxOVoiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==")}.monaco-tree .monaco-tree-rows>.monaco-tree-row.has-children.loading>.content:before{background-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBzdGFuZGFsb25lPSdubycgPz4KPHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZlcnNpb249JzEuMScgd2lkdGg9JzEwcHgnIGhlaWdodD0nMTBweCc+Cgk8c3R5bGU+CiAgICBjaXJjbGUgewogICAgICBhbmltYXRpb246IGJhbGwgMC42cyBsaW5lYXIgaW5maW5pdGU7CiAgICB9CgogICAgY2lyY2xlOm50aC1jaGlsZCgyKSB7IGFuaW1hdGlvbi1kZWxheTogMC4wNzVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDMpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjE1czsgfQogICAgY2lyY2xlOm50aC1jaGlsZCg0KSB7IGFuaW1hdGlvbi1kZWxheTogMC4yMjVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDUpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjNzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDYpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjM3NXM7IH0KICAgIGNpcmNsZTpudGgtY2hpbGQoNykgeyBhbmltYXRpb24tZGVsYXk6IDAuNDVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDgpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjUyNXM7IH0KCiAgICBAa2V5ZnJhbWVzIGJhbGwgewogICAgICBmcm9tIHsgb3BhY2l0eTogMTsgfQogICAgICB0byB7IG9wYWNpdHk6IDAuMzsgfQogICAgfQoJPC9zdHlsZT4KCTxnPgoJCTxjaXJjbGUgY3g9JzUnIGN5PScxJyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJCTxjaXJjbGUgY3g9JzcuODI4NCcgY3k9JzIuMTcxNicgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCQk8Y2lyY2xlIGN4PSc5JyBjeT0nNScgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCQk8Y2lyY2xlIGN4PSc3LjgyODQnIGN5PSc3LjgyODQnIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+CgkJPGNpcmNsZSBjeD0nNScgY3k9JzknIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+CgkJPGNpcmNsZSBjeD0nMi4xNzE2JyBjeT0nNy44Mjg0JyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJCTxjaXJjbGUgY3g9JzEnIGN5PSc1JyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJCTxjaXJjbGUgY3g9JzIuMTcxNicgY3k9JzIuMTcxNicgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCTwvZz4KPC9zdmc+Cg==")}.monaco-tree.highlighted .monaco-tree-rows>.monaco-tree-row:not(.highlighted){opacity:.3}.vs-dark .monaco-tree .monaco-tree-rows.show-twisties>.monaco-tree-row.has-children>.content:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC4wNzE5IDcuOTk5OTlMNS43MTQ2MSAxMi4zNTczTDYuMzMzMzMgMTIuOTc2TDExIDguMzA5MzVWNy42OTA2NEw2LjMzMzMzIDMuMDIzOTdMNS43MTQ2MSAzLjY0MjY5TDEwLjA3MTkgNy45OTk5OVoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==")}.vs-dark .monaco-tree .monaco-tree-rows.show-twisties>.monaco-tree-row.expanded>.content:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03Ljk3NjAyIDEwLjA3MTlMMTIuMzMzMyA1LjcxNDYxTDEyLjk1MiA2LjMzMzMzTDguMjg1MzggMTFMNy42NjY2NiAxMUwzIDYuMzMzMzNMMy42MTg3MiA1LjcxNDYxTDcuOTc2MDIgMTAuMDcxOVoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==")}.vs-dark .monaco-tree .monaco-tree-rows>.monaco-tree-row.has-children.loading>.content:before{background-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBzdGFuZGFsb25lPSdubycgPz4KPHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZlcnNpb249JzEuMScgd2lkdGg9JzEwcHgnIGhlaWdodD0nMTBweCc+Cgk8c3R5bGU+CiAgICBjaXJjbGUgewogICAgICBhbmltYXRpb246IGJhbGwgMC42cyBsaW5lYXIgaW5maW5pdGU7CiAgICB9CgogICAgY2lyY2xlOm50aC1jaGlsZCgyKSB7IGFuaW1hdGlvbi1kZWxheTogMC4wNzVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDMpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjE1czsgfQogICAgY2lyY2xlOm50aC1jaGlsZCg0KSB7IGFuaW1hdGlvbi1kZWxheTogMC4yMjVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDUpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjNzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDYpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjM3NXM7IH0KICAgIGNpcmNsZTpudGgtY2hpbGQoNykgeyBhbmltYXRpb24tZGVsYXk6IDAuNDVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDgpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjUyNXM7IH0KCiAgICBAa2V5ZnJhbWVzIGJhbGwgewogICAgICBmcm9tIHsgb3BhY2l0eTogMTsgfQogICAgICB0byB7IG9wYWNpdHk6IDAuMzsgfQogICAgfQoJPC9zdHlsZT4KCTxnIHN0eWxlPSJmaWxsOmdyZXk7Ij4KCQk8Y2lyY2xlIGN4PSc1JyBjeT0nMScgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCQk8Y2lyY2xlIGN4PSc3LjgyODQnIGN5PScyLjE3MTYnIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+CgkJPGNpcmNsZSBjeD0nOScgY3k9JzUnIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+CgkJPGNpcmNsZSBjeD0nNy44Mjg0JyBjeT0nNy44Mjg0JyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJCTxjaXJjbGUgY3g9JzUnIGN5PSc5JyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJCTxjaXJjbGUgY3g9JzIuMTcxNicgY3k9JzcuODI4NCcgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCQk8Y2lyY2xlIGN4PScxJyBjeT0nNScgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCQk8Y2lyY2xlIGN4PScyLjE3MTYnIGN5PScyLjE3MTYnIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+Cgk8L2c+Cjwvc3ZnPgo=")}.hc-black .monaco-tree .monaco-tree-rows.show-twisties>.monaco-tree-row.has-children>.content:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC4wNzE5IDcuOTk5OTlMNS43MTQ2MSAxMi4zNTczTDYuMzMzMzMgMTIuOTc2TDExIDguMzA5MzVWNy42OTA2M0w2LjMzMzMzIDMuMDIzOTZMNS43MTQ2MSAzLjY0MjY4TDEwLjA3MTkgNy45OTk5OVoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=")}.hc-black .monaco-tree .monaco-tree-rows.show-twisties>.monaco-tree-row.expanded>.content:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03Ljk3NjAyIDEwLjA3MTlMMTIuMzMzMyA1LjcxNDYxTDEyLjk1MiA2LjMzMzMzTDguMjg1MzggMTFMNy42NjY2NiAxMUwzIDYuMzMzMzNMMy42MTg3MiA1LjcxNDYxTDcuOTc2MDIgMTAuMDcxOVoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=")}.hc-black .monaco-tree .monaco-tree-rows>.monaco-tree-row.has-children.loading>.content:before{background-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBzdGFuZGFsb25lPSdubycgPz4KPHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZlcnNpb249JzEuMScgd2lkdGg9JzEwcHgnIGhlaWdodD0nMTBweCc+Cgk8c3R5bGU+CiAgICBjaXJjbGUgewogICAgICBhbmltYXRpb246IGJhbGwgMC42cyBsaW5lYXIgaW5maW5pdGU7CiAgICB9CgogICAgY2lyY2xlOm50aC1jaGlsZCgyKSB7IGFuaW1hdGlvbi1kZWxheTogMC4wNzVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDMpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjE1czsgfQogICAgY2lyY2xlOm50aC1jaGlsZCg0KSB7IGFuaW1hdGlvbi1kZWxheTogMC4yMjVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDUpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjNzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDYpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjM3NXM7IH0KICAgIGNpcmNsZTpudGgtY2hpbGQoNykgeyBhbmltYXRpb24tZGVsYXk6IDAuNDVzOyB9CiAgICBjaXJjbGU6bnRoLWNoaWxkKDgpIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjUyNXM7IH0KCiAgICBAa2V5ZnJhbWVzIGJhbGwgewogICAgICBmcm9tIHsgb3BhY2l0eTogMTsgfQogICAgICB0byB7IG9wYWNpdHk6IDAuMzsgfQogICAgfQoJPC9zdHlsZT4KCTxnIHN0eWxlPSJmaWxsOndoaXRlOyI+CgkJPGNpcmNsZSBjeD0nNScgY3k9JzEnIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+CgkJPGNpcmNsZSBjeD0nNy44Mjg0JyBjeT0nMi4xNzE2JyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJCTxjaXJjbGUgY3g9JzknIGN5PSc1JyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJCTxjaXJjbGUgY3g9JzcuODI4NCcgY3k9JzcuODI4NCcgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCQk8Y2lyY2xlIGN4PSc1JyBjeT0nOScgcj0nMScgc3R5bGU9J29wYWNpdHk6MC4zOycgLz4KCQk8Y2lyY2xlIGN4PScyLjE3MTYnIGN5PSc3LjgyODQnIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+CgkJPGNpcmNsZSBjeD0nMScgY3k9JzUnIHI9JzEnIHN0eWxlPSdvcGFjaXR5OjAuMzsnIC8+CgkJPGNpcmNsZSBjeD0nMi4xNzE2JyBjeT0nMi4xNzE2JyByPScxJyBzdHlsZT0nb3BhY2l0eTowLjM7JyAvPgoJPC9nPgo8L3N2Zz4K")}.monaco-tree-action.collapse-all{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTkgOUg0VjEwSDlWOVoiIGZpbGw9IiM0MjQyNDIiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik01IDNMNiAySDEzTDE0IDNWMTBMMTMgMTFIMTFWMTNMMTAgMTRIM0wyIDEzVjZMMyA1SDVWM1pNNiA1SDEwTDExIDZWMTBIMTNWM0g2VjVaTTEwIDZIM1YxM0gxMFY2WiIgZmlsbD0iIzQyNDI0MiIvPgo8L3N2Zz4K") 50% no-repeat}.vs-dark .monaco-tree-action.collapse-all{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTkgOUg0VjEwSDlWOVoiIGZpbGw9IiNDNUM1QzUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik01IDNMNiAySDEzTDE0IDNWMTBMMTMgMTFIMTFWMTNMMTAgMTRIM0wyIDEzVjZMMyA1SDVWM1pNNiA1SDEwTDExIDZWMTBIMTNWM0g2VjVaTTEwIDZIM1YxM0gxMFY2WiIgZmlsbD0iI0M1QzVDNSIvPgo8L3N2Zz4K") 50% no-repeat}.hc-black .monaco-tree-action.collapse-all{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTkgOUg0VjEwSDlWOVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNSAzTDYgMkgxM0wxNCAzVjEwTDEzIDExSDExVjEzTDEwIDE0SDNMMiAxM1Y2TDMgNUg1VjNaTTYgNUgxMEwxMSA2VjEwSDEzVjNINlY1Wk0xMCA2SDNWMTNIMTBWNloiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=") 50% no-repeat}.monaco-progress-container{width:100%;height:5px;overflow:hidden}.monaco-progress-container .progress-bit{width:2%;height:5px;position:absolute;left:0;display:none}.monaco-progress-container.active .progress-bit{display:inherit}.monaco-progress-container.discrete .progress-bit{left:0;transition:width .1s linear}.monaco-progress-container.discrete.done .progress-bit{width:100%}.monaco-progress-container.infinite .progress-bit{animation-name:progress;animation-duration:4s;animation-iteration-count:infinite;animation-timing-function:linear;-ms-animation-name:progress;-ms-animation-duration:4s;-ms-animation-iteration-count:infinite;-ms-animation-timing-function:linear;-webkit-animation-name:progress;-webkit-animation-duration:4s;-webkit-animation-iteration-count:infinite;-webkit-animation-timing-function:linear;-moz-animation-name:progress;-moz-animation-duration:4s;-moz-animation-iteration-count:infinite;-moz-animation-timing-function:linear;will-change:transform}.monaco-editor-hover{cursor:default;position:absolute;overflow:hidden;z-index:50;-webkit-user-select:text;-ms-user-select:text;-moz-user-select:text;-o-user-select:text;user-select:text;box-sizing:initial;animation:fadein .1s linear;line-height:1.5em}.monaco-editor-hover.hidden{display:none}.monaco-editor-hover .hover-contents{padding:4px 8px}.monaco-editor-hover .markdown-hover>.hover-contents:not(.code-hover-contents){max-width:500px;word-wrap:break-word}.monaco-editor-hover p,.monaco-editor-hover ul{margin:8px 0}.monaco-editor-hover hr{margin:4px -10px -6px;height:1px}.monaco-editor-hover p:first-child,.monaco-editor-hover ul:first-child{margin-top:0}.monaco-editor-hover p:last-child,.monaco-editor-hover ul:last-child{margin-bottom:0}.monaco-editor-hover ul{padding-left:20px}.monaco-editor-hover li>p{margin-bottom:0}.monaco-editor-hover li>ul{margin-top:0}.monaco-editor-hover code{border-radius:3px;padding:0 .4em}.monaco-editor-hover .monaco-tokenized-source{white-space:pre-wrap;word-break:break-all}.monaco-editor-hover .hover-row.status-bar{font-size:12px;line-height:22px}.monaco-editor-hover .hover-row.status-bar .actions{display:-ms-flexbox;display:flex;padding:0 8px}.monaco-editor-hover .hover-row.status-bar .actions .action-container{margin-right:16px;cursor:pointer}.monaco-editor-hover .hover-row.status-bar .actions .action-container .action .icon{padding-right:4px}.colorpicker-widget{height:190px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.monaco-editor .colorpicker-hover:focus{outline:none}.colorpicker-header{display:-ms-flexbox;display:flex;height:24px;position:relative;background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTZEaa/1AAAAHUlEQVQYV2PYvXu3JAi7uLiAMaYAjAGTQBPYLQkAa/0Zef3qRswAAAAASUVORK5CYII=");background-size:9px 9px;-ms-interpolation-mode:nearest-neighbor;image-rendering:pixelated}.colorpicker-header .picked-color{width:216px;line-height:24px;cursor:pointer;color:#fff;-ms-flex:1;flex:1;text-align:center}.colorpicker-header .picked-color.light{color:#000}.colorpicker-header .original-color{width:74px;z-index:inherit;cursor:pointer}.colorpicker-body{display:-ms-flexbox;display:flex;padding:8px;position:relative}.colorpicker-body .saturation-wrap{overflow:hidden;height:150px;position:relative;min-width:220px;-ms-flex:1;flex:1}.colorpicker-body .saturation-box{height:150px;position:absolute}.colorpicker-body .saturation-selection{width:9px;height:9px;margin:-5px 0 0 -5px;border:1px solid #fff;border-radius:100%;box-shadow:0 0 2px rgba(0,0,0,.8);position:absolute}.colorpicker-body .strip{width:25px;height:150px}.colorpicker-body .hue-strip{position:relative;margin-left:8px;cursor:grab;background:linear-gradient(180deg,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red)}.colorpicker-body .opacity-strip{position:relative;margin-left:8px;cursor:grab;background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTZEaa/1AAAAHUlEQVQYV2PYvXu3JAi7uLiAMaYAjAGTQBPYLQkAa/0Zef3qRswAAAAASUVORK5CYII=");background-size:9px 9px;-ms-interpolation-mode:nearest-neighbor;image-rendering:pixelated}.colorpicker-body .strip.grabbing{cursor:grabbing}.colorpicker-body .slider{position:absolute;top:0;left:-2px;width:calc(100% + 4px);height:4px;box-sizing:border-box;border:1px solid hsla(0,0%,100%,.71);box-shadow:0 0 1px rgba(0,0,0,.85)}.colorpicker-body .strip .overlay{height:150px;pointer-events:none}.monaco-editor .tokens-inspect-widget{z-index:50;-webkit-user-select:text;-ms-user-select:text;-moz-user-select:text;-o-user-select:text;user-select:text;padding:10px}.tokens-inspect-separator{height:1px;border:0}.monaco-editor .tokens-inspect-widget .tm-token{font-family:monospace}.monaco-editor .tokens-inspect-widget .tm-token-length{font-weight:400;font-size:60%;float:right}.monaco-editor .tokens-inspect-widget .tm-metadata-table{width:100%}.monaco-editor .tokens-inspect-widget .tm-metadata-value{font-family:monospace;text-align:right}.monaco-editor .tokens-inspect-widget .tm-token-type{font-family:monospace}.monaco-editor .iPadShowKeyboard{width:58px;min-width:0;height:36px;min-height:0;margin:0;padding:0;position:absolute;resize:none;overflow:hidden;background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTMiIGhlaWdodD0iMzYiIHZpZXdCb3g9IjAgMCA1MyAzNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwKSI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNDguMDM2NCA0LjAxMDQySDQuMDA3NzlMNC4wMDc3OSAzMi4wMjg2SDQ4LjAzNjRWNC4wMTA0MlpNNC4wMDc3OSAwLjAwNzgxMjVDMS43OTcyMSAwLjAwNzgxMjUgMC4wMDUxODc5OSAxLjc5OTg0IDAuMDA1MTg3OTkgNC4wMTA0MlYzMi4wMjg2QzAuMDA1MTg3OTkgMzQuMjM5MiAxLjc5NzIxIDM2LjAzMTIgNC4wMDc3OSAzNi4wMzEySDQ4LjAzNjRDNTAuMjQ3IDM2LjAzMTIgNTIuMDM5IDM0LjIzOTIgNTIuMDM5IDMyLjAyODZWNC4wMTA0MkM1Mi4wMzkgMS43OTk4NCA1MC4yNDcgMC4wMDc4MTI1IDQ4LjAzNjQgMC4wMDc4MTI1SDQuMDA3NzlaTTguMDEwNDIgOC4wMTMwMkgxMi4wMTNWMTIuMDE1Nkg4LjAxMDQyVjguMDEzMDJaTTIwLjAxODIgOC4wMTMwMkgxNi4wMTU2VjEyLjAxNTZIMjAuMDE4MlY4LjAxMzAyWk0yNC4wMjA4IDguMDEzMDJIMjguMDIzNFYxMi4wMTU2SDI0LjAyMDhWOC4wMTMwMlpNMzYuMDI4NiA4LjAxMzAySDMyLjAyNlYxMi4wMTU2SDM2LjAyODZWOC4wMTMwMlpNNDAuMDMxMiA4LjAxMzAySDQ0LjAzMzlWMTIuMDE1Nkg0MC4wMzEyVjguMDEzMDJaTTE2LjAxNTYgMTYuMDE4Mkg4LjAxMDQyVjIwLjAyMDhIMTYuMDE1NlYxNi4wMTgyWk0yMC4wMTgyIDE2LjAxODJIMjQuMDIwOFYyMC4wMjA4SDIwLjAxODJWMTYuMDE4MlpNMzIuMDI2IDE2LjAxODJIMjguMDIzNFYyMC4wMjA4SDMyLjAyNlYxNi4wMTgyWk00NC4wMzM5IDE2LjAxODJWMjAuMDIwOEgzNi4wMjg2VjE2LjAxODJINDQuMDMzOVpNMTIuMDEzIDI0LjAyMzRIOC4wMTA0MlYyOC4wMjZIMTIuMDEzVjI0LjAyMzRaTTE2LjAxNTYgMjQuMDIzNEgzNi4wMjg2VjI4LjAyNkgxNi4wMTU2VjI0LjAyMzRaTTQ0LjAzMzkgMjQuMDIzNEg0MC4wMzEyVjI4LjAyNkg0NC4wMzM5VjI0LjAyMzRaIiBmaWxsPSIjNDI0MjQyIi8+CjwvZz4KPGRlZnM+CjxjbGlwUGF0aCBpZD0iY2xpcDAiPgo8cmVjdCB3aWR0aD0iNTMiIGhlaWdodD0iMzYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==") 50% no-repeat;border:4px solid #f6f6f6;border-radius:4px}.monaco-editor.vs-dark .iPadShowKeyboard{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTMiIGhlaWdodD0iMzYiIHZpZXdCb3g9IjAgMCA1MyAzNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwKSI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNDguMDM2NCA0LjAxMDQySDQuMDA3NzlMNC4wMDc3OSAzMi4wMjg2SDQ4LjAzNjRWNC4wMTA0MlpNNC4wMDc3OSAwLjAwNzgxMjVDMS43OTcyMSAwLjAwNzgxMjUgMC4wMDUxODc5OSAxLjc5OTg0IDAuMDA1MTg3OTkgNC4wMTA0MlYzMi4wMjg2QzAuMDA1MTg3OTkgMzQuMjM5MiAxLjc5NzIxIDM2LjAzMTIgNC4wMDc3OSAzNi4wMzEySDQ4LjAzNjRDNTAuMjQ3IDM2LjAzMTIgNTIuMDM5IDM0LjIzOTIgNTIuMDM5IDMyLjAyODZWNC4wMTA0MkM1Mi4wMzkgMS43OTk4NCA1MC4yNDcgMC4wMDc4MTI1IDQ4LjAzNjQgMC4wMDc4MTI1SDQuMDA3NzlaTTguMDEwNDIgOC4wMTMwMkgxMi4wMTNWMTIuMDE1Nkg4LjAxMDQyVjguMDEzMDJaTTIwLjAxODIgOC4wMTMwMkgxNi4wMTU2VjEyLjAxNTZIMjAuMDE4MlY4LjAxMzAyWk0yNC4wMjA4IDguMDEzMDJIMjguMDIzNFYxMi4wMTU2SDI0LjAyMDhWOC4wMTMwMlpNMzYuMDI4NiA4LjAxMzAySDMyLjAyNlYxMi4wMTU2SDM2LjAyODZWOC4wMTMwMlpNNDAuMDMxMiA4LjAxMzAySDQ0LjAzMzlWMTIuMDE1Nkg0MC4wMzEyVjguMDEzMDJaTTE2LjAxNTYgMTYuMDE4Mkg4LjAxMDQyVjIwLjAyMDhIMTYuMDE1NlYxNi4wMTgyWk0yMC4wMTgyIDE2LjAxODJIMjQuMDIwOFYyMC4wMjA4SDIwLjAxODJWMTYuMDE4MlpNMzIuMDI2IDE2LjAxODJIMjguMDIzNFYyMC4wMjA4SDMyLjAyNlYxNi4wMTgyWk00NC4wMzM5IDE2LjAxODJWMjAuMDIwOEgzNi4wMjg2VjE2LjAxODJINDQuMDMzOVpNMTIuMDEzIDI0LjAyMzRIOC4wMTA0MlYyOC4wMjZIMTIuMDEzVjI0LjAyMzRaTTE2LjAxNTYgMjQuMDIzNEgzNi4wMjg2VjI4LjAyNkgxNi4wMTU2VjI0LjAyMzRaTTQ0LjAzMzkgMjQuMDIzNEg0MC4wMzEyVjI4LjAyNkg0NC4wMzM5VjI0LjAyMzRaIiBmaWxsPSIjQzVDNUM1Ii8+CjwvZz4KPGRlZnM+CjxjbGlwUGF0aCBpZD0iY2xpcDAiPgo8cmVjdCB3aWR0aD0iNTMiIGhlaWdodD0iMzYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==") 50% no-repeat;border:4px solid #252526}.monaco-editor .detected-link,.monaco-editor .detected-link-active{text-decoration:underline;text-underline-position:under}.monaco-editor .detected-link-active{cursor:pointer}.monaco-editor .parameter-hints-widget{z-index:10;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;line-height:1.5em}.monaco-editor .parameter-hints-widget>.wrapper{max-width:440px;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.monaco-editor .parameter-hints-widget.multiple{min-height:3.3em;padding:0 0 0 1.9em}.monaco-editor .parameter-hints-widget.visible{transition:left .05s ease-in-out}.monaco-editor .parameter-hints-widget p,.monaco-editor .parameter-hints-widget ul{margin:8px 0}.monaco-editor .parameter-hints-widget .body,.monaco-editor .parameter-hints-widget .monaco-scrollable-element{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.monaco-editor .parameter-hints-widget .signature{padding:4px 5px}.monaco-editor .parameter-hints-widget .docs{padding:0 10px 0 5px;white-space:pre-wrap}.monaco-editor .parameter-hints-widget .docs.empty{display:none}.monaco-editor .parameter-hints-widget .docs .markdown-docs{white-space:normal}.monaco-editor .parameter-hints-widget .docs .code{white-space:pre-wrap}.monaco-editor .parameter-hints-widget .docs code{border-radius:3px;padding:0 .4em}.monaco-editor .parameter-hints-widget .buttons{position:absolute;display:none;bottom:0;left:0}.monaco-editor .parameter-hints-widget.multiple .buttons{display:block}.monaco-editor .parameter-hints-widget.multiple .button{position:absolute;left:2px;width:16px;height:16px;background-repeat:no-repeat;cursor:pointer}.monaco-editor .parameter-hints-widget .button.previous{bottom:24px;background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHBhdGggZmlsbD0iIzQyNDI0MiIgZD0iTTEwLjggOS41bC45LS45TDguMSA1IDQuMiA4LjZsLjkuOSAzLTIuNyAyLjcgMi43eiIvPjwvc3ZnPg==")}.monaco-editor .parameter-hints-widget .button.next{bottom:0;background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHBhdGggZmlsbD0iIzQyNDI0MiIgZD0iTTUuMSA1bC0uOS45IDMuNiAzLjYgMy45LTMuNi0xLS45LTMgMi43TDUuMSA1eiIvPjwvc3ZnPg==")}.monaco-editor .parameter-hints-widget .overloads{position:absolute;display:none;text-align:center;bottom:14px;left:0;width:22px;height:12px;line-height:12px;opacity:.5}.monaco-editor .parameter-hints-widget.multiple .overloads{display:block}.monaco-editor .parameter-hints-widget .signature .parameter.active{font-weight:700;text-decoration:underline}.monaco-editor .parameter-hints-widget .documentation-parameter>.parameter{font-weight:700;margin-right:.5em}.monaco-editor.hc-black .parameter-hints-widget .button.previous,.monaco-editor.vs-dark .parameter-hints-widget .button.previous{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHBhdGggZmlsbD0iI0U4RThFOCIgZD0iTTEwLjggOS41bC45LS45TDguMSA1IDQuMiA4LjZsLjkuOSAzLTIuNyAyLjcgMi43eiIvPjwvc3ZnPg==")}.monaco-editor.hc-black .parameter-hints-widget .button.next,.monaco-editor.vs-dark .parameter-hints-widget .button.next{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHBhdGggZmlsbD0iI0U4RThFOCIgZD0iTTUuMSA1bC0uOS45IDMuNiAzLjYgMy45LTMuNi0xLS45LTMgMi43TDUuMSA1eiIvPjwvc3ZnPg==")}.monaco-quick-open-widget{font-size:13px}.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon,.vs-dark .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjQwIiB2aWV3Qm94PSIwIDAgMzAwIDQwIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTQgMjYuODU3NUw0LjQ4NTUgMjZMOS40ODU1IDIzSDEwLjUxNDVMMTUuNTE0NSAyNkwxNiAyNi44NTc1VjMyLjg1NzVMMTUuNTE0NSAzMy43MTVMMTAuNTE0NSAzNi43MTVIOS40ODU1TDQuNDg1NSAzMy43MTVMNCAzMi44NTc1VjI2Ljg1NzVaTTkuNSAzNS41NTc1TDUgMzIuODU3NVYyNy42OTk4TDkuNSAzMC4xNTQzVjM1LjU1NzVaTTEwLjUgMzUuNTU3NUwxNSAzMi44NTc1VjI3LjY5OThMMTAuNSAzMC4xNTQzVjM1LjU1NzVaTTEwIDIzLjg1NzVMNS4yNTkxMyAyNi43MDJMMTAgMjkuMjg3OUwxNC43NDA5IDI2LjcwMkwxMCAyMy44NTc1WiIgZmlsbD0iI0IxODBENyIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTQgNi44NTc0OUw0LjQ4NTUgNkw5LjQ4NTUgM0gxMC41MTQ1TDE1LjUxNDUgNkwxNiA2Ljg1NzQ5VjEyLjg1NzVMMTUuNTE0NSAxMy43MTVMMTAuNTE0NSAxNi43MTVIOS40ODU1TDQuNDg1NSAxMy43MTVMNCAxMi44NTc1VjYuODU3NDlaTTkuNSAxNS41NTc1TDUgMTIuODU3NVY3LjY5OTc1TDkuNSAxMC4xNTQzVjE1LjU1NzVaTTEwLjUgMTUuNTU3NUwxNSAxMi44NTc1VjcuNjk5NzVMMTAuNSAxMC4xNTQzVjE1LjU1NzVaTTEwIDMuODU3NDlMNS4yNTkxMyA2LjcwMjAxTDEwIDkuMjg3OTRMMTQuNzQwOSA2LjcwMjAxTDEwIDMuODU3NDlaIiBmaWxsPSIjNjUyRDkwIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMjMgMjguMzk0NEwyMy41NTI4IDI3LjVMMzAuNTUyOCAyNEgzMS40NDcyTDM2LjQ0NzIgMjYuNUwzNyAyNy4zOTQ0VjMxLjg5NDRMMzYuNDQ3MiAzMi43ODg5TDI5LjQ0NzIgMzYuMjg4OUgyOC41NTI4TDIzLjU1MjggMzMuNzg4OUwyMyAzMi44OTQ0VjI4LjM5NDRaTTI4LjUgMzUuMTQ0NEwyNCAzMi44OTQ0VjI5LjE3MDlMMjguNSAzMS4yMTY0VjM1LjE0NDRaTTI5LjUgMzUuMTQ0NEwzNiAzMS44OTQ0VjI4LjE3OTVMMjkuNSAzMS4yMTI5VjM1LjE0NDRaTTMxIDI0Ljg5NDRMMjQuMzM3MyAyOC4yMjU4TDI4Ljk5NzIgMzAuMzQ0TDM1LjY3MDYgMjcuMjI5N0wzMSAyNC44OTQ0WiIgZmlsbD0iIzc1QkVGRiIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTIzIDguMzk0NDNMMjMuNTUyOCA3LjVMMzAuNTUyOCA0SDMxLjQ0NzJMMzYuNDQ3MiA2LjVMMzcgNy4zOTQ0M1YxMS44OTQ0TDM2LjQ0NzIgMTIuNzg4OUwyOS40NDcyIDE2LjI4ODlIMjguNTUyOEwyMy41NTI4IDEzLjc4ODlMMjMgMTIuODk0NFY4LjM5NDQzWk0yOC41IDE1LjE0NDRMMjQgMTIuODk0NFY5LjE3MDk0TDI4LjUgMTEuMjE2NFYxNS4xNDQ0Wk0yOS41IDE1LjE0NDRMMzYgMTEuODk0NFY4LjE3OTU0TDI5LjUgMTEuMjEyOVYxNS4xNDQ0Wk0zMSA0Ljg5NDQzTDI0LjMzNzMgOC4yMjU3OUwyOC45OTcyIDEwLjM0NEwzNS42NzA2IDcuMjI5NzNMMzEgNC44OTQ0M1oiIGZpbGw9IiMwMDdBQ0MiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00NS4zNTM2IDI4LjY0NjRMNDQuMDYwNyAyNy4zNTM1TDQ3LjM1MzYgMjQuMDYwN0w0OC42NDY1IDI1LjM1MzVMNDUuMzUzNiAyOC42NDY0Wk00NyAyM0w0MyAyN1YyNy43MDcxTDQ1IDI5LjcwNzFINDUuNzA3MUw0Ni44NTM2IDI4LjU2MDZWMzQuMzUzNUw0Ny4zNTM2IDM0Ljg1MzVINTIuMDA5N1YzNS4zNzQxTDUzLjM0MyAzNi43MDc0SDU0LjA1MDFMNTYuNzE2OCAzNC4wNDA3VjMzLjMzMzZMNTUuMzgzNSAzMi4wMDAzSDU0LjY3NjNMNTIuODIzMSAzMy44NTM1SDQ3Ljg1MzZWMjkuODkzNUg1Mi4wMDk3VjMwLjM3NEw1My4zNDMgMzEuNzA3M0g1NC4wNTAxTDU2LjcxNjggMjkuMDQwN1YyOC4zMzM2TDU1LjM4MzUgMjcuMDAwMkg1NC42NzYzTDUyLjg2MyAyOC44MTM2SDQ3Ljg1MzZWMjcuNTYwNkw0OS43MDcxIDI1LjcwNzFWMjVMNDcuNzA3MSAyM0g0N1pNNTMuMDcwMyAzMC4wMjA1TDUzLjY5NjYgMzAuNjQ2N0w1NS42NTYxIDI4LjY4NzFMNTUuMDI5OSAyOC4wNjA5TDUzLjA3MDMgMzAuMDIwNVpNNTMuMDcwMyAzNS4wMjA1TDUzLjY5NjYgMzUuNjQ2N0w1NS42NTYxIDMzLjY4NzJMNTUuMDI5OSAzMy4wNjFMNTMuMDcwMyAzNS4wMjA1WiIgZmlsbD0iI0VFOUQyOCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTQ1LjM1MzYgOC42NDY0Mkw0NC4wNjA3IDcuMzUzNTNMNDcuMzUzNiA0LjA2MDY1TDQ4LjY0NjUgNS4zNTM1NEw0NS4zNTM2IDguNjQ2NDJaTTQ3IDNMNDMgNi45OTk5OFY3LjcwNzA4TDQ1IDkuNzA3MDdINDUuNzA3MUw0Ni44NTM2IDguNTYwNjNWMTQuMzUzNUw0Ny4zNTM2IDE0Ljg1MzVINTIuMDA5N1YxNS4zNzQxTDUzLjM0MyAxNi43MDc0SDU0LjA1MDFMNTYuNzE2OCAxNC4wNDA3VjEzLjMzMzZMNTUuMzgzNSAxMi4wMDAzSDU0LjY3NjNMNTIuODIzMSAxMy44NTM1SDQ3Ljg1MzZWOS44OTM1NUg1Mi4wMDk3VjEwLjM3NEw1My4zNDMgMTEuNzA3M0g1NC4wNTAxTDU2LjcxNjggOS4wNDA2OFY4LjMzMzU3TDU1LjM4MzUgNy4wMDAyNEg1NC42NzYzTDUyLjg2MyA4LjgxMzU2SDQ3Ljg1MzZWNy41NjA2NEw0OS43MDcxIDUuNzA3MDlWNC45OTk5OUw0Ny43MDcxIDNINDdaTTUzLjA3MDMgMTAuMDIwNUw1My42OTY2IDEwLjY0NjdMNTUuNjU2MSA4LjY4NzEzTDU1LjAyOTkgOC4wNjA5TDUzLjA3MDMgMTAuMDIwNVpNNTMuMDcwMyAxNS4wMjA1TDUzLjY5NjYgMTUuNjQ2N0w1NS42NTYxIDEzLjY4NzJMNTUuMDI5OSAxMy4wNjFMNTMuMDcwMyAxNS4wMjA1WiIgZmlsbD0iI0Q2N0UwMCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTg4IDI0Ljk4MzZWMjQuOTcxOFYyNEg4Ny45MTA4Qzg3LjU5NzQgMjQgODcuMjk0MSAyNC4wNjE2IDg3LjAwMTMgMjQuMTg0N0M4Ni43MDgyIDI0LjMwOCA4Ni40NDk0IDI0LjQ4NDcgODYuMjI1OCAyNC43MTVDODYuMDAzMSAyNC45NDQyIDg1LjgzNzkgMjUuMTk1IDg1LjczMjggMjUuNDY3N0w4NS43MzIzIDI1LjQ2OUM4NS42MzM4IDI1LjczNTIgODUuNTY4MSAyNi4wMTIgODUuNTM1MyAyNi4yOTkyTDg1LjUzNTIgMjYuMzAwNUM4NS41MDY4IDI2LjU4MDUgODUuNDk4NyAyNi44Njg0IDg1LjUxMDggMjcuMTY0M0M4NS41MjI3IDI3LjQ1MzggODUuNTI4NyAyNy43NDMzIDg1LjUyODcgMjguMDMyOEM4NS41Mjg3IDI4LjIzNTYgODUuNDg5NyAyOC40MjU5IDg1LjQxMiAyOC42MDUxTDg1LjQxMTYgMjguNjA2Qzg1LjMzNjkgMjguNzgzIDg1LjIzNDIgMjguOTM4NyA4NS4xMDMyIDI5LjA3MzZDODQuOTc2NCAyOS4yMDQxIDg0LjgyNDcgMjkuMzEwNiA4NC42NDY3IDI5LjM5MjVDODQuNDcwNiAyOS40Njk1IDg0LjI4NSAyOS41MDgyIDg0LjA4OTIgMjkuNTA4Mkg4NFYyOS42VjMwLjRWMzAuNDkxOEg4NC4wODkyQzg0LjI4NDcgMzAuNDkxOCA4NC40NyAzMC41MzI0IDg0LjY0NiAzMC42MTMzTDg0LjY0NzQgMzAuNjE0Qzg0LjgyNDYgMzAuNjkxNiA4NC45NzU4IDMwLjc5NzYgODUuMTAyMiAzMC45MzE2TDg1LjEwNDEgMzAuOTMzNUM4NS4yMzQzIDMxLjA2MzcgODUuMzM2NiAzMS4yMTg3IDg1LjQxMTMgMzEuMzk5NEw4NS40MTIgMzEuNDAxMUM4NS40ODk5IDMxLjU4MDUgODUuNTI4NyAzMS43Njg4IDg1LjUyODcgMzEuOTY3MkM4NS41Mjg3IDMyLjI1NjcgODUuNTIyNyAzMi41NDYyIDg1LjUxMDggMzIuODM1N0M4NS40OTg3IDMzLjEzMTYgODUuNTA2OCAzMy40MjE1IDg1LjUzNTIgMzMuNzA1NUw4NS41MzU0IDMzLjcwNzJDODUuNTY4MiAzMy45OTAzIDg1LjYzMzkgMzQuMjY1IDg1LjczMjMgMzQuNTMxTDg1LjczMjggMzQuNTMyM0M4NS44Mzc5IDM0LjgwNSA4Ni4wMDMxIDM1LjA1NTggODYuMjI1OCAzNS4yODVDODYuNDQ5NCAzNS41MTUzIDg2LjcwODIgMzUuNjkyIDg3LjAwMTMgMzUuODE1M0M4Ny4yOTQxIDM1LjkzODQgODcuNTk3NCAzNiA4Ny45MTA4IDM2SDg4VjM1LjJWMzUuMDE2NEg4Ny45MTA4Qzg3LjcxMDkgMzUuMDE2NCA4Ny41MjM1IDM0Ljk3NzcgODcuMzQ3NiAzNC45MDA4Qzg3LjE3NCAzNC44MTkxIDg3LjAyMTkgMzQuNzEyNiA4Ni44OTA5IDM0LjU4MThDODYuNzYzOSAzNC40NDY5IDg2LjY2MSAzNC4yOTExIDg2LjU4MjIgMzQuMTEzN0M4Ni41MDg0IDMzLjkzNDYgODYuNDcxMyAzMy43NDQgODYuNDcxMyAzMy41NDFDODYuNDcxMyAzMy4zMTI3IDg2LjQ3NTMgMzMuMDg4NSA4Ni40ODMyIDMyLjg2ODZDODYuNDkxMiAzMi42NDExIDg2LjQ5MTMgMzIuNDE5NSA4Ni40ODMyIDMyLjIwMzlDODYuNDc5MSAzMS45ODI1IDg2LjQ2MDggMzEuNzY4OCA4Ni40MjgyIDMxLjU2MzFDODYuMzk1MSAzMS4zNTAyIDg2LjMzOTIgMzEuMTQ3NiA4Ni4yNjA0IDMwLjk1NTRDODYuMTgwOSAzMC43NjE2IDg2LjA3MjYgMzAuNTc3NSA4NS45MzYyIDMwLjQwM0M4NS44MjM1IDMwLjI1ODggODUuNjg1NCAzMC4xMjQ2IDg1LjUyMjggMzBDODUuNjg1NCAyOS44NzU0IDg1LjgyMzUgMjkuNzQxMiA4NS45MzYyIDI5LjU5N0M4Ni4wNzI2IDI5LjQyMjUgODYuMTgwOSAyOS4yMzg0IDg2LjI2MDQgMjkuMDQ0NkM4Ni4zMzkxIDI4Ljg1MjYgODYuMzk1MSAyOC42NTE3IDg2LjQyODMgMjguNDQyOEM4Ni40NjA4IDI4LjIzMzMgODYuNDc5MSAyOC4wMTk3IDg2LjQ4MzIgMjcuODAyMkM4Ni40OTEzIDI3LjU4MjYgODYuNDkxMyAyNy4zNjExIDg2LjQ4MzIgMjcuMTM3NUM4Ni40NzUzIDI2LjkxMzQgODYuNDcxMyAyNi42ODcyIDg2LjQ3MTMgMjYuNDU5Qzg2LjQ3MTMgMjYuMjYwMiA4Ni41MDgzIDI2LjA3MTUgODYuNTgyNCAyNS44OTIxQzg2LjY2MTQgMjUuNzEwMyA4Ni43NjQyIDI1LjU1NDggODYuODkwOSAyNS40MjQ0Qzg3LjAyMTkgMjUuMjg5NCA4Ny4xNzQ2IDI1LjE4MjcgODcuMzQ4IDI1LjEwNTFDODcuNTIzOCAyNS4wMjQzIDg3LjcxMTEgMjQuOTgzNiA4Ny45MTA4IDI0Ljk4MzZIODhaTTkyIDM1LjAxNjRWMzUuMDI4MlYzNkg5Mi4wODkyQzkyLjQwMjYgMzYgOTIuNzA1OSAzNS45Mzg0IDkyLjk5ODcgMzUuODE1M0M5My4yOTE4IDM1LjY5MiA5My41NTA2IDM1LjUxNTMgOTMuNzc0MiAzNS4yODVDOTMuOTk2OSAzNS4wNTU4IDk0LjE2MjEgMzQuODA1IDk0LjI2NzIgMzQuNTMyM0w5NC4yNjc3IDM0LjUzMUM5NC4zNjYyIDM0LjI2NDggOTQuNDMxOSAzMy45ODggOTQuNDY0NyAzMy43MDA4TDk0LjQ2NDggMzMuNjk5NUM5NC40OTMyIDMzLjQxOTUgOTQuNTAxMyAzMy4xMzE2IDk0LjQ4OTIgMzIuODM1N0M5NC40NzczIDMyLjU0NjIgOTQuNDcxMyAzMi4yNTY3IDk0LjQ3MTMgMzEuOTY3MkM5NC40NzEzIDMxLjc2NDQgOTQuNTEwMyAzMS41NzQxIDk0LjU4OCAzMS4zOTQ5TDk0LjU4ODQgMzEuMzk0Qzk0LjY2MzEgMzEuMjE3IDk0Ljc2NTggMzEuMDYxMyA5NC44OTY4IDMwLjkyNjRDOTUuMDIzNiAzMC43OTU5IDk1LjE3NTMgMzAuNjg5NCA5NS4zNTMzIDMwLjYwNzVDOTUuNTI5NCAzMC41MzA1IDk1LjcxNSAzMC40OTE4IDk1LjkxMDggMzAuNDkxOEg5NlYzMC40VjI5LjZWMjkuNTA4Mkg5NS45MTA4Qzk1LjcxNTMgMjkuNTA4MiA5NS41MyAyOS40Njc2IDk1LjM1NCAyOS4zODY3TDk1LjM1MjYgMjkuMzg2Qzk1LjE3NTQgMjkuMzA4NCA5NS4wMjQyIDI5LjIwMjQgOTQuODk3OCAyOS4wNjg0TDk0Ljg5NTkgMjkuMDY2NUM5NC43NjU3IDI4LjkzNjMgOTQuNjYzNCAyOC43ODEzIDk0LjU4ODcgMjguNjAwNkw5NC41ODggMjguNTk4OUM5NC41MTAxIDI4LjQxOTUgOTQuNDcxMyAyOC4yMzEyIDk0LjQ3MTMgMjguMDMyOEM5NC40NzEzIDI3Ljc0MzMgOTQuNDc3MyAyNy40NTM4IDk0LjQ4OTIgMjcuMTY0M0M5NC41MDEzIDI2Ljg2ODQgOTQuNDkzMiAyNi41Nzg1IDk0LjQ2NDggMjYuMjk0NUw5NC40NjQ2IDI2LjI5MjhDOTQuNDMxOCAyNi4wMDk3IDk0LjM2NjEgMjUuNzM1IDk0LjI2NzcgMjUuNDY5TDk0LjI2NzIgMjUuNDY3N0M5NC4xNjIxIDI1LjE5NSA5My45OTY5IDI0Ljk0NDIgOTMuNzc0MiAyNC43MTVDOTMuNTUwNiAyNC40ODQ3IDkzLjI5MTggMjQuMzA4IDkyLjk5ODcgMjQuMTg0N0M5Mi43MDU5IDI0LjA2MTYgOTIuNDAyNiAyNCA5Mi4wODkyIDI0SDkyVjI0LjhWMjQuOTgzNkg5Mi4wODkyQzkyLjI4OTEgMjQuOTgzNiA5Mi40NzY1IDI1LjAyMjMgOTIuNjUyNCAyNS4wOTkyQzkyLjgyNiAyNS4xODA5IDkyLjk3ODEgMjUuMjg3NCA5My4xMDkxIDI1LjQxODJDOTMuMjM2MSAyNS41NTMxIDkzLjMzOSAyNS43MDg5IDkzLjQxNzggMjUuODg2M0M5My40OTE2IDI2LjA2NTQgOTMuNTI4NyAyNi4yNTYgOTMuNTI4NyAyNi40NTlDOTMuNTI4NyAyNi42ODczIDkzLjUyNDcgMjYuOTExNSA5My41MTY4IDI3LjEzMTRDOTMuNTA4OCAyNy4zNTg5IDkzLjUwODcgMjcuNTgwNSA5My41MTY4IDI3Ljc5NjFDOTMuNTIwOSAyOC4wMTc1IDkzLjUzOTIgMjguMjMxMiA5My41NzE4IDI4LjQzNjlDOTMuNjA0OSAyOC42NDk4IDkzLjY2MDggMjguODUyNCA5My43Mzk2IDI5LjA0NDZDOTMuODE5MSAyOS4yMzg0IDkzLjkyNzQgMjkuNDIyNSA5NC4wNjM4IDI5LjU5N0M5NC4xNzY1IDI5Ljc0MTIgOTQuMzE0NiAyOS44NzU0IDk0LjQ3NzIgMzBDOTQuMzE0NiAzMC4xMjQ2IDk0LjE3NjUgMzAuMjU4OCA5NC4wNjM4IDMwLjQwM0M5My45Mjc0IDMwLjU3NzUgOTMuODE5MSAzMC43NjE2IDkzLjczOTYgMzAuOTU1NEM5My42NjA5IDMxLjE0NzQgOTMuNjA0OSAzMS4zNDgzIDkzLjU3MTcgMzEuNTU3MkM5My41MzkyIDMxLjc2NjcgOTMuNTIwOSAzMS45ODAzIDkzLjUxNjggMzIuMTk3OEM5My41MDg3IDMyLjQxNzQgOTMuNTA4NyAzMi42Mzg5IDkzLjUxNjggMzIuODYyNUM5My41MjQ3IDMzLjA4NjYgOTMuNTI4NyAzMy4zMTI4IDkzLjUyODcgMzMuNTQxQzkzLjUyODcgMzMuNzM5OCA5My40OTE3IDMzLjkyODUgOTMuNDE3NiAzNC4xMDc5QzkzLjMzODYgMzQuMjg5NyA5My4yMzU4IDM0LjQ0NTIgOTMuMTA5MSAzNC41NzU2QzkyLjk3ODEgMzQuNzEwNiA5Mi44MjU0IDM0LjgxNzMgOTIuNjUyIDM0Ljg5NDlDOTIuNDc2MiAzNC45NzU3IDkyLjI4ODkgMzUuMDE2NCA5Mi4wODkyIDM1LjAxNjRIOTJaIiBmaWxsPSIjQzVDNUM1Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNODggNC45ODM2MVY0Ljk3MTg0VjRIODcuOTEwOEM4Ny41OTc0IDQgODcuMjk0MSA0LjA2MTYxIDg3LjAwMTMgNC4xODQ3M0M4Ni43MDgyIDQuMzA3OTggODYuNDQ5NCA0LjQ4NDc0IDg2LjIyNTggNC43MTQ5OEM4Ni4wMDMxIDQuOTQ0MjIgODUuODM3OSA1LjE5NDk4IDg1LjczMjggNS40Njc2Nkw4NS43MzIzIDUuNDY4OThDODUuNjMzOCA1LjczNTIgODUuNTY4MSA2LjAxMjAxIDg1LjUzNTMgNi4yOTkxN0w4NS41MzUyIDYuMzAwNTNDODUuNTA2OCA2LjU4MDUgODUuNDk4NyA2Ljg2ODQ0IDg1LjUxMDggNy4xNjQyOEM4NS41MjI3IDcuNDUzNzkgODUuNTI4NyA3Ljc0MzI5IDg1LjUyODcgOC4wMzI3OUM4NS41Mjg3IDguMjM1NTYgODUuNDg5NyA4LjQyNTk0IDg1LjQxMiA4LjYwNTA3TDg1LjQxMTYgOC42MDYwMUM4NS4zMzY5IDguNzgyOTYgODUuMjM0MiA4LjkzODY2IDg1LjEwMzIgOS4wNzM1OUM4NC45NzY0IDkuMjA0MDUgODQuODI0NyA5LjMxMDU1IDg0LjY0NjcgOS4zOTI1Qzg0LjQ3MDYgOS40Njk1NCA4NC4yODUgOS41MDgyIDg0LjA4OTIgOS41MDgySDg0VjkuNlYxMC40VjEwLjQ5MThIODQuMDg5MkM4NC4yODQ3IDEwLjQ5MTggODQuNDcgMTAuNTMyNCA4NC42NDYgMTAuNjEzM0w4NC42NDc0IDEwLjYxNEM4NC44MjQ2IDEwLjY5MTYgODQuOTc1OCAxMC43OTc2IDg1LjEwMjIgMTAuOTMxNkw4NS4xMDQxIDEwLjkzMzVDODUuMjM0MyAxMS4wNjM3IDg1LjMzNjYgMTEuMjE4NyA4NS40MTEzIDExLjM5OTRMODUuNDEyIDExLjQwMTFDODUuNDg5OSAxMS41ODA1IDg1LjUyODcgMTEuNzY4OCA4NS41Mjg3IDExLjk2NzJDODUuNTI4NyAxMi4yNTY3IDg1LjUyMjcgMTIuNTQ2MiA4NS41MTA4IDEyLjgzNTdDODUuNDk4NyAxMy4xMzE2IDg1LjUwNjggMTMuNDIxNSA4NS41MzUyIDEzLjcwNTVMODUuNTM1NCAxMy43MDcyQzg1LjU2ODIgMTMuOTkwMyA4NS42MzM5IDE0LjI2NSA4NS43MzIzIDE0LjUzMUw4NS43MzI4IDE0LjUzMjNDODUuODM3OSAxNC44MDUgODYuMDAzMSAxNS4wNTU4IDg2LjIyNTggMTUuMjg1Qzg2LjQ0OTQgMTUuNTE1MyA4Ni43MDgyIDE1LjY5MiA4Ny4wMDEzIDE1LjgxNTNDODcuMjk0MSAxNS45Mzg0IDg3LjU5NzQgMTYgODcuOTEwOCAxNkg4OFYxNS4yVjE1LjAxNjRIODcuOTEwOEM4Ny43MTA5IDE1LjAxNjQgODcuNTIzNSAxNC45Nzc3IDg3LjM0NzYgMTQuOTAwOEM4Ny4xNzQgMTQuODE5MSA4Ny4wMjE5IDE0LjcxMjYgODYuODkwOSAxNC41ODE4Qzg2Ljc2MzkgMTQuNDQ2OSA4Ni42NjEgMTQuMjkxMSA4Ni41ODIyIDE0LjExMzdDODYuNTA4NCAxMy45MzQ2IDg2LjQ3MTMgMTMuNzQ0IDg2LjQ3MTMgMTMuNTQxQzg2LjQ3MTMgMTMuMzEyNyA4Ni40NzUzIDEzLjA4ODUgODYuNDgzMiAxMi44Njg2Qzg2LjQ5MTIgMTIuNjQxMSA4Ni40OTEzIDEyLjQxOTUgODYuNDgzMiAxMi4yMDM5Qzg2LjQ3OTEgMTEuOTgyNSA4Ni40NjA4IDExLjc2ODggODYuNDI4MiAxMS41NjMxQzg2LjM5NTEgMTEuMzUwMiA4Ni4zMzkyIDExLjE0NzYgODYuMjYwNCAxMC45NTU0Qzg2LjE4MDkgMTAuNzYxNiA4Ni4wNzI2IDEwLjU3NzUgODUuOTM2MiAxMC40MDNDODUuODIzNSAxMC4yNTg4IDg1LjY4NTQgMTAuMTI0NiA4NS41MjI4IDEwQzg1LjY4NTQgOS44NzUzOCA4NS44MjM1IDkuNzQxMTkgODUuOTM2MiA5LjU5NzAyQzg2LjA3MjYgOS40MjI1NCA4Ni4xODA5IDkuMjM4NDMgODYuMjYwNCA5LjA0NDY0Qzg2LjMzOTEgOC44NTI2MyA4Ni4zOTUxIDguNjUxNzUgODYuNDI4MyA4LjQ0Mjg1Qzg2LjQ2MDggOC4yMzMzIDg2LjQ3OTEgOC4wMTk3MyA4Ni40ODMyIDcuODAyMTlDODYuNDkxMyA3LjU4MjYyIDg2LjQ5MTMgNy4zNjEwNSA4Ni40ODMyIDcuMTM3NDlDODYuNDc1MyA2LjkxMzQgODYuNDcxMyA2LjY4NzI1IDg2LjQ3MTMgNi40NTkwMkM4Ni40NzEzIDYuMjYwMTkgODYuNTA4MyA2LjA3MTUyIDg2LjU4MjQgNS44OTIwNUM4Ni42NjE0IDUuNzEwMzQgODYuNzY0MiA1LjU1NDc1IDg2Ljg5MDkgNS40MjQzN0M4Ny4wMjE5IDUuMjg5NDIgODcuMTc0NiA1LjE4Mjc1IDg3LjM0OCA1LjEwNTEzQzg3LjUyMzggNS4wMjQyNyA4Ny43MTExIDQuOTgzNjEgODcuOTEwOCA0Ljk4MzYxSDg4Wk05MiAxNS4wMTY0VjE1LjAyODJWMTZIOTIuMDg5MkM5Mi40MDI2IDE2IDkyLjcwNTkgMTUuOTM4NCA5Mi45OTg3IDE1LjgxNTNDOTMuMjkxOCAxNS42OTIgOTMuNTUwNiAxNS41MTUzIDkzLjc3NDIgMTUuMjg1QzkzLjk5NjkgMTUuMDU1OCA5NC4xNjIxIDE0LjgwNSA5NC4yNjcyIDE0LjUzMjNMOTQuMjY3NyAxNC41MzFDOTQuMzY2MiAxNC4yNjQ4IDk0LjQzMTkgMTMuOTg4IDk0LjQ2NDcgMTMuNzAwOEw5NC40NjQ4IDEzLjY5OTVDOTQuNDkzMiAxMy40MTk1IDk0LjUwMTMgMTMuMTMxNiA5NC40ODkyIDEyLjgzNTdDOTQuNDc3MyAxMi41NDYyIDk0LjQ3MTMgMTIuMjU2NyA5NC40NzEzIDExLjk2NzJDOTQuNDcxMyAxMS43NjQ0IDk0LjUxMDMgMTEuNTc0MSA5NC41ODggMTEuMzk0OUw5NC41ODg0IDExLjM5NEM5NC42NjMxIDExLjIxNyA5NC43NjU4IDExLjA2MTMgOTQuODk2OCAxMC45MjY0Qzk1LjAyMzYgMTAuNzk1OSA5NS4xNzUzIDEwLjY4OTQgOTUuMzUzMyAxMC42MDc1Qzk1LjUyOTQgMTAuNTMwNSA5NS43MTUgMTAuNDkxOCA5NS45MTA4IDEwLjQ5MThIOTZWMTAuNFY5LjZWOS41MDgySDk1LjkxMDhDOTUuNzE1MyA5LjUwODIgOTUuNTMgOS40Njc2MiA5NS4zNTQgOS4zODY2Nkw5NS4zNTI2IDkuMzg2MDRDOTUuMTc1NCA5LjMwODQ0IDk1LjAyNDIgOS4yMDIzOCA5NC44OTc4IDkuMDY4MzlMOTQuODk1OSA5LjA2NjQ4Qzk0Ljc2NTcgOC45MzYzIDk0LjY2MzQgOC43ODEyOSA5NC41ODg3IDguNjAwNThMOTQuNTg4IDguNTk4OTJDOTQuNTEwMSA4LjQxOTUzIDk0LjQ3MTMgOC4yMzExNyA5NC40NzEzIDguMDMyNzlDOTQuNDcxMyA3Ljc0MzI5IDk0LjQ3NzMgNy40NTM3OSA5NC40ODkyIDcuMTY0MjhDOTQuNTAxMyA2Ljg2ODQyIDk0LjQ5MzIgNi41Nzg0OCA5NC40NjQ4IDYuMjk0NTRMOTQuNDY0NiA2LjI5Mjg1Qzk0LjQzMTggNi4wMDk3MSA5NC4zNjYxIDUuNzM1MDIgOTQuMjY3NyA1LjQ2ODk3TDk0LjI2NzIgNS40Njc2NkM5NC4xNjIxIDUuMTk0OTkgOTMuOTk2OSA0Ljk0NDIyIDkzLjc3NDIgNC43MTQ5OEM5My41NTA2IDQuNDg0NzQgOTMuMjkxOCA0LjMwNzk4IDkyLjk5ODcgNC4xODQ3M0M5Mi43MDU5IDQuMDYxNjEgOTIuNDAyNiA0IDkyLjA4OTIgNEg5MlY0LjhWNC45ODM2MUg5Mi4wODkyQzkyLjI4OTEgNC45ODM2MSA5Mi40NzY1IDUuMDIyMyA5Mi42NTI0IDUuMDk5MTdDOTIuODI2IDUuMTgwOTIgOTIuOTc4MSA1LjI4NzM2IDkzLjEwOTEgNS40MTgyM0M5My4yMzYxIDUuNTUzMDUgOTMuMzM5IDUuNzA4ODkgOTMuNDE3OCA1Ljg4NjI4QzkzLjQ5MTYgNi4wNjU0IDkzLjUyODcgNi4yNTU5NiA5My41Mjg3IDYuNDU5MDJDOTMuNTI4NyA2LjY4NzI3IDkzLjUyNDcgNi45MTE0NSA5My41MTY4IDcuMTMxNDJDOTMuNTA4OCA3LjM1ODk0IDkzLjUwODcgNy41ODA0OSA5My41MTY4IDcuNzk2MDVDOTMuNTIwOSA4LjAxNzU0IDkzLjUzOTIgOC4yMzExNyA5My41NzE4IDguNDM2ODhDOTMuNjA0OSA4LjY0OTc2IDkzLjY2MDggOC44NTI0MyA5My43Mzk2IDkuMDQ0NjRDOTMuODE5MSA5LjIzODQzIDkzLjkyNzQgOS40MjI1NCA5NC4wNjM4IDkuNTk3MDJDOTQuMTc2NSA5Ljc0MTE5IDk0LjMxNDYgOS44NzUzOCA5NC40NzcyIDEwQzk0LjMxNDYgMTAuMTI0NiA5NC4xNzY1IDEwLjI1ODggOTQuMDYzOCAxMC40MDNDOTMuOTI3NCAxMC41Nzc1IDkzLjgxOTEgMTAuNzYxNiA5My43Mzk2IDEwLjk1NTRDOTMuNjYwOSAxMS4xNDc0IDkzLjYwNDkgMTEuMzQ4MyA5My41NzE3IDExLjU1NzJDOTMuNTM5MiAxMS43NjY3IDkzLjUyMDkgMTEuOTgwMyA5My41MTY4IDEyLjE5NzhDOTMuNTA4NyAxMi40MTc0IDkzLjUwODcgMTIuNjM4OSA5My41MTY4IDEyLjg2MjVDOTMuNTI0NyAxMy4wODY2IDkzLjUyODcgMTMuMzEyOCA5My41Mjg3IDEzLjU0MUM5My41Mjg3IDEzLjczOTggOTMuNDkxNyAxMy45Mjg1IDkzLjQxNzYgMTQuMTA3OUM5My4zMzg2IDE0LjI4OTcgOTMuMjM1OCAxNC40NDUyIDkzLjEwOTEgMTQuNTc1NkM5Mi45NzgxIDE0LjcxMDYgOTIuODI1NCAxNC44MTczIDkyLjY1MiAxNC44OTQ5QzkyLjQ3NjIgMTQuOTc1NyA5Mi4yODg5IDE1LjAxNjQgOTIuMDg5MiAxNS4wMTY0SDkyWiIgZmlsbD0iIzQyNDI0MiIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTczLjUgMjdDNzIuMTE5MyAyNyA3MSAyOC4xMTkzIDcxIDI5LjVDNzEgMzAuODgwNyA3Mi4xMTkzIDMyIDczLjUgMzJDNzQuODgwNyAzMiA3NiAzMC44ODA3IDc2IDI5LjVDNzYgMjguMTE5MyA3NC44ODA3IDI3IDczLjUgMjdaTTcwLjAzNTQgMzBDNzAuMjc4MSAzMS42OTYxIDcxLjczNjggMzMgNzMuNSAzM0M3NS40MzMgMzMgNzcgMzEuNDMzIDc3IDI5LjVDNzcgMjcuNTY3IDc1LjQzMyAyNiA3My41IDI2QzcxLjczNjggMjYgNzAuMjc4MSAyNy4zMDM5IDcwLjAzNTQgMjlINjYuOTM3QzY2LjcxNSAyOC4xMzc0IDY1LjkzMTkgMjcuNSA2NSAyNy41QzYzLjg5NTQgMjcuNSA2MyAyOC4zOTU0IDYzIDI5LjVDNjMgMzAuNjA0NiA2My44OTU0IDMxLjUgNjUgMzEuNUM2NS45MzE5IDMxLjUgNjYuNzE1IDMwLjg2MjYgNjYuOTM3IDMwSDcwLjAzNTRaIiBmaWxsPSIjNzVCRUZGIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNzMuNSA3QzcyLjExOTMgNyA3MSA4LjExOTI5IDcxIDkuNUM3MSAxMC44ODA3IDcyLjExOTMgMTIgNzMuNSAxMkM3NC44ODA3IDEyIDc2IDEwLjg4MDcgNzYgOS41Qzc2IDguMTE5MjkgNzQuODgwNyA3IDczLjUgN1pNNzAuMDM1NCAxMEM3MC4yNzgxIDExLjY5NjEgNzEuNzM2OCAxMyA3My41IDEzQzc1LjQzMyAxMyA3NyAxMS40MzMgNzcgOS41Qzc3IDcuNTY3IDc1LjQzMyA2IDczLjUgNkM3MS43MzY4IDYgNzAuMjc4MSA3LjMwMzg1IDcwLjAzNTQgOUg2Ni45MzdDNjYuNzE1IDguMTM3MzkgNjUuOTMxOSA3LjUgNjUgNy41QzYzLjg5NTQgNy41IDYzIDguMzk1NDMgNjMgOS41QzYzIDEwLjYwNDYgNjMuODk1NCAxMS41IDY1IDExLjVDNjUuOTMxOSAxMS41IDY2LjcxNSAxMC44NjI2IDY2LjkzNyAxMEg3MC4wMzU0WiIgZmlsbD0iIzAwN0FDQyIvPgo8cGF0aCBkPSJNMTA0LjgwNyAzNi45NzU0QzEwNC41NzEgMzYuOTcyMSAxMDQuMzM4IDM2LjkyMTEgMTA0LjEyMiAzNi44MjU0QzEwMy45MDcgMzYuNzI5NyAxMDMuNzEyIDM2LjU5MTMgMTAzLjU1MiAzNi40MTg2QzEwMy4yMzkgMzYuMTMzNCAxMDMuMDQ0IDM1Ljc0MDggMTAzLjAwOCAzNS4zMTg5QzEwMi45NjYgMzQuODgyOCAxMDMuMDkzIDM0LjQ0NzMgMTAzLjM2MSAzNC4xMDEzQzEwNC41NjggMzIuODI4OSAxMDYuOTQ3IDMwLjQ0OTQgMTA4LjY3OCAyOC43NTQ4QzEwOC4zMSAyNy43NTg5IDEwOC4zMjcgMjYuNjYxMyAxMDguNzI2IDI1LjY3NzRDMTA5LjA1NSAyNC44NTg4IDEwOS42MzkgMjQuMTY4MSAxMTAuMzkxIDIzLjcwODFDMTEwLjk4MiAyMy4zMTcxIDExMS42NiAyMy4wNzk0IDExMi4zNjYgMjMuMDE2N0MxMTMuMDcxIDIyLjk1NCAxMTMuNzgxIDIzLjA2ODIgMTE0LjQzMSAyMy4zNDg5TDExNS4wNDggMjMuNjE2MkwxMTIuMTgyIDI2LjU2NzRMMTEzLjQzNyAyNy44MjU4TDExNi4zODEgMjQuOTQ4OUwxMTYuNjQ4IDI1LjU2NzlDMTE2Ljg3NCAyNi4wODk4IDExNi45OTMgMjYuNjUxMiAxMTcgMjcuMjE5NkMxMTcuMDA2IDI3Ljc4OCAxMTYuOSAyOC4zNTIxIDExNi42ODcgMjguODc5MUMxMTYuNDc2IDI5LjQwMDMgMTE2LjE2MiAyOS44NzM3IDExNS43NjUgMzAuMjcxMkMxMTUuNTM5IDMwLjQ5MTcgMTE1LjI5IDMwLjY4NjUgMTE1LjAyMiAzMC44NTIyQzExNC40NjcgMzEuMjIyOCAxMTMuODMyIDMxLjQ1NjQgMTEzLjE3IDMxLjUzMzhDMTEyLjUwNyAzMS42MTEyIDExMS44MzUgMzEuNTMwMyAxMTEuMjEgMzEuMjk3NkMxMTAuMTEyIDMyLjQxMTMgMTA3LjM3MSAzNS4xNzA0IDEwNS44OTEgMzYuNTUyMkMxMDUuNTk0IDM2LjgyMTkgMTA1LjIwOCAzNi45NzI2IDEwNC44MDcgMzYuOTc1NFpNMTEyLjc0NSAyMy45MjhDMTEyLjA4NyAyMy45MjY0IDExMS40NDQgMjQuMTIwMiAxMTAuODk2IDI0LjQ4NDhDMTEwLjY4MyAyNC42MTUyIDExMC40ODQgMjQuNzY5IDExMC4zMDUgMjQuOTQzM0MxMDkuODI4IDI1LjQyNDIgMTA5LjUwOSAyNi4wMzk1IDEwOS4zOTIgMjYuNzA2N0MxMDkuMjc0IDI3LjM3MzkgMTA5LjM2NCAyOC4wNjEgMTA5LjY0OCAyOC42NzU5TDEwOS43ODMgMjguOTcyOUwxMDkuNTUgMjkuMjAwM0MxMDcuODEyIDMwLjg5NjcgMTA1LjI4MSAzMy40MjAxIDEwNC4wNjUgMzQuNzA0NUMxMDMuOTU2IDM0Ljg2NTggMTAzLjkxIDM1LjA2MDggMTAzLjkzNCAzNS4yNTM1QzEwMy45NTkgMzUuNDQ2MyAxMDQuMDUyIDM1LjYyMzggMTA0LjE5NyAzNS43NTMyQzEwNC4yOCAzNS44NDYyIDEwNC4zODIgMzUuOTIxMSAxMDQuNDk1IDM1Ljk3MzFDMTA0LjU5NiAzNi4wMTg0IDEwNC43MDQgMzYuMDQzIDEwNC44MTQgMzYuMDQ1NUMxMDQuOTgxIDM2LjA0MTMgMTA1LjE0IDM1Ljk3NyAxMDUuMjY0IDM1Ljg2NDZDMTA2LjgzNyAzNC4zOTY0IDEwOS44NzYgMzEuMzI2NCAxMTAuNzY4IDMwLjQyNDNMMTEwLjk5NyAzMC4xOTMzTDExMS4yOTIgMzAuMzI3OEMxMTEuODA2IDMwLjU2NzMgMTEyLjM3MyAzMC42Njk4IDExMi45MzggMzAuNjI1NUMxMTMuNTAzIDMwLjU4MTEgMTE0LjA0NyAzMC4zOTEzIDExNC41MTcgMzAuMDc0NUMxMTQuNzMxIDI5Ljk0MjYgMTE0LjkzIDI5Ljc4NjkgMTE1LjEwOSAyOS42MTA1QzExNS40MTggMjkuMzAxNSAxMTUuNjYzIDI4LjkzMzcgMTE1LjgyOSAyOC41Mjg3QzExNS45OTQgMjguMTIzNyAxMTYuMDc3IDI3LjY4OTcgMTE2LjA3MiAyNy4yNTIzQzExNi4wNzIgMjcuMDM2NiAxMTYuMDUgMjYuODIxNSAxMTYuMDA4IDI2LjYxMDFMMTEzLjQzMSAyOS4xMjUxTDExMC44NzkgMjYuNTc3NkwxMTMuMzk0IDIzLjk4ODNDMTEzLjE4IDIzLjk0NjcgMTEyLjk2MyAyMy45MjY1IDExMi43NDUgMjMuOTI4WiIgZmlsbD0iI0M1QzVDNSIvPgo8cGF0aCBkPSJNMTA0LjgwNyAxNi45NzU0QzEwNC41NzEgMTYuOTcyMSAxMDQuMzM4IDE2LjkyMTEgMTA0LjEyMiAxNi44MjU0QzEwMy45MDcgMTYuNzI5NyAxMDMuNzEyIDE2LjU5MTMgMTAzLjU1MiAxNi40MTg2QzEwMy4yMzkgMTYuMTMzNCAxMDMuMDQ0IDE1Ljc0MDggMTAzLjAwOCAxNS4zMTg5QzEwMi45NjYgMTQuODgyOCAxMDMuMDkzIDE0LjQ0NzMgMTAzLjM2MSAxNC4xMDEzQzEwNC41NjggMTIuODI4OSAxMDYuOTQ3IDEwLjQ0OTQgMTA4LjY3OCA4Ljc1NDc5QzEwOC4zMSA3Ljc1ODg3IDEwOC4zMjcgNi42NjEyNyAxMDguNzI2IDUuNjc3MzlDMTA5LjA1NSA0Ljg1ODc2IDEwOS42MzkgNC4xNjgwNSAxMTAuMzkxIDMuNzA4MDdDMTEwLjk4MiAzLjMxNzA2IDExMS42NiAzLjA3OTQ0IDExMi4zNjYgMy4wMTY3M0MxMTMuMDcxIDIuOTU0MDIgMTEzLjc4MSAzLjA2ODE5IDExNC40MzEgMy4zNDg5MkwxMTUuMDQ4IDMuNjE2MkwxMTIuMTgyIDYuNTY3MzhMMTEzLjQzNyA3LjgyNTgyTDExNi4zODEgNC45NDg4N0wxMTYuNjQ4IDUuNTY3ODhDMTE2Ljg3NCA2LjA4OTc2IDExNi45OTMgNi42NTExOSAxMTcgNy4yMTk2MUMxMTcuMDA2IDcuNzg4MDIgMTE2LjkgOC4zNTIxMSAxMTYuNjg3IDguODc5MTVDMTE2LjQ3NiA5LjQwMDI5IDExNi4xNjIgOS44NzM2OCAxMTUuNzY1IDEwLjI3MTJDMTE1LjUzOSAxMC40OTE3IDExNS4yOSAxMC42ODY1IDExNS4wMjIgMTAuODUyMkMxMTQuNDY3IDExLjIyMjggMTEzLjgzMiAxMS40NTY0IDExMy4xNyAxMS41MzM4QzExMi41MDcgMTEuNjExMiAxMTEuODM1IDExLjUzMDMgMTExLjIxIDExLjI5NzZDMTEwLjExMiAxMi40MTEzIDEwNy4zNzEgMTUuMTcwNCAxMDUuODkxIDE2LjU1MjJDMTA1LjU5NCAxNi44MjE5IDEwNS4yMDggMTYuOTcyNiAxMDQuODA3IDE2Ljk3NTRaTTExMi43NDUgMy45MjgwMkMxMTIuMDg3IDMuOTI2MzcgMTExLjQ0NCA0LjEyMDE4IDExMC44OTYgNC40ODQ4NUMxMTAuNjgzIDQuNjE1MiAxMTAuNDg0IDQuNzY4OTcgMTEwLjMwNSA0Ljk0MzNDMTA5LjgyOCA1LjQyNDIzIDEwOS41MDkgNi4wMzk1MyAxMDkuMzkyIDYuNzA2NjlDMTA5LjI3NCA3LjM3Mzg1IDEwOS4zNjQgOC4wNjA5OCAxMDkuNjQ4IDguNjc1OTFMMTA5Ljc4MyA4Ljk3Mjg4TDEwOS41NSA5LjIwMDI1QzEwNy44MTIgMTAuODk2NyAxMDUuMjgxIDEzLjQyMDEgMTA0LjA2NSAxNC43MDQ1QzEwMy45NTYgMTQuODY1OCAxMDMuOTEgMTUuMDYwOCAxMDMuOTM0IDE1LjI1MzVDMTAzLjk1OSAxNS40NDYzIDEwNC4wNTIgMTUuNjIzOCAxMDQuMTk3IDE1Ljc1MzJDMTA0LjI4IDE1Ljg0NjIgMTA0LjM4MiAxNS45MjExIDEwNC40OTUgMTUuOTczMUMxMDQuNTk2IDE2LjAxODQgMTA0LjcwNCAxNi4wNDMgMTA0LjgxNCAxNi4wNDU1QzEwNC45ODEgMTYuMDQxMyAxMDUuMTQgMTUuOTc3IDEwNS4yNjQgMTUuODY0NkMxMDYuODM3IDE0LjM5NjQgMTA5Ljg3NiAxMS4zMjY0IDExMC43NjggMTAuNDI0M0wxMTAuOTk3IDEwLjE5MzNMMTExLjI5MiAxMC4zMjc4QzExMS44MDYgMTAuNTY3MyAxMTIuMzczIDEwLjY2OTggMTEyLjkzOCAxMC42MjU1QzExMy41MDMgMTAuNTgxMSAxMTQuMDQ3IDEwLjM5MTMgMTE0LjUxNyAxMC4wNzQ1QzExNC43MzEgOS45NDI2IDExNC45MyA5Ljc4Njk0IDExNS4xMDkgOS42MTA0NUMxMTUuNDE4IDkuMzAxNTMgMTE1LjY2MyA4LjkzMzc0IDExNS44MjkgOC41Mjg3NEMxMTUuOTk0IDguMTIzNzUgMTE2LjA3NyA3LjY4OTc0IDExNi4wNzIgNy4yNTIyOEMxMTYuMDcyIDcuMDM2NjIgMTE2LjA1IDYuODIxNDggMTE2LjAwOCA2LjYxMDA3TDExMy40MzEgOS4xMjUwOEwxMTAuODc5IDYuNTc3NTlMMTEzLjM5NCAzLjk4ODM0QzExMy4xOCAzLjk0Njc0IDExMi45NjMgMy45MjY1MyAxMTIuNzQ1IDMuOTI4MDJaIiBmaWxsPSIjNDI0MjQyIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTQ5IDI1TDE1MCAyNEgxNTZMMTU3IDI1VjMwTDE1NiAzMUgxNTJWMzBIMTU2VjI1SDE1MFYyOEgxNDlWMjVaTTE1MCAyOUwxNTEgMzBWMzFWMzVMMTUwIDM2SDE0NEwxNDMgMzVWMzBMMTQ0IDI5SDE0OUgxNTBaTTE1MCAzMFYzMVYzNUgxNDRWMzBIMTQ5SDE1MFpNMTUxLjQxNCAyOUwxNTEgMjguNTg1OFYyOEgxNTVWMjlIMTUxLjQxNFpNMTUxIDI2SDE1NVYyN0gxNTFWMjZaTTE0OSAzMkgxNDVWMzNIMTQ5VjMyWiIgZmlsbD0iIzc1QkVGRiIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE0OSA1TDE1MCA0SDE1NkwxNTcgNVYxMEwxNTYgMTFIMTUyVjEwSDE1NlY1SDE1MFY4SDE0OVY1Wk0xNTAgOUwxNTEgMTBWMTFWMTVMMTUwIDE2SDE0NEwxNDMgMTVWMTBMMTQ0IDlIMTQ5SDE1MFpNMTUwIDEwVjExVjE1SDE0NFYxMEgxNDlIMTUwWk0xNTEuNDE0IDlMMTUxIDguNTg1NzlWOEgxNTVWOUgxNTEuNDE0Wk0xNTEgNkgxNTVWN0gxNTFWNlpNMTQ5IDEySDE0NVYxM0gxNDlWMTJaIiBmaWxsPSIjMDA3QUNDIi8+CjxwYXRoIGQ9Ik0xNzcgNkgxNzJWNUgxNzdWNlpNMTc2IDlIMTc0VjEwSDE3NlY5Wk0xNzIgOUgxNjNWMTBIMTcyVjlaTTE3NCAxNUgxNjNWMTZIMTc0VjE1Wk0xNjkgMTJIMTYzVjEzSDE2OVYxMlpNMTc3IDEySDE3MlYxM0gxNzdWMTJaTTE3MCA0VjdIMTYzVjRIMTcwWk0xNjkgNUgxNjRWNkgxNjlWNVoiIGZpbGw9IiNDNUM1QzUiLz4KPHBhdGggZD0iTTE3NyAyNkgxNzJWMjVIMTc3VjI2Wk0xNzYgMjlIMTc0VjMwSDE3NlYyOVpNMTcyIDI5SDE2M1YzMEgxNzJWMjlaTTE3NCAzNUgxNjNWMzZIMTc0VjM1Wk0xNjkgMzJIMTYzVjMzSDE2OVYzMlpNMTc3IDMySDE3MlYzM0gxNzdWMzJaTTE3MCAyNFYyN0gxNjNWMjRIMTcwWk0xNjkgMjVIMTY0VjI2SDE2OVYyNVoiIGZpbGw9IiM0MjQyNDIiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yMDkuMjIzIDMyLjkzM0MyMDkuNTQ5IDMzLjEyNTQgMjA5LjkyMiAzMy4yMjMxIDIxMC4zIDMzLjIxNUMyMTAuNjM4IDMzLjIyMTggMjEwLjk3MyAzMy4xNDkyIDIxMS4yNzcgMzMuMDAzQzIxMS41ODIgMzIuODU2NyAyMTEuODQ4IDMyLjY0MDkgMjEyLjA1NCAzMi4zNzNDMjEyLjUwOSAzMS43NjUyIDIxMi43NCAzMS4wMTg3IDIxMi43MDggMzAuMjZDMjEyLjc0MSAyOS41ODYyIDIxMi41MzcgMjguOTIyMSAyMTIuMTMyIDI4LjM4M0MyMTEuOTM2IDI4LjE0MTYgMjExLjY4NiAyNy45NDk2IDIxMS40MDIgMjcuODIyM0MyMTEuMTE4IDI3LjY5NSAyMTAuODA5IDI3LjYzNiAyMTAuNDk4IDI3LjY1QzIxMC4wNzUgMjcuNjQ3IDIwOS42NiAyNy43NjA4IDIwOS4yOTggMjcuOTc5QzIwOS4xODMgMjguMDQ4MSAyMDkuMDc1IDI4LjEyNzggMjA4Ljk3NSAyOC4yMTdWMjUuNDc1SDIwNy45ODRWMzMuMUgyMDguOTc5VjMyLjc1NkMyMDkuMDU1IDMyLjgyMTcgMjA5LjEzNyAzMi44ODA5IDIwOS4yMjMgMzIuOTMzWk0yMDkuODUgMjguNzAwMUMyMTAuMDM2IDI4LjYyMSAyMTAuMjM4IDI4LjU4NjggMjEwLjQ0IDI4LjZDMjEwLjYxMyAyOC41OTQ1IDIxMC43ODQgMjguNjMwNSAyMTAuOTQgMjguNzA1QzIxMS4wOTYgMjguNzc5NSAyMTEuMjMyIDI4Ljg5MDIgMjExLjMzNiAyOS4wMjhDMjExLjU5MyAyOS4zOTA1IDIxMS43MTggMjkuODI5NSAyMTEuNjkzIDMwLjI3M0MyMTEuNzIgMzAuNzk3NSAyMTEuNTggMzEuMzE3IDIxMS4yOTMgMzEuNzU3QzIxMS4xODggMzEuOTE1MyAyMTEuMDQ1IDMyLjA0NDcgMjEwLjg3OCAzMi4xMzM1QzIxMC43MSAzMi4yMjIzIDIxMC41MjMgMzIuMjY3NSAyMTAuMzMzIDMyLjI2NUMyMTAuMTQ5IDMyLjI3MzIgMjA5Ljk2NiAzMi4yNCAyMDkuNzk3IDMyLjE2NzhDMjA5LjYyOCAzMi4wOTU2IDIwOS40NzggMzEuOTg2MyAyMDkuMzU3IDMxLjg0OEMyMDkuMTAyIDMxLjU1OTYgMjA4Ljk2NSAzMS4xODUxIDIwOC45NzUgMzAuOFYzMC4yQzIwOC45NjMgMjkuNzgzMyAyMDkuMTAzIDI5LjM3NjUgMjA5LjM2OCAyOS4wNTVDMjA5LjQ5OSAyOC45MDA2IDIwOS42NjQgMjguNzc5MSAyMDkuODUgMjguNzAwMVpNMjA1LjI4OSAyNy42NzVDMjA0Ljk3IDI3LjY3OTMgMjA0LjY1NCAyNy43MzQgMjA0LjM1MiAyNy44MzdDMjA0LjA2NCAyNy45MjI5IDIwMy43OTMgMjguMDU4MyAyMDMuNTUyIDI4LjIzN0wyMDMuNDUyIDI4LjMxNFYyOS41MTRMMjAzLjg3NSAyOS4xNTVDMjA0LjI0NiAyOC44MDQ4IDIwNC43MzEgMjguNjAxNSAyMDUuMjQxIDI4LjU4M0MyMDUuMzY2IDI4LjU3MTYgMjA1LjQ5MiAyOC41OTE1IDIwNS42MDcgMjguNjQwN0MyMDUuNzIyIDI4LjY4OTkgMjA1LjgyNCAyOC43NjcgMjA1LjkwMiAyOC44NjVDMjA2LjA1MiAyOS4wOTcxIDIwNi4xMzIgMjkuMzY3NSAyMDYuMTMzIDI5LjY0NEwyMDQuOSAyOS44MjVDMjA0LjM5NCAyOS44Nzc4IDIwMy45MTUgMzAuMDc3NyAyMDMuNTIyIDMwLjRDMjAzLjM2NyAzMC41NTE4IDIwMy4yNDMgMzAuNzMyNyAyMDMuMTU4IDMwLjkzMjRDMjAzLjA3MyAzMS4xMzIgMjAzLjAyOCAzMS4zNDY0IDIwMy4wMjYgMzEuNTYzNEMyMDMuMDI0IDMxLjc4MDQgMjAzLjA2NSAzMS45OTU3IDIwMy4xNDYgMzIuMTk2OUMyMDMuMjI4IDMyLjM5ODEgMjAzLjM0OCAzMi41ODEzIDIwMy41IDMyLjczNkMyMDMuNjY5IDMyLjg5MDQgMjAzLjg2NiAzMy4wMSAyMDQuMDgxIDMzLjA4NzlDMjA0LjI5NiAzMy4xNjU5IDIwNC41MjUgMzMuMjAwNSAyMDQuNzUzIDMzLjE5QzIwNS4xNDcgMzMuMTkzMSAyMDUuNTMzIDMzLjA3NzQgMjA1Ljg2IDMyLjg1OEMyMDUuOTYyIDMyLjc4OTcgMjA2LjA1NyAzMi43MTMxIDIwNi4xNDYgMzIuNjI5VjMzLjA3M0gyMDcuMDg3VjI5LjcxNUMyMDcuMTIxIDI5LjE3NDIgMjA2Ljk1NCAyOC42Mzk5IDIwNi42MTggMjguMjE1QzIwNi40NSAyOC4wMzI5IDIwNi4yNDMgMjcuODkgMjA2LjAxNCAyNy43OTY3QzIwNS43ODQgMjcuNzAzNCAyMDUuNTM3IDI3LjY2MTggMjA1LjI4OSAyNy42NzVaTTIwNi4xNDYgMzAuNzE2QzIwNi4xNjYgMzEuMTM0MyAyMDYuMDI2IDMxLjU0NDYgMjA1Ljc1NSAzMS44NjRDMjA1LjYzNyAzMi4wMDA1IDIwNS40OSAzMi4xMDkyIDIwNS4zMjUgMzIuMTgyMUMyMDUuMTYgMzIuMjU1MSAyMDQuOTggMzIuMjkwNiAyMDQuOCAzMi4yODZDMjA0LjY5IDMyLjI5NDUgMjA0LjU4IDMyLjI4MTIgMjA0LjQ3NiAzMi4yNDY5QzIwNC4zNzIgMzIuMjEyNSAyMDQuMjc1IDMyLjE1NzkgMjA0LjE5MiAzMi4wODZDMjA0LjA2MSAzMS45MzQ2IDIwMy45ODkgMzEuNzQwOSAyMDMuOTg5IDMxLjU0MDVDMjAzLjk4OSAzMS4zNDAxIDIwNC4wNjEgMzEuMTQ2NCAyMDQuMTkyIDMwLjk5NUMyMDQuNDczIDMwLjgyMTMgMjA0Ljc5MiAzMC43MTg0IDIwNS4xMjIgMzAuNjk1TDIwNi4xNDIgMzAuNTQ3TDIwNi4xNDYgMzAuNzE2Wk0yMTQuNDU5IDMzLjAzMjVDMjE0Ljc2NiAzMy4xNjM4IDIxNS4wOTggMzMuMjI2MSAyMTUuNDMyIDMzLjIxNUMyMTUuOTI3IDMzLjIyNyAyMTYuNDE1IDMzLjEwMDYgMjE2Ljg0MiAzMi44NUwyMTYuOTY1IDMyLjc3NUwyMTYuOTc4IDMyLjc2OFYzMS42MTVMMjE2LjUzMiAzMS45MzVDMjE2LjIxNiAzMi4xNTkyIDIxNS44MzYgMzIuMjc0NyAyMTUuNDQ4IDMyLjI2NEMyMTUuMjUgMzIuMjcxOSAyMTUuMDUyIDMyLjIzNDIgMjE0Ljg3IDMyLjE1MzhDMjE0LjY4OSAzMi4wNzMzIDIxNC41MjggMzEuOTUyMyAyMTQuNCAzMS44QzIxNC4xMTQgMzEuNDI0NSAyMTMuOTczIDMwLjk1OTEgMjE0IDMwLjQ4OEMyMTMuOTc0IDI5Ljk4NzMgMjE0LjEzNSAyOS40OTQ4IDIxNC40NTMgMjkuMTA3QzIxNC41OTMgMjguOTQxMSAyMTQuNzcgMjguODA5MSAyMTQuOTY4IDI4LjcyMTNDMjE1LjE2NyAyOC42MzM1IDIxNS4zODMgMjguNTkyIDIxNS42IDI4LjZDMjE1Ljk0NCAyOC41OTg0IDIxNi4yODEgMjguNjk1MyAyMTYuNTcxIDI4Ljg3OUwyMTcgMjkuMTQ0VjI3Ljk3TDIxNi44MzEgMjcuODk3QzIxNi40NjMgMjcuNzM0MyAyMTYuMDY0IDI3LjY1MDIgMjE1LjY2MSAyNy42NUMyMTUuMyAyNy42Mzk5IDIxNC45NDEgMjcuNzA3NiAyMTQuNjA4IDI3Ljg0ODZDMjE0LjI3NSAyNy45ODk2IDIxMy45NzYgMjguMjAwNSAyMTMuNzMyIDI4LjQ2N0MyMTMuMjI2IDI5LjAyNjggMjEyLjk1OCAyOS43NjE5IDIxMi45ODUgMzAuNTE2QzIxMi45NTcgMzEuMjIzNSAyMTMuMTk2IDMxLjkxNTcgMjEzLjY1NCAzMi40NTVDMjEzLjg3NyAzMi43MDQgMjE0LjE1MiAzMi45MDEyIDIxNC40NTkgMzMuMDMyNVoiIGZpbGw9IiNDNUM1QzUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yMDkuMjIzIDEyLjkzM0MyMDkuNTQ5IDEzLjEyNTQgMjA5LjkyMiAxMy4yMjMxIDIxMC4zIDEzLjIxNUMyMTAuNjM4IDEzLjIyMTggMjEwLjk3MyAxMy4xNDkyIDIxMS4yNzcgMTMuMDAzQzIxMS41ODIgMTIuODU2NyAyMTEuODQ4IDEyLjY0MDkgMjEyLjA1NCAxMi4zNzNDMjEyLjUwOSAxMS43NjUyIDIxMi43NCAxMS4wMTg3IDIxMi43MDggMTAuMjZDMjEyLjc0MSA5LjU4NjIyIDIxMi41MzcgOC45MjIxIDIxMi4xMzIgOC4zODI5OEMyMTEuOTM2IDguMTQxNjEgMjExLjY4NiA3Ljk0OTU3IDIxMS40MDIgNy44MjIyOEMyMTEuMTE4IDcuNjk0OTggMjEwLjgwOSA3LjYzNTk3IDIxMC40OTggNy42NDk5N0MyMTAuMDc1IDcuNjQ2OTkgMjA5LjY2IDcuNzYwODUgMjA5LjI5OCA3Ljk3ODk4QzIwOS4xODMgOC4wNDgwNyAyMDkuMDc1IDguMTI3NzUgMjA4Ljk3NSA4LjIxNjk4VjUuNDc0OThIMjA3Ljk4NFYxMy4xSDIwOC45NzlWMTIuNzU2QzIwOS4wNTUgMTIuODIxNyAyMDkuMTM3IDEyLjg4MDkgMjA5LjIyMyAxMi45MzNaTTIwOS44NSA4LjcwMDA2QzIxMC4wMzYgOC42MjEwNSAyMTAuMjM4IDguNTg2NzcgMjEwLjQ0IDguNTk5OThDMjEwLjYxMyA4LjU5NDUyIDIxMC43ODQgOC42MzA1NCAyMTAuOTQgOC43MDUwMUMyMTEuMDk2IDguNzc5NDggMjExLjIzMiA4Ljg5MDIzIDIxMS4zMzYgOS4wMjc5OEMyMTEuNTkzIDkuMzkwNTMgMjExLjcxOCA5LjgyOTUxIDIxMS42OTMgMTAuMjczQzIxMS43MiAxMC43OTc1IDIxMS41OCAxMS4zMTcgMjExLjI5MyAxMS43NTdDMjExLjE4OCAxMS45MTUzIDIxMS4wNDUgMTIuMDQ0NyAyMTAuODc4IDEyLjEzMzVDMjEwLjcxIDEyLjIyMjMgMjEwLjUyMyAxMi4yNjc1IDIxMC4zMzMgMTIuMjY1QzIxMC4xNDkgMTIuMjczMiAyMDkuOTY2IDEyLjI0IDIwOS43OTcgMTIuMTY3OEMyMDkuNjI4IDEyLjA5NTYgMjA5LjQ3OCAxMS45ODYzIDIwOS4zNTcgMTEuODQ4QzIwOS4xMDIgMTEuNTU5NiAyMDguOTY1IDExLjE4NTEgMjA4Ljk3NSAxMC44VjEwLjJDMjA4Ljk2MyA5Ljc4MzMyIDIwOS4xMDMgOS4zNzY1IDIwOS4zNjggOS4wNTQ5OEMyMDkuNDk5IDguOTAwNjQgMjA5LjY2NCA4Ljc3OTA4IDIwOS44NSA4LjcwMDA2Wk0yMDUuMjg5IDcuNjc0OTlDMjA0Ljk3IDcuNjc5MzMgMjA0LjY1NCA3LjczNCAyMDQuMzUyIDcuODM2OTlDMjA0LjA2NCA3LjkyMjkzIDIwMy43OTMgOC4wNTgyOCAyMDMuNTUyIDguMjM2OTlMMjAzLjQ1MiA4LjMxMzk5VjkuNTEzOTlMMjAzLjg3NSA5LjE1NDk5QzIwNC4yNDYgOC44MDQ3OCAyMDQuNzMxIDguNjAxNDYgMjA1LjI0MSA4LjU4Mjk5QzIwNS4zNjYgOC41NzE2NCAyMDUuNDkyIDguNTkxNDcgMjA1LjYwNyA4LjY0MDY4QzIwNS43MjIgOC42ODk5IDIwNS44MjQgOC43NjY5NyAyMDUuOTAyIDguODY0OTlDMjA2LjA1MiA5LjA5NzEgMjA2LjEzMiA5LjM2NzU0IDIwNi4xMzMgOS42NDM5OUwyMDQuOSA5LjgyNDk5QzIwNC4zOTQgOS44Nzc4MSAyMDMuOTE1IDEwLjA3NzcgMjAzLjUyMiAxMC40QzIwMy4zNjcgMTAuNTUxOCAyMDMuMjQzIDEwLjczMjcgMjAzLjE1OCAxMC45MzI0QzIwMy4wNzMgMTEuMTMyIDIwMy4wMjggMTEuMzQ2NCAyMDMuMDI2IDExLjU2MzRDMjAzLjAyNCAxMS43ODA0IDIwMy4wNjUgMTEuOTk1NyAyMDMuMTQ2IDEyLjE5NjlDMjAzLjIyOCAxMi4zOTgxIDIwMy4zNDggMTIuNTgxMyAyMDMuNSAxMi43MzZDMjAzLjY2OSAxMi44OTA0IDIwMy44NjYgMTMuMDEgMjA0LjA4MSAxMy4wODc5QzIwNC4yOTYgMTMuMTY1OSAyMDQuNTI1IDEzLjIwMDUgMjA0Ljc1MyAxMy4xOUMyMDUuMTQ3IDEzLjE5MzEgMjA1LjUzMyAxMy4wNzc0IDIwNS44NiAxMi44NThDMjA1Ljk2MiAxMi43ODk3IDIwNi4wNTcgMTIuNzEzMSAyMDYuMTQ2IDEyLjYyOVYxMy4wNzNIMjA3LjA4N1Y5LjcxNDk5QzIwNy4xMjEgOS4xNzQyMiAyMDYuOTU0IDguNjM5ODggMjA2LjYxOCA4LjIxNDk5QzIwNi40NSA4LjAzMjg1IDIwNi4yNDMgNy44OTAwMyAyMDYuMDE0IDcuNzk2N0MyMDUuNzg0IDcuNzAzMzYgMjA1LjUzNyA3LjY2MTgxIDIwNS4yODkgNy42NzQ5OVpNMjA2LjE0NiAxMC43MTZDMjA2LjE2NiAxMS4xMzQzIDIwNi4wMjYgMTEuNTQ0NiAyMDUuNzU1IDExLjg2NEMyMDUuNjM3IDEyLjAwMDUgMjA1LjQ5IDEyLjEwOTIgMjA1LjMyNSAxMi4xODIxQzIwNS4xNiAxMi4yNTUxIDIwNC45OCAxMi4yOTA2IDIwNC44IDEyLjI4NkMyMDQuNjkgMTIuMjk0NSAyMDQuNTggMTIuMjgxMiAyMDQuNDc2IDEyLjI0NjlDMjA0LjM3MiAxMi4yMTI1IDIwNC4yNzUgMTIuMTU3OSAyMDQuMTkyIDEyLjA4NkMyMDQuMDYxIDExLjkzNDYgMjAzLjk4OSAxMS43NDA5IDIwMy45ODkgMTEuNTQwNUMyMDMuOTg5IDExLjM0MDEgMjA0LjA2MSAxMS4xNDY0IDIwNC4xOTIgMTAuOTk1QzIwNC40NzMgMTAuODIxMyAyMDQuNzkyIDEwLjcxODQgMjA1LjEyMiAxMC42OTVMMjA2LjE0MiAxMC41NDdMMjA2LjE0NiAxMC43MTZaTTIxNC40NTkgMTMuMDMyNUMyMTQuNzY2IDEzLjE2MzggMjE1LjA5OCAxMy4yMjYxIDIxNS40MzIgMTMuMjE1QzIxNS45MjcgMTMuMjI3IDIxNi40MTUgMTMuMTAwNiAyMTYuODQyIDEyLjg1TDIxNi45NjUgMTIuNzc1TDIxNi45NzggMTIuNzY4VjExLjYxNUwyMTYuNTMyIDExLjkzNUMyMTYuMjE2IDEyLjE1OTIgMjE1LjgzNiAxMi4yNzQ3IDIxNS40NDggMTIuMjY0QzIxNS4yNSAxMi4yNzE5IDIxNS4wNTIgMTIuMjM0MiAyMTQuODcgMTIuMTUzOEMyMTQuNjg5IDEyLjA3MzMgMjE0LjUyOCAxMS45NTIzIDIxNC40IDExLjhDMjE0LjExNCAxMS40MjQ1IDIxMy45NzMgMTAuOTU5MSAyMTQgMTAuNDg4QzIxMy45NzQgOS45ODczMiAyMTQuMTM1IDkuNDk0NzUgMjE0LjQ1MyA5LjEwNzA0QzIxNC41OTMgOC45NDEwNSAyMTQuNzcgOC44MDkxNCAyMTQuOTY4IDguNzIxM0MyMTUuMTY3IDguNjMzNDYgMjE1LjM4MyA4LjU5MiAyMTUuNiA4LjYwMDA0QzIxNS45NDQgOC41OTg0NCAyMTYuMjgxIDguNjk1MjUgMjE2LjU3MSA4Ljg3OTA0TDIxNyA5LjE0NDA0VjcuOTcwMDRMMjE2LjgzMSA3Ljg5NzA0QzIxNi40NjMgNy43MzQzMiAyMTYuMDY0IDcuNjUwMiAyMTUuNjYxIDcuNjUwMDRDMjE1LjMgNy42Mzk5MSAyMTQuOTQxIDcuNzA3NjIgMjE0LjYwOCA3Ljg0ODU5QzIxNC4yNzUgNy45ODk1NiAyMTMuOTc2IDguMjAwNDggMjEzLjczMiA4LjQ2NzA0QzIxMy4yMjYgOS4wMjY4MyAyMTIuOTU4IDkuNzYxODYgMjEyLjk4NSAxMC41MTZDMjEyLjk1NyAxMS4yMjM1IDIxMy4xOTYgMTEuOTE1NyAyMTMuNjU0IDEyLjQ1NUMyMTMuODc3IDEyLjcwNCAyMTQuMTUyIDEyLjkwMTIgMjE0LjQ1OSAxMy4wMzI1WiIgZmlsbD0iIzQyNDI0MiIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTI2NiAzTDI2NSA0VjE2TDI2NiAxN0gyNzVMMjc2IDE2VjdMMjc1LjcwNyA2LjI5Mjg5TDI3Mi43MDcgMy4yOTI4OUwyNzIgM0gyNjZaTTI2NiAxNlY0TDI3MSA0VjhIMjc1VjE2SDI2NlpNMjc1IDdMMjcyIDRWN0wyNzUgN1oiIGZpbGw9IiM0MjQyNDIiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNjYgMjNMMjY1IDI0VjM2TDI2NiAzN0gyNzVMMjc2IDM2VjI3TDI3NS43MDcgMjYuMjkyOUwyNzIuNzA3IDIzLjI5MjlMMjcyIDIzSDI2NlpNMjY2IDM2VjI0TDI3MSAyNFYyOEgyNzVWMzZIMjY2Wk0yNzUgMjdMMjcyIDI0VjI3TDI3NSAyN1oiIGZpbGw9IiNDNUM1QzUiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMzAgMjRMMTI5IDI1VjI4SDEzMFYyNUgxMzZWMzBIMTMyVjMxSDEzNkwxMzcgMzBWMjVMMTM2IDI0SDEzMFpNMTMxIDMwTDEzMCAyOUgxMjlIMTI0TDEyMyAzMFYzNUwxMjQgMzZIMTMwTDEzMSAzNVYzMVYzMFpNMTMwIDMxVjMwSDEyOUgxMjRWMzVIMTMwVjMxWk0xMzEgMjguNTg1OEwxMzEuNDE0IDI5SDEzNVYyOEgxMzFWMjguNTg1OFpNMTM1IDI2SDEzMVYyN0gxMzVWMjZaTTEyOSAzMUgxMjVWMzJIMTI5VjMxWk0xMjUgMzNIMTI5VjM0SDEyNVYzM1oiIGZpbGw9IiNFRTlEMjgiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMzAgNEwxMjkgNVY4SDEzMFY1SDEzNlYxMEgxMzJWMTFIMTM2TDEzNyAxMFY1TDEzNiA0SDEzMFpNMTMxIDEwTDEzMCA5SDEyOUgxMjRMMTIzIDEwVjE1TDEyNCAxNkgxMzBMMTMxIDE1VjExVjEwWk0xMzAgMTFWMTBIMTI5SDEyNFYxNUgxMzBWMTFaTTEzMSA4LjU4NTc5TDEzMS40MTQgOUgxMzVWOEgxMzFWOC41ODU3OVpNMTM1IDZIMTMxVjdIMTM1VjZaTTEyOSAxMUgxMjVWMTJIMTI5VjExWk0xMjUgMTNIMTI5VjE0SDEyNVYxM1oiIGZpbGw9IiNENjdFMDAiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNDYgM0wyNDUgNFYxNkwyNDYgMTdIMjU0TDI1NSAxNlY0TDI1NCAzSDI0NlpNMjQ2IDVWNEgyNTRWMTZIMjQ2VjE1SDI0OFYxNEgyNDZWMTJIMjUwVjExSDI0NlY5SDI0OFY4SDI0NlY2SDI1MFY1SDI0NloiIGZpbGw9IiM0MjQyNDIiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNDYgMjNMMjQ1IDI0VjM2TDI0NiAzN0gyNTRMMjU1IDM2VjI0TDI1NCAyM0gyNDZaTTI0NiAyNVYyNEgyNTRWMzZIMjQ2VjM1SDI0OFYzNEgyNDZWMzJIMjUwVjMxSDI0NlYyOUgyNDhWMjhIMjQ2VjI2SDI1MFYyNUgyNDZaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=");background-repeat:no-repeat;background-position:-2px -22px}.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon,.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constructor,.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.function,.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.method{background-position:-2px -2px}.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.field,.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.variable{background-position:-22px -2px}.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.class{background-position:-42px -2px}.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.interface{background-position:-62px -2px}.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.module{background-position:-82px -2px}.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.property{background-position:-102px -2px}.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum{background-position:-122px -2px}.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.string{background-position:-202px -2px}.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.rule{background-position:-242px -2px}.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.file{background-position:-262px -2px}.vs-dark .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constructor,.vs-dark .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.function,.vs-dark .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.method{background-position:-2px -22px}.vs-dark .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.field,.vs-dark .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.variable{background-position:-22px -22px}.vs-dark .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.class{background-position:-43px -22px}.vs-dark .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.interface{background-position:-63px -22px}.vs-dark .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.module{background-position:-82px -22px}.vs-dark .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.property{background-position:-102px -22px}.vs-dark .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum{background-position:-122px -22px}.vs-dark .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.string{background-position:-202px -22px}.vs-dark .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.rule{background-position:-242px -22px}.vs-dark .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.file{background-position:-262px -22px}.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon{background:none;display:inline}.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon:before{height:16px;width:16px;display:inline-block}.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constructor:before,.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.function:before,.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.method:before,.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon:before{content:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yIDQuODU3NDlMMi40ODU1IDRMNy40ODU1IDFIOC41MTQ1TDEzLjUxNDUgNEwxNCA0Ljg1NzQ5VjEwLjg1NzVMMTMuNTE0NSAxMS43MTVMOC41MTQ1IDE0LjcxNUg3LjQ4NTVMMi40ODU1IDExLjcxNUwyIDEwLjg1NzVWNC44NTc0OVpNNy41IDEzLjU1NzVMMyAxMC44NTc1VjUuNjk5NzVMNy41IDguMTU0M1YxMy41NTc1Wk04LjUgMTMuNTU3NUwxMyAxMC44NTc1VjUuNjk5NzVMOC41IDguMTU0M1YxMy41NTc1Wk04IDEuODU3NDlMMy4yNTkxMyA0LjcwMjAxTDggNy4yODc5NEwxMi43NDA5IDQuNzAyMDFMOCAxLjg1NzQ5WiIgZmlsbD0iI0IxODBENyIvPgo8L3N2Zz4K)}.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.field:before,.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.variable:before{content:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xIDYuMzk0NDNMMS41NTI3OSA1LjVMOC41NTI3OSAySDkuNDQ3MjFMMTQuNDQ3MiA0LjVMMTUgNS4zOTQ0M1Y5Ljg5NDQzTDE0LjQ0NzIgMTAuNzg4OUw3LjQ0NzIxIDE0LjI4ODlINi41NTI3OUwxLjU1Mjc5IDExLjc4ODlMMSAxMC44OTQ0VjYuMzk0NDNaTTYuNSAxMy4xNDQ0TDIgMTAuODk0NFY3LjE3MDk0TDYuNSA5LjIxNjM5VjEzLjE0NDRaTTcuNSAxMy4xNDQ0TDE0IDkuODk0NDNWNi4xNzk1NEw3LjUgOS4yMTI4N1YxMy4xNDQ0Wk05IDIuODk0NDNMMi4zMzcyOCA2LjIyNTc5TDYuOTk3MjUgOC4zNDM5NkwxMy42NzA2IDUuMjI5NzNMOSAyLjg5NDQzWiIgZmlsbD0iIzc1QkVGRiIvPgo8L3N2Zz4K)}.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.class:before{content:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zLjM1MzU2IDYuNjQ2NDJMMi4wNjA2NiA1LjM1MzUzTDUuMzUzNTYgMi4wNjA2NUw2LjY0NjQ1IDMuMzUzNTRMMy4zNTM1NiA2LjY0NjQyWk01IDFMMSA0Ljk5OTk4VjUuNzA3MDhMMyA3LjcwNzA3SDMuNzA3MTFMNC44NTM1NSA2LjU2MDYzVjEyLjM1MzVMNS4zNTM1NSAxMi44NTM1SDEwLjAwOTdWMTMuMzc0MUwxMS4zNDMgMTQuNzA3NEgxMi4wNTAxTDE0LjcxNjggMTIuMDQwN1YxMS4zMzM2TDEzLjM4MzUgMTAuMDAwM0gxMi42NzYzTDEwLjgyMzEgMTEuODUzNUg1Ljg1MzU1VjcuODkzNTVIMTAuMDA5N1Y4LjM3NDAxTDExLjM0MyA5LjcwNzM0SDEyLjA1MDFMMTQuNzE2OCA3LjA0MDY4VjYuMzMzNTdMMTMuMzgzNSA1LjAwMDI0SDEyLjY3NjNMMTAuODYzIDYuODEzNTZINS44NTM1NVY1LjU2MDY0TDcuNzA3MTEgMy43MDcwOVYyLjk5OTk5TDUuNzA3MTEgMUg1Wk0xMS4wNzAzIDguMDIwNDZMMTEuNjk2NiA4LjY0NjY4TDEzLjY1NjEgNi42ODcxM0wxMy4wMjk5IDYuMDYwOUwxMS4wNzAzIDguMDIwNDZaTTExLjA3MDMgMTMuMDIwNUwxMS42OTY2IDEzLjY0NjdMMTMuNjU2MSAxMS42ODcyTDEzLjAyOTkgMTEuMDYxTDExLjA3MDMgMTMuMDIwNVoiIGZpbGw9IiNFRTlEMjgiLz4KPC9zdmc+Cg==)}.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.interface:before{content:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMS41IDVDMTAuMTE5MyA1IDkgNi4xMTkyOSA5IDcuNUM5IDguODgwNzEgMTAuMTE5MyAxMCAxMS41IDEwQzEyLjg4MDcgMTAgMTQgOC44ODA3MSAxNCA3LjVDMTQgNi4xMTkyOSAxMi44ODA3IDUgMTEuNSA1Wk04LjAzNTQ0IDhDOC4yNzgwNiA5LjY5NjE1IDkuNzM2NzYgMTEgMTEuNSAxMUMxMy40MzMgMTEgMTUgOS40MzMgMTUgNy41QzE1IDUuNTY3IDEzLjQzMyA0IDExLjUgNEM5LjczNjc2IDQgOC4yNzgwNiA1LjMwMzg1IDguMDM1NDQgN0g0LjkzNjk5QzQuNzE0OTcgNi4xMzczOSAzLjkzMTkyIDUuNSAzIDUuNUMxLjg5NTQzIDUuNSAxIDYuMzk1NDMgMSA3LjVDMSA4LjYwNDU3IDEuODk1NDMgOS41IDMgOS41QzMuOTMxOTIgOS41IDQuNzE0OTcgOC44NjI2MSA0LjkzNjk5IDhIOC4wMzU0NFoiIGZpbGw9IiM3NUJFRkYiLz4KPC9zdmc+Cg==)}.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.module:before{content:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik02IDIuOTgzNjFWMi45NzE4NFYySDUuOTEwODNDNS41OTc0MyAyIDUuMjk0MDcgMi4wNjE2MSA1LjAwMTI4IDIuMTg0NzNDNC43MDgxOCAyLjMwNzk4IDQuNDQ5NDIgMi40ODQ3NCA0LjIyNTc4IDIuNzE0OThDNC4wMDMxMSAyLjk0NDIyIDMuODM3OTIgMy4xOTQ5OCAzLjczMjgyIDMuNDY3NjZMMy43MzIzMyAzLjQ2ODk4QzMuNjMzODIgMy43MzUyIDMuNTY4MTQgNC4wMTIwMSAzLjUzNTMzIDQuMjk5MTdMMy41MzUxOSA0LjMwMDUzQzMuNTA2NzggNC41ODA1IDMuNDk4NyA0Ljg2ODQ0IDMuNTEwODQgNS4xNjQyOEMzLjUyMjcyIDUuNDUzNzkgMy41Mjg2NiA1Ljc0MzI5IDMuNTI4NjYgNi4wMzI3OUMzLjUyODY2IDYuMjM1NTYgMy40ODk3NCA2LjQyNTk0IDMuNDEyIDYuNjA1MDdMMy40MTE2IDYuNjA2MDFDMy4zMzY4NyA2Ljc4Mjk2IDMuMjM0MjMgNi45Mzg2NiAzLjEwMzE3IDcuMDczNTlDMi45NzY0NCA3LjIwNDA1IDIuODI0NjYgNy4zMTA1NSAyLjY0NjcyIDcuMzkyNUMyLjQ3MDYgNy40Njk1NCAyLjI4NDk3IDcuNTA4MiAyLjA4OTE3IDcuNTA4MkgyVjcuNlY4LjRWOC40OTE4SDIuMDg5MTdDMi4yODQ2NSA4LjQ5MTggMi40NzAwMSA4LjUzMjM4IDIuNjQ2MDEgOC42MTMzNEwyLjY0NzQyIDguNjEzOTZDMi44MjQ1NyA4LjY5MTU3IDIuOTc1NzcgOC43OTc2MiAzLjEwMjIxIDguOTMxNjFMMy4xMDQxMiA4LjkzMzUyQzMuMjM0MjggOS4wNjM3IDMuMzM2NTkgOS4yMTg3MSAzLjQxMTI5IDkuMzk5NDJMMy40MTIwMSA5LjQwMTA4QzMuNDg5ODYgOS41ODA0NyAzLjUyODY2IDkuNzY4ODMgMy41Mjg2NiA5Ljk2NzIxQzMuNTI4NjYgMTAuMjU2NyAzLjUyMjcyIDEwLjU0NjIgMy41MTA4NCAxMC44MzU3QzMuNDk4NyAxMS4xMzE2IDMuNTA2NzcgMTEuNDIxNSAzLjUzNTE2IDExLjcwNTVMMy41MzUzNSAxMS43MDcyQzMuNTY4MTkgMTEuOTkwMyAzLjYzMzg3IDEyLjI2NSAzLjczMjMyIDEyLjUzMUwzLjczMjgzIDEyLjUzMjNDMy44Mzc5MyAxMi44MDUgNC4wMDMxMSAxMy4wNTU4IDQuMjI1NzggMTMuMjg1QzQuNDQ5NDIgMTMuNTE1MyA0LjcwODE4IDEzLjY5MiA1LjAwMTI4IDEzLjgxNTNDNS4yOTQwNyAxMy45Mzg0IDUuNTk3NDMgMTQgNS45MTA4MyAxNEg2VjEzLjJWMTMuMDE2NEg1LjkxMDgzQzUuNzEwOTUgMTMuMDE2NCA1LjUyMzQ2IDEyLjk3NzcgNS4zNDc2MyAxMi45MDA4QzUuMTczOTYgMTIuODE5MSA1LjAyMTk0IDEyLjcxMjYgNC44OTA4NiAxMi41ODE4QzQuNzYzODYgMTIuNDQ2OSA0LjY2MTA0IDEyLjI5MTEgNC41ODIyMyAxMi4xMTM3QzQuNTA4MzggMTEuOTM0NiA0LjQ3MTM0IDExLjc0NCA0LjQ3MTM0IDExLjU0MUM0LjQ3MTM0IDExLjMxMjcgNC40NzUzIDExLjA4ODUgNC40ODMyMSAxMC44Njg2QzQuNDkxMjUgMTAuNjQxMSA0LjQ5MTI3IDEwLjQxOTUgNC40ODMyNCAxMC4yMDM5QzQuNDc5MTQgOS45ODI0NiA0LjQ2MDg0IDkuNzY4ODMgNC40MjgyMyA5LjU2MzEyQzQuMzk1MTMgOS4zNTAyNCA0LjMzOTIxIDkuMTQ3NTcgNC4yNjAzOSA4Ljk1NTM2QzQuMTgwOTEgOC43NjE1NyA0LjA3MjU4IDguNTc3NDYgMy45MzYxNiA4LjQwMjk4QzMuODIzNDUgOC4yNTg4MSAzLjY4NTM4IDguMTI0NjIgMy41MjI4MyA4QzMuNjg1MzggNy44NzUzOCAzLjgyMzQ1IDcuNzQxMTkgMy45MzYxNiA3LjU5NzAyQzQuMDcyNTggNy40MjI1NCA0LjE4MDkxIDcuMjM4NDMgNC4yNjAzOSA3LjA0NDY0QzQuMzM5MTMgNi44NTI2MyA0LjM5NTEzIDYuNjUxNzUgNC40MjgyNiA2LjQ0Mjg1QzQuNDYwODIgNi4yMzMzIDQuNDc5MTQgNi4wMTk3MyA0LjQ4MzI0IDUuODAyMTlDNC40OTEyNyA1LjU4MjYyIDQuNDkxMjUgNS4zNjEwNSA0LjQ4MzIxIDUuMTM3NDlDNC40NzUzIDQuOTEzNCA0LjQ3MTM0IDQuNjg3MjUgNC40NzEzNCA0LjQ1OTAyQzQuNDcxMzQgNC4yNjAxOSA0LjUwODMzIDQuMDcxNTIgNC41ODIzOCAzLjg5MjA1QzQuNjYxMzUgMy43MTAzNCA0Ljc2NDIxIDMuNTU0NzUgNC44OTA4NiAzLjQyNDM3QzUuMDIxOTMgMy4yODk0MiA1LjE3NDYxIDMuMTgyNzUgNS4zNDgwMiAzLjEwNTEzQzUuNTIzOCAzLjAyNDI3IDUuNzExMTMgMi45ODM2MSA1LjkxMDgzIDIuOTgzNjFINlpNMTAgMTMuMDE2NFYxMy4wMjgyVjE0SDEwLjA4OTJDMTAuNDAyNiAxNCAxMC43MDU5IDEzLjkzODQgMTAuOTk4NyAxMy44MTUzQzExLjI5MTggMTMuNjkyIDExLjU1MDYgMTMuNTE1MyAxMS43NzQyIDEzLjI4NUMxMS45OTY5IDEzLjA1NTggMTIuMTYyMSAxMi44MDUgMTIuMjY3MiAxMi41MzIzTDEyLjI2NzcgMTIuNTMxQzEyLjM2NjIgMTIuMjY0OCAxMi40MzE5IDExLjk4OCAxMi40NjQ3IDExLjcwMDhMMTIuNDY0OCAxMS42OTk1QzEyLjQ5MzIgMTEuNDE5NSAxMi41MDEzIDExLjEzMTYgMTIuNDg5MiAxMC44MzU3QzEyLjQ3NzMgMTAuNTQ2MiAxMi40NzEzIDEwLjI1NjcgMTIuNDcxMyA5Ljk2NzIxQzEyLjQ3MTMgOS43NjQ0NCAxMi41MTAzIDkuNTc0MDYgMTIuNTg4IDkuMzk0OTNMMTIuNTg4NCA5LjM5Mzk5QzEyLjY2MzEgOS4yMTcwNCAxMi43NjU4IDkuMDYxMzQgMTIuODk2OCA4LjkyNjQyQzEzLjAyMzYgOC43OTU5NSAxMy4xNzUzIDguNjg5NDUgMTMuMzUzMyA4LjYwNzVDMTMuNTI5NCA4LjUzMDQ2IDEzLjcxNSA4LjQ5MTggMTMuOTEwOCA4LjQ5MThIMTRWOC40VjcuNlY3LjUwODJIMTMuOTEwOEMxMy43MTUzIDcuNTA4MiAxMy41MyA3LjQ2NzYyIDEzLjM1NCA3LjM4NjY2TDEzLjM1MjYgNy4zODYwNEMxMy4xNzU0IDcuMzA4NDQgMTMuMDI0MiA3LjIwMjM4IDEyLjg5NzggNy4wNjgzOUwxMi44OTU5IDcuMDY2NDhDMTIuNzY1NyA2LjkzNjMgMTIuNjYzNCA2Ljc4MTI5IDEyLjU4ODcgNi42MDA1OEwxMi41ODggNi41OTg5MkMxMi41MTAxIDYuNDE5NTMgMTIuNDcxMyA2LjIzMTE3IDEyLjQ3MTMgNi4wMzI3OUMxMi40NzEzIDUuNzQzMjkgMTIuNDc3MyA1LjQ1Mzc5IDEyLjQ4OTIgNS4xNjQyOEMxMi41MDEzIDQuODY4NDIgMTIuNDkzMiA0LjU3ODQ4IDEyLjQ2NDggNC4yOTQ1NEwxMi40NjQ2IDQuMjkyODVDMTIuNDMxOCA0LjAwOTcxIDEyLjM2NjEgMy43MzUwMiAxMi4yNjc3IDMuNDY4OTdMMTIuMjY3MiAzLjQ2NzY2QzEyLjE2MjEgMy4xOTQ5OSAxMS45OTY5IDIuOTQ0MjIgMTEuNzc0MiAyLjcxNDk4QzExLjU1MDYgMi40ODQ3NCAxMS4yOTE4IDIuMzA3OTggMTAuOTk4NyAyLjE4NDczQzEwLjcwNTkgMi4wNjE2MSAxMC40MDI2IDIgMTAuMDg5MiAySDEwVjIuOFYyLjk4MzYxSDEwLjA4OTJDMTAuMjg5MSAyLjk4MzYxIDEwLjQ3NjUgMy4wMjIzIDEwLjY1MjQgMy4wOTkxN0MxMC44MjYgMy4xODA5MiAxMC45NzgxIDMuMjg3MzYgMTEuMTA5MSAzLjQxODIzQzExLjIzNjEgMy41NTMwNSAxMS4zMzkgMy43MDg4OSAxMS40MTc4IDMuODg2MjhDMTEuNDkxNiA0LjA2NTQgMTEuNTI4NyA0LjI1NTk2IDExLjUyODcgNC40NTkwMkMxMS41Mjg3IDQuNjg3MjcgMTEuNTI0NyA0LjkxMTQ1IDExLjUxNjggNS4xMzE0MkMxMS41MDg4IDUuMzU4OTQgMTEuNTA4NyA1LjU4MDQ5IDExLjUxNjggNS43OTYwNUMxMS41MjA5IDYuMDE3NTQgMTEuNTM5MiA2LjIzMTE3IDExLjU3MTggNi40MzY4OEMxMS42MDQ5IDYuNjQ5NzYgMTEuNjYwOCA2Ljg1MjQzIDExLjczOTYgNy4wNDQ2NEMxMS44MTkxIDcuMjM4NDMgMTEuOTI3NCA3LjQyMjU0IDEyLjA2MzggNy41OTcwMkMxMi4xNzY1IDcuNzQxMTkgMTIuMzE0NiA3Ljg3NTM4IDEyLjQ3NzIgOEMxMi4zMTQ2IDguMTI0NjIgMTIuMTc2NSA4LjI1ODgxIDEyLjA2MzggOC40MDI5OEMxMS45Mjc0IDguNTc3NDYgMTEuODE5MSA4Ljc2MTU3IDExLjczOTYgOC45NTUzNkMxMS42NjA5IDkuMTQ3MzcgMTEuNjA0OSA5LjM0ODI1IDExLjU3MTcgOS41NTcxNUMxMS41MzkyIDkuNzY2NyAxMS41MjA5IDkuOTgwMjcgMTEuNTE2OCAxMC4xOTc4QzExLjUwODcgMTAuNDE3NCAxMS41MDg3IDEwLjYzODkgMTEuNTE2OCAxMC44NjI1QzExLjUyNDcgMTEuMDg2NiAxMS41Mjg3IDExLjMxMjggMTEuNTI4NyAxMS41NDFDMTEuNTI4NyAxMS43Mzk4IDExLjQ5MTcgMTEuOTI4NSAxMS40MTc2IDEyLjEwNzlDMTEuMzM4NiAxMi4yODk3IDExLjIzNTggMTIuNDQ1MiAxMS4xMDkxIDEyLjU3NTZDMTAuOTc4MSAxMi43MTA2IDEwLjgyNTQgMTIuODE3MyAxMC42NTIgMTIuODk0OUMxMC40NzYyIDEyLjk3NTcgMTAuMjg4OSAxMy4wMTY0IDEwLjA4OTIgMTMuMDE2NEgxMFoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==)}.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.property:before{content:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTIuODA3MjMgMTQuOTc1NEMyLjU3MTE5IDE0Ljk3MjEgMi4zMzgyNiAxNC45MjExIDIuMTIyNDcgMTQuODI1NEMxLjkwNjY3IDE0LjcyOTcgMS43MTI0OCAxNC41OTEzIDEuNTUxNTggMTQuNDE4NkMxLjIzODUgMTQuMTMzNCAxLjA0NDMzIDEzLjc0MDggMS4wMDc3NSAxMy4zMTg5QzAuOTY2MjI1IDEyLjg4MjggMS4wOTI2OSAxMi40NDczIDEuMzYxMzMgMTIuMTAxM0MyLjU2Nzc5IDEwLjgyODkgNC45NDczIDguNDQ5NCA2LjY3ODExIDYuNzU0NzlDNi4zMDk4MyA1Ljc1ODg3IDYuMzI3MDQgNC42NjEyNyA2LjcyNjM3IDMuNjc3MzlDNy4wNTQ3NCAyLjg1ODc2IDcuNjM4NjkgMi4xNjgwNSA4LjM5MTI5IDEuNzA4MDdDOC45ODE3IDEuMzE3MDYgOS42NjAzMSAxLjA3OTQ0IDEwLjM2NTcgMS4wMTY3M0MxMS4wNzExIDAuOTU0MDIyIDExLjc4MDkgMS4wNjgxOSAxMi40MzExIDEuMzQ4OTJMMTMuMDQ4MiAxLjYxNjJMMTAuMTgyNCA0LjU2NzM4TDExLjQzNzEgNS44MjU4MkwxNC4zODA5IDIuOTQ4ODdMMTQuNjQ4MiAzLjU2Nzg4QzE0Ljg3MzUgNC4wODk3NiAxNC45OTMgNC42NTExOSAxNC45OTk3IDUuMjE5NjFDMTUuMDA2NCA1Ljc4ODAyIDE0LjkwMDIgNi4zNTIxMSAxNC42ODcyIDYuODc5MTVDMTQuNDc2IDcuNDAwMjkgMTQuMTYyMyA3Ljg3MzY4IDEzLjc2NDcgOC4yNzEyMkMxMy41Mzk0IDguNDkxNjkgMTMuMjkwNCA4LjY4NjUzIDEzLjAyMjIgOC44NTIxOEMxMi40NjczIDkuMjIyNzUgMTEuODMyNCA5LjQ1NjM2IDExLjE2OTcgOS41MzM4QzEwLjUwNjkgOS42MTEyNCA5LjgzNTIxIDkuNTMwMyA5LjIwOTgyIDkuMjk3NjRDOC4xMTE5NCAxMC40MTEzIDUuMzcxNDIgMTMuMTcwNCAzLjg5MTE5IDE0LjU1MjJDMy41OTQyNiAxNC44MjE5IDMuMjA4MzIgMTQuOTcyNiAyLjgwNzIzIDE0Ljk3NTRaTTEwLjc0NDggMS45MjgwMkMxMC4wODcgMS45MjYzNyA5LjQ0MzU5IDIuMTIwMTggOC44OTYxNCAyLjQ4NDg1QzguNjgyNjUgMi42MTUyIDguNDg0MzcgMi43Njg5NyA4LjMwNDk4IDIuOTQzM0M3LjgyNzg5IDMuNDI0MjMgNy41MDkyNiA0LjAzOTUzIDcuMzkxODIgNC43MDY2OUM3LjI3NDM4IDUuMzczODUgNy4zNjM3NCA2LjA2MDk4IDcuNjQ3OTIgNi42NzU5MUw3Ljc4MzQyIDYuOTcyODhMNy41NTA0OCA3LjIwMDI1QzUuODEyMjQgOC44OTY3MiAzLjI4MTQ2IDExLjQyMDEgMi4wNjQ3OSAxMi43MDQ1QzEuOTU2NDYgMTIuODY1OCAxLjkxMDEyIDEzLjA2MDggMS45MzQzNSAxMy4yNTM1QzEuOTU4NTcgMTMuNDQ2MyAyLjA1MTcxIDEzLjYyMzggMi4xOTY1NyAxMy43NTMyQzIuMjgwMDUgMTMuODQ2MiAyLjM4MTc3IDEzLjkyMTEgMi40OTU0MSAxMy45NzMxQzIuNTk1NTcgMTQuMDE4NCAyLjcwMzgzIDE0LjA0MyAyLjgxMzczIDE0LjA0NTVDMi45ODA2NCAxNC4wNDEzIDMuMTQwNDQgMTMuOTc3IDMuMjYzODMgMTMuODY0NkM0LjgzNjg3IDEyLjM5NjQgNy44NzYyMiA5LjMyNjQxIDguNzY4MDcgOC40MjQzNUw4Ljk5NzMgOC4xOTMyNkw5LjI5MjQyIDguMzI3ODNDOS44MDYxNyA4LjU2NzMyIDEwLjM3MzEgOC42Njk4NSAxMC45MzgyIDguNjI1NDVDMTEuNTAzMyA4LjU4MTA2IDEyLjA0NzMgOC4zOTEyNSAxMi41MTc0IDguMDc0NDdDMTIuNzMxMyA3Ljk0MjYgMTIuOTI5NiA3Ljc4Njk0IDEzLjEwODUgNy42MTA0NUMxMy40MTgzIDcuMzAxNTMgMTMuNjYzMSA2LjkzMzc0IDEzLjgyODYgNi41Mjg3NEMxMy45OTQgNi4xMjM3NSAxNC4wNzY3IDUuNjg5NzQgMTQuMDcxOSA1LjI1MjI4QzE0LjA3MTkgNS4wMzY2MiAxNC4wNTA1IDQuODIxNDggMTQuMDA3OCA0LjYxMDA3TDExLjQzMDYgNy4xMjUwOEw4Ljg3OTQ0IDQuNTc3NTlMMTEuMzk0NCAxLjk4ODM0QzExLjE4MDQgMS45NDY3NCAxMC45NjI4IDEuOTI2NTMgMTAuNzQ0OCAxLjkyODAyWiIgZmlsbD0iI0M1QzVDNSIvPgo8L3N2Zz4K)}.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum:before,.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.value:before{content:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04IDJMNyAzVjZIOFYzSDE0VjhIMTBWOUgxNEwxNSA4VjNMMTQgMkg4Wk05IDhMOCA3SDdIMkwxIDhWMTNMMiAxNEg4TDkgMTNWOVY4Wk04IDlWOEg3SDJWMTNIOFY5Wk05IDYuNTg1NzlMOS40MTQyMSA3SDEzVjZIOVY2LjU4NTc5Wk0xMyA0SDlWNUgxM1Y0Wk03IDlIM1YxMEg3VjlaTTMgMTFIN1YxMkgzVjExWiIgZmlsbD0iI0VFOUQyOCIvPgo8L3N2Zz4K)}.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.rule:before{content:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00IDFMMyAyVjE0TDQgMTVIMTJMMTMgMTRWMkwxMiAxSDRaTTQgM1YySDEyVjE0SDRWMTNINlYxMkg0VjEwSDhWOUg0VjdINlY2SDRWNEg4VjNINFoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==)}.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.file:before{content:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00IDFMMyAyVjE0TDQgMTVIMTNMMTQgMTRWNUwxMy43MDcxIDQuMjkyODlMMTAuNzA3MSAxLjI5Mjg5TDEwIDFINFpNNCAxNFYyTDkgMlY2SDEzVjE0SDRaTTEzIDVMMTAgMlY1TDEzIDVaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=)}.hc-black .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.string:before{content:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03LjIyMjg5IDEwLjkzM0M3LjU0ODYzIDExLjEyNTQgNy45MjE2MyAxMS4yMjMxIDguMjk5ODkgMTEuMjE1QzguNjM3NzcgMTEuMjIxOCA4Ljk3MjU0IDExLjE0OTIgOS4yNzcyMSAxMS4wMDNDOS41ODE4OCAxMC44NTY3IDkuODQ3OTIgMTAuNjQwOSAxMC4wNTM5IDEwLjM3M0MxMC41MDkxIDkuNzY1MTkgMTAuNzQwMiA5LjAxODY3IDEwLjcwNzkgOC4yNTk5OEMxMC43NDEyIDcuNTg2MjIgMTAuNTM3NCA2LjkyMjEgMTAuMTMxOSA2LjM4Mjk4QzkuOTM1NzUgNi4xNDE2MSA5LjY4NTc3IDUuOTQ5NTcgOS40MDIgNS44MjIyOEM5LjExODI0IDUuNjk0OTggOC44MDg1OCA1LjYzNTk3IDguNDk3ODkgNS42NDk5N0M4LjA3NTIyIDUuNjQ2OTkgNy42NTk5NCA1Ljc2MDg1IDcuMjk3ODkgNS45Nzg5OEM3LjE4MzA0IDYuMDQ4MDcgNy4wNzQ5IDYuMTI3NzUgNi45NzQ4OSA2LjIxNjk4VjMuNDc0OThINS45ODM4OVYxMS4xSDYuOTc4ODlWMTAuNzU2QzcuMDU1MTYgMTAuODIxNyA3LjEzNjc3IDEwLjg4MDkgNy4yMjI4OSAxMC45MzNaTTcuODQ5ODEgNi43MDAwNkM4LjAzNTk4IDYuNjIxMDUgOC4yMzgwNyA2LjU4Njc3IDguNDM5ODkgNi41OTk5OEM4LjYxMjU3IDYuNTk0NTIgOC43ODQwNCA2LjYzMDU0IDguOTM5OTQgNi43MDUwMUM5LjA5NTgzIDYuNzc5NDggOS4yMzE2MSA2Ljg5MDIzIDkuMzM1ODkgNy4wMjc5OEM5LjU5MjUzIDcuMzkwNTMgOS43MTg0IDcuODI5NTEgOS42OTI4OSA4LjI3Mjk3QzkuNzE5NzIgOC43OTc0OCA5LjU3OTY5IDkuMzE3MDEgOS4yOTI4OSA5Ljc1Njk4QzkuMTg4MjIgOS45MTUyNyA5LjA0NTQ2IDEwLjA0NDcgOC44Nzc3MyAxMC4xMzM1QzguNzA5OTkgMTAuMjIyMyA4LjUyMjY0IDEwLjI2NzUgOC4zMzI4OSAxMC4yNjVDOC4xNDkzNCAxMC4yNzMyIDcuOTY2MyAxMC4yNCA3Ljc5NzM0IDEwLjE2NzhDNy42MjgzOCAxMC4wOTU2IDcuNDc3ODQgOS45ODYyOCA3LjM1Njg5IDkuODQ3OTdDNy4xMDE1MiA5LjU1OTU3IDYuOTY1MDEgOS4xODUwNiA2Ljk3NDg5IDguNzk5OThWOC4xOTk5OEM2Ljk2Mjk5IDcuNzgzMzIgNy4xMDI2MyA3LjM3NjUgNy4zNjc4OSA3LjA1NDk4QzcuNDk4NTggNi45MDA2NCA3LjY2MzY0IDYuNzc5MDggNy44NDk4MSA2LjcwMDA2Wk0zLjI4OTAyIDUuNjc0OTlDMi45NzAxMSA1LjY3OTMzIDIuNjUzODggNS43MzQgMi4zNTIwMiA1LjgzNjk5QzIuMDY0MTcgNS45MjI5MyAxLjc5MzQ3IDYuMDU4MjggMS41NTIwMiA2LjIzNjk5TDEuNDUyMDIgNi4zMTM5OVY3LjUxMzk5TDEuODc1MDIgNy4xNTQ5OUMyLjI0NTc5IDYuODA0NzggMi43MzEzMyA2LjYwMTQ2IDMuMjQxMDIgNi41ODI5OUMzLjM2NTkzIDYuNTcxNjQgMy40OTE3IDYuNTkxNDcgMy42MDcwNiA2LjY0MDY4QzMuNzIyNDMgNi42ODk5IDMuODIzNzcgNi43NjY5NyAzLjkwMjAyIDYuODY0OTlDNC4wNTIyIDcuMDk3MSA0LjEzMjM5IDcuMzY3NTQgNC4xMzMwMiA3LjY0Mzk5TDIuOTAwMDIgNy44MjQ5OUMyLjM5NDM1IDcuODc3ODEgMS45MTUyNSA4LjA3NzcyIDEuNTIyMDIgOC4zOTk5OUMxLjM2Njk3IDguNTUxODEgMS4yNDMzOSA4LjczMjcxIDEuMTU4MzUgOC45MzIzNUMxLjA3MzMxIDkuMTMxOTkgMS4wMjg0OCA5LjM0NjQ0IDEuMDI2NDQgOS41NjM0M0MxLjAyNDQgOS43ODA0MiAxLjA2NTE3IDkuOTk1NjggMS4xNDY0NCAxMC4xOTY5QzEuMjI3NyAxMC4zOTgxIDEuMzQ3ODYgMTAuNTgxMyAxLjUwMDAyIDEwLjczNkMxLjY2ODcgMTAuODkwNCAxLjg2NjIyIDExLjAxIDIuMDgxMjUgMTEuMDg3OUMyLjI5NjI3IDExLjE2NTkgMi41MjQ1NiAxMS4yMDA1IDIuNzUzMDIgMTEuMTlDMy4xNDcgMTEuMTkzMSAzLjUzMjc4IDExLjA3NzQgMy44NjAwMiAxMC44NThDMy45NjE1MyAxMC43ODk3IDQuMDU3MiAxMC43MTMxIDQuMTQ2MDIgMTAuNjI5VjExLjA3M0g1LjA4NzAyVjcuNzE0OTlDNS4xMjEzNyA3LjE3NDIyIDQuOTU0MyA2LjYzOTg4IDQuNjE4MDIgNi4yMTQ5OUM0LjQ0OTc5IDYuMDMyODUgNC4yNDM0OCA1Ljg5MDAzIDQuMDEzNzggNS43OTY3QzMuNzg0MDcgNS43MDMzNiAzLjUzNjYxIDUuNjYxODEgMy4yODkwMiA1LjY3NDk5Wk00LjE0NjAyIDguNzE1OTlDNC4xNjU2NCA5LjEzNDM1IDQuMDI1OTIgOS41NDQ1OSAzLjc1NTAyIDkuODY0QzMuNjM2ODkgMTAuMDAwNSAzLjQ4OTk4IDEwLjEwOTIgMy4zMjQ4NiAxMC4xODIxQzMuMTU5NzMgMTAuMjU1MSAyLjk4MDQ5IDEwLjI5MDYgMi44MDAwMiAxMC4yODZDMi42OTA0OSAxMC4yOTQ1IDIuNTgwMzUgMTAuMjgxMiAyLjQ3NTk5IDEwLjI0NjlDMi4zNzE2MyAxMC4yMTI1IDIuMjc1MTEgMTAuMTU3OSAyLjE5MjAyIDEwLjA4NkMyLjA2MDc5IDkuOTM0NTUgMS45ODg1NiA5Ljc0MDg4IDEuOTg4NTYgOS41NDA0OUMxLjk4ODU2IDkuMzQwMTEgMi4wNjA3OSA5LjE0NjQ0IDIuMTkyMDIgOC45OTQ5OUMyLjQ3MzIyIDguODIxMzEgMi43OTIzMyA4LjcxODM3IDMuMTIyMDIgOC42OTQ5OUw0LjE0MjAyIDguNTQ2OTlMNC4xNDYwMiA4LjcxNTk5Wk0xMi40NTg4IDExLjAzMjVDMTIuNzY2IDExLjE2MzggMTMuMDk4MyAxMS4yMjYxIDEzLjQzMjIgMTEuMjE1QzEzLjkyNyAxMS4yMjcgMTQuNDE1MyAxMS4xMDA2IDE0Ljg0MjIgMTAuODVMMTQuOTY1MiAxMC43NzVMMTQuOTc4MiAxMC43NjhWOS42MTUwNEwxNC41MzIyIDkuOTM1MDRDMTQuMjE2IDEwLjE1OTIgMTMuODM1NiAxMC4yNzQ3IDEzLjQ0ODIgMTAuMjY0QzEzLjI0OTcgMTAuMjcxOSAxMy4wNTIgMTAuMjM0MiAxMi44NzAzIDEwLjE1MzhDMTIuNjg4NiAxMC4wNzMzIDEyLjUyNzggOS45NTIzMiAxMi40MDAyIDkuODAwMDRDMTIuMTE0NCA5LjQyNDUzIDExLjk3MjUgOC45NTkxMSAxMi4wMDAyIDguNDg4MDRDMTEuOTczNyA3Ljk4NzMyIDEyLjEzNTIgNy40OTQ3NSAxMi40NTMyIDcuMTA3MDRDMTIuNTkzNCA2Ljk0MTA1IDEyLjc2OTUgNi44MDkxNCAxMi45NjgyIDYuNzIxM0MxMy4xNjcgNi42MzM0NiAxMy4zODMxIDYuNTkyIDEzLjYwMDIgNi42MDAwNEMxMy45NDM5IDYuNTk4NDQgMTQuMjgwOCA2LjY5NTI1IDE0LjU3MTIgNi44NzkwNEwxNS4wMDAyIDcuMTQ0MDRWNS45NzAwNEwxNC44MzEyIDUuODk3MDRDMTQuNDYyNiA1LjczNDMyIDE0LjA2NDEgNS42NTAyIDEzLjY2MTIgNS42NTAwNEMxMy4yOTk5IDUuNjM5OTEgMTIuOTQwNiA1LjcwNzYyIDEyLjYwNzggNS44NDg1OUMxMi4yNzQ5IDUuOTg5NTYgMTEuOTc2MyA2LjIwMDQ4IDExLjczMjIgNi40NjcwNEMxMS4yMjYxIDcuMDI2ODMgMTAuOTU4MSA3Ljc2MTg2IDEwLjk4NTIgOC41MTYwNEMxMC45NTY3IDkuMjIzNDYgMTEuMTk1NSA5LjkxNTY5IDExLjY1NDIgMTAuNDU1QzExLjg3NjkgMTAuNzA0IDEyLjE1MTYgMTAuOTAxMiAxMi40NTg4IDExLjAzMjVaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=)}.monaco-editor .rename-box{z-index:100;color:inherit}.monaco-editor .rename-box .rename-input{padding:4px}.monaco-editor .snippet-placeholder{min-width:2px}.monaco-editor .finish-snippet-placeholder,.monaco-editor .snippet-placeholder{outline-style:solid;outline-width:1px}.monaco-editor .suggest-widget{z-index:40;width:430px}.monaco-editor .suggest-widget>.details,.monaco-editor .suggest-widget>.message,.monaco-editor .suggest-widget>.tree{width:100%;border-style:solid;border-width:1px;box-sizing:border-box}.monaco-editor.hc-black .suggest-widget>.details,.monaco-editor.hc-black .suggest-widget>.message,.monaco-editor.hc-black .suggest-widget>.tree{border-width:2px}.monaco-editor .suggest-widget.docs-side{width:660px}.monaco-editor .suggest-widget.docs-side>.details,.monaco-editor .suggest-widget.docs-side>.tree{width:50%;float:left}.monaco-editor .suggest-widget.docs-side.list-right>.details,.monaco-editor .suggest-widget.docs-side.list-right>.tree{float:right}.monaco-editor .suggest-widget>.message{padding-left:22px}.monaco-editor .suggest-widget>.tree{height:100%}.monaco-editor .suggest-widget .monaco-list{-webkit-user-select:none;-moz-user-select:-moz-none;-ms-user-select:none;-o-user-select:none;user-select:none}.monaco-editor .suggest-widget .monaco-list .monaco-list-row{display:-ms-flexbox;display:flex;-mox-box-sizing:border-box;box-sizing:border-box;padding-right:10px;background-repeat:no-repeat;background-position:2px 2px;white-space:nowrap;cursor:pointer;-ms-touch-action:none;touch-action:none}.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents{-ms-flex:1;flex:1;height:100%;overflow:hidden;padding-left:2px}.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main{display:-ms-flexbox;display:flex;overflow:hidden;text-overflow:ellipsis;white-space:pre}.monaco-editor .suggest-widget:not(.frozen) .monaco-highlighted-label .highlight{font-weight:700}.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.header>.close,.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.readMore{opacity:.6;background-position:50%;background-repeat:no-repeat;background-size:70%;cursor:pointer}.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.header>.close{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03Ljk5OTk4IDguNzA3MDhMMTEuNjQ2NCAxMi4zNTM1TDEyLjM1MzUgMTEuNjQ2NEw4LjcwNzA4IDcuOTk5OThMMTIuMzUzNSA0LjM1MzUzTDExLjY0NjQgMy42NDY0Mkw3Ljk5OTk4IDcuMjkyODdMNC4zNTM1MyAzLjY0NjQyTDMuNjQ2NDIgNC4zNTM1M0w3LjI5Mjg3IDcuOTk5OThMMy42NDY0MiAxMS42NDY0TDQuMzUzNTMgMTIuMzUzNUw3Ljk5OTk4IDguNzA3MDhaIiBmaWxsPSIjNDI0MjQyIi8+Cjwvc3ZnPgo=");position:absolute;top:0;right:0;margin-right:5px}.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.readMore{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zIDcuNUMzIDQuNDYyNDMgNS40NjI0MyAyIDguNSAyQzExLjUzNzYgMiAxNCA0LjQ2MjQzIDE0IDcuNUMxNCAxMC41Mzc2IDExLjUzNzYgMTMgOC41IDEzQzUuNDYyNDMgMTMgMyAxMC41Mzc2IDMgNy41Wk0yIDcuNUMyIDMuOTEwMTUgNC45MTAxNSAxIDguNSAxQzEyLjA4OTkgMSAxNSAzLjkxMDE1IDE1IDcuNUMxNSAxMS4wODk5IDEyLjA4OTkgMTQgOC41IDE0QzQuOTEwMTUgMTQgMiAxMS4wODk5IDIgNy41Wk04IDVWNEg5VjVIOFpNOCAxMFY5TDggN1Y2SDlWN0w5IDlWMTBIOFoiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==")}.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.header>.close:hover,.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.readMore:hover{opacity:1}.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.type-label{margin-left:.8em;-ms-flex:1;flex:1;text-align:right;overflow:hidden;text-overflow:ellipsis;opacity:.7;white-space:nowrap}.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.type-label>.monaco-tokenized-source{display:inline}.monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row.focused>.contents>.main>.readMore,.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused>.contents>.main>.readMore,.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused>.contents>.main>.type-label,.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.readMore,.monaco-editor .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.type-label{display:none}.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused>.contents>.main>.readMore,.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused>.contents>.main>.type-label{display:inline}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.deprecated{opacity:.66;text-decoration:unset}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.deprecated>.monaco-icon-label-description-container{text-decoration:line-through}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label:before{height:100%}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon{display:block;height:16px;width:16px;margin-left:2px;background-repeat:no-repeat;background-size:80%;background-position:50%}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.hide,.monaco-editor .suggest-widget.no-icons .monaco-list .monaco-list-row .icon,.monaco-editor .suggest-widget.no-icons .monaco-list .monaco-list-row .monaco-icon-label.suggest-icon:before{display:none}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.suggest-icon:before{content:" ";background-repeat:no-repeat;background-position:50%;background-size:75%}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constructor:before,.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.function:before,.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.method:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yIDQuODU3NDlMMi40ODU1IDRMNy40ODU1IDFIOC41MTQ1TDEzLjUxNDUgNEwxNCA0Ljg1NzQ5VjEwLjg1NzVMMTMuNTE0NSAxMS43MTVMOC41MTQ1IDE0LjcxNUg3LjQ4NTVMMi40ODU1IDExLjcxNUwyIDEwLjg1NzVWNC44NTc0OVpNNy41IDEzLjU1NzVMMyAxMC44NTc1VjUuNjk5NzVMNy41IDguMTU0M1YxMy41NTc1Wk04LjUgMTMuNTU3NUwxMyAxMC44NTc1VjUuNjk5NzVMOC41IDguMTU0M1YxMy41NTc1Wk04IDEuODU3NDlMMy4yNTkxMyA0LjcwMjAxTDggNy4yODc5NEwxMi43NDA5IDQuNzAyMDFMOCAxLjg1NzQ5WiIgZmlsbD0iIzY1MkQ5MCIvPgo8L3N2Zz4K")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.field:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xIDYuMzk0NDNMMS41NTI3OSA1LjVMOC41NTI3OSAySDkuNDQ3MjFMMTQuNDQ3MiA0LjVMMTUgNS4zOTQ0M1Y5Ljg5NDQzTDE0LjQ0NzIgMTAuNzg4OUw3LjQ0NzIxIDE0LjI4ODlINi41NTI3OUwxLjU1Mjc5IDExLjc4ODlMMSAxMC44OTQ0VjYuMzk0NDNaTTYuNSAxMy4xNDQ0TDIgMTAuODk0NFY3LjE3MDk0TDYuNSA5LjIxNjM5VjEzLjE0NDRaTTcuNSAxMy4xNDQ0TDE0IDkuODk0NDNWNi4xNzk1NEw3LjUgOS4yMTI4N1YxMy4xNDQ0Wk05IDIuODk0NDNMMi4zMzcyOCA2LjIyNTc5TDYuOTk3MjUgOC4zNDM5NkwxMy42NzA2IDUuMjI5NzNMOSAyLjg5NDQzWiIgZmlsbD0iIzAwN0FDQyIvPgo8L3N2Zz4K")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.event:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03LjQxMzU0IDEuNTU5OTZMOC4zMTE1MiAxSDExLjYwNTZMMTIuNDI0IDIuNTc0NjVMMTAuMjM1NiA2SDEyLjAxNzRMMTIuNzM2MyA3LjY5NTEyTDUuNjE5NDMgMTVMNC4wMTY3NSAxMy44MzdMNi4xMTk0MyAxMEg0Ljg5Nzk4TDQgOC41NTk5Nkw3LjQxMzU0IDEuNTU5OTZaTTcuNzgwMzMgOUw0LjkwMDU0IDE0LjMwNDlMMTIuMDE3NCA3SDguMzExNTJMMTEuNjA1NiAySDguMzExNTJMNC44OTc5OCA5SDcuNzgwMzNaIiBmaWxsPSIjRDY3RTAwIi8+Cjwvc3ZnPgo=")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.operator:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yLjg3Mjg5IDEuMTAwMjNDMy4yMDc2OCAxLjIzNTc5IDMuNDc1NDUgMS40OTggMy42MTgwMiAxLjgyOTg4QzMuNjkwMzIgMS45OTk1OSAzLjcyNjc1IDIuMTgyNDIgMy43MjUwMiAyLjM2Njg4QzMuNzI2MTcgMi41NDk5OSAzLjY4OTc1IDIuNzMxNCAzLjYxODAyIDIuODk5ODhDMy41MTI5OSAzLjE0NTY3IDMuMzM3ODIgMy4zNTUwMyAzLjExNDQyIDMuNTAxNzdDMi44OTEwMiAzLjY0ODUxIDIuNjI5MyAzLjcyNjEyIDIuMzYyMDIgMy43MjQ4OEMyLjE3OTI0IDMuNzI1OTIgMS45OTgxOCAzLjY4OTUxIDEuODMwMDIgMy42MTc4OEMxLjU4Mjk4IDMuNTE0MDYgMS4zNzIyNyAzLjMzOTMyIDEuMjI0NTMgMy4xMTU3NUMxLjA3NjggMi44OTIxOSAwLjk5ODY2NiAyLjYyOTg0IDEuMDAwMDIgMi4zNjE4OEMwLjk5OTEzIDIuMTc5MjEgMS4wMzUxOSAxLjk5ODI1IDEuMTA2MDIgMS44Mjk4OEMxLjI0MzM3IDEuNTAzMTQgMS41MDMyOCAxLjI0MzIzIDEuODMwMDIgMS4xMDU4OEMyLjE2MzMyIDAuOTY2NjkyIDIuNTM4MDkgMC45NjQ2NjEgMi44NzI4OSAxLjEwMDIzWk0yLjU3NTAyIDIuODY0ODhDMi43MDU0IDIuODA5MTMgMi44MDkyNyAyLjcwNTI2IDIuODY1MDIgMi41NzQ4OEMyLjg5MjkgMi41MDgzOCAyLjkwNzE4IDIuNDM2OTggMi45MDcwMiAyLjM2NDg4QzIuOTA4MTMgMi4yNjU0IDIuODgyMTUgMi4xNjc1IDIuODMxODUgMi4wODE2N0MyLjc4MTU2IDEuOTk1ODQgMi43MDg4NCAxLjkyNTMxIDIuNjIxNTEgMS44Nzc2N0MyLjUzNDE4IDEuODMwMDIgMi40MzU1MyAxLjgwNzA1IDIuMzM2MTQgMS44MTEyMUMyLjIzNjc0IDEuODE1MzcgMi4xNDAzNSAxLjg0NjUgMi4wNTczMSAxLjkwMTI4QzEuOTc0MjYgMS45NTYwNiAxLjkwNzcgMi4wMzI0MSAxLjg2NDc1IDIuMTIyMTVDMS44MjE4IDIuMjExODggMS44MDQwOSAyLjMxMTYxIDEuODEzNTIgMi40MTA2NUMxLjgyMjk0IDIuNTA5NjggMS44NTkxNSAyLjYwNDI4IDEuOTE4MjUgMi42ODQzQzEuOTc3MzYgMi43NjQzMyAyLjA1NzEzIDIuODI2NzUgMi4xNDkwMiAyLjg2NDg4QzIuMjg1NDkgMi45MjA4OSAyLjQzODU0IDIuOTIwODkgMi41NzUwMiAyLjg2NDg4Wk02LjQyOTk1IDEuMTA5NUwxLjEwOTY3IDYuNDI5NzdMMS43OTU1NyA3LjExNTY3TDcuMTE1ODQgMS43OTU0TDYuNDI5OTUgMS4xMDk1Wk0xMS41IDguOTk5OTlIMTIuNVYxMS41SDE1VjEyLjVIMTIuNVYxNUgxMS41VjEyLjVIOVYxMS41SDExLjVWOC45OTk5OVpNNS43Njc3NyA5LjUyNTA5TDYuNDc0ODcgMTAuMjMyMkw0LjcwNzExIDEyTDYuNDc0ODcgMTMuNzY3N0w1Ljc2Nzc3IDE0LjQ3NDhMNCAxMi43MDcxTDIuMjMyMjMgMTQuNDc0OEwxLjUyNTEzIDEzLjc2NzdMMy4yOTI4OSAxMkwxLjUyNTEzIDEwLjIzMjJMMi4yMzIyMyA5LjUyNTA5TDQgMTEuMjkyOUw1Ljc2Nzc3IDkuNTI1MDlaTTcuMTE4MDIgNS4zMjk4OEM3LjAxNDQyIDUuMDgyNjggNi44Mzk3MyA0Ljg3MTgzIDYuNjE2MTIgNC43MjQwNkM2LjM5MjUgNC41NzYyOSA2LjEzMDA0IDQuNDk4MjYgNS44NjIwMiA0LjQ5OTg4QzUuNjc5MzUgNC40OTg5OSA1LjQ5ODM5IDQuNTM1MDUgNS4zMzAwMiA0LjYwNTg4QzUuMDAzMjggNC43NDMyMyA0Ljc0MzM3IDUuMDAzMTQgNC42MDYwMiA1LjMyOTg4QzQuNTM1ODggNS40OTQ3OCA0LjQ5ODk3IDUuNjcxOTEgNC40OTc0MSA1Ljg1MTFDNC40OTU4NiA2LjAzMDMgNC41Mjk2NyA2LjIwODA0IDQuNTk2OTMgNi4zNzQxNEM0LjY2NDE5IDYuNTQwMjQgNC43NjM1NiA2LjY5MTQzIDQuODg5MzYgNi44MTkwNkM1LjAxNTE2IDYuOTQ2NjkgNS4xNjQ5IDcuMDQ4MjMgNS4zMzAwMiA3LjExNzg4QzUuNDk4NjcgNy4xODg0OCA1LjY3OTY4IDcuMjI0ODQgNS44NjI1MiA3LjIyNDg0QzYuMDQ1MzUgNy4yMjQ4NCA2LjIyNjM2IDcuMTg4NDggNi4zOTUwMiA3LjExNzg4QzYuNjQyMDEgNy4wMTM4OCA2Ljg1MjcgNi44MzkxMyA3LjAwMDU4IDYuNjE1NjNDNy4xNDg0NSA2LjM5MjEzIDcuMjI2ODkgNi4xMjk4NyA3LjIyNjAyIDUuODYxODhDNy4yMjY1NSA1LjY3OTA1IDcuMTg5OCA1LjQ5ODAzIDcuMTE4MDIgNS4zMjk4OFpNNi4zNjUwMiA2LjA3NDg4QzYuMzM3NjYgNi4xMzkzNyA2LjI5ODI5IDYuMTk4MDggNi4yNDkwMiA2LjI0Nzg4QzYuMTk5MDggNi4yOTcyNCA2LjE0MDQyIDYuMzM2OTEgNi4wNzYwMiA2LjM2NDg4QzYuMDA4NTQgNi4zOTI5NyA1LjkzNjExIDYuNDA3MjUgNS44NjMwMiA2LjQwNjg4QzUuNzg5OTEgNi40MDc0NCA1LjcxNzQ0IDYuMzkzMTUgNS42NTAwMiA2LjM2NDg4QzUuNTg1NDEgNi4zMzcyOSA1LjUyNjY4IDYuMjk3NTcgNS40NzcwMiA2LjI0Nzg4QzUuNDI2OTEgNi4xOTg1NiA1LjM4NzEzIDYuMTM5NzUgNS4zNjAwMiA2LjA3NDg4QzUuMzA0MDEgNS45Mzg0IDUuMzA0MDEgNS43ODUzNiA1LjM2MDAyIDUuNjQ4ODhDNS40MTUzNiA1LjUxODQ2IDUuNTE5NDEgNS40MTQ3NyA1LjY1MDAyIDUuMzU5ODhDNS43MTczNyA1LjMzMTI2IDUuNzg5ODQgNS4zMTY2MyA1Ljg2MzAyIDUuMzE2ODhDNS45MzYxOCA1LjMxNjg1IDYuMDA4NiA1LjMzMTQ3IDYuMDc2MDIgNS4zNTk4OEM2LjE0MDM3IDUuMzg3NDkgNi4xOTkwNCA1LjQyNjgyIDYuMjQ5MDIgNS40NzU4OEM2LjI5Nzg2IDUuNTI2MDMgNi4zMzcxNiA1LjU4NDY1IDYuMzY1MDIgNS42NDg4OEM2LjM5MzQgNS43MTYzIDYuNDA4MDIgNS43ODg3MiA2LjQwODAyIDUuODYxODhDNi40MDgwMiA1LjkzNTAzIDYuMzkzNCA2LjAwNzQ1IDYuMzY1MDIgNi4wNzQ4OFpNMTQgM0gxMFY0SDE0VjNaIiBmaWxsPSIjNDI0MjQyIi8+Cjwvc3ZnPgo=")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.variable:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xLjUgNEwxIDQuNVYxMi41TDEuNSAxM0g0VjEySDJWNUg0VjRIMS41Wk0xNC41IDEzTDE1IDEyLjVMMTUgNC41TDE0LjUgNEgxMlY1TDE0IDVMMTQgMTJIMTJWMTNIMTQuNVpNOC43OTY5MyA1TDQuMjk2OTMgN0w0IDcuNDU2OTFWOS45NTY5MUw0LjI0Mjc1IDEwLjM4NTdMNi43NDI3NSAxMS44ODU3TDcuMjAzMDcgMTEuOTEzOEwxMS43MDMxIDkuOTEzODFMMTIgOS40NTY5MVY2Ljk1NjkxTDExLjc1NzIgNi41MjgxNkw5LjI1NzI1IDUuMDI4MTZMOC43OTY5MyA1Wk01IDguMzRWOS42NzM4MUw2LjUgMTAuNTczOFY5LjI0TDUgOC4zNFpNNy41IDkuMjgxODRWMTAuNjg3NUwxMSA5LjEzMTk3VjcuNzI2MjlMNy41IDkuMjgxODRaTTEwLjQxNzggNi44OTA3MUw4Ljk2NTU5IDYuMDE5MzZMNS41ODIxNiA3LjUyMzExTDcuMDM0NDEgOC4zOTQ0NUwxMC40MTc4IDYuODkwNzFaIiBmaWxsPSIjMDA3QUNDIi8+Cjwvc3ZnPgo=")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.class:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zLjM1MzU2IDYuNjQ2NDJMMi4wNjA2NiA1LjM1MzUzTDUuMzUzNTYgMi4wNjA2NUw2LjY0NjQ1IDMuMzUzNTRMMy4zNTM1NiA2LjY0NjQyWk01IDFMMSA0Ljk5OTk4VjUuNzA3MDhMMyA3LjcwNzA3SDMuNzA3MTFMNC44NTM1NSA2LjU2MDYzVjEyLjM1MzVMNS4zNTM1NSAxMi44NTM1SDEwLjAwOTdWMTMuMzc0MUwxMS4zNDMgMTQuNzA3NEgxMi4wNTAxTDE0LjcxNjggMTIuMDQwN1YxMS4zMzM2TDEzLjM4MzUgMTAuMDAwM0gxMi42NzYzTDEwLjgyMzEgMTEuODUzNUg1Ljg1MzU1VjcuODkzNTVIMTAuMDA5N1Y4LjM3NDAxTDExLjM0MyA5LjcwNzM0SDEyLjA1MDFMMTQuNzE2OCA3LjA0MDY4VjYuMzMzNTdMMTMuMzgzNSA1LjAwMDI0SDEyLjY3NjNMMTAuODYzIDYuODEzNTZINS44NTM1NVY1LjU2MDY0TDcuNzA3MTEgMy43MDcwOVYyLjk5OTk5TDUuNzA3MTEgMUg1Wk0xMS4wNzAzIDguMDIwNDZMMTEuNjk2NiA4LjY0NjY4TDEzLjY1NjEgNi42ODcxM0wxMy4wMjk5IDYuMDYwOUwxMS4wNzAzIDguMDIwNDZaTTExLjA3MDMgMTMuMDIwNUwxMS42OTY2IDEzLjY0NjdMMTMuNjU2MSAxMS42ODcyTDEzLjAyOTkgMTEuMDYxTDExLjA3MDMgMTMuMDIwNVoiIGZpbGw9IiNENjdFMDAiLz4KPC9zdmc+Cg==")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.interface:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMS41IDVDMTAuMTE5MyA1IDkgNi4xMTkyOSA5IDcuNUM5IDguODgwNzEgMTAuMTE5MyAxMCAxMS41IDEwQzEyLjg4MDcgMTAgMTQgOC44ODA3MSAxNCA3LjVDMTQgNi4xMTkyOSAxMi44ODA3IDUgMTEuNSA1Wk04LjAzNTQ0IDhDOC4yNzgwNiA5LjY5NjE1IDkuNzM2NzYgMTEgMTEuNSAxMUMxMy40MzMgMTEgMTUgOS40MzMgMTUgNy41QzE1IDUuNTY3IDEzLjQzMyA0IDExLjUgNEM5LjczNjc2IDQgOC4yNzgwNiA1LjMwMzg1IDguMDM1NDQgN0g0LjkzNjk5QzQuNzE0OTcgNi4xMzczOSAzLjkzMTkyIDUuNSAzIDUuNUMxLjg5NTQzIDUuNSAxIDYuMzk1NDMgMSA3LjVDMSA4LjYwNDU3IDEuODk1NDMgOS41IDMgOS41QzMuOTMxOTIgOS41IDQuNzE0OTcgOC44NjI2MSA0LjkzNjk5IDhIOC4wMzU0NFoiIGZpbGw9IiMwMDdBQ0MiLz4KPC9zdmc+Cg==")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.struct:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yIDJMMSAzVjZMMiA3SDE0TDE1IDZWM0wxNCAySDJaTTIgM0gzSDEzSDE0VjRWNVY2SDEzSDNIMlY1VjRWM1pNMSAxMEwyIDlINUw2IDEwVjEzTDUgMTRIMkwxIDEzVjEwWk0zIDEwSDJWMTFWMTJWMTNIM0g0SDVWMTJWMTFWMTBINEgzWk0xMCAxMEwxMSA5SDE0TDE1IDEwVjEzTDE0IDE0SDExTDEwIDEzVjEwWk0xMiAxMEgxMVYxMVYxMlYxM0gxMkgxM0gxNFYxMlYxMVYxMEgxM0gxMloiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.type-parameter:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMSA2SDEwVjUuNUMxMCA1LjIyMzg2IDkuNzc2MTYgNSA5LjUwMDAxIDVIOC40NzkwMlYxMC41QzguNDc5MDIgMTAuNzc2MSA4LjcwMjg4IDExIDguOTc5MDIgMTFIOS40NzkwMlYxMkg2LjQ3OTAyVjExSDYuOTc5MDJDNy4yNTUxNiAxMSA3LjQ3OTAyIDEwLjc3NjEgNy40NzkwMiAxMC41VjVINi41MDAwMUM2LjIyMzg3IDUgNi4wMDAwMSA1LjIyMzg2IDYuMDAwMDEgNS41VjZINS4wMDAwMVY0SDExVjZaTTEzLjkxNDIgOC4wNDgxTDEyLjQ1MTkgNi41ODU4MUwxMy4xNTkgNS44Nzg3MUwxNC45NzQ5IDcuNjk0NTRWOC40MDE2NUwxMy4yMDcxIDEwLjE2OTRMMTIuNSA5LjQ2MjMxTDEzLjkxNDIgOC4wNDgxWk0zLjU0ODEgOS40NjIzTDIuMDg1ODEgOC4wMDAwMkwzLjUwMDAyIDYuNTg1ODFMMi43OTI5MSA1Ljg3ODdMMS4wMjUxNSA3LjY0NjQ3VjguMzUzNTdMMi44NDEgMTAuMTY5NEwzLjU0ODEgOS40NjIzWiIgZmlsbD0iIzQyNDI0MiIvPgo8L3N2Zz4K")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.module:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik02IDIuOTgzNjFWMi45NzE4NFYySDUuOTEwODNDNS41OTc0MyAyIDUuMjk0MDcgMi4wNjE2MSA1LjAwMTI4IDIuMTg0NzNDNC43MDgxOCAyLjMwNzk4IDQuNDQ5NDIgMi40ODQ3NCA0LjIyNTc4IDIuNzE0OThDNC4wMDMxMSAyLjk0NDIyIDMuODM3OTIgMy4xOTQ5OCAzLjczMjgyIDMuNDY3NjZMMy43MzIzMyAzLjQ2ODk4QzMuNjMzODIgMy43MzUyIDMuNTY4MTQgNC4wMTIwMSAzLjUzNTMzIDQuMjk5MTdMMy41MzUxOSA0LjMwMDUzQzMuNTA2NzggNC41ODA1IDMuNDk4NyA0Ljg2ODQ0IDMuNTEwODQgNS4xNjQyOEMzLjUyMjcyIDUuNDUzNzkgMy41Mjg2NiA1Ljc0MzI5IDMuNTI4NjYgNi4wMzI3OUMzLjUyODY2IDYuMjM1NTYgMy40ODk3NCA2LjQyNTk0IDMuNDEyIDYuNjA1MDdMMy40MTE2IDYuNjA2MDFDMy4zMzY4NyA2Ljc4Mjk2IDMuMjM0MjMgNi45Mzg2NiAzLjEwMzE3IDcuMDczNTlDMi45NzY0NCA3LjIwNDA1IDIuODI0NjYgNy4zMTA1NSAyLjY0NjcyIDcuMzkyNUMyLjQ3MDYgNy40Njk1NCAyLjI4NDk3IDcuNTA4MiAyLjA4OTE3IDcuNTA4MkgyVjcuNlY4LjRWOC40OTE4SDIuMDg5MTdDMi4yODQ2NSA4LjQ5MTggMi40NzAwMSA4LjUzMjM4IDIuNjQ2MDEgOC42MTMzNEwyLjY0NzQyIDguNjEzOTZDMi44MjQ1NyA4LjY5MTU3IDIuOTc1NzcgOC43OTc2MiAzLjEwMjIxIDguOTMxNjFMMy4xMDQxMiA4LjkzMzUyQzMuMjM0MjggOS4wNjM3IDMuMzM2NTkgOS4yMTg3MSAzLjQxMTI5IDkuMzk5NDJMMy40MTIwMSA5LjQwMTA4QzMuNDg5ODYgOS41ODA0NyAzLjUyODY2IDkuNzY4ODMgMy41Mjg2NiA5Ljk2NzIxQzMuNTI4NjYgMTAuMjU2NyAzLjUyMjcyIDEwLjU0NjIgMy41MTA4NCAxMC44MzU3QzMuNDk4NyAxMS4xMzE2IDMuNTA2NzcgMTEuNDIxNSAzLjUzNTE2IDExLjcwNTVMMy41MzUzNSAxMS43MDcyQzMuNTY4MTkgMTEuOTkwMyAzLjYzMzg3IDEyLjI2NSAzLjczMjMyIDEyLjUzMUwzLjczMjgzIDEyLjUzMjNDMy44Mzc5MyAxMi44MDUgNC4wMDMxMSAxMy4wNTU4IDQuMjI1NzggMTMuMjg1QzQuNDQ5NDIgMTMuNTE1MyA0LjcwODE4IDEzLjY5MiA1LjAwMTI4IDEzLjgxNTNDNS4yOTQwNyAxMy45Mzg0IDUuNTk3NDMgMTQgNS45MTA4MyAxNEg2VjEzLjJWMTMuMDE2NEg1LjkxMDgzQzUuNzEwOTUgMTMuMDE2NCA1LjUyMzQ2IDEyLjk3NzcgNS4zNDc2MyAxMi45MDA4QzUuMTczOTYgMTIuODE5MSA1LjAyMTk0IDEyLjcxMjYgNC44OTA4NiAxMi41ODE4QzQuNzYzODYgMTIuNDQ2OSA0LjY2MTA0IDEyLjI5MTEgNC41ODIyMyAxMi4xMTM3QzQuNTA4MzggMTEuOTM0NiA0LjQ3MTM0IDExLjc0NCA0LjQ3MTM0IDExLjU0MUM0LjQ3MTM0IDExLjMxMjcgNC40NzUzIDExLjA4ODUgNC40ODMyMSAxMC44Njg2QzQuNDkxMjUgMTAuNjQxMSA0LjQ5MTI3IDEwLjQxOTUgNC40ODMyNCAxMC4yMDM5QzQuNDc5MTQgOS45ODI0NiA0LjQ2MDg0IDkuNzY4ODMgNC40MjgyMyA5LjU2MzEyQzQuMzk1MTMgOS4zNTAyNCA0LjMzOTIxIDkuMTQ3NTcgNC4yNjAzOSA4Ljk1NTM2QzQuMTgwOTEgOC43NjE1NyA0LjA3MjU4IDguNTc3NDYgMy45MzYxNiA4LjQwMjk4QzMuODIzNDUgOC4yNTg4MSAzLjY4NTM4IDguMTI0NjIgMy41MjI4MyA4QzMuNjg1MzggNy44NzUzOCAzLjgyMzQ1IDcuNzQxMTkgMy45MzYxNiA3LjU5NzAyQzQuMDcyNTggNy40MjI1NCA0LjE4MDkxIDcuMjM4NDMgNC4yNjAzOSA3LjA0NDY0QzQuMzM5MTMgNi44NTI2MyA0LjM5NTEzIDYuNjUxNzUgNC40MjgyNiA2LjQ0Mjg1QzQuNDYwODIgNi4yMzMzIDQuNDc5MTQgNi4wMTk3MyA0LjQ4MzI0IDUuODAyMTlDNC40OTEyNyA1LjU4MjYyIDQuNDkxMjUgNS4zNjEwNSA0LjQ4MzIxIDUuMTM3NDlDNC40NzUzIDQuOTEzNCA0LjQ3MTM0IDQuNjg3MjUgNC40NzEzNCA0LjQ1OTAyQzQuNDcxMzQgNC4yNjAxOSA0LjUwODMzIDQuMDcxNTIgNC41ODIzOCAzLjg5MjA1QzQuNjYxMzUgMy43MTAzNCA0Ljc2NDIxIDMuNTU0NzUgNC44OTA4NiAzLjQyNDM3QzUuMDIxOTMgMy4yODk0MiA1LjE3NDYxIDMuMTgyNzUgNS4zNDgwMiAzLjEwNTEzQzUuNTIzOCAzLjAyNDI3IDUuNzExMTMgMi45ODM2MSA1LjkxMDgzIDIuOTgzNjFINlpNMTAgMTMuMDE2NFYxMy4wMjgyVjE0SDEwLjA4OTJDMTAuNDAyNiAxNCAxMC43MDU5IDEzLjkzODQgMTAuOTk4NyAxMy44MTUzQzExLjI5MTggMTMuNjkyIDExLjU1MDYgMTMuNTE1MyAxMS43NzQyIDEzLjI4NUMxMS45OTY5IDEzLjA1NTggMTIuMTYyMSAxMi44MDUgMTIuMjY3MiAxMi41MzIzTDEyLjI2NzcgMTIuNTMxQzEyLjM2NjIgMTIuMjY0OCAxMi40MzE5IDExLjk4OCAxMi40NjQ3IDExLjcwMDhMMTIuNDY0OCAxMS42OTk1QzEyLjQ5MzIgMTEuNDE5NSAxMi41MDEzIDExLjEzMTYgMTIuNDg5MiAxMC44MzU3QzEyLjQ3NzMgMTAuNTQ2MiAxMi40NzEzIDEwLjI1NjcgMTIuNDcxMyA5Ljk2NzIxQzEyLjQ3MTMgOS43NjQ0NCAxMi41MTAzIDkuNTc0MDYgMTIuNTg4IDkuMzk0OTNMMTIuNTg4NCA5LjM5Mzk5QzEyLjY2MzEgOS4yMTcwNCAxMi43NjU4IDkuMDYxMzQgMTIuODk2OCA4LjkyNjQyQzEzLjAyMzYgOC43OTU5NSAxMy4xNzUzIDguNjg5NDUgMTMuMzUzMyA4LjYwNzVDMTMuNTI5NCA4LjUzMDQ2IDEzLjcxNSA4LjQ5MTggMTMuOTEwOCA4LjQ5MThIMTRWOC40VjcuNlY3LjUwODJIMTMuOTEwOEMxMy43MTUzIDcuNTA4MiAxMy41MyA3LjQ2NzYyIDEzLjM1NCA3LjM4NjY2TDEzLjM1MjYgNy4zODYwNEMxMy4xNzU0IDcuMzA4NDQgMTMuMDI0MiA3LjIwMjM4IDEyLjg5NzggNy4wNjgzOUwxMi44OTU5IDcuMDY2NDhDMTIuNzY1NyA2LjkzNjMgMTIuNjYzNCA2Ljc4MTI5IDEyLjU4ODcgNi42MDA1OEwxMi41ODggNi41OTg5MkMxMi41MTAxIDYuNDE5NTMgMTIuNDcxMyA2LjIzMTE3IDEyLjQ3MTMgNi4wMzI3OUMxMi40NzEzIDUuNzQzMjkgMTIuNDc3MyA1LjQ1Mzc5IDEyLjQ4OTIgNS4xNjQyOEMxMi41MDEzIDQuODY4NDIgMTIuNDkzMiA0LjU3ODQ4IDEyLjQ2NDggNC4yOTQ1NEwxMi40NjQ2IDQuMjkyODVDMTIuNDMxOCA0LjAwOTcxIDEyLjM2NjEgMy43MzUwMiAxMi4yNjc3IDMuNDY4OTdMMTIuMjY3MiAzLjQ2NzY2QzEyLjE2MjEgMy4xOTQ5OSAxMS45OTY5IDIuOTQ0MjIgMTEuNzc0MiAyLjcxNDk4QzExLjU1MDYgMi40ODQ3NCAxMS4yOTE4IDIuMzA3OTggMTAuOTk4NyAyLjE4NDczQzEwLjcwNTkgMi4wNjE2MSAxMC40MDI2IDIgMTAuMDg5MiAySDEwVjIuOFYyLjk4MzYxSDEwLjA4OTJDMTAuMjg5MSAyLjk4MzYxIDEwLjQ3NjUgMy4wMjIzIDEwLjY1MjQgMy4wOTkxN0MxMC44MjYgMy4xODA5MiAxMC45NzgxIDMuMjg3MzYgMTEuMTA5MSAzLjQxODIzQzExLjIzNjEgMy41NTMwNSAxMS4zMzkgMy43MDg4OSAxMS40MTc4IDMuODg2MjhDMTEuNDkxNiA0LjA2NTQgMTEuNTI4NyA0LjI1NTk2IDExLjUyODcgNC40NTkwMkMxMS41Mjg3IDQuNjg3MjcgMTEuNTI0NyA0LjkxMTQ1IDExLjUxNjggNS4xMzE0MkMxMS41MDg4IDUuMzU4OTQgMTEuNTA4NyA1LjU4MDQ5IDExLjUxNjggNS43OTYwNUMxMS41MjA5IDYuMDE3NTQgMTEuNTM5MiA2LjIzMTE3IDExLjU3MTggNi40MzY4OEMxMS42MDQ5IDYuNjQ5NzYgMTEuNjYwOCA2Ljg1MjQzIDExLjczOTYgNy4wNDQ2NEMxMS44MTkxIDcuMjM4NDMgMTEuOTI3NCA3LjQyMjU0IDEyLjA2MzggNy41OTcwMkMxMi4xNzY1IDcuNzQxMTkgMTIuMzE0NiA3Ljg3NTM4IDEyLjQ3NzIgOEMxMi4zMTQ2IDguMTI0NjIgMTIuMTc2NSA4LjI1ODgxIDEyLjA2MzggOC40MDI5OEMxMS45Mjc0IDguNTc3NDYgMTEuODE5MSA4Ljc2MTU3IDExLjczOTYgOC45NTUzNkMxMS42NjA5IDkuMTQ3MzcgMTEuNjA0OSA5LjM0ODI1IDExLjU3MTcgOS41NTcxNUMxMS41MzkyIDkuNzY2NyAxMS41MjA5IDkuOTgwMjcgMTEuNTE2OCAxMC4xOTc4QzExLjUwODcgMTAuNDE3NCAxMS41MDg3IDEwLjYzODkgMTEuNTE2OCAxMC44NjI1QzExLjUyNDcgMTEuMDg2NiAxMS41Mjg3IDExLjMxMjggMTEuNTI4NyAxMS41NDFDMTEuNTI4NyAxMS43Mzk4IDExLjQ5MTcgMTEuOTI4NSAxMS40MTc2IDEyLjEwNzlDMTEuMzM4NiAxMi4yODk3IDExLjIzNTggMTIuNDQ1MiAxMS4xMDkxIDEyLjU3NTZDMTAuOTc4MSAxMi43MTA2IDEwLjgyNTQgMTIuODE3MyAxMC42NTIgMTIuODk0OUMxMC40NzYyIDEyLjk3NTcgMTAuMjg4OSAxMy4wMTY0IDEwLjA4OTIgMTMuMDE2NEgxMFoiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.property:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTIuODA3MjMgMTQuOTc1NEMyLjU3MTE5IDE0Ljk3MjEgMi4zMzgyNiAxNC45MjExIDIuMTIyNDcgMTQuODI1NEMxLjkwNjY3IDE0LjcyOTcgMS43MTI0OCAxNC41OTEzIDEuNTUxNTggMTQuNDE4NkMxLjIzODUgMTQuMTMzNCAxLjA0NDMzIDEzLjc0MDggMS4wMDc3NSAxMy4zMTg5QzAuOTY2MjI1IDEyLjg4MjggMS4wOTI2OSAxMi40NDczIDEuMzYxMzMgMTIuMTAxM0MyLjU2Nzc5IDEwLjgyODkgNC45NDczIDguNDQ5NCA2LjY3ODExIDYuNzU0NzlDNi4zMDk4MyA1Ljc1ODg3IDYuMzI3MDQgNC42NjEyNyA2LjcyNjM3IDMuNjc3MzlDNy4wNTQ3NCAyLjg1ODc2IDcuNjM4NjkgMi4xNjgwNSA4LjM5MTI5IDEuNzA4MDdDOC45ODE3IDEuMzE3MDYgOS42NjAzMSAxLjA3OTQ0IDEwLjM2NTcgMS4wMTY3M0MxMS4wNzExIDAuOTU0MDIyIDExLjc4MDkgMS4wNjgxOSAxMi40MzExIDEuMzQ4OTJMMTMuMDQ4MiAxLjYxNjJMMTAuMTgyNCA0LjU2NzM4TDExLjQzNzEgNS44MjU4MkwxNC4zODA5IDIuOTQ4ODdMMTQuNjQ4MiAzLjU2Nzg4QzE0Ljg3MzUgNC4wODk3NiAxNC45OTMgNC42NTExOSAxNC45OTk3IDUuMjE5NjFDMTUuMDA2NCA1Ljc4ODAyIDE0LjkwMDIgNi4zNTIxMSAxNC42ODcyIDYuODc5MTVDMTQuNDc2IDcuNDAwMjkgMTQuMTYyMyA3Ljg3MzY4IDEzLjc2NDcgOC4yNzEyMkMxMy41Mzk0IDguNDkxNjkgMTMuMjkwNCA4LjY4NjUzIDEzLjAyMjIgOC44NTIxOEMxMi40NjczIDkuMjIyNzUgMTEuODMyNCA5LjQ1NjM2IDExLjE2OTcgOS41MzM4QzEwLjUwNjkgOS42MTEyNCA5LjgzNTIxIDkuNTMwMyA5LjIwOTgyIDkuMjk3NjRDOC4xMTE5NCAxMC40MTEzIDUuMzcxNDIgMTMuMTcwNCAzLjg5MTE5IDE0LjU1MjJDMy41OTQyNiAxNC44MjE5IDMuMjA4MzIgMTQuOTcyNiAyLjgwNzIzIDE0Ljk3NTRaTTEwLjc0NDggMS45MjgwMkMxMC4wODcgMS45MjYzNyA5LjQ0MzU5IDIuMTIwMTggOC44OTYxNCAyLjQ4NDg1QzguNjgyNjUgMi42MTUyIDguNDg0MzcgMi43Njg5NyA4LjMwNDk4IDIuOTQzM0M3LjgyNzg5IDMuNDI0MjMgNy41MDkyNiA0LjAzOTUzIDcuMzkxODIgNC43MDY2OUM3LjI3NDM3IDUuMzczODUgNy4zNjM3NCA2LjA2MDk4IDcuNjQ3OTIgNi42NzU5MUw3Ljc4MzQyIDYuOTcyODhMNy41NTA0OCA3LjIwMDI1QzUuODEyMjQgOC44OTY3MiAzLjI4MTQ2IDExLjQyMDEgMi4wNjQ3OSAxMi43MDQ1QzEuOTU2NDYgMTIuODY1OCAxLjkxMDEyIDEzLjA2MDggMS45MzQzNSAxMy4yNTM1QzEuOTU4NTcgMTMuNDQ2MyAyLjA1MTcxIDEzLjYyMzggMi4xOTY1NyAxMy43NTMyQzIuMjgwMDUgMTMuODQ2MiAyLjM4MTc3IDEzLjkyMTEgMi40OTU0MSAxMy45NzMxQzIuNTk1NTcgMTQuMDE4NCAyLjcwMzgzIDE0LjA0MyAyLjgxMzczIDE0LjA0NTVDMi45ODA2NCAxNC4wNDEzIDMuMTQwNDQgMTMuOTc3IDMuMjYzODMgMTMuODY0NkM0LjgzNjg3IDEyLjM5NjQgNy44NzYyMiA5LjMyNjQxIDguNzY4MDcgOC40MjQzNUw4Ljk5NzMgOC4xOTMyNkw5LjI5MjQyIDguMzI3ODNDOS44MDYxOCA4LjU2NzMyIDEwLjM3MzEgOC42Njk4NSAxMC45MzgyIDguNjI1NDVDMTEuNTAzMyA4LjU4MTA2IDEyLjA0NzMgOC4zOTEyNSAxMi41MTc0IDguMDc0NDdDMTIuNzMxMyA3Ljk0MjYgMTIuOTI5NiA3Ljc4Njk0IDEzLjEwODUgNy42MTA0NUMxMy40MTgzIDcuMzAxNTMgMTMuNjYzMSA2LjkzMzc0IDEzLjgyODYgNi41Mjg3NEMxMy45OTQgNi4xMjM3NSAxNC4wNzY3IDUuNjg5NzQgMTQuMDcxOSA1LjI1MjI4QzE0LjA3MTkgNS4wMzY2MiAxNC4wNTA1IDQuODIxNDggMTQuMDA3OCA0LjYxMDA3TDExLjQzMDYgNy4xMjUwOEw4Ljg3OTQ0IDQuNTc3NTlMMTEuMzk0NCAxLjk4ODM0QzExLjE4MDQgMS45NDY3NCAxMC45NjI4IDEuOTI2NTMgMTAuNzQ0OCAxLjkyODAyWiIgZmlsbD0iIzQyNDI0MiIvPgo8L3N2Zz4K")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.unit:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00IDFMMyAyVjE0TDQgMTVIMTJMMTMgMTRWMkwxMiAxSDRaTTQgM1YySDEyVjE0SDRWMTNINlYxMkg0VjEwSDhWOUg0VjdINlY2SDRWNEg4VjNINFoiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constant:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00IDZIMTJWN0g0VjZaTTEyIDlINFYxMEgxMlY5WiIgZmlsbD0iIzQyNDI0MiIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTEgNEwyIDNIMTRMMTUgNFYxMkwxNCAxM0gyTDEgMTJWNFpNMiA0VjEySDE0VjRIMloiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum:before,.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.value:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04IDJMNyAzVjZIOFYzSDE0VjhIMTBWOUgxNEwxNSA4VjNMMTQgMkg4Wk05IDhMOCA3SDdIMkwxIDhWMTNMMiAxNEg4TDkgMTNWOVY4Wk04IDlWOEg3SDJWMTNIOFY5Wk05IDYuNTg1NzlMOS40MTQyMSA3SDEzVjZIOVY2LjU4NTc5Wk0xMyA0SDlWNUgxM1Y0Wk03IDlIM1YxMEg3VjlaTTMgMTFIN1YxMkgzVjExWiIgZmlsbD0iI0Q2N0UwMCIvPgo8L3N2Zz4K")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum-member:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03IDNMOCAySDE0TDE1IDNWOEwxNCA5SDEwVjhIMTRWM0g4VjZIN1YzWk04IDdMOSA4VjlWMTNMOCAxNEgyTDEgMTNWOEwyIDdIN0g4Wk04IDhWOVYxM0gyVjhIN0g4Wk05LjQxNDIxIDdMOSA2LjU4NTc5VjZIMTNWN0g5LjQxNDIxWk05IDRIMTNWNUg5VjRaTTcgMTBIM1YxMUg3VjEwWiIgZmlsbD0iIzAwN0FDQyIvPgo8L3N2Zz4K")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.keyword:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE1IDRIMTBWM0gxNVY0Wk0xNCA3SDEyVjhIMTRWN1pNMTAgN0gxVjhIMTBWN1pNMTIgMTNIMVYxNEgxMlYxM1pNNyAxMEgxVjExSDdWMTBaTTE1IDEwSDEwVjExSDE1VjEwWk04IDJWNUgxVjJIOFpNNyAzSDJWNEg3VjNaIiBmaWxsPSIjNDI0MjQyIi8+Cjwvc3ZnPgo=")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.text:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03LjIyMjg5IDEwLjkzM0M3LjU0ODYzIDExLjEyNTQgNy45MjE2MyAxMS4yMjMxIDguMjk5ODkgMTEuMjE1QzguNjM3NzcgMTEuMjIxOCA4Ljk3MjU0IDExLjE0OTIgOS4yNzcyMSAxMS4wMDNDOS41ODE4OCAxMC44NTY3IDkuODQ3OTIgMTAuNjQwOSAxMC4wNTM5IDEwLjM3M0MxMC41MDkxIDkuNzY1MTkgMTAuNzQwMiA5LjAxODY3IDEwLjcwNzkgOC4yNTk5OEMxMC43NDEyIDcuNTg2MjIgMTAuNTM3NCA2LjkyMjEgMTAuMTMxOSA2LjM4Mjk4QzkuOTM1NzUgNi4xNDE2MSA5LjY4NTc3IDUuOTQ5NTcgOS40MDIgNS44MjIyOEM5LjExODI0IDUuNjk0OTggOC44MDg1OCA1LjYzNTk3IDguNDk3ODkgNS42NDk5N0M4LjA3NTIyIDUuNjQ2OTkgNy42NTk5NCA1Ljc2MDg1IDcuMjk3ODkgNS45Nzg5OEM3LjE4MzA0IDYuMDQ4MDcgNy4wNzQ5IDYuMTI3NzUgNi45NzQ4OSA2LjIxNjk4VjMuNDc0OThINS45ODM4OVYxMS4xSDYuOTc4ODlWMTAuNzU2QzcuMDU1MTYgMTAuODIxNyA3LjEzNjc3IDEwLjg4MDkgNy4yMjI4OSAxMC45MzNaTTcuODQ5ODEgNi43MDAwNkM4LjAzNTk4IDYuNjIxMDUgOC4yMzgwNyA2LjU4Njc3IDguNDM5ODkgNi41OTk5OEM4LjYxMjU3IDYuNTk0NTIgOC43ODQwNCA2LjYzMDU0IDguOTM5OTQgNi43MDUwMUM5LjA5NTgzIDYuNzc5NDggOS4yMzE2MSA2Ljg5MDIzIDkuMzM1ODkgNy4wMjc5OEM5LjU5MjUzIDcuMzkwNTMgOS43MTg0IDcuODI5NTEgOS42OTI4OSA4LjI3Mjk3QzkuNzE5NzIgOC43OTc0OCA5LjU3OTY5IDkuMzE3MDEgOS4yOTI4OSA5Ljc1Njk4QzkuMTg4MjIgOS45MTUyNyA5LjA0NTQ2IDEwLjA0NDcgOC44Nzc3MyAxMC4xMzM1QzguNzA5OTkgMTAuMjIyMyA4LjUyMjY0IDEwLjI2NzUgOC4zMzI4OSAxMC4yNjVDOC4xNDkzNCAxMC4yNzMyIDcuOTY2MyAxMC4yNCA3Ljc5NzM0IDEwLjE2NzhDNy42MjgzOCAxMC4wOTU2IDcuNDc3ODQgOS45ODYyOCA3LjM1Njg5IDkuODQ3OTdDNy4xMDE1MiA5LjU1OTU3IDYuOTY1MDEgOS4xODUwNiA2Ljk3NDg5IDguNzk5OThWOC4xOTk5OEM2Ljk2Mjk5IDcuNzgzMzIgNy4xMDI2MyA3LjM3NjUgNy4zNjc4OSA3LjA1NDk4QzcuNDk4NTggNi45MDA2NCA3LjY2MzY0IDYuNzc5MDggNy44NDk4MSA2LjcwMDA2Wk0zLjI4OTAyIDUuNjc0OTlDMi45NzAxMSA1LjY3OTMzIDIuNjUzODggNS43MzQgMi4zNTIwMiA1LjgzNjk5QzIuMDY0MTcgNS45MjI5MyAxLjc5MzQ3IDYuMDU4MjggMS41NTIwMiA2LjIzNjk5TDEuNDUyMDIgNi4zMTM5OVY3LjUxMzk5TDEuODc1MDIgNy4xNTQ5OUMyLjI0NTc5IDYuODA0NzggMi43MzEzMyA2LjYwMTQ2IDMuMjQxMDIgNi41ODI5OUMzLjM2NTkzIDYuNTcxNjQgMy40OTE3IDYuNTkxNDcgMy42MDcwNiA2LjY0MDY4QzMuNzIyNDMgNi42ODk5IDMuODIzNzcgNi43NjY5NyAzLjkwMjAyIDYuODY0OTlDNC4wNTIyIDcuMDk3MSA0LjEzMjM5IDcuMzY3NTQgNC4xMzMwMiA3LjY0Mzk5TDIuOTAwMDIgNy44MjQ5OUMyLjM5NDM1IDcuODc3ODEgMS45MTUyNSA4LjA3NzcyIDEuNTIyMDIgOC4zOTk5OUMxLjM2Njk3IDguNTUxODEgMS4yNDMzOSA4LjczMjcxIDEuMTU4MzUgOC45MzIzNUMxLjA3MzMxIDkuMTMxOTkgMS4wMjg0OCA5LjM0NjQ0IDEuMDI2NDQgOS41NjM0M0MxLjAyNDQgOS43ODA0MiAxLjA2NTE3IDkuOTk1NjggMS4xNDY0NCAxMC4xOTY5QzEuMjI3NyAxMC4zOTgxIDEuMzQ3ODYgMTAuNTgxMyAxLjUwMDAyIDEwLjczNkMxLjY2ODcgMTAuODkwNCAxLjg2NjIyIDExLjAxIDIuMDgxMjUgMTEuMDg3OUMyLjI5NjI3IDExLjE2NTkgMi41MjQ1NiAxMS4yMDA1IDIuNzUzMDIgMTEuMTlDMy4xNDcgMTEuMTkzMSAzLjUzMjc4IDExLjA3NzQgMy44NjAwMiAxMC44NThDMy45NjE1MyAxMC43ODk3IDQuMDU3MiAxMC43MTMxIDQuMTQ2MDIgMTAuNjI5VjExLjA3M0g1LjA4NzAyVjcuNzE0OTlDNS4xMjEzNyA3LjE3NDIyIDQuOTU0MyA2LjYzOTg4IDQuNjE4MDIgNi4yMTQ5OUM0LjQ0OTc5IDYuMDMyODUgNC4yNDM0OCA1Ljg5MDAzIDQuMDEzNzggNS43OTY3QzMuNzg0MDcgNS43MDMzNiAzLjUzNjYxIDUuNjYxODEgMy4yODkwMiA1LjY3NDk5Wk00LjE0NjAyIDguNzE1OTlDNC4xNjU2NCA5LjEzNDM1IDQuMDI1OTIgOS41NDQ1OSAzLjc1NTAyIDkuODY0QzMuNjM2ODkgMTAuMDAwNSAzLjQ4OTk4IDEwLjEwOTIgMy4zMjQ4NiAxMC4xODIxQzMuMTU5NzMgMTAuMjU1MSAyLjk4MDQ5IDEwLjI5MDYgMi44MDAwMiAxMC4yODZDMi42OTA0OSAxMC4yOTQ1IDIuNTgwMzUgMTAuMjgxMiAyLjQ3NTk5IDEwLjI0NjlDMi4zNzE2MyAxMC4yMTI1IDIuMjc1MTEgMTAuMTU3OSAyLjE5MjAyIDEwLjA4NkMyLjA2MDc5IDkuOTM0NTUgMS45ODg1NiA5Ljc0MDg4IDEuOTg4NTYgOS41NDA0OUMxLjk4ODU2IDkuMzQwMTEgMi4wNjA3OSA5LjE0NjQ0IDIuMTkyMDIgOC45OTQ5OUMyLjQ3MzIyIDguODIxMzEgMi43OTIzMyA4LjcxODM3IDMuMTIyMDIgOC42OTQ5OUw0LjE0MjAyIDguNTQ2OTlMNC4xNDYwMiA4LjcxNTk5Wk0xMi40NTg4IDExLjAzMjVDMTIuNzY2IDExLjE2MzggMTMuMDk4MyAxMS4yMjYxIDEzLjQzMjIgMTEuMjE1QzEzLjkyNyAxMS4yMjcgMTQuNDE1MyAxMS4xMDA2IDE0Ljg0MjIgMTAuODVMMTQuOTY1MiAxMC43NzVMMTQuOTc4MiAxMC43NjhWOS42MTUwNEwxNC41MzIyIDkuOTM1MDRDMTQuMjE2IDEwLjE1OTIgMTMuODM1NiAxMC4yNzQ3IDEzLjQ0ODIgMTAuMjY0QzEzLjI0OTcgMTAuMjcxOSAxMy4wNTIgMTAuMjM0MiAxMi44NzAzIDEwLjE1MzhDMTIuNjg4NiAxMC4wNzMzIDEyLjUyNzggOS45NTIzMiAxMi40MDAyIDkuODAwMDRDMTIuMTE0NCA5LjQyNDUzIDExLjk3MjUgOC45NTkxMSAxMi4wMDAyIDguNDg4MDRDMTEuOTczNyA3Ljk4NzMyIDEyLjEzNTIgNy40OTQ3NSAxMi40NTMyIDcuMTA3MDRDMTIuNTkzNCA2Ljk0MTA1IDEyLjc2OTUgNi44MDkxNCAxMi45NjgyIDYuNzIxM0MxMy4xNjcgNi42MzM0NiAxMy4zODMxIDYuNTkyIDEzLjYwMDIgNi42MDAwNEMxMy45NDM5IDYuNTk4NDQgMTQuMjgwOCA2LjY5NTI1IDE0LjU3MTIgNi44NzkwNEwxNS4wMDAyIDcuMTQ0MDRWNS45NzAwNEwxNC44MzEyIDUuODk3MDRDMTQuNDYyNiA1LjczNDMyIDE0LjA2NDEgNS42NTAyIDEzLjY2MTIgNS42NTAwNEMxMy4yOTk5IDUuNjM5OTEgMTIuOTQwNiA1LjcwNzYyIDEyLjYwNzggNS44NDg1OUMxMi4yNzQ5IDUuOTg5NTYgMTEuOTc2MyA2LjIwMDQ4IDExLjczMjIgNi40NjcwNEMxMS4yMjYxIDcuMDI2ODMgMTAuOTU4MSA3Ljc2MTg2IDEwLjk4NTIgOC41MTYwNEMxMC45NTY3IDkuMjIzNDYgMTEuMTk1NSA5LjkxNTY5IDExLjY1NDIgMTAuNDU1QzExLjg3NjkgMTAuNzA0IDEyLjE1MTYgMTAuOTAxMiAxMi40NTg4IDExLjAzMjVaIiBmaWxsPSIjNDI0MjQyIi8+Cjwvc3ZnPgo=")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.color:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik02LjYgMTEuNzY3M0M3LjMyODcxIDExLjAzODMgNy4zMjg3MSA5Ljg1NjM4IDYuNiA5LjEyNzQyQzUuODcwODMgOC4zOTg0NSA0LjY4OTEzIDguMzk4NDUgMy45NTk5NiA5LjEyNzQyQzIuOTE4MTIgMTAuMjI0OSAxLjEwNDExIDkuOTM5MDEgMS4wMTI5IDguNDI4NTNDMS4wMDQzNCA4LjI4Njc5IDEgOC4xNDM5IDEgOEMxIDQuMTMzOTkgNC4xMzQwNSAxIDggMUMxMS44NjYgMSAxNSA0LjEzMzk5IDE1IDhDMTUgMTEuODY2IDExLjg2NiAxNSA4IDE1QzcuNzk3NzQgMTUgNy41OTc0OSAxNC45OTE0IDcuMzk5NTkgMTQuOTc0NkM1LjgwNjQ2IDE0LjgzOTMgNS40OTg2IDEyLjkyNjMgNi42IDExLjc2NzNaTTYuOTY2NzUgMTMuNTQzNEM2LjkwMjUxIDEzLjI0NjQgNi45ODgyMSAxMi44MTUgNy4zMTgxOSAxMi40NjMyQzguNDI2MjkgMTEuMzQyOSA4LjQyMjY0IDkuNTM2MjMgNy4zMDcyMyA4LjQyMDQzTDcuMzA3MDEgOC40MjAyMUM2LjE4NzM0IDcuMzAwODUgNC4zNzI2MiA3LjMwMDg1IDMuMjUyOTUgOC40MjAyMUwzLjI0MzcxIDguNDI5NDVMMy4yMzQ3MSA4LjQzODkzQzIuOTM5MDMgOC43NTA0IDIuNTcwOTEgOC44MjU5OSAyLjMzMjUgOC43NzY2OEMyLjIxOTM2IDguNzUzMjggMi4xNTAyMSA4LjcwNjY3IDIuMTA5NzkgOC42NjE2MUMyLjA3MzkzIDguNjIxNjIgMi4wMjE0NiA4LjU0MDE2IDIuMDExMDggOC4zNjgyNkMyLjAwMzczIDguMjQ2NTYgMiA4LjEyMzc4IDIgOEMyIDQuNjg2MjggNC42ODYzMyAyIDggMkMxMS4zMTM3IDIgMTQgNC42ODYyOCAxNCA4QzE0IDExLjMxMzcgMTEuMzEzNyAxNCA4IDE0QzcuODI2MDIgMTQgNy42NTQgMTMuOTkyNiA3LjQ4NDIxIDEzLjk3ODJDNy4yODk5MiAxMy45NjE3IDcuMTg0ODggMTMuODk4NyA3LjEyNDE0IDEzLjg0MjlDNy4wNTgzMSAxMy43ODI0IDYuOTk3NjcgMTMuNjg2NCA2Ljk2Njc1IDEzLjU0MzRaTTUgNkM1LjU1MjI4IDYgNiA1LjU1MjI4IDYgNUM2IDQuNDQ3NzIgNS41NTIyOCA0IDUgNEM0LjQ0NzcyIDQgNCA0LjQ0NzcyIDQgNUM0IDUuNTUyMjggNC40NDc3MiA2IDUgNlpNMTIgMTFDMTIgMTEuNTUyMyAxMS41NTIzIDEyIDExIDEyQzEwLjQ0NzcgMTIgMTAgMTEuNTUyMyAxMCAxMUMxMCAxMC40NDc3IDEwLjQ0NzcgMTAgMTEgMTBDMTEuNTUyMyAxMCAxMiAxMC40NDc3IDEyIDExWk04IDVDOC41NTIyOCA1IDkgNC41NTIyOCA5IDRDOSAzLjQ0NzcyIDguNTUyMjggMyA4IDNDNy40NDc3MiAzIDcgMy40NDc3MiA3IDRDNyA0LjU1MjI4IDcuNDQ3NzIgNSA4IDVaTTEzIDhDMTMgOC41NTIyOCAxMi41NTIzIDkgMTIgOUMxMS40NDc3IDkgMTEgOC41NTIyOCAxMSA4QzExIDcuNDQ3NzIgMTEuNDQ3NyA3IDEyIDdDMTIuNTUyMyA3IDEzIDcuNDQ3NzIgMTMgOFpNMTEgNkMxMS41NTIzIDYgMTIgNS41NTIyOCAxMiA1QzEyIDQuNDQ3NzIgMTEuNTUyMyA0IDExIDRDMTAuNDQ3NyA0IDEwIDQuNDQ3NzIgMTAgNUMxMCA1LjU1MjI4IDEwLjQ0NzcgNiAxMSA2WiIgZmlsbD0iIzQyNDI0MiIvPgo8L3N2Zz4K")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.file:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00IDFMMyAyVjE0TDQgMTVIMTNMMTQgMTRWNUwxMy43MDcxIDQuMjkyODlMMTAuNzA3MSAxLjI5Mjg5TDEwIDFINFpNNCAxNFYyTDkgMlY2SDEzVjE0SDRaTTEzIDVMMTAgMlY1TDEzIDVaIiBmaWxsPSIjNDI0MjQyIi8+Cjwvc3ZnPgo=")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.reference:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04LjA2MDY1IDMuODUzNTZMNS45MTQyMSA2TDUuMjA3MSA1LjI5Mjg5TDYuNDk5OTkgNEgzLjVDMy4xMDIxOCA0IDIuNzIwNjQgNC4xNTgwNCAyLjQzOTM0IDQuNDM5MzRDMi4xNTgwNCA0LjcyMDY1IDIgNS4xMDIxOCAyIDUuNUMyIDUuODk3ODMgMi4xNTgwNCA2LjI3OTM2IDIuNDM5MzQgNi41NjA2NkMyLjcyMDY0IDYuODQxOTcgMy4xMDIxOCA3IDMuNSA3SDRWOEgzLjVDMi44MzY5NiA4IDIuMjAxMDcgNy43MzY2MSAxLjczMjIzIDcuMjY3NzdDMS4yNjMzOSA2Ljc5ODkzIDEgNi4xNjMwNSAxIDUuNUMxIDQuODM2OTYgMS4yNjMzOSA0LjIwMTA4IDEuNzMyMjMgMy43MzIyNEMyLjIwMTA3IDMuMjYzNCAyLjgzNjk2IDMgMy41IDNINi40OTk5OUw2LjQ5OTk5IDNINi40OTk5Nkw2IDIuNTAwMDRWMi41MDAwMUw1LjIwNzEgMS43MDcxMUw1LjkxNDIxIDFMOC4wNjA2NSAzLjE0NjQ1TDguMDYwNjUgMy44NTM1NlpNNSA2LjUwMDAzTDUuOTE0MjEgNy40MTQyNEw2IDcuMzI4NDVWMTRIMTRWN0gxMFYzSDkuMDYwNjVWMi43MzIyN0w4LjMyODM4IDJIMTEuMkwxMS41IDIuMUwxNC45IDUuNkwxNSA2VjE0LjVMMTQuNSAxNUg1LjVMNSAxNC41VjkuMDAwMDNWNi41MDAwM1pNMTEgM1Y2SDEzLjkwMzJMMTEgM1oiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.snippet:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yLjUgMUwyIDEuNVYxM0gzVjJIMTRWMTNIMTVWMS41TDE0LjUgMUgyLjVaTTIgMTVWMTRIM1YxNUgyWk01IDE0LjAwMDFINFYxNS4wMDAxSDVWMTQuMDAwMVpNNiAxNC4wMDAxSDdWMTUuMDAwMUg2VjE0LjAwMDFaTTkgMTQuMDAwMUg4VjE1LjAwMDFIOVYxNC4wMDAxWk0xMCAxNC4wMDAxSDExVjE1LjAwMDFIMTBWMTQuMDAwMVpNMTUgMTUuMDAwMVYxNC4wMDAxSDE0VjE1LjAwMDFIMTVaTTEyIDE0LjAwMDFIMTNWMTUuMDAwMUgxMlYxNC4wMDAxWiIgZmlsbD0iIzQyNDI0MiIvPgo8L3N2Zz4K")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.customcolor:before{background-image:none}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.folder:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xLjAxMDg3IDIuNUwxLjUxMDg3IDJINi41MDcxM0w2Ljg2MDY4IDIuMTQ2NDVMNy43MTM0OSAyLjk5OTI1SDE0LjUwMTFMMTUuMDAxMSAzLjQ5OTI1VjguOTk1MTJMMTQuOTkwMyA5LjAwNTk5VjEzLjUwMjFMMTQuNDkwMyAxNC4wMDIxSDEuNUwxIDEzLjUwMjFWNi41MDczNUwxLjAxMDg3IDYuNDk2NDhWMi41Wk0xNC4wMDExIDMuOTk5MjVWNS4wMDMxMUg3LjUwMDVMNy4xNDY5NSA1LjE0OTU2TDYuMjg5MTUgNi4wMDczNUgyLjAxMDg3VjNINi4zMDAwMkw3LjE1MjgzIDMuODUyOEw3LjUwNjM4IDMuOTk5MjVIMTQuMDAxMVpNNi40OTYyNiA3LjAwNzM1SDIuMDEwODdWNy40OTU4OEgxLjk5OTYzVjExLjQ5MjlIMlYxMy4wMDIxSDEzLjk5MDNWMTEuNDkyOUgxMy45OTA2VjcuNDk1ODhIMTMuOTkwM1Y2LjAwMzExSDcuNzA3NjFMNi44NDk4MSA2Ljg2MDlMNi40OTYyNiA3LjAwNzM1WiIgZmlsbD0iIzQyNDI0MiIvPgo8L3N2Zz4K")}.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.customcolor .colorspan{margin:0 0 0 .3em;border:.1em solid #000;width:.7em;height:.7em;display:inline-block}.monaco-editor .suggest-widget .details{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;cursor:default}.monaco-editor .suggest-widget .details.no-docs{display:none}.monaco-editor .suggest-widget.docs-below .details{border-top-width:0}.monaco-editor .suggest-widget .details>.monaco-scrollable-element{-ms-flex:1;flex:1}.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body{position:absolute;box-sizing:border-box;height:100%;width:100%}.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.header>.type{-ms-flex:2;flex:2;overflow:hidden;text-overflow:ellipsis;opacity:.7;word-break:break-all;margin:0 24px 0 0;padding:4px 0 12px 5px}.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.docs{margin:0;padding:4px 5px;white-space:pre-wrap}.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.docs.markdown-docs{padding:0;white-space:normal}.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.docs.markdown-docs>div,.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.docs.markdown-docs>span:not(:empty){padding:4px 5px}.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.docs.markdown-docs>div>p:first-child{margin-top:0}.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.docs.markdown-docs>div>p:last-child{margin-bottom:0}.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>.docs .code{white-space:pre-wrap;word-wrap:break-word}.monaco-editor .suggest-widget .details>.monaco-scrollable-element>.body>p:empty{display:none}.monaco-editor .suggest-widget .details code{border-radius:3px;padding:0 .4em}.monaco-editor.hc-black .suggest-widget .details>.monaco-scrollable-element>.body>.header>.close,.monaco-editor.vs-dark .suggest-widget .details>.monaco-scrollable-element>.body>.header>.close{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03Ljk5OTk4IDguNzA3MDhMMTEuNjQ2NCAxMi4zNTM1TDEyLjM1MzUgMTEuNjQ2NEw4LjcwNzA4IDcuOTk5OThMMTIuMzUzNSA0LjM1MzUzTDExLjY0NjQgMy42NDY0Mkw3Ljk5OTk4IDcuMjkyODdMNC4zNTM1MyAzLjY0NjQyTDMuNjQ2NDIgNC4zNTM1M0w3LjI5Mjg3IDcuOTk5OThMMy42NDY0MiAxMS42NDY0TDQuMzUzNTMgMTIuMzUzNUw3Ljk5OTk4IDguNzA3MDhaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.readMore,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row>.contents>.main>.readMore{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zIDcuNUMzIDQuNDYyNDMgNS40NjI0MyAyIDguNSAyQzExLjUzNzYgMiAxNCA0LjQ2MjQzIDE0IDcuNUMxNCAxMC41Mzc2IDExLjUzNzYgMTMgOC41IDEzQzUuNDYyNDMgMTMgMyAxMC41Mzc2IDMgNy41Wk0yIDcuNUMyIDMuOTEwMTUgNC45MTAxNSAxIDguNSAxQzEyLjA4OTkgMSAxNSAzLjkxMDE1IDE1IDcuNUMxNSAxMS4wODk5IDEyLjA4OTkgMTQgOC41IDE0QzQuOTEwMTUgMTQgMiAxMS4wODk5IDIgNy41Wk04IDRWNUg5VjRIOFpNOCA2TDggMTBIOUw5IDZIOFoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constructor:before,.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.function:before,.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.method:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constructor:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.function:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.method:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yIDQuODU3NDlMMi40ODU1IDRMNy40ODU1IDFIOC41MTQ1TDEzLjUxNDUgNEwxNCA0Ljg1NzQ5VjEwLjg1NzVMMTMuNTE0NSAxMS43MTVMOC41MTQ1IDE0LjcxNUg3LjQ4NTVMMi40ODU1IDExLjcxNUwyIDEwLjg1NzVWNC44NTc0OVpNNy41IDEzLjU1NzVMMyAxMC44NTc1VjUuNjk5NzVMNy41IDguMTU0M1YxMy41NTc1Wk04LjUgMTMuNTU3NUwxMyAxMC44NTc1VjUuNjk5NzVMOC41IDguMTU0M1YxMy41NTc1Wk04IDEuODU3NDlMMy4yNTkxMyA0LjcwMjAxTDggNy4yODc5NEwxMi43NDA5IDQuNzAyMDFMOCAxLjg1NzQ5WiIgZmlsbD0iI0IxODBENyIvPgo8L3N2Zz4K")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.field:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.field:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xIDYuMzk0NDNMMS41NTI3OSA1LjVMOC41NTI3OSAySDkuNDQ3MjFMMTQuNDQ3MiA0LjVMMTUgNS4zOTQ0M1Y5Ljg5NDQzTDE0LjQ0NzIgMTAuNzg4OUw3LjQ0NzIxIDE0LjI4ODlINi41NTI3OUwxLjU1Mjc5IDExLjc4ODlMMSAxMC44OTQ0VjYuMzk0NDNaTTYuNSAxMy4xNDQ0TDIgMTAuODk0NFY3LjE3MDk0TDYuNSA5LjIxNjM5VjEzLjE0NDRaTTcuNSAxMy4xNDQ0TDE0IDkuODk0NDNWNi4xNzk1NEw3LjUgOS4yMTI4N1YxMy4xNDQ0Wk05IDIuODk0NDNMMi4zMzcyOCA2LjIyNTc5TDYuOTk3MjUgOC4zNDM5NkwxMy42NzA2IDUuMjI5NzNMOSAyLjg5NDQzWiIgZmlsbD0iIzc1QkVGRiIvPgo8L3N2Zz4K")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.event:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.event:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03LjQxMzU0IDEuNTU5OTZMOC4zMTE1MiAxSDExLjYwNTZMMTIuNDI0IDIuNTc0NjVMMTAuMjM1NiA2SDEyLjAxNzRMMTIuNzM2MyA3LjY5NTEyTDUuNjE5NDMgMTVMNC4wMTY3NSAxMy44MzdMNi4xMTk0MyAxMEg0Ljg5Nzk4TDQgOC41NTk5Nkw3LjQxMzU0IDEuNTU5OTZaTTcuNzgwMzMgOUw0LjkwMDU0IDE0LjMwNDlMMTIuMDE3NCA3SDguMzExNTJMMTEuNjA1NiAySDguMzExNTJMNC44OTc5OCA5SDcuNzgwMzNaIiBmaWxsPSIjRDY3RTAwIi8+Cjwvc3ZnPgo=")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.operator:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.operator:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yLjg3Mjg5IDEuMTAwMjNDMy4yMDc2OCAxLjIzNTc5IDMuNDc1NDUgMS40OTggMy42MTgwMiAxLjgyOTg4QzMuNjkwMzIgMS45OTk1OSAzLjcyNjc1IDIuMTgyNDIgMy43MjUwMiAyLjM2Njg4QzMuNzI2MTcgMi41NDk5OSAzLjY4OTc1IDIuNzMxNCAzLjYxODAyIDIuODk5ODhDMy41MTI5OSAzLjE0NTY3IDMuMzM3ODIgMy4zNTUwMyAzLjExNDQyIDMuNTAxNzdDMi44OTEwMiAzLjY0ODUxIDIuNjI5MyAzLjcyNjEyIDIuMzYyMDIgMy43MjQ4OEMyLjE3OTI0IDMuNzI1OTIgMS45OTgxOCAzLjY4OTUxIDEuODMwMDIgMy42MTc4OEMxLjU4Mjk4IDMuNTE0MDYgMS4zNzIyNyAzLjMzOTMyIDEuMjI0NTMgMy4xMTU3NUMxLjA3NjggMi44OTIxOSAwLjk5ODY2NiAyLjYyOTg0IDEuMDAwMDIgMi4zNjE4OEMwLjk5OTEzIDIuMTc5MjEgMS4wMzUxOSAxLjk5ODI1IDEuMTA2MDIgMS44Mjk4OEMxLjI0MzM3IDEuNTAzMTQgMS41MDMyOCAxLjI0MzIzIDEuODMwMDIgMS4xMDU4OEMyLjE2MzMyIDAuOTY2NjkyIDIuNTM4MDkgMC45NjQ2NjEgMi44NzI4OSAxLjEwMDIzWk0yLjU3NTAyIDIuODY0ODhDMi43MDU0IDIuODA5MTMgMi44MDkyNyAyLjcwNTI2IDIuODY1MDIgMi41NzQ4OEMyLjg5MjkgMi41MDgzOCAyLjkwNzE4IDIuNDM2OTggMi45MDcwMiAyLjM2NDg4QzIuOTA4MTMgMi4yNjU0IDIuODgyMTUgMi4xNjc1IDIuODMxODUgMi4wODE2N0MyLjc4MTU2IDEuOTk1ODQgMi43MDg4NCAxLjkyNTMxIDIuNjIxNTEgMS44Nzc2N0MyLjUzNDE4IDEuODMwMDIgMi40MzU1MyAxLjgwNzA1IDIuMzM2MTQgMS44MTEyMUMyLjIzNjc0IDEuODE1MzcgMi4xNDAzNSAxLjg0NjUgMi4wNTczMSAxLjkwMTI4QzEuOTc0MjYgMS45NTYwNiAxLjkwNzcgMi4wMzI0MSAxLjg2NDc1IDIuMTIyMTVDMS44MjE4IDIuMjExODggMS44MDQwOSAyLjMxMTYxIDEuODEzNTIgMi40MTA2NUMxLjgyMjk0IDIuNTA5NjggMS44NTkxNSAyLjYwNDI4IDEuOTE4MjUgMi42ODQzQzEuOTc3MzYgMi43NjQzMyAyLjA1NzEzIDIuODI2NzUgMi4xNDkwMiAyLjg2NDg4QzIuMjg1NDkgMi45MjA4OSAyLjQzODU0IDIuOTIwODkgMi41NzUwMiAyLjg2NDg4Wk02LjQyOTk1IDEuMTA5NUwxLjEwOTY3IDYuNDI5NzdMMS43OTU1NyA3LjExNTY3TDcuMTE1ODQgMS43OTU0TDYuNDI5OTUgMS4xMDk1Wk0xMS41IDguOTk5OTlIMTIuNVYxMS41SDE1VjEyLjVIMTIuNVYxNUgxMS41VjEyLjVIOVYxMS41SDExLjVWOC45OTk5OVpNNS43Njc3NyA5LjUyNTA5TDYuNDc0ODcgMTAuMjMyMkw0LjcwNzExIDEyTDYuNDc0ODcgMTMuNzY3N0w1Ljc2Nzc3IDE0LjQ3NDhMNCAxMi43MDcxTDIuMjMyMjMgMTQuNDc0OEwxLjUyNTEzIDEzLjc2NzdMMy4yOTI4OSAxMkwxLjUyNTEzIDEwLjIzMjJMMi4yMzIyMyA5LjUyNTA5TDQgMTEuMjkyOUw1Ljc2Nzc3IDkuNTI1MDlaTTcuMTE4MDIgNS4zMjk4OEM3LjAxNDQyIDUuMDgyNjggNi44Mzk3MyA0Ljg3MTgzIDYuNjE2MTIgNC43MjQwNkM2LjM5MjUgNC41NzYyOSA2LjEzMDA0IDQuNDk4MjYgNS44NjIwMiA0LjQ5OTg4QzUuNjc5MzUgNC40OTg5OSA1LjQ5ODM5IDQuNTM1MDUgNS4zMzAwMiA0LjYwNTg4QzUuMDAzMjggNC43NDMyMyA0Ljc0MzM3IDUuMDAzMTQgNC42MDYwMiA1LjMyOTg4QzQuNTM1ODggNS40OTQ3OCA0LjQ5ODk3IDUuNjcxOTEgNC40OTc0MSA1Ljg1MTFDNC40OTU4NiA2LjAzMDMgNC41Mjk2NyA2LjIwODA0IDQuNTk2OTMgNi4zNzQxNEM0LjY2NDE5IDYuNTQwMjQgNC43NjM1NiA2LjY5MTQzIDQuODg5MzYgNi44MTkwNkM1LjAxNTE2IDYuOTQ2NjkgNS4xNjQ5IDcuMDQ4MjMgNS4zMzAwMiA3LjExNzg4QzUuNDk4NjcgNy4xODg0OCA1LjY3OTY4IDcuMjI0ODQgNS44NjI1MiA3LjIyNDg0QzYuMDQ1MzUgNy4yMjQ4NCA2LjIyNjM2IDcuMTg4NDggNi4zOTUwMiA3LjExNzg4QzYuNjQyMDEgNy4wMTM4OCA2Ljg1MjcgNi44MzkxMyA3LjAwMDU4IDYuNjE1NjNDNy4xNDg0NSA2LjM5MjEzIDcuMjI2ODkgNi4xMjk4NyA3LjIyNjAyIDUuODYxODhDNy4yMjY1NSA1LjY3OTA1IDcuMTg5OCA1LjQ5ODAzIDcuMTE4MDIgNS4zMjk4OFpNNi4zNjUwMiA2LjA3NDg4QzYuMzM3NjYgNi4xMzkzNyA2LjI5ODI5IDYuMTk4MDggNi4yNDkwMiA2LjI0Nzg4QzYuMTk5MDggNi4yOTcyNCA2LjE0MDQyIDYuMzM2OTEgNi4wNzYwMiA2LjM2NDg4QzYuMDA4NTQgNi4zOTI5NyA1LjkzNjExIDYuNDA3MjUgNS44NjMwMiA2LjQwNjg4QzUuNzg5OTEgNi40MDc0NCA1LjcxNzQ0IDYuMzkzMTUgNS42NTAwMiA2LjM2NDg4QzUuNTg1NDEgNi4zMzcyOSA1LjUyNjY4IDYuMjk3NTcgNS40NzcwMiA2LjI0Nzg4QzUuNDI2OTEgNi4xOTg1NiA1LjM4NzEzIDYuMTM5NzUgNS4zNjAwMiA2LjA3NDg4QzUuMzA0MDEgNS45Mzg0IDUuMzA0MDEgNS43ODUzNiA1LjM2MDAyIDUuNjQ4ODhDNS40MTUzNiA1LjUxODQ2IDUuNTE5NDEgNS40MTQ3NyA1LjY1MDAyIDUuMzU5ODhDNS43MTczNyA1LjMzMTI2IDUuNzg5ODQgNS4zMTY2MyA1Ljg2MzAyIDUuMzE2ODhDNS45MzYxOCA1LjMxNjg1IDYuMDA4NiA1LjMzMTQ3IDYuMDc2MDIgNS4zNTk4OEM2LjE0MDM3IDUuMzg3NDkgNi4xOTkwNCA1LjQyNjgyIDYuMjQ5MDIgNS40NzU4OEM2LjI5Nzg2IDUuNTI2MDMgNi4zMzcxNyA1LjU4NDY1IDYuMzY1MDIgNS42NDg4OEM2LjM5MzQgNS43MTYzIDYuNDA4MDIgNS43ODg3MiA2LjQwODAyIDUuODYxODhDNi40MDgwMiA1LjkzNTAzIDYuMzkzNCA2LjAwNzQ1IDYuMzY1MDIgNi4wNzQ4OFpNMTQgM0gxMFY0SDE0VjNaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.variable:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.variable:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xLjUgNEwxIDQuNVYxMi41TDEuNSAxM0g0VjEySDJWNUg0VjRIMS41Wk0xNC41IDEzTDE1IDEyLjVMMTUgNC41TDE0LjUgNEgxMlY1TDE0IDVMMTQgMTJIMTJWMTNIMTQuNVpNOC43OTY5MyA1TDQuMjk2OTMgN0w0IDcuNDU2OTFWOS45NTY5MUw0LjI0Mjc1IDEwLjM4NTdMNi43NDI3NSAxMS44ODU3TDcuMjAzMDcgMTEuOTEzOEwxMS43MDMxIDkuOTEzODFMMTIgOS40NTY5MVY2Ljk1NjkxTDExLjc1NzIgNi41MjgxNkw5LjI1NzI1IDUuMDI4MTZMOC43OTY5MyA1Wk01IDguMzRWOS42NzM4MUw2LjUgMTAuNTczOFY5LjI0TDUgOC4zNFpNNy41IDkuMjgxODRWMTAuNjg3NUwxMSA5LjEzMTk3VjcuNzI2MjlMNy41IDkuMjgxODRaTTEwLjQxNzggNi44OTA3MUw4Ljk2NTU5IDYuMDE5MzZMNS41ODIxNiA3LjUyMzExTDcuMDM0NDEgOC4zOTQ0NUwxMC40MTc4IDYuODkwNzFaIiBmaWxsPSIjNzVCRUZGIi8+Cjwvc3ZnPgo=")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.class:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.class:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zLjM1MzU2IDYuNjQ2NDJMMi4wNjA2NiA1LjM1MzUzTDUuMzUzNTYgMi4wNjA2NUw2LjY0NjQ1IDMuMzUzNTRMMy4zNTM1NiA2LjY0NjQyWk01IDFMMSA0Ljk5OTk4VjUuNzA3MDhMMyA3LjcwNzA3SDMuNzA3MTFMNC44NTM1NSA2LjU2MDYzVjEyLjM1MzVMNS4zNTM1NSAxMi44NTM1SDEwLjAwOTdWMTMuMzc0MUwxMS4zNDMgMTQuNzA3NEgxMi4wNTAxTDE0LjcxNjggMTIuMDQwN1YxMS4zMzM2TDEzLjM4MzUgMTAuMDAwM0gxMi42NzYzTDEwLjgyMzEgMTEuODUzNUg1Ljg1MzU1VjcuODkzNTVIMTAuMDA5N1Y4LjM3NDAxTDExLjM0MyA5LjcwNzM0SDEyLjA1MDFMMTQuNzE2OCA3LjA0MDY4VjYuMzMzNTdMMTMuMzgzNSA1LjAwMDI0SDEyLjY3NjNMMTAuODYzIDYuODEzNTZINS44NTM1NVY1LjU2MDY0TDcuNzA3MTEgMy43MDcwOVYyLjk5OTk5TDUuNzA3MTEgMUg1Wk0xMS4wNzAzIDguMDIwNDZMMTEuNjk2NiA4LjY0NjY4TDEzLjY1NjEgNi42ODcxM0wxMy4wMjk5IDYuMDYwOUwxMS4wNzAzIDguMDIwNDZaTTExLjA3MDMgMTMuMDIwNUwxMS42OTY2IDEzLjY0NjdMMTMuNjU2MSAxMS42ODcyTDEzLjAyOTkgMTEuMDYxTDExLjA3MDMgMTMuMDIwNVoiIGZpbGw9IiNFRTlEMjgiLz4KPC9zdmc+Cg==")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.interface:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.interface:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMS41IDVDMTAuMTE5MyA1IDkgNi4xMTkyOSA5IDcuNUM5IDguODgwNzEgMTAuMTE5MyAxMCAxMS41IDEwQzEyLjg4MDcgMTAgMTQgOC44ODA3MSAxNCA3LjVDMTQgNi4xMTkyOSAxMi44ODA3IDUgMTEuNSA1Wk04LjAzNTQ0IDhDOC4yNzgwNiA5LjY5NjE1IDkuNzM2NzYgMTEgMTEuNSAxMUMxMy40MzMgMTEgMTUgOS40MzMgMTUgNy41QzE1IDUuNTY3IDEzLjQzMyA0IDExLjUgNEM5LjczNjc2IDQgOC4yNzgwNiA1LjMwMzg1IDguMDM1NDQgN0g0LjkzNjk5QzQuNzE0OTcgNi4xMzczOSAzLjkzMTkyIDUuNSAzIDUuNUMxLjg5NTQzIDUuNSAxIDYuMzk1NDMgMSA3LjVDMSA4LjYwNDU3IDEuODk1NDMgOS41IDMgOS41QzMuOTMxOTIgOS41IDQuNzE0OTcgOC44NjI2MSA0LjkzNjk5IDhIOC4wMzU0NFoiIGZpbGw9IiM3NUJFRkYiLz4KPC9zdmc+Cg==")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.struct:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.struct:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yIDJMMSAzVjZMMiA3SDE0TDE1IDZWM0wxNCAySDJaTTIgM0gzSDEzSDE0VjRWNVY2SDEzSDNIMlY1VjRWM1pNMSAxMEwyIDlINUw2IDEwVjEzTDUgMTRIMkwxIDEzVjEwWk0zIDEwSDJWMTFWMTJWMTNIM0g0SDVWMTJWMTFWMTBINEgzWk0xMCAxMEwxMSA5SDE0TDE1IDEwVjEzTDE0IDE0SDExTDEwIDEzVjEwWk0xMiAxMEgxMVYxMVYxMlYxM0gxMkgxM0gxNFYxMlYxMVYxMEgxM0gxMloiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.type-parameter:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.type-parameter:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMSA2SDEwVjUuNUMxMCA1LjIyMzg2IDkuNzc2MTYgNSA5LjUwMDAxIDVIOC40NzkwMlYxMC41QzguNDc5MDIgMTAuNzc2MSA4LjcwMjg4IDExIDguOTc5MDIgMTFIOS40NzkwMlYxMkg2LjQ3OTAyVjExSDYuOTc5MDJDNy4yNTUxNiAxMSA3LjQ3OTAyIDEwLjc3NjEgNy40NzkwMiAxMC41VjVINi41MDAwMUM2LjIyMzg3IDUgNi4wMDAwMSA1LjIyMzg2IDYuMDAwMDEgNS41VjZINS4wMDAwMVY0SDExVjZaTTEzLjkxNDIgOC4wNDgxTDEyLjQ1MTkgNi41ODU4MUwxMy4xNTkgNS44Nzg3MUwxNC45NzQ5IDcuNjk0NTRWOC40MDE2NUwxMy4yMDcxIDEwLjE2OTRMMTIuNSA5LjQ2MjMxTDEzLjkxNDIgOC4wNDgxWk0zLjU0ODEgOS40NjIzTDIuMDg1ODEgOC4wMDAwMkwzLjUwMDAyIDYuNTg1ODFMMi43OTI5MSA1Ljg3ODdMMS4wMjUxNSA3LjY0NjQ3VjguMzUzNTdMMi44NDEgMTAuMTY5NEwzLjU0ODEgOS40NjIzWiIgZmlsbD0iI0M1QzVDNSIvPgo8L3N2Zz4K")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.module:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.module:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik02IDIuOTgzNjFWMi45NzE4NFYySDUuOTEwODNDNS41OTc0MyAyIDUuMjk0MDcgMi4wNjE2MSA1LjAwMTI4IDIuMTg0NzNDNC43MDgxOCAyLjMwNzk4IDQuNDQ5NDIgMi40ODQ3NCA0LjIyNTc4IDIuNzE0OThDNC4wMDMxMSAyLjk0NDIyIDMuODM3OTIgMy4xOTQ5OCAzLjczMjgyIDMuNDY3NjZMMy43MzIzMyAzLjQ2ODk4QzMuNjMzODIgMy43MzUyIDMuNTY4MTQgNC4wMTIwMSAzLjUzNTMzIDQuMjk5MTdMMy41MzUxOSA0LjMwMDUzQzMuNTA2NzggNC41ODA1IDMuNDk4NyA0Ljg2ODQ0IDMuNTEwODQgNS4xNjQyOEMzLjUyMjcyIDUuNDUzNzkgMy41Mjg2NiA1Ljc0MzI5IDMuNTI4NjYgNi4wMzI3OUMzLjUyODY2IDYuMjM1NTYgMy40ODk3NCA2LjQyNTk0IDMuNDEyIDYuNjA1MDdMMy40MTE2IDYuNjA2MDFDMy4zMzY4NyA2Ljc4Mjk2IDMuMjM0MjMgNi45Mzg2NiAzLjEwMzE3IDcuMDczNTlDMi45NzY0NCA3LjIwNDA1IDIuODI0NjYgNy4zMTA1NSAyLjY0NjcyIDcuMzkyNUMyLjQ3MDYgNy40Njk1NCAyLjI4NDk3IDcuNTA4MiAyLjA4OTE3IDcuNTA4MkgyVjcuNlY4LjRWOC40OTE4SDIuMDg5MTdDMi4yODQ2NSA4LjQ5MTggMi40NzAwMSA4LjUzMjM4IDIuNjQ2MDEgOC42MTMzNEwyLjY0NzQyIDguNjEzOTZDMi44MjQ1NyA4LjY5MTU3IDIuOTc1NzcgOC43OTc2MiAzLjEwMjIxIDguOTMxNjFMMy4xMDQxMiA4LjkzMzUyQzMuMjM0MjggOS4wNjM3IDMuMzM2NTkgOS4yMTg3MSAzLjQxMTI5IDkuMzk5NDJMMy40MTIwMSA5LjQwMTA4QzMuNDg5ODYgOS41ODA0NyAzLjUyODY2IDkuNzY4ODMgMy41Mjg2NiA5Ljk2NzIxQzMuNTI4NjYgMTAuMjU2NyAzLjUyMjcyIDEwLjU0NjIgMy41MTA4NCAxMC44MzU3QzMuNDk4NyAxMS4xMzE2IDMuNTA2NzcgMTEuNDIxNSAzLjUzNTE2IDExLjcwNTVMMy41MzUzNSAxMS43MDcyQzMuNTY4MTkgMTEuOTkwMyAzLjYzMzg3IDEyLjI2NSAzLjczMjMyIDEyLjUzMUwzLjczMjgzIDEyLjUzMjNDMy44Mzc5MyAxMi44MDUgNC4wMDMxMSAxMy4wNTU4IDQuMjI1NzggMTMuMjg1QzQuNDQ5NDIgMTMuNTE1MyA0LjcwODE4IDEzLjY5MiA1LjAwMTI4IDEzLjgxNTNDNS4yOTQwNyAxMy45Mzg0IDUuNTk3NDMgMTQgNS45MTA4MyAxNEg2VjEzLjJWMTMuMDE2NEg1LjkxMDgzQzUuNzEwOTUgMTMuMDE2NCA1LjUyMzQ2IDEyLjk3NzcgNS4zNDc2MyAxMi45MDA4QzUuMTczOTYgMTIuODE5MSA1LjAyMTk0IDEyLjcxMjYgNC44OTA4NiAxMi41ODE4QzQuNzYzODYgMTIuNDQ2OSA0LjY2MTA0IDEyLjI5MTEgNC41ODIyMyAxMi4xMTM3QzQuNTA4MzggMTEuOTM0NiA0LjQ3MTM0IDExLjc0NCA0LjQ3MTM0IDExLjU0MUM0LjQ3MTM0IDExLjMxMjcgNC40NzUzIDExLjA4ODUgNC40ODMyMSAxMC44Njg2QzQuNDkxMjUgMTAuNjQxMSA0LjQ5MTI3IDEwLjQxOTUgNC40ODMyNCAxMC4yMDM5QzQuNDc5MTQgOS45ODI0NiA0LjQ2MDg0IDkuNzY4ODMgNC40MjgyMyA5LjU2MzEyQzQuMzk1MTMgOS4zNTAyNCA0LjMzOTIxIDkuMTQ3NTcgNC4yNjAzOSA4Ljk1NTM2QzQuMTgwOTEgOC43NjE1NyA0LjA3MjU4IDguNTc3NDYgMy45MzYxNiA4LjQwMjk4QzMuODIzNDUgOC4yNTg4MSAzLjY4NTM4IDguMTI0NjIgMy41MjI4MyA4QzMuNjg1MzggNy44NzUzOCAzLjgyMzQ1IDcuNzQxMTkgMy45MzYxNiA3LjU5NzAyQzQuMDcyNTggNy40MjI1NCA0LjE4MDkxIDcuMjM4NDMgNC4yNjAzOSA3LjA0NDY0QzQuMzM5MTMgNi44NTI2MyA0LjM5NTEzIDYuNjUxNzUgNC40MjgyNiA2LjQ0Mjg1QzQuNDYwODIgNi4yMzMzIDQuNDc5MTQgNi4wMTk3MyA0LjQ4MzI0IDUuODAyMTlDNC40OTEyNyA1LjU4MjYyIDQuNDkxMjUgNS4zNjEwNSA0LjQ4MzIxIDUuMTM3NDlDNC40NzUzIDQuOTEzNCA0LjQ3MTM0IDQuNjg3MjUgNC40NzEzNCA0LjQ1OTAyQzQuNDcxMzQgNC4yNjAxOSA0LjUwODMzIDQuMDcxNTIgNC41ODIzOCAzLjg5MjA1QzQuNjYxMzUgMy43MTAzNCA0Ljc2NDIxIDMuNTU0NzUgNC44OTA4NiAzLjQyNDM3QzUuMDIxOTMgMy4yODk0MiA1LjE3NDYxIDMuMTgyNzUgNS4zNDgwMiAzLjEwNTEzQzUuNTIzOCAzLjAyNDI3IDUuNzExMTMgMi45ODM2MSA1LjkxMDgzIDIuOTgzNjFINlpNMTAgMTMuMDE2NFYxMy4wMjgyVjE0SDEwLjA4OTJDMTAuNDAyNiAxNCAxMC43MDU5IDEzLjkzODQgMTAuOTk4NyAxMy44MTUzQzExLjI5MTggMTMuNjkyIDExLjU1MDYgMTMuNTE1MyAxMS43NzQyIDEzLjI4NUMxMS45OTY5IDEzLjA1NTggMTIuMTYyMSAxMi44MDUgMTIuMjY3MiAxMi41MzIzTDEyLjI2NzcgMTIuNTMxQzEyLjM2NjIgMTIuMjY0OCAxMi40MzE5IDExLjk4OCAxMi40NjQ3IDExLjcwMDhMMTIuNDY0OCAxMS42OTk1QzEyLjQ5MzIgMTEuNDE5NSAxMi41MDEzIDExLjEzMTYgMTIuNDg5MiAxMC44MzU3QzEyLjQ3NzMgMTAuNTQ2MiAxMi40NzEzIDEwLjI1NjcgMTIuNDcxMyA5Ljk2NzIxQzEyLjQ3MTMgOS43NjQ0NCAxMi41MTAzIDkuNTc0MDYgMTIuNTg4IDkuMzk0OTNMMTIuNTg4NCA5LjM5Mzk5QzEyLjY2MzEgOS4yMTcwNCAxMi43NjU4IDkuMDYxMzQgMTIuODk2OCA4LjkyNjQyQzEzLjAyMzYgOC43OTU5NSAxMy4xNzUzIDguNjg5NDUgMTMuMzUzMyA4LjYwNzVDMTMuNTI5NCA4LjUzMDQ2IDEzLjcxNSA4LjQ5MTggMTMuOTEwOCA4LjQ5MThIMTRWOC40VjcuNlY3LjUwODJIMTMuOTEwOEMxMy43MTUzIDcuNTA4MiAxMy41MyA3LjQ2NzYyIDEzLjM1NCA3LjM4NjY2TDEzLjM1MjYgNy4zODYwNEMxMy4xNzU0IDcuMzA4NDQgMTMuMDI0MiA3LjIwMjM4IDEyLjg5NzggNy4wNjgzOUwxMi44OTU5IDcuMDY2NDhDMTIuNzY1NyA2LjkzNjMgMTIuNjYzNCA2Ljc4MTI5IDEyLjU4ODcgNi42MDA1OEwxMi41ODggNi41OTg5MkMxMi41MTAxIDYuNDE5NTMgMTIuNDcxMyA2LjIzMTE3IDEyLjQ3MTMgNi4wMzI3OUMxMi40NzEzIDUuNzQzMjkgMTIuNDc3MyA1LjQ1Mzc5IDEyLjQ4OTIgNS4xNjQyOEMxMi41MDEzIDQuODY4NDIgMTIuNDkzMiA0LjU3ODQ4IDEyLjQ2NDggNC4yOTQ1NEwxMi40NjQ2IDQuMjkyODVDMTIuNDMxOCA0LjAwOTcxIDEyLjM2NjEgMy43MzUwMiAxMi4yNjc3IDMuNDY4OTdMMTIuMjY3MiAzLjQ2NzY2QzEyLjE2MjEgMy4xOTQ5OSAxMS45OTY5IDIuOTQ0MjIgMTEuNzc0MiAyLjcxNDk4QzExLjU1MDYgMi40ODQ3NCAxMS4yOTE4IDIuMzA3OTggMTAuOTk4NyAyLjE4NDczQzEwLjcwNTkgMi4wNjE2MSAxMC40MDI2IDIgMTAuMDg5MiAySDEwVjIuOFYyLjk4MzYxSDEwLjA4OTJDMTAuMjg5MSAyLjk4MzYxIDEwLjQ3NjUgMy4wMjIzIDEwLjY1MjQgMy4wOTkxN0MxMC44MjYgMy4xODA5MiAxMC45NzgxIDMuMjg3MzYgMTEuMTA5MSAzLjQxODIzQzExLjIzNjEgMy41NTMwNSAxMS4zMzkgMy43MDg4OSAxMS40MTc4IDMuODg2MjhDMTEuNDkxNiA0LjA2NTQgMTEuNTI4NyA0LjI1NTk2IDExLjUyODcgNC40NTkwMkMxMS41Mjg3IDQuNjg3MjcgMTEuNTI0NyA0LjkxMTQ1IDExLjUxNjggNS4xMzE0MkMxMS41MDg4IDUuMzU4OTQgMTEuNTA4NyA1LjU4MDQ5IDExLjUxNjggNS43OTYwNUMxMS41MjA5IDYuMDE3NTQgMTEuNTM5MiA2LjIzMTE3IDExLjU3MTggNi40MzY4OEMxMS42MDQ5IDYuNjQ5NzYgMTEuNjYwOCA2Ljg1MjQzIDExLjczOTYgNy4wNDQ2NEMxMS44MTkxIDcuMjM4NDMgMTEuOTI3NCA3LjQyMjU0IDEyLjA2MzggNy41OTcwMkMxMi4xNzY1IDcuNzQxMTkgMTIuMzE0NiA3Ljg3NTM4IDEyLjQ3NzIgOEMxMi4zMTQ2IDguMTI0NjIgMTIuMTc2NSA4LjI1ODgxIDEyLjA2MzggOC40MDI5OEMxMS45Mjc0IDguNTc3NDYgMTEuODE5MSA4Ljc2MTU3IDExLjczOTYgOC45NTUzNkMxMS42NjA5IDkuMTQ3MzcgMTEuNjA0OSA5LjM0ODI1IDExLjU3MTcgOS41NTcxNUMxMS41MzkyIDkuNzY2NyAxMS41MjA5IDkuOTgwMjcgMTEuNTE2OCAxMC4xOTc4QzExLjUwODcgMTAuNDE3NCAxMS41MDg3IDEwLjYzODkgMTEuNTE2OCAxMC44NjI1QzExLjUyNDcgMTEuMDg2NiAxMS41Mjg3IDExLjMxMjggMTEuNTI4NyAxMS41NDFDMTEuNTI4NyAxMS43Mzk4IDExLjQ5MTcgMTEuOTI4NSAxMS40MTc2IDEyLjEwNzlDMTEuMzM4NiAxMi4yODk3IDExLjIzNTggMTIuNDQ1MiAxMS4xMDkxIDEyLjU3NTZDMTAuOTc4MSAxMi43MTA2IDEwLjgyNTQgMTIuODE3MyAxMC42NTIgMTIuODk0OUMxMC40NzYyIDEyLjk3NTcgMTAuMjg4OSAxMy4wMTY0IDEwLjA4OTIgMTMuMDE2NEgxMFoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.property:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.property:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTIuODA3MjMgMTQuOTc1NEMyLjU3MTE5IDE0Ljk3MjEgMi4zMzgyNiAxNC45MjExIDIuMTIyNDcgMTQuODI1NEMxLjkwNjY3IDE0LjcyOTcgMS43MTI0OCAxNC41OTEzIDEuNTUxNTggMTQuNDE4NkMxLjIzODUgMTQuMTMzNCAxLjA0NDMzIDEzLjc0MDggMS4wMDc3NSAxMy4zMTg5QzAuOTY2MjI1IDEyLjg4MjggMS4wOTI2OSAxMi40NDczIDEuMzYxMzMgMTIuMTAxM0MyLjU2Nzc5IDEwLjgyODkgNC45NDczIDguNDQ5NCA2LjY3ODExIDYuNzU0NzlDNi4zMDk4MyA1Ljc1ODg3IDYuMzI3MDQgNC42NjEyNyA2LjcyNjM3IDMuNjc3MzlDNy4wNTQ3NCAyLjg1ODc2IDcuNjM4NjkgMi4xNjgwNSA4LjM5MTI5IDEuNzA4MDdDOC45ODE3IDEuMzE3MDYgOS42NjAzMSAxLjA3OTQ0IDEwLjM2NTcgMS4wMTY3M0MxMS4wNzExIDAuOTU0MDIyIDExLjc4MDkgMS4wNjgxOSAxMi40MzExIDEuMzQ4OTJMMTMuMDQ4MiAxLjYxNjJMMTAuMTgyNCA0LjU2NzM4TDExLjQzNzEgNS44MjU4MkwxNC4zODA5IDIuOTQ4ODdMMTQuNjQ4MiAzLjU2Nzg4QzE0Ljg3MzUgNC4wODk3NiAxNC45OTMgNC42NTExOSAxNC45OTk3IDUuMjE5NjFDMTUuMDA2NCA1Ljc4ODAyIDE0LjkwMDIgNi4zNTIxMSAxNC42ODcyIDYuODc5MTVDMTQuNDc2IDcuNDAwMjkgMTQuMTYyMyA3Ljg3MzY4IDEzLjc2NDcgOC4yNzEyMkMxMy41Mzk0IDguNDkxNjkgMTMuMjkwNCA4LjY4NjUzIDEzLjAyMjIgOC44NTIxOEMxMi40NjczIDkuMjIyNzUgMTEuODMyNCA5LjQ1NjM2IDExLjE2OTcgOS41MzM4QzEwLjUwNjkgOS42MTEyNCA5LjgzNTIxIDkuNTMwMyA5LjIwOTgyIDkuMjk3NjRDOC4xMTE5NCAxMC40MTEzIDUuMzcxNDIgMTMuMTcwNCAzLjg5MTE5IDE0LjU1MjJDMy41OTQyNiAxNC44MjE5IDMuMjA4MzIgMTQuOTcyNiAyLjgwNzIzIDE0Ljk3NTRaTTEwLjc0NDggMS45MjgwMkMxMC4wODcgMS45MjYzNyA5LjQ0MzU5IDIuMTIwMTggOC44OTYxNCAyLjQ4NDg1QzguNjgyNjUgMi42MTUyIDguNDg0MzcgMi43Njg5NyA4LjMwNDk4IDIuOTQzM0M3LjgyNzg5IDMuNDI0MjMgNy41MDkyNiA0LjAzOTUzIDcuMzkxODIgNC43MDY2OUM3LjI3NDM3IDUuMzczODUgNy4zNjM3NCA2LjA2MDk4IDcuNjQ3OTIgNi42NzU5MUw3Ljc4MzQyIDYuOTcyODhMNy41NTA0OCA3LjIwMDI1QzUuODEyMjQgOC44OTY3MiAzLjI4MTQ2IDExLjQyMDEgMi4wNjQ3OSAxMi43MDQ1QzEuOTU2NDYgMTIuODY1OCAxLjkxMDEyIDEzLjA2MDggMS45MzQzNSAxMy4yNTM1QzEuOTU4NTcgMTMuNDQ2MyAyLjA1MTcxIDEzLjYyMzggMi4xOTY1NyAxMy43NTMyQzIuMjgwMDUgMTMuODQ2MiAyLjM4MTc3IDEzLjkyMTEgMi40OTU0MSAxMy45NzMxQzIuNTk1NTcgMTQuMDE4NCAyLjcwMzgzIDE0LjA0MyAyLjgxMzczIDE0LjA0NTVDMi45ODA2NCAxNC4wNDEzIDMuMTQwNDQgMTMuOTc3IDMuMjYzODMgMTMuODY0NkM0LjgzNjg3IDEyLjM5NjQgNy44NzYyMiA5LjMyNjQxIDguNzY4MDcgOC40MjQzNUw4Ljk5NzMgOC4xOTMyNkw5LjI5MjQyIDguMzI3ODNDOS44MDYxOCA4LjU2NzMyIDEwLjM3MzEgOC42Njk4NSAxMC45MzgyIDguNjI1NDVDMTEuNTAzMyA4LjU4MTA2IDEyLjA0NzMgOC4zOTEyNSAxMi41MTc0IDguMDc0NDdDMTIuNzMxMyA3Ljk0MjYgMTIuOTI5NiA3Ljc4Njk0IDEzLjEwODUgNy42MTA0NUMxMy40MTgzIDcuMzAxNTMgMTMuNjYzMSA2LjkzMzc0IDEzLjgyODYgNi41Mjg3NEMxMy45OTQgNi4xMjM3NSAxNC4wNzY3IDUuNjg5NzQgMTQuMDcxOSA1LjI1MjI4QzE0LjA3MTkgNS4wMzY2MiAxNC4wNTA1IDQuODIxNDggMTQuMDA3OCA0LjYxMDA3TDExLjQzMDYgNy4xMjUwOEw4Ljg3OTQ0IDQuNTc3NTlMMTEuMzk0NCAxLjk4ODM0QzExLjE4MDQgMS45NDY3NCAxMC45NjI4IDEuOTI2NTMgMTAuNzQ0OCAxLjkyODAyWiIgZmlsbD0iI0M1QzVDNSIvPgo8L3N2Zz4K")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.unit:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.unit:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00IDFMMyAyVjE0TDQgMTVIMTJMMTMgMTRWMkwxMiAxSDRaTTQgM1YySDEyVjE0SDRWMTNINlYxMkg0VjEwSDhWOUg0VjdINlY2SDRWNEg4VjNINFoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constant:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constant:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00IDZIMTJWN0g0VjZaTTEyIDlINFYxMEgxMlY5WiIgZmlsbD0iI0M1QzVDNSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTEgNEwyIDNIMTRMMTUgNFYxMkwxNCAxM0gyTDEgMTJWNFpNMiA0VjEySDE0VjRIMloiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum:before,.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.value:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.value:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04IDJMNyAzVjZIOFYzSDE0VjhIMTBWOUgxNEwxNSA4VjNMMTQgMkg4Wk05IDhMOCA3SDdIMkwxIDhWMTNMMiAxNEg4TDkgMTNWOVY4Wk04IDlWOEg3SDJWMTNIOFY5Wk05IDYuNTg1NzlMOS40MTQyMSA3SDEzVjZIOVY2LjU4NTc5Wk0xMyA0SDlWNUgxM1Y0Wk03IDlIM1YxMEg3VjlaTTMgMTFIN1YxMkgzVjExWiIgZmlsbD0iI0VFOUQyOCIvPgo8L3N2Zz4K")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum-member:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum-member:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03IDNMOCAySDE0TDE1IDNWOEwxNCA5SDEwVjhIMTRWM0g4VjZIN1YzWk04IDdMOSA4VjlWMTNMOCAxNEgyTDEgMTNWOEwyIDdIN0g4Wk04IDhWOVYxM0gyVjhIN0g4Wk05LjQxNDIxIDdMOSA2LjU4NTc5VjZIMTNWN0g5LjQxNDIxWk05IDRIMTNWNUg5VjRaTTcgMTBIM1YxMUg3VjEwWiIgZmlsbD0iIzc1QkVGRiIvPgo8L3N2Zz4K")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.keyword:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.keyword:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE1IDRIMTBWM0gxNVY0Wk0xNCA3SDEyVjhIMTRWN1pNMTAgN0gxVjhIMTBWN1pNMTIgMTNIMVYxNEgxMlYxM1pNNyAxMEgxVjExSDdWMTBaTTE1IDEwSDEwVjExSDE1VjEwWk04IDJWNUgxVjJIOFpNNyAzSDJWNEg3VjNaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.text:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.text:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03LjIyMjg5IDEwLjkzM0M3LjU0ODYzIDExLjEyNTQgNy45MjE2MyAxMS4yMjMxIDguMjk5ODkgMTEuMjE1QzguNjM3NzcgMTEuMjIxOCA4Ljk3MjU0IDExLjE0OTIgOS4yNzcyMSAxMS4wMDNDOS41ODE4OCAxMC44NTY3IDkuODQ3OTIgMTAuNjQwOSAxMC4wNTM5IDEwLjM3M0MxMC41MDkxIDkuNzY1MTkgMTAuNzQwMiA5LjAxODY3IDEwLjcwNzkgOC4yNTk5OEMxMC43NDEyIDcuNTg2MjIgMTAuNTM3NCA2LjkyMjEgMTAuMTMxOSA2LjM4Mjk4QzkuOTM1NzUgNi4xNDE2MSA5LjY4NTc3IDUuOTQ5NTcgOS40MDIgNS44MjIyOEM5LjExODI0IDUuNjk0OTggOC44MDg1OCA1LjYzNTk3IDguNDk3ODkgNS42NDk5N0M4LjA3NTIyIDUuNjQ2OTkgNy42NTk5NCA1Ljc2MDg1IDcuMjk3ODkgNS45Nzg5OEM3LjE4MzA0IDYuMDQ4MDcgNy4wNzQ5IDYuMTI3NzUgNi45NzQ4OSA2LjIxNjk4VjMuNDc0OThINS45ODM4OVYxMS4xSDYuOTc4ODlWMTAuNzU2QzcuMDU1MTYgMTAuODIxNyA3LjEzNjc3IDEwLjg4MDkgNy4yMjI4OSAxMC45MzNaTTcuODQ5ODEgNi43MDAwNkM4LjAzNTk4IDYuNjIxMDUgOC4yMzgwNyA2LjU4Njc3IDguNDM5ODkgNi41OTk5OEM4LjYxMjU3IDYuNTk0NTIgOC43ODQwNCA2LjYzMDU0IDguOTM5OTQgNi43MDUwMUM5LjA5NTgzIDYuNzc5NDggOS4yMzE2MSA2Ljg5MDIzIDkuMzM1ODkgNy4wMjc5OEM5LjU5MjUzIDcuMzkwNTMgOS43MTg0IDcuODI5NTEgOS42OTI4OSA4LjI3Mjk3QzkuNzE5NzIgOC43OTc0OCA5LjU3OTY5IDkuMzE3MDEgOS4yOTI4OSA5Ljc1Njk4QzkuMTg4MjIgOS45MTUyNyA5LjA0NTQ2IDEwLjA0NDcgOC44Nzc3MyAxMC4xMzM1QzguNzA5OTkgMTAuMjIyMyA4LjUyMjY0IDEwLjI2NzUgOC4zMzI4OSAxMC4yNjVDOC4xNDkzNCAxMC4yNzMyIDcuOTY2MyAxMC4yNCA3Ljc5NzM0IDEwLjE2NzhDNy42MjgzOCAxMC4wOTU2IDcuNDc3ODQgOS45ODYyOCA3LjM1Njg5IDkuODQ3OTdDNy4xMDE1MiA5LjU1OTU3IDYuOTY1MDEgOS4xODUwNiA2Ljk3NDg5IDguNzk5OThWOC4xOTk5OEM2Ljk2Mjk5IDcuNzgzMzIgNy4xMDI2MyA3LjM3NjUgNy4zNjc4OSA3LjA1NDk4QzcuNDk4NTggNi45MDA2NCA3LjY2MzY0IDYuNzc5MDggNy44NDk4MSA2LjcwMDA2Wk0zLjI4OTAyIDUuNjc0OTlDMi45NzAxMSA1LjY3OTMzIDIuNjUzODggNS43MzQgMi4zNTIwMiA1LjgzNjk5QzIuMDY0MTcgNS45MjI5MyAxLjc5MzQ3IDYuMDU4MjggMS41NTIwMiA2LjIzNjk5TDEuNDUyMDIgNi4zMTM5OVY3LjUxMzk5TDEuODc1MDIgNy4xNTQ5OUMyLjI0NTc5IDYuODA0NzggMi43MzEzMyA2LjYwMTQ2IDMuMjQxMDIgNi41ODI5OUMzLjM2NTkzIDYuNTcxNjQgMy40OTE3IDYuNTkxNDcgMy42MDcwNiA2LjY0MDY4QzMuNzIyNDMgNi42ODk5IDMuODIzNzcgNi43NjY5NyAzLjkwMjAyIDYuODY0OTlDNC4wNTIyIDcuMDk3MSA0LjEzMjM5IDcuMzY3NTQgNC4xMzMwMiA3LjY0Mzk5TDIuOTAwMDIgNy44MjQ5OUMyLjM5NDM1IDcuODc3ODEgMS45MTUyNSA4LjA3NzcyIDEuNTIyMDIgOC4zOTk5OUMxLjM2Njk3IDguNTUxODEgMS4yNDMzOSA4LjczMjcxIDEuMTU4MzUgOC45MzIzNUMxLjA3MzMxIDkuMTMxOTkgMS4wMjg0OCA5LjM0NjQ0IDEuMDI2NDQgOS41NjM0M0MxLjAyNDQgOS43ODA0MiAxLjA2NTE3IDkuOTk1NjggMS4xNDY0NCAxMC4xOTY5QzEuMjI3NyAxMC4zOTgxIDEuMzQ3ODYgMTAuNTgxMyAxLjUwMDAyIDEwLjczNkMxLjY2ODcgMTAuODkwNCAxLjg2NjIyIDExLjAxIDIuMDgxMjUgMTEuMDg3OUMyLjI5NjI3IDExLjE2NTkgMi41MjQ1NiAxMS4yMDA1IDIuNzUzMDIgMTEuMTlDMy4xNDcgMTEuMTkzMSAzLjUzMjc4IDExLjA3NzQgMy44NjAwMiAxMC44NThDMy45NjE1MyAxMC43ODk3IDQuMDU3MiAxMC43MTMxIDQuMTQ2MDIgMTAuNjI5VjExLjA3M0g1LjA4NzAyVjcuNzE0OTlDNS4xMjEzNyA3LjE3NDIyIDQuOTU0MyA2LjYzOTg4IDQuNjE4MDIgNi4yMTQ5OUM0LjQ0OTc5IDYuMDMyODUgNC4yNDM0OCA1Ljg5MDAzIDQuMDEzNzggNS43OTY3QzMuNzg0MDcgNS43MDMzNiAzLjUzNjYxIDUuNjYxODEgMy4yODkwMiA1LjY3NDk5Wk00LjE0NjAyIDguNzE1OTlDNC4xNjU2NCA5LjEzNDM1IDQuMDI1OTIgOS41NDQ1OSAzLjc1NTAyIDkuODY0QzMuNjM2ODkgMTAuMDAwNSAzLjQ4OTk4IDEwLjEwOTIgMy4zMjQ4NiAxMC4xODIxQzMuMTU5NzMgMTAuMjU1MSAyLjk4MDQ5IDEwLjI5MDYgMi44MDAwMiAxMC4yODZDMi42OTA0OSAxMC4yOTQ1IDIuNTgwMzUgMTAuMjgxMiAyLjQ3NTk5IDEwLjI0NjlDMi4zNzE2MyAxMC4yMTI1IDIuMjc1MTEgMTAuMTU3OSAyLjE5MjAyIDEwLjA4NkMyLjA2MDc5IDkuOTM0NTUgMS45ODg1NiA5Ljc0MDg4IDEuOTg4NTYgOS41NDA0OUMxLjk4ODU2IDkuMzQwMTEgMi4wNjA3OSA5LjE0NjQ0IDIuMTkyMDIgOC45OTQ5OUMyLjQ3MzIyIDguODIxMzEgMi43OTIzMyA4LjcxODM3IDMuMTIyMDIgOC42OTQ5OUw0LjE0MjAyIDguNTQ2OTlMNC4xNDYwMiA4LjcxNTk5Wk0xMi40NTg4IDExLjAzMjVDMTIuNzY2IDExLjE2MzggMTMuMDk4MyAxMS4yMjYxIDEzLjQzMjIgMTEuMjE1QzEzLjkyNyAxMS4yMjcgMTQuNDE1MyAxMS4xMDA2IDE0Ljg0MjIgMTAuODVMMTQuOTY1MiAxMC43NzVMMTQuOTc4MiAxMC43NjhWOS42MTUwNEwxNC41MzIyIDkuOTM1MDRDMTQuMjE2IDEwLjE1OTIgMTMuODM1NiAxMC4yNzQ3IDEzLjQ0ODIgMTAuMjY0QzEzLjI0OTcgMTAuMjcxOSAxMy4wNTIgMTAuMjM0MiAxMi44NzAzIDEwLjE1MzhDMTIuNjg4NiAxMC4wNzMzIDEyLjUyNzggOS45NTIzMiAxMi40MDAyIDkuODAwMDRDMTIuMTE0NCA5LjQyNDUzIDExLjk3MjUgOC45NTkxMSAxMi4wMDAyIDguNDg4MDRDMTEuOTczNyA3Ljk4NzMyIDEyLjEzNTIgNy40OTQ3NSAxMi40NTMyIDcuMTA3MDRDMTIuNTkzNCA2Ljk0MTA1IDEyLjc2OTUgNi44MDkxNCAxMi45NjgyIDYuNzIxM0MxMy4xNjcgNi42MzM0NiAxMy4zODMxIDYuNTkyIDEzLjYwMDIgNi42MDAwNEMxMy45NDM5IDYuNTk4NDQgMTQuMjgwOCA2LjY5NTI1IDE0LjU3MTIgNi44NzkwNEwxNS4wMDAyIDcuMTQ0MDRWNS45NzAwNEwxNC44MzEyIDUuODk3MDRDMTQuNDYyNiA1LjczNDMyIDE0LjA2NDEgNS42NTAyIDEzLjY2MTIgNS42NTAwNEMxMy4yOTk5IDUuNjM5OTEgMTIuOTQwNiA1LjcwNzYyIDEyLjYwNzggNS44NDg1OUMxMi4yNzQ5IDUuOTg5NTYgMTEuOTc2MyA2LjIwMDQ4IDExLjczMjIgNi40NjcwNEMxMS4yMjYxIDcuMDI2ODMgMTAuOTU4MSA3Ljc2MTg2IDEwLjk4NTIgOC41MTYwNEMxMC45NTY3IDkuMjIzNDYgMTEuMTk1NSA5LjkxNTY5IDExLjY1NDIgMTAuNDU1QzExLjg3NjkgMTAuNzA0IDEyLjE1MTYgMTAuOTAxMiAxMi40NTg4IDExLjAzMjVaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.color:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.color:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik02LjYgMTEuNzY3M0M3LjMyODcxIDExLjAzODMgNy4zMjg3MSA5Ljg1NjM4IDYuNiA5LjEyNzQyQzUuODcwODMgOC4zOTg0NSA0LjY4OTEzIDguMzk4NDUgMy45NTk5NiA5LjEyNzQyQzIuOTE4MTIgMTAuMjI0OSAxLjEwNDExIDkuOTM5MDEgMS4wMTI5IDguNDI4NTNDMS4wMDQzNCA4LjI4Njc5IDEgOC4xNDM5IDEgOEMxIDQuMTMzOTkgNC4xMzQwNSAxIDggMUMxMS44NjYgMSAxNSA0LjEzMzk5IDE1IDhDMTUgMTEuODY2IDExLjg2NiAxNSA4IDE1QzcuNzk3NzQgMTUgNy41OTc0OSAxNC45OTE0IDcuMzk5NTkgMTQuOTc0NkM1LjgwNjQ2IDE0LjgzOTMgNS40OTg2IDEyLjkyNjMgNi42IDExLjc2NzNaTTYuOTY2NzUgMTMuNTQzNEM2LjkwMjUxIDEzLjI0NjQgNi45ODgyMSAxMi44MTUgNy4zMTgxOSAxMi40NjMyQzguNDI2MjkgMTEuMzQyOSA4LjQyMjY0IDkuNTM2MjMgNy4zMDcyMyA4LjQyMDQzTDcuMzA3MDEgOC40MjAyMUM2LjE4NzM0IDcuMzAwODUgNC4zNzI2MiA3LjMwMDg1IDMuMjUyOTUgOC40MjAyMUwzLjI0MzcxIDguNDI5NDVMMy4yMzQ3MSA4LjQzODkzQzIuOTM5MDMgOC43NTA0IDIuNTcwOTEgOC44MjU5OSAyLjMzMjUgOC43NzY2OEMyLjIxOTM2IDguNzUzMjggMi4xNTAyMSA4LjcwNjY3IDIuMTA5NzkgOC42NjE2MUMyLjA3MzkzIDguNjIxNjIgMi4wMjE0NiA4LjU0MDE2IDIuMDExMDggOC4zNjgyNkMyLjAwMzczIDguMjQ2NTYgMiA4LjEyMzc4IDIgOEMyIDQuNjg2MjggNC42ODYzMyAyIDggMkMxMS4zMTM3IDIgMTQgNC42ODYyOCAxNCA4QzE0IDExLjMxMzcgMTEuMzEzNyAxNCA4IDE0QzcuODI2MDIgMTQgNy42NTQgMTMuOTkyNiA3LjQ4NDIxIDEzLjk3ODJDNy4yODk5MiAxMy45NjE3IDcuMTg0ODggMTMuODk4NyA3LjEyNDE0IDEzLjg0MjlDNy4wNTgzMSAxMy43ODI0IDYuOTk3NjcgMTMuNjg2NCA2Ljk2Njc1IDEzLjU0MzRaTTUgNkM1LjU1MjI4IDYgNiA1LjU1MjI4IDYgNUM2IDQuNDQ3NzIgNS41NTIyOCA0IDUgNEM0LjQ0NzcyIDQgNCA0LjQ0NzcyIDQgNUM0IDUuNTUyMjggNC40NDc3MiA2IDUgNlpNMTIgMTFDMTIgMTEuNTUyMyAxMS41NTIzIDEyIDExIDEyQzEwLjQ0NzcgMTIgMTAgMTEuNTUyMyAxMCAxMUMxMCAxMC40NDc3IDEwLjQ0NzcgMTAgMTEgMTBDMTEuNTUyMyAxMCAxMiAxMC40NDc3IDEyIDExWk04IDVDOC41NTIyOCA1IDkgNC41NTIyOCA5IDRDOSAzLjQ0NzcyIDguNTUyMjggMyA4IDNDNy40NDc3MiAzIDcgMy40NDc3MiA3IDRDNyA0LjU1MjI4IDcuNDQ3NzIgNSA4IDVaTTEzIDhDMTMgOC41NTIyOCAxMi41NTIzIDkgMTIgOUMxMS40NDc3IDkgMTEgOC41NTIyOCAxMSA4QzExIDcuNDQ3NzIgMTEuNDQ3NyA3IDEyIDdDMTIuNTUyMyA3IDEzIDcuNDQ3NzIgMTMgOFpNMTEgNkMxMS41NTIzIDYgMTIgNS41NTIyOCAxMiA1QzEyIDQuNDQ3NzIgMTEuNTUyMyA0IDExIDRDMTAuNDQ3NyA0IDEwIDQuNDQ3NzIgMTAgNUMxMCA1LjU1MjI4IDEwLjQ0NzcgNiAxMSA2WiIgZmlsbD0iI0M1QzVDNSIvPgo8L3N2Zz4K")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.file:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.file:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00IDFMMyAyVjE0TDQgMTVIMTNMMTQgMTRWNUwxMy43MDcxIDQuMjkyODlMMTAuNzA3MSAxLjI5Mjg5TDEwIDFINFpNNCAxNFYyTDkgMlY2SDEzVjE0SDRaTTEzIDVMMTAgMlY1TDEzIDVaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.reference:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.reference:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04LjA2MDY1IDMuODUzNTZMNS45MTQyMSA2TDUuMjA3MSA1LjI5Mjg5TDYuNDk5OTkgNEgzLjVDMy4xMDIxOCA0IDIuNzIwNjQgNC4xNTgwNCAyLjQzOTM0IDQuNDM5MzRDMi4xNTgwNCA0LjcyMDY1IDIgNS4xMDIxOCAyIDUuNUMyIDUuODk3ODMgMi4xNTgwNCA2LjI3OTM2IDIuNDM5MzQgNi41NjA2NkMyLjcyMDY0IDYuODQxOTcgMy4xMDIxOCA3IDMuNSA3SDRWOEgzLjVDMi44MzY5NiA4IDIuMjAxMDcgNy43MzY2MSAxLjczMjIzIDcuMjY3NzdDMS4yNjMzOSA2Ljc5ODkzIDEgNi4xNjMwNSAxIDUuNUMxIDQuODM2OTYgMS4yNjMzOSA0LjIwMTA4IDEuNzMyMjMgMy43MzIyNEMyLjIwMTA3IDMuMjYzNCAyLjgzNjk2IDMgMy41IDNINi40OTk5OUw2LjQ5OTk5IDNINi40OTk5Nkw2IDIuNTAwMDRWMi41MDAwMUw1LjIwNzEgMS43MDcxMUw1LjkxNDIxIDFMOC4wNjA2NSAzLjE0NjQ1TDguMDYwNjUgMy44NTM1NlpNNSA2LjUwMDAzTDUuOTE0MjEgNy40MTQyNEw2IDcuMzI4NDVWMTRIMTRWN0gxMFYzSDkuMDYwNjVWMi43MzIyN0w4LjMyODM4IDJIMTEuMkwxMS41IDIuMUwxNC45IDUuNkwxNSA2VjE0LjVMMTQuNSAxNUg1LjVMNSAxNC41VjkuMDAwMDNWNi41MDAwM1pNMTEgM1Y2SDEzLjkwMzJMMTEgM1oiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.snippet:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.snippet:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yLjUgMUwyIDEuNVYxM0gzVjJIMTRWMTNIMTVWMS41TDE0LjUgMUgyLjVaTTIgMTVWMTRIM1YxNUgyWk01IDE0LjAwMDFINFYxNS4wMDAxSDVWMTQuMDAwMVpNNiAxNC4wMDAxSDdWMTUuMDAwMUg2VjE0LjAwMDFaTTkgMTQuMDAwMUg4VjE1LjAwMDFIOVYxNC4wMDAxWk0xMCAxNC4wMDAxSDExVjE1LjAwMDFIMTBWMTQuMDAwMVpNMTUgMTUuMDAwMVYxNC4wMDAxSDE0VjE1LjAwMDFIMTVaTTEyIDE0LjAwMDFIMTNWMTUuMDAwMUgxMlYxNC4wMDAxWiIgZmlsbD0iI0M1QzVDNSIvPgo8L3N2Zz4K")}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.customcolor:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.customcolor:before{background-image:none}.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.folder:before,.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.folder:before{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xLjAxMDg3IDIuNUwxLjUxMDg3IDJINi41MDcxM0w2Ljg2MDY4IDIuMTQ2NDVMNy43MTM0OSAyLjk5OTI1SDE0LjUwMTFMMTUuMDAxMSAzLjQ5OTI1VjguOTk1MTJMMTQuOTkwMyA5LjAwNTk5VjEzLjUwMjFMMTQuNDkwMyAxNC4wMDIxSDEuNUwxIDEzLjUwMjFWNi41MDczNUwxLjAxMDg3IDYuNDk2NDhWMi41Wk0xNC4wMDExIDMuOTk5MjVWNS4wMDMxMUg3LjUwMDVMNy4xNDY5NSA1LjE0OTU2TDYuMjg5MTUgNi4wMDczNUgyLjAxMDg3VjNINi4zMDAwMkw3LjE1MjgzIDMuODUyOEw3LjUwNjM4IDMuOTk5MjVIMTQuMDAxMVpNNi40OTYyNiA3LjAwNzM1SDIuMDEwODdWNy40OTU4OEgxLjk5OTYzVjExLjQ5MjlIMlYxMy4wMDIxSDEzLjk5MDNWMTEuNDkyOUgxMy45OTA2VjcuNDk1ODhIMTMuOTkwM1Y2LjAwMzExSDcuNzA3NjFMNi44NDk4MSA2Ljg2MDlMNi40OTYyNiA3LjAwNzM1WiIgZmlsbD0iI0M1QzVDNSIvPgo8L3N2Zz4K")}.monaco-editor{font-family:-apple-system,BlinkMacSystemFont,Segoe WPC,Segoe UI,HelveticaNeue-Light,Ubuntu,Droid Sans,sans-serif}.monaco-editor.hc-black .monaco-menu .monaco-action-bar.vertical .action-menu-item:focus .action-label,.monaco-editor.vs-dark .monaco-menu .monaco-action-bar.vertical .action-menu-item:focus .action-label,.monaco-menu .monaco-action-bar.vertical .action-item .action-menu-item:focus .action-label{stroke-width:1.2px}.monaco-editor-hover p{margin:0}.monaco-editor.hc-black{-ms-high-contrast-adjust:none}@media screen and (-ms-high-contrast:active){.monaco-editor.vs-dark .view-overlays .current-line,.monaco-editor.vs .view-overlays .current-line{border-color:windowtext!important;border-left:0;border-right:0}.monaco-editor.vs-dark .cursor,.monaco-editor.vs .cursor{background-color:windowtext!important}.monaco-editor.vs-dark .dnd-target,.monaco-editor.vs .dnd-target{border-color:windowtext!important}.monaco-editor.vs-dark .selected-text,.monaco-editor.vs .selected-text{background-color:highlight!important}.monaco-editor.vs-dark .view-line,.monaco-editor.vs .view-line{-ms-high-contrast-adjust:none}.monaco-editor.vs-dark .view-line span,.monaco-editor.vs .view-line span{color:windowtext!important}.monaco-editor.vs-dark .view-line span.inline-selected-text,.monaco-editor.vs .view-line span.inline-selected-text{color:highlighttext!important}.monaco-editor.vs-dark .view-overlays,.monaco-editor.vs .view-overlays{-ms-high-contrast-adjust:none}.monaco-editor.vs-dark .reference-decoration,.monaco-editor.vs-dark .selectionHighlight,.monaco-editor.vs-dark .wordHighlight,.monaco-editor.vs-dark .wordHighlightStrong,.monaco-editor.vs .reference-decoration,.monaco-editor.vs .selectionHighlight,.monaco-editor.vs .wordHighlight,.monaco-editor.vs .wordHighlightStrong{border:2px dotted highlight!important;background:transparent!important;box-sizing:border-box}.monaco-editor.vs-dark .rangeHighlight,.monaco-editor.vs .rangeHighlight{background:transparent!important;border:1px dotted activeborder!important;box-sizing:border-box}.monaco-editor.vs-dark .bracket-match,.monaco-editor.vs .bracket-match{border-color:windowtext!important;background:transparent!important}.monaco-editor.vs-dark .currentFindMatch,.monaco-editor.vs-dark .findMatch,.monaco-editor.vs .currentFindMatch,.monaco-editor.vs .findMatch{border:2px dotted activeborder!important;background:transparent!important;box-sizing:border-box}.monaco-editor.vs-dark .find-widget,.monaco-editor.vs .find-widget{border:1px solid windowtext}.monaco-editor.vs-dark .monaco-list .monaco-list-row,.monaco-editor.vs .monaco-list .monaco-list-row{-ms-high-contrast-adjust:none;color:windowtext!important}.monaco-editor.vs-dark .monaco-list .monaco-list-row.focused,.monaco-editor.vs .monaco-list .monaco-list-row.focused{color:highlighttext!important;background-color:highlight!important}.monaco-editor.vs-dark .monaco-list .monaco-list-row:hover,.monaco-editor.vs .monaco-list .monaco-list-row:hover{background:transparent!important;border:1px solid highlight;box-sizing:border-box}.monaco-editor.vs-dark .monaco-tree .monaco-tree-row,.monaco-editor.vs .monaco-tree .monaco-tree-row{-ms-high-contrast-adjust:none;color:windowtext!important}.monaco-editor.vs-dark .monaco-tree .monaco-tree-row.focused,.monaco-editor.vs-dark .monaco-tree .monaco-tree-row.selected,.monaco-editor.vs .monaco-tree .monaco-tree-row.focused,.monaco-editor.vs .monaco-tree .monaco-tree-row.selected{color:highlighttext!important;background-color:highlight!important}.monaco-editor.vs-dark .monaco-tree .monaco-tree-row:hover,.monaco-editor.vs .monaco-tree .monaco-tree-row:hover{background:transparent!important;border:1px solid highlight;box-sizing:border-box}.monaco-editor.vs-dark .monaco-scrollable-element>.scrollbar,.monaco-editor.vs .monaco-scrollable-element>.scrollbar{-ms-high-contrast-adjust:none;background:background!important;border:1px solid windowtext;box-sizing:border-box}.monaco-editor.vs-dark .monaco-scrollable-element>.scrollbar>.slider,.monaco-editor.vs .monaco-scrollable-element>.scrollbar>.slider{background:windowtext!important}.monaco-editor.vs-dark .monaco-scrollable-element>.scrollbar>.slider.active,.monaco-editor.vs-dark .monaco-scrollable-element>.scrollbar>.slider:hover,.monaco-editor.vs .monaco-scrollable-element>.scrollbar>.slider.active,.monaco-editor.vs .monaco-scrollable-element>.scrollbar>.slider:hover{background:highlight!important}.monaco-editor.vs-dark .decorationsOverviewRuler,.monaco-editor.vs .decorationsOverviewRuler{opacity:0}.monaco-editor.vs-dark .minimap,.monaco-editor.vs .minimap{display:none}.monaco-editor.vs-dark .squiggly-d-error,.monaco-editor.vs .squiggly-d-error{background:transparent!important;border-bottom:4px double #e47777}.monaco-editor.vs-dark .squiggly-b-info,.monaco-editor.vs-dark .squiggly-c-warning,.monaco-editor.vs .squiggly-b-info,.monaco-editor.vs .squiggly-c-warning{border-bottom:4px double #71b771}.monaco-editor.vs-dark .squiggly-a-hint,.monaco-editor.vs .squiggly-a-hint{border-bottom:4px double #6c6c6c}.monaco-editor.vs-dark .monaco-menu .monaco-action-bar.vertical .action-menu-item:focus .action-label,.monaco-editor.vs .monaco-menu .monaco-action-bar.vertical .action-menu-item:focus .action-label{-ms-high-contrast-adjust:none;color:highlighttext!important;background-color:highlight!important}.monaco-editor.vs-dark .monaco-menu .monaco-action-bar.vertical .action-menu-item:hover .action-label,.monaco-editor.vs .monaco-menu .monaco-action-bar.vertical .action-menu-item:hover .action-label{-ms-high-contrast-adjust:none;background:transparent!important;border:1px solid highlight;box-sizing:border-box}.monaco-diff-editor.vs-dark .diffOverviewRuler,.monaco-diff-editor.vs .diffOverviewRuler{display:none}.monaco-editor.vs-dark .line-delete,.monaco-editor.vs-dark .line-insert,.monaco-editor.vs .line-delete,.monaco-editor.vs .line-insert{background:transparent!important;border:1px solid highlight!important;box-sizing:border-box}.monaco-editor.vs-dark .char-delete,.monaco-editor.vs-dark .char-insert,.monaco-editor.vs .char-delete,.monaco-editor.vs .char-insert{background:transparent!important}}.monaco-diff-editor .diffOverview{z-index:9}.monaco-diff-editor.vs .diffOverview{background:rgba(0,0,0,.03)}.monaco-diff-editor.vs-dark .diffOverview{background:hsla(0,0%,100%,.01)}.monaco-diff-editor .diffViewport{box-shadow:inset 0 0 1px 0 #b9b9b9;background:rgba(0,0,0,.1)}.monaco-diff-editor.hc-black .diffViewport,.monaco-diff-editor.vs-dark .diffViewport{background:hsla(0,0%,100%,.1)}.monaco-scrollable-element.modified-in-monaco-diff-editor.vs-dark .scrollbar,.monaco-scrollable-element.modified-in-monaco-diff-editor.vs .scrollbar{background:transparent}.monaco-scrollable-element.modified-in-monaco-diff-editor.hc-black .scrollbar{background:none}.monaco-scrollable-element.modified-in-monaco-diff-editor .slider{z-index:10}.modified-in-monaco-diff-editor .slider.active{background:hsla(0,0%,67%,.4)}.modified-in-monaco-diff-editor.hc-black .slider.active{background:none}.monaco-diff-editor .delete-sign,.monaco-diff-editor .insert-sign,.monaco-editor .delete-sign,.monaco-editor .insert-sign{background-size:60%;opacity:.7;background-repeat:no-repeat;background-position:75%;background-size:11px 11px}.monaco-diff-editor.hc-black .delete-sign,.monaco-diff-editor.hc-black .insert-sign,.monaco-editor.hc-black .delete-sign,.monaco-editor.hc-black .insert-sign{opacity:1}.monaco-diff-editor .insert-sign,.monaco-editor .insert-sign{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDdWOEg4VjE0SDdWOEgxVjdIN1YxSDhWN0gxNFoiIGZpbGw9IiM0MjQyNDIiLz4KPC9zdmc+Cg==")}.monaco-diff-editor .delete-sign,.monaco-editor .delete-sign{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE1IDhIMVY3SDE1VjhaIiBmaWxsPSIjNDI0MjQyIi8+Cjwvc3ZnPgo=")}.monaco-diff-editor.hc-black .insert-sign,.monaco-diff-editor.vs-dark .insert-sign,.monaco-editor.hc-black .insert-sign,.monaco-editor.vs-dark .insert-sign{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0IDdWOEg4VjE0SDdWOEgxVjdIN1YxSDhWN0gxNFoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==")}.monaco-diff-editor.hc-black .delete-sign,.monaco-diff-editor.vs-dark .delete-sign,.monaco-editor.hc-black .delete-sign,.monaco-editor.vs-dark .delete-sign{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE1IDhIMVY3SDE1VjhaIiBmaWxsPSIjQzVDNUM1Ii8+Cjwvc3ZnPgo=")}.monaco-editor .inline-added-margin-view-zone,.monaco-editor .inline-deleted-margin-view-zone{text-align:right}.monaco-editor .diagonal-fill{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAChJREFUKFNjOH/+fAMDDgCSu3Dhwn9c8gwwBTgNGR4KQP4HhQOhsAIAZCBTkhtqePcAAAAASUVORK5CYII=")}.monaco-editor.vs-dark .diagonal-fill{opacity:.2}.monaco-editor.hc-black .diagonal-fill{background:none}.monaco-editor .view-zones .view-lines .view-line span{display:inline-block}.monaco-editor .margin-view-zones .inline-deleted-margin-view-zone .lightbulb-glyph{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMS42NzA4IDguNjU4MDZDMTEuMzMxOSA4Ljk5MTYgMTEuMDcxNiA5LjM2Mjc4IDEwLjg4ODYgOS43NzE3MkMxMC43MTA1IDEwLjE3OTIgMTAuNjIxIDEwLjYyMTkgMTAuNjIxIDExLjEwMDlWMTIuNzAxMkMxMC42MjEgMTIuODgwNyAxMC41ODcyIDEzLjA1MDMgMTAuNTE4OSAxMy4yMDkxQzEwLjQ1MTMgMTMuMzY2MSAxMC4zNTg2IDEzLjUwMzggMTAuMjQwNyAxMy42MjEzQzEwLjEyMjggMTMuNzM4OCA5Ljk4NDY0IDEzLjgzMTEgOS44MjcyMyAxMy44OTg0QzkuNjY4MDYgMTMuOTY2MyA5LjQ5ODA2IDE0IDkuMzE4MjMgMTRINy43MTIwNUM3LjUzMjIzIDE0IDcuMzYyMjMgMTMuOTY2MyA3LjIwMzA2IDEzLjg5ODRDNy4wNDU2NCAxMy44MzExIDYuOTA3NTMgMTMuNzM4OCA2Ljc4OTYxIDEzLjYyMTNDNi42NzE2OCAxMy41MDM4IDYuNTc4OTUgMTMuMzY2MSA2LjUxMTQxIDEzLjIwOTFDNi40NDMxMSAxMy4wNTAzIDYuNDA5MjcgMTIuODgwNyA2LjQwOTI3IDEyLjcwMTJWMTEuMTAwOUM2LjQwOTI3IDEwLjYyMiA2LjMxNzcyIDEwLjE3OTUgNi4xMzU1MyA5Ljc3MjA5QzUuOTU2ODMgOS4zNjMzNiA1LjY5ODMyIDguOTkxNTYgNS4zNTk1MyA4LjY1ODA2QzQuOTI0NjggOC4yMjkwMyA0LjU4ODk2IDcuNzUwMDMgNC4zNTM2MSA3LjIyMTM0QzQuMTE3NTYgNi42OTEwNyA0IDYuMTE2NzIgNCA1LjQ5OTUzQzQgNS4wODY2NCA0LjA1MzQyIDQuNjg4MDIgNC4xNjA0OCA0LjMwMzk3QzQuMjY3MjggMy45MjA4OSA0LjQxOTA3IDMuNTYyODYgNC42MTU5NSAzLjIzMDE4QzQuODEyNTcgMi44OTM3NyA1LjA0Nzc3IDIuNTg5MTEgNS4zMjE0NiAyLjMxNjQxQzUuNTk1MDMgMi4wNDM4MyA1Ljg5ODU4IDEuODA5NTMgNi4yMzE5NSAxLjYxMzY0QzYuNTY5NzkgMS40MTc2NCA2LjkzMTQ2IDEuMjY2MiA3LjMxNTc4IDEuMTU5ODNDNy43MDEwNiAxLjA1MzIgOC4xMDA5NCAxIDguNTE1MTQgMUM4LjkyOTM0IDEgOS4zMjkyMyAxLjA1MzIgOS43MTQ1MSAxLjE1OTgzQzEwLjA5ODggMS4yNjYyIDEwLjQ1OCAxLjQxNzM5IDEwLjc5MTggMS42MTM1MUMxMS4xMjk0IDEuODA5MzggMTEuNDM1MSAyLjA0MzcgMTEuNzA4OCAyLjMxNjQxQzExLjk4MjUgMi41ODkxIDEyLjIxNzcgMi44OTM3NiAxMi40MTQzIDMuMjMwMTZDMTIuNjExMiAzLjU2Mjg1IDEyLjc2MyAzLjkyMDg4IDEyLjg2OTggNC4zMDM5N0MxMi45NzY5IDQuNjg4MDIgMTMuMDMwMyA1LjA4NjY0IDEzLjAzMDMgNS40OTk1M0MxMy4wMzAzIDYuMTE2NzIgMTIuOTEyNyA2LjY5MTA3IDEyLjY3NjcgNy4yMjEzNEMxMi40NDEzIDcuNzUwMDMgMTIuMTA1NiA4LjIyOTAzIDExLjY3MDggOC42NTgwNlpNOS42MjE2MiAxMC41SDcuNDA4NjdWMTIuNzAxMkM3LjQwODY3IDEyLjc4MjMgNy40MzcyIDEyLjg1MTIgNy40OTg4OCAxMi45MTI3QzcuNTYwNTggMTIuOTc0MSA3LjYzMDA3IDEzLjAwMjggNy43MTIwNSAxMy4wMDI4SDkuMzE4MjNDOS40MDAyMiAxMy4wMDI4IDkuNDY5NzEgMTIuOTc0MSA5LjUzMTQgMTIuOTEyN0M5LjU5MzA5IDEyLjg1MTIgOS42MjE2MiAxMi43ODIzIDkuNjIxNjIgMTIuNzAxMlYxMC41WiIgZmlsbD0iIzQyNDI0MiIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTExLjY3MDggOC42NTgwNkMxMS4zMzE5IDguOTkxNiAxMS4wNzE2IDkuMzYyNzggMTAuODg4NiA5Ljc3MTcyQzEwLjcxMDUgMTAuMTc5MiAxMC42MjEgMTAuNjIxOSAxMC42MjEgMTEuMTAwOVYxMi43MDEyQzEwLjYyMSAxMi44ODA3IDEwLjU4NzIgMTMuMDUwMyAxMC41MTg5IDEzLjIwOTFDMTAuNDUxMyAxMy4zNjYxIDEwLjM1ODYgMTMuNTAzOCAxMC4yNDA3IDEzLjYyMTNDMTAuMTIyOCAxMy43Mzg4IDkuOTg0NjQgMTMuODMxMSA5LjgyNzIzIDEzLjg5ODRDOS42NjgwNiAxMy45NjYzIDkuNDk4MDYgMTQgOS4zMTgyMyAxNEg3LjcxMjA1QzcuNTMyMjMgMTQgNy4zNjIyMyAxMy45NjYzIDcuMjAzMDYgMTMuODk4NEM3LjA0NTY0IDEzLjgzMTEgNi45MDc1MyAxMy43Mzg4IDYuNzg5NjEgMTMuNjIxM0M2LjY3MTY4IDEzLjUwMzggNi41Nzg5NSAxMy4zNjYxIDYuNTExNDEgMTMuMjA5MUM2LjQ0MzExIDEzLjA1MDMgNi40MDkyNyAxMi44ODA3IDYuNDA5MjcgMTIuNzAxMlYxMS4xMDA5QzYuNDA5MjcgMTAuNjIyIDYuMzE3NzIgMTAuMTc5NSA2LjEzNTUzIDkuNzcyMDlDNS45NTY4MyA5LjM2MzM2IDUuNjk4MzIgOC45OTE1NiA1LjM1OTUzIDguNjU4MDZDNC45MjQ2OCA4LjIyOTAzIDQuNTg4OTYgNy43NTAwMyA0LjM1MzYxIDcuMjIxMzRDNC4xMTc1NiA2LjY5MTA3IDQgNi4xMTY3MiA0IDUuNDk5NTNDNCA1LjA4NjY0IDQuMDUzNDIgNC42ODgwMiA0LjE2MDQ4IDQuMzAzOTdDNC4yNjcyOCAzLjkyMDg5IDQuNDE5MDcgMy41NjI4NiA0LjYxNTk1IDMuMjMwMThDNC44MTI1NyAyLjg5Mzc3IDUuMDQ3NzcgMi41ODkxMSA1LjMyMTQ2IDIuMzE2NDFDNS41OTUwMyAyLjA0MzgzIDUuODk4NTggMS44MDk1MyA2LjIzMTk1IDEuNjEzNjRDNi41Njk3OSAxLjQxNzY0IDYuOTMxNDYgMS4yNjYyIDcuMzE1NzggMS4xNTk4M0M3LjcwMTA2IDEuMDUzMiA4LjEwMDk0IDEgOC41MTUxNCAxQzguOTI5MzQgMSA5LjMyOTIzIDEuMDUzMiA5LjcxNDUxIDEuMTU5ODNDMTAuMDk4OCAxLjI2NjIgMTAuNDU4IDEuNDE3MzkgMTAuNzkxOCAxLjYxMzUxQzExLjEyOTQgMS44MDkzOCAxMS40MzUxIDIuMDQzNyAxMS43MDg4IDIuMzE2NDFDMTEuOTgyNSAyLjU4OTEgMTIuMjE3NyAyLjg5Mzc2IDEyLjQxNDMgMy4yMzAxNkMxMi42MTEyIDMuNTYyODUgMTIuNzYzIDMuOTIwODggMTIuODY5OCA0LjMwMzk3QzEyLjk3NjkgNC42ODgwMiAxMy4wMzAzIDUuMDg2NjQgMTMuMDMwMyA1LjQ5OTUzQzEzLjAzMDMgNi4xMTY3MiAxMi45MTI3IDYuNjkxMDcgMTIuNjc2NyA3LjIyMTM0QzEyLjQ0MTMgNy43NTAwMyAxMi4xMDU2IDguMjI5MDMgMTEuNjcwOCA4LjY1ODA2Wk05LjYyMTYyIDEwLjVINy40MDg2N1YxMi43MDEyQzcuNDA4NjcgMTIuNzgyMyA3LjQzNzIgMTIuODUxMiA3LjQ5ODg4IDEyLjkxMjdDNy41NjA1OCAxMi45NzQxIDcuNjMwMDcgMTMuMDAyOCA3LjcxMjA1IDEzLjAwMjhIOS4zMTgyM0M5LjQwMDIyIDEzLjAwMjggOS40Njk3MSAxMi45NzQxIDkuNTMxNCAxMi45MTI3QzkuNTkzMDkgMTIuODUxMiA5LjYyMTYyIDEyLjc4MjMgOS42MjE2MiAxMi43MDEyVjEwLjVaIiBmaWxsPSIjNDI0MjQyIi8+Cjwvc3ZnPgo=") 50% no-repeat}.monaco-editor.hc-dark .margin-view-zones .inline-deleted-margin-view-zone .lightbulb-glyph,.monaco-editor.vs-dark .margin-view-zones .inline-deleted-margin-view-zone .lightbulb-glyph{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMS42NzA4IDguNjU4MDZDMTEuMzMxOSA4Ljk5MTYgMTEuMDcxNiA5LjM2Mjc4IDEwLjg4ODYgOS43NzE3MkMxMC43MTA1IDEwLjE3OTIgMTAuNjIxIDEwLjYyMTkgMTAuNjIxIDExLjEwMDlWMTIuNzAxMkMxMC42MjEgMTIuODgwNyAxMC41ODcyIDEzLjA1MDMgMTAuNTE4OSAxMy4yMDkxQzEwLjQ1MTMgMTMuMzY2MSAxMC4zNTg2IDEzLjUwMzggMTAuMjQwNyAxMy42MjEzQzEwLjEyMjggMTMuNzM4OCA5Ljk4NDY0IDEzLjgzMTEgOS44MjcyMyAxMy44OTg0QzkuNjY4MDYgMTMuOTY2MyA5LjQ5ODA2IDE0IDkuMzE4MjMgMTRINy43MTIwNUM3LjUzMjIzIDE0IDcuMzYyMjMgMTMuOTY2MyA3LjIwMzA2IDEzLjg5ODRDNy4wNDU2NCAxMy44MzExIDYuOTA3NTMgMTMuNzM4OCA2Ljc4OTYxIDEzLjYyMTNDNi42NzE2OCAxMy41MDM4IDYuNTc4OTUgMTMuMzY2MSA2LjUxMTQxIDEzLjIwOTFDNi40NDMxMSAxMy4wNTAzIDYuNDA5MjcgMTIuODgwNyA2LjQwOTI3IDEyLjcwMTJWMTEuMTAwOUM2LjQwOTI3IDEwLjYyMiA2LjMxNzcyIDEwLjE3OTUgNi4xMzU1MyA5Ljc3MjA5QzUuOTU2ODMgOS4zNjMzNiA1LjY5ODMyIDguOTkxNTYgNS4zNTk1MyA4LjY1ODA2QzQuOTI0NjggOC4yMjkwMyA0LjU4ODk2IDcuNzUwMDMgNC4zNTM2MSA3LjIyMTM0QzQuMTE3NTYgNi42OTEwNyA0IDYuMTE2NzIgNCA1LjQ5OTUzQzQgNS4wODY2NCA0LjA1MzQyIDQuNjg4MDIgNC4xNjA0OCA0LjMwMzk3QzQuMjY3MjggMy45MjA4OSA0LjQxOTA3IDMuNTYyODYgNC42MTU5NSAzLjIzMDE4QzQuODEyNTcgMi44OTM3NyA1LjA0Nzc3IDIuNTg5MTEgNS4zMjE0NiAyLjMxNjQxQzUuNTk1MDMgMi4wNDM4MyA1Ljg5ODU4IDEuODA5NTMgNi4yMzE5NSAxLjYxMzY0QzYuNTY5NzkgMS40MTc2NCA2LjkzMTQ2IDEuMjY2MiA3LjMxNTc4IDEuMTU5ODNDNy43MDEwNiAxLjA1MzIgOC4xMDA5NCAxIDguNTE1MTQgMUM4LjkyOTM0IDEgOS4zMjkyMyAxLjA1MzIgOS43MTQ1MSAxLjE1OTgzQzEwLjA5ODggMS4yNjYyIDEwLjQ1OCAxLjQxNzM5IDEwLjc5MTggMS42MTM1MUMxMS4xMjk0IDEuODA5MzggMTEuNDM1MSAyLjA0MzcgMTEuNzA4OCAyLjMxNjQxQzExLjk4MjUgMi41ODkxIDEyLjIxNzcgMi44OTM3NiAxMi40MTQzIDMuMjMwMTZDMTIuNjExMiAzLjU2Mjg1IDEyLjc2MyAzLjkyMDg4IDEyLjg2OTggNC4zMDM5N0MxMi45NzY5IDQuNjg4MDIgMTMuMDMwMyA1LjA4NjY0IDEzLjAzMDMgNS40OTk1M0MxMy4wMzAzIDYuMTE2NzIgMTIuOTEyNyA2LjY5MTA3IDEyLjY3NjcgNy4yMjEzNEMxMi40NDEzIDcuNzUwMDMgMTIuMTA1NiA4LjIyOTAzIDExLjY3MDggOC42NTgwNlpNOS42MjE2MiAxMC41SDcuNDA4NjdWMTIuNzAxMkM3LjQwODY3IDEyLjc4MjMgNy40MzcyIDEyLjg1MTIgNy40OTg4OCAxMi45MTI3QzcuNTYwNTggMTIuOTc0MSA3LjYzMDA3IDEzLjAwMjggNy43MTIwNSAxMy4wMDI4SDkuMzE4MjNDOS40MDAyMiAxMy4wMDI4IDkuNDY5NzEgMTIuOTc0MSA5LjUzMTQgMTIuOTEyN0M5LjU5MzA5IDEyLjg1MTIgOS42MjE2MiAxMi43ODIzIDkuNjIxNjIgMTIuNzAxMlYxMC41WiIgZmlsbD0iI0MyQzJDMiIvPgo8L3N2Zz4K") 50% no-repeat}.monaco-editor .margin-view-zones .lightbulb-glyph:hover{cursor:pointer}.monaco-diff-editor .diff-review-line-number{text-align:right;display:inline-block}.monaco-diff-editor .diff-review{position:absolute;-webkit-user-select:none;-ms-user-select:none;-moz-user-select:none;-o-user-select:none;user-select:none}.monaco-diff-editor .diff-review-summary{padding-left:10px}.monaco-diff-editor .diff-review-shadow{position:absolute}.monaco-diff-editor .diff-review-row{white-space:pre}.monaco-diff-editor .diff-review-table{display:table;min-width:100%}.monaco-diff-editor .diff-review-row{display:table-row;width:100%}.monaco-diff-editor .diff-review-cell{display:table-cell}.monaco-diff-editor .diff-review-spacer{display:inline-block;width:10px}.monaco-diff-editor .diff-review-actions{display:inline-block;position:absolute;right:10px;top:2px}.monaco-diff-editor .diff-review-actions .action-label{width:16px;height:16px;margin:2px 0}.monaco-diff-editor .action-label.icon.close-diff-review{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04LjAwMDAxIDguNzA3MTFMMTEuNjQ2NSAxMi4zNTM2TDEyLjM1MzYgMTEuNjQ2NUw4LjcwNzExIDguMDAwMDFMMTIuMzUzNiA0LjM1MzU2TDExLjY0NjUgMy42NDY0NUw4LjAwMDAxIDcuMjkyOUw0LjM1MzU2IDMuNjQ2NDVMMy42NDY0NSA0LjM1MzU2TDcuMjkyOSA4LjAwMDAxTDMuNjQ2NDUgMTEuNjQ2NUw0LjM1MzU2IDEyLjM1MzZMOC4wMDAwMSA4LjcwNzExWiIgZmlsbD0iIzQyNDI0MiIvPgo8L3N2Zz4K") 50% no-repeat}.monaco-diff-editor.hc-black .action-label.icon.close-diff-review,.monaco-diff-editor.vs-dark .action-label.icon.close-diff-review{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04LjAwMDAxIDguNzA3MTFMMTEuNjQ2NSAxMi4zNTM2TDEyLjM1MzYgMTEuNjQ2NUw4LjcwNzExIDguMDAwMDFMMTIuMzUzNiA0LjM1MzU2TDExLjY0NjUgMy42NDY0NUw4LjAwMDAxIDcuMjkyOUw0LjM1MzU2IDMuNjQ2NDVMMy42NDY0NSA0LjM1MzU2TDcuMjkyOSA4LjAwMDAxTDMuNjQ2NDUgMTEuNjQ2NUw0LjM1MzU2IDEyLjM1MzZMOC4wMDAwMSA4LjcwNzExWiIgZmlsbD0iI0M1QzVDNSIvPgo8L3N2Zz4K") 50% no-repeat}.context-view .monaco-menu{min-width:130px}.context-view-block{position:fixed;left:0;top:0;z-index:-1;width:100%;height:100%}.monaco-menu .monaco-action-bar.vertical{margin-left:0;overflow:visible}.monaco-menu .monaco-action-bar.vertical .actions-container{display:block}.monaco-menu .monaco-action-bar.vertical .action-item{padding:0;transform:none;display:-ms-flexbox;display:flex}.monaco-menu .monaco-action-bar.vertical .action-item.active{transform:none}.monaco-menu .monaco-action-bar.vertical .action-menu-item{-ms-flex:1 1 auto;flex:1 1 auto;display:-ms-flexbox;display:flex;height:2em;-ms-flex-align:center;align-items:center;position:relative}.monaco-menu .monaco-action-bar.vertical .action-label{-ms-flex:1 1 auto;flex:1 1 auto;text-decoration:none;padding:0 1em;background:none;font-size:12px;line-height:1}.monaco-menu .monaco-action-bar.vertical .keybinding,.monaco-menu .monaco-action-bar.vertical .submenu-indicator{display:inline-block;-ms-flex:2 1 auto;flex:2 1 auto;padding:0 1em;text-align:right;font-size:12px;line-height:1}.monaco-menu .monaco-action-bar.vertical .submenu-indicator{height:100%;-webkit-mask:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHZpZXdCb3g9IjAgMCAxNCAxNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuNTIwNTEgMTIuMzY0M0w5Ljg3NzkzIDdMNC41MjA1MSAxLjYzNTc0Mkw1LjEzNTc0IDEuMDIwNTA3OEwxMS4xMjIxIDdMNS4xMzU3NCAxMi45Nzk1TDQuNTIwNTEgMTIuMzY0M1oiIGZpbGw9ImJsYWNrIi8+Cjwvc3ZnPgo=") no-repeat 90% 50%/13px 13px;mask:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHZpZXdCb3g9IjAgMCAxNCAxNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuNTIwNTEgMTIuMzY0M0w5Ljg3NzkzIDdMNC41MjA1MSAxLjYzNTc0Mkw1LjEzNTc0IDEuMDIwNTA3OEwxMS4xMjIxIDdMNS4xMzU3NCAxMi45Nzk1TDQuNTIwNTEgMTIuMzY0M1oiIGZpbGw9ImJsYWNrIi8+Cjwvc3ZnPgo=") no-repeat 90% 50%/13px 13px}.monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding,.monaco-menu .monaco-action-bar.vertical .action-item.disabled .submenu-indicator{opacity:.4}.monaco-menu .monaco-action-bar.vertical .action-label:not(.separator){display:inline-block;-o-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;margin:0}.monaco-menu .monaco-action-bar.vertical .action-item{position:static;overflow:visible}.monaco-menu .monaco-action-bar.vertical .action-item .monaco-submenu{position:absolute}.monaco-menu .monaco-action-bar.vertical .action-label.separator{padding:.5em 0 0;margin-bottom:.5em;width:100%}.monaco-menu .monaco-action-bar.vertical .action-label.separator.text{padding:.7em 1em .1em;font-weight:700;opacity:1}.monaco-menu .monaco-action-bar.vertical .action-label:hover{color:inherit}.monaco-menu .monaco-action-bar.vertical .menu-item-check{position:absolute;visibility:hidden;-webkit-mask:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNC40MzE1IDMuMzIzMkw1Ljk2MTUxIDEzLjMyMzJMNS4xNzA4IDEzLjI4NzRMMS44MjA4IDguNTE3NEwyLjYzOTE1IDcuOTQyNjhMNS42MTY5NyAxMi4xODI3TDEzLjY2ODQgMi42NzY4OEwxNC40MzE1IDMuMzIzMloiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==") no-repeat 50% 56%/15px 15px;mask:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNC40MzE1IDMuMzIzMkw1Ljk2MTUxIDEzLjMyMzJMNS4xNzA4IDEzLjI4NzRMMS44MjA4IDguNTE3NEwyLjYzOTE1IDcuOTQyNjhMNS42MTY5NyAxMi4xODI3TDEzLjY2ODQgMi42NzY4OEwxNC40MzE1IDMuMzIzMloiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==") no-repeat 50% 56%/15px 15px;width:1em;height:100%}.monaco-menu .monaco-action-bar.vertical .action-menu-item.checked .menu-item-check{visibility:visible}.context-view.monaco-menu-container{outline:0;border:none;animation:fadeIn 83ms linear}.context-view.monaco-menu-container .monaco-action-bar.vertical:focus,.context-view.monaco-menu-container .monaco-action-bar.vertical :focus,.context-view.monaco-menu-container :focus{outline:0}.monaco-menu .monaco-action-bar.vertical .action-item{border:thin solid transparent}.hc-black .context-view.monaco-menu-container{box-shadow:none}.hc-black .monaco-menu .monaco-action-bar.vertical .action-item.focused{background:none}.menubar{display:-ms-flexbox;display:flex;-ms-flex-negative:1;flex-shrink:1;box-sizing:border-box;height:30px;overflow:hidden;-ms-flex-wrap:wrap;flex-wrap:wrap}.fullscreen .menubar{margin:0;padding:0 5px}.menubar>.menubar-menu-button{-ms-flex-align:center;align-items:center;box-sizing:border-box;padding:0 8px;cursor:default;-webkit-app-region:no-drag;zoom:1;white-space:nowrap;outline:0}.menubar .menubar-menu-items-holder{position:absolute;left:0;opacity:1;z-index:2000}.menubar .menubar-menu-items-holder.monaco-menu-container{outline:0;border:none}.menubar .menubar-menu-items-holder.monaco-menu-container :focus{outline:0}.menubar .toolbar-toggle-more{background-position:50%;background-repeat:no-repeat;background-size:14px;width:20px;height:100%;display:inline-block;padding:0;-webkit-mask:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQgOEM0IDguMTk3NzggMy45NDEzNSA4LjM5MTEyIDMuODMxNDcgOC41NTU1N0MzLjcyMTU5IDguNzIwMDIgMy41NjU0MSA4Ljg0ODE5IDMuMzgyNjggOC45MjM4OEMzLjE5OTk2IDguOTk5NTcgMi45OTg4OSA5LjAxOTM3IDIuODA0OTEgOC45ODA3OUMyLjYxMDkzIDguOTQyMiAyLjQzMjc1IDguODQ2OTYgMi4yOTI4OSA4LjcwNzExQzIuMTUzMDQgOC41NjcyNSAyLjA1NzggOC4zODkwNyAyLjAxOTIyIDguMTk1MDlDMS45ODA2MyA4LjAwMTExIDIuMDAwNDMgNy44MDAwNCAyLjA3NjEyIDcuNjE3MzJDMi4xNTE4MSA3LjQzNDU5IDIuMjc5OTggNy4yNzg0MSAyLjQ0NDQzIDcuMTY4NTNDMi42MDg4OCA3LjA1ODY1IDIuODAyMjIgNyAzIDdDMy4yNjUyMiA3IDMuNTE5NTcgNy4xMDUzNiAzLjcwNzExIDcuMjkyODlDMy44OTQ2NCA3LjQ4MDQzIDQgNy43MzQ3OCA0IDhaIiBmaWxsPSIjQzVDNUM1Ii8+CjxwYXRoIGQ9Ik05IDhDOSA4LjE5Nzc4IDguOTQxMzUgOC4zOTExMiA4LjgzMTQ3IDguNTU1NTdDOC43MjE1OSA4LjcyMDAyIDguNTY1NDEgOC44NDgxOSA4LjM4MjY4IDguOTIzODhDOC4xOTk5NiA4Ljk5OTU3IDcuOTk4ODkgOS4wMTkzNyA3LjgwNDkxIDguOTgwNzlDNy42MTA5MyA4Ljk0MjIgNy40MzI3NSA4Ljg0Njk2IDcuMjkyODkgOC43MDcxMUM3LjE1MzA0IDguNTY3MjUgNy4wNTc4IDguMzg5MDcgNy4wMTkyMiA4LjE5NTA5QzYuOTgwNjMgOC4wMDExMSA3LjAwMDQzIDcuODAwMDQgNy4wNzYxMiA3LjYxNzMyQzcuMTUxODEgNy40MzQ1OSA3LjI3OTk4IDcuMjc4NDEgNy40NDQ0MyA3LjE2ODUzQzcuNjA4ODggNy4wNTg2NSA3LjgwMjIyIDcgOCA3QzguMjY1MjIgNyA4LjUxOTU3IDcuMTA1MzYgOC43MDcxMSA3LjI5Mjg5QzguODk0NjQgNy40ODA0MyA5IDcuNzM0NzggOSA4WiIgZmlsbD0iI0M1QzVDNSIvPgo8cGF0aCBkPSJNMTQgOEMxNCA4LjE5Nzc4IDEzLjk0MTQgOC4zOTExMiAxMy44MzE1IDguNTU1NTdDMTMuNzIxNiA4LjcyMDAyIDEzLjU2NTQgOC44NDgxOSAxMy4zODI3IDguOTIzODhDMTMuMiA4Ljk5OTU3IDEyLjk5ODkgOS4wMTkzNyAxMi44MDQ5IDguOTgwNzlDMTIuNjEwOSA4Ljk0MjIgMTIuNDMyNyA4Ljg0Njk2IDEyLjI5MjkgOC43MDcxMUMxMi4xNTMgOC41NjcyNSAxMi4wNTc4IDguMzg5MDcgMTIuMDE5MiA4LjE5NTA5QzExLjk4MDYgOC4wMDExMSAxMi4wMDA0IDcuODAwMDQgMTIuMDc2MSA3LjYxNzMyQzEyLjE1MTggNy40MzQ1OSAxMi4yOCA3LjI3ODQxIDEyLjQ0NDQgNy4xNjg1M0MxMi42MDg5IDcuMDU4NjUgMTIuODAyMiA3IDEzIDdDMTMuMjY1MiA3IDEzLjUxOTYgNy4xMDUzNiAxMy43MDcxIDcuMjkyODlDMTMuODk0NiA3LjQ4MDQzIDE0IDcuNzM0NzggMTQgOFoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==") no-repeat 50% 55%/14px 14px;mask:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQgOEM0IDguMTk3NzggMy45NDEzNSA4LjM5MTEyIDMuODMxNDcgOC41NTU1N0MzLjcyMTU5IDguNzIwMDIgMy41NjU0MSA4Ljg0ODE5IDMuMzgyNjggOC45MjM4OEMzLjE5OTk2IDguOTk5NTcgMi45OTg4OSA5LjAxOTM3IDIuODA0OTEgOC45ODA3OUMyLjYxMDkzIDguOTQyMiAyLjQzMjc1IDguODQ2OTYgMi4yOTI4OSA4LjcwNzExQzIuMTUzMDQgOC41NjcyNSAyLjA1NzggOC4zODkwNyAyLjAxOTIyIDguMTk1MDlDMS45ODA2MyA4LjAwMTExIDIuMDAwNDMgNy44MDAwNCAyLjA3NjEyIDcuNjE3MzJDMi4xNTE4MSA3LjQzNDU5IDIuMjc5OTggNy4yNzg0MSAyLjQ0NDQzIDcuMTY4NTNDMi42MDg4OCA3LjA1ODY1IDIuODAyMjIgNyAzIDdDMy4yNjUyMiA3IDMuNTE5NTcgNy4xMDUzNiAzLjcwNzExIDcuMjkyODlDMy44OTQ2NCA3LjQ4MDQzIDQgNy43MzQ3OCA0IDhaIiBmaWxsPSIjQzVDNUM1Ii8+CjxwYXRoIGQ9Ik05IDhDOSA4LjE5Nzc4IDguOTQxMzUgOC4zOTExMiA4LjgzMTQ3IDguNTU1NTdDOC43MjE1OSA4LjcyMDAyIDguNTY1NDEgOC44NDgxOSA4LjM4MjY4IDguOTIzODhDOC4xOTk5NiA4Ljk5OTU3IDcuOTk4ODkgOS4wMTkzNyA3LjgwNDkxIDguOTgwNzlDNy42MTA5MyA4Ljk0MjIgNy40MzI3NSA4Ljg0Njk2IDcuMjkyODkgOC43MDcxMUM3LjE1MzA0IDguNTY3MjUgNy4wNTc4IDguMzg5MDcgNy4wMTkyMiA4LjE5NTA5QzYuOTgwNjMgOC4wMDExMSA3LjAwMDQzIDcuODAwMDQgNy4wNzYxMiA3LjYxNzMyQzcuMTUxODEgNy40MzQ1OSA3LjI3OTk4IDcuMjc4NDEgNy40NDQ0MyA3LjE2ODUzQzcuNjA4ODggNy4wNTg2NSA3LjgwMjIyIDcgOCA3QzguMjY1MjIgNyA4LjUxOTU3IDcuMTA1MzYgOC43MDcxMSA3LjI5Mjg5QzguODk0NjQgNy40ODA0MyA5IDcuNzM0NzggOSA4WiIgZmlsbD0iI0M1QzVDNSIvPgo8cGF0aCBkPSJNMTQgOEMxNCA4LjE5Nzc4IDEzLjk0MTQgOC4zOTExMiAxMy44MzE1IDguNTU1NTdDMTMuNzIxNiA4LjcyMDAyIDEzLjU2NTQgOC44NDgxOSAxMy4zODI3IDguOTIzODhDMTMuMiA4Ljk5OTU3IDEyLjk5ODkgOS4wMTkzNyAxMi44MDQ5IDguOTgwNzlDMTIuNjEwOSA4Ljk0MjIgMTIuNDMyNyA4Ljg0Njk2IDEyLjI5MjkgOC43MDcxMUMxMi4xNTMgOC41NjcyNSAxMi4wNTc4IDguMzg5MDcgMTIuMDE5MiA4LjE5NTA5QzExLjk4MDYgOC4wMDExMSAxMi4wMDA0IDcuODAwMDQgMTIuMDc2MSA3LjYxNzMyQzEyLjE1MTggNy40MzQ1OSAxMi4yOCA3LjI3ODQxIDEyLjQ0NDQgNy4xNjg1M0MxMi42MDg5IDcuMDU4NjUgMTIuODAyMiA3IDEzIDdDMTMuMjY1MiA3IDEzLjUxOTYgNy4xMDUzNiAxMy43MDcxIDcuMjkyODlDMTMuODk0NiA3LjQ4MDQzIDE0IDcuNzM0NzggMTQgOFoiIGZpbGw9IiNDNUM1QzUiLz4KPC9zdmc+Cg==") no-repeat 50% 55%/14px 14px}.context-view{position:absolute;z-index:2000}.rule-view .el-card{margin-top:24px;min-height:150px}.rule-view .page-title .el-breadcrumb{text-transform:none}.resources-view .el-table{margin-top:24px}.resources-view .status-wrapper{padding:0;list-style-type:none}.resources-view .status-wrapper .status-item{padding:2px 6px}.resources-view .status-wrapper .status-item>span{margin-right:12px}.resources-view .el-form-item__content{clear:both}.resources-view .expand-column .cell{opacity:0}.topic-metrics .sub-tip{font-size:14px;color:#9e9e9f;text-transform:none;margin-right:10px}.topic-metrics .el-table{margin-top:24px}.topic-metrics .el-table .expand-header{height:32px;line-height:32px;margin-bottom:20px}.topic-metrics .el-table .topic-qos-radio{float:right}.topic-metrics .el-table .message-card{height:112px;border-radius:4px;padding:6px 12px}.topic-metrics .el-table .message-card .message-card--body{font-size:28px;height:80px;line-height:80px;text-align:center}.topic-metrics .el-table .message-card .message-rate{float:right}.alarms-view .table-title{margin:24px 0 20px;font-size:16px}.alarms-view .table-title .table-oper{float:right}.alarms-view .el-table{margin-bottom:32px}.alarms-view .details{margin-right:5px;color:#a7a7a7;cursor:pointer}.plugins-view .el-card.plugin-config,.plugins-view .el-table{margin-top:24px}.plugins-view .el-row{margin-top:20px}.plugins-view .oper{min-width:50px;font-size:14px;margin-bottom:4px}.plugins-view .tutorial{margin-left:5px;color:#a7a7a7}.plugins-view .tutorial:hover{color:#34c388}.plugin-manage .page-title{text-transform:none}.plugin-manage .el-card{margin-top:24px;min-height:150px}.plugin-manage .el-card .config-null{text-align:center;margin:20px auto}.plugin-manage .el-input.input-radius,.plugin-manage .el-textarea.input-radius{width:100%}.auth-clientid-table .el-form-item__error,.auth-username-table .el-form-item__error{display:none}.generate-jwt .monaco-container{height:200px}.generate-jwt .el-date-editor.el-input,.generate-jwt .el-date-editor.el-input__inner,.generate-jwt .el-select{width:100%}.generate-jwt .el-table{margin-top:40px}.generate-jwt .jwt-payload-desc{font-size:14px;padding:6px;border-radius:4px;line-height:1.4;margin:16px 0}.modules-view .el-table{margin-top:24px}.modules-view .oper{min-width:50px;font-size:14px;margin-bottom:4px}.listeners-view .el-table{margin-top:24px}.websocket-view .el-form-item--small.el-form-item{margin-bottom:2px}.websocket-view .check-area .el-form-item__content{line-height:20px!important}.websocket-view .operation-area{margin-top:10px!important}.websocket-view .el-input .el-input--medium{margin-bottom:10px!important}.websocket-view .el-select{width:100%}.websocket-view .refresh-btn{font-size:12px;margin-left:8px;cursor:pointer}.websocket-view .connect-state{display:inline-block;margin-left:20px;font-size:14px;color:#a7a7a7}.websocket-view .connect-state span{margin-left:4px}.websocket-view .el-table{margin-top:5px;border-width:0!important}.websocket-view .el-card{margin-top:24px}.websocket-view .el-checkbox,.websocket-view .el-input{margin:5px 0 20px}.http-api{margin-top:0!important}.http-api .link-disabled{cursor:not-allowed}.http-api .el-card{margin-top:24px}.http-api .el-table{border-width:0!important}.http-api .refresh-btn{cursor:pointer;font-size:16px;padding:0!important}.http-api .response-container{line-height:1.5}.http-api .response-container h3{font-size:14px!important}.http-api .response-container a{margin-left:12px}.applications-view .el-date-editor,.applications-view .el-select{width:100%}.applications-view .el-table{margin-top:24px}.applications-view .el-row{margin-top:20px}.applications-view .search-btn{margin-left:8px}.applications-view .el-breadcrumb{margin-top:10px;margin-bottom:20px}.applications-view .el-date-picker{max-width:600px!important}.applications-view .el-form-item--medium .el-form-item__content{line-height:38px}.app-info .el-input.is-disabled .el-input__inner{cursor:text}.users-view .el-table{margin-top:24px}.users-view .change-password{text-align:left}.settings-view .el-card{margin-top:24px}.settings-view .el-switch.is-checked .el-switch__core{background-color:#42d885;border-color:#42d885}.settings-view .confirm-btn .el-form-item{margin-bottom:4px!important}.help-view{width:80%}.help-view .help-item h3{font-size:16px}.help-view .help-item p{color:#b0b0b0;line-height:1.8}.help-view .help-item a{margin-right:16px}.help-view .help-item .follow-link{display:inline-block;width:64px;height:52px;text-align:center;line-height:52px;background:#232429;color:#9ea3b1;margin-bottom:20px}.help-view .help-item .follow-link .iconfont{font-size:24px}.help-view .el-divider{margin:30px 0;background-color:#2b2c30}@media (max-width:996px){.help-view{width:100%}}.not-found{text-align:center;padding:50px}.not-found p{margin-top:30px;font-size:20px}.not-found a{margin-right:20px}.not-found a:hover{text-decoration:underline}@font-face{font-family:element-icons;src:url(/static/fonts/element-icons.535877f.woff) format("woff"),url(/static/fonts/element-icons.732389d.ttf) format("truetype");font-weight:400;font-style:normal}[class*=" el-icon-"],[class^=el-icon-]{font-family:element-icons!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;vertical-align:baseline;display:inline-block;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-icon-ice-cream-round:before{content:"\E6A0"}.el-icon-ice-cream-square:before{content:"\E6A3"}.el-icon-lollipop:before{content:"\E6A4"}.el-icon-potato-strips:before{content:"\E6A5"}.el-icon-milk-tea:before{content:"\E6A6"}.el-icon-ice-drink:before{content:"\E6A7"}.el-icon-ice-tea:before{content:"\E6A9"}.el-icon-coffee:before{content:"\E6AA"}.el-icon-orange:before{content:"\E6AB"}.el-icon-pear:before{content:"\E6AC"}.el-icon-apple:before{content:"\E6AD"}.el-icon-cherry:before{content:"\E6AE"}.el-icon-watermelon:before{content:"\E6AF"}.el-icon-grape:before{content:"\E6B0"}.el-icon-refrigerator:before{content:"\E6B1"}.el-icon-goblet-square-full:before{content:"\E6B2"}.el-icon-goblet-square:before{content:"\E6B3"}.el-icon-goblet-full:before{content:"\E6B4"}.el-icon-goblet:before{content:"\E6B5"}.el-icon-cold-drink:before{content:"\E6B6"}.el-icon-coffee-cup:before{content:"\E6B8"}.el-icon-water-cup:before{content:"\E6B9"}.el-icon-hot-water:before{content:"\E6BA"}.el-icon-ice-cream:before{content:"\E6BB"}.el-icon-dessert:before{content:"\E6BC"}.el-icon-sugar:before{content:"\E6BD"}.el-icon-tableware:before{content:"\E6BE"}.el-icon-burger:before{content:"\E6BF"}.el-icon-knife-fork:before{content:"\E6C1"}.el-icon-fork-spoon:before{content:"\E6C2"}.el-icon-chicken:before{content:"\E6C3"}.el-icon-food:before{content:"\E6C4"}.el-icon-dish-1:before{content:"\E6C5"}.el-icon-dish:before{content:"\E6C6"}.el-icon-moon-night:before{content:"\E6EE"}.el-icon-moon:before{content:"\E6F0"}.el-icon-cloudy-and-sunny:before{content:"\E6F1"}.el-icon-partly-cloudy:before{content:"\E6F2"}.el-icon-cloudy:before{content:"\E6F3"}.el-icon-sunny:before{content:"\E6F6"}.el-icon-sunset:before{content:"\E6F7"}.el-icon-sunrise-1:before{content:"\E6F8"}.el-icon-sunrise:before{content:"\E6F9"}.el-icon-heavy-rain:before{content:"\E6FA"}.el-icon-lightning:before{content:"\E6FB"}.el-icon-light-rain:before{content:"\E6FC"}.el-icon-wind-power:before{content:"\E6FD"}.el-icon-baseball:before{content:"\E712"}.el-icon-soccer:before{content:"\E713"}.el-icon-football:before{content:"\E715"}.el-icon-basketball:before{content:"\E716"}.el-icon-ship:before{content:"\E73F"}.el-icon-truck:before{content:"\E740"}.el-icon-bicycle:before{content:"\E741"}.el-icon-mobile-phone:before{content:"\E6D3"}.el-icon-service:before{content:"\E6D4"}.el-icon-key:before{content:"\E6E2"}.el-icon-unlock:before{content:"\E6E4"}.el-icon-lock:before{content:"\E6E5"}.el-icon-watch:before{content:"\E6FE"}.el-icon-watch-1:before{content:"\E6FF"}.el-icon-timer:before{content:"\E702"}.el-icon-alarm-clock:before{content:"\E703"}.el-icon-map-location:before{content:"\E704"}.el-icon-delete-location:before{content:"\E705"}.el-icon-add-location:before{content:"\E706"}.el-icon-location-information:before{content:"\E707"}.el-icon-location-outline:before{content:"\E708"}.el-icon-location:before{content:"\E79E"}.el-icon-place:before{content:"\E709"}.el-icon-discover:before{content:"\E70A"}.el-icon-first-aid-kit:before{content:"\E70B"}.el-icon-trophy-1:before{content:"\E70C"}.el-icon-trophy:before{content:"\E70D"}.el-icon-medal:before{content:"\E70E"}.el-icon-medal-1:before{content:"\E70F"}.el-icon-stopwatch:before{content:"\E710"}.el-icon-mic:before{content:"\E711"}.el-icon-copy-document:before{content:"\E718"}.el-icon-full-screen:before{content:"\E719"}.el-icon-switch-button:before{content:"\E71B"}.el-icon-aim:before{content:"\E71C"}.el-icon-crop:before{content:"\E71D"}.el-icon-odometer:before{content:"\E71E"}.el-icon-time:before{content:"\E71F"}.el-icon-bangzhu:before{content:"\E724"}.el-icon-close-notification:before{content:"\E726"}.el-icon-microphone:before{content:"\E727"}.el-icon-turn-off-microphone:before{content:"\E728"}.el-icon-position:before{content:"\E729"}.el-icon-postcard:before{content:"\E72A"}.el-icon-message:before{content:"\E72B"}.el-icon-chat-line-square:before{content:"\E72D"}.el-icon-chat-dot-square:before{content:"\E72E"}.el-icon-chat-dot-round:before{content:"\E72F"}.el-icon-chat-square:before{content:"\E730"}.el-icon-chat-line-round:before{content:"\E731"}.el-icon-chat-round:before{content:"\E732"}.el-icon-set-up:before{content:"\E733"}.el-icon-turn-off:before{content:"\E734"}.el-icon-open:before{content:"\E735"}.el-icon-connection:before{content:"\E736"}.el-icon-link:before{content:"\E737"}.el-icon-cpu:before{content:"\E738"}.el-icon-thumb:before{content:"\E739"}.el-icon-female:before{content:"\E73A"}.el-icon-male:before{content:"\E73B"}.el-icon-guide:before{content:"\E73C"}.el-icon-news:before{content:"\E73E"}.el-icon-price-tag:before{content:"\E744"}.el-icon-discount:before{content:"\E745"}.el-icon-wallet:before{content:"\E747"}.el-icon-coin:before{content:"\E748"}.el-icon-money:before{content:"\E749"}.el-icon-bank-card:before{content:"\E74A"}.el-icon-box:before{content:"\E74B"}.el-icon-present:before{content:"\E74C"}.el-icon-sell:before{content:"\E6D5"}.el-icon-sold-out:before{content:"\E6D6"}.el-icon-shopping-bag-2:before{content:"\E74D"}.el-icon-shopping-bag-1:before{content:"\E74E"}.el-icon-shopping-cart-2:before{content:"\E74F"}.el-icon-shopping-cart-1:before{content:"\E750"}.el-icon-shopping-cart-full:before{content:"\E751"}.el-icon-smoking:before{content:"\E752"}.el-icon-no-smoking:before{content:"\E753"}.el-icon-house:before{content:"\E754"}.el-icon-table-lamp:before{content:"\E755"}.el-icon-school:before{content:"\E756"}.el-icon-office-building:before{content:"\E757"}.el-icon-toilet-paper:before{content:"\E758"}.el-icon-notebook-2:before{content:"\E759"}.el-icon-notebook-1:before{content:"\E75A"}.el-icon-files:before{content:"\E75B"}.el-icon-collection:before{content:"\E75C"}.el-icon-receiving:before{content:"\E75D"}.el-icon-suitcase-1:before{content:"\E760"}.el-icon-suitcase:before{content:"\E761"}.el-icon-film:before{content:"\E763"}.el-icon-collection-tag:before{content:"\E765"}.el-icon-data-analysis:before{content:"\E766"}.el-icon-pie-chart:before{content:"\E767"}.el-icon-data-board:before{content:"\E768"}.el-icon-data-line:before{content:"\E76D"}.el-icon-reading:before{content:"\E769"}.el-icon-magic-stick:before{content:"\E76A"}.el-icon-coordinate:before{content:"\E76B"}.el-icon-mouse:before{content:"\E76C"}.el-icon-brush:before{content:"\E76E"}.el-icon-headset:before{content:"\E76F"}.el-icon-umbrella:before{content:"\E770"}.el-icon-scissors:before{content:"\E771"}.el-icon-mobile:before{content:"\E773"}.el-icon-attract:before{content:"\E774"}.el-icon-monitor:before{content:"\E775"}.el-icon-search:before{content:"\E778"}.el-icon-takeaway-box:before{content:"\E77A"}.el-icon-paperclip:before{content:"\E77D"}.el-icon-printer:before{content:"\E77E"}.el-icon-document-add:before{content:"\E782"}.el-icon-document:before{content:"\E785"}.el-icon-document-checked:before{content:"\E786"}.el-icon-document-copy:before{content:"\E787"}.el-icon-document-delete:before{content:"\E788"}.el-icon-document-remove:before{content:"\E789"}.el-icon-tickets:before{content:"\E78B"}.el-icon-folder-checked:before{content:"\E77F"}.el-icon-folder-delete:before{content:"\E780"}.el-icon-folder-remove:before{content:"\E781"}.el-icon-folder-add:before{content:"\E783"}.el-icon-folder-opened:before{content:"\E784"}.el-icon-folder:before{content:"\E78A"}.el-icon-edit-outline:before{content:"\E764"}.el-icon-edit:before{content:"\E78C"}.el-icon-date:before{content:"\E78E"}.el-icon-c-scale-to-original:before{content:"\E7C6"}.el-icon-view:before{content:"\E6CE"}.el-icon-loading:before{content:"\E6CF"}.el-icon-rank:before{content:"\E6D1"}.el-icon-sort-down:before{content:"\E7C4"}.el-icon-sort-up:before{content:"\E7C5"}.el-icon-sort:before{content:"\E6D2"}.el-icon-finished:before{content:"\E6CD"}.el-icon-refresh-left:before{content:"\E6C7"}.el-icon-refresh-right:before{content:"\E6C8"}.el-icon-refresh:before{content:"\E6D0"}.el-icon-video-play:before{content:"\E7C0"}.el-icon-video-pause:before{content:"\E7C1"}.el-icon-d-arrow-right:before{content:"\E6DC"}.el-icon-d-arrow-left:before{content:"\E6DD"}.el-icon-arrow-up:before{content:"\E6E1"}.el-icon-arrow-down:before{content:"\E6DF"}.el-icon-arrow-right:before{content:"\E6E0"}.el-icon-arrow-left:before{content:"\E6DE"}.el-icon-top-right:before{content:"\E6E7"}.el-icon-top-left:before{content:"\E6E8"}.el-icon-top:before{content:"\E6E6"}.el-icon-bottom:before{content:"\E6EB"}.el-icon-right:before{content:"\E6E9"}.el-icon-back:before{content:"\E6EA"}.el-icon-bottom-right:before{content:"\E6EC"}.el-icon-bottom-left:before{content:"\E6ED"}.el-icon-caret-top:before{content:"\E78F"}.el-icon-caret-bottom:before{content:"\E790"}.el-icon-caret-right:before{content:"\E791"}.el-icon-caret-left:before{content:"\E792"}.el-icon-d-caret:before{content:"\E79A"}.el-icon-share:before{content:"\E793"}.el-icon-menu:before{content:"\E798"}.el-icon-s-grid:before{content:"\E7A6"}.el-icon-s-check:before{content:"\E7A7"}.el-icon-s-data:before{content:"\E7A8"}.el-icon-s-opportunity:before{content:"\E7AA"}.el-icon-s-custom:before{content:"\E7AB"}.el-icon-s-claim:before{content:"\E7AD"}.el-icon-s-finance:before{content:"\E7AE"}.el-icon-s-comment:before{content:"\E7AF"}.el-icon-s-flag:before{content:"\E7B0"}.el-icon-s-marketing:before{content:"\E7B1"}.el-icon-s-shop:before{content:"\E7B4"}.el-icon-s-open:before{content:"\E7B5"}.el-icon-s-management:before{content:"\E7B6"}.el-icon-s-ticket:before{content:"\E7B7"}.el-icon-s-release:before{content:"\E7B8"}.el-icon-s-home:before{content:"\E7B9"}.el-icon-s-promotion:before{content:"\E7BA"}.el-icon-s-operation:before{content:"\E7BB"}.el-icon-s-unfold:before{content:"\E7BC"}.el-icon-s-fold:before{content:"\E7A9"}.el-icon-s-platform:before{content:"\E7BD"}.el-icon-s-order:before{content:"\E7BE"}.el-icon-s-cooperation:before{content:"\E7BF"}.el-icon-bell:before{content:"\E725"}.el-icon-message-solid:before{content:"\E799"}.el-icon-video-camera:before{content:"\E772"}.el-icon-video-camera-solid:before{content:"\E796"}.el-icon-camera:before{content:"\E779"}.el-icon-camera-solid:before{content:"\E79B"}.el-icon-download:before{content:"\E77C"}.el-icon-upload2:before{content:"\E77B"}.el-icon-upload:before{content:"\E7C3"}.el-icon-picture-outline-round:before{content:"\E75F"}.el-icon-picture-outline:before{content:"\E75E"}.el-icon-picture:before{content:"\E79F"}.el-icon-close:before{content:"\E6DB"}.el-icon-check:before{content:"\E6DA"}.el-icon-plus:before{content:"\E6D9"}.el-icon-minus:before{content:"\E6D8"}.el-icon-help:before{content:"\E73D"}.el-icon-s-help:before{content:"\E7B3"}.el-icon-circle-close:before{content:"\E78D"}.el-icon-circle-check:before{content:"\E720"}.el-icon-circle-plus-outline:before{content:"\E723"}.el-icon-remove-outline:before{content:"\E722"}.el-icon-zoom-out:before{content:"\E776"}.el-icon-zoom-in:before{content:"\E777"}.el-icon-error:before{content:"\E79D"}.el-icon-success:before{content:"\E79C"}.el-icon-circle-plus:before{content:"\E7A0"}.el-icon-remove:before{content:"\E7A2"}.el-icon-info:before{content:"\E7A1"}.el-icon-question:before{content:"\E7A4"}.el-icon-warning-outline:before{content:"\E6C9"}.el-icon-warning:before{content:"\E7A3"}.el-icon-goods:before{content:"\E7C2"}.el-icon-s-goods:before{content:"\E7B2"}.el-icon-star-off:before{content:"\E717"}.el-icon-star-on:before{content:"\E797"}.el-icon-more-outline:before{content:"\E6CC"}.el-icon-more:before{content:"\E794"}.el-icon-phone-outline:before{content:"\E6CB"}.el-icon-phone:before{content:"\E795"}.el-icon-user:before{content:"\E6E3"}.el-icon-user-solid:before{content:"\E7A5"}.el-icon-setting:before{content:"\E6CA"}.el-icon-s-tools:before{content:"\E7AC"}.el-icon-delete:before{content:"\E6D7"}.el-icon-delete-solid:before{content:"\E7C9"}.el-icon-eleme:before{content:"\E7C7"}.el-icon-platform-eleme:before{content:"\E7CA"}.el-icon-loading{animation:rotating 2s linear infinite}.el-icon--right{margin-left:5px}.el-icon--left{margin-right:5px}@keyframes rotating{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.el-pagination{white-space:nowrap;padding:2px 5px;color:#303133;font-weight:700}.el-pagination:after,.el-pagination:before{display:table;content:""}.el-pagination:after{clear:both}.el-pagination button,.el-pagination span:not([class*=suffix]){display:inline-block;font-size:13px;min-width:35.5px;height:28px;line-height:28px;vertical-align:top;box-sizing:border-box}.el-pagination .el-input__inner{text-align:center;-moz-appearance:textfield;line-height:normal}.el-pagination .el-input__suffix{right:0;transform:scale(.8)}.el-pagination .el-select .el-input{width:100px;margin:0 5px}.el-pagination .el-select .el-input .el-input__inner{padding-right:25px;border-radius:3px}.el-pagination button{border:none;padding:0 6px;background:transparent}.el-pagination button:focus{outline:none}.el-pagination button:hover{color:#42d885}.el-pagination button:disabled{color:#c0c4cc;background-color:#fff;cursor:not-allowed}.el-pagination .btn-next,.el-pagination .btn-prev{background:50% no-repeat;background-size:16px;background-color:#fff;cursor:pointer;margin:0;color:#303133}.el-pagination .btn-next .el-icon,.el-pagination .btn-prev .el-icon{display:block;font-size:12px;font-weight:700}.el-pagination .btn-prev{padding-right:12px}.el-pagination .btn-next{padding-left:12px}.el-pagination .el-pager li.disabled{color:#c0c4cc;cursor:not-allowed}.el-pagination--small .btn-next,.el-pagination--small .btn-prev,.el-pagination--small .el-pager li,.el-pagination--small .el-pager li.btn-quicknext,.el-pagination--small .el-pager li.btn-quickprev,.el-pagination--small .el-pager li:last-child{border-color:transparent;font-size:12px;line-height:22px;height:22px;min-width:22px}.el-pagination--small .arrow.disabled{visibility:hidden}.el-pagination--small .more:before,.el-pagination--small li.more:before{line-height:24px}.el-pagination--small button,.el-pagination--small span:not([class*=suffix]){height:22px;line-height:22px}.el-pagination--small .el-pagination__editor,.el-pagination--small .el-pagination__editor.el-input .el-input__inner{height:22px}.el-pagination__sizes{margin:0 10px 0 0;font-weight:400;color:#606266}.el-pagination__sizes .el-input .el-input__inner{font-size:13px;padding-left:8px}.el-pagination__sizes .el-input .el-input__inner:hover{border-color:#42d885}.el-pagination__total{margin-right:10px;font-weight:400;color:#606266}.el-pagination__jump{margin-left:24px;font-weight:400;color:#606266}.el-pagination__jump .el-input__inner{padding:0 3px}.el-pagination__rightwrapper{float:right}.el-pagination__editor{line-height:18px;padding:0 2px;height:28px;text-align:center;margin:0 2px;box-sizing:border-box;border-radius:3px}.el-pagination__editor.el-input{width:50px}.el-pagination__editor.el-input .el-input__inner{height:28px}.el-pagination__editor .el-input__inner::-webkit-inner-spin-button,.el-pagination__editor .el-input__inner::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.el-pagination.is-background .btn-next,.el-pagination.is-background .btn-prev,.el-pagination.is-background .el-pager li{margin:0 5px;background-color:#f4f4f5;color:#606266;min-width:30px;border-radius:2px}.el-pagination.is-background .btn-next.disabled,.el-pagination.is-background .btn-prev.disabled,.el-pagination.is-background .el-pager li.disabled{color:#c0c4cc}.el-pagination.is-background .btn-next,.el-pagination.is-background .btn-prev{padding:0}.el-pagination.is-background .btn-next:disabled,.el-pagination.is-background .btn-prev:disabled{color:#c0c4cc}.el-pagination.is-background .el-pager li:not(.disabled):hover{color:#42d885}.el-pagination.is-background .el-pager li:not(.disabled).active{background-color:#42d885;color:#fff}.el-pagination.is-background.el-pagination--small .btn-next,.el-pagination.is-background.el-pagination--small .btn-prev,.el-pagination.is-background.el-pagination--small .el-pager li{margin:0 3px;min-width:22px}.el-pager{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;list-style:none;display:inline-block;vertical-align:top;font-size:0;padding:0;margin:0}.el-pager .more:before{line-height:30px}.el-pager li{padding:0 4px;background:#fff;vertical-align:top;display:inline-block;font-size:13px;min-width:35.5px;height:28px;line-height:28px;cursor:pointer;box-sizing:border-box;text-align:center;margin:0}.el-pager li.btn-quicknext,.el-pager li.btn-quickprev{line-height:28px;color:#303133}.el-pager li.btn-quicknext.disabled,.el-pager li.btn-quickprev.disabled{color:#c0c4cc}.el-pager li.btn-quicknext:hover,.el-pager li.btn-quickprev:hover{cursor:pointer}.el-pager li.active+li{border-left:0}.el-pager li:hover{color:#42d885}.el-pager li.active{color:#42d885;cursor:default}.el-dialog{position:relative;margin:0 auto 50px;background:#fff;border-radius:2px;box-shadow:0 1px 3px rgba(0,0,0,.3);box-sizing:border-box;width:50%}.el-dialog.is-fullscreen{width:100%;margin-top:0;margin-bottom:0;height:100%;overflow:auto}.el-dialog__wrapper{position:fixed;top:0;right:0;bottom:0;left:0;overflow:auto;margin:0}.el-dialog__header{padding:20px;padding-bottom:10px}.el-dialog__headerbtn{position:absolute;top:20px;right:20px;padding:0;background:transparent;border:none;outline:none;cursor:pointer;font-size:16px}.el-dialog__headerbtn .el-dialog__close{color:#909399}.el-dialog__headerbtn:focus .el-dialog__close,.el-dialog__headerbtn:hover .el-dialog__close{color:#42d885}.el-dialog__title{line-height:24px;font-size:18px;color:#303133}.el-dialog__body{padding:30px 20px;color:#606266;font-size:14px;word-break:break-all}.el-dialog__footer{padding:20px;padding-top:10px;text-align:right;box-sizing:border-box}.el-dialog--center{text-align:center}.el-dialog--center .el-dialog__body{text-align:initial;padding:25px 25px 30px}.el-dialog--center .el-dialog__footer{text-align:inherit}.dialog-fade-enter-active{animation:dialog-fade-in .3s}.dialog-fade-leave-active{animation:dialog-fade-out .3s}@keyframes dialog-fade-in{0%{transform:translate3d(0,-20px,0);opacity:0}to{transform:translateZ(0);opacity:1}}@keyframes dialog-fade-out{0%{transform:translateZ(0);opacity:1}to{transform:translate3d(0,-20px,0);opacity:0}}.el-autocomplete{position:relative;display:inline-block}.el-autocomplete-suggestion{margin:5px 0;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);border-radius:4px;border:1px solid #e4e7ed;box-sizing:border-box;background-color:#fff}.el-autocomplete-suggestion__wrap{max-height:280px;padding:10px 0;box-sizing:border-box}.el-autocomplete-suggestion__list{margin:0;padding:0}.el-autocomplete-suggestion li{padding:0 20px;margin:0;line-height:34px;cursor:pointer;color:#606266;font-size:14px;list-style:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.el-autocomplete-suggestion li.highlighted,.el-autocomplete-suggestion li:hover{background-color:#f5f7fa}.el-autocomplete-suggestion li.divider{margin-top:6px;border-top:1px solid #000}.el-autocomplete-suggestion li.divider:last-child{margin-bottom:-6px}.el-autocomplete-suggestion.is-loading li{text-align:center;height:100px;line-height:100px;font-size:20px;color:#999}.el-autocomplete-suggestion.is-loading li:after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-autocomplete-suggestion.is-loading li:hover{background-color:#fff}.el-autocomplete-suggestion.is-loading .el-icon-loading{vertical-align:middle}.el-dropdown{display:inline-block;position:relative;color:#606266;font-size:14px}.el-dropdown .el-button-group{display:block}.el-dropdown .el-button-group .el-button{float:none}.el-dropdown .el-dropdown__caret-button{padding-left:5px;padding-right:5px;position:relative;border-left:none}.el-dropdown .el-dropdown__caret-button:before{content:"";position:absolute;display:block;width:1px;top:5px;bottom:5px;left:0;background:hsla(0,0%,100%,.5)}.el-dropdown .el-dropdown__caret-button.el-button--default:before{background:rgba(220,223,230,.5)}.el-dropdown .el-dropdown__caret-button:hover:before{top:0;bottom:0}.el-dropdown .el-dropdown__caret-button .el-dropdown__icon{padding-left:0}.el-dropdown__icon{font-size:12px;margin:0 3px}.el-dropdown .el-dropdown-selfdefine:focus:active,.el-dropdown .el-dropdown-selfdefine:focus:not(.focusing){outline-width:0}.el-dropdown-menu{position:absolute;top:0;left:0;z-index:10;padding:10px 0;margin:5px 0;background-color:#fff;border:1px solid #ebeef5;border-radius:4px;box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-dropdown-menu__item{list-style:none;line-height:36px;padding:0 20px;margin:0;font-size:14px;color:#606266;cursor:pointer;outline:none}.el-dropdown-menu__item:focus,.el-dropdown-menu__item:not(.is-disabled):hover{background-color:#ecfbf3;color:#68e09d}.el-dropdown-menu__item i{margin-right:5px}.el-dropdown-menu__item--divided{position:relative;margin-top:6px;border-top:1px solid #ebeef5}.el-dropdown-menu__item--divided:before{content:"";height:6px;display:block;margin:0 -20px;background-color:#fff}.el-dropdown-menu__item.is-disabled{cursor:default;color:#bbb;pointer-events:none}.el-dropdown-menu--medium{padding:6px 0}.el-dropdown-menu--medium .el-dropdown-menu__item{line-height:30px;padding:0 17px;font-size:14px}.el-dropdown-menu--medium .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:6px}.el-dropdown-menu--medium .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:6px;margin:0 -17px}.el-dropdown-menu--small{padding:6px 0}.el-dropdown-menu--small .el-dropdown-menu__item{line-height:27px;padding:0 15px;font-size:13px}.el-dropdown-menu--small .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:4px}.el-dropdown-menu--small .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:4px;margin:0 -15px}.el-dropdown-menu--mini{padding:3px 0}.el-dropdown-menu--mini .el-dropdown-menu__item{line-height:24px;padding:0 10px;font-size:12px}.el-dropdown-menu--mini .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:3px}.el-dropdown-menu--mini .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:3px;margin:0 -10px}.el-menu{border-right:1px solid #e6e6e6;list-style:none;position:relative;margin:0;padding-left:0;background-color:#fff}.el-menu:after,.el-menu:before{display:table;content:""}.el-menu:after{clear:both}.el-menu.el-menu--horizontal{border-bottom:1px solid #e6e6e6}.el-menu--horizontal{border-right:none}.el-menu--horizontal>.el-menu-item{float:left;height:60px;line-height:60px;margin:0;border-bottom:2px solid transparent;color:#909399}.el-menu--horizontal>.el-menu-item a,.el-menu--horizontal>.el-menu-item a:hover{color:inherit}.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus,.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover{background-color:#fff}.el-menu--horizontal>.el-submenu{float:left}.el-menu--horizontal>.el-submenu:focus,.el-menu--horizontal>.el-submenu:hover{outline:none}.el-menu--horizontal>.el-submenu:focus .el-submenu__title,.el-menu--horizontal>.el-submenu:hover .el-submenu__title{color:#303133}.el-menu--horizontal>.el-submenu.is-active .el-submenu__title{border-bottom:2px solid #42d885;color:#303133}.el-menu--horizontal>.el-submenu .el-submenu__title{height:60px;line-height:60px;border-bottom:2px solid transparent;color:#909399}.el-menu--horizontal>.el-submenu .el-submenu__title:hover{background-color:#fff}.el-menu--horizontal>.el-submenu .el-submenu__icon-arrow{position:static;vertical-align:middle;margin-left:8px;margin-top:-3px}.el-menu--horizontal .el-menu .el-menu-item,.el-menu--horizontal .el-menu .el-submenu__title{background-color:#fff;float:none;height:36px;line-height:36px;padding:0 10px;color:#909399}.el-menu--horizontal .el-menu .el-menu-item.is-active,.el-menu--horizontal .el-menu .el-submenu.is-active>.el-submenu__title{color:#303133}.el-menu--horizontal .el-menu-item:not(.is-disabled):focus,.el-menu--horizontal .el-menu-item:not(.is-disabled):hover{outline:none;color:#303133}.el-menu--horizontal>.el-menu-item.is-active{border-bottom:2px solid #42d885;color:#303133}.el-menu--collapse{width:64px}.el-menu--collapse>.el-menu-item [class^=el-icon-],.el-menu--collapse>.el-submenu>.el-submenu__title [class^=el-icon-]{margin:0;vertical-align:middle;width:24px;text-align:center}.el-menu--collapse>.el-menu-item .el-submenu__icon-arrow,.el-menu--collapse>.el-submenu>.el-submenu__title .el-submenu__icon-arrow{display:none}.el-menu--collapse>.el-menu-item span,.el-menu--collapse>.el-submenu>.el-submenu__title span{height:0;width:0;overflow:hidden;visibility:hidden;display:inline-block}.el-menu--collapse>.el-menu-item.is-active i{color:inherit}.el-menu--collapse .el-menu .el-submenu{min-width:200px}.el-menu--collapse .el-submenu{position:relative}.el-menu--collapse .el-submenu .el-menu{position:absolute;margin-left:5px;top:0;left:100%;z-index:10;border:1px solid #e4e7ed;border-radius:2px;box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-menu--collapse .el-submenu.is-opened>.el-submenu__title .el-submenu__icon-arrow{transform:none}.el-menu--popup{z-index:100;min-width:200px;border:none;padding:5px 0;border-radius:2px;box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-menu--popup-bottom-start{margin-top:5px}.el-menu--popup-right-start{margin-left:5px;margin-right:5px}.el-menu-item{height:56px;line-height:56px;font-size:14px;color:#303133;padding:0 20px;list-style:none;cursor:pointer;position:relative;transition:border-color .3s,background-color .3s,color .3s;box-sizing:border-box;white-space:nowrap}.el-menu-item *{vertical-align:middle}.el-menu-item i{color:#909399}.el-menu-item:focus,.el-menu-item:hover{outline:none;background-color:#ecfbf3}.el-menu-item.is-disabled{opacity:.25;cursor:not-allowed;background:none!important}.el-menu-item [class^=el-icon-]{margin-right:5px;width:24px;text-align:center;font-size:18px;vertical-align:middle}.el-menu-item.is-active{color:#42d885}.el-menu-item.is-active i{color:inherit}.el-submenu{list-style:none;margin:0;padding-left:0}.el-submenu__title{height:56px;line-height:56px;font-size:14px;color:#303133;padding:0 20px;list-style:none;cursor:pointer;position:relative;transition:border-color .3s,background-color .3s,color .3s;box-sizing:border-box;white-space:nowrap}.el-submenu__title *{vertical-align:middle}.el-submenu__title i{color:#909399}.el-submenu__title:focus,.el-submenu__title:hover{outline:none;background-color:#ecfbf3}.el-submenu__title.is-disabled{opacity:.25;cursor:not-allowed;background:none!important}.el-submenu__title:hover{background-color:#ecfbf3}.el-submenu .el-menu{border:none}.el-submenu .el-menu-item{height:50px;line-height:50px;padding:0 45px;min-width:200px}.el-submenu__icon-arrow{position:absolute;top:50%;right:20px;margin-top:-7px;transition:transform .3s;font-size:12px}.el-submenu.is-active .el-submenu__title{border-bottom-color:#42d885}.el-submenu.is-opened>.el-submenu__title .el-submenu__icon-arrow{transform:rotate(180deg)}.el-submenu.is-disabled .el-menu-item,.el-submenu.is-disabled .el-submenu__title{opacity:.25;cursor:not-allowed;background:none!important}.el-submenu [class^=el-icon-]{vertical-align:middle;margin-right:5px;width:24px;text-align:center;font-size:18px}.el-menu-item-group>ul{padding:0}.el-menu-item-group__title{padding:7px 0 7px 20px;line-height:normal;font-size:12px;color:#909399}.horizontal-collapse-transition .el-submenu__title .el-submenu__icon-arrow{transition:.2s;opacity:0}.el-radio-group{display:inline-block;line-height:1;vertical-align:middle;font-size:0}.el-radio-button,.el-radio-button__inner{position:relative;display:inline-block;outline:none}.el-radio-button__inner{line-height:1;white-space:nowrap;vertical-align:middle;background:#fff;border:1px solid #dcdfe6;font-weight:500;border-left:0;color:#606266;-webkit-appearance:none;text-align:center;box-sizing:border-box;margin:0;cursor:pointer;transition:all .3s cubic-bezier(.645,.045,.355,1);padding:12px 20px;font-size:14px;border-radius:0}.el-radio-button__inner.is-round{padding:12px 20px}.el-radio-button__inner:hover{color:#42d885}.el-radio-button__inner [class*=el-icon-]{line-height:.9}.el-radio-button__inner [class*=el-icon-]+span{margin-left:5px}.el-radio-button:first-child .el-radio-button__inner{border-left:1px solid #dcdfe6;border-radius:4px 0 0 4px;box-shadow:none!important}.el-radio-button__orig-radio{opacity:0;outline:none;position:absolute;z-index:-1}.el-radio-button__orig-radio:checked+.el-radio-button__inner{color:#fff;background-color:#42d885;border-color:#42d885;box-shadow:-1px 0 0 0 #42d885}.el-radio-button__orig-radio:disabled+.el-radio-button__inner{color:#c0c4cc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#ebeef5;box-shadow:none}.el-radio-button__orig-radio:disabled:checked+.el-radio-button__inner{background-color:#f2f6fc}.el-radio-button:last-child .el-radio-button__inner{border-radius:0 4px 4px 0}.el-radio-button:first-child:last-child .el-radio-button__inner{border-radius:4px}.el-radio-button--medium .el-radio-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.el-radio-button--medium .el-radio-button__inner.is-round{padding:10px 20px}.el-radio-button--small .el-radio-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.el-radio-button--small .el-radio-button__inner.is-round{padding:9px 15px}.el-radio-button--mini .el-radio-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.el-radio-button--mini .el-radio-button__inner.is-round{padding:7px 15px}.el-radio-button:focus:not(.is-focus):not(:active):not(.is-disabled){box-shadow:0 0 2px 2px #42d885}.el-switch{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;position:relative;font-size:14px;line-height:20px;height:20px;vertical-align:middle}.el-switch.is-disabled .el-switch__core,.el-switch.is-disabled .el-switch__label{cursor:not-allowed}.el-switch__label{transition:.2s;height:20px;display:inline-block;font-size:14px;font-weight:500;cursor:pointer;vertical-align:middle;color:#303133}.el-switch__label.is-active{color:#42d885}.el-switch__label--left{margin-right:10px}.el-switch__label--right{margin-left:10px}.el-switch__label *{line-height:1;font-size:14px;display:inline-block}.el-switch__input{position:absolute;width:0;height:0;opacity:0;margin:0}.el-switch__core{margin:0;display:inline-block;position:relative;width:40px;height:20px;border:1px solid #dcdfe6;outline:none;border-radius:10px;box-sizing:border-box;background:#dcdfe6;cursor:pointer;transition:border-color .3s,background-color .3s;vertical-align:middle}.el-switch__core:after{content:"";position:absolute;top:1px;left:1px;border-radius:100%;transition:all .3s;width:16px;height:16px;background-color:#fff}.el-switch.is-checked .el-switch__core{border-color:#42d885;background-color:#42d885}.el-switch.is-checked .el-switch__core:after{left:100%;margin-left:-17px}.el-switch.is-disabled{opacity:.6}.el-switch--wide .el-switch__label.el-switch__label--left span{left:10px}.el-switch--wide .el-switch__label.el-switch__label--right span{right:10px}.el-switch .label-fade-enter,.el-switch .label-fade-leave-active{opacity:0}.el-select-dropdown{position:absolute;z-index:1001;border:1px solid #e4e7ed;border-radius:4px;background-color:#fff;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-sizing:border-box;margin:5px 0}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected{color:#42d885;background-color:#fff}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected.hover{background-color:#f5f7fa}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected:after{position:absolute;right:20px;font-family:element-icons;content:"\E6DA";font-size:12px;font-weight:700;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-select-dropdown .el-scrollbar.is-empty .el-select-dropdown__list{padding:0}.el-select-dropdown__empty{padding:10px 0;margin:0;text-align:center;color:#999;font-size:14px}.el-select-dropdown__wrap{max-height:274px}.el-select-dropdown__list{list-style:none;padding:6px 0;margin:0;box-sizing:border-box}.el-select-dropdown__item{font-size:14px;padding:0 20px;position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#606266;height:34px;line-height:34px;box-sizing:border-box;cursor:pointer}.el-select-dropdown__item.is-disabled{color:#c0c4cc;cursor:not-allowed}.el-select-dropdown__item.is-disabled:hover{background-color:#fff}.el-select-dropdown__item.hover,.el-select-dropdown__item:hover{background-color:#f5f7fa}.el-select-dropdown__item.selected{color:#42d885;font-weight:700}.el-select-group{margin:0;padding:0}.el-select-group__wrap{position:relative;list-style:none;margin:0;padding:0}.el-select-group__wrap:not(:last-of-type){padding-bottom:24px}.el-select-group__wrap:not(:last-of-type):after{content:"";position:absolute;display:block;left:20px;right:20px;bottom:12px;height:1px;background:#e4e7ed}.el-select-group__title{padding-left:20px;font-size:12px;color:#909399;line-height:30px}.el-select-group .el-select-dropdown__item{padding-left:20px}.el-select{display:inline-block;position:relative}.el-select .el-select__tags>span{display:contents}.el-select:hover .el-input__inner{border-color:#c0c4cc}.el-select .el-input__inner{cursor:pointer;padding-right:35px}.el-select .el-input__inner:focus{border-color:#42d885}.el-select .el-input .el-select__caret{color:#c0c4cc;font-size:14px;transition:transform .3s;transform:rotate(180deg);cursor:pointer}.el-select .el-input .el-select__caret.is-reverse{transform:rotate(0deg)}.el-select .el-input .el-select__caret.is-show-close{font-size:14px;text-align:center;transform:rotate(180deg);border-radius:100%;color:#c0c4cc;transition:color .2s cubic-bezier(.645,.045,.355,1)}.el-select .el-input .el-select__caret.is-show-close:hover{color:#909399}.el-select .el-input.is-disabled .el-input__inner{cursor:not-allowed}.el-select .el-input.is-disabled .el-input__inner:hover{border-color:#e4e7ed}.el-select .el-input.is-focus .el-input__inner{border-color:#42d885}.el-select>.el-input{display:block}.el-select__input{border:none;outline:none;padding:0;margin-left:15px;color:#666;font-size:14px;-webkit-appearance:none;-moz-appearance:none;appearance:none;height:28px;background-color:transparent}.el-select__input.is-mini{height:14px}.el-select__close{cursor:pointer;position:absolute;top:8px;z-index:1000;right:25px;color:#c0c4cc;line-height:18px;font-size:14px}.el-select__close:hover{color:#909399}.el-select__tags{position:absolute;line-height:normal;white-space:normal;z-index:1;top:50%;transform:translateY(-50%);display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-wrap:wrap;flex-wrap:wrap}.el-select .el-tag__close{margin-top:-2px}.el-select .el-tag{box-sizing:border-box;border-color:transparent;margin:2px 0 2px 6px;background-color:#f0f2f5}.el-select .el-tag__close.el-icon-close{background-color:#c0c4cc;right:-7px;top:0;color:#fff}.el-select .el-tag__close.el-icon-close:hover{background-color:#909399}.el-select .el-tag__close.el-icon-close:before{display:block;transform:translateY(.5px)}.el-table{position:relative;overflow:hidden;box-sizing:border-box;-ms-flex:1;flex:1;width:100%;max-width:100%;background-color:#fff;font-size:14px;color:#606266}.el-table__empty-block{min-height:60px;text-align:center;width:100%;height:100%;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center}.el-table__empty-text{line-height:60px;width:50%;color:#909399}.el-table__expand-column .cell{padding:0;text-align:center}.el-table__expand-icon{position:relative;cursor:pointer;color:#666;font-size:12px;transition:transform .2s ease-in-out;height:20px}.el-table__expand-icon--expanded{transform:rotate(90deg)}.el-table__expand-icon>.el-icon{position:absolute;left:50%;top:50%;margin-left:-5px;margin-top:-5px}.el-table__expanded-cell{background-color:#fff}.el-table__expanded-cell[class*=cell]{padding:20px 50px}.el-table__expanded-cell:hover{background-color:transparent!important}.el-table__placeholder{display:inline-block;width:20px}.el-table__append-wrapper{overflow:hidden}.el-table--fit{border-right:0;border-bottom:0}.el-table--fit td.gutter,.el-table--fit th.gutter{border-right-width:1px}.el-table--scrollable-x .el-table__body-wrapper{overflow-x:auto}.el-table--scrollable-y .el-table__body-wrapper{overflow-y:auto}.el-table thead{color:#909399;font-weight:500}.el-table thead.is-group th{background:#f5f7fa}.el-table td,.el-table th{padding:12px 0;min-width:0;box-sizing:border-box;text-overflow:ellipsis;vertical-align:middle;position:relative;text-align:left}.el-table td.is-center,.el-table th.is-center{text-align:center}.el-table td.is-right,.el-table th.is-right{text-align:right}.el-table td.gutter,.el-table th.gutter{width:15px;border-right-width:0;border-bottom-width:0;padding:0}.el-table td.is-hidden>*,.el-table th.is-hidden>*{visibility:hidden}.el-table--medium td,.el-table--medium th{padding:10px 0}.el-table--small{font-size:12px}.el-table--small td,.el-table--small th{padding:8px 0}.el-table--mini{font-size:12px}.el-table--mini td,.el-table--mini th{padding:6px 0}.el-table tr{background-color:#fff}.el-table tr input[type=checkbox]{margin:0}.el-table td,.el-table th.is-leaf{border-bottom:1px solid #ebeef5}.el-table th.is-sortable{cursor:pointer}.el-table th{white-space:nowrap;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#fff}.el-table th div{padding-left:10px;padding-right:10px;line-height:40px;overflow:hidden;white-space:nowrap}.el-table th>.cell,.el-table th div{display:inline-block;box-sizing:border-box;text-overflow:ellipsis}.el-table th>.cell{position:relative;word-wrap:normal;vertical-align:middle;width:100%}.el-table th>.cell.highlight{color:#42d885}.el-table th.required>div:before{display:inline-block;content:"";width:8px;height:8px;border-radius:50%;background:#ff4d51;margin-right:5px;vertical-align:middle}.el-table td div{box-sizing:border-box}.el-table td.gutter{width:0}.el-table .cell{box-sizing:border-box;overflow:hidden;text-overflow:ellipsis;white-space:normal;word-break:break-all;line-height:23px;padding-left:10px;padding-right:10px}.el-table .cell.el-tooltip{white-space:nowrap;min-width:50px}.el-table--border,.el-table--group{border:1px solid #ebeef5}.el-table--border:after,.el-table--group:after,.el-table:before{content:"";position:absolute;background-color:#ebeef5;z-index:1}.el-table--border:after,.el-table--group:after{top:0;right:0;width:1px;height:100%}.el-table:before{left:0;bottom:0;width:100%;height:1px}.el-table--border{border-right:none;border-bottom:none}.el-table--border.el-loading-parent--relative{border-color:transparent}.el-table--border td,.el-table--border th{border-right:1px solid #ebeef5}.el-table--border td:first-child .cell,.el-table--border th:first-child .cell{padding-left:10px}.el-table--border th.gutter:last-of-type{border-bottom:1px solid #ebeef5;border-bottom-width:1px}.el-table--border th{border-bottom:1px solid #ebeef5}.el-table--hidden{visibility:hidden}.el-table__fixed,.el-table__fixed-right{position:absolute;top:0;left:0;overflow-x:hidden;overflow-y:hidden;box-shadow:0 0 10px rgba(0,0,0,.12)}.el-table__fixed-right:before,.el-table__fixed:before{content:"";position:absolute;left:0;bottom:0;width:100%;height:1px;background-color:#ebeef5;z-index:4}.el-table__fixed-right-patch{position:absolute;top:-1px;right:0;background-color:#fff;border-bottom:1px solid #ebeef5}.el-table__fixed-right{top:0;left:auto;right:0}.el-table__fixed-right .el-table__fixed-body-wrapper,.el-table__fixed-right .el-table__fixed-footer-wrapper,.el-table__fixed-right .el-table__fixed-header-wrapper{left:auto;right:0}.el-table__fixed-header-wrapper{position:absolute;left:0;top:0;z-index:3}.el-table__fixed-footer-wrapper{position:absolute;left:0;bottom:0;z-index:3}.el-table__fixed-footer-wrapper tbody td{border-top:1px solid #ebeef5;background-color:#f5f7fa;color:#606266}.el-table__fixed-body-wrapper{position:absolute;left:0;top:37px;overflow:hidden;z-index:3}.el-table__body-wrapper,.el-table__footer-wrapper,.el-table__header-wrapper{width:100%}.el-table__footer-wrapper{margin-top:-1px}.el-table__footer-wrapper td{border-top:1px solid #ebeef5}.el-table__body,.el-table__footer,.el-table__header{table-layout:fixed;border-collapse:separate}.el-table__footer-wrapper,.el-table__header-wrapper{overflow:hidden}.el-table__footer-wrapper tbody td,.el-table__header-wrapper tbody td{background-color:#f5f7fa;color:#606266}.el-table__body-wrapper{overflow:hidden;position:relative}.el-table__body-wrapper.is-scrolling-left~.el-table__fixed,.el-table__body-wrapper.is-scrolling-none~.el-table__fixed,.el-table__body-wrapper.is-scrolling-none~.el-table__fixed-right,.el-table__body-wrapper.is-scrolling-right~.el-table__fixed-right{box-shadow:none}.el-table__body-wrapper .el-table--border.is-scrolling-right~.el-table__fixed-right{border-left:1px solid #ebeef5}.el-table__body-wrapper .el-table--border.is-scrolling-left~.el-table__fixed{border-right:1px solid #ebeef5}.el-table .caret-wrapper{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-align:center;align-items:center;height:34px;width:24px;vertical-align:middle;cursor:pointer;overflow:initial;position:relative}.el-table .sort-caret{width:0;height:0;border:5px solid transparent;position:absolute;left:7px}.el-table .sort-caret.ascending{border-bottom-color:#c0c4cc;top:5px}.el-table .sort-caret.descending{border-top-color:#c0c4cc;bottom:7px}.el-table .ascending .sort-caret.ascending{border-bottom-color:#42d885}.el-table .descending .sort-caret.descending{border-top-color:#42d885}.el-table .hidden-columns{visibility:hidden;position:absolute;z-index:-1}.el-table--striped .el-table__body tr.el-table__row--striped td{background:#fafafa}.el-table--striped .el-table__body tr.el-table__row--striped.current-row td{background-color:#ecfbf3}.el-table__body tr.hover-row.current-row>td,.el-table__body tr.hover-row.el-table__row--striped.current-row>td,.el-table__body tr.hover-row.el-table__row--striped>td,.el-table__body tr.hover-row>td{background-color:#f5f7fa}.el-table__body tr.current-row>td{background-color:#ecfbf3}.el-table__column-resize-proxy{position:absolute;left:200px;top:0;bottom:0;width:0;border-left:1px solid #ebeef5;z-index:10}.el-table__column-filter-trigger{display:inline-block;line-height:34px;cursor:pointer}.el-table__column-filter-trigger i{color:#909399;font-size:12px;transform:scale(.75)}.el-table--enable-row-transition .el-table__body td{transition:background-color .25s ease}.el-table--enable-row-hover .el-table__body tr:hover>td{background-color:#f5f7fa}.el-table--fluid-height .el-table__fixed,.el-table--fluid-height .el-table__fixed-right{bottom:0;overflow:hidden}.el-table [class*=el-table__row--level] .el-table__expand-icon{display:inline-block;width:20px;line-height:20px;height:20px;text-align:center;margin-right:3px}.el-table-column--selection .cell{padding-left:14px;padding-right:14px}.el-table-filter{border:1px solid #ebeef5;border-radius:2px;background-color:#fff;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-sizing:border-box;margin:2px 0}.el-table-filter__list{padding:5px 0;margin:0;list-style:none;min-width:100px}.el-table-filter__list-item{line-height:36px;padding:0 10px;cursor:pointer;font-size:14px}.el-table-filter__list-item:hover{background-color:#ecfbf3;color:#68e09d}.el-table-filter__list-item.is-active{background-color:#42d885;color:#fff}.el-table-filter__content{min-width:100px}.el-table-filter__bottom{border-top:1px solid #ebeef5;padding:8px}.el-table-filter__bottom button{background:transparent;border:none;color:#606266;cursor:pointer;font-size:13px;padding:0 3px}.el-table-filter__bottom button:hover{color:#42d885}.el-table-filter__bottom button:focus{outline:none}.el-table-filter__bottom button.is-disabled{color:#c0c4cc;cursor:not-allowed}.el-table-filter__wrap{max-height:280px}.el-table-filter__checkbox-group{padding:10px}.el-table-filter__checkbox-group label.el-checkbox{display:block;margin-right:5px;margin-bottom:8px;margin-left:5px}.el-table-filter__checkbox-group .el-checkbox:last-child{margin-bottom:0}.el-date-table{font-size:12px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.el-date-table.is-week-mode .el-date-table__row:hover div{background-color:#f2f6fc}.el-date-table.is-week-mode .el-date-table__row:hover td.available:hover{color:#606266}.el-date-table.is-week-mode .el-date-table__row:hover td:first-child div{margin-left:5px;border-top-left-radius:15px;border-bottom-left-radius:15px}.el-date-table.is-week-mode .el-date-table__row:hover td:last-child div{margin-right:5px;border-top-right-radius:15px;border-bottom-right-radius:15px}.el-date-table.is-week-mode .el-date-table__row.current div{background-color:#f2f6fc}.el-date-table td{width:32px;height:30px;padding:4px 0;box-sizing:border-box;text-align:center;cursor:pointer;position:relative}.el-date-table td div{height:30px;padding:3px 0;box-sizing:border-box}.el-date-table td span{width:24px;height:24px;display:block;margin:0 auto;line-height:24px;position:absolute;left:50%;transform:translateX(-50%);border-radius:50%}.el-date-table td.next-month,.el-date-table td.prev-month{color:#c0c4cc}.el-date-table td.today{position:relative}.el-date-table td.today span{color:#42d885;font-weight:700}.el-date-table td.today.end-date span,.el-date-table td.today.start-date span{color:#fff}.el-date-table td.available:hover{color:#42d885}.el-date-table td.in-range div,.el-date-table td.in-range div:hover{background-color:#f2f6fc}.el-date-table td.current:not(.disabled) span{color:#fff;background-color:#42d885}.el-date-table td.end-date div,.el-date-table td.start-date div{color:#fff}.el-date-table td.end-date span,.el-date-table td.start-date span{background-color:#42d885}.el-date-table td.start-date div{margin-left:5px;border-top-left-radius:15px;border-bottom-left-radius:15px}.el-date-table td.end-date div{margin-right:5px;border-top-right-radius:15px;border-bottom-right-radius:15px}.el-date-table td.disabled div{background-color:#f5f7fa;opacity:1;cursor:not-allowed;color:#c0c4cc}.el-date-table td.selected div{margin-left:5px;margin-right:5px;background-color:#f2f6fc;border-radius:15px}.el-date-table td.selected div:hover{background-color:#f2f6fc}.el-date-table td.selected span{background-color:#42d885;color:#fff;border-radius:15px}.el-date-table td.week{font-size:80%;color:#606266}.el-date-table th{padding:5px;color:#606266;font-weight:400;border-bottom:1px solid #ebeef5}.el-month-table{font-size:12px;margin:-1px;border-collapse:collapse}.el-month-table td{text-align:center;padding:8px 0;cursor:pointer}.el-month-table td div{height:48px;padding:6px 0;box-sizing:border-box}.el-month-table td.today .cell{color:#42d885;font-weight:700}.el-month-table td.today.end-date .cell,.el-month-table td.today.start-date .cell{color:#fff}.el-month-table td.disabled .cell{background-color:#f5f7fa;cursor:not-allowed;color:#c0c4cc}.el-month-table td.disabled .cell:hover{color:#c0c4cc}.el-month-table td .cell{width:60px;height:36px;display:block;line-height:36px;color:#606266;margin:0 auto;border-radius:18px}.el-month-table td .cell:hover{color:#42d885}.el-month-table td.in-range div,.el-month-table td.in-range div:hover{background-color:#f2f6fc}.el-month-table td.end-date div,.el-month-table td.start-date div{color:#fff}.el-month-table td.end-date .cell,.el-month-table td.start-date .cell{color:#fff;background-color:#42d885}.el-month-table td.start-date div{border-top-left-radius:24px;border-bottom-left-radius:24px}.el-month-table td.end-date div{border-top-right-radius:24px;border-bottom-right-radius:24px}.el-month-table td.current:not(.disabled) .cell{color:#42d885}.el-year-table{font-size:12px;margin:-1px;border-collapse:collapse}.el-year-table .el-icon{color:#303133}.el-year-table td{text-align:center;padding:20px 3px;cursor:pointer}.el-year-table td.today .cell{color:#42d885;font-weight:700}.el-year-table td.disabled .cell{background-color:#f5f7fa;cursor:not-allowed;color:#c0c4cc}.el-year-table td.disabled .cell:hover{color:#c0c4cc}.el-year-table td .cell{width:48px;height:32px;display:block;line-height:32px;color:#606266;margin:0 auto}.el-year-table td .cell:hover,.el-year-table td.current:not(.disabled) .cell{color:#42d885}.el-date-range-picker{width:646px}.el-date-range-picker.has-sidebar{width:756px}.el-date-range-picker table{table-layout:fixed;width:100%}.el-date-range-picker .el-picker-panel__body{min-width:513px}.el-date-range-picker .el-picker-panel__content{margin:0}.el-date-range-picker__header{position:relative;text-align:center;height:28px}.el-date-range-picker__header [class*=arrow-left]{float:left}.el-date-range-picker__header [class*=arrow-right]{float:right}.el-date-range-picker__header div{font-size:16px;font-weight:500;margin-right:50px}.el-date-range-picker__content{float:left;width:50%;box-sizing:border-box;margin:0;padding:16px}.el-date-range-picker__content.is-left{border-right:1px solid #e4e4e4}.el-date-range-picker__content .el-date-range-picker__header div{margin-left:50px;margin-right:50px}.el-date-range-picker__editors-wrap{box-sizing:border-box;display:table-cell}.el-date-range-picker__editors-wrap.is-right{text-align:right}.el-date-range-picker__time-header{position:relative;border-bottom:1px solid #e4e4e4;font-size:12px;padding:8px 5px 5px;display:table;width:100%;box-sizing:border-box}.el-date-range-picker__time-header>.el-icon-arrow-right{font-size:20px;vertical-align:middle;display:table-cell;color:#303133}.el-date-range-picker__time-picker-wrap{position:relative;display:table-cell;padding:0 5px}.el-date-range-picker__time-picker-wrap .el-picker-panel{position:absolute;top:13px;right:0;z-index:1;background:#fff}.el-date-picker{width:322px}.el-date-picker.has-sidebar.has-time{width:434px}.el-date-picker.has-sidebar{width:438px}.el-date-picker.has-time .el-picker-panel__body-wrapper{position:relative}.el-date-picker .el-picker-panel__content{width:292px}.el-date-picker table{table-layout:fixed;width:100%}.el-date-picker__editor-wrap{position:relative;display:table-cell;padding:0 5px}.el-date-picker__time-header{position:relative;border-bottom:1px solid #e4e4e4;font-size:12px;padding:8px 5px 5px;display:table;width:100%;box-sizing:border-box}.el-date-picker__header{margin:12px;text-align:center}.el-date-picker__header--bordered{margin-bottom:0;padding-bottom:12px;border-bottom:1px solid #ebeef5}.el-date-picker__header--bordered+.el-picker-panel__content{margin-top:0}.el-date-picker__header-label{font-size:16px;font-weight:500;padding:0 5px;line-height:22px;text-align:center;cursor:pointer;color:#606266}.el-date-picker__header-label.active,.el-date-picker__header-label:hover{color:#42d885}.el-date-picker__prev-btn{float:left}.el-date-picker__next-btn{float:right}.el-date-picker__time-wrap{padding:10px;text-align:center}.el-date-picker__time-label{float:left;cursor:pointer;line-height:30px;margin-left:10px}.time-select{margin:5px 0;min-width:0}.time-select .el-picker-panel__content{max-height:200px;margin:0}.time-select-item{padding:8px 10px;font-size:14px;line-height:20px}.time-select-item.selected:not(.disabled){color:#42d885;font-weight:700}.time-select-item.disabled{color:#e4e7ed;cursor:not-allowed}.time-select-item:hover{background-color:#f5f7fa;font-weight:700;cursor:pointer}.el-date-editor{position:relative;display:inline-block;text-align:left}.el-date-editor.el-input,.el-date-editor.el-input__inner{width:220px}.el-date-editor--monthrange.el-input,.el-date-editor--monthrange.el-input__inner{width:300px}.el-date-editor--daterange.el-input,.el-date-editor--daterange.el-input__inner,.el-date-editor--timerange.el-input,.el-date-editor--timerange.el-input__inner{width:350px}.el-date-editor--datetimerange.el-input,.el-date-editor--datetimerange.el-input__inner{width:400px}.el-date-editor--dates .el-input__inner{text-overflow:ellipsis;white-space:nowrap}.el-date-editor .el-icon-circle-close{cursor:pointer}.el-date-editor .el-range__icon{font-size:14px;margin-left:-5px;color:#c0c4cc;float:left;line-height:32px}.el-date-editor .el-range-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:none;outline:none;display:inline-block;height:100%;margin:0;padding:0;width:39%;text-align:center;font-size:14px;color:#606266}.el-date-editor .el-range-input::-webkit-input-placeholder{color:#c0c4cc}.el-date-editor .el-range-input::-ms-input-placeholder{color:#c0c4cc}.el-date-editor .el-range-input::placeholder{color:#c0c4cc}.el-date-editor .el-range-separator{display:inline-block;height:100%;padding:0 5px;margin:0;text-align:center;line-height:32px;font-size:14px;width:5%;color:#303133}.el-date-editor .el-range__close-icon{font-size:14px;color:#c0c4cc;width:25px;display:inline-block;float:right;line-height:32px}.el-range-editor.el-input__inner{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding:3px 10px}.el-range-editor .el-range-input{line-height:1}.el-range-editor.is-active,.el-range-editor.is-active:hover{border-color:#42d885}.el-range-editor--medium.el-input__inner{height:36px}.el-range-editor--medium .el-range-separator{line-height:28px;font-size:14px}.el-range-editor--medium .el-range-input{font-size:14px}.el-range-editor--medium .el-range__close-icon,.el-range-editor--medium .el-range__icon{line-height:28px}.el-range-editor--small.el-input__inner{height:32px}.el-range-editor--small .el-range-separator{line-height:24px;font-size:13px}.el-range-editor--small .el-range-input{font-size:13px}.el-range-editor--small .el-range__close-icon,.el-range-editor--small .el-range__icon{line-height:24px}.el-range-editor--mini.el-input__inner{height:28px}.el-range-editor--mini .el-range-separator{line-height:20px;font-size:12px}.el-range-editor--mini .el-range-input{font-size:12px}.el-range-editor--mini .el-range__close-icon,.el-range-editor--mini .el-range__icon{line-height:20px}.el-range-editor.is-disabled{background-color:#f5f7fa;border-color:#e4e7ed;color:#c0c4cc;cursor:not-allowed}.el-range-editor.is-disabled:focus,.el-range-editor.is-disabled:hover{border-color:#e4e7ed}.el-range-editor.is-disabled input{background-color:#f5f7fa;color:#c0c4cc;cursor:not-allowed}.el-range-editor.is-disabled input::-webkit-input-placeholder{color:#c0c4cc}.el-range-editor.is-disabled input::-ms-input-placeholder{color:#c0c4cc}.el-range-editor.is-disabled input::placeholder{color:#c0c4cc}.el-range-editor.is-disabled .el-range-separator{color:#c0c4cc}.el-picker-panel{color:#606266;border:1px solid #e4e7ed;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);background:#fff;border-radius:4px;line-height:30px;margin:5px 0}.el-picker-panel__body-wrapper:after,.el-picker-panel__body:after{content:"";display:table;clear:both}.el-picker-panel__content{position:relative;margin:15px}.el-picker-panel__footer{border-top:1px solid #e4e4e4;padding:4px;text-align:right;background-color:#fff;position:relative;font-size:0}.el-picker-panel__shortcut{display:block;width:100%;border:0;background-color:transparent;line-height:28px;font-size:14px;color:#606266;padding-left:12px;text-align:left;outline:none;cursor:pointer}.el-picker-panel__shortcut:hover{color:#42d885}.el-picker-panel__shortcut.active{background-color:#e6f1fe;color:#42d885}.el-picker-panel__btn{border:1px solid #dcdcdc;color:#333;line-height:24px;border-radius:2px;padding:0 20px;cursor:pointer;background-color:transparent;outline:none;font-size:12px}.el-picker-panel__btn[disabled]{color:#ccc;cursor:not-allowed}.el-picker-panel__icon-btn{font-size:12px;color:#303133;border:0;background:transparent;cursor:pointer;outline:none;margin-top:8px}.el-picker-panel__icon-btn:hover{color:#42d885}.el-picker-panel__icon-btn.is-disabled{color:#bbb}.el-picker-panel__icon-btn.is-disabled:hover{cursor:not-allowed}.el-picker-panel__link-btn{vertical-align:middle}.el-picker-panel [slot=sidebar],.el-picker-panel__sidebar{position:absolute;top:0;bottom:0;width:110px;border-right:1px solid #e4e4e4;box-sizing:border-box;padding-top:6px;background-color:#fff;overflow:auto}.el-picker-panel [slot=sidebar]+.el-picker-panel__body,.el-picker-panel__sidebar+.el-picker-panel__body{margin-left:110px}.el-time-spinner.has-seconds .el-time-spinner__wrapper{width:33.3%}.el-time-spinner__wrapper{max-height:190px;overflow:auto;display:inline-block;width:50%;vertical-align:top;position:relative}.el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default){padding-bottom:15px}.el-time-spinner__wrapper.is-arrow{box-sizing:border-box;text-align:center;overflow:hidden}.el-time-spinner__wrapper.is-arrow .el-time-spinner__list{transform:translateY(-32px)}.el-time-spinner__wrapper.is-arrow .el-time-spinner__item:hover:not(.disabled):not(.active){background:#fff;cursor:default}.el-time-spinner__arrow{font-size:12px;color:#909399;position:absolute;left:0;width:100%;z-index:1;text-align:center;height:30px;line-height:30px;cursor:pointer}.el-time-spinner__arrow:hover{color:#42d885}.el-time-spinner__arrow.el-icon-arrow-up{top:10px}.el-time-spinner__arrow.el-icon-arrow-down{bottom:10px}.el-time-spinner__input.el-input{width:70%}.el-time-spinner__input.el-input .el-input__inner,.el-time-spinner__list{padding:0;text-align:center}.el-time-spinner__list{margin:0;list-style:none}.el-time-spinner__list:after,.el-time-spinner__list:before{content:"";display:block;width:100%;height:80px}.el-time-spinner__item{height:32px;line-height:32px;font-size:12px;color:#606266}.el-time-spinner__item:hover:not(.disabled):not(.active){background:#f5f7fa;cursor:pointer}.el-time-spinner__item.active:not(.disabled){color:#303133;font-weight:700}.el-time-spinner__item.disabled{color:#c0c4cc;cursor:not-allowed}.el-time-panel{margin:5px 0;border:1px solid #e4e7ed;background-color:#fff;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);border-radius:2px;position:absolute;width:180px;left:0;z-index:1000;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;box-sizing:content-box}.el-time-panel__content{font-size:0;position:relative;overflow:hidden}.el-time-panel__content:after,.el-time-panel__content:before{content:"";top:50%;position:absolute;margin-top:-15px;height:32px;z-index:-1;left:0;right:0;box-sizing:border-box;padding-top:6px;text-align:left;border-top:1px solid #e4e7ed;border-bottom:1px solid #e4e7ed}.el-time-panel__content:after{left:50%;margin-left:12%;margin-right:12%}.el-time-panel__content:before{padding-left:50%;margin-right:12%;margin-left:12%}.el-time-panel__content.has-seconds:after{left:66.66667%}.el-time-panel__content.has-seconds:before{padding-left:33.33333%}.el-time-panel__footer{border-top:1px solid #e4e4e4;padding:4px;height:36px;line-height:25px;text-align:right;box-sizing:border-box}.el-time-panel__btn{border:none;line-height:28px;padding:0 5px;margin:0 5px;cursor:pointer;background-color:transparent;outline:none;font-size:12px;color:#303133}.el-time-panel__btn.confirm{font-weight:800;color:#42d885}.el-time-range-picker{width:354px;overflow:visible}.el-time-range-picker__content{position:relative;text-align:center;padding:10px}.el-time-range-picker__cell{box-sizing:border-box;margin:0;padding:4px 7px 7px;width:50%;display:inline-block}.el-time-range-picker__header{margin-bottom:5px;text-align:center;font-size:14px}.el-time-range-picker__body{border-radius:2px;border:1px solid #e4e7ed}.el-popover{position:absolute;background:#fff;min-width:150px;border-radius:4px;border:1px solid #ebeef5;padding:12px;z-index:2000;color:#606266;line-height:1.4;text-align:justify;font-size:14px;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);word-break:break-all}.el-popover--plain{padding:18px 20px}.el-popover__title{color:#303133;font-size:16px;line-height:1;margin-bottom:12px}.el-popover:focus,.el-popover:focus:active,.el-popover__reference:focus:hover,.el-popover__reference:focus:not(.focusing){outline-width:0}.v-modal-enter{animation:v-modal-in .2s ease}.v-modal-leave{animation:v-modal-out .2s ease forwards}@keyframes v-modal-in{0%{opacity:0}}@keyframes v-modal-out{to{opacity:0}}.v-modal{position:fixed;left:0;top:0;width:100%;height:100%;opacity:.5;background:#000}.el-popup-parent--hidden{overflow:hidden}.el-message-box{display:inline-block;width:420px;padding-bottom:10px;vertical-align:middle;background-color:#fff;border-radius:4px;border:1px solid #ebeef5;font-size:18px;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);text-align:left;overflow:hidden;-webkit-backface-visibility:hidden;backface-visibility:hidden}.el-message-box__wrapper{position:fixed;top:0;bottom:0;left:0;right:0;text-align:center}.el-message-box__wrapper:after{content:"";display:inline-block;height:100%;width:0;vertical-align:middle}.el-message-box__header{position:relative;padding:15px;padding-bottom:10px}.el-message-box__title{padding-left:0;margin-bottom:0;font-size:18px;line-height:1;color:#303133}.el-message-box__headerbtn{position:absolute;top:15px;right:15px;padding:0;border:none;outline:none;background:transparent;font-size:16px;cursor:pointer}.el-message-box__headerbtn .el-message-box__close{color:#909399}.el-message-box__headerbtn:focus .el-message-box__close,.el-message-box__headerbtn:hover .el-message-box__close{color:#42d885}.el-message-box__content{position:relative;padding:10px 15px;color:#606266;font-size:14px}.el-message-box__input{padding-top:15px}.el-message-box__input input.invalid,.el-message-box__input input.invalid:focus{border-color:#ff6d6d}.el-message-box__status{position:absolute;top:50%;transform:translateY(-50%);font-size:24px!important}.el-message-box__status:before{padding-left:1px}.el-message-box__status+.el-message-box__message{padding-left:36px;padding-right:12px}.el-message-box__status.el-icon-success{color:#42d885}.el-message-box__status.el-icon-info{color:#909399}.el-message-box__status.el-icon-warning{color:#f9c855}.el-message-box__status.el-icon-error{color:#ff6d6d}.el-message-box__message{margin:0}.el-message-box__message p{margin:0;line-height:24px}.el-message-box__errormsg{color:#ff6d6d;font-size:12px;min-height:18px;margin-top:2px}.el-message-box__btns{padding:5px 15px 0;text-align:right}.el-message-box__btns button:nth-child(2){margin-left:10px}.el-message-box__btns-reverse{-ms-flex-direction:row-reverse;flex-direction:row-reverse}.el-message-box--center{padding-bottom:30px}.el-message-box--center .el-message-box__header{padding-top:30px}.el-message-box--center .el-message-box__title{position:relative;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.el-message-box--center .el-message-box__status{position:relative;top:auto;padding-right:5px;text-align:center;transform:translateY(-1px)}.el-message-box--center .el-message-box__message{margin-left:0}.el-message-box--center .el-message-box__btns,.el-message-box--center .el-message-box__content{text-align:center}.el-message-box--center .el-message-box__content{padding-left:27px;padding-right:27px}.msgbox-fade-enter-active{animation:msgbox-fade-in .3s}.msgbox-fade-leave-active{animation:msgbox-fade-out .3s}@keyframes msgbox-fade-in{0%{transform:translate3d(0,-20px,0);opacity:0}to{transform:translateZ(0);opacity:1}}@keyframes msgbox-fade-out{0%{transform:translateZ(0);opacity:1}to{transform:translate3d(0,-20px,0);opacity:0}}.el-breadcrumb{font-size:14px;line-height:1}.el-breadcrumb:after,.el-breadcrumb:before{display:table;content:""}.el-breadcrumb:after{clear:both}.el-breadcrumb__separator{margin:0 9px;font-weight:700;color:#c0c4cc}.el-breadcrumb__separator[class*=icon]{margin:0 6px;font-weight:400}.el-breadcrumb__item{float:left}.el-breadcrumb__inner{color:#606266}.el-breadcrumb__inner.is-link,.el-breadcrumb__inner a{font-weight:700;text-decoration:none;transition:color .2s cubic-bezier(.645,.045,.355,1);color:#303133}.el-breadcrumb__inner.is-link:hover,.el-breadcrumb__inner a:hover{color:#42d885;cursor:pointer}.el-breadcrumb__item:last-child .el-breadcrumb__inner,.el-breadcrumb__item:last-child .el-breadcrumb__inner:hover,.el-breadcrumb__item:last-child .el-breadcrumb__inner a,.el-breadcrumb__item:last-child .el-breadcrumb__inner a:hover{font-weight:400;color:#606266;cursor:text}.el-breadcrumb__item:last-child .el-breadcrumb__separator{display:none}.el-form--label-left .el-form-item__label{text-align:left}.el-form--label-top .el-form-item__label{float:none;display:inline-block;text-align:left;padding:0 0 10px}.el-form--inline .el-form-item{display:inline-block;margin-right:10px;vertical-align:top}.el-form--inline .el-form-item__label{float:none;display:inline-block}.el-form--inline .el-form-item__content{display:inline-block;vertical-align:top}.el-form--inline.el-form--label-top .el-form-item__content{display:block}.el-form-item{margin-bottom:22px}.el-form-item:after,.el-form-item:before{display:table;content:""}.el-form-item:after{clear:both}.el-form-item .el-form-item{margin-bottom:0}.el-form-item .el-input__validateIcon{display:none}.el-form-item--medium .el-form-item__content,.el-form-item--medium .el-form-item__label{line-height:36px}.el-form-item--small .el-form-item__content,.el-form-item--small .el-form-item__label{line-height:32px}.el-form-item--small.el-form-item{margin-bottom:18px}.el-form-item--small .el-form-item__error{padding-top:2px}.el-form-item--mini .el-form-item__content,.el-form-item--mini .el-form-item__label{line-height:28px}.el-form-item--mini.el-form-item{margin-bottom:18px}.el-form-item--mini .el-form-item__error{padding-top:1px}.el-form-item__label-wrap{float:left}.el-form-item__label-wrap .el-form-item__label{display:inline-block;float:none}.el-form-item__label{text-align:right;vertical-align:middle;float:left;font-size:14px;color:#606266;line-height:40px;padding:0 12px 0 0;box-sizing:border-box}.el-form-item__content{line-height:40px;position:relative;font-size:14px}.el-form-item__content:after,.el-form-item__content:before{display:table;content:""}.el-form-item__content:after{clear:both}.el-form-item__content .el-input-group{vertical-align:top}.el-form-item__error{color:#ff6d6d;font-size:12px;line-height:1;padding-top:4px;position:absolute;top:100%;left:0}.el-form-item__error--inline{position:relative;top:auto;left:auto;display:inline-block;margin-left:10px}.el-form-item.is-required:not(.is-no-asterisk) .el-form-item__label-wrap>.el-form-item__label:before,.el-form-item.is-required:not(.is-no-asterisk)>.el-form-item__label:before{content:"*";color:#ff6d6d;margin-right:4px}.el-form-item.is-error .el-input__inner,.el-form-item.is-error .el-input__inner:focus,.el-form-item.is-error .el-textarea__inner,.el-form-item.is-error .el-textarea__inner:focus{border-color:#ff6d6d}.el-form-item.is-error .el-input-group__append .el-input__inner,.el-form-item.is-error .el-input-group__prepend .el-input__inner{border-color:transparent}.el-form-item.is-error .el-input__validateIcon{color:#ff6d6d}.el-form-item--feedback .el-input__validateIcon{display:inline-block}.el-tabs__header{padding:0;position:relative;margin:0 0 15px}.el-tabs__active-bar{position:absolute;bottom:0;left:0;height:2px;background-color:#42d885;z-index:1;transition:transform .3s cubic-bezier(.645,.045,.355,1);list-style:none}.el-tabs__new-tab{float:right;border:1px solid #d3dce6;height:18px;width:18px;line-height:18px;margin:12px 0 9px 10px;border-radius:3px;text-align:center;font-size:12px;color:#d3dce6;cursor:pointer;transition:all .15s}.el-tabs__new-tab .el-icon-plus{transform:scale(.8)}.el-tabs__new-tab:hover{color:#42d885}.el-tabs__nav-wrap{overflow:hidden;margin-bottom:-1px;position:relative}.el-tabs__nav-wrap:after{content:"";position:absolute;left:0;bottom:0;width:100%;height:2px;background-color:#e4e7ed;z-index:1}.el-tabs__nav-wrap.is-scrollable{padding:0 20px;box-sizing:border-box}.el-tabs__nav-scroll{overflow:hidden}.el-tabs__nav-next,.el-tabs__nav-prev{position:absolute;cursor:pointer;line-height:44px;font-size:12px;color:#909399}.el-tabs__nav-next{right:0}.el-tabs__nav-prev{left:0}.el-tabs__nav{white-space:nowrap;position:relative;transition:transform .3s;float:left;z-index:2}.el-tabs__nav.is-stretch{min-width:100%;display:-ms-flexbox;display:flex}.el-tabs__nav.is-stretch>*{-ms-flex:1;flex:1;text-align:center}.el-tabs__item{padding:0 20px;height:40px;box-sizing:border-box;line-height:40px;display:inline-block;list-style:none;font-size:14px;font-weight:500;color:#303133;position:relative}.el-tabs__item:focus,.el-tabs__item:focus:active{outline:none}.el-tabs__item:focus.is-active.is-focus:not(:active){box-shadow:inset 0 0 2px 2px #42d885;border-radius:3px}.el-tabs__item .el-icon-close{border-radius:50%;text-align:center;transition:all .3s cubic-bezier(.645,.045,.355,1);margin-left:5px}.el-tabs__item .el-icon-close:before{transform:scale(.9);display:inline-block}.el-tabs__item .el-icon-close:hover{background-color:#c0c4cc;color:#fff}.el-tabs__item.is-active{color:#42d885}.el-tabs__item:hover{color:#42d885;cursor:pointer}.el-tabs__item.is-disabled{color:#c0c4cc;cursor:default}.el-tabs__content{overflow:hidden;position:relative}.el-tabs--card>.el-tabs__header{border-bottom:1px solid #e4e7ed}.el-tabs--card>.el-tabs__header .el-tabs__nav-wrap:after{content:none}.el-tabs--card>.el-tabs__header .el-tabs__nav{border:1px solid #e4e7ed;border-bottom:none;border-radius:4px 4px 0 0;box-sizing:border-box}.el-tabs--card>.el-tabs__header .el-tabs__active-bar{display:none}.el-tabs--card>.el-tabs__header .el-tabs__item .el-icon-close{position:relative;font-size:12px;width:0;height:14px;vertical-align:middle;line-height:15px;overflow:hidden;top:-1px;right:-2px;transform-origin:100% 50%}.el-tabs--card>.el-tabs__header .el-tabs__item{border-bottom:1px solid transparent;border-left:1px solid #e4e7ed;transition:color .3s cubic-bezier(.645,.045,.355,1),padding .3s cubic-bezier(.645,.045,.355,1)}.el-tabs--card>.el-tabs__header .el-tabs__item:first-child{border-left:none}.el-tabs--card>.el-tabs__header .el-tabs__item.is-closable:hover{padding-left:13px;padding-right:13px}.el-tabs--card>.el-tabs__header .el-tabs__item.is-closable:hover .el-icon-close{width:14px}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active{border-bottom-color:#fff}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active.is-closable{padding-left:20px;padding-right:20px}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active.is-closable .el-icon-close{width:14px}.el-tabs--border-card{background:#fff;border:1px solid #dcdfe6;box-shadow:0 2px 4px 0 rgba(0,0,0,.12),0 0 6px 0 rgba(0,0,0,.04)}.el-tabs--border-card>.el-tabs__content{padding:15px}.el-tabs--border-card>.el-tabs__header{background-color:#f5f7fa;border-bottom:1px solid #e4e7ed;margin:0}.el-tabs--border-card>.el-tabs__header .el-tabs__nav-wrap:after{content:none}.el-tabs--border-card>.el-tabs__header .el-tabs__item{transition:all .3s cubic-bezier(.645,.045,.355,1);border:1px solid transparent;margin-top:-1px;color:#909399}.el-tabs--border-card>.el-tabs__header .el-tabs__item+.el-tabs__item,.el-tabs--border-card>.el-tabs__header .el-tabs__item:first-child{margin-left:-1px}.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-active{color:#42d885;background-color:#fff;border-right-color:#dcdfe6;border-left-color:#dcdfe6}.el-tabs--border-card>.el-tabs__header .el-tabs__item:not(.is-disabled):hover{color:#42d885}.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-disabled{color:#c0c4cc}.el-tabs--border-card>.el-tabs__header .is-scrollable .el-tabs__item:first-child{margin-left:0}.el-tabs--bottom .el-tabs__item.is-bottom:nth-child(2),.el-tabs--bottom .el-tabs__item.is-top:nth-child(2),.el-tabs--top .el-tabs__item.is-bottom:nth-child(2),.el-tabs--top .el-tabs__item.is-top:nth-child(2){padding-left:0}.el-tabs--bottom .el-tabs__item.is-bottom:last-child,.el-tabs--bottom .el-tabs__item.is-top:last-child,.el-tabs--top .el-tabs__item.is-bottom:last-child,.el-tabs--top .el-tabs__item.is-top:last-child{padding-right:0}.el-tabs--bottom.el-tabs--border-card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom.el-tabs--card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom .el-tabs--left>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom .el-tabs--right>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top.el-tabs--border-card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top.el-tabs--card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top .el-tabs--left>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top .el-tabs--right>.el-tabs__header .el-tabs__item:nth-child(2){padding-left:20px}.el-tabs--bottom.el-tabs--border-card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom.el-tabs--card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom .el-tabs--left>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom .el-tabs--right>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top.el-tabs--border-card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top.el-tabs--card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top .el-tabs--left>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top .el-tabs--right>.el-tabs__header .el-tabs__item:last-child{padding-right:20px}.el-tabs--bottom .el-tabs__header.is-bottom{margin-bottom:0;margin-top:10px}.el-tabs--bottom.el-tabs--border-card .el-tabs__header.is-bottom{border-bottom:0;border-top:1px solid #dcdfe6}.el-tabs--bottom.el-tabs--border-card .el-tabs__nav-wrap.is-bottom{margin-top:-1px;margin-bottom:0}.el-tabs--bottom.el-tabs--border-card .el-tabs__item.is-bottom:not(.is-active){border:1px solid transparent}.el-tabs--bottom.el-tabs--border-card .el-tabs__item.is-bottom{margin:0 -1px -1px}.el-tabs--left,.el-tabs--right{overflow:hidden}.el-tabs--left .el-tabs__header.is-left,.el-tabs--left .el-tabs__header.is-right,.el-tabs--left .el-tabs__nav-scroll,.el-tabs--left .el-tabs__nav-wrap.is-left,.el-tabs--left .el-tabs__nav-wrap.is-right,.el-tabs--right .el-tabs__header.is-left,.el-tabs--right .el-tabs__header.is-right,.el-tabs--right .el-tabs__nav-scroll,.el-tabs--right .el-tabs__nav-wrap.is-left,.el-tabs--right .el-tabs__nav-wrap.is-right{height:100%}.el-tabs--left .el-tabs__active-bar.is-left,.el-tabs--left .el-tabs__active-bar.is-right,.el-tabs--right .el-tabs__active-bar.is-left,.el-tabs--right .el-tabs__active-bar.is-right{top:0;bottom:auto;width:2px;height:auto}.el-tabs--left .el-tabs__nav-wrap.is-left,.el-tabs--left .el-tabs__nav-wrap.is-right,.el-tabs--right .el-tabs__nav-wrap.is-left,.el-tabs--right .el-tabs__nav-wrap.is-right{margin-bottom:0}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev{height:30px;line-height:30px;width:100%;text-align:center;cursor:pointer}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next i,.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev i,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next i,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev i,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next i,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev i,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next i,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev i{transform:rotate(90deg)}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev{left:auto;top:0}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next{right:auto;bottom:0}.el-tabs--left .el-tabs__nav-wrap.is-left.is-scrollable,.el-tabs--left .el-tabs__nav-wrap.is-right.is-scrollable,.el-tabs--right .el-tabs__nav-wrap.is-left.is-scrollable,.el-tabs--right .el-tabs__nav-wrap.is-right.is-scrollable{padding:30px 0}.el-tabs--left .el-tabs__nav-wrap.is-left:after,.el-tabs--left .el-tabs__nav-wrap.is-right:after,.el-tabs--right .el-tabs__nav-wrap.is-left:after,.el-tabs--right .el-tabs__nav-wrap.is-right:after{height:100%;width:2px;bottom:auto;top:0}.el-tabs--left .el-tabs__nav.is-left,.el-tabs--left .el-tabs__nav.is-right,.el-tabs--right .el-tabs__nav.is-left,.el-tabs--right .el-tabs__nav.is-right{float:none}.el-tabs--left .el-tabs__item.is-left,.el-tabs--left .el-tabs__item.is-right,.el-tabs--right .el-tabs__item.is-left,.el-tabs--right .el-tabs__item.is-right{display:block}.el-tabs--left .el-tabs__header.is-left{float:left;margin-bottom:0;margin-right:10px}.el-tabs--left .el-tabs__nav-wrap.is-left{margin-right:-1px}.el-tabs--left .el-tabs__active-bar.is-left,.el-tabs--left .el-tabs__nav-wrap.is-left:after{left:auto;right:0}.el-tabs--left .el-tabs__item.is-left{text-align:right}.el-tabs--left.el-tabs--card .el-tabs__active-bar.is-left{display:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left{border-left:none;border-right:1px solid #e4e7ed;border-bottom:none;border-top:1px solid #e4e7ed;text-align:left}.el-tabs--left.el-tabs--card .el-tabs__item.is-left:first-child{border-right:1px solid #e4e7ed;border-top:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active{border:1px solid #e4e7ed;border-right-color:#fff;border-left:none;border-bottom:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active:first-child{border-top:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active:last-child{border-bottom:none}.el-tabs--left.el-tabs--card .el-tabs__nav{border-radius:4px 0 0 4px;border-bottom:1px solid #e4e7ed;border-right:none}.el-tabs--left.el-tabs--card .el-tabs__new-tab{float:none}.el-tabs--left.el-tabs--border-card .el-tabs__header.is-left{border-right:1px solid #dfe4ed}.el-tabs--left.el-tabs--border-card .el-tabs__item.is-left{border:1px solid transparent;margin:-1px 0 -1px -1px}.el-tabs--left.el-tabs--border-card .el-tabs__item.is-left.is-active{border-color:transparent;border-top-color:#d1dbe5;border-bottom-color:#d1dbe5}.el-tabs--right .el-tabs__header.is-right{float:right;margin-bottom:0;margin-left:10px}.el-tabs--right .el-tabs__nav-wrap.is-right{margin-left:-1px}.el-tabs--right .el-tabs__nav-wrap.is-right:after{left:0;right:auto}.el-tabs--right .el-tabs__active-bar.is-right{left:0}.el-tabs--right.el-tabs--card .el-tabs__active-bar.is-right{display:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right{border-bottom:none;border-top:1px solid #e4e7ed}.el-tabs--right.el-tabs--card .el-tabs__item.is-right:first-child{border-left:1px solid #e4e7ed;border-top:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active{border:1px solid #e4e7ed;border-left-color:#fff;border-right:none;border-bottom:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active:first-child{border-top:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active:last-child{border-bottom:none}.el-tabs--right.el-tabs--card .el-tabs__nav{border-radius:0 4px 4px 0;border-bottom:1px solid #e4e7ed;border-left:none}.el-tabs--right.el-tabs--border-card .el-tabs__header.is-right{border-left:1px solid #dfe4ed}.el-tabs--right.el-tabs--border-card .el-tabs__item.is-right{border:1px solid transparent;margin:-1px -1px -1px 0}.el-tabs--right.el-tabs--border-card .el-tabs__item.is-right.is-active{border-color:transparent;border-top-color:#d1dbe5;border-bottom-color:#d1dbe5}.slideInLeft-transition,.slideInRight-transition{display:inline-block}.slideInRight-enter{animation:slideInRight-enter .3s}.slideInRight-leave{position:absolute;left:0;right:0;animation:slideInRight-leave .3s}.slideInLeft-enter{animation:slideInLeft-enter .3s}.slideInLeft-leave{position:absolute;left:0;right:0;animation:slideInLeft-leave .3s}@keyframes slideInRight-enter{0%{opacity:0;transform-origin:0 0;transform:translateX(100%)}to{opacity:1;transform-origin:0 0;transform:translateX(0)}}@keyframes slideInRight-leave{0%{transform-origin:0 0;transform:translateX(0);opacity:1}to{transform-origin:0 0;transform:translateX(100%);opacity:0}}@keyframes slideInLeft-enter{0%{opacity:0;transform-origin:0 0;transform:translateX(-100%)}to{opacity:1;transform-origin:0 0;transform:translateX(0)}}@keyframes slideInLeft-leave{0%{transform-origin:0 0;transform:translateX(0);opacity:1}to{transform-origin:0 0;transform:translateX(-100%);opacity:0}}.el-tree{position:relative;cursor:default;background:#fff;color:#606266}.el-tree__empty-block{position:relative;min-height:60px;text-align:center;width:100%;height:100%}.el-tree__empty-text{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);color:#909399}.el-tree__drop-indicator{position:absolute;left:0;right:0;height:1px;background-color:#42d885}.el-tree-node{white-space:nowrap;outline:none}.el-tree-node:focus>.el-tree-node__content{background-color:#f5f7fa}.el-tree-node.is-drop-inner>.el-tree-node__content .el-tree-node__label{background-color:#42d885;color:#fff}.el-tree-node__content{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;height:26px;cursor:pointer}.el-tree-node__content>.el-tree-node__expand-icon{padding:6px}.el-tree-node__content>.el-checkbox{margin-right:8px}.el-tree-node__content:hover{background-color:#f5f7fa}.el-tree.is-dragging .el-tree-node__content{cursor:move}.el-tree.is-dragging .el-tree-node__content *{pointer-events:none}.el-tree.is-dragging.is-drop-not-allow .el-tree-node__content{cursor:not-allowed}.el-tree-node__expand-icon{cursor:pointer;color:#c0c4cc;font-size:12px;transform:rotate(0deg);transition:transform .3s ease-in-out}.el-tree-node__expand-icon.expanded{transform:rotate(90deg)}.el-tree-node__expand-icon.is-leaf{color:transparent;cursor:default}.el-tree-node__label{font-size:14px}.el-tree-node__loading-icon{margin-right:8px;font-size:14px;color:#c0c4cc}.el-tree-node>.el-tree-node__children{overflow:hidden;background-color:transparent}.el-tree-node.is-expanded>.el-tree-node__children{display:block}.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content{background-color:#f0fcf5}.el-alert{width:100%;padding:8px 16px;margin:0;box-sizing:border-box;border-radius:4px;position:relative;background-color:#fff;overflow:hidden;opacity:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;transition:opacity .2s}.el-alert.is-light .el-alert__closebtn{color:#c0c4cc}.el-alert.is-dark .el-alert__closebtn,.el-alert.is-dark .el-alert__description{color:#fff}.el-alert.is-center{-ms-flex-pack:center;justify-content:center}.el-alert--success.is-light{background-color:#ecfbf3;color:#42d885}.el-alert--success.is-light .el-alert__description{color:#42d885}.el-alert--success.is-dark{background-color:#42d885;color:#fff}.el-alert--info.is-light{background-color:#f4f4f5;color:#909399}.el-alert--info.is-dark{background-color:#909399;color:#fff}.el-alert--info .el-alert__description{color:#909399}.el-alert--warning.is-light{background-color:#fefaee;color:#f9c855}.el-alert--warning.is-light .el-alert__description{color:#f9c855}.el-alert--warning.is-dark{background-color:#f9c855;color:#fff}.el-alert--error.is-light{background-color:#fff0f0;color:#ff6d6d}.el-alert--error.is-light .el-alert__description{color:#ff6d6d}.el-alert--error.is-dark{background-color:#ff6d6d;color:#fff}.el-alert__content{display:table-cell;padding:0 8px}.el-alert__icon{font-size:16px;width:16px}.el-alert__icon.is-big{font-size:28px;width:28px}.el-alert__title{font-size:13px;line-height:18px}.el-alert__title.is-bold{font-weight:700}.el-alert .el-alert__description{font-size:12px;margin:5px 0 0}.el-alert__closebtn{font-size:12px;opacity:1;position:absolute;top:12px;right:15px;cursor:pointer}.el-alert__closebtn.is-customed{font-style:normal;font-size:13px;top:9px}.el-alert-fade-enter,.el-alert-fade-leave-active{opacity:0}.el-notification{display:-ms-flexbox;display:flex;width:330px;padding:14px 26px 14px 13px;border-radius:8px;box-sizing:border-box;border:1px solid #ebeef5;position:fixed;background-color:#fff;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);transition:opacity .3s,transform .3s,left .3s,right .3s,top .4s,bottom .3s;overflow:hidden}.el-notification.right{right:16px}.el-notification.left{left:16px}.el-notification__group{margin-left:13px;margin-right:8px}.el-notification__title{font-weight:700;font-size:16px;color:#303133;margin:0}.el-notification__content{font-size:14px;line-height:21px;margin:6px 0 0;color:#606266;text-align:justify}.el-notification__content p{margin:0}.el-notification__icon{height:24px;width:24px;font-size:24px}.el-notification__closeBtn{position:absolute;top:18px;right:15px;cursor:pointer;color:#909399;font-size:16px}.el-notification__closeBtn:hover{color:#606266}.el-notification .el-icon-success{color:#42d885}.el-notification .el-icon-error{color:#ff6d6d}.el-notification .el-icon-info{color:#909399}.el-notification .el-icon-warning{color:#f9c855}.el-notification-fade-enter.right{right:0;transform:translateX(100%)}.el-notification-fade-enter.left{left:0;transform:translateX(-100%)}.el-notification-fade-leave-active{opacity:0}.el-input-number{position:relative;display:inline-block;width:180px;line-height:38px}.el-input-number .el-input{display:block}.el-input-number .el-input__inner{-webkit-appearance:none;padding-left:50px;padding-right:50px;text-align:center}.el-input-number__decrease,.el-input-number__increase{position:absolute;z-index:1;top:1px;width:40px;height:auto;text-align:center;background:#f5f7fa;color:#606266;cursor:pointer;font-size:13px}.el-input-number__decrease:hover,.el-input-number__increase:hover{color:#42d885}.el-input-number__decrease:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled),.el-input-number__increase:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled){border-color:#42d885}.el-input-number__decrease.is-disabled,.el-input-number__increase.is-disabled{color:#c0c4cc;cursor:not-allowed}.el-input-number__increase{right:1px;border-radius:0 4px 4px 0;border-left:1px solid #dcdfe6}.el-input-number__decrease{left:1px;border-radius:4px 0 0 4px;border-right:1px solid #dcdfe6}.el-input-number.is-disabled .el-input-number__decrease,.el-input-number.is-disabled .el-input-number__increase{border-color:#e4e7ed;color:#e4e7ed}.el-input-number.is-disabled .el-input-number__decrease:hover,.el-input-number.is-disabled .el-input-number__increase:hover{color:#e4e7ed;cursor:not-allowed}.el-input-number--medium{width:200px;line-height:34px}.el-input-number--medium .el-input-number__decrease,.el-input-number--medium .el-input-number__increase{width:36px;font-size:14px}.el-input-number--medium .el-input__inner{padding-left:43px;padding-right:43px}.el-input-number--small{width:130px;line-height:30px}.el-input-number--small .el-input-number__decrease,.el-input-number--small .el-input-number__increase{width:32px;font-size:13px}.el-input-number--small .el-input-number__decrease [class*=el-icon],.el-input-number--small .el-input-number__increase [class*=el-icon]{transform:scale(.9)}.el-input-number--small .el-input__inner{padding-left:39px;padding-right:39px}.el-input-number--mini{width:130px;line-height:26px}.el-input-number--mini .el-input-number__decrease,.el-input-number--mini .el-input-number__increase{width:28px;font-size:12px}.el-input-number--mini .el-input-number__decrease [class*=el-icon],.el-input-number--mini .el-input-number__increase [class*=el-icon]{transform:scale(.8)}.el-input-number--mini .el-input__inner{padding-left:35px;padding-right:35px}.el-input-number.is-without-controls .el-input__inner{padding-left:15px;padding-right:15px}.el-input-number.is-controls-right .el-input__inner{padding-left:15px;padding-right:50px}.el-input-number.is-controls-right .el-input-number__decrease,.el-input-number.is-controls-right .el-input-number__increase{height:auto;line-height:19px}.el-input-number.is-controls-right .el-input-number__decrease [class*=el-icon],.el-input-number.is-controls-right .el-input-number__increase [class*=el-icon]{transform:scale(.8)}.el-input-number.is-controls-right .el-input-number__increase{border-radius:0 4px 0 0;border-bottom:1px solid #dcdfe6}.el-input-number.is-controls-right .el-input-number__decrease{right:1px;bottom:1px;top:auto;left:auto;border-right:none;border-left:1px solid #dcdfe6;border-radius:0 0 4px 0}.el-input-number.is-controls-right[class*=medium] [class*=decrease],.el-input-number.is-controls-right[class*=medium] [class*=increase]{line-height:17px}.el-input-number.is-controls-right[class*=small] [class*=decrease],.el-input-number.is-controls-right[class*=small] [class*=increase]{line-height:15px}.el-input-number.is-controls-right[class*=mini] [class*=decrease],.el-input-number.is-controls-right[class*=mini] [class*=increase]{line-height:13px}.el-tooltip:focus:hover,.el-tooltip:focus:not(.focusing){outline-width:0}.el-tooltip__popper{position:absolute;border-radius:4px;padding:10px;z-index:2000;font-size:12px;line-height:1.2;min-width:10px;word-wrap:break-word}.el-tooltip__popper .popper__arrow,.el-tooltip__popper .popper__arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.el-tooltip__popper .popper__arrow{border-width:6px}.el-tooltip__popper .popper__arrow:after{content:" ";border-width:5px}.el-tooltip__popper[x-placement^=top]{margin-bottom:12px}.el-tooltip__popper[x-placement^=top] .popper__arrow{bottom:-6px;border-top-color:#303133;border-bottom-width:0}.el-tooltip__popper[x-placement^=top] .popper__arrow:after{bottom:1px;margin-left:-5px;border-top-color:#303133;border-bottom-width:0}.el-tooltip__popper[x-placement^=bottom]{margin-top:12px}.el-tooltip__popper[x-placement^=bottom] .popper__arrow{top:-6px;border-top-width:0;border-bottom-color:#303133}.el-tooltip__popper[x-placement^=bottom] .popper__arrow:after{top:1px;margin-left:-5px;border-top-width:0;border-bottom-color:#303133}.el-tooltip__popper[x-placement^=right]{margin-left:12px}.el-tooltip__popper[x-placement^=right] .popper__arrow{left:-6px;border-right-color:#303133;border-left-width:0}.el-tooltip__popper[x-placement^=right] .popper__arrow:after{bottom:-5px;left:1px;border-right-color:#303133;border-left-width:0}.el-tooltip__popper[x-placement^=left]{margin-right:12px}.el-tooltip__popper[x-placement^=left] .popper__arrow{right:-6px;border-right-width:0;border-left-color:#303133}.el-tooltip__popper[x-placement^=left] .popper__arrow:after{right:1px;bottom:-5px;margin-left:-5px;border-right-width:0;border-left-color:#303133}.el-tooltip__popper.is-dark{background:#303133;color:#fff}.el-tooltip__popper.is-light{background:#fff;border:1px solid #303133}.el-tooltip__popper.is-light[x-placement^=top] .popper__arrow{border-top-color:#303133}.el-tooltip__popper.is-light[x-placement^=top] .popper__arrow:after{border-top-color:#fff}.el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow{border-bottom-color:#303133}.el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow:after{border-bottom-color:#fff}.el-tooltip__popper.is-light[x-placement^=left] .popper__arrow{border-left-color:#303133}.el-tooltip__popper.is-light[x-placement^=left] .popper__arrow:after{border-left-color:#fff}.el-tooltip__popper.is-light[x-placement^=right] .popper__arrow{border-right-color:#303133}.el-tooltip__popper.is-light[x-placement^=right] .popper__arrow:after{border-right-color:#fff}.el-slider:after,.el-slider:before{display:table;content:""}.el-slider:after{clear:both}.el-slider__runway{width:100%;height:6px;margin:16px 0;background-color:#e4e7ed;border-radius:3px;position:relative;cursor:pointer;vertical-align:middle}.el-slider__runway.show-input{margin-right:160px;width:auto}.el-slider__runway.disabled{cursor:default}.el-slider__runway.disabled .el-slider__bar{background-color:#c0c4cc}.el-slider__runway.disabled .el-slider__button{border-color:#c0c4cc}.el-slider__runway.disabled .el-slider__button-wrapper.dragging,.el-slider__runway.disabled .el-slider__button-wrapper.hover,.el-slider__runway.disabled .el-slider__button-wrapper:hover{cursor:not-allowed}.el-slider__runway.disabled .el-slider__button.dragging,.el-slider__runway.disabled .el-slider__button.hover,.el-slider__runway.disabled .el-slider__button:hover{transform:scale(1)}.el-slider__runway.disabled .el-slider__button.dragging,.el-slider__runway.disabled .el-slider__button.hover,.el-slider__runway.disabled .el-slider__button:hover{cursor:not-allowed}.el-slider__input{float:right;margin-top:3px;width:130px}.el-slider__input.el-input-number--mini{margin-top:5px}.el-slider__input.el-input-number--medium{margin-top:0}.el-slider__input.el-input-number--large{margin-top:-2px}.el-slider__bar{height:6px;background-color:#42d885;border-top-left-radius:3px;border-bottom-left-radius:3px;position:absolute}.el-slider__button-wrapper{height:36px;width:36px;position:absolute;z-index:1001;top:-15px;transform:translateX(-50%);background-color:transparent;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;line-height:normal}.el-slider__button-wrapper:after{content:"";height:100%}.el-slider__button-wrapper .el-tooltip,.el-slider__button-wrapper:after{display:inline-block;vertical-align:middle}.el-slider__button-wrapper.hover,.el-slider__button-wrapper:hover{cursor:grab}.el-slider__button-wrapper.dragging{cursor:grabbing}.el-slider__button{width:16px;height:16px;border:2px solid #42d885;background-color:#fff;border-radius:50%;transition:.2s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.el-slider__button.dragging,.el-slider__button.hover,.el-slider__button:hover{transform:scale(1.2)}.el-slider__button.hover,.el-slider__button:hover{cursor:grab}.el-slider__button.dragging{cursor:grabbing}.el-slider__stop{position:absolute;height:6px;width:6px;border-radius:100%;background-color:#fff;transform:translateX(-50%)}.el-slider__marks{top:0;left:12px;width:18px;height:100%}.el-slider__marks-text{position:absolute;transform:translateX(-50%);font-size:14px;color:#909399;margin-top:15px}.el-slider.is-vertical{position:relative}.el-slider.is-vertical .el-slider__runway{width:6px;height:100%;margin:0 16px}.el-slider.is-vertical .el-slider__bar{width:6px;height:auto;border-radius:0 0 3px 3px}.el-slider.is-vertical .el-slider__button-wrapper{top:auto;left:-15px;transform:translateY(50%)}.el-slider.is-vertical .el-slider__stop{transform:translateY(50%)}.el-slider.is-vertical.el-slider--with-input{padding-bottom:58px}.el-slider.is-vertical.el-slider--with-input .el-slider__input{overflow:visible;float:none;position:absolute;bottom:22px;width:36px;margin-top:15px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input__inner{text-align:center;padding-left:5px;padding-right:5px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__decrease,.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase{top:32px;margin-top:-1px;border:1px solid #dcdfe6;line-height:20px;box-sizing:border-box;transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__decrease{width:18px;right:18px;border-bottom-left-radius:4px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase{width:19px;border-bottom-right-radius:4px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase~.el-input .el-input__inner{border-bottom-left-radius:0;border-bottom-right-radius:0}.el-slider.is-vertical.el-slider--with-input .el-slider__input:hover .el-input-number__decrease,.el-slider.is-vertical.el-slider--with-input .el-slider__input:hover .el-input-number__increase{border-color:#c0c4cc}.el-slider.is-vertical.el-slider--with-input .el-slider__input:active .el-input-number__decrease,.el-slider.is-vertical.el-slider--with-input .el-slider__input:active .el-input-number__increase{border-color:#42d885}.el-slider.is-vertical .el-slider__marks-text{margin-top:0;left:15px;transform:translateY(50%)}.el-loading-parent--relative{position:relative!important}.el-loading-parent--hidden{overflow:hidden!important}.el-loading-mask{position:absolute;z-index:2000;background-color:hsla(0,0%,100%,.9);margin:0;top:0;right:0;bottom:0;left:0;transition:opacity .3s}.el-loading-mask.is-fullscreen{position:fixed}.el-loading-mask.is-fullscreen .el-loading-spinner{margin-top:-25px}.el-loading-mask.is-fullscreen .el-loading-spinner .circular{height:50px;width:50px}.el-loading-spinner{top:50%;margin-top:-21px;width:100%;text-align:center;position:absolute}.el-loading-spinner .el-loading-text{color:#42d885;margin:3px 0;font-size:14px}.el-loading-spinner .circular{height:42px;width:42px;animation:loading-rotate 2s linear infinite}.el-loading-spinner .path{animation:loading-dash 1.5s ease-in-out infinite;stroke-dasharray:90,150;stroke-dashoffset:0;stroke-width:2;stroke:#42d885;stroke-linecap:round}.el-loading-spinner i{color:#42d885}.el-loading-fade-enter,.el-loading-fade-leave-active{opacity:0}@keyframes loading-rotate{to{transform:rotate(1turn)}}@keyframes loading-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-40px}to{stroke-dasharray:90,150;stroke-dashoffset:-120px}}.el-row{position:relative;box-sizing:border-box}.el-row:after,.el-row:before{display:table;content:""}.el-row:after{clear:both}.el-row--flex{display:-ms-flexbox;display:flex}.el-row--flex:after,.el-row--flex:before{display:none}.el-row--flex.is-justify-center{-ms-flex-pack:center;justify-content:center}.el-row--flex.is-justify-end{-ms-flex-pack:end;justify-content:flex-end}.el-row--flex.is-justify-space-between{-ms-flex-pack:justify;justify-content:space-between}.el-row--flex.is-justify-space-around{-ms-flex-pack:distribute;justify-content:space-around}.el-row--flex.is-align-middle{-ms-flex-align:center;align-items:center}.el-row--flex.is-align-bottom{-ms-flex-align:end;align-items:flex-end}[class*=el-col-]{float:left;box-sizing:border-box}.el-col-0{display:none;width:0}.el-col-offset-0{margin-left:0}.el-col-pull-0{position:relative;right:0}.el-col-push-0{position:relative;left:0}.el-col-1{width:4.16667%}.el-col-offset-1{margin-left:4.16667%}.el-col-pull-1{position:relative;right:4.16667%}.el-col-push-1{position:relative;left:4.16667%}.el-col-2{width:8.33333%}.el-col-offset-2{margin-left:8.33333%}.el-col-pull-2{position:relative;right:8.33333%}.el-col-push-2{position:relative;left:8.33333%}.el-col-3{width:12.5%}.el-col-offset-3{margin-left:12.5%}.el-col-pull-3{position:relative;right:12.5%}.el-col-push-3{position:relative;left:12.5%}.el-col-4{width:16.66667%}.el-col-offset-4{margin-left:16.66667%}.el-col-pull-4{position:relative;right:16.66667%}.el-col-push-4{position:relative;left:16.66667%}.el-col-5{width:20.83333%}.el-col-offset-5{margin-left:20.83333%}.el-col-pull-5{position:relative;right:20.83333%}.el-col-push-5{position:relative;left:20.83333%}.el-col-6{width:25%}.el-col-offset-6{margin-left:25%}.el-col-pull-6{position:relative;right:25%}.el-col-push-6{position:relative;left:25%}.el-col-7{width:29.16667%}.el-col-offset-7{margin-left:29.16667%}.el-col-pull-7{position:relative;right:29.16667%}.el-col-push-7{position:relative;left:29.16667%}.el-col-8{width:33.33333%}.el-col-offset-8{margin-left:33.33333%}.el-col-pull-8{position:relative;right:33.33333%}.el-col-push-8{position:relative;left:33.33333%}.el-col-9{width:37.5%}.el-col-offset-9{margin-left:37.5%}.el-col-pull-9{position:relative;right:37.5%}.el-col-push-9{position:relative;left:37.5%}.el-col-10{width:41.66667%}.el-col-offset-10{margin-left:41.66667%}.el-col-pull-10{position:relative;right:41.66667%}.el-col-push-10{position:relative;left:41.66667%}.el-col-11{width:45.83333%}.el-col-offset-11{margin-left:45.83333%}.el-col-pull-11{position:relative;right:45.83333%}.el-col-push-11{position:relative;left:45.83333%}.el-col-12{width:50%}.el-col-offset-12{margin-left:50%}.el-col-pull-12{position:relative;right:50%}.el-col-push-12{position:relative;left:50%}.el-col-13{width:54.16667%}.el-col-offset-13{margin-left:54.16667%}.el-col-pull-13{position:relative;right:54.16667%}.el-col-push-13{position:relative;left:54.16667%}.el-col-14{width:58.33333%}.el-col-offset-14{margin-left:58.33333%}.el-col-pull-14{position:relative;right:58.33333%}.el-col-push-14{position:relative;left:58.33333%}.el-col-15{width:62.5%}.el-col-offset-15{margin-left:62.5%}.el-col-pull-15{position:relative;right:62.5%}.el-col-push-15{position:relative;left:62.5%}.el-col-16{width:66.66667%}.el-col-offset-16{margin-left:66.66667%}.el-col-pull-16{position:relative;right:66.66667%}.el-col-push-16{position:relative;left:66.66667%}.el-col-17{width:70.83333%}.el-col-offset-17{margin-left:70.83333%}.el-col-pull-17{position:relative;right:70.83333%}.el-col-push-17{position:relative;left:70.83333%}.el-col-18{width:75%}.el-col-offset-18{margin-left:75%}.el-col-pull-18{position:relative;right:75%}.el-col-push-18{position:relative;left:75%}.el-col-19{width:79.16667%}.el-col-offset-19{margin-left:79.16667%}.el-col-pull-19{position:relative;right:79.16667%}.el-col-push-19{position:relative;left:79.16667%}.el-col-20{width:83.33333%}.el-col-offset-20{margin-left:83.33333%}.el-col-pull-20{position:relative;right:83.33333%}.el-col-push-20{position:relative;left:83.33333%}.el-col-21{width:87.5%}.el-col-offset-21{margin-left:87.5%}.el-col-pull-21{position:relative;right:87.5%}.el-col-push-21{position:relative;left:87.5%}.el-col-22{width:91.66667%}.el-col-offset-22{margin-left:91.66667%}.el-col-pull-22{position:relative;right:91.66667%}.el-col-push-22{position:relative;left:91.66667%}.el-col-23{width:95.83333%}.el-col-offset-23{margin-left:95.83333%}.el-col-pull-23{position:relative;right:95.83333%}.el-col-push-23{position:relative;left:95.83333%}.el-col-24{width:100%}.el-col-offset-24{margin-left:100%}.el-col-pull-24{position:relative;right:100%}.el-col-push-24{position:relative;left:100%}@media only screen and (max-width:767px){.el-col-xs-0{display:none;width:0}.el-col-xs-offset-0{margin-left:0}.el-col-xs-pull-0{position:relative;right:0}.el-col-xs-push-0{position:relative;left:0}.el-col-xs-1{width:4.16667%}.el-col-xs-offset-1{margin-left:4.16667%}.el-col-xs-pull-1{position:relative;right:4.16667%}.el-col-xs-push-1{position:relative;left:4.16667%}.el-col-xs-2{width:8.33333%}.el-col-xs-offset-2{margin-left:8.33333%}.el-col-xs-pull-2{position:relative;right:8.33333%}.el-col-xs-push-2{position:relative;left:8.33333%}.el-col-xs-3{width:12.5%}.el-col-xs-offset-3{margin-left:12.5%}.el-col-xs-pull-3{position:relative;right:12.5%}.el-col-xs-push-3{position:relative;left:12.5%}.el-col-xs-4{width:16.66667%}.el-col-xs-offset-4{margin-left:16.66667%}.el-col-xs-pull-4{position:relative;right:16.66667%}.el-col-xs-push-4{position:relative;left:16.66667%}.el-col-xs-5{width:20.83333%}.el-col-xs-offset-5{margin-left:20.83333%}.el-col-xs-pull-5{position:relative;right:20.83333%}.el-col-xs-push-5{position:relative;left:20.83333%}.el-col-xs-6{width:25%}.el-col-xs-offset-6{margin-left:25%}.el-col-xs-pull-6{position:relative;right:25%}.el-col-xs-push-6{position:relative;left:25%}.el-col-xs-7{width:29.16667%}.el-col-xs-offset-7{margin-left:29.16667%}.el-col-xs-pull-7{position:relative;right:29.16667%}.el-col-xs-push-7{position:relative;left:29.16667%}.el-col-xs-8{width:33.33333%}.el-col-xs-offset-8{margin-left:33.33333%}.el-col-xs-pull-8{position:relative;right:33.33333%}.el-col-xs-push-8{position:relative;left:33.33333%}.el-col-xs-9{width:37.5%}.el-col-xs-offset-9{margin-left:37.5%}.el-col-xs-pull-9{position:relative;right:37.5%}.el-col-xs-push-9{position:relative;left:37.5%}.el-col-xs-10{width:41.66667%}.el-col-xs-offset-10{margin-left:41.66667%}.el-col-xs-pull-10{position:relative;right:41.66667%}.el-col-xs-push-10{position:relative;left:41.66667%}.el-col-xs-11{width:45.83333%}.el-col-xs-offset-11{margin-left:45.83333%}.el-col-xs-pull-11{position:relative;right:45.83333%}.el-col-xs-push-11{position:relative;left:45.83333%}.el-col-xs-12{width:50%}.el-col-xs-offset-12{margin-left:50%}.el-col-xs-pull-12{position:relative;right:50%}.el-col-xs-push-12{position:relative;left:50%}.el-col-xs-13{width:54.16667%}.el-col-xs-offset-13{margin-left:54.16667%}.el-col-xs-pull-13{position:relative;right:54.16667%}.el-col-xs-push-13{position:relative;left:54.16667%}.el-col-xs-14{width:58.33333%}.el-col-xs-offset-14{margin-left:58.33333%}.el-col-xs-pull-14{position:relative;right:58.33333%}.el-col-xs-push-14{position:relative;left:58.33333%}.el-col-xs-15{width:62.5%}.el-col-xs-offset-15{margin-left:62.5%}.el-col-xs-pull-15{position:relative;right:62.5%}.el-col-xs-push-15{position:relative;left:62.5%}.el-col-xs-16{width:66.66667%}.el-col-xs-offset-16{margin-left:66.66667%}.el-col-xs-pull-16{position:relative;right:66.66667%}.el-col-xs-push-16{position:relative;left:66.66667%}.el-col-xs-17{width:70.83333%}.el-col-xs-offset-17{margin-left:70.83333%}.el-col-xs-pull-17{position:relative;right:70.83333%}.el-col-xs-push-17{position:relative;left:70.83333%}.el-col-xs-18{width:75%}.el-col-xs-offset-18{margin-left:75%}.el-col-xs-pull-18{position:relative;right:75%}.el-col-xs-push-18{position:relative;left:75%}.el-col-xs-19{width:79.16667%}.el-col-xs-offset-19{margin-left:79.16667%}.el-col-xs-pull-19{position:relative;right:79.16667%}.el-col-xs-push-19{position:relative;left:79.16667%}.el-col-xs-20{width:83.33333%}.el-col-xs-offset-20{margin-left:83.33333%}.el-col-xs-pull-20{position:relative;right:83.33333%}.el-col-xs-push-20{position:relative;left:83.33333%}.el-col-xs-21{width:87.5%}.el-col-xs-offset-21{margin-left:87.5%}.el-col-xs-pull-21{position:relative;right:87.5%}.el-col-xs-push-21{position:relative;left:87.5%}.el-col-xs-22{width:91.66667%}.el-col-xs-offset-22{margin-left:91.66667%}.el-col-xs-pull-22{position:relative;right:91.66667%}.el-col-xs-push-22{position:relative;left:91.66667%}.el-col-xs-23{width:95.83333%}.el-col-xs-offset-23{margin-left:95.83333%}.el-col-xs-pull-23{position:relative;right:95.83333%}.el-col-xs-push-23{position:relative;left:95.83333%}.el-col-xs-24{width:100%}.el-col-xs-offset-24{margin-left:100%}.el-col-xs-pull-24{position:relative;right:100%}.el-col-xs-push-24{position:relative;left:100%}}@media only screen and (min-width:768px){.el-col-sm-0{display:none;width:0}.el-col-sm-offset-0{margin-left:0}.el-col-sm-pull-0{position:relative;right:0}.el-col-sm-push-0{position:relative;left:0}.el-col-sm-1{width:4.16667%}.el-col-sm-offset-1{margin-left:4.16667%}.el-col-sm-pull-1{position:relative;right:4.16667%}.el-col-sm-push-1{position:relative;left:4.16667%}.el-col-sm-2{width:8.33333%}.el-col-sm-offset-2{margin-left:8.33333%}.el-col-sm-pull-2{position:relative;right:8.33333%}.el-col-sm-push-2{position:relative;left:8.33333%}.el-col-sm-3{width:12.5%}.el-col-sm-offset-3{margin-left:12.5%}.el-col-sm-pull-3{position:relative;right:12.5%}.el-col-sm-push-3{position:relative;left:12.5%}.el-col-sm-4{width:16.66667%}.el-col-sm-offset-4{margin-left:16.66667%}.el-col-sm-pull-4{position:relative;right:16.66667%}.el-col-sm-push-4{position:relative;left:16.66667%}.el-col-sm-5{width:20.83333%}.el-col-sm-offset-5{margin-left:20.83333%}.el-col-sm-pull-5{position:relative;right:20.83333%}.el-col-sm-push-5{position:relative;left:20.83333%}.el-col-sm-6{width:25%}.el-col-sm-offset-6{margin-left:25%}.el-col-sm-pull-6{position:relative;right:25%}.el-col-sm-push-6{position:relative;left:25%}.el-col-sm-7{width:29.16667%}.el-col-sm-offset-7{margin-left:29.16667%}.el-col-sm-pull-7{position:relative;right:29.16667%}.el-col-sm-push-7{position:relative;left:29.16667%}.el-col-sm-8{width:33.33333%}.el-col-sm-offset-8{margin-left:33.33333%}.el-col-sm-pull-8{position:relative;right:33.33333%}.el-col-sm-push-8{position:relative;left:33.33333%}.el-col-sm-9{width:37.5%}.el-col-sm-offset-9{margin-left:37.5%}.el-col-sm-pull-9{position:relative;right:37.5%}.el-col-sm-push-9{position:relative;left:37.5%}.el-col-sm-10{width:41.66667%}.el-col-sm-offset-10{margin-left:41.66667%}.el-col-sm-pull-10{position:relative;right:41.66667%}.el-col-sm-push-10{position:relative;left:41.66667%}.el-col-sm-11{width:45.83333%}.el-col-sm-offset-11{margin-left:45.83333%}.el-col-sm-pull-11{position:relative;right:45.83333%}.el-col-sm-push-11{position:relative;left:45.83333%}.el-col-sm-12{width:50%}.el-col-sm-offset-12{margin-left:50%}.el-col-sm-pull-12{position:relative;right:50%}.el-col-sm-push-12{position:relative;left:50%}.el-col-sm-13{width:54.16667%}.el-col-sm-offset-13{margin-left:54.16667%}.el-col-sm-pull-13{position:relative;right:54.16667%}.el-col-sm-push-13{position:relative;left:54.16667%}.el-col-sm-14{width:58.33333%}.el-col-sm-offset-14{margin-left:58.33333%}.el-col-sm-pull-14{position:relative;right:58.33333%}.el-col-sm-push-14{position:relative;left:58.33333%}.el-col-sm-15{width:62.5%}.el-col-sm-offset-15{margin-left:62.5%}.el-col-sm-pull-15{position:relative;right:62.5%}.el-col-sm-push-15{position:relative;left:62.5%}.el-col-sm-16{width:66.66667%}.el-col-sm-offset-16{margin-left:66.66667%}.el-col-sm-pull-16{position:relative;right:66.66667%}.el-col-sm-push-16{position:relative;left:66.66667%}.el-col-sm-17{width:70.83333%}.el-col-sm-offset-17{margin-left:70.83333%}.el-col-sm-pull-17{position:relative;right:70.83333%}.el-col-sm-push-17{position:relative;left:70.83333%}.el-col-sm-18{width:75%}.el-col-sm-offset-18{margin-left:75%}.el-col-sm-pull-18{position:relative;right:75%}.el-col-sm-push-18{position:relative;left:75%}.el-col-sm-19{width:79.16667%}.el-col-sm-offset-19{margin-left:79.16667%}.el-col-sm-pull-19{position:relative;right:79.16667%}.el-col-sm-push-19{position:relative;left:79.16667%}.el-col-sm-20{width:83.33333%}.el-col-sm-offset-20{margin-left:83.33333%}.el-col-sm-pull-20{position:relative;right:83.33333%}.el-col-sm-push-20{position:relative;left:83.33333%}.el-col-sm-21{width:87.5%}.el-col-sm-offset-21{margin-left:87.5%}.el-col-sm-pull-21{position:relative;right:87.5%}.el-col-sm-push-21{position:relative;left:87.5%}.el-col-sm-22{width:91.66667%}.el-col-sm-offset-22{margin-left:91.66667%}.el-col-sm-pull-22{position:relative;right:91.66667%}.el-col-sm-push-22{position:relative;left:91.66667%}.el-col-sm-23{width:95.83333%}.el-col-sm-offset-23{margin-left:95.83333%}.el-col-sm-pull-23{position:relative;right:95.83333%}.el-col-sm-push-23{position:relative;left:95.83333%}.el-col-sm-24{width:100%}.el-col-sm-offset-24{margin-left:100%}.el-col-sm-pull-24{position:relative;right:100%}.el-col-sm-push-24{position:relative;left:100%}}@media only screen and (min-width:992px){.el-col-md-0{display:none;width:0}.el-col-md-offset-0{margin-left:0}.el-col-md-pull-0{position:relative;right:0}.el-col-md-push-0{position:relative;left:0}.el-col-md-1{width:4.16667%}.el-col-md-offset-1{margin-left:4.16667%}.el-col-md-pull-1{position:relative;right:4.16667%}.el-col-md-push-1{position:relative;left:4.16667%}.el-col-md-2{width:8.33333%}.el-col-md-offset-2{margin-left:8.33333%}.el-col-md-pull-2{position:relative;right:8.33333%}.el-col-md-push-2{position:relative;left:8.33333%}.el-col-md-3{width:12.5%}.el-col-md-offset-3{margin-left:12.5%}.el-col-md-pull-3{position:relative;right:12.5%}.el-col-md-push-3{position:relative;left:12.5%}.el-col-md-4{width:16.66667%}.el-col-md-offset-4{margin-left:16.66667%}.el-col-md-pull-4{position:relative;right:16.66667%}.el-col-md-push-4{position:relative;left:16.66667%}.el-col-md-5{width:20.83333%}.el-col-md-offset-5{margin-left:20.83333%}.el-col-md-pull-5{position:relative;right:20.83333%}.el-col-md-push-5{position:relative;left:20.83333%}.el-col-md-6{width:25%}.el-col-md-offset-6{margin-left:25%}.el-col-md-pull-6{position:relative;right:25%}.el-col-md-push-6{position:relative;left:25%}.el-col-md-7{width:29.16667%}.el-col-md-offset-7{margin-left:29.16667%}.el-col-md-pull-7{position:relative;right:29.16667%}.el-col-md-push-7{position:relative;left:29.16667%}.el-col-md-8{width:33.33333%}.el-col-md-offset-8{margin-left:33.33333%}.el-col-md-pull-8{position:relative;right:33.33333%}.el-col-md-push-8{position:relative;left:33.33333%}.el-col-md-9{width:37.5%}.el-col-md-offset-9{margin-left:37.5%}.el-col-md-pull-9{position:relative;right:37.5%}.el-col-md-push-9{position:relative;left:37.5%}.el-col-md-10{width:41.66667%}.el-col-md-offset-10{margin-left:41.66667%}.el-col-md-pull-10{position:relative;right:41.66667%}.el-col-md-push-10{position:relative;left:41.66667%}.el-col-md-11{width:45.83333%}.el-col-md-offset-11{margin-left:45.83333%}.el-col-md-pull-11{position:relative;right:45.83333%}.el-col-md-push-11{position:relative;left:45.83333%}.el-col-md-12{width:50%}.el-col-md-offset-12{margin-left:50%}.el-col-md-pull-12{position:relative;right:50%}.el-col-md-push-12{position:relative;left:50%}.el-col-md-13{width:54.16667%}.el-col-md-offset-13{margin-left:54.16667%}.el-col-md-pull-13{position:relative;right:54.16667%}.el-col-md-push-13{position:relative;left:54.16667%}.el-col-md-14{width:58.33333%}.el-col-md-offset-14{margin-left:58.33333%}.el-col-md-pull-14{position:relative;right:58.33333%}.el-col-md-push-14{position:relative;left:58.33333%}.el-col-md-15{width:62.5%}.el-col-md-offset-15{margin-left:62.5%}.el-col-md-pull-15{position:relative;right:62.5%}.el-col-md-push-15{position:relative;left:62.5%}.el-col-md-16{width:66.66667%}.el-col-md-offset-16{margin-left:66.66667%}.el-col-md-pull-16{position:relative;right:66.66667%}.el-col-md-push-16{position:relative;left:66.66667%}.el-col-md-17{width:70.83333%}.el-col-md-offset-17{margin-left:70.83333%}.el-col-md-pull-17{position:relative;right:70.83333%}.el-col-md-push-17{position:relative;left:70.83333%}.el-col-md-18{width:75%}.el-col-md-offset-18{margin-left:75%}.el-col-md-pull-18{position:relative;right:75%}.el-col-md-push-18{position:relative;left:75%}.el-col-md-19{width:79.16667%}.el-col-md-offset-19{margin-left:79.16667%}.el-col-md-pull-19{position:relative;right:79.16667%}.el-col-md-push-19{position:relative;left:79.16667%}.el-col-md-20{width:83.33333%}.el-col-md-offset-20{margin-left:83.33333%}.el-col-md-pull-20{position:relative;right:83.33333%}.el-col-md-push-20{position:relative;left:83.33333%}.el-col-md-21{width:87.5%}.el-col-md-offset-21{margin-left:87.5%}.el-col-md-pull-21{position:relative;right:87.5%}.el-col-md-push-21{position:relative;left:87.5%}.el-col-md-22{width:91.66667%}.el-col-md-offset-22{margin-left:91.66667%}.el-col-md-pull-22{position:relative;right:91.66667%}.el-col-md-push-22{position:relative;left:91.66667%}.el-col-md-23{width:95.83333%}.el-col-md-offset-23{margin-left:95.83333%}.el-col-md-pull-23{position:relative;right:95.83333%}.el-col-md-push-23{position:relative;left:95.83333%}.el-col-md-24{width:100%}.el-col-md-offset-24{margin-left:100%}.el-col-md-pull-24{position:relative;right:100%}.el-col-md-push-24{position:relative;left:100%}}@media only screen and (min-width:1200px){.el-col-lg-0{display:none;width:0}.el-col-lg-offset-0{margin-left:0}.el-col-lg-pull-0{position:relative;right:0}.el-col-lg-push-0{position:relative;left:0}.el-col-lg-1{width:4.16667%}.el-col-lg-offset-1{margin-left:4.16667%}.el-col-lg-pull-1{position:relative;right:4.16667%}.el-col-lg-push-1{position:relative;left:4.16667%}.el-col-lg-2{width:8.33333%}.el-col-lg-offset-2{margin-left:8.33333%}.el-col-lg-pull-2{position:relative;right:8.33333%}.el-col-lg-push-2{position:relative;left:8.33333%}.el-col-lg-3{width:12.5%}.el-col-lg-offset-3{margin-left:12.5%}.el-col-lg-pull-3{position:relative;right:12.5%}.el-col-lg-push-3{position:relative;left:12.5%}.el-col-lg-4{width:16.66667%}.el-col-lg-offset-4{margin-left:16.66667%}.el-col-lg-pull-4{position:relative;right:16.66667%}.el-col-lg-push-4{position:relative;left:16.66667%}.el-col-lg-5{width:20.83333%}.el-col-lg-offset-5{margin-left:20.83333%}.el-col-lg-pull-5{position:relative;right:20.83333%}.el-col-lg-push-5{position:relative;left:20.83333%}.el-col-lg-6{width:25%}.el-col-lg-offset-6{margin-left:25%}.el-col-lg-pull-6{position:relative;right:25%}.el-col-lg-push-6{position:relative;left:25%}.el-col-lg-7{width:29.16667%}.el-col-lg-offset-7{margin-left:29.16667%}.el-col-lg-pull-7{position:relative;right:29.16667%}.el-col-lg-push-7{position:relative;left:29.16667%}.el-col-lg-8{width:33.33333%}.el-col-lg-offset-8{margin-left:33.33333%}.el-col-lg-pull-8{position:relative;right:33.33333%}.el-col-lg-push-8{position:relative;left:33.33333%}.el-col-lg-9{width:37.5%}.el-col-lg-offset-9{margin-left:37.5%}.el-col-lg-pull-9{position:relative;right:37.5%}.el-col-lg-push-9{position:relative;left:37.5%}.el-col-lg-10{width:41.66667%}.el-col-lg-offset-10{margin-left:41.66667%}.el-col-lg-pull-10{position:relative;right:41.66667%}.el-col-lg-push-10{position:relative;left:41.66667%}.el-col-lg-11{width:45.83333%}.el-col-lg-offset-11{margin-left:45.83333%}.el-col-lg-pull-11{position:relative;right:45.83333%}.el-col-lg-push-11{position:relative;left:45.83333%}.el-col-lg-12{width:50%}.el-col-lg-offset-12{margin-left:50%}.el-col-lg-pull-12{position:relative;right:50%}.el-col-lg-push-12{position:relative;left:50%}.el-col-lg-13{width:54.16667%}.el-col-lg-offset-13{margin-left:54.16667%}.el-col-lg-pull-13{position:relative;right:54.16667%}.el-col-lg-push-13{position:relative;left:54.16667%}.el-col-lg-14{width:58.33333%}.el-col-lg-offset-14{margin-left:58.33333%}.el-col-lg-pull-14{position:relative;right:58.33333%}.el-col-lg-push-14{position:relative;left:58.33333%}.el-col-lg-15{width:62.5%}.el-col-lg-offset-15{margin-left:62.5%}.el-col-lg-pull-15{position:relative;right:62.5%}.el-col-lg-push-15{position:relative;left:62.5%}.el-col-lg-16{width:66.66667%}.el-col-lg-offset-16{margin-left:66.66667%}.el-col-lg-pull-16{position:relative;right:66.66667%}.el-col-lg-push-16{position:relative;left:66.66667%}.el-col-lg-17{width:70.83333%}.el-col-lg-offset-17{margin-left:70.83333%}.el-col-lg-pull-17{position:relative;right:70.83333%}.el-col-lg-push-17{position:relative;left:70.83333%}.el-col-lg-18{width:75%}.el-col-lg-offset-18{margin-left:75%}.el-col-lg-pull-18{position:relative;right:75%}.el-col-lg-push-18{position:relative;left:75%}.el-col-lg-19{width:79.16667%}.el-col-lg-offset-19{margin-left:79.16667%}.el-col-lg-pull-19{position:relative;right:79.16667%}.el-col-lg-push-19{position:relative;left:79.16667%}.el-col-lg-20{width:83.33333%}.el-col-lg-offset-20{margin-left:83.33333%}.el-col-lg-pull-20{position:relative;right:83.33333%}.el-col-lg-push-20{position:relative;left:83.33333%}.el-col-lg-21{width:87.5%}.el-col-lg-offset-21{margin-left:87.5%}.el-col-lg-pull-21{position:relative;right:87.5%}.el-col-lg-push-21{position:relative;left:87.5%}.el-col-lg-22{width:91.66667%}.el-col-lg-offset-22{margin-left:91.66667%}.el-col-lg-pull-22{position:relative;right:91.66667%}.el-col-lg-push-22{position:relative;left:91.66667%}.el-col-lg-23{width:95.83333%}.el-col-lg-offset-23{margin-left:95.83333%}.el-col-lg-pull-23{position:relative;right:95.83333%}.el-col-lg-push-23{position:relative;left:95.83333%}.el-col-lg-24{width:100%}.el-col-lg-offset-24{margin-left:100%}.el-col-lg-pull-24{position:relative;right:100%}.el-col-lg-push-24{position:relative;left:100%}}@media only screen and (min-width:1920px){.el-col-xl-0{display:none;width:0}.el-col-xl-offset-0{margin-left:0}.el-col-xl-pull-0{position:relative;right:0}.el-col-xl-push-0{position:relative;left:0}.el-col-xl-1{width:4.16667%}.el-col-xl-offset-1{margin-left:4.16667%}.el-col-xl-pull-1{position:relative;right:4.16667%}.el-col-xl-push-1{position:relative;left:4.16667%}.el-col-xl-2{width:8.33333%}.el-col-xl-offset-2{margin-left:8.33333%}.el-col-xl-pull-2{position:relative;right:8.33333%}.el-col-xl-push-2{position:relative;left:8.33333%}.el-col-xl-3{width:12.5%}.el-col-xl-offset-3{margin-left:12.5%}.el-col-xl-pull-3{position:relative;right:12.5%}.el-col-xl-push-3{position:relative;left:12.5%}.el-col-xl-4{width:16.66667%}.el-col-xl-offset-4{margin-left:16.66667%}.el-col-xl-pull-4{position:relative;right:16.66667%}.el-col-xl-push-4{position:relative;left:16.66667%}.el-col-xl-5{width:20.83333%}.el-col-xl-offset-5{margin-left:20.83333%}.el-col-xl-pull-5{position:relative;right:20.83333%}.el-col-xl-push-5{position:relative;left:20.83333%}.el-col-xl-6{width:25%}.el-col-xl-offset-6{margin-left:25%}.el-col-xl-pull-6{position:relative;right:25%}.el-col-xl-push-6{position:relative;left:25%}.el-col-xl-7{width:29.16667%}.el-col-xl-offset-7{margin-left:29.16667%}.el-col-xl-pull-7{position:relative;right:29.16667%}.el-col-xl-push-7{position:relative;left:29.16667%}.el-col-xl-8{width:33.33333%}.el-col-xl-offset-8{margin-left:33.33333%}.el-col-xl-pull-8{position:relative;right:33.33333%}.el-col-xl-push-8{position:relative;left:33.33333%}.el-col-xl-9{width:37.5%}.el-col-xl-offset-9{margin-left:37.5%}.el-col-xl-pull-9{position:relative;right:37.5%}.el-col-xl-push-9{position:relative;left:37.5%}.el-col-xl-10{width:41.66667%}.el-col-xl-offset-10{margin-left:41.66667%}.el-col-xl-pull-10{position:relative;right:41.66667%}.el-col-xl-push-10{position:relative;left:41.66667%}.el-col-xl-11{width:45.83333%}.el-col-xl-offset-11{margin-left:45.83333%}.el-col-xl-pull-11{position:relative;right:45.83333%}.el-col-xl-push-11{position:relative;left:45.83333%}.el-col-xl-12{width:50%}.el-col-xl-offset-12{margin-left:50%}.el-col-xl-pull-12{position:relative;right:50%}.el-col-xl-push-12{position:relative;left:50%}.el-col-xl-13{width:54.16667%}.el-col-xl-offset-13{margin-left:54.16667%}.el-col-xl-pull-13{position:relative;right:54.16667%}.el-col-xl-push-13{position:relative;left:54.16667%}.el-col-xl-14{width:58.33333%}.el-col-xl-offset-14{margin-left:58.33333%}.el-col-xl-pull-14{position:relative;right:58.33333%}.el-col-xl-push-14{position:relative;left:58.33333%}.el-col-xl-15{width:62.5%}.el-col-xl-offset-15{margin-left:62.5%}.el-col-xl-pull-15{position:relative;right:62.5%}.el-col-xl-push-15{position:relative;left:62.5%}.el-col-xl-16{width:66.66667%}.el-col-xl-offset-16{margin-left:66.66667%}.el-col-xl-pull-16{position:relative;right:66.66667%}.el-col-xl-push-16{position:relative;left:66.66667%}.el-col-xl-17{width:70.83333%}.el-col-xl-offset-17{margin-left:70.83333%}.el-col-xl-pull-17{position:relative;right:70.83333%}.el-col-xl-push-17{position:relative;left:70.83333%}.el-col-xl-18{width:75%}.el-col-xl-offset-18{margin-left:75%}.el-col-xl-pull-18{position:relative;right:75%}.el-col-xl-push-18{position:relative;left:75%}.el-col-xl-19{width:79.16667%}.el-col-xl-offset-19{margin-left:79.16667%}.el-col-xl-pull-19{position:relative;right:79.16667%}.el-col-xl-push-19{position:relative;left:79.16667%}.el-col-xl-20{width:83.33333%}.el-col-xl-offset-20{margin-left:83.33333%}.el-col-xl-pull-20{position:relative;right:83.33333%}.el-col-xl-push-20{position:relative;left:83.33333%}.el-col-xl-21{width:87.5%}.el-col-xl-offset-21{margin-left:87.5%}.el-col-xl-pull-21{position:relative;right:87.5%}.el-col-xl-push-21{position:relative;left:87.5%}.el-col-xl-22{width:91.66667%}.el-col-xl-offset-22{margin-left:91.66667%}.el-col-xl-pull-22{position:relative;right:91.66667%}.el-col-xl-push-22{position:relative;left:91.66667%}.el-col-xl-23{width:95.83333%}.el-col-xl-offset-23{margin-left:95.83333%}.el-col-xl-pull-23{position:relative;right:95.83333%}.el-col-xl-push-23{position:relative;left:95.83333%}.el-col-xl-24{width:100%}.el-col-xl-offset-24{margin-left:100%}.el-col-xl-pull-24{position:relative;right:100%}.el-col-xl-push-24{position:relative;left:100%}}.el-upload{display:inline-block;text-align:center;cursor:pointer;outline:none}.el-upload__input{display:none}.el-upload__tip{font-size:12px;color:#606266;margin-top:7px}.el-upload iframe{position:absolute;z-index:-1;top:0;left:0;opacity:0;filter:alpha(opacity=0)}.el-upload--picture-card{background-color:#fbfdff;border:1px dashed #c0ccda;border-radius:6px;box-sizing:border-box;width:148px;height:148px;cursor:pointer;line-height:146px;vertical-align:top}.el-upload--picture-card i{font-size:28px;color:#8c939d}.el-upload--picture-card:hover,.el-upload:focus{border-color:#42d885;color:#42d885}.el-upload:focus .el-upload-dragger{border-color:#42d885}.el-upload-dragger{background-color:#fff;border:1px dashed #d9d9d9;border-radius:6px;box-sizing:border-box;width:360px;height:180px;text-align:center;cursor:pointer;position:relative;overflow:hidden}.el-upload-dragger .el-icon-upload{font-size:67px;color:#c0c4cc;margin:40px 0 16px;line-height:50px}.el-upload-dragger+.el-upload__tip{text-align:center}.el-upload-dragger~.el-upload__files{border-top:1px solid #dcdfe6;margin-top:7px;padding-top:5px}.el-upload-dragger .el-upload__text{color:#606266;font-size:14px;text-align:center}.el-upload-dragger .el-upload__text em{color:#42d885;font-style:normal}.el-upload-dragger:hover{border-color:#42d885}.el-upload-dragger.is-dragover{background-color:rgba(32,159,255,.06);border:2px dashed #42d885}.el-upload-list{margin:0;padding:0;list-style:none}.el-upload-list__item{transition:all .5s cubic-bezier(.55,0,.1,1);font-size:14px;color:#606266;line-height:1.8;margin-top:5px;position:relative;box-sizing:border-box;border-radius:4px;width:100%}.el-upload-list__item .el-progress{position:absolute;top:20px;width:100%}.el-upload-list__item .el-progress__text{position:absolute;right:0;top:-13px}.el-upload-list__item .el-progress-bar{margin-right:0;padding-right:0}.el-upload-list__item:first-child{margin-top:10px}.el-upload-list__item .el-icon-upload-success{color:#42d885}.el-upload-list__item .el-icon-close{display:none;position:absolute;top:5px;right:5px;cursor:pointer;opacity:.75;color:#606266}.el-upload-list__item .el-icon-close:hover{opacity:1}.el-upload-list__item .el-icon-close-tip{display:none;position:absolute;top:5px;right:5px;font-size:12px;cursor:pointer;opacity:1;color:#42d885}.el-upload-list__item:hover{background-color:#f5f7fa}.el-upload-list__item:hover .el-icon-close{display:inline-block}.el-upload-list__item:hover .el-progress__text{display:none}.el-upload-list__item.is-success .el-upload-list__item-status-label{display:block}.el-upload-list__item.is-success .el-upload-list__item-name:focus,.el-upload-list__item.is-success .el-upload-list__item-name:hover{color:#42d885;cursor:pointer}.el-upload-list__item.is-success:focus:not(:hover) .el-icon-close-tip{display:inline-block}.el-upload-list__item.is-success:active,.el-upload-list__item.is-success:not(.focusing):focus{outline-width:0}.el-upload-list__item.is-success:active .el-icon-close-tip,.el-upload-list__item.is-success:focus .el-upload-list__item-status-label,.el-upload-list__item.is-success:hover .el-upload-list__item-status-label,.el-upload-list__item.is-success:not(.focusing):focus .el-icon-close-tip{display:none}.el-upload-list.is-disabled .el-upload-list__item:hover .el-upload-list__item-status-label{display:block}.el-upload-list__item-name{color:#606266;display:block;margin-right:40px;overflow:hidden;padding-left:4px;text-overflow:ellipsis;transition:color .3s;white-space:nowrap}.el-upload-list__item-name [class^=el-icon]{height:100%;margin-right:7px;color:#909399;line-height:inherit}.el-upload-list__item-status-label{position:absolute;right:5px;top:0;line-height:inherit;display:none}.el-upload-list__item-delete{position:absolute;right:10px;top:0;font-size:12px;color:#606266;display:none}.el-upload-list__item-delete:hover{color:#42d885}.el-upload-list--picture-card{margin:0;display:inline;vertical-align:top}.el-upload-list--picture-card .el-upload-list__item{overflow:hidden;background-color:#fff;border:1px solid #c0ccda;border-radius:6px;box-sizing:border-box;width:148px;height:148px;margin:0 8px 8px 0;display:inline-block}.el-upload-list--picture-card .el-upload-list__item .el-icon-check,.el-upload-list--picture-card .el-upload-list__item .el-icon-circle-check{color:#fff}.el-upload-list--picture-card .el-upload-list__item .el-icon-close,.el-upload-list--picture-card .el-upload-list__item:hover .el-upload-list__item-status-label{display:none}.el-upload-list--picture-card .el-upload-list__item:hover .el-progress__text{display:block}.el-upload-list--picture-card .el-upload-list__item-name{display:none}.el-upload-list--picture-card .el-upload-list__item-thumbnail{width:100%;height:100%}.el-upload-list--picture-card .el-upload-list__item-status-label{position:absolute;right:-15px;top:-6px;width:40px;height:24px;background:#13ce66;text-align:center;transform:rotate(45deg);box-shadow:0 0 1pc 1px rgba(0,0,0,.2)}.el-upload-list--picture-card .el-upload-list__item-status-label i{font-size:12px;margin-top:11px;transform:rotate(-45deg)}.el-upload-list--picture-card .el-upload-list__item-actions{position:absolute;width:100%;height:100%;left:0;top:0;cursor:default;text-align:center;color:#fff;opacity:0;font-size:20px;background-color:rgba(0,0,0,.5);transition:opacity .3s}.el-upload-list--picture-card .el-upload-list__item-actions:after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-upload-list--picture-card .el-upload-list__item-actions span{display:none;cursor:pointer}.el-upload-list--picture-card .el-upload-list__item-actions span+span{margin-left:15px}.el-upload-list--picture-card .el-upload-list__item-actions .el-upload-list__item-delete{position:static;font-size:inherit;color:inherit}.el-upload-list--picture-card .el-upload-list__item-actions:hover{opacity:1}.el-upload-list--picture-card .el-upload-list__item-actions:hover span{display:inline-block}.el-upload-list--picture-card .el-progress{top:50%;left:50%;transform:translate(-50%,-50%);bottom:auto;width:126px}.el-upload-list--picture-card .el-progress .el-progress__text{top:50%}.el-upload-list--picture .el-upload-list__item{overflow:hidden;z-index:0;background-color:#fff;border:1px solid #c0ccda;border-radius:6px;box-sizing:border-box;margin-top:10px;padding:10px 10px 10px 90px;height:92px}.el-upload-list--picture .el-upload-list__item .el-icon-check,.el-upload-list--picture .el-upload-list__item .el-icon-circle-check{color:#fff}.el-upload-list--picture .el-upload-list__item:hover .el-upload-list__item-status-label{background:transparent;box-shadow:none;top:-2px;right:-12px}.el-upload-list--picture .el-upload-list__item:hover .el-progress__text{display:block}.el-upload-list--picture .el-upload-list__item.is-success .el-upload-list__item-name{line-height:70px;margin-top:0}.el-upload-list--picture .el-upload-list__item.is-success .el-upload-list__item-name i{display:none}.el-upload-list--picture .el-upload-list__item-thumbnail{vertical-align:middle;display:inline-block;width:70px;height:70px;float:left;position:relative;z-index:1;margin-left:-80px;background-color:#fff}.el-upload-list--picture .el-upload-list__item-name{display:block;margin-top:20px}.el-upload-list--picture .el-upload-list__item-name i{font-size:70px;line-height:1;position:absolute;left:9px;top:10px}.el-upload-list--picture .el-upload-list__item-status-label{position:absolute;right:-17px;top:-7px;width:46px;height:26px;background:#13ce66;text-align:center;transform:rotate(45deg);box-shadow:0 1px 1px #ccc}.el-upload-list--picture .el-upload-list__item-status-label i{font-size:12px;margin-top:12px;transform:rotate(-45deg)}.el-upload-list--picture .el-progress{position:relative;top:-7px}.el-upload-cover{position:absolute;left:0;top:0;width:100%;height:100%;overflow:hidden;z-index:10;cursor:default}.el-upload-cover:after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-upload-cover img{display:block;width:100%;height:100%}.el-upload-cover__label{position:absolute;right:-15px;top:-6px;width:40px;height:24px;background:#13ce66;text-align:center;transform:rotate(45deg);box-shadow:0 0 1pc 1px rgba(0,0,0,.2)}.el-upload-cover__label i{font-size:12px;margin-top:11px;transform:rotate(-45deg);color:#fff}.el-upload-cover__progress{display:inline-block;vertical-align:middle;position:static;width:243px}.el-upload-cover__progress+.el-upload__inner{opacity:0}.el-upload-cover__content{position:absolute;top:0;left:0;width:100%;height:100%}.el-upload-cover__interact{position:absolute;bottom:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,.72);text-align:center}.el-upload-cover__interact .btn{display:inline-block;color:#fff;font-size:14px;cursor:pointer;vertical-align:middle;transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);margin-top:60px}.el-upload-cover__interact .btn i{margin-top:0}.el-upload-cover__interact .btn span{opacity:0;transition:opacity .15s linear}.el-upload-cover__interact .btn:not(:first-child){margin-left:35px}.el-upload-cover__interact .btn:hover{transform:translateY(-13px)}.el-upload-cover__interact .btn:hover span{opacity:1}.el-upload-cover__interact .btn i{color:#fff;display:block;font-size:24px;line-height:inherit;margin:0 auto 5px}.el-upload-cover__title{position:absolute;bottom:0;left:0;background-color:#fff;height:36px;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:400;text-align:left;padding:0 10px;margin:0;line-height:36px;font-size:14px;color:#303133}.el-upload-cover+.el-upload__inner{opacity:0;position:relative;z-index:1}.el-progress{position:relative;line-height:1}.el-progress__text{font-size:14px;color:#606266;display:inline-block;vertical-align:middle;margin-left:10px;line-height:1}.el-progress__text i{vertical-align:middle;display:block}.el-progress--circle,.el-progress--dashboard{display:inline-block}.el-progress--circle .el-progress__text,.el-progress--dashboard .el-progress__text{position:absolute;top:50%;left:0;width:100%;text-align:center;margin:0;transform:translateY(-50%)}.el-progress--circle .el-progress__text i,.el-progress--dashboard .el-progress__text i{vertical-align:middle;display:inline-block}.el-progress--without-text .el-progress__text{display:none}.el-progress--without-text .el-progress-bar{padding-right:0;margin-right:0;display:block}.el-progress--text-inside .el-progress-bar{padding-right:0;margin-right:0}.el-progress.is-success .el-progress-bar__inner{background-color:#42d885}.el-progress.is-success .el-progress__text{color:#42d885}.el-progress.is-warning .el-progress-bar__inner{background-color:#f9c855}.el-progress.is-warning .el-progress__text{color:#f9c855}.el-progress.is-exception .el-progress-bar__inner{background-color:#ff6d6d}.el-progress.is-exception .el-progress__text{color:#ff6d6d}.el-progress-bar{padding-right:50px;display:inline-block;vertical-align:middle;width:100%;margin-right:-55px;box-sizing:border-box}.el-progress-bar__outer{height:6px;border-radius:100px;background-color:#ebeef5;overflow:hidden;position:relative;vertical-align:middle}.el-progress-bar__inner{position:absolute;left:0;top:0;height:100%;background-color:#42d885;text-align:right;border-radius:100px;line-height:1;white-space:nowrap;transition:width .6s ease}.el-progress-bar__inner:after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-progress-bar__innerText{display:inline-block;vertical-align:middle;color:#fff;font-size:12px;margin:0 5px}@keyframes progress{0%{background-position:0 0}to{background-position:32px 0}}.el-time-spinner{width:100%;white-space:nowrap}.el-spinner{display:inline-block;vertical-align:middle}.el-spinner-inner{animation:rotate 2s linear infinite;width:50px;height:50px}.el-spinner-inner .path{stroke:#ececec;stroke-linecap:round;animation:dash 1.5s ease-in-out infinite}@keyframes rotate{to{transform:rotate(1turn)}}@keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}to{stroke-dasharray:90,150;stroke-dashoffset:-124}}.el-message{min-width:380px;box-sizing:border-box;border-radius:4px;border:1px solid #ebeef5;position:fixed;left:50%;top:20px;transform:translateX(-50%);background-color:#edf2fc;transition:opacity .3s,transform .4s,top .4s;overflow:hidden;padding:15px 15px 15px 20px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.el-message.is-center{-ms-flex-pack:center;justify-content:center}.el-message.is-closable .el-message__content{padding-right:16px}.el-message p{margin:0}.el-message--info .el-message__content{color:#909399}.el-message--success{background-color:#ecfbf3;border-color:#d9f7e7}.el-message--success .el-message__content{color:#42d885}.el-message--warning{background-color:#fefaee;border-color:#fef4dd}.el-message--warning .el-message__content{color:#f9c855}.el-message--error{background-color:#fff0f0;border-color:#ffe2e2}.el-message--error .el-message__content{color:#ff6d6d}.el-message__icon{margin-right:10px}.el-message__content{padding:0;font-size:14px;line-height:1}.el-message__content:focus{outline-width:0}.el-message__closeBtn{position:absolute;top:50%;right:15px;transform:translateY(-50%);cursor:pointer;color:#c0c4cc;font-size:16px}.el-message__closeBtn:focus{outline-width:0}.el-message__closeBtn:hover{color:#909399}.el-message .el-icon-success{color:#42d885}.el-message .el-icon-error{color:#ff6d6d}.el-message .el-icon-info{color:#909399}.el-message .el-icon-warning{color:#f9c855}.el-message-fade-enter,.el-message-fade-leave-active{opacity:0;transform:translate(-50%,-100%)}.el-badge{position:relative;vertical-align:middle;display:inline-block}.el-badge__content{background-color:#ff6d6d;border-radius:10px;color:#fff;display:inline-block;font-size:12px;height:18px;line-height:18px;padding:0 6px;text-align:center;white-space:nowrap;border:1px solid #fff}.el-badge__content.is-fixed{position:absolute;top:0;right:10px;transform:translateY(-50%) translateX(100%)}.el-badge__content.is-fixed.is-dot{right:5px}.el-badge__content.is-dot{height:8px;width:8px;padding:0;right:0;border-radius:50%}.el-badge__content--primary,.el-badge__content--success{background-color:#42d885}.el-badge__content--warning{background-color:#f9c855}.el-badge__content--info{background-color:#909399}.el-badge__content--danger{background-color:#ff6d6d}.el-card{border-radius:4px;border:1px solid #ebeef5;background-color:#fff;overflow:hidden;color:#303133;transition:.3s}.el-card.is-always-shadow,.el-card.is-hover-shadow:focus,.el-card.is-hover-shadow:hover{box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-card__header{padding:18px 20px;border-bottom:1px solid #ebeef5;box-sizing:border-box}.el-card__body{padding:20px}.el-rate{height:20px;line-height:1}.el-rate:active,.el-rate:focus{outline-width:0}.el-rate__item{font-size:0;vertical-align:middle}.el-rate__icon,.el-rate__item{display:inline-block;position:relative}.el-rate__icon{font-size:18px;margin-right:6px;color:#c0c4cc;transition:.3s}.el-rate__icon.hover{transform:scale(1.15)}.el-rate__decimal,.el-rate__icon .path2{position:absolute;left:0;top:0}.el-rate__decimal{display:inline-block;overflow:hidden}.el-rate__text{font-size:14px;vertical-align:middle}.el-steps{display:-ms-flexbox;display:flex}.el-steps--simple{padding:13px 8%;border-radius:4px;background:#f5f7fa}.el-steps--horizontal{white-space:nowrap}.el-steps--vertical{height:100%;-ms-flex-flow:column;flex-flow:column}.el-step{position:relative;-ms-flex-negative:1;flex-shrink:1}.el-step:last-of-type .el-step__line{display:none}.el-step:last-of-type.is-flex{-ms-flex-preferred-size:auto!important;flex-basis:auto!important;-ms-flex-negative:0;flex-shrink:0;-ms-flex-positive:0;flex-grow:0}.el-step:last-of-type .el-step__description,.el-step:last-of-type .el-step__main{padding-right:0}.el-step__head{position:relative;width:100%}.el-step__head.is-process{color:#303133;border-color:#303133}.el-step__head.is-wait{color:#c0c4cc;border-color:#c0c4cc}.el-step__head.is-success{color:#42d885;border-color:#42d885}.el-step__head.is-error{color:#ff6d6d;border-color:#ff6d6d}.el-step__head.is-finish{color:#42d885;border-color:#42d885}.el-step__icon{position:relative;z-index:1;display:-ms-inline-flexbox;display:inline-flex;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center;width:24px;height:24px;font-size:14px;box-sizing:border-box;background:#fff;transition:.15s ease-out}.el-step__icon.is-text{border-radius:50%;border:2px solid;border-color:inherit}.el-step__icon.is-icon{width:40px}.el-step__icon-inner{display:inline-block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-align:center;font-weight:700;line-height:1;color:inherit}.el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:25px;font-weight:400}.el-step__icon-inner.is-status{transform:translateY(1px)}.el-step__line{position:absolute;border-color:inherit;background-color:#c0c4cc}.el-step__line-inner{display:block;border-width:1px;border-style:solid;border-color:inherit;transition:.15s ease-out;box-sizing:border-box;width:0;height:0}.el-step__main{white-space:normal;text-align:left}.el-step__title{font-size:16px;line-height:38px}.el-step__title.is-process{font-weight:700;color:#303133}.el-step__title.is-wait{color:#c0c4cc}.el-step__title.is-success{color:#42d885}.el-step__title.is-error{color:#ff6d6d}.el-step__title.is-finish{color:#42d885}.el-step__description{padding-right:10%;margin-top:-5px;font-size:12px;line-height:20px;font-weight:400}.el-step__description.is-process{color:#303133}.el-step__description.is-wait{color:#c0c4cc}.el-step__description.is-success{color:#42d885}.el-step__description.is-error{color:#ff6d6d}.el-step__description.is-finish{color:#42d885}.el-step.is-horizontal{display:inline-block}.el-step.is-horizontal .el-step__line{height:2px;top:11px;left:0;right:0}.el-step.is-vertical{display:-ms-flexbox;display:flex}.el-step.is-vertical .el-step__head{-ms-flex-positive:0;flex-grow:0;width:24px}.el-step.is-vertical .el-step__main{padding-left:10px;-ms-flex-positive:1;flex-grow:1}.el-step.is-vertical .el-step__title{line-height:24px;padding-bottom:8px}.el-step.is-vertical .el-step__line{width:2px;top:0;bottom:0;left:11px}.el-step.is-vertical .el-step__icon.is-icon{width:24px}.el-step.is-center .el-step__head,.el-step.is-center .el-step__main{text-align:center}.el-step.is-center .el-step__description{padding-left:20%;padding-right:20%}.el-step.is-center .el-step__line{left:50%;right:-50%}.el-step.is-simple{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.el-step.is-simple .el-step__head{width:auto;font-size:0;padding-right:10px}.el-step.is-simple .el-step__icon{background:transparent;width:16px;height:16px;font-size:12px}.el-step.is-simple .el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:18px}.el-step.is-simple .el-step__icon-inner.is-status{transform:scale(.8) translateY(1px)}.el-step.is-simple .el-step__main{position:relative;display:-ms-flexbox;display:flex;-ms-flex-align:stretch;align-items:stretch;-ms-flex-positive:1;flex-grow:1}.el-step.is-simple .el-step__title{font-size:16px;line-height:20px}.el-step.is-simple:not(:last-of-type) .el-step__title{max-width:50%;word-break:break-all}.el-step.is-simple .el-step__arrow{-ms-flex-positive:1;flex-grow:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.el-step.is-simple .el-step__arrow:after,.el-step.is-simple .el-step__arrow:before{content:"";display:inline-block;position:absolute;height:15px;width:1px;background:#c0c4cc}.el-step.is-simple .el-step__arrow:before{transform:rotate(-45deg) translateY(-4px);transform-origin:0 0}.el-step.is-simple .el-step__arrow:after{transform:rotate(45deg) translateY(4px);transform-origin:100% 100%}.el-step.is-simple:last-of-type .el-step__arrow{display:none}.el-carousel{position:relative}.el-carousel--horizontal{overflow-x:hidden}.el-carousel--vertical{overflow-y:hidden}.el-carousel__container{position:relative;height:300px}.el-carousel__arrow{border:none;outline:none;padding:0;margin:0;height:36px;width:36px;cursor:pointer;transition:.3s;border-radius:50%;background-color:rgba(31,45,61,.11);color:#fff;position:absolute;top:50%;z-index:10;transform:translateY(-50%);text-align:center;font-size:12px}.el-carousel__arrow--left{left:16px}.el-carousel__arrow--right{right:16px}.el-carousel__arrow:hover{background-color:rgba(31,45,61,.23)}.el-carousel__arrow i{cursor:pointer}.el-carousel__indicators{position:absolute;list-style:none;margin:0;padding:0;z-index:2}.el-carousel__indicators--horizontal{bottom:0;left:50%;transform:translateX(-50%)}.el-carousel__indicators--vertical{right:0;top:50%;transform:translateY(-50%)}.el-carousel__indicators--outside{bottom:26px;text-align:center;position:static;transform:none}.el-carousel__indicators--outside .el-carousel__indicator:hover button{opacity:.64}.el-carousel__indicators--outside button{background-color:#c0c4cc;opacity:.24}.el-carousel__indicators--labels{left:0;right:0;transform:none;text-align:center}.el-carousel__indicators--labels .el-carousel__button{height:auto;width:auto;padding:2px 18px;font-size:12px}.el-carousel__indicators--labels .el-carousel__indicator{padding:6px 4px}.el-carousel__indicator{background-color:transparent;cursor:pointer}.el-carousel__indicator:hover button{opacity:.72}.el-carousel__indicator--horizontal{display:inline-block;padding:12px 4px}.el-carousel__indicator--vertical{padding:4px 12px}.el-carousel__indicator--vertical .el-carousel__button{width:2px;height:15px}.el-carousel__indicator.is-active button{opacity:1}.el-carousel__button{display:block;opacity:.48;width:30px;height:2px;background-color:#fff;border:none;outline:none;padding:0;margin:0;cursor:pointer;transition:.3s}.carousel-arrow-left-enter,.carousel-arrow-left-leave-active{transform:translateY(-50%) translateX(-10px);opacity:0}.carousel-arrow-right-enter,.carousel-arrow-right-leave-active{transform:translateY(-50%) translateX(10px);opacity:0}.el-carousel__item{position:absolute;top:0;left:0;width:100%;height:100%;display:inline-block;overflow:hidden;z-index:0}.el-carousel__item.is-active{z-index:2}.el-carousel__item--card,.el-carousel__item.is-animating{transition:transform .4s ease-in-out}.el-carousel__item--card{width:50%}.el-carousel__item--card.is-in-stage{cursor:pointer;z-index:1}.el-carousel__item--card.is-in-stage.is-hover .el-carousel__mask,.el-carousel__item--card.is-in-stage:hover .el-carousel__mask{opacity:.12}.el-carousel__item--card.is-active{z-index:2}.el-carousel__mask{position:absolute;width:100%;height:100%;top:0;left:0;background-color:#fff;opacity:.24;transition:.2s}.fade-in-linear-enter-active,.fade-in-linear-leave-active{transition:opacity .2s linear}.fade-in-linear-enter,.fade-in-linear-leave,.fade-in-linear-leave-active{opacity:0}.el-fade-in-linear-enter-active,.el-fade-in-linear-leave-active{transition:opacity .2s linear}.el-fade-in-linear-enter,.el-fade-in-linear-leave,.el-fade-in-linear-leave-active{opacity:0}.el-fade-in-enter-active,.el-fade-in-leave-active{transition:all .3s cubic-bezier(.55,0,.1,1)}.el-fade-in-enter,.el-fade-in-leave-active{opacity:0}.el-zoom-in-center-enter-active,.el-zoom-in-center-leave-active{transition:all .3s cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter,.el-zoom-in-center-leave-active{opacity:0;transform:scaleX(0)}.el-zoom-in-top-enter-active,.el-zoom-in-top-leave-active{opacity:1;transform:scaleY(1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transform-origin:center top}.el-zoom-in-top-enter,.el-zoom-in-top-leave-active{opacity:0;transform:scaleY(0)}.el-zoom-in-bottom-enter-active,.el-zoom-in-bottom-leave-active{opacity:1;transform:scaleY(1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transform-origin:center bottom}.el-zoom-in-bottom-enter,.el-zoom-in-bottom-leave-active{opacity:0;transform:scaleY(0)}.el-zoom-in-left-enter-active,.el-zoom-in-left-leave-active{opacity:1;transform:scale(1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transform-origin:top left}.el-zoom-in-left-enter,.el-zoom-in-left-leave-active{opacity:0;transform:scale(.45)}.collapse-transition{transition:height .3s ease-in-out,padding-top .3s ease-in-out,padding-bottom .3s ease-in-out}.horizontal-collapse-transition{transition:width .3s ease-in-out,padding-left .3s ease-in-out,padding-right .3s ease-in-out}.el-list-enter-active,.el-list-leave-active{transition:all 1s}.el-list-enter,.el-list-leave-active{opacity:0;transform:translateY(-30px)}.el-opacity-transition{transition:opacity .3s cubic-bezier(.55,0,.1,1)}.el-collapse{border-top:1px solid #ebeef5;border-bottom:1px solid #ebeef5}.el-collapse-item.is-disabled .el-collapse-item__header{color:#bbb;cursor:not-allowed}.el-collapse-item__header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;height:48px;line-height:48px;background-color:#fff;color:#303133;cursor:pointer;border-bottom:1px solid #ebeef5;font-size:13px;font-weight:500;transition:border-bottom-color .3s;outline:none}.el-collapse-item__arrow{margin:0 8px 0 auto;transition:transform .3s;font-weight:300}.el-collapse-item__arrow.is-active{transform:rotate(90deg)}.el-collapse-item__header.focusing:focus:not(:hover){color:#42d885}.el-collapse-item__header.is-active{border-bottom-color:transparent}.el-collapse-item__wrap{will-change:height;background-color:#fff;overflow:hidden;box-sizing:border-box;border-bottom:1px solid #ebeef5}.el-collapse-item__content{padding-bottom:25px;font-size:13px;color:#303133;line-height:1.769230769230769}.el-collapse-item:last-child{margin-bottom:-1px}.el-popper .popper__arrow,.el-popper .popper__arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.el-popper .popper__arrow{border-width:6px;filter:drop-shadow(0 2px 12px rgba(0,0,0,.03))}.el-popper .popper__arrow:after{content:" ";border-width:6px}.el-popper[x-placement^=top]{margin-bottom:12px}.el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#ebeef5;border-bottom-width:0}.el-popper[x-placement^=top] .popper__arrow:after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.el-popper[x-placement^=bottom]{margin-top:12px}.el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#ebeef5}.el-popper[x-placement^=bottom] .popper__arrow:after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.el-popper[x-placement^=right]{margin-left:12px}.el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#ebeef5;border-left-width:0}.el-popper[x-placement^=right] .popper__arrow:after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.el-popper[x-placement^=left]{margin-right:12px}.el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#ebeef5}.el-popper[x-placement^=left] .popper__arrow:after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff}.el-tag{background-color:#ecfbf3;border:1px solid #d9f7e7;display:inline-block;height:32px;padding:0 10px;line-height:30px;font-size:12px;color:#42d885;border-radius:4px;box-sizing:border-box;white-space:nowrap}.el-tag.is-hit{border-color:#42d885}.el-tag .el-tag__close{color:#42d885}.el-tag .el-tag__close:hover{color:#fff;background-color:#42d885}.el-tag.el-tag--info{background-color:#f4f4f5;border-color:#e9e9eb;color:#909399}.el-tag.el-tag--info.is-hit{border-color:#909399}.el-tag.el-tag--info .el-tag__close{color:#909399}.el-tag.el-tag--info .el-tag__close:hover{color:#fff;background-color:#909399}.el-tag.el-tag--success{background-color:#ecfbf3;border-color:#d9f7e7;color:#42d885}.el-tag.el-tag--success.is-hit{border-color:#42d885}.el-tag.el-tag--success .el-tag__close{color:#42d885}.el-tag.el-tag--success .el-tag__close:hover{color:#fff;background-color:#42d885}.el-tag.el-tag--warning{background-color:#fefaee;border-color:#fef4dd;color:#f9c855}.el-tag.el-tag--warning.is-hit{border-color:#f9c855}.el-tag.el-tag--warning .el-tag__close{color:#f9c855}.el-tag.el-tag--warning .el-tag__close:hover{color:#fff;background-color:#f9c855}.el-tag.el-tag--danger{background-color:#fff0f0;border-color:#ffe2e2;color:#ff6d6d}.el-tag.el-tag--danger.is-hit{border-color:#ff6d6d}.el-tag.el-tag--danger .el-tag__close{color:#ff6d6d}.el-tag.el-tag--danger .el-tag__close:hover{color:#fff;background-color:#ff6d6d}.el-tag .el-icon-close{border-radius:50%;text-align:center;position:relative;cursor:pointer;font-size:12px;height:16px;width:16px;line-height:16px;vertical-align:middle;top:-1px;right:-5px}.el-tag .el-icon-close:before{display:block}.el-tag--dark{background-color:#42d885;color:#fff}.el-tag--dark,.el-tag--dark.is-hit{border-color:#42d885}.el-tag--dark .el-tag__close{color:#fff}.el-tag--dark .el-tag__close:hover{color:#fff;background-color:#68e09d}.el-tag--dark.el-tag--info{background-color:#909399;border-color:#909399;color:#fff}.el-tag--dark.el-tag--info.is-hit{border-color:#909399}.el-tag--dark.el-tag--info .el-tag__close{color:#fff}.el-tag--dark.el-tag--info .el-tag__close:hover{color:#fff;background-color:#a6a9ad}.el-tag--dark.el-tag--success{background-color:#42d885;border-color:#42d885;color:#fff}.el-tag--dark.el-tag--success.is-hit{border-color:#42d885}.el-tag--dark.el-tag--success .el-tag__close{color:#fff}.el-tag--dark.el-tag--success .el-tag__close:hover{color:#fff;background-color:#68e09d}.el-tag--dark.el-tag--warning{background-color:#f9c855;border-color:#f9c855;color:#fff}.el-tag--dark.el-tag--warning.is-hit{border-color:#f9c855}.el-tag--dark.el-tag--warning .el-tag__close{color:#fff}.el-tag--dark.el-tag--warning .el-tag__close:hover{color:#fff;background-color:#fad377}.el-tag--dark.el-tag--danger{background-color:#ff6d6d;border-color:#ff6d6d;color:#fff}.el-tag--dark.el-tag--danger.is-hit{border-color:#ff6d6d}.el-tag--dark.el-tag--danger .el-tag__close{color:#fff}.el-tag--dark.el-tag--danger .el-tag__close:hover{color:#fff;background-color:#ff8a8a}.el-tag--plain{background-color:#fff;border-color:#b3efce;color:#42d885}.el-tag--plain.is-hit{border-color:#42d885}.el-tag--plain .el-tag__close{color:#42d885}.el-tag--plain .el-tag__close:hover{color:#fff;background-color:#42d885}.el-tag--plain.el-tag--info{background-color:#fff;border-color:#d3d4d6;color:#909399}.el-tag--plain.el-tag--info.is-hit{border-color:#909399}.el-tag--plain.el-tag--info .el-tag__close{color:#909399}.el-tag--plain.el-tag--info .el-tag__close:hover{color:#fff;background-color:#909399}.el-tag--plain.el-tag--success{background-color:#fff;border-color:#b3efce;color:#42d885}.el-tag--plain.el-tag--success.is-hit{border-color:#42d885}.el-tag--plain.el-tag--success .el-tag__close{color:#42d885}.el-tag--plain.el-tag--success .el-tag__close:hover{color:#fff;background-color:#42d885}.el-tag--plain.el-tag--warning{background-color:#fff;border-color:#fde9bb;color:#f9c855}.el-tag--plain.el-tag--warning.is-hit{border-color:#f9c855}.el-tag--plain.el-tag--warning .el-tag__close{color:#f9c855}.el-tag--plain.el-tag--warning .el-tag__close:hover{color:#fff;background-color:#f9c855}.el-tag--plain.el-tag--danger{background-color:#fff;border-color:#ffc5c5;color:#ff6d6d}.el-tag--plain.el-tag--danger.is-hit{border-color:#ff6d6d}.el-tag--plain.el-tag--danger .el-tag__close{color:#ff6d6d}.el-tag--plain.el-tag--danger .el-tag__close:hover{color:#fff;background-color:#ff6d6d}.el-tag--medium{height:28px;line-height:26px}.el-tag--medium .el-icon-close{transform:scale(.8)}.el-tag--small{height:24px;padding:0 8px;line-height:22px}.el-tag--small .el-icon-close{transform:scale(.8)}.el-tag--mini{height:20px;padding:0 5px;line-height:19px}.el-tag--mini .el-icon-close{margin-left:-3px;transform:scale(.7)}.el-cascader{display:inline-block;position:relative;font-size:14px;line-height:40px}.el-cascader:not(.is-disabled):hover .el-input__inner{cursor:pointer;border-color:#c0c4cc}.el-cascader .el-input{cursor:pointer}.el-cascader .el-input .el-input__inner{text-overflow:ellipsis}.el-cascader .el-input .el-input__inner:focus{border-color:#42d885}.el-cascader .el-input .el-icon-arrow-down{transition:transform .3s;font-size:14px}.el-cascader .el-input .el-icon-arrow-down.is-reverse{transform:rotate(180deg)}.el-cascader .el-input .el-icon-circle-close:hover{color:#909399}.el-cascader .el-input.is-focus .el-input__inner{border-color:#42d885}.el-cascader--medium{font-size:14px;line-height:36px}.el-cascader--small{font-size:13px;line-height:32px}.el-cascader--mini{font-size:12px;line-height:28px}.el-cascader.is-disabled .el-cascader__label{z-index:2;color:#c0c4cc}.el-cascader__dropdown{margin:5px 0;font-size:14px;background:#fff;border:1px solid #e4e7ed;border-radius:4px;box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-cascader__tags{position:absolute;left:0;right:30px;top:50%;transform:translateY(-50%);display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;line-height:normal;text-align:left;box-sizing:border-box}.el-cascader__tags .el-tag{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;max-width:100%;margin:2px 0 2px 6px;text-overflow:ellipsis;background:#f0f2f5}.el-cascader__tags .el-tag:not(.is-hit){border-color:transparent}.el-cascader__tags .el-tag>span{-ms-flex:1;flex:1;overflow:hidden;text-overflow:ellipsis}.el-cascader__tags .el-tag .el-icon-close{-ms-flex:none;flex:none;background-color:#c0c4cc;color:#fff}.el-cascader__tags .el-tag .el-icon-close:hover{background-color:#909399}.el-cascader__suggestion-panel{border-radius:4px}.el-cascader__suggestion-list{max-height:204px;margin:0;padding:6px 0;font-size:14px;color:#606266;text-align:center}.el-cascader__suggestion-item{display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between;-ms-flex-align:center;align-items:center;height:34px;padding:0 15px;text-align:left;outline:none;cursor:pointer}.el-cascader__suggestion-item:focus,.el-cascader__suggestion-item:hover{background:#f5f7fa}.el-cascader__suggestion-item.is-checked{color:#42d885;font-weight:700}.el-cascader__suggestion-item>span{margin-right:10px}.el-cascader__empty-text{margin:10px 0;color:#c0c4cc}.el-cascader__search-input{-ms-flex:1;flex:1;height:24px;min-width:60px;margin:2px 0 2px 15px;padding:0;color:#606266;border:none;outline:none;box-sizing:border-box}.el-cascader__search-input::-webkit-input-placeholder{color:#c0c4cc}.el-cascader__search-input::-ms-input-placeholder{color:#c0c4cc}.el-cascader__search-input::placeholder{color:#c0c4cc}.el-color-predefine{display:-ms-flexbox;display:flex;font-size:12px;margin-top:8px;width:280px}.el-color-predefine__colors{display:-ms-flexbox;display:flex;-ms-flex:1;flex:1;-ms-flex-wrap:wrap;flex-wrap:wrap}.el-color-predefine__color-selector{margin:0 0 8px 8px;width:20px;height:20px;border-radius:4px;cursor:pointer}.el-color-predefine__color-selector:nth-child(10n+1){margin-left:0}.el-color-predefine__color-selector.selected{box-shadow:0 0 3px 2px #42d885}.el-color-predefine__color-selector>div{display:-ms-flexbox;display:flex;height:100%;border-radius:3px}.el-color-predefine__color-selector.is-alpha{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-hue-slider{position:relative;box-sizing:border-box;width:280px;height:12px;background-color:red;padding:0 2px}.el-color-hue-slider__bar{position:relative;background:linear-gradient(90deg,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red);height:100%}.el-color-hue-slider__thumb{position:absolute;cursor:pointer;box-sizing:border-box;left:0;top:0;width:4px;height:100%;border-radius:1px;background:#fff;border:1px solid #f0f0f0;box-shadow:0 0 2px rgba(0,0,0,.6);z-index:1}.el-color-hue-slider.is-vertical{width:12px;height:180px;padding:2px 0}.el-color-hue-slider.is-vertical .el-color-hue-slider__bar{background:linear-gradient(180deg,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red)}.el-color-hue-slider.is-vertical .el-color-hue-slider__thumb{left:0;top:0;width:100%;height:4px}.el-color-svpanel{position:relative;width:280px;height:180px}.el-color-svpanel__black,.el-color-svpanel__white{position:absolute;top:0;left:0;right:0;bottom:0}.el-color-svpanel__white{background:linear-gradient(90deg,#fff,hsla(0,0%,100%,0))}.el-color-svpanel__black{background:linear-gradient(0deg,#000,transparent)}.el-color-svpanel__cursor{position:absolute}.el-color-svpanel__cursor>div{cursor:head;width:4px;height:4px;box-shadow:0 0 0 1.5px #fff,inset 0 0 1px 1px rgba(0,0,0,.3),0 0 1px 2px rgba(0,0,0,.4);border-radius:50%;transform:translate(-2px,-2px)}.el-color-alpha-slider{position:relative;box-sizing:border-box;width:280px;height:12px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-alpha-slider__bar{position:relative;background:linear-gradient(90deg,hsla(0,0%,100%,0) 0,#fff);height:100%}.el-color-alpha-slider__thumb{position:absolute;cursor:pointer;box-sizing:border-box;left:0;top:0;width:4px;height:100%;border-radius:1px;background:#fff;border:1px solid #f0f0f0;box-shadow:0 0 2px rgba(0,0,0,.6);z-index:1}.el-color-alpha-slider.is-vertical{width:20px;height:180px}.el-color-alpha-slider.is-vertical .el-color-alpha-slider__bar{background:linear-gradient(180deg,hsla(0,0%,100%,0) 0,#fff)}.el-color-alpha-slider.is-vertical .el-color-alpha-slider__thumb{left:0;top:0;width:100%;height:4px}.el-color-dropdown{width:300px}.el-color-dropdown__main-wrapper{margin-bottom:6px}.el-color-dropdown__main-wrapper:after{content:"";display:table;clear:both}.el-color-dropdown__btns{margin-top:6px;text-align:right}.el-color-dropdown__value{float:left;line-height:26px;font-size:12px;color:#000;width:160px}.el-color-dropdown__btn{border:1px solid #dcdcdc;color:#333;line-height:24px;border-radius:2px;padding:0 20px;cursor:pointer;background-color:transparent;outline:none;font-size:12px}.el-color-dropdown__btn[disabled]{color:#ccc;cursor:not-allowed}.el-color-dropdown__btn:hover{color:#42d885;border-color:#42d885}.el-color-dropdown__link-btn{cursor:pointer;color:#42d885;text-decoration:none;padding:15px;font-size:12px}.el-color-dropdown__link-btn:hover{color:tint(#42d885,20%)}.el-color-picker{display:inline-block;position:relative;line-height:normal;height:40px}.el-color-picker.is-disabled .el-color-picker__trigger{cursor:not-allowed}.el-color-picker--medium{height:36px}.el-color-picker--medium .el-color-picker__trigger{height:36px;width:36px}.el-color-picker--medium .el-color-picker__mask{height:34px;width:34px}.el-color-picker--small{height:32px}.el-color-picker--small .el-color-picker__trigger{height:32px;width:32px}.el-color-picker--small .el-color-picker__mask{height:30px;width:30px}.el-color-picker--small .el-color-picker__empty,.el-color-picker--small .el-color-picker__icon{transform:translate3d(-50%,-50%,0) scale(.8)}.el-color-picker--mini{height:28px}.el-color-picker--mini .el-color-picker__trigger{height:28px;width:28px}.el-color-picker--mini .el-color-picker__mask{height:26px;width:26px}.el-color-picker--mini .el-color-picker__empty,.el-color-picker--mini .el-color-picker__icon{transform:translate3d(-50%,-50%,0) scale(.8)}.el-color-picker__mask{height:38px;width:38px;border-radius:4px;position:absolute;top:1px;left:1px;z-index:1;cursor:not-allowed;background-color:hsla(0,0%,100%,.7)}.el-color-picker__trigger{display:inline-block;box-sizing:border-box;height:40px;width:40px;padding:4px;border:1px solid #e6e6e6;border-radius:4px;font-size:0;position:relative;cursor:pointer}.el-color-picker__color{position:relative;display:block;box-sizing:border-box;border:1px solid #999;border-radius:2px;width:100%;height:100%;text-align:center}.el-color-picker__color.is-alpha{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-picker__color-inner{position:absolute;left:0;top:0;right:0;bottom:0}.el-color-picker__empty{color:#999}.el-color-picker__empty,.el-color-picker__icon{font-size:12px;position:absolute;top:50%;left:50%;transform:translate3d(-50%,-50%,0)}.el-color-picker__icon{display:inline-block;width:100%;color:#fff;text-align:center}.el-color-picker__panel{position:absolute;z-index:10;padding:6px;box-sizing:content-box;background-color:#fff;border:1px solid #ebeef5;border-radius:4px;box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-textarea{position:relative;display:inline-block;width:100%;vertical-align:bottom;font-size:14px}.el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;box-sizing:border-box;width:100%;font-size:inherit;color:#606266;background-color:#fff;background-image:none;border:1px solid #dcdfe6;border-radius:4px;transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.el-textarea__inner::-webkit-input-placeholder{color:#c0c4cc}.el-textarea__inner::-ms-input-placeholder{color:#c0c4cc}.el-textarea__inner::placeholder{color:#c0c4cc}.el-textarea__inner:hover{border-color:#c0c4cc}.el-textarea__inner:focus{outline:none;border-color:#42d885}.el-textarea .el-input__count{color:#909399;background:#fff;position:absolute;font-size:12px;bottom:5px;right:10px}.el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#e4e7ed;color:#c0c4cc;cursor:not-allowed}.el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#c0c4cc}.el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#c0c4cc}.el-textarea.is-disabled .el-textarea__inner::placeholder{color:#c0c4cc}.el-textarea.is-exceed .el-textarea__inner{border-color:#ff6d6d}.el-textarea.is-exceed .el-input__count{color:#ff6d6d}.el-input{position:relative;font-size:14px;display:inline-block;width:100%}.el-input::-webkit-scrollbar{z-index:11;width:6px}.el-input::-webkit-scrollbar:horizontal{height:6px}.el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.el-input::-webkit-scrollbar-corner,.el-input::-webkit-scrollbar-track{background:#fff}.el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.el-input .el-input__clear{color:#c0c4cc;font-size:14px;cursor:pointer;transition:color .2s cubic-bezier(.645,.045,.355,1)}.el-input .el-input__clear:hover{color:#909399}.el-input .el-input__count{height:100%;display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;color:#909399;font-size:12px}.el-input .el-input__count .el-input__count-inner{background:#fff;line-height:normal;display:inline-block;padding:0 5px}.el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #dcdfe6;box-sizing:border-box;color:#606266;display:inline-block;font-size:inherit;height:40px;line-height:40px;outline:none;padding:0 15px;transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.el-input__inner::-webkit-input-placeholder{color:#c0c4cc}.el-input__inner::-ms-input-placeholder{color:#c0c4cc}.el-input__inner::placeholder{color:#c0c4cc}.el-input__inner:hover{border-color:#c0c4cc}.el-input__inner:focus{outline:none;border-color:#42d885}.el-input__suffix{position:absolute;height:100%;right:5px;top:0;text-align:center;color:#c0c4cc;transition:all .3s;pointer-events:none}.el-input__suffix-inner{pointer-events:all}.el-input__prefix{position:absolute;left:5px;top:0;color:#c0c4cc}.el-input__icon,.el-input__prefix{height:100%;text-align:center;transition:all .3s}.el-input__icon{width:25px;line-height:40px}.el-input__icon:after{content:"";height:100%;width:0;display:inline-block;vertical-align:middle}.el-input__validateIcon{pointer-events:none}.el-input.is-active .el-input__inner{outline:none;border-color:#42d885}.el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#e4e7ed;color:#c0c4cc;cursor:not-allowed}.el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__inner::placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__icon{cursor:not-allowed}.el-input.is-exceed .el-input__inner{border-color:#ff6d6d}.el-input.is-exceed .el-input__suffix .el-input__count{color:#ff6d6d}.el-input--suffix .el-input__inner{padding-right:30px}.el-input--prefix .el-input__inner{padding-left:30px}.el-input--medium{font-size:14px}.el-input--medium .el-input__inner{height:36px;line-height:36px}.el-input--medium .el-input__icon{line-height:36px}.el-input--small{font-size:13px}.el-input--small .el-input__inner{height:32px;line-height:32px}.el-input--small .el-input__icon{line-height:32px}.el-input--mini{font-size:12px}.el-input--mini .el-input__inner{height:28px;line-height:28px}.el-input--mini .el-input__icon{line-height:28px}.el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate;border-spacing:0}.el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.el-input-group__append,.el-input-group__prepend{background-color:#f5f7fa;color:#909399;vertical-align:middle;display:table-cell;position:relative;border:1px solid #dcdfe6;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.el-input-group__append:focus,.el-input-group__prepend:focus{outline:none}.el-input-group__append .el-button,.el-input-group__append .el-select,.el-input-group__prepend .el-button,.el-input-group__prepend .el-select{display:inline-block;margin:-10px -20px}.el-input-group__append button.el-button,.el-input-group__append div.el-select .el-input__inner,.el-input-group__append div.el-select:hover .el-input__inner,.el-input-group__prepend button.el-button,.el-input-group__prepend div.el-select .el-input__inner,.el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.el-input-group__append .el-button,.el-input-group__append .el-input,.el-input-group__prepend .el-button,.el-input-group__prepend .el-input{font-size:inherit}.el-input-group__prepend{border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.el-input-group__append{border-left:0}.el-input-group--prepend .el-input__inner,.el-input-group__append{border-top-left-radius:0;border-bottom-left-radius:0}.el-input-group--prepend .el-select .el-input.is-focus .el-input__inner{border-color:transparent}.el-input-group--append .el-input__inner{border-top-right-radius:0;border-bottom-right-radius:0}.el-input-group--append .el-select .el-input.is-focus .el-input__inner{border-color:transparent}.el-input__inner::-ms-clear{display:none;width:0;height:0}.el-button{display:inline-block;line-height:1;white-space:nowrap;cursor:pointer;background:#fff;border:1px solid #dcdfe6;border-color:#dcdfe6;color:#606266;-webkit-appearance:none;text-align:center;box-sizing:border-box;outline:none;margin:0;transition:.1s;font-weight:500;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;padding:12px 20px;font-size:14px;border-radius:4px}.el-button+.el-button{margin-left:10px}.el-button.is-round{padding:12px 20px}.el-button:focus,.el-button:hover{color:#42d885;border-color:#c6f3da;background-color:#ecfbf3}.el-button:active{color:#3bc278;border-color:#3bc278;outline:none}.el-button::-moz-focus-inner{border:0}.el-button [class*=el-icon-]+span{margin-left:5px}.el-button.is-plain:focus,.el-button.is-plain:hover{background:#fff;border-color:#42d885;color:#42d885}.el-button.is-plain:active{background:#fff;outline:none}.el-button.is-active,.el-button.is-plain:active{border-color:#3bc278;color:#3bc278}.el-button.is-disabled,.el-button.is-disabled:focus,.el-button.is-disabled:hover{color:#c0c4cc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#ebeef5}.el-button.is-disabled.el-button--text{background-color:transparent}.el-button.is-disabled.is-plain,.el-button.is-disabled.is-plain:focus,.el-button.is-disabled.is-plain:hover{background-color:#fff;border-color:#ebeef5;color:#c0c4cc}.el-button.is-loading{position:relative;pointer-events:none}.el-button.is-loading:before{pointer-events:none;content:"";position:absolute;left:-1px;top:-1px;right:-1px;bottom:-1px;border-radius:inherit;background-color:hsla(0,0%,100%,.35)}.el-button.is-round{border-radius:20px;padding:12px 23px}.el-button.is-circle{border-radius:50%;padding:12px}.el-button--primary{color:#fff;background-color:#42d885;border-color:#42d885}.el-button--primary:focus,.el-button--primary:hover{background:#68e09d;border-color:#68e09d;color:#fff}.el-button--primary:active{outline:none}.el-button--primary.is-active,.el-button--primary:active{background:#3bc278;border-color:#3bc278;color:#fff}.el-button--primary.is-disabled,.el-button--primary.is-disabled:active,.el-button--primary.is-disabled:focus,.el-button--primary.is-disabled:hover{color:#fff;background-color:#a1ecc2;border-color:#a1ecc2}.el-button--primary.is-plain{color:#42d885;background:#ecfbf3;border-color:#b3efce}.el-button--primary.is-plain:focus,.el-button--primary.is-plain:hover{background:#42d885;border-color:#42d885;color:#fff}.el-button--primary.is-plain:active{background:#3bc278;border-color:#3bc278;color:#fff;outline:none}.el-button--primary.is-plain.is-disabled,.el-button--primary.is-plain.is-disabled:active,.el-button--primary.is-plain.is-disabled:focus,.el-button--primary.is-plain.is-disabled:hover{color:#8ee8b6;background-color:#ecfbf3;border-color:#d9f7e7}.el-button--success{color:#fff;background-color:#42d885;border-color:#42d885}.el-button--success:focus,.el-button--success:hover{background:#68e09d;border-color:#68e09d;color:#fff}.el-button--success:active{outline:none}.el-button--success.is-active,.el-button--success:active{background:#3bc278;border-color:#3bc278;color:#fff}.el-button--success.is-disabled,.el-button--success.is-disabled:active,.el-button--success.is-disabled:focus,.el-button--success.is-disabled:hover{color:#fff;background-color:#a1ecc2;border-color:#a1ecc2}.el-button--success.is-plain{color:#42d885;background:#ecfbf3;border-color:#b3efce}.el-button--success.is-plain:focus,.el-button--success.is-plain:hover{background:#42d885;border-color:#42d885;color:#fff}.el-button--success.is-plain:active{background:#3bc278;border-color:#3bc278;color:#fff;outline:none}.el-button--success.is-plain.is-disabled,.el-button--success.is-plain.is-disabled:active,.el-button--success.is-plain.is-disabled:focus,.el-button--success.is-plain.is-disabled:hover{color:#8ee8b6;background-color:#ecfbf3;border-color:#d9f7e7}.el-button--warning{color:#fff;background-color:#f9c855;border-color:#f9c855}.el-button--warning:focus,.el-button--warning:hover{background:#fad377;border-color:#fad377;color:#fff}.el-button--warning:active{outline:none}.el-button--warning.is-active,.el-button--warning:active{background:#e0b44d;border-color:#e0b44d;color:#fff}.el-button--warning.is-disabled,.el-button--warning.is-disabled:active,.el-button--warning.is-disabled:focus,.el-button--warning.is-disabled:hover{color:#fff;background-color:#fce4aa;border-color:#fce4aa}.el-button--warning.is-plain{color:#f9c855;background:#fefaee;border-color:#fde9bb}.el-button--warning.is-plain:focus,.el-button--warning.is-plain:hover{background:#f9c855;border-color:#f9c855;color:#fff}.el-button--warning.is-plain:active{background:#e0b44d;border-color:#e0b44d;color:#fff;outline:none}.el-button--warning.is-plain.is-disabled,.el-button--warning.is-plain.is-disabled:active,.el-button--warning.is-plain.is-disabled:focus,.el-button--warning.is-plain.is-disabled:hover{color:#fbde99;background-color:#fefaee;border-color:#fef4dd}.el-button--danger{color:#fff;background-color:#ff6d6d;border-color:#ff6d6d}.el-button--danger:focus,.el-button--danger:hover{background:#ff8a8a;border-color:#ff8a8a;color:#fff}.el-button--danger:active{outline:none}.el-button--danger.is-active,.el-button--danger:active{background:#e66262;border-color:#e66262;color:#fff}.el-button--danger.is-disabled,.el-button--danger.is-disabled:active,.el-button--danger.is-disabled:focus,.el-button--danger.is-disabled:hover{color:#fff;background-color:#ffb6b6;border-color:#ffb6b6}.el-button--danger.is-plain{color:#ff6d6d;background:#fff0f0;border-color:#ffc5c5}.el-button--danger.is-plain:focus,.el-button--danger.is-plain:hover{background:#ff6d6d;border-color:#ff6d6d;color:#fff}.el-button--danger.is-plain:active{background:#e66262;border-color:#e66262;color:#fff;outline:none}.el-button--danger.is-plain.is-disabled,.el-button--danger.is-plain.is-disabled:active,.el-button--danger.is-plain.is-disabled:focus,.el-button--danger.is-plain.is-disabled:hover{color:#ffa7a7;background-color:#fff0f0;border-color:#ffe2e2}.el-button--info{color:#fff;background-color:#909399;border-color:#909399}.el-button--info:focus,.el-button--info:hover{background:#a6a9ad;border-color:#a6a9ad;color:#fff}.el-button--info:active{outline:none}.el-button--info.is-active,.el-button--info:active{background:#82848a;border-color:#82848a;color:#fff}.el-button--info.is-disabled,.el-button--info.is-disabled:active,.el-button--info.is-disabled:focus,.el-button--info.is-disabled:hover{color:#fff;background-color:#c8c9cc;border-color:#c8c9cc}.el-button--info.is-plain{color:#909399;background:#f4f4f5;border-color:#d3d4d6}.el-button--info.is-plain:focus,.el-button--info.is-plain:hover{background:#909399;border-color:#909399;color:#fff}.el-button--info.is-plain:active{background:#82848a;border-color:#82848a;color:#fff;outline:none}.el-button--info.is-plain.is-disabled,.el-button--info.is-plain.is-disabled:active,.el-button--info.is-plain.is-disabled:focus,.el-button--info.is-plain.is-disabled:hover{color:#bcbec2;background-color:#f4f4f5;border-color:#e9e9eb}.el-button--medium{padding:10px 20px;font-size:14px;border-radius:4px}.el-button--medium.is-round{padding:10px 20px}.el-button--medium.is-circle{padding:10px}.el-button--small{padding:9px 15px;font-size:12px;border-radius:3px}.el-button--small.is-round{padding:9px 15px}.el-button--small.is-circle{padding:9px}.el-button--mini{padding:7px 15px;font-size:12px;border-radius:3px}.el-button--mini.is-round{padding:7px 15px}.el-button--mini.is-circle{padding:7px}.el-button--text{border-color:transparent;color:#42d885;background:transparent;padding-left:0;padding-right:0}.el-button--text:focus,.el-button--text:hover{color:#68e09d;border-color:transparent;background-color:transparent}.el-button--text:active{color:#3bc278;background-color:transparent}.el-button--text.is-disabled,.el-button--text.is-disabled:focus,.el-button--text.is-disabled:hover,.el-button--text:active{border-color:transparent}.el-button-group{display:inline-block;vertical-align:middle}.el-button-group:after,.el-button-group:before{display:table;content:""}.el-button-group:after{clear:both}.el-button-group>.el-button{float:left;position:relative}.el-button-group>.el-button+.el-button{margin-left:0}.el-button-group>.el-button.is-disabled{z-index:1}.el-button-group>.el-button:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.el-button-group>.el-button:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.el-button-group>.el-button:first-child:last-child{border-top-right-radius:4px;border-bottom-right-radius:4px;border-top-left-radius:4px;border-bottom-left-radius:4px}.el-button-group>.el-button:first-child:last-child.is-round{border-radius:20px}.el-button-group>.el-button:first-child:last-child.is-circle{border-radius:50%}.el-button-group>.el-button:not(:first-child):not(:last-child){border-radius:0}.el-button-group>.el-button:not(:last-child){margin-right:-1px}.el-button-group>.el-button.is-active,.el-button-group>.el-button:active,.el-button-group>.el-button:focus,.el-button-group>.el-button:hover{z-index:1}.el-button-group>.el-dropdown>.el-button{border-top-left-radius:0;border-bottom-left-radius:0;border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--primary:first-child{border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--primary:last-child{border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--primary:not(:first-child):not(:last-child){border-left-color:hsla(0,0%,100%,.5);border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--success:first-child{border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--success:last-child{border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--success:not(:first-child):not(:last-child){border-left-color:hsla(0,0%,100%,.5);border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--warning:first-child{border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--warning:last-child{border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--warning:not(:first-child):not(:last-child){border-left-color:hsla(0,0%,100%,.5);border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--danger:first-child{border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--danger:last-child{border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--danger:not(:first-child):not(:last-child){border-left-color:hsla(0,0%,100%,.5);border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--info:first-child{border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--info:last-child{border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--info:not(:first-child):not(:last-child){border-left-color:hsla(0,0%,100%,.5);border-right-color:hsla(0,0%,100%,.5)}.el-transfer{font-size:14px}.el-transfer__buttons{display:inline-block;vertical-align:middle;padding:0 30px}.el-transfer__button{display:block;margin:0 auto;padding:10px;border-radius:50%;color:#fff;background-color:#42d885;font-size:0}.el-transfer__button.is-with-texts{border-radius:4px}.el-transfer__button.is-disabled,.el-transfer__button.is-disabled:hover{border:1px solid #dcdfe6;background-color:#f5f7fa;color:#c0c4cc}.el-transfer__button:first-child{margin-bottom:10px}.el-transfer__button:nth-child(2){margin:0}.el-transfer__button i,.el-transfer__button span{font-size:14px}.el-transfer__button [class*=el-icon-]+span{margin-left:0}.el-transfer-panel{border:1px solid #ebeef5;border-radius:4px;overflow:hidden;background:#fff;display:inline-block;vertical-align:middle;width:200px;max-height:100%;box-sizing:border-box;position:relative}.el-transfer-panel__body{height:246px}.el-transfer-panel__body.is-with-footer{padding-bottom:40px}.el-transfer-panel__list{margin:0;padding:6px 0;list-style:none;height:246px;overflow:auto;box-sizing:border-box}.el-transfer-panel__list.is-filterable{height:194px;padding-top:0}.el-transfer-panel__item{height:30px;line-height:30px;padding-left:15px;display:block}.el-transfer-panel__item+.el-transfer-panel__item{margin-left:0}.el-transfer-panel__item.el-checkbox{color:#606266}.el-transfer-panel__item:hover{color:#42d885}.el-transfer-panel__item.el-checkbox .el-checkbox__label{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;box-sizing:border-box;padding-left:24px;line-height:30px}.el-transfer-panel__item .el-checkbox__input{position:absolute;top:8px}.el-transfer-panel__filter{text-align:center;margin:15px;box-sizing:border-box;display:block;width:auto}.el-transfer-panel__filter .el-input__inner{height:32px;width:100%;font-size:12px;display:inline-block;box-sizing:border-box;border-radius:16px;padding-right:10px;padding-left:30px}.el-transfer-panel__filter .el-input__icon{margin-left:5px}.el-transfer-panel__filter .el-icon-circle-close{cursor:pointer}.el-transfer-panel .el-transfer-panel__header{height:40px;line-height:40px;background:#f5f7fa;margin:0;padding-left:15px;border-bottom:1px solid #ebeef5;box-sizing:border-box;color:#000}.el-transfer-panel .el-transfer-panel__header .el-checkbox{display:block;line-height:40px}.el-transfer-panel .el-transfer-panel__header .el-checkbox .el-checkbox__label{font-size:16px;color:#303133;font-weight:400}.el-transfer-panel .el-transfer-panel__header .el-checkbox .el-checkbox__label span{position:absolute;right:15px;color:#909399;font-size:12px;font-weight:400}.el-transfer-panel .el-transfer-panel__footer{height:40px;background:#fff;margin:0;padding:0;border-top:1px solid #ebeef5;position:absolute;bottom:0;left:0;width:100%;z-index:1}.el-transfer-panel .el-transfer-panel__footer:after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-transfer-panel .el-transfer-panel__footer .el-checkbox{padding-left:20px;color:#606266}.el-transfer-panel .el-transfer-panel__empty{margin:0;height:30px;line-height:30px;padding:6px 15px 0;color:#909399;text-align:center}.el-transfer-panel .el-checkbox__label{padding-left:8px}.el-transfer-panel .el-checkbox__inner{height:14px;width:14px;border-radius:3px}.el-transfer-panel .el-checkbox__inner:after{height:6px;width:3px;left:4px}.el-container{display:-ms-flexbox;display:flex;-ms-flex-direction:row;flex-direction:row;-ms-flex:1;flex:1;-ms-flex-preferred-size:auto;flex-basis:auto;box-sizing:border-box;min-width:0}.el-container.is-vertical{-ms-flex-direction:column;flex-direction:column}.el-header{padding:0 20px}.el-aside,.el-header{box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0}.el-aside,.el-main{overflow:auto}.el-main{display:block;-ms-flex:1;flex:1;-ms-flex-preferred-size:auto;flex-basis:auto;padding:20px}.el-footer,.el-main{box-sizing:border-box}.el-footer{padding:0 20px;-ms-flex-negative:0;flex-shrink:0}.el-timeline{margin:0;font-size:14px;list-style:none}.el-timeline .el-timeline-item:last-child .el-timeline-item__tail{display:none}.el-timeline-item{position:relative;padding-bottom:20px}.el-timeline-item__wrapper{position:relative;padding-left:28px;top:-3px}.el-timeline-item__tail{position:absolute;left:4px;height:100%;border-left:2px solid #e4e7ed}.el-timeline-item__icon{color:#fff;font-size:13px}.el-timeline-item__node{position:absolute;background-color:#e4e7ed;border-radius:50%;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center}.el-timeline-item__node--normal{left:-1px;width:12px;height:12px}.el-timeline-item__node--large{left:-2px;width:14px;height:14px}.el-timeline-item__node--primary,.el-timeline-item__node--success{background-color:#42d885}.el-timeline-item__node--warning{background-color:#f9c855}.el-timeline-item__node--danger{background-color:#ff6d6d}.el-timeline-item__node--info{background-color:#909399}.el-timeline-item__dot{position:absolute;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center}.el-timeline-item__content{color:#303133}.el-timeline-item__timestamp{color:#909399;line-height:1;font-size:13px}.el-timeline-item__timestamp.is-top{margin-bottom:8px;padding-top:4px}.el-timeline-item__timestamp.is-bottom{margin-top:8px}.el-link{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-direction:row;flex-direction:row;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;vertical-align:middle;position:relative;text-decoration:none;outline:none;cursor:pointer;padding:0;font-size:14px;font-weight:500}.el-link.is-underline:hover:after{content:"";position:absolute;left:0;right:0;height:0;bottom:0;border-bottom:1px solid #42d885}.el-link.is-disabled{cursor:not-allowed}.el-link [class*=el-icon-]+span{margin-left:5px}.el-link.el-link--default{color:#606266}.el-link.el-link--default:hover{color:#42d885}.el-link.el-link--default:after{border-color:#42d885}.el-link.el-link--default.is-disabled{color:#c0c4cc}.el-link.el-link--primary{color:#42d885}.el-link.el-link--primary:hover{color:#68e09d}.el-link.el-link--primary:after{border-color:#42d885}.el-link.el-link--primary.is-disabled{color:#a1ecc2}.el-link.el-link--primary.is-underline:hover:after{border-color:#42d885}.el-link.el-link--danger{color:#ff6d6d}.el-link.el-link--danger:hover{color:#ff8a8a}.el-link.el-link--danger:after{border-color:#ff6d6d}.el-link.el-link--danger.is-disabled{color:#ffb6b6}.el-link.el-link--danger.is-underline:hover:after{border-color:#ff6d6d}.el-link.el-link--success{color:#42d885}.el-link.el-link--success:hover{color:#68e09d}.el-link.el-link--success:after{border-color:#42d885}.el-link.el-link--success.is-disabled{color:#a1ecc2}.el-link.el-link--success.is-underline:hover:after{border-color:#42d885}.el-link.el-link--warning{color:#f9c855}.el-link.el-link--warning:hover{color:#fad377}.el-link.el-link--warning:after{border-color:#f9c855}.el-link.el-link--warning.is-disabled{color:#fce4aa}.el-link.el-link--warning.is-underline:hover:after{border-color:#f9c855}.el-link.el-link--info{color:#909399}.el-link.el-link--info:hover{color:#a6a9ad}.el-link.el-link--info:after{border-color:#909399}.el-link.el-link--info.is-disabled{color:#c8c9cc}.el-link.el-link--info.is-underline:hover:after{border-color:#909399}.el-divider{background-color:#dcdfe6;position:relative}.el-divider--horizontal{display:block;height:1px;width:100%;margin:24px 0}.el-divider--vertical{display:inline-block;width:1px;height:1em;margin:0 8px;vertical-align:middle;position:relative}.el-divider__text{position:absolute;background-color:#fff;padding:0 20px;font-weight:500;color:#303133;font-size:14px}.el-divider__text.is-left{left:20px;transform:translateY(-50%)}.el-divider__text.is-center{left:50%;transform:translateX(-50%) translateY(-50%)}.el-divider__text.is-right{right:20px;transform:translateY(-50%)}.el-image__error,.el-image__inner,.el-image__placeholder{width:100%;height:100%}.el-image{position:relative;display:inline-block;overflow:hidden}.el-image__inner{vertical-align:top}.el-image__inner--center{position:relative;top:50%;left:50%;transform:translate(-50%,-50%);display:block}.el-image__error,.el-image__placeholder{background:#f5f7fa}.el-image__error{display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center;font-size:14px;color:#c0c4cc;vertical-align:middle}.el-image__preview{cursor:pointer}.el-image-viewer__wrapper{position:fixed;top:0;right:0;bottom:0;left:0}.el-image-viewer__btn{position:absolute;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;border-radius:50%;opacity:.8;cursor:pointer;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.el-image-viewer__close{top:40px;right:40px;width:40px;height:40px;font-size:40px}.el-image-viewer__canvas{width:100%;height:100%;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center}.el-image-viewer__actions{left:50%;bottom:30px;transform:translateX(-50%);width:282px;height:44px;padding:0 23px;background-color:#606266;border-color:#fff;border-radius:22px}.el-image-viewer__actions__inner{width:100%;height:100%;text-align:justify;cursor:default;font-size:23px;color:#fff;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:distribute;justify-content:space-around}.el-image-viewer__prev{left:40px}.el-image-viewer__next,.el-image-viewer__prev{top:50%;transform:translateY(-50%);width:44px;height:44px;font-size:24px;color:#fff;background-color:#606266;border-color:#fff}.el-image-viewer__next{right:40px;text-indent:2px}.el-image-viewer__mask{position:absolute;width:100%;height:100%;top:0;left:0;opacity:.5;background:#000}.viewer-fade-enter-active{animation:viewer-fade-in .3s}.viewer-fade-leave-active{animation:viewer-fade-out .3s}@keyframes viewer-fade-in{0%{transform:translate3d(0,-20px,0);opacity:0}to{transform:translateZ(0);opacity:1}}@keyframes viewer-fade-out{0%{transform:translateZ(0);opacity:1}to{transform:translate3d(0,-20px,0);opacity:0}}.el-calendar{background-color:#fff}.el-calendar__header{display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between;padding:12px 20px;border-bottom:1px solid #ebeef5}.el-calendar__title{color:#000;-ms-flex-item-align:center;align-self:center}.el-calendar__body{padding:12px 20px 35px}.el-calendar-table{table-layout:fixed;width:100%}.el-calendar-table thead th{padding:12px 0;color:#606266;font-weight:400}.el-calendar-table:not(.is-range) td.next,.el-calendar-table:not(.is-range) td.prev{color:#c0c4cc}.el-calendar-table td{border-bottom:1px solid #ebeef5;border-right:1px solid #ebeef5;vertical-align:top;transition:background-color .2s ease}.el-calendar-table td.is-selected{background-color:#f2f8fe}.el-calendar-table td.is-today{color:#42d885}.el-calendar-table tr:first-child td{border-top:1px solid #ebeef5}.el-calendar-table tr td:first-child{border-left:1px solid #ebeef5}.el-calendar-table tr.el-calendar-table__row--hide-border td{border-top:none}.el-calendar-table .el-calendar-day{box-sizing:border-box;padding:8px;height:85px}.el-calendar-table .el-calendar-day:hover{cursor:pointer;background-color:#f2f8fe}.el-backtop{position:fixed;background-color:#fff;width:40px;height:40px;border-radius:50%;color:#42d885;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;font-size:20px;box-shadow:0 0 6px rgba(0,0,0,.12);cursor:pointer;z-index:5}.el-backtop:hover{background-color:#f2f6fc}.el-page-header{display:-ms-flexbox;display:flex;line-height:24px}.el-page-header__left{display:-ms-flexbox;display:flex;cursor:pointer;margin-right:40px;position:relative}.el-page-header__left:after{content:"";position:absolute;width:1px;height:16px;right:-20px;top:50%;transform:translateY(-50%);background-color:#dcdfe6}.el-page-header__left .el-icon-back{font-size:18px;margin-right:6px;-ms-flex-item-align:center;align-self:center}.el-page-header__title{font-size:14px;font-weight:500}.el-page-header__content{font-size:18px;color:#303133}.el-checkbox{color:#606266;font-weight:500;font-size:14px;position:relative;cursor:pointer;display:inline-block;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;margin-right:30px}.el-checkbox.is-bordered{padding:9px 20px 9px 10px;border-radius:4px;border:1px solid #dcdfe6;box-sizing:border-box;line-height:normal;height:40px}.el-checkbox.is-bordered.is-checked{border-color:#42d885}.el-checkbox.is-bordered.is-disabled{border-color:#ebeef5;cursor:not-allowed}.el-checkbox.is-bordered+.el-checkbox.is-bordered{margin-left:10px}.el-checkbox.is-bordered.el-checkbox--medium{padding:7px 20px 7px 10px;border-radius:4px;height:36px}.el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__label{line-height:17px;font-size:14px}.el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__inner{height:14px;width:14px}.el-checkbox.is-bordered.el-checkbox--small{padding:5px 15px 5px 10px;border-radius:3px;height:32px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__label{line-height:15px;font-size:12px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner{height:12px;width:12px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner:after{height:6px;width:2px}.el-checkbox.is-bordered.el-checkbox--mini{padding:3px 15px 3px 10px;border-radius:3px;height:28px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__label{line-height:12px;font-size:12px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner{height:12px;width:12px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner:after{height:6px;width:2px}.el-checkbox__input{white-space:nowrap;cursor:pointer;outline:none;display:inline-block;line-height:1;position:relative;vertical-align:middle}.el-checkbox__input.is-disabled .el-checkbox__inner{background-color:#edf2fc;border-color:#dcdfe6;cursor:not-allowed}.el-checkbox__input.is-disabled .el-checkbox__inner:after{cursor:not-allowed;border-color:#c0c4cc}.el-checkbox__input.is-disabled .el-checkbox__inner+.el-checkbox__label{cursor:not-allowed}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner{background-color:#f2f6fc;border-color:#dcdfe6}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner:after{border-color:#c0c4cc}.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner{background-color:#f2f6fc;border-color:#dcdfe6}.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner:before{background-color:#c0c4cc;border-color:#c0c4cc}.el-checkbox__input.is-disabled+span.el-checkbox__label{color:#c0c4cc;cursor:not-allowed}.el-checkbox__input.is-checked .el-checkbox__inner{background-color:#42d885;border-color:#42d885}.el-checkbox__input.is-checked .el-checkbox__inner:after{transform:rotate(45deg) scaleY(1)}.el-checkbox__input.is-checked+.el-checkbox__label{color:#42d885}.el-checkbox__input.is-focus .el-checkbox__inner{border-color:#42d885}.el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:#42d885;border-color:#42d885}.el-checkbox__input.is-indeterminate .el-checkbox__inner:before{content:"";position:absolute;display:block;background-color:#fff;height:2px;transform:scale(.5);left:0;right:0;top:5px}.el-checkbox__input.is-indeterminate .el-checkbox__inner:after{display:none}.el-checkbox__inner{display:inline-block;position:relative;border:1px solid #dcdfe6;border-radius:2px;box-sizing:border-box;width:14px;height:14px;background-color:#fff;z-index:1;transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46)}.el-checkbox__inner:hover{border-color:#42d885}.el-checkbox__inner:after{box-sizing:content-box;content:"";border:1px solid #fff;border-left:0;border-top:0;height:7px;left:4px;position:absolute;top:1px;transform:rotate(45deg) scaleY(0);width:3px;transition:transform .15s ease-in .05s;transform-origin:center}.el-checkbox__original{opacity:0;outline:none;position:absolute;margin:0;width:0;height:0;z-index:-1}.el-checkbox__label{display:inline-block;padding-left:10px;line-height:19px;font-size:14px}.el-checkbox:last-of-type{margin-right:0}.el-checkbox-button,.el-checkbox-button__inner{position:relative;display:inline-block}.el-checkbox-button__inner{line-height:1;font-weight:500;white-space:nowrap;vertical-align:middle;cursor:pointer;background:#fff;border:1px solid #dcdfe6;border-left:0;color:#606266;-webkit-appearance:none;text-align:center;box-sizing:border-box;outline:none;margin:0;transition:all .3s cubic-bezier(.645,.045,.355,1);-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;padding:12px 20px;font-size:14px;border-radius:0}.el-checkbox-button__inner.is-round{padding:12px 20px}.el-checkbox-button__inner:hover{color:#42d885}.el-checkbox-button__inner [class*=el-icon-]{line-height:.9}.el-checkbox-button__inner [class*=el-icon-]+span{margin-left:5px}.el-checkbox-button__original{opacity:0;outline:none;position:absolute;margin:0;z-index:-1}.el-checkbox-button.is-checked .el-checkbox-button__inner{color:#fff;background-color:#42d885;border-color:#42d885;box-shadow:-1px 0 0 0 #8ee8b6}.el-checkbox-button.is-checked:first-child .el-checkbox-button__inner{border-left-color:#42d885}.el-checkbox-button.is-disabled .el-checkbox-button__inner{color:#c0c4cc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#ebeef5;box-shadow:none}.el-checkbox-button.is-disabled:first-child .el-checkbox-button__inner{border-left-color:#ebeef5}.el-checkbox-button:first-child .el-checkbox-button__inner{border-left:1px solid #dcdfe6;border-radius:4px 0 0 4px;box-shadow:none!important}.el-checkbox-button.is-focus .el-checkbox-button__inner{border-color:#42d885}.el-checkbox-button:last-child .el-checkbox-button__inner{border-radius:0 4px 4px 0}.el-checkbox-button--medium .el-checkbox-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.el-checkbox-button--medium .el-checkbox-button__inner.is-round{padding:10px 20px}.el-checkbox-button--small .el-checkbox-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.el-checkbox-button--small .el-checkbox-button__inner.is-round{padding:9px 15px}.el-checkbox-button--mini .el-checkbox-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.el-checkbox-button--mini .el-checkbox-button__inner.is-round{padding:7px 15px}.el-checkbox-group{font-size:0}.el-radio{color:#606266;font-weight:500;line-height:1;position:relative;cursor:pointer;display:inline-block;white-space:nowrap;outline:none;font-size:14px;margin-right:30px;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.el-radio.is-bordered{padding:12px 20px 0 10px;border-radius:4px;border:1px solid #dcdfe6;box-sizing:border-box;height:40px}.el-radio.is-bordered.is-checked{border-color:#42d885}.el-radio.is-bordered.is-disabled{cursor:not-allowed;border-color:#ebeef5}.el-radio.is-bordered+.el-radio.is-bordered{margin-left:10px}.el-radio--medium.is-bordered{padding:10px 20px 0 10px;border-radius:4px;height:36px}.el-radio--medium.is-bordered .el-radio__label{font-size:14px}.el-radio--medium.is-bordered .el-radio__inner{height:14px;width:14px}.el-radio--small.is-bordered{padding:8px 15px 0 10px;border-radius:3px;height:32px}.el-radio--small.is-bordered .el-radio__label{font-size:12px}.el-radio--small.is-bordered .el-radio__inner{height:12px;width:12px}.el-radio--mini.is-bordered{padding:6px 15px 0 10px;border-radius:3px;height:28px}.el-radio--mini.is-bordered .el-radio__label{font-size:12px}.el-radio--mini.is-bordered .el-radio__inner{height:12px;width:12px}.el-radio:last-child{margin-right:0}.el-radio__input{white-space:nowrap;cursor:pointer;outline:none;display:inline-block;line-height:1;position:relative;vertical-align:middle}.el-radio__input.is-disabled .el-radio__inner{background-color:#f5f7fa;border-color:#e4e7ed;cursor:not-allowed}.el-radio__input.is-disabled .el-radio__inner:after{cursor:not-allowed;background-color:#f5f7fa}.el-radio__input.is-disabled .el-radio__inner+.el-radio__label{cursor:not-allowed}.el-radio__input.is-disabled.is-checked .el-radio__inner{background-color:#f5f7fa;border-color:#e4e7ed}.el-radio__input.is-disabled.is-checked .el-radio__inner:after{background-color:#c0c4cc}.el-radio__input.is-disabled+span.el-radio__label{color:#c0c4cc;cursor:not-allowed}.el-radio__input.is-checked .el-radio__inner{border-color:#42d885;background:#42d885}.el-radio__input.is-checked .el-radio__inner:after{transform:translate(-50%,-50%) scale(1)}.el-radio__input.is-checked+.el-radio__label{color:#42d885}.el-radio__input.is-focus .el-radio__inner{border-color:#42d885}.el-radio__inner{border:1px solid #dcdfe6;border-radius:100%;width:14px;height:14px;background-color:#fff;position:relative;cursor:pointer;display:inline-block;box-sizing:border-box}.el-radio__inner:hover{border-color:#42d885}.el-radio__inner:after{width:4px;height:4px;border-radius:100%;background-color:#fff;content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%) scale(0);transition:transform .15s ease-in}.el-radio__original{opacity:0;outline:none;position:absolute;z-index:-1;top:0;left:0;right:0;bottom:0;margin:0}.el-radio:focus:not(.is-focus):not(:active):not(.is-disabled) .el-radio__inner{box-shadow:0 0 2px 2px #42d885}.el-radio__label{font-size:14px;padding-left:10px}.el-scrollbar{overflow:hidden;position:relative}.el-scrollbar:active>.el-scrollbar__bar,.el-scrollbar:focus>.el-scrollbar__bar,.el-scrollbar:hover>.el-scrollbar__bar{opacity:1;transition:opacity .34s ease-out}.el-scrollbar__wrap{overflow:scroll;height:100%}.el-scrollbar__wrap--hidden-default::-webkit-scrollbar{width:0;height:0}.el-scrollbar__thumb{position:relative;display:block;width:0;height:0;cursor:pointer;border-radius:inherit;background-color:hsla(220,4%,58%,.3);transition:background-color .3s}.el-scrollbar__thumb:hover{background-color:hsla(220,4%,58%,.5)}.el-scrollbar__bar{position:absolute;right:2px;bottom:2px;z-index:1;border-radius:4px;opacity:0;transition:opacity .12s ease-out}.el-scrollbar__bar.is-vertical{width:6px;top:2px}.el-scrollbar__bar.is-vertical>div{width:100%}.el-scrollbar__bar.is-horizontal{height:6px;left:2px}.el-scrollbar__bar.is-horizontal>div{height:100%}.el-cascader-panel{display:-ms-flexbox;display:flex;border-radius:4px;font-size:14px}.el-cascader-panel.is-bordered{border:1px solid #e4e7ed;border-radius:4px}.el-cascader-menu{min-width:180px;box-sizing:border-box;color:#606266;border-right:1px solid #e4e7ed}.el-cascader-menu:last-child{border-right:none}.el-cascader-menu:last-child .el-cascader-node{padding-right:20px}.el-cascader-menu__wrap{height:204px}.el-cascader-menu__list{position:relative;min-height:100%;margin:0;padding:6px 0;list-style:none;box-sizing:border-box}.el-cascader-menu__hover-zone{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.el-cascader-menu__empty-text{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;color:#c0c4cc}.el-cascader-node{position:relative;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:0 30px 0 20px;height:34px;line-height:34px;outline:none}.el-cascader-node.is-selectable.in-active-path{color:#606266}.el-cascader-node.in-active-path,.el-cascader-node.is-active,.el-cascader-node.is-selectable.in-checked-path{color:#42d885;font-weight:700}.el-cascader-node:not(.is-disabled){cursor:pointer}.el-cascader-node:not(.is-disabled):focus,.el-cascader-node:not(.is-disabled):hover{background:#f5f7fa}.el-cascader-node.is-disabled{color:#c0c4cc;cursor:not-allowed}.el-cascader-node__prefix{position:absolute;left:10px}.el-cascader-node__postfix{position:absolute;right:10px}.el-cascader-node__label{-ms-flex:1;flex:1;padding:0 10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.el-cascader-node>.el-checkbox,.el-cascader-node>.el-radio{margin-right:0}.el-cascader-node>.el-radio .el-radio__label{padding-left:0}.el-avatar{display:inline-block;box-sizing:border-box;text-align:center;overflow:hidden;color:#fff;background:#c0c4cc;width:40px;height:40px;line-height:40px;font-size:14px}.el-avatar>img{display:block;height:100%;vertical-align:middle}.el-avatar--circle{border-radius:50%}.el-avatar--square{border-radius:4px}.el-avatar--icon{font-size:18px}.el-avatar--large{width:40px;height:40px;line-height:40px}.el-avatar--medium{width:36px;height:36px;line-height:36px}.el-avatar--small{width:28px;height:28px;line-height:28px}@keyframes el-drawer-fade-in{0%{opacity:0}to{opacity:1}}@keyframes rtl-drawer-in{0%{transform:translate(100%)}to{transform:translate(0)}}@keyframes rtl-drawer-out{0%{transform:translate(0)}to{transform:translate(100%)}}@keyframes ltr-drawer-in{0%{transform:translate(-100%)}to{transform:translate(0)}}@keyframes ltr-drawer-out{0%{transform:translate(0)}to{transform:translate(-100%)}}@keyframes ttb-drawer-in{0%{transform:translateY(-100%)}to{transform:translate(0)}}@keyframes ttb-drawer-out{0%{transform:translate(0)}to{transform:translateY(-100%)}}@keyframes btt-drawer-in{0%{transform:translateY(100%)}to{transform:translate(0)}}@keyframes btt-drawer-out{0%{transform:translate(0)}to{transform:translateY(100%)}}.el-drawer{position:absolute;box-sizing:border-box;background-color:#fff;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12);overflow:hidden}.el-drawer.rtl{animation:rtl-drawer-out 225ms cubic-bezier(0,0,.2,1) 0ms}.el-drawer__open .el-drawer.rtl{animation:rtl-drawer-in 225ms cubic-bezier(0,0,.2,1) 0ms}.el-drawer.ltr{animation:ltr-drawer-out 225ms cubic-bezier(0,0,.2,1) 0ms}.el-drawer__open .el-drawer.ltr{animation:ltr-drawer-in 225ms cubic-bezier(0,0,.2,1) 0ms}.el-drawer.ttb{animation:ttb-drawer-out 225ms cubic-bezier(0,0,.2,1) 0ms}.el-drawer__open .el-drawer.ttb{animation:ttb-drawer-in 225ms cubic-bezier(0,0,.2,1) 0ms}.el-drawer.btt{animation:btt-drawer-out 225ms cubic-bezier(0,0,.2,1) 0ms}.el-drawer__open .el-drawer.btt{animation:btt-drawer-in 225ms cubic-bezier(0,0,.2,1) 0ms}.el-drawer__header{-ms-flex-align:center;align-items:center;color:#72767b;display:-ms-flexbox;display:flex;margin-bottom:32px;padding:20px;padding-bottom:0}.el-drawer__header>:first-child{-ms-flex:1;flex:1}.el-drawer__title{margin:0;-ms-flex:1;flex:1;line-height:inherit;font-size:1rem}.el-drawer__close-btn{border:none;cursor:pointer;font-size:20px;color:inherit;background-color:transparent}.el-drawer__body{-ms-flex:1;flex:1}.el-drawer__body>*{box-sizing:border-box}.el-drawer.ltr,.el-drawer.rtl{height:100%;top:0;bottom:0}.el-drawer.btt,.el-drawer.ttb{width:100%;left:0;right:0}.el-drawer.ltr{left:0}.el-drawer.rtl{right:0}.el-drawer.ttb{top:0}.el-drawer.btt{bottom:0}.el-drawer__container{position:relative;left:0;right:0;top:0;bottom:0;height:100%;width:100%}.el-drawer-fade-enter-active{animation:el-drawer-fade-in 225ms cubic-bezier(0,0,.2,1) 0ms}.el-drawer-fade-leave-active{animation:el-drawer-fade-in 225ms cubic-bezier(0,0,.2,1) 0ms reverse}.dark-themes #app,.dark-themes body,.dark-themeshtml{background-color:#161616;color:#a7a7a7}.dark-themes .h1,.dark-themes .h2,.dark-themes .h3,.dark-themes .h4,.dark-themes .h5,.dark-themes .h6,.dark-themes h1,.dark-themes h2,.dark-themes h3,.dark-themes h4,.dark-themes h5,.dark-themes h6,.dark-themes p{color:#a7a7a7;font-weight:400}.dark-themes .el-table:not(.el-table--public){color:#a7a7a7!important;border:1px solid #292929;background-color:transparent!important}.dark-themes .el-table:not(.el-table--public) .el-table__empty-block,.dark-themes .el-table:not(.el-table--public) th,.dark-themes .el-table:not(.el-table--public) tr{background-color:#1f1d1d!important}.dark-themes .el-table:not(.el-table--public) .el-table__header-wrapper,.dark-themes .el-table:not(.el-table--public) td,.dark-themes .el-table:not(.el-table--public) th.is-leaf{border-bottom:1px solid #161616}.dark-themes .el-table:not(.el-table--public) .el-table__header-wrapper .cell{color:#ddd!important}.dark-themes .el-table:not(.el-table--public) .el-table__expanded-cell{background-color:#242327}.dark-themes .el-table:not(.el-table--public) .el-table__body-wrapper .el-table__body tr.current-row>td{background-color:transparent}.dark-themes .el-table:not(.el-table--public) .el-table__body-wrapper .el-table__body tr:hover td{background-color:#393a3e!important;color:#ddd!important}.dark-themes .el-table:not(.el-table--public) .el-table__body-wrapper .el-table__body tr:last-child td{border-bottom:none}.dark-themes .el-table__body tr.hover-row.current-row>td,.dark-themes .el-table__body tr.hover-row.el-table__row--striped.current-row>td,.dark-themes .el-table__body tr.hover-row.el-table__row--striped>td,.dark-themes .el-table__body tr.hover-row>td,.dark-themes .el-table__body tr:hover>td{background-color:#393a3e!important}.dark-themes .el-table__fixed,.dark-themes .el-table__fixed-right{box-shadow:0 0 10px rgba(0,0,0,.48)}.dark-themes .el-checkbox .el-checkbox__input.is-checked+.el-checkbox__label{color:#606266}.dark-themes .el-select:not(.el-select--public) .el-input__inner{background-color:transparent!important;color:#a7a7a7}.dark-themes .el-select:not(.el-select--public) .el-input__inner:focus,.dark-themes .el-select:not(.el-select--public) .el-input__inner:hover{border-color:#737373}.dark-themes .el-select:not(.el-select--public).select-radius .el-input__inner{border-color:#37363d!important}.dark-themes .el-select:not(.el-select--public):hover .el-input__inner{border-color:#737373!important}.dark-themes .el-dialog:not(.el-dialog--private) .el-dialog__body{padding:10px 20px}.dark-themes .el-select-dropdown:not(.el-select--public){border-color:#373737;background-color:#373737;border-left:none;border-right:none}.dark-themes .el-select-dropdown:not(.el-select--public) .el-select-dropdown__item{color:#a7a7a7!important}.dark-themes .el-select-dropdown:not(.el-select--public) .el-select-dropdown__item.hover,.dark-themes .el-select-dropdown:not(.el-select--public) .el-select-dropdown__item.selected,.dark-themes .el-select-dropdown:not(.el-select--public) .el-select-dropdown__item:hover{background-color:#282828}.dark-themes .el-select-dropdown:not(.el-select--public) .popper__arrow,.dark-themes .el-select-dropdown:not(.el-select--public) .popper__arrow:after{border-bottom-color:#373737}.dark-themes .el-date-picker .el-popper[x-placement^=bottom] .popper__arrow,.dark-themes .el-date-picker .el-popper[x-placement^=bottom] .popper__arrow:after{border-bottom-color:#fff!important}.dark-themes .el-form:not(.el-form--public) .el-form-item .el-checkbox__label,.dark-themes .el-form:not(.el-form--public) .el-form-item .el-form-item__label,.dark-themes .page-title .el-form-item .el-checkbox__label,.dark-themes .page-title .el-form-item .el-form-item__label{color:#a7a7a7!important}.dark-themes .el-form:not(.el-form--public) .el-input .el-input__inner,.dark-themes .el-form:not(.el-form--public) .el-input .el-textarea__inner,.dark-themes .el-form:not(.el-form--public) .el-textarea .el-input__inner,.dark-themes .el-form:not(.el-form--public) .el-textarea .el-textarea__inner,.dark-themes .page-title .el-input .el-input__inner,.dark-themes .page-title .el-input .el-textarea__inner,.dark-themes .page-title .el-textarea .el-input__inner,.dark-themes .page-title .el-textarea .el-textarea__inner{background-color:transparent!important;color:#a7a7a7!important;border-color:#37363d!important}.dark-themes .el-form:not(.el-form--public) .el-input.is-disabled .el-input__inner,.dark-themes .el-form:not(.el-form--public) .el-input.is-disabled .el-textarea__inner,.dark-themes .el-form:not(.el-form--public) .el-textarea.is-disabled .el-input__inner,.dark-themes .el-form:not(.el-form--public) .el-textarea.is-disabled .el-textarea__inner,.dark-themes .page-title .el-input.is-disabled .el-input__inner,.dark-themes .page-title .el-input.is-disabled .el-textarea__inner,.dark-themes .page-title .el-textarea.is-disabled .el-input__inner,.dark-themes .page-title .el-textarea.is-disabled .el-textarea__inner{background-color:#242325!important;border-color:#242325!important}.dark-themes .el-form:not(.el-form--public) .el-input.is-disabled:hover .el-input__inner,.dark-themes .el-form:not(.el-form--public) .el-input.is-disabled:hover .el-textarea__inner,.dark-themes .el-form:not(.el-form--public) .el-textarea.is-disabled:hover .el-input__inner,.dark-themes .el-form:not(.el-form--public) .el-textarea.is-disabled:hover .el-textarea__inner,.dark-themes .page-title .el-input.is-disabled:hover .el-input__inner,.dark-themes .page-title .el-input.is-disabled:hover .el-textarea__inner,.dark-themes .page-title .el-textarea.is-disabled:hover .el-input__inner,.dark-themes .page-title .el-textarea.is-disabled:hover .el-textarea__inner{border-color:#242325!important}.dark-themes .el-form:not(.el-form--public) .el-input.input-radius .el-input__inner,.dark-themes .el-form:not(.el-form--public) .el-textarea.input-radius .el-input__inner,.dark-themes .page-title .el-input.input-radius .el-input__inner,.dark-themes .page-title .el-textarea.input-radius .el-input__inner{border-color:#37363d!important;border-radius:40px}.dark-themes .el-form:not(.el-form--public) .el-input:hover .el-input__inner,.dark-themes .el-form:not(.el-form--public) .el-input:hover .el-textarea__inner,.dark-themes .el-form:not(.el-form--public) .el-textarea:hover .el-input__inner,.dark-themes .el-form:not(.el-form--public) .el-textarea:hover .el-textarea__inner,.dark-themes .page-title .el-input:hover .el-input__inner,.dark-themes .page-title .el-input:hover .el-textarea__inner,.dark-themes .page-title .el-textarea:hover .el-input__inner,.dark-themes .page-title .el-textarea:hover .el-textarea__inner{border-color:#737373!important}.dark-themes .el-form:not(.el-form--public) input::-webkit-input-placeholder,.dark-themes .el-form:not(.el-form--public) textarea::-webkit-input-placeholder,.dark-themes .page-title input::-webkit-input-placeholder,.dark-themes .page-title textarea::-webkit-input-placeholder{color:hsla(0,0%,65%,.5)!important}.dark-themes .el-form:not(.el-form--public) input:-moz-placeholder,.dark-themes .el-form:not(.el-form--public) input::-moz-placeholder,.dark-themes .el-form:not(.el-form--public) textarea:-moz-placeholder,.dark-themes .el-form:not(.el-form--public) textarea::-moz-placeholder,.dark-themes .page-title input:-moz-placeholder,.dark-themes .page-title input::-moz-placeholder,.dark-themes .page-title textarea:-moz-placeholder,.dark-themes .page-title textarea::-moz-placeholder{color:hsla(0,0%,65%,.5)!important}.dark-themes .el-form:not(.el-form--public) input:-ms-input-placeholder,.dark-themes .el-form:not(.el-form--public) textarea:-ms-input-placeholder,.dark-themes .page-title input:-ms-input-placeholder,.dark-themes .page-title textarea:-ms-input-placeholder{color:hsla(0,0%,65%,.5)!important}.dark-themes .el-form.el-form--public .el-input.is-disabled .el-input__inner,.dark-themes .el-form.el-form--public .el-input.is-disabled .el-textarea__inner,.dark-themes .el-form.el-form--public .el-input.is-disabled:hover .el-input__inner,.dark-themes .el-form.el-form--public .el-input.is-disabled:hover .el-textarea__inner{border-color:#f5f7fa!important}.dark-themes .el-loading-mask{background-color:#232429!important}.dark-themes .el-button.is-plain{color:#a7a7a7!important;background-color:transparent!important;border-color:#44444c!important}.dark-themes .el-button.is-plain.el-button--success:hover{border-color:#42d885!important;background-color:#42d885!important;color:#fff!important}.dark-themes .el-button.is-plain.el-button--warning:hover{border-color:#f9c855!important;background-color:#f9c855!important;color:#fff!important}.dark-themes .el-button.is-plain.el-button--danger:hover{border-color:#ff6d6d!important;background-color:#ff6d6d!important;color:#fff!important}.dark-themes .el-card.el-card--self{color:#a7a7a7!important;background-color:#1f1d1d!important}.dark-themes .el-card.el-card--self .el-card__header{color:#fff!important}.dark-themes .cache-btn{color:#a7a7a7!important}.dark-themes .cache-btn:hover{color:#42d885!important}.dark-themes .refresh-btn{color:#fff!important}.dark-themes .page-title,.dark-themes .refresh-btn:hover{color:#fff}.dark-themes .page-title .el-breadcrumb .el-breadcrumb__inner{color:#e8e8e8!important}.dark-themes .page-title .el-breadcrumb .el-breadcrumb__inner:hover{color:#fff!important}.dark-themes .page-title .el-breadcrumb .el-breadcrumb__item.breadcrumb-name .el-breadcrumb__inner,.dark-themes .page-title .el-breadcrumb .el-breadcrumb__item.breadcrumb-name:hover .el-breadcrumb__inner{color:#929299!important}.dark-themes .overview-view .card-box .card-title{color:#fff}.dark-themes .overview-view .card-item{background-color:#1f1d1d;border:1px solid #292929}.dark-themes .overview-view .card-item .icon{color:#227d51;border-color:#227d51;background-color:#232d2e}.dark-themes .overview-view .card-item .desc h3{color:#fff}.dark-themes .overview-view .card-item .desc p{color:#929299}.dark-themes .status:before{background-color:#a7a7a7!important}.dark-themes .status.running{color:#227d51!important}.dark-themes .status.stopped.danger:before{background-color:#f56c6c!important}.dark-themes .status.running:before{background-color:#227d51!important}.dark-themes .el-pagination .el-select .el-input .el-input__inner{border-color:#37363d}.dark-themes .el-pagination.is-background{color:#a7a7a7!important}.dark-themes .el-pagination.is-background button{background-color:transparent!important}.dark-themes .el-pagination.is-background button:hover{color:#42d885}.dark-themes .el-pagination.is-background .el-pager .number{color:#a7a7a7!important;background-color:transparent!important}.dark-themes .el-pagination.is-background .el-pager .number:hover{color:#42d885!important}.dark-themes .el-pagination.is-background .el-pager .number.active{background-color:#42d885!important;color:#fff!important}.dark-themes .rule-create .form-block--wrapper{border-bottom:1px solid #37363d}.dark-themes .rule-create .sql-tips{border:4px dashed #37363d;color:#c0c4cc}.dark-themes .rule-create .sql-tips .title{color:#fff}.dark-themes .rule-create .sql-tips p{color:#c0c4cc}.dark-themes .rule-create .code{background-color:rgba(55,54,61,.5)}.dark-themes .rule-create .code-sql__editor{border:1px solid #37363d;background-color:#2b292d}.dark-themes .rule-create .code-sql__editor .CodeMirror-gutters{background-color:#2b292d!important;border-right-color:#37363d!important}.dark-themes .rule-create .code-sql__editor .CodeMirror-scroll{background-color:#1e1c1c}.dark-themes .rule-create .code-sql__editor .CodeMirror-line{color:#606266}.dark-themes .rule-create .code-sql__editor .cm-keyword,.dark-themes .rule-create .code-sql__editor .cm-operator{color:#f92371}.dark-themes .rule-create .payload-type{background-color:#37363d80;border:1px solid #37363d;border-top:none}.dark-themes .rule-actions .action-card{background:#37363d80}.dark-themes .rule-actions .action-card .action-footer{border-top:1px solid #37363d}.dark-themes .rule-actions .action-card .filed-item .title{color:#ddd}.dark-themes .monaco-container{height:100%;border:1px solid #37363d}.dark-themes .jwt-payload-desc{background:rgba(55,54,61,.5)}.dark-themes .clients-view .card-subtitle{color:#f8f8f8}.dark-themes .custom-pagination a{border:1px solid #292929}.dark-themes .topic-qos-radio .el-radio-button__inner{background:#1b1c20;color:#9e9e9f;border:1px solid #35363b}.dark-themes .topic-qos-radio .el-radio-button__orig-radio:checked+.el-radio-button__inner{background-color:#42d885;color:#fff;box-shadow:none}.dark-themes .topic-metrics .message-card.in{background:linear-gradient(90deg,#1d1e27,#272644)}.dark-themes .topic-metrics .message-card.out{background:linear-gradient(90deg,#1c2023,#1e322d)}.dark-themes .topic-metrics .message-card.drop{background:linear-gradient(90deg,#1a1f27,#212d44)}.dark-themes .alarms-view .table-title{color:#fff}.light-themes #app,.light-themes body,.light-themeshtml{background-color:#f6f7fb;color:#71737d}.light-themes .h1,.light-themes .h2,.light-themes .h3,.light-themes .h4,.light-themes .h5,.light-themes .h6,.light-themes h1,.light-themes h2,.light-themes h3,.light-themes h4,.light-themes h5,.light-themes h6,.light-themes p{color:#71737d;font-weight:400}.light-themes .el-table:not(.el-table--public){color:#71737d;border:1px solid #f1f1f1;background-color:transparent!important}.light-themes .el-table:not(.el-table--public) .el-table__empty-block,.light-themes .el-table:not(.el-table--public) th,.light-themes .el-table:not(.el-table--public) tr{background-color:#fff!important}.light-themes .el-table:not(.el-table--public) .el-table__header-wrapper,.light-themes .el-table:not(.el-table--public) td,.light-themes .el-table:not(.el-table--public) th.is-leaf{border-bottom:1px solid #f1f1f1}.light-themes .el-table:not(.el-table--public) .el-table__header-wrapper .cell{color:#606266!important}.light-themes .el-table:not(.el-table--public) .el-table__body-wrapper .el-table__body tr.current-row>td{background-color:transparent}.light-themes .el-table:not(.el-table--public) .el-table__body-wrapper .el-table__body tr:hover td{background-color:#f5f5f5!important;color:#71737d!important}.light-themes .el-table:not(.el-table--public) .el-table__body-wrapper .el-table__body tr:last-child td{border-bottom:none}.light-themes .el-table__body tr.hover-row.current-row>td,.light-themes .el-table__body tr.hover-row.el-table__row--striped.current-row>td,.light-themes .el-table__body tr.hover-row.el-table__row--striped>td,.light-themes .el-table__body tr.hover-row>td,.light-themes .el-table__body tr:hover>td{background-color:#f5f5f5!important}.light-themes .el-checkbox .el-checkbox__input.is-checked+.el-checkbox__label{color:#606266}.light-themes .el-select:not(.el-select--public) .el-input__inner{background-color:transparent!important;color:#71737d}.light-themes .el-select:not(.el-select--public) .el-input__inner:focus,.light-themes .el-select:not(.el-select--public) .el-input__inner:hover{border-color:#c0c4cc}.light-themes .el-dialog:not(.el-dialog--private) .el-dialog__body{padding:10px 20px}.light-themes .el-select-dropdown:not(.el-select--public){background-color:#fff}.light-themes .el-select-dropdown:not(.el-select--public) .el-select-dropdown__item{color:#71737d!important}.light-themes .el-select-dropdown:not(.el-select--public) .el-select-dropdown__item.hover,.light-themes .el-select-dropdown:not(.el-select--public) .el-select-dropdown__item.selected,.light-themes .el-select-dropdown:not(.el-select--public) .el-select-dropdown__item:hover{background-color:#f5f5f5}.light-themes .el-form:not(.el-form--public) .el-form-item .el-checkbox__label,.light-themes .el-form:not(.el-form--public) .el-form-item .el-form-item__label,.light-themes .page-title .el-form-item .el-checkbox__label,.light-themes .page-title .el-form-item .el-form-item__label{color:#71737d!important}.light-themes .el-form:not(.el-form--public) .el-input .el-input__inner,.light-themes .el-form:not(.el-form--public) .el-input .el-textarea__inner,.light-themes .el-form:not(.el-form--public) .el-textarea .el-input__inner,.light-themes .el-form:not(.el-form--public) .el-textarea .el-textarea__inner,.light-themes .page-title .el-input .el-input__inner,.light-themes .page-title .el-input .el-textarea__inner,.light-themes .page-title .el-textarea .el-input__inner,.light-themes .page-title .el-textarea .el-textarea__inner{background-color:transparent!important}.light-themes .el-form:not(.el-form--public) .el-input.is-disabled .el-input__inner,.light-themes .el-form:not(.el-form--public) .el-input.is-disabled .el-textarea__inner,.light-themes .el-form:not(.el-form--public) .el-textarea.is-disabled .el-input__inner,.light-themes .el-form:not(.el-form--public) .el-textarea.is-disabled .el-textarea__inner,.light-themes .page-title .el-input.is-disabled .el-input__inner,.light-themes .page-title .el-input.is-disabled .el-textarea__inner,.light-themes .page-title .el-textarea.is-disabled .el-input__inner,.light-themes .page-title .el-textarea.is-disabled .el-textarea__inner{background-color:#f5f5f5!important;border-color:#f5f5f5!important}.light-themes .el-form:not(.el-form--public) .el-input.is-disabled:hover .el-input__inner,.light-themes .el-form:not(.el-form--public) .el-input.is-disabled:hover .el-textarea__inner,.light-themes .el-form:not(.el-form--public) .el-textarea.is-disabled:hover .el-input__inner,.light-themes .el-form:not(.el-form--public) .el-textarea.is-disabled:hover .el-textarea__inner,.light-themes .page-title .el-input.is-disabled:hover .el-input__inner,.light-themes .page-title .el-input.is-disabled:hover .el-textarea__inner,.light-themes .page-title .el-textarea.is-disabled:hover .el-input__inner,.light-themes .page-title .el-textarea.is-disabled:hover .el-textarea__inner{border-color:#f5f5f5!important}.light-themes .el-form:not(.el-form--public) .el-input.input-radius .el-input__inner,.light-themes .el-form:not(.el-form--public) .el-textarea.input-radius .el-input__inner,.light-themes .page-title .el-input.input-radius .el-input__inner,.light-themes .page-title .el-textarea.input-radius .el-input__inner{border-radius:40px}.light-themes .el-form:not(.el-form--public) .el-input.input-radius .el-input__inner:focus,.light-themes .el-form:not(.el-form--public) .el-input.input-radius .el-input__inner:hover,.light-themes .el-form:not(.el-form--public) .el-textarea.input-radius .el-input__inner:focus,.light-themes .el-form:not(.el-form--public) .el-textarea.input-radius .el-input__inner:hover,.light-themes .page-title .el-input.input-radius .el-input__inner:focus,.light-themes .page-title .el-input.input-radius .el-input__inner:hover,.light-themes .page-title .el-textarea.input-radius .el-input__inner:focus,.light-themes .page-title .el-textarea.input-radius .el-input__inner:hover{border-color:#c0c4cc}.light-themes .el-form.el-form--public .el-input.is-disabled .el-input__inner,.light-themes .el-form.el-form--public .el-input.is-disabled .el-textarea__inner,.light-themes .el-form.el-form--public .el-input.is-disabled:hover .el-input__inner,.light-themes .el-form.el-form--public .el-input.is-disabled:hover .el-textarea__inner{border-color:#f5f7fa!important}.light-themes .el-loading-mask{background-color:#f5f5f5!important}.light-themes .el-button.is-plain{color:#71737d!important;background-color:transparent!important;border-color:#d6d9e2!important}.light-themes .el-button.is-plain.el-button--success:hover{border-color:#42d885!important;background-color:#42d885!important;color:#fff!important}.light-themes .el-button.is-plain.el-button--warning:hover{border-color:#f9c855!important;background-color:#f9c855!important;color:#fff!important}.light-themes .el-button.is-plain.el-button--danger:hover{border-color:#ff6d6d!important;background-color:#ff6d6d!important;color:#fff!important}.light-themes .el-card.el-card--self{color:#71737d!important;background-color:#fff!important}.light-themes .cache-btn,.light-themes .el-card.el-card--self .el-card__header{color:#71737d!important}.light-themes .cache-btn:hover{color:#42d885!important}.light-themes .refresh-btn{color:#71737d!important}.light-themes .refresh-btn:hover{color:#444!important}.light-themes .page-title{color:#444}.light-themes .page-title .el-breadcrumb .el-breadcrumb__inner{color:#6e6e75!important}.light-themes .page-title .el-breadcrumb .el-breadcrumb__inner:hover{color:#444!important}.light-themes .page-title .el-breadcrumb .el-breadcrumb__item.breadcrumb-name .el-breadcrumb__inner,.light-themes .page-title .el-breadcrumb .el-breadcrumb__item.breadcrumb-name:hover .el-breadcrumb__inner{color:#929299!important}.light-themes .overview-view .card-box .card-title{color:#606266}.light-themes .overview-view .card-item{background-color:#fff!important;border:1px solid #f1f1f1}.light-themes .overview-view .card-item .icon{color:#34c388;border-color:#34c388;background-color:#eaf9f3}.light-themes .overview-view .card-item .desc h3{color:#444}.light-themes .overview-view .card-item .desc p{color:#71737d}.light-themes .status:before{background-color:#a7a7a7!important}.light-themes .status.running{color:#34c388!important}.light-themes .status.stopped.danger:before{background-color:#f56c6c!important}.light-themes .status.running:before{background-color:#34c388!important}.light-themes .el-pagination .el-select .el-input .el-input__inner{border-color:#c0c4cc}.light-themes .el-pagination.is-background{color:#71737d!important}.light-themes .el-pagination.is-background button{background-color:transparent!important}.light-themes .el-pagination.is-background button:hover{color:#42d885}.light-themes .el-pagination.is-background .el-pager .number{color:#71737d!important;background-color:transparent!important}.light-themes .el-pagination.is-background .el-pager .number:hover{color:#42d885!important}.light-themes .el-pagination.is-background .el-pager .number.active{background-color:#42d885!important;color:#fff!important}.light-themes .rule-create .form-block--wrapper{border-bottom:1px solid #dcdfe6}.light-themes .rule-create .sql-tips{border:4px dashed #d8d8d8;color:#71737d}.light-themes .rule-create .sql-tips .title{color:#666}.light-themes .rule-create .sql-tips p{color:#71737d}.light-themes .rule-create .code{background-color:hsla(0,0%,87%,.8)}.light-themes .rule-create .code-sql__editor{border:1px solid #dcdfe6;background-color:hsla(0,0%,87%,.8)}.light-themes .rule-create .code-sql__editor .CodeMirror-gutters{background-color:hsla(0,0%,87%,.8)!important;border-right-color:hsla(0,0%,87%,.8)!important}.light-themes .rule-create .code-sql__editor .CodeMirror-scroll{background-color:#fff}.light-themes .rule-create .code-sql__editor .CodeMirror-line{color:#71737d}.light-themes .rule-create .code-sql__editor .cm-keyword,.light-themes .rule-create .code-sql__editor .cm-operator{color:#a11}.light-themes .rule-create .payload-type{background-color:#dfdfdfcc;border:1px solid #dcdfe6;border-top:none}.light-themes .rule-actions .action-card{background:#dfdfdfcc}.light-themes .rule-actions .action-card .action-footer{border-top:1px solid #dcdfe6}.light-themes .rule-actions .action-card .filed-item .title{color:#6e6e75}.light-themes .left-bar{box-shadow:none}.light-themes .help-view{width:80%}.light-themes .help-view .help-item h3{color:#444}.light-themes .help-view .help-item p{color:#606266}.light-themes .help-view .help-item .follow-link{background:#ececec;color:#606266}.light-themes .help-view .el-divider{background-color:#dcdfe6}.light-themes .el-tabs.normal-tabs>.el-tabs__header .el-tabs__item{background:#fff;border-bottom:1px solid #fff}.light-themes .el-tabs.normal-tabs>.el-tabs__header .el-tabs__item.is-active{background:#fff;border:1px solid #fff;border-left:2px solid;color:#34c388}.light-themes .clients-basic .clients-basic-form .form-subtitle{color:#343434;font-size:14px;margin:24px 0}.light-themes .clients-basic .clients-basic-form .el-form-item__content{color:#343434}.light-themes .custom-pagination a{border:1px solid #e6e6e6}.light-themes .monaco-container{height:100%;border:1px solid #dcdfe6}.light-themes .jwt-payload-desc{background:hsla(0,0%,87%,.8)}.light-themes .clients-view .card-subtitle{color:#6e6e75}.light-themes .topic-metrics .message-card{color:#fff}.light-themes .topic-metrics .message-card.in{background:linear-gradient(90deg,#3e3ab4,#4c5ae0)}.light-themes .topic-metrics .message-card.out{background:linear-gradient(90deg,#0c7cd1,#19bcc2)}.light-themes .topic-metrics .message-card.drop{background:linear-gradient(90deg,#00ac70,#34c388)}.light-themes .alarms-view .table-title{color:#444}.el-form--label-top .el-form-item__label{padding:0}.el-table:not(.el-table--public){border-radius:4px}.el-table:not(.el-table--public).el-table--small{font-size:14px}.el-table:not(.el-table--public):before{height:0}.el-table:not(.el-table--public).el-table--border:after,.el-table:not(.el-table--public).el-table--group:after{width:0}.el-table:not(.el-table--public) td,.el-table:not(.el-table--public) th{border-right:none}.el-table:not(.el-table--public) th{border-bottom:none}.el-table__fixed-right:before,.el-table__fixed:before{display:none}.el-checkbox .el-checkbox__inner{width:18px;height:18px;border-radius:4px}.el-checkbox .el-checkbox__inner:after{border-left:0;border-top:0;height:8px;left:5px;position:absolute;top:2px;width:4px}.el-select.el-select--small .el-input__inner{height:40px!important}.el-select.select-radius{width:240px}.el-select.select-radius .el-input__inner{border-radius:40px}.el-select-dropdown{min-height:40px}.el-input.input-radius,.el-textarea.input-radius{width:240px}.el-input.input-radius .el-input__inner,.el-textarea.input-radius .el-input__inner{border-radius:40px}.el-input .el-input__suffix,.el-textarea .el-input__suffix{width:30px;font-size:16px;cursor:pointer}.el-input .el-input__suffix .el-input__suffix-inner,.el-textarea .el-input__suffix .el-input__suffix-inner{font-weight:bolder}.el-button.el-button--mini{padding:4px}.el-button.is-plain{font-weight:400!important}.el-card{border:none!important;font-size:16px;font-weight:600}.el-card .el-card__header{border-bottom:1px solid #f1f1f1}.el-card.el-card--self{font-weight:400}.el-card.el-card--self .el-card__header{border-bottom:none!important}.cache-btn{cursor:pointer;font-weight:500}.el-switch__input:focus~.el-switch__core{outline:none!important}.el-dialog .el-dialog__header{border-bottom:1px solid #f1f1f1!important}.el-loading-mask{opacity:.8}.el-tabs.normal-tabs{margin-top:24px}.el-tabs.normal-tabs>.el-tabs__header{border-bottom:0;margin-bottom:0}.el-tabs.normal-tabs>.el-tabs__header .el-tabs__nav{border:none}.el-tabs.normal-tabs>.el-tabs__header .el-tabs__item{min-width:120px;text-align:center;background:#1d1e24;border-radius:4px 4px 0 0;margin-right:8px;border:1px solid transparent;border-bottom:1px solid #1f1d1d;line-height:38px;color:#adafb4;transition:all .5s;font-size:16px}.el-tabs.normal-tabs>.el-tabs__header .el-tabs__item.is-active{background:#1f1d1d;border:1px solid #1f1d1d;border-left:2px solid;color:#34c388}.el-dropdown{cursor:pointer}.el-pagination{float:none!important}#app,body,html{height:100%;margin:0}@font-face{font-family:Roboto;font-style:normal;font-weight:300;src:local("Roboto Light"),local("Roboto-Light"),url(/static/fonts/Roboto-Light.woff) format("woff"),url(/static/fonts/Roboto-Light.ttf) format("truetype")}body{font-family:Roboto,Helvetica Neue,Helvetica,Work sans,Arial,sans-serif;-webkit-font-smoothing:subpixel-antialiased;font-size:14px;font-weight:400}body #nprogress .bar{background:#42d885}body #nprogress .peg{box-shadow:0 0 10px #42d885,0 0 5px #42d885}*{box-sizing:border-box}a{color:#42d885;text-decoration:none!important}.clear-fix{clear:both}::-ms-clear,::-ms-reveal{display:none}.page-title{font-size:24px;text-transform:capitalize;padding:30px 0 10px!important}.page-title .el-select{float:right}.page-title .el-select input{height:40px!important}.page-title .el-breadcrumb{margin-top:2px;font-size:24px}.page-title .el-breadcrumb .uppercase{text-transform:Capitalize}.operation-area{margin-top:20px}.operation-area .el-button.el-button--text{padding-right:20px}.cache-btn,.confirm-btn{min-width:80px}input{line-height:normal!important}.empty-content{width:100%;height:200px;line-height:200px;margin-top:-20px;text-align:center}.desc--text{font-size:14px;font-weight:400;line-height:1.4}.el-pagination{margin:20px 0;float:right}.el-card.thin__body .el-card__body{padding:0 20px;font-size:16px}.el-card .card-header--like{padding:18px 0;color:#6e6e75}.status:before{content:"";display:inline-block;height:8px;width:8px;margin-right:3px;border-radius:4px}.btn{cursor:pointer;color:#34c388}.a-line:hover{text-decoration:underline}.blank{max-width:500px;width:100%;margin:100px auto;text-align:center;height:200px}.blank .icon{width:80px;height:80px;margin:4px auto;border:2px solid #42d885;border-radius:50%;font-size:36px;color:#42d885;line-height:80px}.guide-doc{text-align:left}.emq-link{cursor:pointer;color:#42d885}.dialog-preview .option-item{margin:0 auto;padding:6px;min-height:32px;line-height:32px;clear:both}.dialog-preview .option-item .option-title{width:48%;float:left;color:#888}.dialog-preview .option-item .option-value{width:48%;float:left;color:#000}.dialog-preview .option-item .option-all{clear:both;width:100%}.dialog-preview .option-item .option-all .option-item{padding:6px 0}.status-circle{width:8px;height:8px;display:inline-block;border-radius:50%;margin-right:5px}.connected{color:#34c388}.connected.status-circle{background:#34c388}.disconnected{color:#ff6d6d}.disconnected.status-circle{background:#ff6d6d}.center-align{text-align:center}.data-table .cell .el-input__inner{border-color:transparent}.data-table .cell .el-input__inner:focus{border-color:#dcdfe6}.data-table .data-input{position:relative}.data-table .data-input .el-input-group__append{background-color:#fff;border:none;font-size:12px;color:#ff6d6d}.data-table .oper-icon{width:40px;height:26px;line-height:28px;visibility:hidden;cursor:pointer;text-align:center}.data-table td{padding:2px 0!important}.data-table .cell{padding:0 4px}.data-table .el-table__row.hover-row .el-icon-close{visibility:visible}.data-table .el-input__inner{border-radius:0}.data-table .el-input.is-error .el-input__inner{border-width:2px;border-color:#ff6d6d}.data-table .data-value,.data-table.disable .el-input{display:none}.data-table.disable .data-value{display:inline-block}.data-table .value-column .cell{display:-ms-flexbox;display:flex}.data-table .value-column .cell .data-input{-ms-flex:1;flex:1}.data-table th.is-leaf{font-weight:400} \ No newline at end of file diff --git a/apps/emqx_dashboard/priv/www/static/css/font-awesome.min.css b/apps/emqx_dashboard/priv/www/static/css/font-awesome.min.css new file mode 100644 index 000000000..540440ce8 --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} diff --git a/apps/emqx_dashboard/priv/www/static/css/iconfont.css b/apps/emqx_dashboard/priv/www/static/css/iconfont.css new file mode 100644 index 000000000..3b6afc896 --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/css/iconfont.css @@ -0,0 +1,1237 @@ +@font-face {font-family: "iconfont"; + src: url('iconfont.eot?t=1567498326614'); /* IE9 */ + src: url('iconfont.eot?t=1567498326614#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAIAcAAsAAAABIDAAAH/IAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgClaAqD4lyC+0UBNgIkA4lIC4RmAAQgBYRtB6B1G2jpN8TbR6K4HcA7n5UfNypqUuBlZmWd5Kyg7P//Py+pjKFpgLSATHHK7j4jwcNNrbaF6ejGN2/NMUbDC/sIaZg4YmqaHd43nD0Q7+0K5u6Qz6hUCU6r6J3juhOjwEWFjxOHmFTSgZklynmL7sp+cmX4FP+HB2ISVerX/sv87JVnJL+U/xRbImP64hXbum/byvtSqdtjIQp8nJxJP/ZAhSxwnAehIycP/O/vv33uuQ/fIycySX0SmZhArP5nSpkIJRlneN5tvQ+oiPBZTlABx8ycgw+uhWOXkGNWrpWClgPLXJWoKWhD256zMpNsLCtrX0vu6uquuytbEzAIDit7Il8it5i0T1wAQIXLzV5PvhpQCiX5yc2XrF+tJBc34IvJvbuIfxFhgRW6LN06dhdRIAPb7R0J+kcP6vVP45ss+pKf4gFR7VPnNx6vFD1lZT/JkDjosD8eAJbHRcNFfb6lmu+xTgxJ0aH6RedAV7+PqWhuQBLafaQESOJiGAjylrOUhFhppjDlbTcTRAjbQFlh5w7o7Xj/OELlYdvO3mYVUg/ds537SSIhJZAAvOtomwez/ILoO/jES1hITT2VRcCxOfq8kghlGr+JJ7GMHAzxmzCoxmIxW+IH1VAvrOyAeKKO4P/qrP6X7OS9L8mKF6jbotu96q4p586yAkOevJkMwbIaq7Xa4QWCY2oD872b/WeAbs+ZSQI9p8AVt3avya6PUFjBWq46P+Q3/I4AQVbAa+t+TZLwTKZyq5IveV1VBFgx8FX1+p+Sb1L7Q3wN6XSDzzhkVIGWe9pPuflNrpNdfzMT2fWQIOtyuHnAq4FS9bkk1+RaAyoGRGry6bqczOr308T+6FZ4IMns7t4rgQQUJrrodG0rVGd29woY6vybqtViBMnxArUhVfLbpw3RoWhkX0xF43dNNT8MgD9//oAzA4AcDEAhkLIAkFoAQ8oIpD0AufaAov1AivaC3iRvlDZSm+KAIG2ClLwgKXlFhzvKUb4c8utTaK9srr13RdVcUfaHDDmVA1jWMqwVYSQRShK0KXoOmf+NjZQiRYKUDvkZc/8f2Bp3WxnlQkU9AuIRsXrvz6EyNEMQ2v9tGdmQcft/mcvn7Y7JtehtBwpIkBky/k82tD8yZ9+AtvtaD+MTAQEnYypCAoEkAlr/ODUBldpZBRr1C4sFZbm4o4Aw7y97HShPmvzm4KAsKC5OmIewt4Jytm2LYc/43g9vmhOGTCGKTsbM9c8Aw56D1hJ67QOaHEdK/D8dRzMiugPyJu1cdsfN0Jt0H7Ry9TetPE6MAZgwWj0HtWn1rf41pB4Px4aG7dZUmE0R2e+E66puCk//fbTSzlPHtGV1q5oy8VxtUl1/49btbDFz94pmWK5v/vd1tQ4teuXGCD0YkJ+fl1hu+84zXunsNZhwES7DXXgKn0s/67PJZpf/Z3GjTzD9rUyuUKrUGq2hUXEYm2SHD1o3fLP8FZQ2lu1wutweLwQjKIfL4wuEIgmGEyRFMzIQHq/PHwiGwpFoLJ5IcrzQ29c/MDg0PDIKgBCMoBhOkBTNsBwviJKiarphWrbjer4AyrgAqVZtrPMhplwqbvtxNsRO58v1dgdACEZQDCdIimZYjhdESVZUTTdMy3Zczw/CKE7SLC/Kqm7arh/GaV7WbT8ez9f78/39IRhBMZwgKZphOV4QJVlRNd0wbcf1/CCM4iTN8qKs6qbt+mGc5mXd9uO87uf9QKcmBZlcoVSpNVraOnTq0q1HLwQjPL5AKBJLpBhOkBRdB4rruQCK7u8GekyuV/pUcMKIqknpULXgY1BdKR2rG6VTdad0rh6ULtWT0rV6UbpVb0r36kPpUdmUnhWh9KocGFa5lN7VN0+fP2ijhrQ8e8dRmjQ4QjsNDZRmjW5QNBXCelkH7SUNOkg6dJQM6CSZ0FmyoItkQ1fJgW6SC90lD3pIPvSU9dArG6B3NkKfFEDfFEK/FEH/FMOAlMDAlMKglMHglMOQVMDQVMLwVMHIVMOo1MDobIIxEcHYtMC4iGF8WmFCtsLEbINJaYPJ2Q5T0g5T0wHT0gnT0wUz0g0zswNmZSfMzi6Ykx6YGwnMl16YP32wQPphweyGhTIAC2cQFokUFo0MFssQLJ5hWCJ7YMnshaWyD5bOflgmM7BsjsNyOQHL5zqskCVYMTdhpTyAlfM7rJKHsGqWYbUoYPX8CWvkGayZ57BW/oK18wLWyUtYN29g/azABnkLG+YdbJT3sHG+wyb5AZvmJ2yWX7B5lLCFALYUgq1EwdaqwjaqwbZiYTvVYXtxsIMasKN42EkC7CwMu0iEXSXBblJhdzVhD7VgT7VhL3Vgb3VhH/VgX2mwn3TYX304QAM4UEM4SAYcLBMOkQWHagSHaQyHawJHaApHagZHaQ5HawHHaAnHagXHuRqO1xZO0A5O1AFO0hFO1glO0RlO1QVO0xVO1w3O0B3O1APO0hPO1gvO0RvO1QfOkw3ni8AFcuBCuXCRvnCxfnCJ/nCpAXCZgXC5QXCFwXClIXCVoXC1YXCN4XCtEXCdkXC9PLjBKLjRaLjJGLjZWLjFOLjVeLjNBLjdRLjDJLjTZLjLFLjbNXCPa+FeU+E++XC/AnjAdfCgafCQ6fCwGfCImfCoWfCY2fC4OfCEufCkefAUMB+eBq6HZ4Ab4FngRngOWADPAwvhBWARvAgshpeAJfAysBReAZbBq8ByeA1YAa8DK+ENYBW8CayGt4A18DZwE7wDrIV3gXXwHlAI7wNF8AGwHj4ENsBHwM3wCXALfApshM+ATfA5sBm+ALbAl0AxfAVsha+BW+Eb4Db4FtgG3wG3w/fAdqjADmgAdkIjsAuagN3QDNwBAe4EgbugBdgDrUAJ/ADshR/tg5/sh5/dDb84AL86CL8phd+VwR8OwZ8Ow1/ugb/dC/+4D/51P/znCPzvKMzzAAUeJNQE2BYZbIcI2yOHHVCAHVGEnVCCnVGGXVABu6ISdkMV7I5q2AM1sCdqYS/Uwd5oEOyDhsG+aBTsh8bBDdA8uCFaBDfCuSuGEiiFGMQhAUlIQRoy0ND4f+BkcG96UPoGdWbzuuRbxu9uPOG9NXVIv0imO2hOKTf8IEUvwd/fsMKNK0oR2TROFrvRNFt0wAXNAdMeH2pM82FhcMt0IulTsVTCaPDBHHRDwiBILgynBEiwYNoRpF7pnCCh60eoeFgeyAnfsQDaCww1E8IlGlIWkrLgZfI9MFgDgJXRFbjLbXQkwDIZZcDgSLzDMkIB1LQEkzGhR4U0A1wBPcFjs3ggYV0xB0dXBC09YOn9AZTFq9/FMdMrFSzDLM9vQj3y0eBmisKA/Z7s0Qg9wYhJ1IHymA6SfMhyb8S9dtDn05aNqNasp4k8+pI1DP0Fm+ms6PPLlPw928vvjSPr8ThlfEOGTNZleYv8vmXYhpLtVSe3+8MmBC4LPR6oL7kZP/SGTFgDN8sH5h3/GwNzsZ0NI5bFJd+4oo/V9mQk9yobcqYkuWH0cK/AadfDgUxRdEpcXGKoG1dj0i4w6Vwx0bYo+/UmjMPOKchwEMapXdNQFCSBtLlJYSA1X9U7uX9ARNc+NbVt5MAPT+efKGdA/AGSbPaclLYwUFVdxwekfoiTXgGJ5btPk8NzXALIZDNkRTt2LQxgCgvmBODIsP44c8VCwT4B3eWkKKNWMZNX6UHZlMykJRuI1VJ8IekkgawxtPtQ7xtr+nQxcnLy+5btCYyy9BexB23LaxG2iwe+6faBIDZxr73X8YzT3vtrVQY23Gei3rP08kw2T1jG6HWpF4t2ax6G25R7zEqpGTG1y1oC1bhkKIGhiZQUOEnP4mnUNCZnVk3JsdiStie7jDnctdm8y6GxbjZ5HptM5ot5y0IUOK9/FH1SvBgVtxpCiGeU361qtNd4ORdPjXkkYkDoDihCoWQa7kkjawn96XB48lqcROC2NHWqAEGBau9uSG+d+4y/DUUAW/AkVPm2v8X6rNeWL+S2iMoXiRMPJHX95j5rXwO9y96EpzzpaJv8452EAP6UCStieQlHSq3BGRROiCgpY8nzrzRIZTRUjC8m37I1UlhXeDSzMxDa6lYPH4IZY0B/Ovho7eIxBcvErz7SWqZ1QpUJ0ISEzsXw0mc9T5I1V/ao9kKHvE7eMXHhcokSucM0OMODNy1AsC1ZYgZG9V2M23tznTcDVUccQ22jHi9pCvzImtlw1SYpQ1rkSTU1lX3v3a2Gi8+ztlAt4HfUSBYM+cfV2JZkLyShgFItacFqUFpgCeBsIZGwztl3VRy5FsSRqyZaCK5RUnJjjx07+/xYljWhDjnXZhh9juigst+g0HqBzDavkrUo8kVcYI8wDgpd5OAyu6bR4owgocDrL0ME9KAhaSiDTH3dQZPNthjSZnK607S/dL92vsnB20NvCmmRfwSdyAtsP72XzaA/aQOXa3HDdHOzEj42Hy8227xfU/7BxadcSATKSHl/6v+0yLbjkOlZsPgs6KHrI/YhrpgLlAwvEvjX4cktxcfT7ui4puxuMnUlHE2KbVidGon1aLmwh05fhLVBuOlQnpz4xNcMd2sneglEr04MNAlUs/Fgs50qASwrKHCPRBUMZPUK2MFZqLDvVjt3++qQE44kFYnZrqal24VtcTGW6ehGrlcu7KMq5SpimstZaQ86Sk5JGWFL6zbHV+qy7MoB08QuvAvLVxSrCaW9JIDJPFARVcsK8KIgVzjgTpT1RkmzwCTX8qgcZSpOKa3lAFOZ6ZfC7SghmMcuRapZxojIn1WmEpkXMA8C3iA1WEdlmNKXlEpfZCrC2Hqhs41EhAh5ngcKMpSWyJro3aF/CBegQWst4VWeumZ/hmmovA0Q5FKWIiyKgtaKwfFSCp9oRrysVKY3aDySWFDIBgTADtSrKgEO7oBu3zKRGoz15rkY8wKCUpZcVRbQ53hnjgMRUBCGWJYP9UUAdlG7sNf55bWqzJsHtgE6aHrfqBakwSZ4mGBpWp/WjjEx04iHVOEBCUi2SVDRYkWbQHFRKnNqblZIVIxbNeXAq0p5IphJ1AxD4oI5TaEruVDhzXAx1IZKfCd/OEFV7a9rogx4xCHWCdDIH7ChH3R3HKXl6v2w2PuqF/Zr/Q7LmstATT5Owy0xDD5RcyvFUOsSUsdaB8DFVdzLrZqIkc4u9S9G7zjBEr266vxIDgVN+9/kRjW2RPpcWLFSeozQMhOUEm8nUGfYvRb61LZMtDpC+FDLbOEJJ8RJ72xeGuKWVtEU4Ojj9hx2VZDWngVia6NQlpESluR5UhnPhQ2Z491bkGp4UyBzkL/ptcIAe/M4Utow/GClMCK+ArQiUMfAsaXk4n1caW0XpKoXNAjNtVb0ymrNx5Eq76r2/xLkmXrbcblo/0FgkzqXfQ095NDWb24De00Y/1O/dZcP6yjsojAE4X6e8jADD04Qy3eSRSFba3rtRrsjjIAHPcPdEA1+hJVBMWih3uYaiO12JrolDsYM9UmwNtDB0GCHejoQCtJ6d3stOGrwRjtIB6B7b3TyoOfwzrb1+F4u+kpeBBB/4iSB3nQ2KPU7ZZgpMtcpK3Q5bfQIVL2DQeUBpaisXO1VHR90SWDdIFz1rHtGx8fQ4xSaCiiXQ06IjrhBN7ang5o+J1kdxX32+PsdXY66vjiB6N+KYg88H0O9NSpOfWka5lG54WyMuDQXXUtfXqzR8kRB14SPdE5EmEuAkwpyrBkAzLLTOvRGO2NELoeH/H22IJcyZuofRB8TWpRGXEL6nraStXJQlOAqwcoFWEWvcKxQDWMsYVpvH/4DtsG9dNv6h6AUIppjxU0HNQgKWCO77tMe1Xn3kMjdS06UTTPCAVw2jyBksAvfs0c9sK9KYnVlRi3volkTbnfUlpljFt5gnLzQYoqT6/52ksw4+mbdrqOt4Pr2VPDyEuzyZ0SvytRdF4SVJ2yRFBYVmxCwYC5Sn22pzGuwHdGUJnCZnLgpzaRPSrem3Z/7RMUv2DrdACLcR0CiYCSQVnCa2mCxu4u+ZUei09FlTjyZdgGB05VIpOJKiccsOVQZ/uDbnU3bIma4OSrCbZ3bzWBvL3iMyW2Ki1xQbboudg2+pIKHEUVxU2TiKWGgBHBDkyVpMNCa1Pw7AhDD6x4r9FmevHQMwF1co1R2wSrRjpJT+odn1A9wX0vS2w5yNEObB3rq3pGa3HIkqZi9igwCxYWhNvgdSG4dxwJdCD+0ADlOINOsoHQyL7gyeLhyF1kfA72ycOsNYRRnMKk2dBtSerwIPxL51+1qZYOd+X7NbUcm/vP1+reONHwa7G+10n9e03/92/z/xz08ifET7TCc8P9Ae+iI3dojs49xcBQOa7T/5qKOkQ+83Ctv3z08JkoG4a2X7LGQca/PPzrM3CUQ8OC9ChDRfXSLzGlQeB0j9JGBEKevbx86pcEMKLBtpeOySA/wQxH77G2Ai/ffBTAFuxpwcXp1aG9fvkAGM7UY5gJnVLO/ExzWr/Jdsem3VhAxPis9gUctbVWxPlNQ1RmQX2g1dT7/NKMW5p9xlM+YKXJeHFIvpkYAY2JuguC+FVd6VYHBen5GBhO92ehn8n0q5QKJMlOQza1ctknKNF1W1hb5S/IcF6nNW0swOMHDglWKkAgXM5YlV/Gbm2WkzK0VnK8ndzip+o625HmR9doL89XU0b2blgMnO2pB5b0mt9Azgw7cUMyDmm/WAPNMq84yPWNTwzxXbzDSSfk6lfFGm8yJ6GGLUvn1luD0FLNigsli2TbcMZjK1jZgg0gqKRPtJmJfdfKKGqM904qxHOwr3kRuyoSS+7FHKu79dFY6BwJwfvZpQBCdgphzKlRfTpolGQABSxfRHSpFLOqQRzpJzZRHRXXXUS/E7OMgBQqJkAiye0ich5gUTvgtIt+BlJ/mNqpGsrVr7tOs/GoUe4a0jWeeUf2NnfnAyQr3FPamZIMNyKxohny3pnMXR6OjptJtjM3A/zakyv7+cE8E4C8H3ubfbu/+9UzGBfQK/hqtFFV+suWS8qencLFtayakfaRPcFBGQSwuHO0QXWVVT7xGiadCiI+1iUUo+InGj99u/kyV2GxQfaxe0VbxYDEy8cga1nXSqd6QOlkzF6e0yMXIxNQyz0eC3PpctwAF0V9/HN92ceA2+hy5anHtVVgjV+/edDWeYbcfO3U7QfbOK8N3krX02pUN1zKQX7Nn8zVsFt9x/PQdFDNkDu1jBC+tIhqubySYHt2JYX54P6ZseTUFtrWFMnLiUft4e23+3URcGFpklXOD1mcEZJODxoEGQYPOAKqPD07tQzG7E+gLwF/N0TAFI2kbfHlanfS+T0qZZnE+09E9FCDmkZ4gg0Ccf9/2UB0Z1uMMcVoMpOmY05I7tGccUF9MLrDTQz0JgJw5psslGBoQwnGCVJLhNAIy0hTBQb92lR5UAYqmTn7w2J0wJGvSQuZQBQFasAqiahf7WVdDGNpYgjHEbBRFNBAKekJp/DEQYAphSQp1k1RHM5GlRG0LukjBUlpMMhcJTuEMlogL4WPwimYaUE3fKHyQ25qKthjTg+3tVZMjFiyPDJgfUWAcfDRY7Y4UX96gM8kR4jwCU5ySCVuxpCxzsImS59g4KCdXz0qAWtVnKJKNBn1Pjd02tsf5/FEea7bZhVsJE4lJNpmya5OsxxjkoyIXXsejQZNtVImckimYIDJJQ6kBlD3g04KYSzB6s6V2UBxrOXsJB6RKKWliif4mgqWlscbDTRnhEPnAGA1+aNhfvY4wjzcd/HrI+Xqc+JNpemFWYWdd1CFkrzyMR8AdiX7EbX/c5mugJZrohDCVD2qQ4HVHU8x/MZVREmUBA0ssUEpcZQXKFtvklAGJSheleW7JiQBahGxOpdeLAHyld5VkmDUk/Rs1pvJUdgwe1QmS+tVsfBcypYx4EZRgTGbCW73fax6GWoIgGmxEUxzXJtCzfYFiSrbBrGSaFgAdATpL15dJG7TaEU7ITbZdP2iXY+9b2Qt1KGq2+cYjyN7Lt/U7IraxshxeJDZkqfZBpeVnOinAc4JWIQkr3SdCxExJhEqF0e5bk6MIb1Gew+2bOsKQrijoBFidlEsmK5I3dN2hbkN6hBnGtD3vtUhUsfkQZPACf++msZvMXAn7HnpiocgLArTfcL8ey8fO9bOpZJ7t5Yk+lcyqZkJJEHAv0oAE8eFNhaFkvoxpt5IgavS7DgvG7KiUteoECwS4SKpRgGZos9K2AhGMO3XIOaY6DkapV10ENMZJDU56ai4qz37gkfLxw/eoaOhqLmpv2KO8wBUMrBwYOCkt9rVfSGx+96AZ6zgbC9X3KHYCvNVm1bygRqoo6p8AKmVqUlfhEsw+K/M/gH3h3wB94nO55o05lY4HXyTn1lhjzHNXptBoIZGa7wJaVH6bIDvtx8ivlEYLj2PoFMHLhx3+lulyiFMrK7+7azG7sXv+2+fXP5U/s4edEYdkbA0eEwMIJGf+QSwZuCgeZSVYwdbUclCxWSFS50w6QXRDuSieWeLJFPKdjATqy02o3o+dN0COQxRFYizBiU4bNukMGYjI1EDS7HvU0eERNoEZBXomaESI4Q7XgQWL1UAkp60Ib8VbuAKbCIgL+PlUuTvYQpvjwgMza1YRKBhGN6W4TyujopsQEooMIZmEXGYOsUN6GNuzmqvgRtnPaApVNjnE/lctNXQt79teUhEx/5D+kdCgcVi+lT3LL4Zr3hXSUZv87gAxsmefPikjDDb9Dabt+lwN64x5SI08QljiSKv0wAaM1BFqZxTAlmVcCx3G9JaZEIqlh/m68lhVGEaKPjCmt94LoVszbTRxFsu/aCK18lILJs6fw1Q8PASlqPC889PzFan4BX0EB63sErygqTPMMESZgTyVnnCU/ppVUOpMlmXYwqZ5llw3h7M4Q0wkpjlBroOqmLR7QbjymDTQLojNT+hwW5arBfA01EDgDch7sSgzWE1ld9Xlscpx+ZnTN3j5pU6mtfdkxzENGb92m9BztMPkxiAgc5jp8MqyeeI2NFn2dhFd3bkmrYMAr23RP3V1fuG73nfv3WvvuXf2rnueu/uu/L2/qE7ciQZBIeEoTpSdYl/bEHd0ljqgKWQCxSgq6GNLArKb/gxZCpFAMgWUEcQEV1y2EdA00Aan12evBVF0NTNnRaq34QRqImsY1P4UtYyzSUa3hntXavA2N1piRbT83jsLeR4jYZWgC1I7bREEwp4BsgWrZb8mQe05yRrbpTDznQbZhaDhWih4xRECg/155LWmiatlpqx2xDB4qr5ZuVtMzn2kayqTrKFoqtOU11Qayw3L8sBZkAESgXCB8Y/XmfakJJzDQeYW63dKV5JpGZcmYwWFC50fPnpBR2WUkyvFtqLzxh4JAosotwrV4BEzPYOBFYjqugYAsqfM/RpuBzrG+A+wwZ3/i2lA5eO6FwVTl8WMwT5I6fyHevqm+Bc8q5HTSYDOk9HaqrUG5Xi2qkXoBiGODDFZUs52RV+Riplzo4lT0jAH5BthTPk8VG/hjKaZEPAOdkhvgkuErqKiDwRiRethFqbnBySDszTlhTJYpmgZood8Cj43coVBhOj6ba5b0HvqP28mM4eTw8VkzS7dPX1Mrvbe+8GMcIJ0i+/v+Trupv8fGdlrZvJ4G7UYPUemjxMZKABlvUSyp4nVvnI9igK8WZ/GJfqnGq6UmV/rSD1ylmnGvtn9L25GGdrCcK96ADkaPOMNLvuX2bCQq+uDqlGNQGBCPv0e3k+7vLlOOvJm0vsPn3Qwx8mtcx6yh7/BD2uwlsI2lT1uxr3mjnDa1iXgCA8LoubBvchQDQUmHHqR6WzQwKfhNjIwxTJCyfXAQs2IlhXN2UH1OhrGpP/c1F4OI7meS4iJFmF9XBM/3GVkwtBgoPNNwFl8bIsXdE8J0N50lnbIQR9FcRZW4lmjTUt6kdvf5NgDDo4PvCGIE5No0LO0bgbw8AME8Zjfvfk3Z4/H3Rex1XXCCmDdQ5xg4RvVIBNw1r4Y+BILPozJ7DS0fffgioUqO7DcqOydGVf2FFS2aSUHtGXJ4w83tdJevhNsvd4mwZVWrfrMqXgp1J3S3raFfCI9RbwONlU5VxH7m1/UVXFxFFyFFSVUgWrFFlWHl3n0R068VfZ1blWRVwxsUzbyV/RWebi62h1tA3YSem2pzLVsmnBApiHIS831yfpq8g3yXNGrOnve4Lxuz22tgfZwgC2Rg23LlX5nj18NWQoqv2oK6LhNHgLvIvPm/tSUnm6ogc5x3wl4SVfiFkv4qFYi4ZAvalat8T8Br/9Bm0J4MMbGwcP4j8bq6AD3E10VL7Xq3/ziuv3a0VKYRly7/eOIVuz1/tXoGAcvxRwHlVEjHHFg2Xt9Vv4Ofz1U30bAKfiTIOuwGB1957XjUAlcfTs7FfgTluwMT34vcoRNe8PnY08JUD1aa34WLo30JC24PHATb7TECZ6hN803OAgx3cX6wcZjfISEXumN5utbSXild3pPjfPNRyf2lLCQQlw05W+OXM5EhhmlPJiB9ptjpsyVQSjbUQMfaHALzanPgdQ5x/CAgq2NcEMQ5EsW3iUoW9Ks3cPrgJTbPS58YO8yJ1V2md2yS5bkFl8NKMmhaaSJUfYOiDdR6RtwIIpzwBEYzeg3ZkSwhyL1TDAdIi1PJTSOugL+TbgwEzcRjBEOMevfdyexZFZxIdZRGQVY9n7ybmD+7Mf4WOyR9+BZFkxMeZM2AE28o9j5cMQQeNvfH2ZAAYZ+YfBxHA0ISvH8WoNwJ/FkJ4LtwDiTBNbokRVFV4Bz4xs7BrSY1v00TU5ZJ6AFYpwveqUrDP14bXoDKatBTqXe4lSX1rsqmpeyvgdtT696BuIu8NjObloFtvh3KVMZg7eC+LVMGZBFEOawhRAqmbALtofgqoX0UA6bpg7Q1x05foBId2vRHjQeg7XZDy++f/XT90eouqAu/QGWibvkb5njEjoRM4Rh+5AIIrtZ+inbTriBnU4z2LpUutsrd26HiUXRqql40azWQW28t6vRDiyjvb3xbO8dFRmHvEEjAeIk3BQjtgd+rqlaRq2dPikFbVMq8FqELK1A58F5SUsvgIgXQme5gKtBPdv15PuBxPxATxVG9vxCITOXZHGUY3coGAfeVWIv2gv/pVmLvCidklAr2LdWqaULq/DJsvtOkWX9J2BeAteCkV/wnMgZRxPsJPuoij77lil9UJci9rJjDTfZZ4Bkn2K9ewg0CGcoAH2zZ2dFapHKN6gHuU7td2X6me1SIto63tKjjr8ktYEl4th4oaf2rB/1pzdN/KpmJa/dUhvsSDe9UsP6sc+Yl918vvl6iPKyySOQZg1DKpENRYDkqZDD9mYJdVlzdLesQkZ1nRnNOdMpPEpIqHlcVlXOaEEVNnf/raPK660SXFZaij2CXepHODiBQNbGJnRoV0jRPhrVkJCUZ7W86k9AAviWh9mfkwMiZipM5faM7UXfxG22hw3jT3R0+egq0At6sMewIgs0gXoV0VFNbLLTQZk6P7XNdK23WC9b/DpbyGvsK2XaTD9WipFt4UoBZkTdlSShTTBkUn6e9IjJ1A8VNnSu+lT0Nuiy+qqKtw6hjyWS2ahn4fSBp+QvU5mdNjdnPLBEJBz0/Gj+TczkgOyTAgMngXkswAvizp8WSqU7CA5wk2LvyrmqiVi/ZiulHo4u56S+gF6gjcttZXn3NFXwYN/Di9fM8Z9hKhka2IhIDu/sPxxnop3ceoq2coTRqac27fHqkIJZPVhPo1vnOTRLo7rA4C+eC/TQrAC87cvapbPHEaRCziwQ635Lj4YhGZ8UTHNRu+RphYkv48NIMKNFP91zm1sQKwlE4//Pwaa7978UHGXP0zLnbxAyEILfElhTYAE7DturHoSp5DwLFduiA5jmKtwc0tJWq1/A8Vhy0eR6Ti+VS9wNogFoijRh2gVbsdDXfhUv056GYVN6YLCvPpEpjHky+Cwqr2Fv3C6l4sWQBihViHJXdK+3oyWjFlRxbf1i6FhqJZ0OZ92245lno0eWOlkUYlHLN8RezEGeuTwpP7WhRXevhXr7q6rFGPm9sqc8vW5oqa7i6MVUvVUbBunwWmX13oS+zWPTw0pRCF3cQyK4fQfu9+FKjlnPadduRcQgkaUU8GDL9HytLPnrbjbKrxKR7OvYSiJHJqvZpRwv9xMaB9EWdJC1qEAwIbCB9itOE4K84aMc8LnrNXN68hHzhWSp2gzaCYlO5hPMSWq5RjhXRw7GG+z8Qv7+KjDxUnF+QK6MGnULZQsN4b+Eoi3SIDlwU9zwJrqWD3upA7fGfvvy5snIRw9ZDNME03lUSgjBTSocJYln8pyo7b+smE2HiicR6C7QXJzQdMhpyJIygZlMUA5WGx5k8aIpAq7Sntd4u+4jSEtTkybTX46gwt7KkdGScMtBbA7sK86t4ajl5QYpvxW5Ww8v7h6n/E2HFv6+Nb9Ws++/u3iwP5+8uef+cXyFoVcrt62GdowqQpuWI1eo1c0D8BR0A+bItIJEf6fl/P9q8kcXBK9eYyJ7fJO6STgj/lhF/Nqm35PpXGhMlsgT4fvvqR+NArUSxCiW6BmMegH/CPbYztIaMx62x7Ixp6dvBeWG+vVgpWmvlSx4zUpS9P6qrUo0rWv8F9/Fo6urXpuNQo3DzMCu8zbykBqotUYpzoprVYLOm/rwELe6BFE8Dhg4K2gIQ58W5SUBNeKSyKEs0CTIapTTJntVpRZszxtw+/NJHceHl9l+jJk6hTQyCERLD86z8PBij+N/oy8hoz32WjWucGntxuBKIsvUVQ4wLAINFBcokQqolE8ccUJ6WzPKo0jquhUCrSWI8OZNgncd2C8tIvogPXpa6K4Z6xYF9jPJmE4PRCwFkEi5gVdnzG2sxKAoVSIPWwUeG+e91F+73xrug0q7ZDEPYOYklsgQv887TYnFNnQlNK8Cve/7Q8fBeQH3xOSgv3lRYbNq0o8JIQxvHSP4C4YXts5/yrYMtXgf+3JBNiDh5tqZN6979aHHr3vjxscffaLwxq1/fDc+uJ4Y2FleQrKI3YrY0WnZuFTqR9DOmTKPubhp9DD76FKNCvy0Z07hyaaFkzUnoZFotf9TZlCNs2917Z+j5iLfcEtSyXQKo72Is0YhD9qrzwgNiatM0sahJmJufR1D9i4udeLkWOO+MSU3Iz9GVHBwFbM+6kVbLHGAIV97nkFtOzwcGxva24cGf9Sr04lhbcujyqjWO3FfPblEBm/6Mx67P5rjsPx9VWJcorspDG2ojaYgespXaUcN0dWcodJ0N2jWuqSEkhlCYubBkiQn5WcuLB/R//1rRja8lfa6E7Myg4yId1QjfWGs8afPgnCjn9pajy8vEb02HDiyFJgReUpluKhD4YhaejwupkLtCpdwr/FEZT9+nYrohhELsAlGIl1PqbSI2jVox62eHlQBKYG2LRul1q9hEzOnV0z9L2KEO65lasd5EOtAzMuiAwOnlOgpMuX+DP8dI64SlxMYl3k7G+C11Gj4hM1NfpCZd6qRwPDExIN2Hw1+da/y9/Jghh7lE1rT8nUxv1dtbUqcqvKkaFLXVsM8GYMeNUXwHIvMWfJI8cB4ec6NnIp8QraowKqFlF4X1m33CJI5rbUtlWu4GnqDx/DbgMn7u7cvt3d57+4Jof9xtGf/8ZuXB44G3ha3vm87Zqs8iLeIk2hHYJBNRzwnVhZwlaMxFJETdNy5nhMvKIFKJuayt1tCM/pmbBGlzTVpGiFWZ2IRW0dSf46MCPuZ4IM6AojJFwYrRaNkbNbgcgwBNdOK+QVTqYSoVLIK63kq5m6EsohRk4GcQn0SJNn3ACCwkTAj/SlB6OEo0DBnhBAhulWRNATJdzFSHnTu3Ht2p88pJR3XUq6tXyANb+B6Z1COQUq02ErEXL88Af8fFs7Juv7nT30NS3nI+wT7mSpbETFUerh4kk1cZK5vgaXFcsFvTwX+8+DAA8PNcQ70LXVlOHD0bFGaH/2SfKZs6BvS5R4b7BNopNCNW9jl0CDLeuUn7/fomqoRaWZY+z0CF21D6mBKXi6x1m0pn3/q6My0D7zCPZYD5ZK3eju0C+TcQq7TGvpQD80vSSJG4uqrIns4PIOM9Ly2aLxh841nEHQVNowjODBqKevWoxxS/dYMSH1U8aqUHq4aogCkz8SHircKiWonS4h59hwERYx9mB90IL2MzAa7jihHJWxJwsu7wZGocmEhX1xr90ma+kf8+8OLyvlftcFPpks2PLQoF8c2fWzgFNkHen7S5zQPbce563O/HoRBsrU+r6KK5DHeZpTButxAjK7Yedwp7QRkCBHBnjhmsKfi2Z7HE9lxtafZk72XJkQBhBS14C+w9j7XL1mnZB4BKxs8pk/Veuh8O4KIvGamRSdCD3JBHRpkQMKcZRLRIc3NYcM2y0njJ3owVgPvFWxqAaXQ8+TFpxUWSxmzyb4MLGtSzVo17+JEGu0wvefgPZlF4pUOeBFONGR6BXn6AbxEilytbvokUK7Q5wGr0wzxaaDh02F9KbokKmcqiyY4kjnUzA0xRJgDj+xVZjCuS3cq7wrBZMbGXutZZjSnzvgA2ayGtHP6Kz8ovugFS15fQUl2gC14O5XvYBRIWt8z1lvh/XSeNZN8p0VX8awsBjaOwMBRkwMYyugE5eitAS0AAbQkg+TMOih/sKgProz0iS32EGDhQQWETtwOvwZN6NUc7NWQ9naTSg9q0K/Y5wkR9d26GDIjQUcYh4KAI/bL2501+I2uHcL58eMBkZqiSwvrxXXnIim/3mZ/vuIGUw47w2QGCVac58bcn9dal4nIK/sWXAtcg2tyQ2GzVEKDkbAQB7z6kWUsGEIGkpflllk21dWYHUrCxhISuiWmuSw1sZedA6Vdae98VI7uFB+gOZpyydd4f1Wjmuj6eFI3lwkAS7qPa7blfhQi4KyW+NREY4Ez1Au681vuuAFygWf2HpOjO8Lj0b01+MWKnOYx+4rC3TBgwpQaqo/UBwESY/ESzT/afgaowHu1iteIAvv+aK4whthy0xFNIekYVRG6bjD2UKSkQcKqWiWjZUEwEuiHqr3scDifoBYsdhFdHHZ2B2j4huTykx/9ce71f/UXh46wxArzBMy4cHncJA2sAAQGC1KcpjpRDrQOMkgu6RhM1lmR+qeYSen7RiS1jFWnBZvoo/I9OnkQM9DHUKcDDFQRtQ5TknLSJ3ezQV/Dkio8k+AAJwbfcnB/T2/EXLrJF30vyH1hvJmNCR7Wq5xhVWPPFfGDsi14rhlYmVKb81uKlikJzfSC6TUcS9ujl43He6XcxnGQ/KR1Fq7h34hs1KDvFqyso4XNTLd/NtHNeMt74X9RjBL/ZIGHym9g83CMdDIRlsAmGuId1DTS/TsREmw6yrT1AQuIiSDJRqSpM1QtonApP88oy2HCKRX4mH+HAH/fRuo96MErnk/g6yF1Zb1FiV5T4caf2XqNBh1LgfiihWYXb0IUA0q/RTpcnOroVfoRKkxAMcHIfR2HdhauJVKolcHG5EnMKKS9I78+mzytqW6SlhAMVDZ4DeNUE/wIpd4tMxIkFroF1jy0slnwtjAOi44QtG2I4bGp0dQqscS+haX1zaTmmmAHJ27RzkJ9HPIe7qxQ7N76/44zvrslUv0S0KrtHsl1kyX7EPPVFf+ph7cMIWVuxrA/WZiLwLHVxZoXtOAkj83mb/cV/Xx9YSaPi9muI82jp3QQI5mOO6cSAvfxXpojWRLB54lTVDCO2e3CH6B6o7lnPCjXl6F8WwhY9+f0PR69PIZTjQRkiDVBDOjLCgJZR5aS/k0ETM1Oy9kHWqQLvQgcamXIWiSaikEDPvCR4qjdoYJMRM0gThRIKmVvPSGaKk3R50gRU9Xg6YnrtvmjKStxbt+fPi7TSSR9XJdw40EB27giU/HkzbbjsVlx5EnrXNnwn4Srp/9i5pq27CMvD7sWS6AVCjY+9qi0E0RFlnScj1965Eo86S6GU7z50RqTuRudi0iox4azvG8pMAfkrBhW4YH4FJy0BiZOGKY/5xm6EKU6lfmlhRAKdKfGFtfX/oxvnZbwYr5P/Qun6eblsedm155eD4G4f2R1zZw50wV/jnY9d2pdF9052DV78uRUzPKyqqGttGK+n5cVTNCr+qNDBxSdbiP3axj2rNrofQ+clzZdJ8TsSh+l/dmqhUlLmuq+0l1y+ONVaRBFesxUOEqsxdwDCDNbQMM189kgoUcm+fb3F9E1rMPd2cFwHki111VJaT4Vq1p5UJJx1al9b5srit4c/vHG6hJv/Rhdp/uZx6Xnx3DspVlCmzrV2wxQqwTu3dAuH3Qa7EMjtiGQ1OMfaDEim34RsB1Nvsiy5ntmk/iFtiMgwBsQcYSGEZxijZvg1KtxSczOY91JeBGTzZw3fshUvyuur4/SBSB0AyCFR8zk7QJDLO8UYUoM0dBe0CQSIPXuk9WQCWaPnPoluInbPbS2IkW7aeYIXeYMxjo2kikbcsWB56oFre3KERFZp0mm1DU8k5ThFUik7eBqMGgqtMpe6OfeLq0yOqQB1Sta1prjuIFCyD+EKSoVAJNG5bX8p0QrOaFtbGXuqxCAISxnpXTWqpbLfUeLSeD1ZPOvE8muaiww0TsB7f2LWC6bLgbsekM0hEVIlu+eGSX1eJqD2XKR6NU/Q94zCf3/EhZ1Tz9oAyWOGIqdD8GgCThva5vwZd2PX/ZngAn+px7eCpjyb2yH1wRuNfrD4zD56wdud/FT8fL1SQn/c7r+W6kk0F/pTvkLPNsh1wuj85OgadvYsEv991uPoBEBNFjHCrXALlSZUoX+oQD+nI/gsRAeQfFAsmUItjNwYVMLB9nDCTzEAvfGHeesoB/qpUrc1D/YL5gigOYmdvRCZBPFWxjdStjmePjJvp3YrzYPNGyTKssteLog7qbKoe1ckAu/oyJjeYpJ4WyLHpI8ZeSjJGymVJxHvVfoIRkHyIDs45ZKx441fQVbKJN3L0vkdK/WIZLPFxj5bB4v5Glhgc0XCBB17K2hutMimDuJDSusVvUhtp8t/JgbX8KDJ7UP8BJXriRnWN8xEl3F8z/IUQzlAfdDtob78B7xRlR7f1vlUzTwg9Nt55UVM5xrV2s5hsN0ZK7zFMEQR/NeAg9bzoSvvheT5s+1Vqx0QIOjfMkQ9Gd817rtcG31FQeD2bp1cZkA0bfLGfUxKmf58SafTvpTl1g5a4dnO86wnJoTvMnLKl/Ih2baT+JbIIGkOtRtcrIwSKTkEoYKREVUQEXIfhhfeTF+GQd5lPmvWP447vAeaoetFY48S9/CsQINg5wha704FDSYfm6erKyA5i4yMXUhHCefay0xYRPQSM/NP+2+kydRVo9iygiXwrS4RtevmLFe3hNqOZcxMUFfJk1odLqo19L444lYi4HEEFSLt7K5TCSTy2qHF5J0FhaE5T7OgY3PoLt/NNMd7SjLUzgkmRzcql1MhBBWqDSl3wE9XhCI15it3PewydEBrWt3Jcvrr4A0IBhogIG77geume69ZHH8jj7AASIk/W/icfC62cgN69n7iRG4x/XHrA9dNd932fLAEbTosMXoJZP9V6wOHoUgQkF8Qt+0ZvdtBVyKBuiZdb17VzNtx95WNifVP3NiINFphmx4zFn8yefFyR/Hxazs88h55zeZre79vmvDeretfXwiyzKZb6K8E+aySsAc5vsPjHxznQdQO8nyTogCHmgRIorDItg4kUwuslwE4q1fz4P0RWSOOETOEMrkzG1tgUqMtSheeTd+/qDM0cXRVXPeYxmPUSKyofhYUrMJXRWtdURagUE8a15QkMohio1/1ZmVBO/fhbdTYviNt3QbWUaT/FTNnM8foqVRNylIdZqsOkMK7GYpLlZXoOpMR9Nb1/kbDAPGN1OYrE+GTfucuMm1qM8EpuhTYBNLWYoTJ88RQgk2LSBAH3Li4lWCgnhB+/BnafAx8hLBiLBEjmv989Ou9YfQu6t1lG1wFgeRtrKEirKMwDAv9hdMhmWawl/Gfn0r1DPYuL8NmVOkHs5eurUQuKIk7JPrHdfIccNW2nZa/wb25BqHNUXZ0G/Zv9XsNvTPV/eXL0P/B9r+zp10nczmo6Og/HxIvqgOkMUQvdPgSQuAJgDNz4DJMFAATF0DJK6RZ4JEQADbBvTJncIk3BlTsbycQmDpswgpx7jZV8HuwZ7eOwvY/bHBPZpNBpbfp5NI6XY/fjd9X3P//SeblJ5OQt8YcF/+/fKWiP5AfOcTPXzgiTLNshP5gdF70hmI70/QTHBb07b9JzGfP2NOJvsN/3xs2bCY6dEa3mLqvi2VD915tAl1aTRZ7HJbnGMPxGNO7rXMEkyG89tSo3lMpioveqGkFsMylKopilUld4hFM4+U8synt9yho2QwGQx9G0yabrqYvV+aPFV2PyebOJ1zr3zqa1v5CXmq6ULO/p/tTpS3f0DOqEjGKbujWPHOhK8c0hgp6MtyJeCxxLBsvnOoTeOMCCbDGkNgM6IzbRp3Dlk1N46krzVGOb41LpnorsSp3den4IpjNo6if1+tEnfPP9NQup8Znd/2NIWnDGAFBhqTg5JCcFmEkCKKwwJsnEiOnMC4+67har4wUTNR2a0NfIu8qPV8W/MsVlvtb2h1apFr1eHGZtiPWtnjfFzsEpngJ7gIigeITqRPJCeiB9BNIRdvwlV2eHRU4j6h+zRh9+fovLSco4tGlxLIbcMRG1K8YDLwgAQCpJLBVG7g+Xqv+MDsyIDCMzyePsorjO7nnvEmQot+s2+69jlQnzpMbqjY4JSD/20FbwZ+z2GH+YTsWGe4o2a1hKBSXC7MAhmgpFq1kHFpVc5mNbupUGNO3NbfgRlecQZyPhMR/Hwhpp0d78vfkrR+wR6/edfOalAHJH24FtRQvmR81Rqv8DVF5PpoNdU0Sfh1nRKwOgZFtjPTuenKGsCPJpGmNSXHxomYANdwqUY5nCN07mK7sTWN/s9uVIFDy9LkAVT89DSBTCfjQd3R2UJAoNDJhAR//qSGuiUdRuq9E4evupcs+sxI9MRA3NlzdMOz7j/ObseEPpoqKe8oVgNBkYa5umWTJV9DVY4ApVBe7pHxy10bFUUPDstt2e8l9Gfx699j8fZ2qeOFhWAY/eABenh9cw7/JaRovsPiHGUiWOrVAt3d5lJqXmA+OGiLmfvRCFjeHQYAEy24ioFhjHZehWGvUYA/WzOGfvUKDd9YB8iiDtBhMkxIy2AyocAOfdBi1gYstyrwcNAqbeskWEPx4ZoaIVIgkuOISW9xVHyRKa1EQf7rDtm0mLECKPzwA2kR/Fma4aMVtBPnJedP0BRW8CXKvTFp4OPwGgsY+gZcjVnbok1IrScITVsMPDm5mmtzGquTPRmr++5v/rijySNaEHzgtCp/dUDZtIC6i+ATE2Nyn38uZ7RpKr/6IAaucarG8yHoT1kde/f+epKO8o2N8UwuNY0dPJmBKfncJPujxszgY7T28XCKxvXbJG4WyZe6lipiTevba20d+Ckm+pGmSX7EyimKQRGupaOyowU3jURxBY0CBjsZKJ41y6aLpQDhmaM8KtiBKyTUvKAcyiklU9uQV962DgY/Fl3Z1+fmByP371SJiIvrAokJGF5Tud1c0B+3B3UD1ZKs8OMt4/hUaQdJ/HYBhMhTC8F59GgPnCMlBybDI0RfRTpMplPgdE2fxWeaIzALuEDIt3gdnHuqZ7qLMtmsERKR6ZJmyUqXdE93vrpu3LcWxXD1c/JX8nPqFuM/hSl9Etc1tpoMwTQrpdlWbczS3UG7pZyMUld1I5AJGstdKEIx9RW1mHBpyrAEE5G6IFuqM2b5FEorLBaoLEbD+HD+QdzH+A2uqADVK3/7dlTacl2tm0iQb1KSr3xQ5ZGiWm0O5HrEMSUETkM17aG3CjYuOg5r7bDrh9M/fx+ICWOiLy/g7fDHqrUL+82oXXP+C4czUns7xA/+dIzk6hxL3iDQ1LCQX0lb21PY8/tC31ULZNU+bSfaEpnwKwXwaErNFkbNM0i6j/5FIBucYiuGgC6aKU+gAEGs4dEe7RYx+Fs/adLZCPk05yDg+bZEVZXuBAL12YUyb2rL3HQrwcfU5dNKeDy1xXskG7tj4XIX7NoGwfSeqxxtPh9nyp/D/GBDC/EoDH76y5vjaPLxpYufxqxfXy4LhuOLVUKxobn8UEjO5yReKoVQKBUMBgVRUH4sFonJ5CEY5q/xM9HpJ898BE7Qv4olMv2U8qgnDJq25oSfVl39mOoOm81b0P4hIbVhYejAsnSfHp1rm9Zq3gszVa/l1apn9AgEsluAIHawKWUIcga7lntpxnriggIJzWjr1Pu2Ond1VU6siPryk2B23ToxTfyrhd6yzv2sdkEdVRSTU2XLUe/svFJ65XDp4VWyZk7zwYPoEzGaXA9mpFfvbOTV14cjD7VMRDI9fKkx/XYHDlRy8g/n510Nv6q8N/c9Y6z9ixwqCDD0pom4Y90fi2bz8iV6EtVM0Jh3OaYRltJ77O6Vq6cK9h5ctbnZxrqhxaa2nqOl7VelISPv4Wv7tqwyYnQwHDnbcbUa1End/AyXAI6W5WbOF3DqznSGo7a2b5zpQHq6tl+LzdifYXokujSdOFr+0f+4uHC1rDejKyDm4Q9h7K9gjOYOVBQmeIdmFMpoATwA0whdgcHknd7HMFK4qd7fUx3aECQgnYl0f74QQQhZRcZWOGapgDS16gg6q6d0qmIme9/6VlgjwTZAVYiheqge0tOWMTnZBp+pGGGA6rOHVAPeXTNVOdM8teW4Eb06Ik0NZKlUOGLJAdYhhFt3PMJvriVlBwJ1f8/6TaJ6RwIlZBvy2NporBese3sD+4C8TjAvFTAFbD5jDZoJ3JGZixdNPVFtp05tR/m/cgNMlj/Bb3a2sHBmBmSS4ozQG4/6b3Gca0N5Rl+66ORkAAyU1V8cX5gF7FxkvfjL1dmtGJL0oooZG0ZHZ2bGxuLggJGRe4xiVK8EKnaLKfnrRQUCTJojeb16RjRJ5NQjJXqXCkCpyFMNlouTL5vIRcuuRXBPcmbmeuS1lUfh4NWpiRPnjZwdxGIqRJ08c3DYd71+jV1HDxZTbpRFGpUuPmQhGzYtiElKcuScPrA2latx5YB+L+jTP/iDQlm7ZmQCL1GKaaE9TMpKDV4CBLWKATRzrq351lYAQfrV2iamjgmJLOSpI4A2NRXF7te5v1bV9uaL9yAIeLU77ZB7mAdo52kHmHvIL2/vEaWJEngVuDQYopmN1ELwt4bqzXy36oJ+8aITHQvMTInKtMfZ/Mr9566cVymZwbRfkJzbX5l/drV2tRt/c3Vh3+CS41ztz6P3T06JU87+vr1KyYgmksBxxKBOZNN1I522sekGEUK2F3s9qr0dJd/6DpDFmIIXP2WsuxZ5vL+gLVN3g1igGmG20jQE5s2Q3pqTiSBah9R9MBFqmfzwka/+wx1lxHvrqinSPkOwXMAaKUG/sYXLUfBsAplA5gZBZA5CAuMpZAKLXoAKy8wIhcKg+D6ACgO+0TkcvGU7SzaLqx6TBwvsO31H+BXz9itO9s1Uxwlao3LBqEPJ/v8B4eBI4ovS6Redm3ruPMMm5AdGHfjAlrDRek09CVDyg6iLqGmlNMIMhqkAKKWpUF0dJF9qB8hiSN71a0Vdi5SCMzSwHBaoGSjmAlk0nzfgEpOiQrkHID/ptxUCj3R5E5LgGP7XOqiotO0iA1dcVfXvpm5B7Sb1Esaltlpdq0YPE25S22VSDGHlt1SNCvuDxZIMglB1u/PZKyk+eOfkEVhMbuXRSHfMi2s3iXactWKy+bDv2vhVFD/Rx3RS4+CuOlAL+rpJm9OwRa3nw/y4ySLghkYAeSKAgRYg236MDeRAKd7qhfPMWmXLrYHAQC6Y37cAQ0cSKdk35bF1kMsph9Cha5P+yXh2Mslp6tq4S9DVUAfa49BYfDJ7+wVhMb92N5lSMg8Tsvpj49wrK72j/U9mUg5nEU74u8dVVkZ7x/ZTt9wq7QYChffocYuJxxvR3qf6tN7T5t/ZyewKpB2yjgK+HMhFiKKRHFIgAJEbb5vcEY8H88ssxUzzbKbjFln82vpZ+ttHQWk3vbBR9knk0bt3cxM0sXzrOhR5uOtK5MmjT0UCBpvowIhVNblFSzazejo0dXt7tzTjFUw3qQbqtIE4URVW3Q3W7mx/5U7SwYaaDDrCYnLmO4cieHisDgqBfOOSOFCTbOfKbr/dKzuPLY8d9nVu1G5U4fND+16jmd/TI7jcdyb6tYgZwDPF3lfxOYpYOaiWwmT4u+C714wuewFg+F1bu254C7yQtMY5s82WI0Ke6ZkoGBwRRxUX8MMJ+MtyRI5rl8llyJh87MPylGzqg/Sq7OqVyEJ5YcfU2NTHsatjwAsNRIEISmsbKSJAGBeWyIReea9f7AQrtJ4TBPLyb2J6b3XtqS6LxfTMjEwxS/SjANwgGYrETFqDQsFkmFisBSa3CXegL1xA70BtGz8jhlNhsnb3xWfGaQdC9Qo20EMFzjU8VtKW+iTDs3lV+6PDIRQXduYVQIBfGqUMt4ew9I1EJLmn8eY/+o/FEqvH61KeTDnEWyh8YXIi1ee/xAj5BTGNpS4GSiWreQphtq2P5pzODBZZVCLo4QcPhtFd5dYci/Lianf2cNUj262hHeLQQ5jMA7w+pd9XouYr7siOF0EjaKa4U6IuwWKetlj9tg0azVZSURRHsi/THsVUZaJ2kFnGFBSVrURHCGPGjeCaYfgMAtBn1HKkPgAeKAWSx+chJiAAkmX3kJBaT5g3mczYMBnmvgHFEThCCPMnauOuzkIUefC7/tOKJDQxSGNF2WMUiX4D9bcxlwKdn1B08cY4iHy0hKJ/7vrIF6i1wC1qT/uAiAkXd1UAFr8D7FLifqc5lF860qPTfEjy/GNvhxG29Su+pVNypH+ivxkI5NWw9whbZFsDwrwILXevS2ji8qbwwCfnjeadl2Jg7XlqxH9RYdVbxYQDr0eGZmctHZI0clyshaGELgbzUlEVangYJV9V51FhmvZDVGdY0ainAzxo/XqIJ5q2vlenWNXTFG1rh0UAEJuugSUy4ablaTcJZHu6Or+Agtzdb11vG4EM4cyUMoLA+UElxAERyL/2XtwlOTu0MPGIVrx470KLzi657B0j3tRFhnsGk2ulK6+JxNcrap/toJ0spNEFNcbhmzYchsnw1jegvixAyRCtSN/LC0++gbnX2FowBAGEZCiUzg+aDO4+Gc8q+mrPRJayBeDXSDQzWE06e+bJpBFsXvlZaibFftjcaPLJvYONeqQ1CcKmvxXSOT3t85B0lmGmuJj74sXgpwNjdki6zERtTL9alY2oqB6k2HeQ6yjP2iE51/HgPl6X2r1LdlxxKChVdH6az+VjiN3FAAKJ8MMpDJ/m70UyacA77O9e2r50D0VMvCP3kE5soe9QpJupq1colRvALUHIhCWjJQJZxBnyee7jNcAByIRLr5E+maA01f9UNrRTI6AoSyhn2QQIYp+eYiYT1TO5wmRurcEC83zxwsbHY10XiiDJ3ocwCrVQ2D2/PU8uHAaBQsmrn/98nDnHpUHr322CJLb876ZVa3beWcVmLN6X75sknRis5PHZBsndfKVmDbt3SlNwS+Be9KBXT+LwYSXhxdr9e/FdPy0jn7moPPbPj83iYNX3mt9E1RZIvXgcjWnZfD/ojJc/e14/dhDwV7lm9oWqflBROXCyV48xkpdhBIFGBFG09VQFIiOhCYY9ocJAQN3CMweuZzV+LYIy93L3qwDxcSLJGKk2qr4tltnYiVbE5yp45VTitojisNkc1KLb4SuN4vU94QEWCd3tvbxKW3FgYJxzovF2THQMertRXGFASaJa5v/dmI6M+iZoE5pB+FJj+JBqjksxuSTXeS3yqw/VbhT3JnRIS7L8K4uEK8KacGzjwqM69IfO82jBwaYvBlYFGMWh2iv30I8e59MF7ykeEILsg0VMZk3k0ahoaXWJOSjkwPD/kyDqBARKvz4ZZgpu0l5Ajoy8B5Nk1YGJQJcl+eCZRrjtnDfk02zYjNg/X6SZWbCBjSgQIUNkIb5wbgLUZQnkGzKfZpyjbFzAf0/hTpHwYXlV+I5neBf38uHCgY6vSYHS8nEBqYyJiI0+1z+C+nMaA6TG1eD5/EaS94Xo4z7H06/5kAKaNSpSCZSzMpvKABzHwsZOl+9eyMhvhcnwmKYfY4+dFjJ2aQjwcgHpm83nJm/Si3/uzbnjXsfPvjnbMiWuE5+rba1rBfZJt84nrV04EU3z8IPjvkat8RckUyuj+aWmZmcWXm4+fGGUp3uMHH08aqsbj6ZXHJ1UaOzkK9Gj1o925bUlaAZxD/cLRIbondsvzjuLaPu9BpcZa6paaBKspmi0Iad1a6n72rPncsPO/Gv/3CqlwoQg7rTXE0uVixwwLn3zpqWA+VpAY/LnEDHW12ixy3Jx0w3bnfqqudNXIrLHtnpEtdT3nBl+lK7/74HtwM6RmBn4ghG+rmZvtSV/s7hmH6/AtgYWBNkWwxVRv6L3icgtyWRhjjK6IxzHccCFF9032NolsKzZ86jagre5Nb+qYWLsBv2ff3hoRDWjVrtVisj7f0W5ROiOh3qGu07oeIb8ivbRLyCVuqcB14VqSUfL6PnHxx+tinn0frFJr3fuURUNXDHPHWUG5Y16RzgAG9/VsOk1ovyFbtmppmhiRFNU5ITdCg+5gfBWIqQdkR1JDglm+0sBjo8zdh3kdbKuHZiv1K3U6/aay/g1zz5J4FWoohGkMv+s3PXeJWT35n/pGY8me0YKxSGCuS5Xrs81AX4m5KjuCvVh7GgXWk8y2dMjmnbghUIS0UJ+eBGzKDwdMUTxB93wQdKtA0K0sHIVO0HhP6xS7y45DxnQHsRIu3PAUAI5YM8jIuTYm6srPTB19fEdoQc0apXKxb/10WFftwNukKLFWwS1DF6sY7UJfOwWtC2+Zc+1oJonJ5vlQ3WAvMboNRVIw1vm2/k5fj0tlyMiUad9OYM9IlFcdJxIBPjL17qEHHVNJBchAEFMILoA9QqFiGWx50VlwWxqTmylB5HbOgGe8gXXibej4hMvvaOCIX8jYaH7/FenTT/tR8X/vWcN10eQOVbAe8x+fn7QaFBO7USmiEl0kTEa0+m08xN+X9KwjCYbTqrxRE1LqdcXKOQ6ECrDTUm3IrGylPXLUGhRciuSjoxJZ9OJURa4dla3xt55/iGPg/2yg8423W0Olsv2c8OtJh4mBm1Dmrj7gU66azplpnbuzpa3tjBpTEEQ4thUEtYs3EBBCvL5LNO4i4kVv4Z2qaJncnHgBSkUQUHs1IWcYsAD4T6/DrdCT3Y4CzOG9m22zp2pKQG70TeX0APkYy270Rlgf6p9TGoqcL9FhyAj2lOvXQpFpoCTPqW1v5QXyucL5B3y9nbeGEc4NMZT8DqM8o4ICAIuaOBe3zD3Ro7JQegJqMKAZRzHW6s/O/o+L4eeegq2d/5As26zHrPH6VbA+WbU0cle+xcN1qtiLW+U51jv3js6IxABH87UP1sGXMZOtvaIBLKjAwfX292uvHfx+ybwfxSR/ygcHXtpDnq9BvqmphI9B5qk8NFK8/LaBsh2ObjAhcWmpgYGclrF0Ul0UODpleVQ9GptyWG0eHkd7t1WBuwa3ILgCp/Z10yzCbH8IgPsXlOco5LTeaBrwfiw0ePTfRG1HXbMNyQ5FY/ebIi3k4/h5Si6LdyBU0nZhJdjLO3k1XgLToe6Fz/ZpfnCkIe2tjlGy6HX7VEHzlbOOWBdhLz83WZythVaPmiumnTHyrz8lbP3X1dGEV4PHZh2659he7w/8zHROD9Oyy/NGR0clJD4YL82VJVcohNspfcqxjpVfUE11KJ1I+uw0QUSZWJnzWp99tB7gubRz8tjqAvAEX34/keEuyR6TVnDXufE/Esl1yeJ0lpg6vL3eQNfvQ0+7nGd2lbeWO+wtWrP7RsCQXJob9CpHt2eDen5wxxUZlNTpnyozsPHuXXe3B0CpaVBIbZBdbkZ5Nt1bKGQtLSQI7xe/e9p4F6Ia21N8OoCMd2bH0F0kHBV0LZtLXQjPQfyJJeDDeP/SaOCQY3kGTeuR1px4XSSwoVU7LGO5jGLOGEWdQFijhN8pqmGN9qxtKgXdJFOghGhFWZ1iYv6PLRWgj1DZx127nyFbhfQWTrM6kYAoIOaVzTaxgCF85+Il8RI7j7AIFDonn7c4eALYTL4CHaJQ6b6a9DXwxdFxhGU9gb7p78JFxBk29rKlmTIknUTl9F0iSFVyBTLkYWgsGP9eMwlxRJQKvvwTXKOo0rrU4vD022FG+MOHnTEOcrJGGcvPBI3mTOdPZ29n/ld9H2e+6crZiomqxPWhvqZGqmzvmdycig6kaYyG6AUwmR4nRcAO/RnY/n+Bw864ByUiSvAbsM6vsKw/Ue8W8IdNfbcfm37TcpNcgTTzQlOd4xz00+IS2P+EoC19ivQHPAA5yHIiUeCvFJTBb65d8vjdOtH2cmV0zGDBDKB6dc/xzNB3qlTem1HZdIJFXi3vl1mwLZxbSzxqEv2qP/+9UBHLaqGhMUgrSYBf2qqEk5F74Fs+BRvLVXi6ZIXGuh/gaGlJdPYgeitHmfFtLqjgbJZiVsiOxkQ09XhQhZTwaTHgdC/pvA9HvswKKWj8tjLa9ULMYTALFBQSlQllk4RXdXx2x4HBKA4iBq8eZqYgMXtdiamX5uHVSz2Teq7EUsnNTx5/pYp3njt29DNnz8SQXHJWum7HIM38UwhG+rTN2P+VPPnB5BAtvHEFAV4QCBS9i/fZ8g4PKGuSnlFtCE1qWv8HktyNchQYWt+riDiV1dPGOuCUY8ODE35JBXV6hF1/K/vFFUTF3eNsrrC4wwpUZ3UuEBKwJEmxBrBxzaLIfmwDDCoQiyxcZOvJsbgiNkpB2fk9FzIBf8YK6gezP1e0aRBWh6eoYn7OZwq5TvRltSD03gVQ3Y3zMBwtXR7iHj73ZOmuuD4mg4YLoUkFbWt+9Xh13SKqplriEZ52PlR6uZ1tZoshmE3Bq1GbrpMrvQ3f3n/i0W93btBXSy5CQdbTg5qFxqqoERYkt/zjCHMTPqBHZjhsbCrnOyoTxR+yd1NSHeNEwO9jcanyFhjqxSygT5tC/qZKnFSc7wIPLtGdU52Gk0rVjoOu123qiY7m6+rr6pKjHDbg1UlPSRYwaXquEu+REdaNNqTMpVN0LBOqGfql6AUy9KgfwiHVVSrdmHxj1+RVFlUN5wK6xg5o/8qdJK91QgpWBcO2cda3isMofcYBmGh6IKNMSgkCOLHQwiKHZH7iFMUEqJjUHKBf5Y75PPMrCMy3Iq+J3MFqCz4mnpWJNpP3ez9le94ngSyBmvHPJURRuhQ+ddesCxsLA67kbVyAA4NYrOUgdw0QD74fVg0NV0IYhsiLO2QUhohZ8CN4iM4hyh6RP2aUeezLMoBwUXxAbdWYhBBwrVXOYhNF67gcuMOO7Q/nBf/UD/m0WB9k3bTQP2r2FXh/bureiqtWzEYqcfojQyE0mqwvpwbg9Nqyks4ssuRR4CSKEP8aQNuINVvypIALZKWulL8UoblpEWIYDllX7VrKX1wMiwo4R5B0JQjyGnCrYAztKSbu3P1zlxdIbHVeI2BCxqR5IjItRf0SsBz6H++9GFpAJzfAVEmVoTNFJ0GKhVjzEKw7BR8ymb72Gl1GkNqAajhNFl0/SV6hA/rEDcANt+5KXzmCwDaUUvf7UOgKGd/Mdr3j2pY+83dWooi/2b3bv6bQ59h33eHY+/sVNNDrxLdFgEhCFAiOoiO+xBQshGcmGcprRiVVQI99U/qegJYIdpnFe49mS6gk98bpyNGV7+gv55fEE+jTQgxuCL0faM0Nz0dWrybYkHv83XOhMIJZDqLQOawAWmkyBA3iHxUezaeQqcQjDj46BzgC4VnZtLv1FG1Y8v/XJPgnU/+2Ce3MEFkWI6ISmBgFL7txMlmFLPxjW+rRgUGqiAa9sgmn3Xs3RVttMKwAS3HoQRie7pN+JrViDpiYvF4TW5QHB4jT1andjIhmZngGvMqkkskpzxk6Ef6S5O91egQUoBv2a57IqKbUSH7rsdtluaa3h1fapFJ5YCqVMa2p4Bwprwl9yJkuKXVpbVWjpVRK8stLzHsGcYRJN9LicbCkBHcf+tWP8yYdvfDl4+F+yfkGjpS2/kZIhw9ShApFxBEsxIRlDhwBoWV1tSLtH9tlHe4Bby+xq/tSO8J9a1W6rSt6ieGH3P9uI+Hra0MoMWoFTlEg+RyFFrmX4V4yaEHCdYsojXpYCOYZ5fRoJAtF4C4R2JXtHvsqKt6nz3SmXqGkNDJv6JZV5Iq2Rc8xUY9Wzp1vCFEmGjRwUMiVD0qrocSMREmlNncNYkCucFwEDQsBO3uHTICQDSwzYY8P1LkE3HKhZeR4FW44GPaYkF/oca/ibbzwm5o0Dlnsk46gC06Wl75q0ltg+xF6fcyaln2uyfAc/yb2eu0ZJNbsePjVEeKSOTgaLRnxdm8OyEUoTjJ1DT5IePtazh/Wih+EeFfqwe0DlrO5gDljVC1DYWdVLqIi5xlhF693kWx4SxZ7KVZSe0E6J3nz++UD1151AnsxhWyOoIWAIIeOTx5GQWMebm47kO24ZQTly6nporn9d7SMsvbuLwspl3+MezSE8CR8J9ChO7hU2tPpZUoCUPACS0QjWAXYKSFj0ggYszXFgQgBRHkJCdCQnZTx+svXyorxK7K9u3J7SYS1ZmDHnDnNYf9B1W3TRcyPdfBLvA6z8VfzhVU/drjOBEhUOBTm36tc3L3THf903kephAQeGiQeSL+r/gTSKGc/B4P0op2ptJTKyhaWpSK1J3/PT9Iw30E88vEv54wr96aRew4e5pfTnC0T82T6r13XOTsJd+v84f3ZD6DiYj8e5oyOyj78kATZ7CP8K9PisBILTmgmcQzMQIstKAffeM6ut+dmfvTxElj3ezKdRzfiQpfe93R/U+d+l8XBQeraQUHCgSjW0vtXAaozmRksJxYz58bOxqnmy8jfUY1buTokDFVjJxRjOJIzbBYOsWZgiJ7R3sgji4uPuG8yKfEsPCijeBs2NxgrmG+JgYQg+NLXdEGAz67DTIMdvsMGKAEB9ZocU2IAXDNWyGLshpZaqFQRgYkX2gHyKt2CN5EfWnQfUGx1bp89tqksbZ5zY8Osw7wq9d48tp92laL8kJ3vHx1v5CxLGAbKx/JXdiF1bFQYSEUSz54Xaq7sHsL6tgxlG8LeMQAX4nYA6MEifMRuBIREACQamQ95XYmQ/29Q8PnpNL4mVvCeuCg9vANpQZwhqi/rlzR5613Edg1GPlyYbmSXV5XczPsN1ciqzftKOdAP0C6wjkaqRUYphUUeZS7MghKd8QMcpVSZelKXhB/S3WkVlCYVqC7bV5QwZYaqnF5bWlPrd6RGpT32CA93yDjiurHddJDfI8O/uem0h/nJaSWVBsbl7NjQXf9wnWxxuijmR926IjDhyr61wqa+OaNW4oqLEuv99B0Lp5Mlzz6W6wnuaXXVt6266Rpe9seYfWWjUHV1FqqVOlwNEo7lKcddsBObcuA/cr2X4z8Ykae0vPSANNabhJQ8gVDneGssxDoSbnd5jL7xxWrBoH90RdGfqnmed6Bn9GL6i2pQcmrHm19K4mMXIx7UfhcRvI2L2jjlryrfgEBU6GL4KH6zYZZIanEJDHKA2DpiHNsGLWMYEEJZWUJ8kE9ACUYeaznXRs3Svr+8vb/rc/jnblpRIDp3nsJjKsjSScvCcbYoRIlBCOAU7UPUAA+AzCZIr8O0JlHBIiUP//StZpvLtsvtYg+8O6ZVAe4cBV/6K5PZUYcrBpmyrNnTp1IPH+3aBA8mWOfAse8t7F3A2n9gLp0fUnCNfr9L31EP4VVYid9Q7Pz76p1efWt596CSH4sIUS9npDaC8aRX7cjjPA0Att0pfk9A3/OKHtU2R0QKe6w1xUH8Ew8XSp35Jnb1Z8e6pXod0ALkNYak8F3K9kHYSJsBqJAlhkdrwCWb/k7GzqZICCQgTtl5/4deXXz+Y5qz03UGtl743Pb7uYHRR84l31k4lDq/ePJU2dP+WnFzKiMsRs5h8cpFCctn9ml9C8mzz4/zpsKdT1wOGXr95Ts9U15/brJuVbpw/yCP0WFamuAB0Dx4CsZFPi5pjhFABq0TYwxKq6M9ET7Grkuvd+Ex/n8/+U+M1xVMOKrPBVlikDsUn1EGNDQ6DxjasMhpgiUPJWr4t1bvLRduZfY5WRovWfyLGgTE+0GYO7fRj5DvRZUKEQkmY9J8CbXrTmY5hTB7HRLPA7AaYmcMgKKRIZYxumJMMAtqlOUyQKFwgES/GS9ThZcnWqJ26V9euLIIo/i+8fTaCLAxbVMzQa8rjOBidV40s9bNihAJj60AB6QXCSLizQijS9BmQzCNUiNPonsnDhUAxlTdpLNRJgCyv5lIWKGGKnXWuohwIN34IpJ7NfDQWeqLM08vWpvimax6QoFmaTyQ6bpA5mGeLvQIYmkeTysyDDyQYfLpjpuOxpGOfN5BJL6u4K/mbxEoS+sQB6fTapmhsVxuIj8V+fvdGZr66cPd7RTw19rJbFs24Qek3Nrr7N3/enH5W3H7c/aCcyYyNR21WdEveD2B3fpZKEnyOcsxr1Qm+d/zsRkfq4Ai+k4MC8ssvPpIgmdryVwuts8I7YL50/pIm/9vXU0ZGJj/9H2Sd72iUq6mM0E211AMmRQBMyKPCgwl5sLKjZjQr47Y1mkEClJc+f6T1axxG9d5x5EWiGIVWTbo0CfDxJGyaO2cyEEibR6MOfybuMQcam7vjVyS4XPVhrhd7/CO5e5O7PNJ1dykEAoUCCYHwkJBJBRNmokX8TjF+X4dl3paHSjUqCGwp789mkWQ77yexIlKddK2kjt7Rrn5kht38+N7zp/vguv/fxabaS5cxrb7ztRfOoUcAKyJT/X3ey4as01R4HmyiU0kvqFUpAgcAq7KQ/OdOoOCq6o39N3bA2j3/mQLiL2Y2zYG/g2WM+cHahjzDLraYjM3/5dfk7rjEDb8q05pTyrcIr03xn6G/F7jd9K6GSwfVxz1nyWfYg6RIojXRyUiTvb/Oz5c30zYhYjk2jWSMpkxJEaS4g9L4tYoi+R7PCtLGuuSNkVI1+fGPsnrB4wGDVUshvrhwok+X6tNnHIDqLw/9HjVzrtah1UD0KFXH8DwLKTiQYJQfnaOe2hmnV1LrRKtMcqnTfdFSKafuh2fW4IVfTnKvZGoHN3LR7oAfzaQg9v98JtAypclYFxm+Nk/zvkXf/2lbgQXd2xmRF1bEZUfFScSCddDukSPAr/Pz9K4yHOt8yNtmRW3h1/rM+NrGhnlfaq0IOA873dqMtj+ykOHcm4DVxQJ81u/0iaXbFl3OUTZ9heMUPlCiZBP0ufcCqtEyUpSlwu4TTOT05gWuzwvoFTG+9Euit98rphwqlNhJMq6GQ0UergDLwa1rc9LfSeAHJwO5d/abB/A0FA+PVThIjmDwj9R/17B2S5HVENMQoI9qtAYUjnI6nuF7vejvVw7ysnm1f164gBi3f/XEPLc3biIT4t6sZ6kspiKm+YEVgrj6muufPA98Uk0agH68wlp1YX1K4unBXTKTSxkUmS1CYB2f14rsHikX9AyOTFkImE9exXyZYnyO5X9bYbcmwnK0YrzkeZmiZYCbSybdbsuKuvswEd+ijHsi2lqSYnxYgTZHDTQX9y4VywDxAOdW4LX9t/ZCBy5Wfmr5e/Pd7WeOJK+JXDIPSI+iXEFD5jfiFDuPyl0HNKs/vrH2d/Plk1N4hZULTY0ye3nmyjXoNlhsWr51bc8jUFfj6kS9x6D/f8s09Zdie5Tt+XOJ20YxHqj/B3MRp9WH4g13q81LR54IV89LtqlTa2YayE7j+vghpAZ4UWfiz0yEKjdtgVXbZ5v7d07341u7FZF+xHbCAkovatj5O90Xkti5O91nmTAWD0MfAYN6aOweq5BKxu0T704wvoneaNrrVr//3zrti8Fucos0dm0Y4wGd5CYMEoCNNkMXs3PuOuLiMoxKrLBFV1fE8riq1n9qCrCIZx3qXG2TjV+ngLVSFmCpLO4quidz6+gK0IiEM/9aMylCuTWuC/f6c6YKKROqDpohFc43zW1wFT9tNWP4MLOqOVrwU7uietZZAU2ow9/AHkL3V/1tLuKWupd3+v5e3bo6cf0q/9RQZNWdd37vTQbN88tQow0gUS1NUZVKtxsUPR2n/flToZF+D8aFoyR5w7TIaTBqyeiZ5KUGCUVoHDas5p8yR3KyETtXFfdx2qFrW/u6JbG1qgW6/OfN0Nk+GcvQH+av2eMixo2BLutUucm8KIQvWbRqdUtjav+bNidblzOdj6Uy2WDeK8QwMNnJ+FVoslobYBgWvNM8D7B/pBXk8KAV+udqJwA65wa27Eeng8q3lVTb0Fl7Y67rbAab1gv58FqeZODrdsjvLre9bTiRU6xy2XxBc/xoCfX+GNqeaZyqkLDjEOApEVHBxiLuzPmsmevOFXCPxkIeAU3IxMudXpn9H+RkbFokjf/h69zLwMvOWhqEyNtI+aWo02uwQ7lsoImh/TMjVQXnw/qKsXhu1voqq9ZB2Kjn8GFWISaoebAA8oUzQeF9WrRwR6k+OfermERqVJ4qLGRQ3mRkSmINIU2VSx2GB5taLINGWVaZ3fvXXOyUd2RZFERNUVfwDDlVVTUZM7jWBFdIZpIcABhRg/TFpzoUWNZ4krMzSCG40lTfv947apbtOk6g7fpT1vI03jreJWLiYumlC00LN1BCp0WlvvUUyzE+O3C4aAHEiVNf804cUxDnD8hOU+c5GpN981r9Ez0X+kX9LZ8hU/BKfDU399PCRp1um5dKTcIe0bEE5CQRweb8QgjvAAb7Xl4A7DOQULnzdYrI4sSvJE+y8n+QWEuWZ8DwvRtt0/oEPL1zOyrC3yDF3vgQBZ8tZ1ki++Wgs7SHjVvr6AU7XOtdqiDUfonfpHass49nnGzPeKAXaP107zCUO0VjepjKun0onBBZzSs2xZOEw3Gud53Ds9X5Pp8vvZ01zTTS4bPE9t2E3vph8Bb5aJN8PeNiX4PSZocgIvBBQaC9T6HUIq5QI+LMfDxg/D1IA76janiXDpvDIQehUfovrGTUK4wBvm7j8EjEuciutvofnN014uh9W9iwG2VGAXKBVBtnIZ3C5ss0BcM2j6NITlPechFZuAFOBl0HjmOYrGKJtVO7b1au2QpiM0Fu2IRCvI3GZW04nQlBsTqGeQIIfTNpBrRliNi4Co24Oluh9iFjUzC1Ajv7+oFGQJFwFlidFli8t+mvMfVcB65AINHfNX7hREvt43dAztwsgqotqbNJgMq5c9QagY/FK6e0gkKQqCup/nMArFI7q7py/h0V2jAwAmw8VvTkc1g+MOAjKLScLdako+ab8yax/SdAtHYrLIguMOzXW90EK4rW0L9UL6/zLOb8btSNWbrDHTDyNEQ8w41E59HhRB0I8g81DL7NQdOJtzwEeWFZwNLiNMm5r6ha1LU0aOnM0JNKOFm2TD2ED+g0ENvAOHsZ4S5tCXYXezWKW5xkYXcQqZncztKZ9aF0H3synLT7TwXcKhuDjf0CTT4ksmm9i7/1JT0dDnYRzTgz0RwavGykXqDvOqmc3nLCpCp7jMh97pmpjTX7QWXbtP9dVsJBn+hmUO5H/MJx/f8OFwN3AnH9HO9Jaz6CwCz7jySCnpAKnsOqs8IGNXdczEyxxxdEdj6I+3FOMJ41vevlSMrek4R4cztNwQzU9FQrTKWRW0sPijZnLugWTWhqlsTRXzJrdC+MDSRleXk7nLrBCHF6lzfqtzhNN0IicIkI62ZpZoq6kIw0Z51MKHvbwNlUF7r4b0/QLML0dfSFnK7bKAtVnIxCf1YBTDaOeWSBXtkpYsuZFUc36Mfm1qxr2tbN/8W+9l1K31MRF7PmlKjgtmyPSPN/l1HhgAMgQI+HwAzyqa5yMyxN4qDf5hWT+Z7GmpOivcObVLkjtR+lNqbhea6pXiOw7tsvd6IVupYTp+wKTIvSv3kF0hoyXr/304Xr6HaPCohgEmGhHQPgGIDqlNCABfSQqZnCQCtffhL7kZ81UyzozKugw3cOkN9NXIQ8voZIKIQD6u1pp0DGzJL6+pTLHfOdWcfGal5NQ0pQTl1Cu9Ht4IXegzrrbvJ+mnauI1ciXtzKi/PNym4oPfqfO6ia2B16T6fLyz7I2rL/Q1Gsgm9lrUMdn2AQ80E8kDAcY6jyvliJwvFeWYgI7X5wNEQLp819+GPVbuxkSBuBjAY/SyxfDWesrISVRjHnWzVp2TA0YOP6e36uKcbOc8VOvEXItGq3p4+FDnSKs7k1xjtjk/cs+zyUOV7d5dhi5HBxKTCwIFAb4v9fmImkUnqkRVbHZ/xgRuo/np2BY62zubuoqM++a2/PN8s6TSNoA2Tmik4SyE8wMaZ9PsFXT6sQ2PpeQyTJKsHqQnS39Fv4liFaDfOQhVJhER3JQqcbPXUT7/1dmqnp7FdZNVKdyICJOpHknl2Vd8/lGv5j3Du3xBuZKmdj0PISLNs1lclXL69b9TTVXSNLRg+e/r08csoYcgQxOVkp4Sd9sFnrQLO8+G7qE+1V+PPOJRjzpLLyD01KlTBWn5LkM3qbOcyxI9I3WJf7ZPtr9E3UhP8poRkeiaa1m/TSzLMqMQrbMWuy0WiQ41rY415s27xiq9OodBK79Gczj+B2YDRqH1R9le+zW42OCeHvw5mfI9mBHyriGcfLd6ttcmCiFvS9yYZ5IWIQG0SIIc8lG1FjWmxgQoWHDjsRTQjnS4R43ZTTd7So22QXlRcstT2G7R645CTV5f3P4O/ct/dycZ/8eZX056k6UmVW9f0Ow4/qowO1hFGPBvIxo63rUkIjr5S56tiJ5NLwn+DtW3XpmIcssC+6Gxx8ADtSLn9bIEeHIOsjD8pFMEb5gX4bTQlde1hNsidkjHezqRMEwg0sMmNj3JUOm9USq3uweTh00dArv6z32Hr+43mz2Nzwlt2AU5yf0aKh4KROW+BKe0nYidGHh1dbQ9zfPYgDOS5cqMcNfaAaar2QBYbvjdH7LB5adjW57HS/2pVHws3iepkiur5AjX3/pWMZnI/dKZWpgM9/gLcB2BQZBaOdffIZAJe86Rxj9ka+IZs+fvN/SYlHt2mkBe+rJEJpyEyX+RIdSx0fF3M6hz0xfCp7x98ORG84k+QIuOvp9Ji/Y+J8C739X9UXFZl42rHHzVfmWdVRRjknfn5dSW5wXV8tCy+DKyi8pbn/DOwNW+N4qP1wa1gTE0/qBcfhCvcUguP9RhDTKNZrOuehnoCL4SkSMCLQyE7LwFKDgvXIKN3hpIVDpsk38TZByTF8qBc5e5OoXjZBATH3vP2tfRUz04uqmcXq7fiI/TbzOtzQje4nLodsMbuBGNDt8aDpF/DQVtsUFgsD2MmFoMwj2xWrwx4EaPiaCRnSj5wO3PgzFp7/SyIjKibNKjwtzHKd5BHqGU8SCf/HgDb/LJLRdfxSoZcwh1fbxPkHvvxiQDmNNi9N5FZMUe9+jkxK52EdnEG6wCbhBBvfRQzlRL2Fo/gtOP775jfrMH/NTUAX+5rIXke4f3HwMcwM0QT34qLTe0iDvtLKotqIbiBo5u9SFRaGy+WyHl3bEBXtH+RdtNzN3G+zyJ4fBtm9ocG8BYFglES1qPsD38w/qErOBJoYIZOB94GQfC/XE/ve26G93voK8K0viul1NcTYzYCDvZVeSKCyqCKVauVpRgHHlZtOwSpsXL85Uxa6mGDZ8SyL79fyCGDXzc2ho29UJP2vfI95ymrabJ+qrbuQN2ZA4KIaEYL89tAvyyklIQrbdh7f//giyfi9vPcsDrFYHyD+5ZO6c/udCNNze4wNtvHXC92QpKgk6Wgwu2+Ipz4QqzcKyQ+dexEFAn1Mh71DZ5HjYrl64hd6sNvS2IRm1lsVP7MvP3piGPZHsmT9recVRs5hKAeJfqZaXCjSySH1lbHaxGM0VykcBA4R4UCIAytkcy0lvY20eJ+yuBGSrTnodT0aQd2oqpJAV/zScvKbNEJlNfzCds7Mi+g5YFaTpaJWtSrbK4I63p/ME2WjJfRibLebNO/om5+wxzDETKrn8F7pOt/SGBc8/k+D5xxGju5squWytHoCNKClU/+ET09v4CLzST2b784UVeHD7dOv1agl/mjBen+T1QFJgM3xq9RY8OuLIf/R0AsGVLzweHaCkChrtNCXBTMx7vd//e4p074VTglSY+9kdS0rEn03fhq/DdC8fuBQePe3HhLnyJcO/uraHIgnMD0dmzW/wKz3cEmEpCDtCnH6p4f9RO4en5073CxREwz+9xP7mD/GrxM3+aToo2La7omTb2Bzu4FT+w44+KluupKgmqmtFq66MNDfKjorBchtAPJDpTNROd/CDy0a1Jvcn6WwRzo7DvVjIwXB8draapmqBCrXd8C4ie4QaSgxkbgj5ox7Q7iFZAi/2IFciLB/HQYA+zJuQv2qowxLoO9b21W6t0qmq39Yr6IRfIKz9VIcY6dIrrHBaq23bKZXgurxywdxPCL563hje93L5jowZE4Trp8xJjZ3T9yJ60iKgmHI5EJDPAeZz5Xf2eQezQN04ti0WIbrXJlXVAje49cJZ9Oxuo5byr9+yavPMLff5xC9bP8qjMgPob9yEZMIYvRD4fyCow/1hDbr9yBGbJyDBqsqWuZRP5Poe5OvNH5D61BUjwzczFl/sthjAcLn67F5M7/E40ITiEZUfefP1ll2Hm73pOMXBWjlDtjLMu7gQ2BDTBZk6QC4DH+L6bRFfUM28R9UQbP0qn/+sJAzPxDSCAwel2dDKodPc6Yr/TtS6Lq1d4nwcYsuIMGC9B/JpWGbKHde3a6JYh56ZBvK9HaYIn5rwvmHaEXzpsAMtDlTdfL742aOYi3GYD/cKblePZZ8OQjmeYSvVtP3OwaOQxUwDWlS4tm5JaR8+8+yTVW9OCSpH/kLvWCqF54fz3COCQL2D8MFxabfHexbqUVpeYUEcrWZXe3R1duGqTdlI0o8q4NKUqRZcze7c2GUQwfowTblj3GZNp7Q0UHicJZF1DOx+G/DazhSJEJF9+QIBSChRAudyNOs97VA4iEAm00fIbZsFrwdKyelRbfhZEPvhdMnzMjtt7pP425hKZMHBj7BsaIJDJYG28WP85CPuL5JwVUccjZ8W7ORo/K0VBmeTkJxyUr2fKXZZ6oomqrR9W1ZeTgGL5+BduMRXM30jV/XbhCl6xlDYU1GqqDXB4dX3g+FXDh5fUVi6J4rBD2DgRtAB5LyV61E6JUjoZasv1PVR1TK++w5wS132dcaQhtxomw9eR6wccVcW+gKw7mIoej6nAQ8/k0tf3InCT/0Stvtqx/1zI9rG9IMx4kR2VE6Fpi3v+tUOnoe3+vTkihRkzzSdvdMj/VfifGbHKgh48Nzfses3MhO/kbpVZML7o1jDX57Vrn0d3rlidRROzfX0PzAccBsKHl3Ye2/b0QEuBZ2eibL4zoFF641l2FPrhHo3A+Xe6eMD7lcbya67IrcMDrVmMms3DPVHoGJZLeQRQHnt5qOxDvnai7n9jGcRerDFI1DT/cF5720pjoN5vEPPfWKKuzu4/YmMmEGzy6azokP8v7tyD5XqcZlA/gJCXsgF155Mxs+ADYD5GUaFvb74fhPwv+l3PNP7bmfZkvRgfyfbMsoHhrd31h4nGvM5YNmBbBiifFR2frBjJnhbO37073p7p7JGKyaLKr0G6v7qZulVYJgiIa/9sCLigchJv3okmIlhdIHWWRxMjIJVIs17TFMTXM8KqHsr3DoaUp8bNrzUISotoIAn4KGSiRb9jmEzM7+QDzL1GAWHPpbMYNBqDnWfRaC9qsvYisojpwywiTtCqOjkaM/oQe5B0EIujuPVYHaLlGLdDwoJBRQ5vrqVljrfkfH6T4M3ljCzgJI7YMjUMBNV7HyymFjWsTRCyaQG/cbTfX5K2t48sBNLO0AO1kIKEtQX1JZqPJbGaFJ8UvSxDt+13Q7eiJL4ZKmijaDVej9NpLOAvxRvmdUKCVz7NBXTSlRMcpzVoe+ISiS7gNr0CqdkdsQySGvGJqCFgkHZHEIVoMr+xTm4inzlmqvYW4tfV8QVZmHdd/ls1M1u+6X3GrR9wtHna5gz/Q5Wtf1z9JdtpEI8KtmZkVuccpELuKuInFrOzG9b7nP/7ZUOzQz9gHt8zvDiMdi0CoRLM6ADuYX6TBl7Fs+I3rOH+q+dxz/+K/+tl3MuX8UDFEUk+WVbtB9wCpUael+kbcGwkMiCGqfwUv4DIwxx8A/IygS1fXRrmoaj/E3wrUhmTCUV/Wo4yBEUqmIbQ4c+fnbazp+hodGJjEZNqQNpYfGQfyK0hEjMziUR8AYVyEYUSA+x5kSlK5fy/HeAB3etAOsC2n60baRstuGvQZZ6CPPtCyszIBl/jdA29LB391xq5O/fk7d2bn2Bv8X6lOSQH/rlMz6rvTDPj+tmtnQbOpORw1x0gGiRxZbV1g5wECKUOJUID3E2buAMgQVUFJHCldbUy7uQQ91eL+E/NU1AaNfrxqX8ENmWxMa9OPabyoHWzKI5N6efpjAmoUf8935qCVaXLnIHd4dBWSkRv7vEZ1/DFYyGFU8cDII+OR2HmtPu7+6HkkrM1/EK/NGyHexopyZsZNQ971OFeMDkTCLl3XA13pd+XDkBrqJFbK2Q+d1Yu+Db7mBod0vnAJc+A/ykixSXT/9Nqnuf6IC7FWbTjgVmeQarLszpn+n3ST829l/5rnIFCMLEwoShrKAtMgpPhpKSgZkLyZDKh+YwCyhRA6TOH+f79EfTk+9xrFGg8s+hDX7+Ohq+vA+RVH5gdg3Zf7S3cc6L+fU1V81ZjhHon/KFJfzrGfHHGyNvTz9fJj5xlPNtfDvibQ+0K0VH79u/7r2zxQtOE4vgEelZoWGh0tKNWEq379tdi4MHE+q34vel/5GyT7pXYlZ7oR7GParUv/yK32NpY45iY2hjgjkgsmizuOTU9b+primpilvQU7F5r6Xelqi1npP9LIi30SiBQN2bXPa5TMbZxe+xGzP876u/qJwewuTcicWfj1vKt4temPPWm0b6+wGiP8bWAAJ2/T2zLv8bEnFx989baW38rZw248t6+/aT0G5XZatLOaH93ac2lrIV1tSP+h9Ydunfibi1SgvTeJdSbRrtqE7QvASaanXFrYLPKMVLz90WiAzS+A7pz7mL7v68L/ru8nXu5ZI43XoK1bx7j/ntssw6lkjTWg758Gd1DPk7eg3ZONzj11Swi7VvGJhPuHJpJ7fA7eMpvbEq6kpTUSDDhsaNEP3+2R7N3ndCY5PCUpsbVptmcjv//7+Cwsb7Ezo1uG2mpXLvrP35cZ3I37qdt5DJfHnA7biqtwA38M1ACwEUrDSZtKPhmNnDMMhvYj3Axbr7o0rb1dQYlAs9HGjX/mNr3ttl9XkPf0ZnXXHsHmJb+H+9HOqLCTb+Ex0KSk0OS7jrEGkjKfBPkdsmSMXZXl5ERXH3QcanRQQqobZggT1RunmCWyejTVzwF2KIoWh9lHHOTHqsgxxTB7tIryZLEh8SQuZbXkDlDLjljHvuSXxqQMlfNcOGNbpzMp+2UHRN9ZYUH4gGFs/zZy7yfPGQP50p1yHj1MTfxogq/6WzHkFt06RWKrQQU4YXuqVL6zg4Qj/v0WFVDN4hw87jl7rWDC4RURQgyza3fz7ziuqq8NKOM76YU9d7PsQXSrLO69pjSAtktP/acU0J0TqdDsuiMPTQJxgry36+U0oULxYambz2nVmKIW7aF1tYkeTyLV4VwsloB2SFi8jzslOyTPUs6tEgkN2/nYB1StlqnbvfH/57H4yfvws4JO3P0jLc8oEbnDYnCvjFmCRMC8Zry6FNkWIQsJVVgxSPDBD2EcRNx0VmyGUMhSgQyhYls0VkxLbtlKlrPM64u3WG1hSbnNSqEm3LuvGGZLKbxK2yngiUKWg5I+zy3RvZts4CwV6I9LqNWxvr3uhy3nI2YylhwmrJ7GJ17DMfWjr1gnhs2kekBccsyr8l5Y44OuYTv/rBe/iG+AHSBeY/dTSQXa9sEprTVMJGIuXI52Bt7FvJAeaCNcr6iKjALozIP7dtHHfmP25zgyhA2oKiRnKRpv0cZhzD98tMAaTsZBHKOUC4vG8VeFRfGyakdxvxyTFtlnJd4eHKIZIcIlg/BvmRCSx56xl+qz3sBIV7K60ma7Bk2Di2zXR5VQOOxG7wTsEluzB7teVx7Rm6Eqw6pwoUJvgK1AddoZVs9PGNJd5zlYBOND5ibDtrIdj+Paxby0MWyQVprFdV4dTKImf+wYvOwQRMZD62jFJExfIWoqmR6hXmPPfYv9Sii6Psqoif8Aclfr/NUZFKrx8p1Zue4akEWhImREGnpqt5TjCc7Ro05+wTtpSngMe7/FOXRbQ1isgHi2XvN3LNYbMOxXfeV9IhSP4wt9YeskBWys9gC3gXpcZAJLWcCqfNCcSn78ubxcflsHnMT4hE8VJX5ZIirbMviCoz6c9+WGg8IfjY4+l8LtETsfmEIrfxL6Q87MXgv3GhDxOPrS3piD2MgzdwAmT15jQWZsgwtG8L6jxHdPb+ap09yT69vo5GDWwcT+CdUWi8J6UaiLIMb/6eHlFyU4PrduxNeIqFvvrG8T7aW/ftFAIQbmP/fsRLOjUL+UYL0TxryX//0S4f/WRdgX8XoNQmEvsYbAshQ+VrHf5pqbyNPXZ/UlBt9kjKD57szJW13R2XtuXxH990FVfrvLioburtSN4Enr9JYsSjkFeiqcbI7aODY7kyto7ujBq5x+Y4nuwua+X93UYPguyuNDdQvQpWOYXrSPRSgCIKwRjSPQcaA3fHgL1Cbo+Xg8Xf9Qal6H5mH6frACwQoItrUXS2IksgSPXmenB44F0kq0QDHYUVMt+MoozYx8OhvuocCFEEQ1j6vqHkM8raxu/me/wK1OVoSqm6v+Ael6redzMPEwb3QwFUVSnHd1YJUl+R9bImePFMd3GsmkhTdygDHYc2RSrcjnUzy0qF65sde2vnGe7nlv6bp/5rqTW5r7/i/Ux3W9f/ygnMg/AFPJDle6O3rHxgcGh4ZBUAIRlAMJ8i/62INDMvxgijJyr9+4H92umFatuN6fhBGcZJmeVFWddN2/TBO87Ju+3Fe9/N+PwjBCIrhBEnRDMvxgijJiqrphmnZjuv5QRjFSZrlRVnVTdv1wzjNy7rtx+P5en++vz8EIyiGEyRFMyzHC6IkK6r2V5B+1TQt23E9PwijOEmzvCirumm7fhineVm3/Tiv+3k/oCgh9KI+1n7LSk03TMt2XM+HYATlcHl8gVAklkgxnCApmpHJFUqVWqM1NDI2MTULJqXcOrWQyGm5UR1ONt5+/ORW3ZVf3VPtPsYt8aGwhmOEdPTY8ZK4HuKckVwRMUh6VMxwktFzwzPfRcw1JFyya8UJL9pB3ILc0HrjXktqGL1dbLL3PqupN10M3rAYx3aVPGDlyPKAuNLAHeBoGkMoIS1TMFGx7aUjzUukRw9Sb1W+nEXFTRw5SlyVJZnrmJI7slaoqhbSk+FshbzWehtIGpKqgZVVUj1tLO5Y7yTv15I4TyoMxypF+1r9YYvJxNjWB7VDYeDDqnpOteJ6iwYbJBfaWDw7oR4kQa2oysUX0BiJ16tSGnqkTk+WyXpI65mUXWdsUDucWy+YWAYVN0PSdfRCTRFQukgP4n+uxa4kpUcKmjNIhxJvn2f0vHqMbEhbk1MFFQrBt1WUjAlpTtHVGxE/qg0c1dUki38aa7V+A0bizqGQmckx59Nn6ErhEl2ANF+IuGCnEnoK7L1b+MK1t71YlSn+gO7b2g8c4oqvEehY3H7z1LU8Z4IdZpqQR+I+9V7UnG7CkCxqz0Vdbxg4IzkuJUtbZacGa2Hfk+NOkRXv4yO6mPCKMDq246RaE3iGZPhWiZWLnogNNMnO/nrqrNeZ6l1TlgJwp2ElizMV55erxwx6o0XAM5EMhGxT9DBZbSvmSowzJCeWpBBw1Leez7HxCYknLTG5SGdwavkqQEdYouuVqvqldvikP35erjrlySxoLLUGeBIPuZGyDSMpQd7dhCfGSq62dJlqST9QJEhOz2oTxj2Oa+nBrcfxagOSAHGtvyoicVnvvE3jd0KeokVQW00X7PqS1I4qAEQ5fdorGrauJq6rBy/SbaBlDAG2K5FzYDBCesz5FNcaeVJkR+JLv2RJA3vqbKbVe1cCPhKrJ4mgxysU691h3KR70EqViU0pl3KyDg9YvjqDNCr3Uh2VqyQLEQ94qSzJfX/Id32uaZ3e3X4bvbXeklNJD35adqQU+P6AbR6vP2zuz/9uXo6fN++3XzYfl1/yI1g9HI8esYq4zrT7SJ1THFgJBwaBL2q9kbwIPrZ57pR2KBXsYwwhl5HVBv+MlTC2E1Qp+K2A6YFwoRqtFtCDO3qxudj+KTgqCQs0pFVdG03zDM4gLS6xNrRYoDSD5PDWg1KxGu1XteXQKpaZTj4jqLjVI2pr3pCkU+rytqUncReSwVAqdMTlYZx/bKwkcY3GEVIkBFRpCvbpQe7TzinRJBhoS1lM5E66eu5gB3Fpv5fk5J9vueXUCl72hpEHI5S1JK03Rsywhc+g7LWKJ926kYW2/3z6mYiXZpNpPkkD0qjez0pSiUFDR2tPikuZMwmTOByBrRHL9BXUONJlQU5Gq4svbcEIfdfJqk7WGRIDScuPUXphpA7rMRpJtBZlWOYwRThqdOTsWFxWXW7Y9RjFAwH5y1K/o7eGYnmjbaLeODnGQZEkgyjigUQXWBuODsP8FeaQzZH48qP3JC52LmoPbJhuoxI2H8f/6VAPxFBrlWFvzSe96K5pjh0cohcZRug6LV8ELbb+Pz6u2yg5haDUiWcWN3oUOMQhZZpk6yOt2q+/mpZl5AH1COMFjMkNatHJMhXLD4RnLMvgyGQN9sofKA5nkBjvJLqVrh5bFIFjDEjFgxk2P60GJYZ2HaOlvj/pxg8EUjmUCAw7NBTqW8VrRnYYMS03RZGMXEnSL+q0psTig65ojNLTTErzC1xwuMyWKkSeIDuc7U64ggujazUMjxGELBd787axJFmjmDtKLwI6wo2SYtB1Xg9qbUcFqcEmJCWLHLSYUXgMB1QLYwYhQsjdP4ND0e9pIRk5udTK0IMSy3PA/CSP88DRYnQuIU4R318njRFIyoc05IxYoRqkuBXiq/BGEqPqbIpGPzpbVFmDuqDMeJ4CX/SMkaNm8FXNLlc2C1MYAdvUctVtcYjpvBbCtotcHkgwSHaCLCnkg61FgtrNlY64NvbcAyVQfszGRM4vHQbCrlatpSKAlpONnKXom2x31np8tdivNWkYV/SyRpJCYU71TmxrN5bwyek/2ytvFOG7vzfggpYCVK4Yg2sWOqznEaNkYvGkBTeIcO0dh/31aeZvx+eJ6XX1kKLUxTw441ZO0rJqDBD2Uj5DhjXnXG09BSTzpNfeJ+ZZBC+yk/mOAZvEhBd9+j6MFAO91fEzsxSNO1J0D0QDqTSwa8TK37b0/dQpq/8x8E3oxvEzk2mESvZ9uCAgHG2vr/Rb/XwDAAAA') format('woff2'), + url('iconfont.woff?t=1567498326614') format('woff'), + url('iconfont.ttf?t=1567498326614') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ + url('iconfont.svg?t=1567498326614#iconfont') format('svg'); /* iOS 4.1- */ +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-logout:before { + content: "\e838"; +} + +.icon-facebook:before { + content: "\e645"; +} + +.icon-icons-google_groups:before { + content: "\ee07"; +} + +.icon-youtube:before { + content: "\e733"; +} + +.icon-tiaoshi:before { + content: "\eb61"; +} + +.icon-changjingguanli:before { + content: "\eb62"; +} + +.icon-bianji:before { + content: "\eb63"; +} + +.icon-guanlianshebei:before { + content: "\eb64"; +} + +.icon-guanfangbanben:before { + content: "\eb65"; +} + +.icon-gongnengdingyi:before { + content: "\eb66"; +} + +.icon-jichuguanli:before { + content: "\eb67"; +} + +.icon-jishufuwu:before { + content: "\eb68"; +} + +.icon-hezuohuobanmiyueguanli:before { + content: "\eb69"; +} + +.icon-ceshishenqing:before { + content: "\eb6a"; +} + +.icon-jiedianguanli:before { + content: "\eb6b"; +} + +.icon-jinggao:before { + content: "\eb6c"; +} + +.icon-peiwangyindao:before { + content: "\eb6d"; +} + +.icon-renjijiaohu:before { + content: "\eb6e"; +} + +.icon-shiyongwendang:before { + content: "\eb6f"; +} + +.icon-quanxianshenpi:before { + content: "\eb70"; +} + +.icon-yishouquan:before { + content: "\eb71"; +} + +.icon-tianshenpi:before { + content: "\eb72"; +} + +.icon-shujukanban:before { + content: "\eb73"; +} + +.icon-yingyongguanli:before { + content: "\eb74"; +} + +.icon-yibiaopan:before { + content: "\eb75"; +} + +.icon-zhanghaoquanxianguanli:before { + content: "\eb76"; +} + +.icon-yuanquyunwei:before { + content: "\eb77"; +} + +.icon-slack:before { + content: "\e641"; +} + +.icon-jizhanguanli:before { + content: "\eb78"; +} + +.icon-gongju:before { + content: "\e600"; +} + +.icon-guanbi:before { + content: "\eb79"; +} + +.icon-zidingyi:before { + content: "\eb7a"; +} + +.icon-xiajiantou:before { + content: "\eb7b"; +} + +.icon-shangjiantou:before { + content: "\eb7c"; +} + +.icon-icon_loading:before { + content: "\eb80"; +} + +.icon-icon_renwujincheng:before { + content: "\eb88"; +} + +.icon-icon_rukou:before { + content: "\eb89"; +} + +.icon-icon_yiwenkongxin:before { + content: "\eb8a"; +} + +.icon-icon_fabu:before { + content: "\eb8b"; +} + +.icon-icon_tianjia:before { + content: "\eb8c"; +} + +.icon-icon_yulan:before { + content: "\eb8d"; +} + +.icon-icon_zhanghao:before { + content: "\eb8e"; +} + +.icon-icon_wangye:before { + content: "\eb8f"; +} + +.icon-icon_shezhi:before { + content: "\eb90"; +} + +.icon-icon_baocun:before { + content: "\eb91"; +} + +.icon-icon_yingyongguanli:before { + content: "\eb92"; +} + +.icon-icon_shiyongwendang:before { + content: "\eb93"; +} + +.icon-icon_bangzhuwendang:before { + content: "\eb94"; +} + +.icon-biaodanzujian-shurukuang:before { + content: "\eb95"; +} + +.icon-biaodanzujian-biaoge:before { + content: "\eb96"; +} + +.icon-biaodanzujian-xialakuang:before { + content: "\eb97"; +} + +.icon-tubiao-bingtu:before { + content: "\eb98"; +} + +.icon-biaodanzujian-anniu:before { + content: "\eb99"; +} + +.icon-gongyezujian-yibiaopan:before { + content: "\eb9a"; +} + +.icon-tubiao-qiapian:before { + content: "\eb9b"; +} + +.icon-gongyezujian-zhishideng:before { + content: "\eb9c"; +} + +.icon-tubiao-zhexiantu:before { + content: "\eb9d"; +} + +.icon-xingzhuang-juxing:before { + content: "\eb9e"; +} + +.icon-xingzhuang-jianxing:before { + content: "\eb9f"; +} + +.icon-gongyezujian-kaiguan:before { + content: "\eba0"; +} + +.icon-tubiao-zhuzhuangtu:before { + content: "\eba1"; +} + +.icon-xingzhuang-tupian:before { + content: "\eba2"; +} + +.icon-xingzhuang-wenzi:before { + content: "\eba3"; +} + +.icon-xingzhuang-tuoyuanxing:before { + content: "\eba4"; +} + +.icon-xingzhuang-sanjiaoxing:before { + content: "\eba5"; +} + +.icon-xingzhuang-xingxing:before { + content: "\eba6"; +} + +.icon-guize:before { + content: "\ebb7"; +} + +.icon-shebeiguanli:before { + content: "\ebb8"; +} + +.icon-gongnengdingyi1:before { + content: "\ebb9"; +} + +.icon-jishufuwu1:before { + content: "\ebce"; +} + +.icon-yunyingzhongxin:before { + content: "\ebd0"; +} + +.icon-yunyingguanli:before { + content: "\ebd1"; +} + +.icon-zuzhixiaxia:before { + content: "\ebd8"; +} + +.icon-zuzhizhankai:before { + content: "\ebd9"; +} + +.icon-zuzhiqunzu:before { + content: "\ebda"; +} + +.icon-dakai:before { + content: "\ebdf"; +} + +.icon-yingwen:before { + content: "\ebe0"; +} + +.icon-zhongwen:before { + content: "\ebe2"; +} + +.icon-miwen:before { + content: "\ebe3"; +} + +.icon-xianhao:before { + content: "\ebe4"; +} + +.icon-kongxinduigou:before { + content: "\ebe5"; +} + +.icon-huixingzhen:before { + content: "\ebe6"; +} + +.icon-duigou:before { + content: "\ebe7"; +} + +.icon-stack-overflow:before { + content: "\e970"; +} + +.icon-xiayibu:before { + content: "\ebef"; +} + +.icon-shangyibu:before { + content: "\ebf0"; +} + +.icon-kongjianxuanzhong:before { + content: "\ebf1"; +} + +.icon-kongjianweixuan:before { + content: "\ebf2"; +} + +.icon-kongjianyixuan:before { + content: "\ebf3"; +} + +.icon--diangan:before { + content: "\ebfb"; +} + +.icon-rongxuejirongjiechi:before { + content: "\ebfc"; +} + +.icon-lubiantingchechang:before { + content: "\ebfd"; +} + +.icon--lumingpai:before { + content: "\ebfe"; +} + +.icon-jietouzuoyi:before { + content: "\ebff"; +} + +.icon--zhongdaweixian:before { + content: "\ec00"; +} + +.icon--jiaotongbiaozhipai:before { + content: "\ec01"; +} + +.icon-gongcezhishipai:before { + content: "\ec02"; +} + +.icon-fangkuai:before { + content: "\ec06"; +} + +.icon-fangkuai-:before { + content: "\ec07"; +} + +.icon-shuaxin:before { + content: "\ec08"; +} + +.icon-baocun:before { + content: "\ec09"; +} + +.icon-fabu:before { + content: "\ec0a"; +} + +.icon-xiayibu1:before { + content: "\ec0b"; +} + +.icon-shangyibu1:before { + content: "\ec0c"; +} + +.icon-xiangxiazhanhang:before { + content: "\ec0d"; +} + +.icon-xiangshangzhanhang:before { + content: "\ec0e"; +} + +.icon-tupianjiazaishibai:before { + content: "\ec0f"; +} + +.icon-fuwudiqiu:before { + content: "\ec10"; +} + +.icon-suoxiao:before { + content: "\ec13"; +} + +.icon-fangda:before { + content: "\ec14"; +} + +.icon-huanyuanhuabu:before { + content: "\ec15"; +} + +.icon-quanping:before { + content: "\ec16"; +} + +.icon-biaodanzujian-biaoge1:before { + content: "\ec17"; +} + +.icon-APIshuchu:before { + content: "\ec18"; +} + +.icon-APIjieru:before { + content: "\ec19"; +} + +.icon-wenjianjia:before { + content: "\ec1a"; +} + +.icon-DOC:before { + content: "\ec1b"; +} + +.icon-BMP:before { + content: "\ec1c"; +} + +.icon-GIF:before { + content: "\ec1d"; +} + +.icon-JPG:before { + content: "\ec1e"; +} + +.icon-PNG:before { + content: "\ec1f"; +} + +.icon-weizhigeshi:before { + content: "\ec20"; +} + +.icon-gengduo:before { + content: "\ec21"; +} + +.icon-yunduanxiazai:before { + content: "\ec22"; +} + +.icon-yunduanshangchuan:before { + content: "\ec23"; +} + +.icon-dian:before { + content: "\ec24"; +} + +.icon-mian:before { + content: "\ec25"; +} + +.icon-xian:before { + content: "\ec26"; +} + +.icon-shebeizhuangtai:before { + content: "\ec27"; +} + +.icon-fenzuguanli:before { + content: "\ec28"; +} + +.icon-kuaisubianpai:before { + content: "\ec29"; +} + +.icon-APPkaifa:before { + content: "\ec2a"; +} + +.icon-wentijieda:before { + content: "\ec2e"; +} + +.icon-kefu:before { + content: "\ec2f"; +} + +.icon-ruanjiankaifabao:before { + content: "\ec30"; +} + +.icon-sousuobianxiao:before { + content: "\ec32"; +} + +.icon-sousuofangda:before { + content: "\ec33"; +} + +.icon-dingwei:before { + content: "\ec34"; +} + +.icon-wumoxing:before { + content: "\ec35"; +} + +.icon-gaojing:before { + content: "\ec36"; +} + +.icon-renwujincheng:before { + content: "\ec37"; +} + +.icon-xiaoxitongzhi:before { + content: "\ec38"; +} + +.icon-youhui:before { + content: "\ec39"; +} + +.icon-gaojing1:before { + content: "\ec3a"; +} + +.icon-zhihangfankui:before { + content: "\ec3b"; +} + +.icon-gongdanqueren:before { + content: "\ec3c"; +} + +.icon-guangbo:before { + content: "\ec3d"; +} + +.icon-gongdan:before { + content: "\ec3e"; +} + +.icon-xiaoxi:before { + content: "\ec3f"; +} + +.icon-ditu-qi:before { + content: "\ec40"; +} + +.icon-ditu-dibiao:before { + content: "\ec41"; +} + +.icon-ditu-cha:before { + content: "\ec42"; +} + +.icon-ditu-qipao:before { + content: "\ec43"; +} + +.icon-ditu-tuding:before { + content: "\ec44"; +} + +.icon-ditu-huan:before { + content: "\ec45"; +} + +.icon-ditu-xing:before { + content: "\ec46"; +} + +.icon-ditu-yuan:before { + content: "\ec47"; +} + +.icon-chehuisekuai:before { + content: "\ec48"; +} + +.icon-shanchusekuai:before { + content: "\ec49"; +} + +.icon-fabusekuai:before { + content: "\ec4a"; +} + +.icon-xinhao:before { + content: "\ec4b"; +} + +.icon-lanya:before { + content: "\ec4c"; +} + +.icon-Wi-Fi:before { + content: "\ec4d"; +} + +.icon-chaxun:before { + content: "\ec4e"; +} + +.icon-dianbiao:before { + content: "\ec4f"; +} + +.icon-anquan:before { + content: "\ec50"; +} + +.icon-daibanshixiang:before { + content: "\ec51"; +} + +.icon-bingxiang:before { + content: "\ec52"; +} + +.icon-fanshe:before { + content: "\ec53"; +} + +.icon-fengche:before { + content: "\ec54"; +} + +.icon-guandao:before { + content: "\ec55"; +} + +.icon-guize1:before { + content: "\ec56"; +} + +.icon-guizeyinqing:before { + content: "\ec57"; +} + +.icon-huowudui:before { + content: "\ec58"; +} + +.icon-jianceqi:before { + content: "\ec59"; +} + +.icon-jinggai:before { + content: "\ec5a"; +} + +.icon-liujisuan:before { + content: "\ec5b"; +} + +.icon-hanshu:before { + content: "\ec5c"; +} + +.icon-lianjieliu:before { + content: "\ec5d"; +} + +.icon-ludeng:before { + content: "\ec5e"; +} + +.icon-shexiangji:before { + content: "\ec5f"; +} + +.icon-rentijiance:before { + content: "\ec60"; +} + +.icon-moshubang:before { + content: "\ec61"; +} + +.icon-shujuwajue:before { + content: "\ec62"; +} + +.icon-wangguan:before { + content: "\ec63"; +} + +.icon-shenjing:before { + content: "\ec64"; +} + +.icon-chucun:before { + content: "\ec65"; +} + +.icon-wuguan:before { + content: "\ec66"; +} + +.icon-yunduanshuaxin:before { + content: "\ec67"; +} + +.icon-yunhang:before { + content: "\ec68"; +} + +.icon-luyouqi:before { + content: "\ec69"; +} + +.icon-bug:before { + content: "\ec6a"; +} + +.icon-get:before { + content: "\ec6b"; +} + +.icon-PIR:before { + content: "\ec6c"; +} + +.icon-zhexiantu:before { + content: "\ec6d"; +} + +.icon-shuibiao:before { + content: "\ec6e"; +} + +.icon-js:before { + content: "\ec6f"; +} + +.icon-zihangche:before { + content: "\ec70"; +} + +.icon-liebiao:before { + content: "\ec71"; +} + +.icon-qichedingwei:before { + content: "\ec72"; +} + +.icon-dici:before { + content: "\ec73"; +} + +.icon-mysql:before { + content: "\ec74"; +} + +.icon-qiche:before { + content: "\ec75"; +} + +.icon-shenjing1:before { + content: "\ec76"; +} + +.icon-chengshi:before { + content: "\ec77"; +} + +.icon-tixingshixin:before { + content: "\ec78"; +} + +.icon-menci:before { + content: "\ec79"; +} + +.icon-chazuo:before { + content: "\ec7a"; +} + +.icon-ranqijianceqi:before { + content: "\ec7b"; +} + +.icon-kaiguan:before { + content: "\ec7c"; +} + +.icon-chatou:before { + content: "\ec7d"; +} + +.icon-xiyiji:before { + content: "\ec7e"; +} + +.icon-yijiankaiguan:before { + content: "\ec7f"; +} + +.icon-yanwubaojingqi:before { + content: "\ec80"; +} + +.icon-wuxiandianbo:before { + content: "\ec81"; +} + +.icon-fuzhi:before { + content: "\ec82"; +} + +.icon-tuite:before { + content: "\e615"; +} + +.icon-shanchu:before { + content: "\ec83"; +} + +.icon-bianjisekuai:before { + content: "\ec84"; +} + +.icon-ishipinshixiao:before { + content: "\ec85"; +} + +.icon-iframetianjia:before { + content: "\ec86"; +} + +.icon-tupiantianjia:before { + content: "\ec87"; +} + +.icon-liebiaomoshi_kuai:before { + content: "\ec88"; +} + +.icon-qiapianmoshi_kuai:before { + content: "\ec89"; +} + +.icon-fenlan:before { + content: "\ec8a"; +} + +.icon-fengexian:before { + content: "\ec8b"; +} + +.icon-dianzan:before { + content: "\ec8c"; +} + +.icon-charulianjie:before { + content: "\ec8d"; +} + +.icon-charutupian:before { + content: "\ec8e"; +} + +.icon-quxiaolianjie:before { + content: "\ec8f"; +} + +.icon-wuxupailie:before { + content: "\ec90"; +} + +.icon-juzhongduiqi:before { + content: "\ec91"; +} + +.icon-yinyong:before { + content: "\ec92"; +} + +.icon-youxupailie:before { + content: "\ec93"; +} + +.icon-youduiqi:before { + content: "\ec94"; +} + +.icon-zitidaima:before { + content: "\ec95"; +} + +.icon-xiaolian:before { + content: "\ec96"; +} + +.icon-zitijiacu:before { + content: "\ec97"; +} + +.icon-zitishanchuxian:before { + content: "\ec98"; +} + +.icon-zitishangbiao:before { + content: "\ec99"; +} + +.icon-zitibiaoti:before { + content: "\ec9a"; +} + +.icon-zitixiahuaxian:before { + content: "\ec9b"; +} + +.icon-zitixieti:before { + content: "\ec9c"; +} + +.icon-zitiyanse:before { + content: "\ec9d"; +} + +.icon-zuoduiqi:before { + content: "\ec9e"; +} + +.icon-zitiyulan:before { + content: "\ec9f"; +} + +.icon-zitixiabiao:before { + content: "\eca0"; +} + +.icon-zuoyouduiqi:before { + content: "\eca1"; +} + +.icon-tianxie:before { + content: "\eca2"; +} + +.icon-huowudui1:before { + content: "\eca3"; +} + +.icon-yingjian:before { + content: "\eca4"; +} + +.icon-shebeikaifa:before { + content: "\eca5"; +} + +.icon-dianzan_kuai:before { + content: "\eca6"; +} + +.icon-zhihuan:before { + content: "\eca7"; +} + +.icon-tuoguan:before { + content: "\eca8"; +} + +.icon-duigoux:before { + content: "\eca9"; +} + +.icon-guanbi1:before { + content: "\ecaa"; +} + +.icon-aixin_shixin:before { + content: "\ecab"; +} + +.icon-ranqixieloubaojingqi:before { + content: "\ecac"; +} + +.icon-dianbiao_shiti:before { + content: "\ecad"; +} + +.icon-aixin:before { + content: "\ecae"; +} + +.icon-shuibiao_shiti:before { + content: "\ecaf"; +} + +.icon-zhinengxiaofangshuan:before { + content: "\ecb0"; +} + +.icon-ranqibiao_shiti:before { + content: "\ecb1"; +} + +.icon-shexiangtou_shiti:before { + content: "\ecb2"; +} + +.icon-shexiangtou_guanbi:before { + content: "\ecb3"; +} + +.icon-shexiangtou:before { + content: "\ecb4"; +} + +.icon-shengyin_shiti:before { + content: "\ecb5"; +} + +.icon-shengyinkai:before { + content: "\ecb6"; +} + +.icon-shoucang_shixin:before { + content: "\ecb7"; +} + +.icon-shoucang:before { + content: "\ecb8"; +} + +.icon-shengyinwu:before { + content: "\ecb9"; +} + +.icon-shengyinjingyin:before { + content: "\ecba"; +} + +.icon-zhunbeiliangchan:before { + content: "\ecbb"; +} + +.icon-shebeikaifa1:before { + content: "\ecbc"; +} + +.icon-document-circle:before { + content: "\e6b8"; +} + +.icon-git:before { + content: "\e64a"; +} + +.icon-Notificationlisttongzhiliebiao:before { + content: "\e782"; +} + +.icon-kongxinwenhao:before { + content: "\ed19"; +} + +.icon-cuowukongxin:before { + content: "\ed1a"; +} + +.icon-fangkuai1:before { + content: "\ed1b"; +} + +.icon-fangkuai2:before { + content: "\ed1c"; +} + +.icon-kongjianxuanzhong1:before { + content: "\ed1d"; +} + +.icon-kongxinduigou1:before { + content: "\ed1e"; +} + +.icon-xinxikongxin:before { + content: "\ed1f"; +} + +.icon-kongjian:before { + content: "\ed20"; +} + +.icon-gaojingkongxin:before { + content: "\ed21"; +} + +.icon-duigou_kuai:before { + content: "\ed22"; +} + +.icon-cuocha_kuai:before { + content: "\ed23"; +} + +.icon-jia_sekuai:before { + content: "\ed24"; +} + +.icon-jian_sekuai:before { + content: "\ed25"; +} + +.icon-fenxiangfangshi:before { + content: "\ed2e"; +} + +.icon-i18n:before { + content: "\e614"; +} + +.icon-gongju1:before { + content: "\e637"; +} + +.icon-bangzhu:before { + content: "\e638"; +} + +.icon-xitong:before { + content: "\e639"; +} + +.icon-guanli:before { + content: "\e63a"; +} + +.icon-jiankong:before { + content: "\e63b"; +} + +.icon-guize2:before { + content: "\e63c"; +} + +.icon-arrow:before { + content: "\e63d"; +} + +.icon-systemname:before { + content: "\e63e"; +} + +.icon-version:before { + content: "\e63f"; +} + +.icon-Systemtime:before { + content: "\e640"; +} + +.icon-uptime:before { + content: "\e642"; +} + +.icon-in:before { + content: "\e643"; +} + diff --git a/apps/emqx_dashboard/priv/www/static/css/iconfont.eot b/apps/emqx_dashboard/priv/www/static/css/iconfont.eot new file mode 100644 index 000000000..0ddb250fc Binary files /dev/null and b/apps/emqx_dashboard/priv/www/static/css/iconfont.eot differ diff --git a/apps/emqx_dashboard/priv/www/static/css/iconfont.js b/apps/emqx_dashboard/priv/www/static/css/iconfont.js new file mode 100644 index 000000000..801ac4009 --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/css/iconfont.js @@ -0,0 +1 @@ +!function(v){var a,l='',h=(a=document.getElementsByTagName("script"))[a.length-1].getAttribute("data-injectcss");if(h&&!v.__iconfont__svg__cssinject__){v.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}!function(a){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(a,0);else{var h=function(){document.removeEventListener("DOMContentLoaded",h,!1),a()};document.addEventListener("DOMContentLoaded",h,!1)}else document.attachEvent&&(c=a,i=v.document,o=!1,(z=function(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(z,50)}l()})(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,l())});function l(){o||(o=!0,c())}var c,i,o,z}(function(){var a,h;(a=document.createElement("div")).innerHTML=l,l=null,(h=a.getElementsByTagName("svg")[0])&&(h.setAttribute("aria-hidden","true"),h.style.position="absolute",h.style.width=0,h.style.height=0,h.style.overflow="hidden",function(a,h){h.firstChild?function(a,h){h.parentNode.insertBefore(a,h)}(a,h.firstChild):h.appendChild(a)}(h,document.body))})}(window); \ No newline at end of file diff --git a/apps/emqx_dashboard/priv/www/static/css/iconfont.svg b/apps/emqx_dashboard/priv/www/static/css/iconfont.svg new file mode 100644 index 000000000..bb47f2b06 --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/css/iconfont.svg @@ -0,0 +1,941 @@ + + + + + +Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/emqx_dashboard/priv/www/static/css/iconfont.ttf b/apps/emqx_dashboard/priv/www/static/css/iconfont.ttf new file mode 100644 index 000000000..e0772f2bc Binary files /dev/null and b/apps/emqx_dashboard/priv/www/static/css/iconfont.ttf differ diff --git a/apps/emqx_dashboard/priv/www/static/css/iconfont.woff b/apps/emqx_dashboard/priv/www/static/css/iconfont.woff new file mode 100644 index 000000000..b0bae09a5 Binary files /dev/null and b/apps/emqx_dashboard/priv/www/static/css/iconfont.woff differ diff --git a/apps/emqx_dashboard/priv/www/static/css/iconfont.woff2 b/apps/emqx_dashboard/priv/www/static/css/iconfont.woff2 new file mode 100644 index 000000000..538f1da1c Binary files /dev/null and b/apps/emqx_dashboard/priv/www/static/css/iconfont.woff2 differ diff --git a/apps/emqx_dashboard/priv/www/static/editor.worker.js b/apps/emqx_dashboard/priv/www/static/editor.worker.js new file mode 100644 index 000000000..b596dd49a --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/editor.worker.js @@ -0,0 +1 @@ +!function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s="/p8O")}({"/p8O":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=new(function(){function e(){this.listeners=[],this.unexpectedErrorHandler=function(e){setTimeout(function(){if(e.stack)throw new Error(e.message+"\n\n"+e.stack);throw e},0)}}return e.prototype.emit=function(e){this.listeners.forEach(function(t){t(e)})},e.prototype.onUnexpectedError=function(e){this.unexpectedErrorHandler(e),this.emit(e)},e.prototype.onUnexpectedExternalError=function(e){this.unexpectedErrorHandler(e)},e}());function i(e){u(e)||r.onUnexpectedError(e)}function o(e){return e instanceof Error?{$isError:!0,name:e.name,message:e.message,stack:e.stacktrace||e.stack}:e}var s="Canceled";function u(e){return e instanceof Error&&e.name===s&&e.message===s}var a=!1,l="__is_disposable_tracked__";function h(e){if(a&&e&&e!==m.None)try{e[l]=!0}catch(e){}}function c(e){if(!a)return e;var t=new Error("Potentially leaked disposable").stack;return setTimeout(function(){e[l]||console.log(t)},3e3),e}function f(){for(var e=[],t=0;tr?e[a]=o[u++]:u>i?e[a]=o[s++]:t(o[u],o[s])<0?e[a]=o[u++]:e[a]=o[s++]}(t,n,r,s,i,o)}(e,t,0,e.length-1,[]),e}var E=function(){function e(e,t,n,r){this.originalStart=e,this.originalLength=t,this.modifiedStart=n,this.modifiedLength=r}return e.prototype.getOriginalEnd=function(){return this.originalStart+this.originalLength},e.prototype.getModifiedEnd=function(){return this.modifiedStart+this.modifiedLength},e}();function S(e){return{getLength:function(){return e.length},getElementAtIndex:function(t){return e.charCodeAt(t)}}}function w(e,t,n){return new T(S(e),S(t)).ComputeDiff(n)}var A,P=function(){function e(){}return e.Assert=function(e,t){if(!e)throw new Error(t)},e}(),M=function(){function e(){}return e.Copy=function(e,t,n,r,i){for(var o=0;o0||this.m_modifiedCount>0)&&this.m_changes.push(new E(this.m_originalStart,this.m_originalCount,this.m_modifiedStart,this.m_modifiedCount)),this.m_originalCount=0,this.m_modifiedCount=0,this.m_originalStart=Number.MAX_VALUE,this.m_modifiedStart=Number.MAX_VALUE},e.prototype.AddOriginalElement=function(e,t){this.m_originalStart=Math.min(this.m_originalStart,e),this.m_modifiedStart=Math.min(this.m_modifiedStart,t),this.m_originalCount++},e.prototype.AddModifiedElement=function(e,t){this.m_originalStart=Math.min(this.m_originalStart,e),this.m_modifiedStart=Math.min(this.m_modifiedStart,t),this.m_modifiedCount++},e.prototype.getChanges=function(){return(this.m_originalCount>0||this.m_modifiedCount>0)&&this.MarkNextChange(),this.m_changes},e.prototype.getReverseChanges=function(){return(this.m_originalCount>0||this.m_modifiedCount>0)&&this.MarkNextChange(),this.m_changes.reverse(),this.m_changes},e}(),T=function(){function e(e,t,n){void 0===n&&(n=null),this.OriginalSequence=e,this.ModifiedSequence=t,this.ContinueProcessingPredicate=n,this.m_forwardHistory=[],this.m_reverseHistory=[]}return e.prototype.ElementsAreEqual=function(e,t){return this.OriginalSequence.getElementAtIndex(e)===this.ModifiedSequence.getElementAtIndex(t)},e.prototype.OriginalElementsAreEqual=function(e,t){return this.OriginalSequence.getElementAtIndex(e)===this.OriginalSequence.getElementAtIndex(t)},e.prototype.ModifiedElementsAreEqual=function(e,t){return this.ModifiedSequence.getElementAtIndex(e)===this.ModifiedSequence.getElementAtIndex(t)},e.prototype.ComputeDiff=function(e){return this._ComputeDiff(0,this.OriginalSequence.getLength()-1,0,this.ModifiedSequence.getLength()-1,e)},e.prototype._ComputeDiff=function(e,t,n,r,i){var o=this.ComputeDiffRecursive(e,t,n,r,[!1]);return i?this.PrettifyChanges(o):o},e.prototype.ComputeDiffRecursive=function(e,t,n,r,i){for(i[0]=!1;e<=t&&n<=r&&this.ElementsAreEqual(e,n);)e++,n++;for(;t>=e&&r>=n&&this.ElementsAreEqual(t,r);)t--,r--;if(e>t||n>r){var o=void 0;return n<=r?(P.Assert(e===t+1,"originalStart should only be one more than originalEnd"),o=[new E(e,0,n,r-n+1)]):e<=t?(P.Assert(n===r+1,"modifiedStart should only be one more than modifiedEnd"),o=[new E(e,t-e+1,n,0)]):(P.Assert(e===t+1,"originalStart should only be one more than originalEnd"),P.Assert(n===r+1,"modifiedStart should only be one more than modifiedEnd"),o=[]),o}var s=[0],u=[0],a=this.ComputeRecursionPoint(e,t,n,r,s,u,i),l=s[0],h=u[0];if(null!==a)return a;if(!i[0]){var c=this.ComputeDiffRecursive(e,l,n,h,i),f=[];return f=i[0]?[new E(l+1,t-(l+1)+1,h+1,r-(h+1)+1)]:this.ComputeDiffRecursive(l+1,t,h+1,r,i),this.ConcatenateChanges(c,f)}return[new E(e,t-e+1,n,r-n+1)]},e.prototype.WALKTRACE=function(e,t,n,r,i,o,s,u,a,l,h,c,f,d,m,p,g,_){var v,y,C=null,b=new O,L=t,N=n,S=f[0]-p[0]-r,w=Number.MIN_VALUE,A=this.m_forwardHistory.length-1;do{(y=S+e)===L||y=0&&(e=(a=this.m_forwardHistory[A])[0],L=1,N=a.length-1)}while(--A>=-1);if(v=b.getReverseChanges(),_[0]){var P=f[0]+1,M=p[0]+1;if(null!==v&&v.length>0){var T=v[v.length-1];P=Math.max(P,T.getOriginalEnd()),M=Math.max(M,T.getModifiedEnd())}C=[new E(P,c-P+1,M,m-M+1)]}else{b=new O,L=o,N=s,S=f[0]-p[0]-u,w=Number.MAX_VALUE,A=g?this.m_reverseHistory.length-1:this.m_reverseHistory.length-2;do{(y=S+i)===L||y=l[y+1]?(d=(h=l[y+1]-1)-S-u,h>w&&b.MarkNextChange(),w=h+1,b.AddOriginalElement(h+1,d+1),S=y+1-i):(d=(h=l[y-1])-S-u,h>w&&b.MarkNextChange(),w=h,b.AddModifiedElement(h+1,d+1),S=y-1-i),A>=0&&(i=(l=this.m_reverseHistory[A])[0],L=1,N=l.length-1)}while(--A>=-1);C=b.getChanges()}return this.ConcatenateChanges(v,C)},e.prototype.ComputeRecursionPoint=function(e,t,n,r,i,o,s){var u,a=0,l=0,h=0,c=0,f=0,d=0;e--,n--,i[0]=0,o[0]=0,this.m_forwardHistory=[],this.m_reverseHistory=[];var m,p,g=t-e+(r-n),_=g+1,v=new Array(_),y=new Array(_),C=r-n,b=t-e,L=e-n,N=t-r,S=(b-C)%2==0;for(v[C]=e,y[b]=t,s[0]=!1,u=1;u<=g/2+1;u++){var w=0,A=0;for(h=this.ClipDiagonalBound(C-u,u,C,_),c=this.ClipDiagonalBound(C+u,u,C,_),m=h;m<=c;m+=2){for(l=(a=m===h||mw+A&&(w=a,A=l),!S&&Math.abs(m-b)<=u-1&&a>=y[m])return i[0]=a,o[0]=l,p<=y[m]&&u<=1448?this.WALKTRACE(C,h,c,L,b,f,d,N,v,y,a,t,i,l,r,o,S,s):null}var P=(w-e+(A-n)-u)/2;if(null!==this.ContinueProcessingPredicate&&!this.ContinueProcessingPredicate(w,this.OriginalSequence,P))return s[0]=!0,i[0]=w,o[0]=A,P>0&&u<=1448?this.WALKTRACE(C,h,c,L,b,f,d,N,v,y,a,t,i,l,r,o,S,s):[new E(++e,t-e+1,++n,r-n+1)];for(f=this.ClipDiagonalBound(b-u,u,b,_),d=this.ClipDiagonalBound(b+u,u,b,_),m=f;m<=d;m+=2){for(l=(a=m===f||m=y[m+1]?y[m+1]-1:y[m-1])-(m-b)-N,p=a;a>e&&l>n&&this.ElementsAreEqual(a,l);)a--,l--;if(y[m]=a,S&&Math.abs(m-C)<=u&&a<=v[m])return i[0]=a,o[0]=l,p>=v[m]&&u<=1448?this.WALKTRACE(C,h,c,L,b,f,d,N,v,y,a,t,i,l,r,o,S,s):null}if(u<=1447){var O=new Array(c-h+2);O[0]=C-h+1,M.Copy(v,h,O,1,c-h+1),this.m_forwardHistory.push(O),(O=new Array(d-f+2))[0]=b-f+1,M.Copy(y,f,O,1,d-f+1),this.m_reverseHistory.push(O)}}return this.WALKTRACE(C,h,c,L,b,f,d,N,v,y,a,t,i,l,r,o,S,s)},e.prototype.PrettifyChanges=function(e){for(var t=0;t0,s=n.modifiedLength>0;n.originalStart+n.originalLength=0;t--){n=e[t],r=0,i=0;if(t>0){var a=e[t-1];a.originalLength>0&&(r=a.originalStart+a.originalLength),a.modifiedLength>0&&(i=a.modifiedStart+a.modifiedLength)}o=n.originalLength>0,s=n.modifiedLength>0;for(var l=0,h=this._boundaryScore(n.originalStart,n.originalLength,n.modifiedStart,n.modifiedLength),c=1;;c++){var f=n.originalStart-c,d=n.modifiedStart-c;if(fh&&(h=m,l=c)}n.originalStart-=l,n.modifiedStart-=l}return e},e.prototype._OriginalIsBoundary=function(e){if(e<=0||e>=this.OriginalSequence.getLength()-1)return!0;var t=this.OriginalSequence.getElementAtIndex(e);return"string"==typeof t&&/^\s*$/.test(t)},e.prototype._OriginalRegionIsBoundary=function(e,t){if(this._OriginalIsBoundary(e)||this._OriginalIsBoundary(e-1))return!0;if(t>0){var n=e+t;if(this._OriginalIsBoundary(n-1)||this._OriginalIsBoundary(n))return!0}return!1},e.prototype._ModifiedIsBoundary=function(e){if(e<=0||e>=this.ModifiedSequence.getLength()-1)return!0;var t=this.ModifiedSequence.getElementAtIndex(e);return"string"==typeof t&&/^\s*$/.test(t)},e.prototype._ModifiedRegionIsBoundary=function(e,t){if(this._ModifiedIsBoundary(e)||this._ModifiedIsBoundary(e-1))return!0;if(t>0){var n=e+t;if(this._ModifiedIsBoundary(n-1)||this._ModifiedIsBoundary(n))return!0}return!1},e.prototype._boundaryScore=function(e,t,n,r){return(this._OriginalRegionIsBoundary(e,t)?1:0)+(this._ModifiedRegionIsBoundary(n,r)?1:0)},e.prototype.ConcatenateChanges=function(e,t){var n=[];if(0===e.length||0===t.length)return t.length>0?t:e;if(this.ChangesOverlap(e[e.length-1],t[0],n)){var r=new Array(e.length+t.length-1);return M.Copy(e,0,r,0,e.length-1),r[e.length-1]=n[0],M.Copy(t,1,r,e.length,t.length-1),r}r=new Array(e.length+t.length);return M.Copy(e,0,r,0,e.length),M.Copy(t,0,r,e.length,t.length),r},e.prototype.ChangesOverlap=function(e,t,n){if(P.Assert(e.originalStart<=t.originalStart,"Left change is not less than or equal to right change"),P.Assert(e.modifiedStart<=t.modifiedStart,"Left change is not less than or equal to right change"),e.originalStart+e.originalLength>=t.originalStart||e.modifiedStart+e.modifiedLength>=t.modifiedStart){var r=e.originalStart,i=e.originalLength,o=e.modifiedStart,s=e.modifiedLength;return e.originalStart+e.originalLength>=t.originalStart&&(i=t.originalStart+t.originalLength-e.originalStart),e.modifiedStart+e.modifiedLength>=t.modifiedStart&&(s=t.modifiedStart+t.modifiedLength-e.modifiedStart),n[0]=new E(r,i,o,s),!0}return n[0]=null,!1},e.prototype.ClipDiagonalBound=function(e,t,n,r){if(e>=0&&e=n?I:{done:!1,value:e[t++]}}}},e.from=function(t){return t?Array.isArray(t)?e.fromArray(t):t:e.empty()},e.map=function(e,t){return{next:function(){var n=e.next();return n.done?I:{done:!1,value:t(n.value)}}}},e.filter=function(e,t){return{next:function(){for(;;){var n=e.next();if(n.done)return I;if(t(n.value))return{done:!1,value:n.value}}}}},e.forEach=function(e,t){for(var n=e.next();!n.done;n=e.next())t(n.value)},e.collect=function(e,t){void 0===t&&(t=Number.POSITIVE_INFINITY);var n=[];if(0===t)return n;for(var r=0,i=e.next();!(i.done||(n.push(i.value),++r>=t));i=e.next());return n},e.concat=function(){for(var e=[],t=0;t=e.length)return I;var t=e[n].next();return t.done?(n++,this.next()):t}}}}(A||(A={}));(function(e){function t(t,n,r,i){return void 0===n&&(n=0),void 0===r&&(r=t.length),void 0===i&&(i=n-1),e.call(this,t,n,r,i)||this}x(t,e),t.prototype.current=function(){return e.prototype.current.call(this)},t.prototype.previous=function(){return this.index=Math.max(this.index-1,this.start-1),this.current()},t.prototype.first=function(){return this.index=this.start,this.current()},t.prototype.last=function(){return this.index=this.end-1,this.current()},t.prototype.parent=function(){return null}})(function(){function e(e,t,n,r){void 0===t&&(t=0),void 0===n&&(n=e.length),void 0===r&&(r=t-1),this.items=e,this.start=t,this.end=n,this.index=r}return e.prototype.first=function(){return this.index=this.start,this.current()},e.prototype.next=function(){return this.index=Math.min(this.index+1,this.end),this.current()},e.prototype.current=function(){return this.index===this.start-1||this.index===this.end?null:this.items[this.index]},e}()),function(){function e(e,t){this.iterator=e,this.fn=t}e.prototype.next=function(){return this.fn(this.iterator.next())}}();var R,U=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),k=/^\w[\w\d+.-]*$/,D=/^\//,F=/^\/\//,K=!0;var q="",V="/",B=/^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/,Y=function(){function e(e,t,n,r,i,o){void 0===o&&(o=!1),"object"==typeof e?(this.scheme=e.scheme||q,this.authority=e.authority||q,this.path=e.path||q,this.query=e.query||q,this.fragment=e.fragment||q):(this.scheme=function(e,t){return t||K?e||q:(e||(console.trace("BAD uri lacks scheme, falling back to file-scheme."),e="file"),e)}(e,o),this.authority=t||q,this.path=function(e,t){switch(e){case"https":case"http":case"file":t?t[0]!==V&&(t=V+t):t=V}return t}(this.scheme,n||q),this.query=r||q,this.fragment=i||q,function(e,t){if(!e.scheme){if(t||K)throw new Error('[UriError]: Scheme is missing: {scheme: "", authority: "'+e.authority+'", path: "'+e.path+'", query: "'+e.query+'", fragment: "'+e.fragment+'"}');console.warn('[UriError]: Scheme is missing: {scheme: "", authority: "'+e.authority+'", path: "'+e.path+'", query: "'+e.query+'", fragment: "'+e.fragment+'"}')}if(e.scheme&&!k.test(e.scheme))throw new Error("[UriError]: Scheme contains illegal characters.");if(e.path)if(e.authority){if(!D.test(e.path))throw new Error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character')}else if(F.test(e.path))throw new Error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")')}(this,o))}return e.isUri=function(t){return t instanceof e||!!t&&("string"==typeof t.authority&&"string"==typeof t.fragment&&"string"==typeof t.path&&"string"==typeof t.query&&"string"==typeof t.scheme&&"function"==typeof t.fsPath&&"function"==typeof t.with&&"function"==typeof t.toString)},Object.defineProperty(e.prototype,"fsPath",{get:function(){return z(this)},enumerable:!0,configurable:!0}),e.prototype.with=function(e){if(!e)return this;var t=e.scheme,n=e.authority,r=e.path,i=e.query,o=e.fragment;return void 0===t?t=this.scheme:null===t&&(t=q),void 0===n?n=this.authority:null===n&&(n=q),void 0===r?r=this.path:null===r&&(r=q),void 0===i?i=this.query:null===i&&(i=q),void 0===o?o=this.fragment:null===o&&(o=q),t===this.scheme&&n===this.authority&&r===this.path&&i===this.query&&o===this.fragment?this:new W(t,n,r,i,o)},e.parse=function(e,t){void 0===t&&(t=!1);var n=B.exec(e);return n?new W(n[2]||q,decodeURIComponent(n[4]||q),decodeURIComponent(n[5]||q),decodeURIComponent(n[7]||q),decodeURIComponent(n[9]||q),t):new W(q,q,q,q,q)},e.file=function(e){var t=q;if(p.c&&(e=e.replace(/\\/g,V)),e[0]===V&&e[1]===V){var n=e.indexOf(V,2);-1===n?(t=e.substring(2),e=V):(t=e.substring(2,n),e=e.substring(n)||V)}return new W("file",t,e,q,q)},e.from=function(e){return new W(e.scheme,e.authority,e.path,e.query,e.fragment)},e.prototype.toString=function(e){return void 0===e&&(e=!1),X(this,e)},e.prototype.toJSON=function(){return this},e.revive=function(t){if(t){if(t instanceof e)return t;var n=new W(t);return n._formatted=t.external,n._fsPath=t._sep===j?t.fsPath:null,n}return t},e}(),j=p.c?1:void 0,W=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t._formatted=null,t._fsPath=null,t}return U(t,e),Object.defineProperty(t.prototype,"fsPath",{get:function(){return this._fsPath||(this._fsPath=z(this)),this._fsPath},enumerable:!0,configurable:!0}),t.prototype.toString=function(e){return void 0===e&&(e=!1),e?X(this,!0):(this._formatted||(this._formatted=X(this,!1)),this._formatted)},t.prototype.toJSON=function(){var e={$mid:1};return this._fsPath&&(e.fsPath=this._fsPath,e._sep=j),this._formatted&&(e.external=this._formatted),this.path&&(e.path=this.path),this.scheme&&(e.scheme=this.scheme),this.authority&&(e.authority=this.authority),this.query&&(e.query=this.query),this.fragment&&(e.fragment=this.fragment),e},t}(Y),H=((R={})[58]="%3A",R[47]="%2F",R[63]="%3F",R[35]="%23",R[91]="%5B",R[93]="%5D",R[64]="%40",R[33]="%21",R[36]="%24",R[38]="%26",R[39]="%27",R[40]="%28",R[41]="%29",R[42]="%2A",R[43]="%2B",R[44]="%2C",R[59]="%3B",R[61]="%3D",R[32]="%20",R);function G(e,t){for(var n=void 0,r=-1,i=0;i=97&&o<=122||o>=65&&o<=90||o>=48&&o<=57||45===o||46===o||95===o||126===o||t&&47===o)-1!==r&&(n+=encodeURIComponent(e.substring(r,i)),r=-1),void 0!==n&&(n+=e.charAt(i));else{void 0===n&&(n=e.substr(0,i));var s=H[o];void 0!==s?(-1!==r&&(n+=encodeURIComponent(e.substring(r,i)),r=-1),n+=s):-1===r&&(r=i)}}return-1!==r&&(n+=encodeURIComponent(e.substring(r))),void 0!==n?n:e}function Q(e){for(var t=void 0,n=0;n1&&"file"===e.scheme?"//"+e.authority+e.path:47===e.path.charCodeAt(0)&&(e.path.charCodeAt(1)>=65&&e.path.charCodeAt(1)<=90||e.path.charCodeAt(1)>=97&&e.path.charCodeAt(1)<=122)&&58===e.path.charCodeAt(2)?e.path[1].toLowerCase()+e.path.substr(2):e.path,p.c&&(t=t.replace(/\//g,"\\")),t}function X(e,t){var n=t?Q:G,r="",i=e.scheme,o=e.authority,s=e.path,u=e.query,a=e.fragment;if(i&&(r+=i,r+=":"),(o||"file"===i)&&(r+=V,r+=V),o){var l=o.indexOf("@");if(-1!==l){var h=o.substr(0,l);o=o.substr(l+1),-1===(l=h.indexOf(":"))?r+=n(h,!1):(r+=n(h.substr(0,l),!1),r+=":",r+=n(h.substr(l+1),!1)),r+="@"}-1===(l=(o=o.toLowerCase()).indexOf(":"))?r+=n(o,!1):(r+=n(o.substr(0,l),!1),r+=o.substr(l))}if(s){if(s.length>=3&&47===s.charCodeAt(0)&&58===s.charCodeAt(2))(c=s.charCodeAt(1))>=65&&c<=90&&(s="/"+String.fromCharCode(c+32)+":"+s.substr(3));else if(s.length>=2&&58===s.charCodeAt(1)){var c;(c=s.charCodeAt(0))>=65&&c<=90&&(s=String.fromCharCode(c+32)+":"+s.substr(2))}r+=n(s,!0)}return u&&(r+="?",r+=n(u,!1)),a&&(r+="#",r+=t?a:G(a,!1)),r}var Z=function(){function e(e,t){this.lineNumber=e,this.column=t}return e.prototype.with=function(t,n){return void 0===t&&(t=this.lineNumber),void 0===n&&(n=this.column),t===this.lineNumber&&n===this.column?this:new e(t,n)},e.prototype.delta=function(e,t){return void 0===e&&(e=0),void 0===t&&(t=0),this.with(this.lineNumber+e,this.column+t)},e.prototype.equals=function(t){return e.equals(this,t)},e.equals=function(e,t){return!e&&!t||!!e&&!!t&&e.lineNumber===t.lineNumber&&e.column===t.column},e.prototype.isBefore=function(t){return e.isBefore(this,t)},e.isBefore=function(e,t){return e.lineNumbern||e===n&&t>r?(this.startLineNumber=n,this.startColumn=r,this.endLineNumber=e,this.endColumn=t):(this.startLineNumber=e,this.startColumn=t,this.endLineNumber=n,this.endColumn=r)}return e.prototype.isEmpty=function(){return e.isEmpty(this)},e.isEmpty=function(e){return e.startLineNumber===e.endLineNumber&&e.startColumn===e.endColumn},e.prototype.containsPosition=function(t){return e.containsPosition(this,t)},e.containsPosition=function(e,t){return!(t.lineNumbere.endLineNumber)&&(!(t.lineNumber===e.startLineNumber&&t.columne.endColumn))},e.prototype.containsRange=function(t){return e.containsRange(this,t)},e.containsRange=function(e,t){return!(t.startLineNumbere.endLineNumber||t.endLineNumber>e.endLineNumber)&&(!(t.startLineNumber===e.startLineNumber&&t.startColumne.endColumn)))},e.prototype.strictContainsRange=function(t){return e.strictContainsRange(this,t)},e.strictContainsRange=function(e,t){return!(t.startLineNumbere.endLineNumber||t.endLineNumber>e.endLineNumber)&&(!(t.startLineNumber===e.startLineNumber&&t.startColumn<=e.startColumn)&&!(t.endLineNumber===e.endLineNumber&&t.endColumn>=e.endColumn)))},e.prototype.plusRange=function(t){return e.plusRange(this,t)},e.plusRange=function(t,n){var r,i,o,s;return n.startLineNumbert.endLineNumber?(o=n.endLineNumber,s=n.endColumn):n.endLineNumber===t.endLineNumber?(o=n.endLineNumber,s=Math.max(n.endColumn,t.endColumn)):(o=t.endLineNumber,s=t.endColumn),new e(r,i,o,s)},e.prototype.intersectRanges=function(t){return e.intersectRanges(this,t)},e.intersectRanges=function(t,n){var r=t.startLineNumber,i=t.startColumn,o=t.endLineNumber,s=t.endColumn,u=n.startLineNumber,a=n.startColumn,l=n.endLineNumber,h=n.endColumn;return rl?(o=l,s=h):o===l&&(s=Math.min(s,h)),r>o?null:r===o&&i>s?null:new e(r,i,o,s)},e.prototype.equalsRange=function(t){return e.equalsRange(this,t)},e.equalsRange=function(e,t){return!!e&&!!t&&e.startLineNumber===t.startLineNumber&&e.startColumn===t.startColumn&&e.endLineNumber===t.endLineNumber&&e.endColumn===t.endColumn},e.prototype.getEndPosition=function(){return new Z(this.endLineNumber,this.endColumn)},e.prototype.getStartPosition=function(){return new Z(this.startLineNumber,this.startColumn)},e.prototype.toString=function(){return"["+this.startLineNumber+","+this.startColumn+" -> "+this.endLineNumber+","+this.endColumn+"]"},e.prototype.setEndPosition=function(t,n){return new e(this.startLineNumber,this.startColumn,t,n)},e.prototype.setStartPosition=function(t,n){return new e(t,n,this.endLineNumber,this.endColumn)},e.prototype.collapseToStart=function(){return e.collapseToStart(this)},e.collapseToStart=function(t){return new e(t.startLineNumber,t.startColumn,t.startLineNumber,t.startColumn)},e.fromPositions=function(t,n){return void 0===n&&(n=t),new e(t.lineNumber,t.column,n.lineNumber,n.column)},e.lift=function(t){return t?new e(t.startLineNumber,t.startColumn,t.endLineNumber,t.endColumn):null},e.isIRange=function(e){return e&&"number"==typeof e.startLineNumber&&"number"==typeof e.startColumn&&"number"==typeof e.endLineNumber&&"number"==typeof e.endColumn},e.areIntersectingOrTouching=function(e,t){return!(e.endLineNumbere.startLineNumber},e}();String.fromCharCode(65279);var $=5e3,ee=3;function te(e,t,n,r){return new T(e,t,n).ComputeDiff(r)}var ne=function(){function e(t){for(var n=[],r=[],i=0,o=t.length;i=0;n--){var r=e.charCodeAt(n);if(32!==r&&9!==r)return n}return-1}(e);return-1===n?t:n+2},e.prototype.getCharSequence=function(e,t,n){for(var r=[],i=[],o=[],s=0,u=t;u<=n;u++)for(var a=this._lines[u],l=e?this._startColumns[u]:1,h=e?this._endColumns[u]:a.length+1,c=l;c1&&m>1;){if(c.charCodeAt(d-2)!==f.charCodeAt(m-2))break;d--,m--}(d>1||m>1)&&this._pushTrimWhitespaceCharChange(i,o+1,1,d,s+1,1,m);for(var p=ne._getLastNonBlankColumn(c,1),g=ne._getLastNonBlankColumn(f,1),_=c.length+1,v=f.length+1;p<_&&g255?255:0|e}function le(e){return e<0?0:e>4294967295?4294967295:0|e}var he=function(){return function(e,t){this.index=e,this.remainder=t}}(),ce=function(){function e(e){this.values=e,this.prefixSum=new Uint32Array(e.length),this.prefixSumValidIndex=new Int32Array(1),this.prefixSumValidIndex[0]=-1}return e.prototype.getCount=function(){return this.values.length},e.prototype.insertValues=function(e,t){e=le(e);var n=this.values,r=this.prefixSum,i=t.length;return 0!==i&&(this.values=new Uint32Array(n.length+i),this.values.set(n.subarray(0,e),0),this.values.set(n.subarray(e),e+i),this.values.set(t,e),e-1=0&&this.prefixSum.set(r.subarray(0,this.prefixSumValidIndex[0]+1)),!0)},e.prototype.changeValue=function(e,t){return e=le(e),t=le(t),this.values[e]!==t&&(this.values[e]=t,e-1=n.length)return!1;var i=n.length-e;return t>=i&&(t=i),0!==t&&(this.values=new Uint32Array(n.length-t),this.values.set(n.subarray(0,e),0),this.values.set(n.subarray(e+t),e),this.prefixSum=new Uint32Array(this.values.length),e-1=0&&this.prefixSum.set(r.subarray(0,this.prefixSumValidIndex[0]+1)),!0)},e.prototype.getTotalValue=function(){return 0===this.values.length?0:this._getAccumulatedValue(this.values.length-1)},e.prototype.getAccumulatedValue=function(e){return e<0?0:(e=le(e),this._getAccumulatedValue(e))},e.prototype._getAccumulatedValue=function(e){if(e<=this.prefixSumValidIndex[0])return this.prefixSum[e];var t=this.prefixSumValidIndex[0]+1;0===t&&(this.prefixSum[0]=this.values[0],t++),e>=this.values.length&&(e=this.values.length-1);for(var n=t;n<=e;n++)this.prefixSum[n]=this.prefixSum[n-1]+this.values[n];return this.prefixSumValidIndex[0]=Math.max(this.prefixSumValidIndex[0],e),this.prefixSum[e]},e.prototype.getIndexOf=function(e){e=Math.floor(e),this.getTotalValue();for(var t=0,n=this.values.length-1,r=0,i=0,o=0;t<=n;)if(r=t+(n-t)/2|0,e<(o=(i=this.prefixSum[r])-this.values[r]))n=r-1;else{if(!(e>=i))break;t=r+1}return new he(r,e-o)},e}(),fe=(function(){function e(e){this._cacheAccumulatedValueStart=0,this._cache=null,this._actual=new ce(e),this._bustCache()}e.prototype._bustCache=function(){this._cacheAccumulatedValueStart=0,this._cache=null},e.prototype.insertValues=function(e,t){this._actual.insertValues(e,t)&&this._bustCache()},e.prototype.changeValue=function(e,t){this._actual.changeValue(e,t)&&this._bustCache()},e.prototype.removeValues=function(e,t){this._actual.removeValues(e,t)&&this._bustCache()},e.prototype.getTotalValue=function(){return this._actual.getTotalValue()},e.prototype.getAccumulatedValue=function(e){return this._actual.getAccumulatedValue(e)},e.prototype.getIndexOf=function(e){if(e=Math.floor(e),null!==this._cache){var t=e-this._cacheAccumulatedValueStart;if(t>=0&&t/?";var me=function(e){void 0===e&&(e="");for(var t="(-?\\d*\\.\\d\\w*)|([^",n=0,r=de;n=0||(t+="\\"+i)}return t+="\\s]+)",new RegExp(t,"g")}();var pe=function(){function e(t){var n=ae(t);this._defaultValue=n,this._asciiMap=e._createAsciiMap(n),this._map=new Map}return e._createAsciiMap=function(e){for(var t=new Uint8Array(256),n=0;n<256;n++)t[n]=e;return t},e.prototype.set=function(e,t){var n=ae(t);e>=0&&e<256?this._asciiMap[e]=n:this._map.set(e,n)},e.prototype.get=function(e){return e>=0&&e<256?this._asciiMap[e]:this._map.get(e)||this._defaultValue},e}(),ge=(function(){function e(){this._actual=new pe(0)}e.prototype.add=function(e){this._actual.set(e,1)},e.prototype.has=function(e){return 1===this._actual.get(e)}}(),function(){function e(e){for(var t=0,n=0,r=0,i=e.length;rt&&(t=u),s>n&&(n=s),a>n&&(n=a)}var l=new ue(++n,++t,0);for(r=0,i=e.length;r=this._maxCharCode?0:this._states.get(e,t)},e}()),_e=null;var ve=null;var ye=function(){function e(){}return e._createLink=function(e,t,n,r,i){var o=i-1;do{var s=t.charCodeAt(o);if(2!==e.get(s))break;o--}while(o>r);if(r>0){var u=t.charCodeAt(r-1),a=t.charCodeAt(o);(40===u&&41===a||91===u&&93===a||123===u&&125===a)&&o--}return{range:{startLineNumber:n,startColumn:r+1,endLineNumber:n,endColumn:o+2},url:t.substring(r,o+1)}},e.computeLinks=function(t,n){void 0===n&&(null===_e&&(_e=new ge([[1,104,2],[1,72,2],[1,102,6],[1,70,6],[2,116,3],[2,84,3],[3,116,4],[3,84,4],[4,112,5],[4,80,5],[5,115,9],[5,83,9],[5,58,10],[6,105,7],[6,73,7],[7,108,8],[7,76,8],[8,101,9],[8,69,9],[9,58,10],[10,47,11],[11,47,12]])),n=_e);for(var r=function(){if(null===ve){ve=new pe(0);for(var e=0;e<" \t<>'\"、。。、,.:;?!@#$%&*‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…".length;e++)ve.set(" \t<>'\"、。。、,.:;?!@#$%&*‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…".charCodeAt(e),1);for(e=0;e<".,;".length;e++)ve.set(".,;".charCodeAt(e),2)}return ve}(),i=[],o=1,s=t.getLineCount();o<=s;o++){for(var u=t.getLineContent(o),a=u.length,l=0,h=0,c=0,f=1,d=!1,m=!1,p=!1;l=0?((r+=n?1:-1)<0?r=e.length-1:r%=e.length,e[r]):null},e.INSTANCE=new e,e}();n("tZcU");var be,Le=function(){function e(t){this.element=t,this.next=e.Undefined,this.prev=e.Undefined}return e.Undefined=new e(void 0),e}(),Ne=function(){function e(){this._first=Le.Undefined,this._last=Le.Undefined,this._size=0}return Object.defineProperty(e.prototype,"size",{get:function(){return this._size},enumerable:!0,configurable:!0}),e.prototype.isEmpty=function(){return this._first===Le.Undefined},e.prototype.clear=function(){this._first=Le.Undefined,this._last=Le.Undefined,this._size=0},e.prototype.unshift=function(e){return this._insert(e,!1)},e.prototype.push=function(e){return this._insert(e,!0)},e.prototype._insert=function(e,t){var n=this,r=new Le(e);if(this._first===Le.Undefined)this._first=r,this._last=r;else if(t){var i=this._last;this._last=r,r.prev=i,i.next=r}else{var o=this._first;this._first=r,r.next=o,o.prev=r}this._size+=1;var s=!1;return function(){s||(s=!0,n._remove(r))}},e.prototype.shift=function(){if(this._first!==Le.Undefined){var e=this._first.element;return this._remove(this._first),e}},e.prototype.pop=function(){if(this._last!==Le.Undefined){var e=this._last.element;return this._remove(this._last),e}},e.prototype._remove=function(e){if(e.prev!==Le.Undefined&&e.next!==Le.Undefined){var t=e.prev;t.next=e.next,e.next.prev=t}else e.prev===Le.Undefined&&e.next===Le.Undefined?(this._first=Le.Undefined,this._last=Le.Undefined):e.next===Le.Undefined?(this._last=this._last.prev,this._last.next=Le.Undefined):e.prev===Le.Undefined&&(this._first=this._first.next,this._first.prev=Le.Undefined);this._size-=1},e.prototype.iterator=function(){var e,t=this._first;return{next:function(){return t===Le.Undefined?I:(e?e.value=t.element:e={done:!1,value:t.element},t=t.next,e)}}},e.prototype.toArray=function(){for(var e=[],t=this._first;t!==Le.Undefined;t=t.next)e.push(t.element);return e},e}(),Ee=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();!function(e){function t(e){return function(t,n,r){void 0===n&&(n=null);var i,o=!1;return i=e(function(e){if(!o)return i?i.dispose():o=!0,t.call(n,e)},null,r),o&&i.dispose(),i}}function n(e,t){return s(function(n,r,i){return void 0===r&&(r=null),e(function(e){return n.call(r,t(e))},null,i)})}function r(e,t){return s(function(n,r,i){return void 0===r&&(r=null),e(function(e){t(e),n.call(r,e)},null,i)})}function i(e,t){return s(function(n,r,i){return void 0===r&&(r=null),e(function(e){return t(e)&&n.call(r,e)},null,i)})}function o(e,t,r){var i=r;return n(e,function(e){return i=t(i,e)})}function s(e){var t,n=new Pe({onFirstListenerAdd:function(){t=e(n.fire,n)},onLastListenerRemove:function(){t.dispose()}});return n.event}function u(e){var t,n=!0;return i(e,function(e){var r=n||e!==t;return n=!1,t=e,r})}e.None=function(){return m.None},e.once=t,e.map=n,e.forEach=r,e.filter=i,e.signal=function(e){return e},e.any=function(){for(var e=[],t=0;t1)&&l.fire(e),a=0},n)})},onLastListenerRemove:function(){o.dispose()}});return l.event},e.stopwatch=function(e){var r=(new Date).getTime();return n(t(e),function(e){return(new Date).getTime()-r})},e.latch=u,e.buffer=function(e,t,n){void 0===t&&(t=!1),void 0===n&&(n=[]);var r=n.slice(),i=e(function(e){r?r.push(e):s.fire(e)}),o=function(){r&&r.forEach(function(e){return s.fire(e)}),r=null},s=new Pe({onFirstListenerAdd:function(){i||(i=e(function(e){return s.fire(e)}))},onFirstListenerDidAdd:function(){r&&(t?setTimeout(o):o())},onLastListenerRemove:function(){i&&i.dispose(),i=null}});return s.event};var a=function(){function e(e){this.event=e}return e.prototype.map=function(t){return new e(n(this.event,t))},e.prototype.forEach=function(t){return new e(r(this.event,t))},e.prototype.filter=function(t){return new e(i(this.event,t))},e.prototype.reduce=function(t,n){return new e(o(this.event,t,n))},e.prototype.latch=function(){return new e(u(this.event))},e.prototype.on=function(e,t,n){return this.event(e,t,n)},e.prototype.once=function(e,n,r){return t(this.event)(e,n,r)},e}();e.chain=function(e){return new a(e)},e.fromNodeEventEmitter=function(e,t,n){void 0===n&&(n=function(e){return e});var r=function(){for(var e=[],t=0;t0?new Ae(this._options&&this._options.leakWarningThreshold):void 0}return Object.defineProperty(e.prototype,"event",{get:function(){var t=this;return this._event||(this._event=function(n,r,i){t._listeners||(t._listeners=new Ne);var o=t._listeners.isEmpty();o&&t._options&&t._options.onFirstListenerAdd&&t._options.onFirstListenerAdd(t);var s,u,a=t._listeners.push(r?[n,r]:n);return o&&t._options&&t._options.onFirstListenerDidAdd&&t._options.onFirstListenerDidAdd(t),t._options&&t._options.onListenerDidAdd&&t._options.onListenerDidAdd(t,n,r),t._leakageMon&&(s=t._leakageMon.check(t._listeners.size)),u={dispose:function(){(s&&s(),u.dispose=e._noop,t._disposed)||(a(),t._options&&t._options.onLastListenerRemove&&(t._listeners&&!t._listeners.isEmpty()||t._options.onLastListenerRemove(t)))}},i instanceof d?i.add(u):Array.isArray(i)&&i.push(u),u}),this._event},enumerable:!0,configurable:!0}),e.prototype.fire=function(e){if(this._listeners){this._deliveryQueue||(this._deliveryQueue=new Ne);for(var t=this._listeners.iterator(),n=t.next();!n.done;n=t.next())this._deliveryQueue.push([n.value,e]);for(;this._deliveryQueue.size>0;){var r=this._deliveryQueue.shift(),o=r[0],s=r[1];try{"function"==typeof o?o.call(void 0,s):o[0].call(o[1],s)}catch(n){i(n)}}}},e.prototype.dispose=function(){this._listeners&&this._listeners.clear(),this._deliveryQueue&&this._deliveryQueue.clear(),this._leakageMon&&this._leakageMon.dispose(),this._disposed=!0},e._noop=function(){},e}(),Me=(function(e){function t(t){var n=e.call(this,t)||this;return n._isPaused=0,n._eventQueue=new Ne,n._mergeFn=t&&t.merge,n}Ee(t,e),t.prototype.pause=function(){this._isPaused++},t.prototype.resume=function(){if(0!==this._isPaused&&0==--this._isPaused)if(this._mergeFn){var t=this._eventQueue.toArray();this._eventQueue.clear(),e.prototype.fire.call(this,this._mergeFn(t))}else for(;!this._isPaused&&0!==this._eventQueue.size;)e.prototype.fire.call(this,this._eventQueue.shift())},t.prototype.fire=function(t){this._listeners&&(0!==this._isPaused?this._eventQueue.push(t):e.prototype.fire.call(this,t))}}(Pe),function(){function e(){var e=this;this.hasListeners=!1,this.events=[],this.emitter=new Pe({onFirstListenerAdd:function(){return e.onFirstListenerAdd()},onLastListenerRemove:function(){return e.onLastListenerRemove()}})}Object.defineProperty(e.prototype,"event",{get:function(){return this.emitter.event},enumerable:!0,configurable:!0}),e.prototype.add=function(e){var t=this,n={event:e,listener:null};this.events.push(n),this.hasListeners&&this.hook(n);var r,i;return r=function(e){var t,n=this,r=!1;return function(){return r?t:(r=!0,t=e.apply(n,arguments))}}(function(){t.hasListeners&&t.unhook(n);var e=t.events.indexOf(n);t.events.splice(e,1)}),i=c({dispose:function(){h(i),r()}})},e.prototype.onFirstListenerAdd=function(){var e=this;this.hasListeners=!0,this.events.forEach(function(t){return e.hook(t)})},e.prototype.onLastListenerRemove=function(){var e=this;this.hasListeners=!1,this.events.forEach(function(t){return e.unhook(t)})},e.prototype.hook=function(e){var t=this;e.listener=e.event(function(e){return t.emitter.fire(e)})},e.prototype.unhook=function(e){e.listener&&e.listener.dispose(),e.listener=null},e.prototype.dispose=function(){this.emitter.dispose()}}(),function(){function e(){this.buffers=[]}e.prototype.wrapEvent=function(e){var t=this;return function(n,r,i){return e(function(e){var i=t.buffers[t.buffers.length-1];i?i.push(function(){return n.call(r,e)}):n.call(r,e)},void 0,i)}},e.prototype.bufferEvents=function(e){var t=[];this.buffers.push(t);var n=e();return this.buffers.pop(),t.forEach(function(e){return e()}),n}}(),function(){function e(){var e=this;this.listening=!1,this.inputEvent=be.None,this.inputEventListener=m.None,this.emitter=new Pe({onFirstListenerDidAdd:function(){e.listening=!0,e.inputEventListener=e.inputEvent(e.emitter.fire,e.emitter)},onLastListenerRemove:function(){e.listening=!1,e.inputEventListener.dispose()}}),this.event=this.emitter.event}Object.defineProperty(e.prototype,"input",{set:function(e){this.inputEvent=e,this.listening&&(this.inputEventListener.dispose(),this.inputEventListener=e(this.emitter.fire,this.emitter))},enumerable:!0,configurable:!0}),e.prototype.dispose=function(){this.inputEventListener.dispose(),this.emitter.dispose()}}(),Object.freeze(function(e,t){var n=setTimeout(e.bind(t),0);return{dispose:function(){clearTimeout(n)}}}));!function(e){e.isCancellationToken=function(t){return t===e.None||t===e.Cancelled||t instanceof Te||!(!t||"object"!=typeof t)&&"boolean"==typeof t.isCancellationRequested&&"function"==typeof t.onCancellationRequested},e.None=Object.freeze({isCancellationRequested:!1,onCancellationRequested:be.None}),e.Cancelled=Object.freeze({isCancellationRequested:!0,onCancellationRequested:Me})}(Se||(Se={}));var Oe,Te=function(){function e(){this._isCancelled=!1,this._emitter=null}return e.prototype.cancel=function(){this._isCancelled||(this._isCancelled=!0,this._emitter&&(this._emitter.fire(void 0),this.dispose()))},Object.defineProperty(e.prototype,"isCancellationRequested",{get:function(){return this._isCancelled},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onCancellationRequested",{get:function(){return this._isCancelled?Me:(this._emitter||(this._emitter=new Pe),this._emitter.event)},enumerable:!0,configurable:!0}),e.prototype.dispose=function(){this._emitter&&(this._emitter.dispose(),this._emitter=null)},e}(),xe=function(){function e(e){this._token=void 0,this._parentListener=void 0,this._parentListener=e&&e.onCancellationRequested(this.cancel,this)}return Object.defineProperty(e.prototype,"token",{get:function(){return this._token||(this._token=new Te),this._token},enumerable:!0,configurable:!0}),e.prototype.cancel=function(){this._token?this._token instanceof Te&&this._token.cancel():this._token=Se.Cancelled},e.prototype.dispose=function(){this._parentListener&&this._parentListener.dispose(),this._token?this._token instanceof Te&&this._token.dispose():this._token=Se.None},e}(),Ie=function(){function e(){this._keyCodeToStr=[],this._strToKeyCode=Object.create(null)}return e.prototype.define=function(e,t){this._keyCodeToStr[e]=t,this._strToKeyCode[t.toLowerCase()]=e},e.prototype.keyCodeToStr=function(e){return this._keyCodeToStr[e]},e.prototype.strToKeyCode=function(e){return this._strToKeyCode[e.toLowerCase()]||0},e}(),Re=new Ie,Ue=new Ie,ke=new Ie;!function(){function e(e,t,n,r){void 0===n&&(n=t),void 0===r&&(r=n),Re.define(e,t),Ue.define(e,n),ke.define(e,r)}e(0,"unknown"),e(1,"Backspace"),e(2,"Tab"),e(3,"Enter"),e(4,"Shift"),e(5,"Ctrl"),e(6,"Alt"),e(7,"PauseBreak"),e(8,"CapsLock"),e(9,"Escape"),e(10,"Space"),e(11,"PageUp"),e(12,"PageDown"),e(13,"End"),e(14,"Home"),e(15,"LeftArrow","Left"),e(16,"UpArrow","Up"),e(17,"RightArrow","Right"),e(18,"DownArrow","Down"),e(19,"Insert"),e(20,"Delete"),e(21,"0"),e(22,"1"),e(23,"2"),e(24,"3"),e(25,"4"),e(26,"5"),e(27,"6"),e(28,"7"),e(29,"8"),e(30,"9"),e(31,"A"),e(32,"B"),e(33,"C"),e(34,"D"),e(35,"E"),e(36,"F"),e(37,"G"),e(38,"H"),e(39,"I"),e(40,"J"),e(41,"K"),e(42,"L"),e(43,"M"),e(44,"N"),e(45,"O"),e(46,"P"),e(47,"Q"),e(48,"R"),e(49,"S"),e(50,"T"),e(51,"U"),e(52,"V"),e(53,"W"),e(54,"X"),e(55,"Y"),e(56,"Z"),e(57,"Meta"),e(58,"ContextMenu"),e(59,"F1"),e(60,"F2"),e(61,"F3"),e(62,"F4"),e(63,"F5"),e(64,"F6"),e(65,"F7"),e(66,"F8"),e(67,"F9"),e(68,"F10"),e(69,"F11"),e(70,"F12"),e(71,"F13"),e(72,"F14"),e(73,"F15"),e(74,"F16"),e(75,"F17"),e(76,"F18"),e(77,"F19"),e(78,"NumLock"),e(79,"ScrollLock"),e(80,";",";","OEM_1"),e(81,"=","=","OEM_PLUS"),e(82,",",",","OEM_COMMA"),e(83,"-","-","OEM_MINUS"),e(84,".",".","OEM_PERIOD"),e(85,"/","/","OEM_2"),e(86,"`","`","OEM_3"),e(110,"ABNT_C1"),e(111,"ABNT_C2"),e(87,"[","[","OEM_4"),e(88,"\\","\\","OEM_5"),e(89,"]","]","OEM_6"),e(90,"'","'","OEM_7"),e(91,"OEM_8"),e(92,"OEM_102"),e(93,"NumPad0"),e(94,"NumPad1"),e(95,"NumPad2"),e(96,"NumPad3"),e(97,"NumPad4"),e(98,"NumPad5"),e(99,"NumPad6"),e(100,"NumPad7"),e(101,"NumPad8"),e(102,"NumPad9"),e(103,"NumPad_Multiply"),e(104,"NumPad_Add"),e(105,"NumPad_Separator"),e(106,"NumPad_Subtract"),e(107,"NumPad_Decimal"),e(108,"NumPad_Divide")}(),function(e){e.toString=function(e){return Re.keyCodeToStr(e)},e.fromString=function(e){return Re.strToKeyCode(e)},e.toUserSettingsUS=function(e){return Ue.keyCodeToStr(e)},e.toUserSettingsGeneral=function(e){return ke.keyCodeToStr(e)},e.fromUserSettings=function(e){return Ue.strToKeyCode(e)||ke.strToKeyCode(e)}}(Oe||(Oe={}));!function(){function e(e,t,n,r,i){this.ctrlKey=e,this.shiftKey=t,this.altKey=n,this.metaKey=r,this.keyCode=i}e.prototype.equals=function(e){return this.ctrlKey===e.ctrlKey&&this.shiftKey===e.shiftKey&&this.altKey===e.altKey&&this.metaKey===e.metaKey&&this.keyCode===e.keyCode},e.prototype.isModifierKey=function(){return 0===this.keyCode||5===this.keyCode||57===this.keyCode||6===this.keyCode||4===this.keyCode},e.prototype.toChord=function(){return new dt([this])},e.prototype.isDuplicateModifierCase=function(){return this.ctrlKey&&5===this.keyCode||this.shiftKey&&4===this.keyCode||this.altKey&&6===this.keyCode||this.metaKey&&57===this.keyCode}}();var De,Fe,Ke,qe,Ve,Be,Ye,je,We,He,Ge,Qe,ze,Xe,Ze,Je,$e,et,tt,nt,rt,it,ot,st,ut,at,lt,ht,ct,ft,dt=function(){function e(e){if(0===e.length)throw(t="parts")?new Error("Illegal argument: "+t):new Error("Illegal argument");var t;this.parts=e}return e.prototype.equals=function(e){if(null===e)return!1;if(this.parts.length!==e.parts.length)return!1;for(var t=0;t "+this.positionLineNumber+","+this.positionColumn+"]"},t.prototype.equalsSelection=function(e){return t.selectionsEqual(this,e)},t.selectionsEqual=function(e,t){return e.selectionStartLineNumber===t.selectionStartLineNumber&&e.selectionStartColumn===t.selectionStartColumn&&e.positionLineNumber===t.positionLineNumber&&e.positionColumn===t.positionColumn},t.prototype.getDirection=function(){return this.selectionStartLineNumber===this.startLineNumber&&this.selectionStartColumn===this.startColumn?0:1},t.prototype.setEndPosition=function(e,n){return 0===this.getDirection()?new t(this.startLineNumber,this.startColumn,e,n):new t(e,n,this.startLineNumber,this.startColumn)},t.prototype.getPosition=function(){return new Z(this.positionLineNumber,this.positionColumn)},t.prototype.setStartPosition=function(e,n){return 0===this.getDirection()?new t(e,n,this.endLineNumber,this.endColumn):new t(this.endLineNumber,this.endColumn,e,n)},t.fromPositions=function(e,n){return void 0===n&&(n=e),new t(e.lineNumber,e.column,n.lineNumber,n.column)},t.liftSelection=function(e){return new t(e.selectionStartLineNumber,e.selectionStartColumn,e.positionLineNumber,e.positionColumn)},t.selectionsArrEqual=function(e,t){if(e&&!t||!e&&t)return!1;if(!e&&!t)return!0;if(e.length!==t.length)return!1;for(var n=0,r=e.length;n>>0)>>>0}(e,t)},e.CtrlCmd=2048,e.Shift=1024,e.Alt=512,e.WinCtrl=256,e}();var vt=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),yt=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return vt(t,e),Object.defineProperty(t.prototype,"uri",{get:function(){return this._uri},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"version",{get:function(){return this._versionId},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"eol",{get:function(){return this._eol},enumerable:!0,configurable:!0}),t.prototype.getValue=function(){return this.getText()},t.prototype.getLinesContent=function(){return this._lines.slice(0)},t.prototype.getLineCount=function(){return this._lines.length},t.prototype.getLineContent=function(e){return this._lines[e-1]},t.prototype.getWordAtPosition=function(e,t){var n=function(e,t,n,r){t.lastIndex=0;var i=t.exec(n);if(!i)return null;var o=i[0].indexOf(" ")>=0?function(e,t,n,r){var i,o=e-1-r;for(t.lastIndex=0;i=t.exec(n);){var s=i.index||0;if(s>o)return null;if(t.lastIndex>=o)return{word:i[0],startColumn:r+1+s,endColumn:r+1+t.lastIndex}}return null}(e,t,n,r):function(e,t,n,r){var i,o=e-1-r,s=n.lastIndexOf(" ",o-1)+1;for(t.lastIndex=s;i=t.exec(n);){var u=i.index||0;if(u<=o&&t.lastIndex>=o)return{word:i[0],startColumn:r+1+u,endColumn:r+1+t.lastIndex}}return null}(e,t,n,r);return t.lastIndex=0,o}(e.column,function(e){var t=me;if(e&&e instanceof RegExp)if(e.global)t=e;else{var n="g";e.ignoreCase&&(n+="i"),e.multiline&&(n+="m"),e.unicode&&(n+="u"),t=new RegExp(e.source,n)}return t.lastIndex=0,t}(t),this._lines[e.lineNumber-1],0);return n?new J(e.lineNumber,n.startColumn,e.lineNumber,n.endColumn):null},t.prototype.getWordUntilPosition=function(e,t){var n=this.getWordAtPosition(e,t);return n?{word:this._lines[e.lineNumber-1].substring(n.startColumn-1,e.column-1),startColumn:n.startColumn,endColumn:e.column}:{word:"",startColumn:e.column,endColumn:e.column}},t.prototype.createWordIterator=function(e){var t,n,r=this,i=0,o=0,s=[],u=function(){if(o=r._lines.length?I:(n=r._lines[i],s=r._wordenize(n,e),o=0,i+=1,u())};return{next:u}},t.prototype.getLineWords=function(e,t){for(var n=this._lines[e-1],r=[],i=0,o=this._wordenize(n,t);ithis._lines.length)t=this._lines.length,n=this._lines[t-1].length+1,r=!0;else{var i=this._lines[t-1].length+1;n<1?(n=1,r=!0):n>i&&(n=i,r=!0)}return r?{lineNumber:t,column:n}:e},t}(fe),Ct=function(){function e(e,t){this._host=e,this._models=Object.create(null),this._foreignModuleFactory=t,this._foreignModule=null}return e.prototype.dispose=function(){this._models=Object.create(null)},e.prototype._getModel=function(e){return this._models[e]},e.prototype._getModels=function(){var e=this,t=[];return Object.keys(this._models).forEach(function(n){return t.push(e._models[n])}),t},e.prototype.acceptNewModel=function(e){this._models[e.url]=new yt(Y.parse(e.url),e.lines,e.EOL,e.versionId)},e.prototype.acceptModelChanged=function(e,t){this._models[e]&&this._models[e].onEvents(t)},e.prototype.acceptRemovedModel=function(e){this._models[e]&&delete this._models[e]},e.prototype.computeDiff=function(e,t,n){var r=this._getModel(e),i=this._getModel(t);if(!r||!i)return Promise.resolve(null);var o=r.getLinesContent(),s=i.getLinesContent(),u=new se(o,s,{shouldComputeCharChanges:!0,shouldPostProcessCharChanges:!0,shouldIgnoreTrimWhitespace:n,shouldMakePrettyDiff:!0}).computeDiff(),a=!(u.length>0)&&this._modelsAreIdentical(r,i);return Promise.resolve({identical:a,changes:u})},e.prototype._modelsAreIdentical=function(e,t){var n=e.getLineCount();if(n!==t.getLineCount())return!1;for(var r=1;r<=n;r++){if(e.getLineContent(r)!==t.getLineContent(r))return!1}return!0},e.prototype.computeMoreMinimalEdits=function(t,n){var r=this._getModel(t);if(!r)return Promise.resolve(n);for(var i=[],o=void 0,s=0,u=n=N(n,function(e,t){return e.range&&t.range?J.compareRangesUsingStarts(e.range,t.range):(e.range?0:1)-(t.range?0:1)});se._diffLimit)i.push({range:l,text:h});else for(var d=w(f,h,!1),m=r.offsetAt(J.lift(l).getStartPosition()),p=0,g=d;p1)for(var n=1;n=0,o=a.indexOf("Macintosh")>=0,s=a.indexOf("Linux")>=0,u=!0,navigator.language;var d=i,m=u,p="object"==typeof self?self:"object"==typeof r?r:{}}).call(t,n("W2nU"),n("DuR2"))},tZcU:function(e,t,n){(function(e){(function(){"use strict";function t(e){var t=this.constructor;return this.then(function(n){return t.resolve(e()).then(function(){return n})},function(n){return t.resolve(e()).then(function(){return t.reject(n)})})}var n=setTimeout;function r(){}function i(e){if(!(this instanceof i))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],l(e,this)}function o(e,t){for(;3===e._state;)e=e._value;0!==e._state?(e._handled=!0,i._immediateFn(function(){var n=1===e._state?t.onFulfilled:t.onRejected;if(null!==n){var r;try{r=n(e._value)}catch(e){return void u(t.promise,e)}s(t.promise,r)}else(1===e._state?s:u)(t.promise,e._value)})):e._deferreds.push(t)}function s(e,t){try{if(t===e)throw new TypeError("A promise cannot be resolved with itself.");if(t&&("object"==typeof t||"function"==typeof t)){var n=t.then;if(t instanceof i)return e._state=3,e._value=t,void a(e);if("function"==typeof n)return void l((r=n,o=t,function(){r.apply(o,arguments)}),e)}e._state=1,e._value=t,a(e)}catch(t){u(e,t)}var r,o}function u(e,t){e._state=2,e._value=t,a(e)}function a(e){2===e._state&&0===e._deferreds.length&&i._immediateFn(function(){e._handled||i._unhandledRejectionFn(e._value)});for(var t=0,n=e._deferreds.length;t + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/emqx_dashboard/priv/www/static/fonts/Roboto.ttf b/apps/emqx_dashboard/priv/www/static/fonts/Roboto.ttf new file mode 100644 index 000000000..7b25f3ce9 Binary files /dev/null and b/apps/emqx_dashboard/priv/www/static/fonts/Roboto.ttf differ diff --git a/apps/emqx_dashboard/priv/www/static/fonts/Roboto.woff b/apps/emqx_dashboard/priv/www/static/fonts/Roboto.woff new file mode 100644 index 000000000..941dfa4ba Binary files /dev/null and b/apps/emqx_dashboard/priv/www/static/fonts/Roboto.woff differ diff --git a/apps/emqx_dashboard/priv/www/static/fonts/Roboto.woff2 b/apps/emqx_dashboard/priv/www/static/fonts/Roboto.woff2 new file mode 100644 index 000000000..120796bb7 Binary files /dev/null and b/apps/emqx_dashboard/priv/www/static/fonts/Roboto.woff2 differ diff --git a/apps/emqx_dashboard/priv/www/static/fonts/element-icons.535877f.woff b/apps/emqx_dashboard/priv/www/static/fonts/element-icons.535877f.woff new file mode 100644 index 000000000..02b9a2539 Binary files /dev/null and b/apps/emqx_dashboard/priv/www/static/fonts/element-icons.535877f.woff differ diff --git a/apps/emqx_dashboard/priv/www/static/fonts/element-icons.732389d.ttf b/apps/emqx_dashboard/priv/www/static/fonts/element-icons.732389d.ttf new file mode 100644 index 000000000..91b74de36 Binary files /dev/null and b/apps/emqx_dashboard/priv/www/static/fonts/element-icons.732389d.ttf differ diff --git a/apps/emqx_dashboard/priv/www/static/fonts/fontawesome-webfont.eot b/apps/emqx_dashboard/priv/www/static/fonts/fontawesome-webfont.eot new file mode 100644 index 000000000..e9f60ca95 Binary files /dev/null and b/apps/emqx_dashboard/priv/www/static/fonts/fontawesome-webfont.eot differ diff --git a/apps/emqx_dashboard/priv/www/static/fonts/fontawesome-webfont.svg b/apps/emqx_dashboard/priv/www/static/fonts/fontawesome-webfont.svg new file mode 100644 index 000000000..855c845e5 --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/emqx_dashboard/priv/www/static/fonts/fontawesome-webfont.ttf b/apps/emqx_dashboard/priv/www/static/fonts/fontawesome-webfont.ttf new file mode 100644 index 000000000..35acda2fa Binary files /dev/null and b/apps/emqx_dashboard/priv/www/static/fonts/fontawesome-webfont.ttf differ diff --git a/apps/emqx_dashboard/priv/www/static/fonts/fontawesome-webfont.woff b/apps/emqx_dashboard/priv/www/static/fonts/fontawesome-webfont.woff new file mode 100644 index 000000000..400014a4b Binary files /dev/null and b/apps/emqx_dashboard/priv/www/static/fonts/fontawesome-webfont.woff differ diff --git a/apps/emqx_dashboard/priv/www/static/fonts/fontawesome-webfont.woff2 b/apps/emqx_dashboard/priv/www/static/fonts/fontawesome-webfont.woff2 new file mode 100644 index 000000000..4d13fc604 Binary files /dev/null and b/apps/emqx_dashboard/priv/www/static/fonts/fontawesome-webfont.woff2 differ diff --git a/apps/emqx_dashboard/priv/www/static/js/0.e5c2d59c47ae56b10376.js b/apps/emqx_dashboard/priv/www/static/js/0.e5c2d59c47ae56b10376.js new file mode 100644 index 000000000..30946810d --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/js/0.e5c2d59c47ae56b10376.js @@ -0,0 +1,8 @@ +webpackJsonp([0],{"1H6C":function(e,t,r){var n=function(){return this}()||Function("return this")(),i=n.regeneratorRuntime&&Object.getOwnPropertyNames(n).indexOf("regeneratorRuntime")>=0,a=i&&n.regeneratorRuntime;if(n.regeneratorRuntime=void 0,e.exports=r("HhN8"),i)n.regeneratorRuntime=a;else try{delete n.regeneratorRuntime}catch(e){n.regeneratorRuntime=void 0}},"3IRH":function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},FcGO:function(e,t,r){"use strict";var n=r("Xxa5"),i=r.n(n),a=r("exGp"),s=r.n(a),o={name:"emq-select",components:{},props:{value:{},field:{type:Object,required:!0},fieldName:{type:Object,default:function(){return{label:"label",value:"value"}}},disabledItem:{type:Array,default:function(){return[]}},refresh:{type:Boolean}},data:function(){return{options:[],parserField:{}}},computed:{rawValue:{get:function(){return"boolean"==typeof this.value?this.value.toString():this.value},set:function(e){var t=this.fieldName.value;this.options.find(function(r){return r[t]===e})&&this.parserField[t]&&(e="true"===e),this.$emit("update:value",e)}}},watch:{refresh:function(e){e&&this.loadData()},field:{handler:function(){this.loadData()},deep:!0}},created:function(){this.loadData()},methods:{loadData:function(){var e=this;return s()(i.a.mark(function t(){var r,n,a;return i.a.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,e.getOptions();case 2:r=t.sent,e.parserField={},n=e.fieldName.value,a=e.fieldName.label,e.options=r.map(function(t){var r=t[n],i=t[a];return"boolean"==typeof r&&(e.parserField[n]="boolean",t[n]=r.toString(),"boolean"==typeof i&&(t[a]=i.toString())),t}),e.$emit("update:refresh",!1);case 8:case"end":return t.stop()}},t,e)}))()},isDisabled:function(e){return this.disabledItem.includes(e[this.fieldName.value])},getOptions:function(){var e=this;return s()(i.a.mark(function t(){var r,n,a,s,o;return i.a.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:if(r=e.field,n=r.api,r.url,a=r.options,s=r.list,o=[],!a){t.next=6;break}o=a,t.next=14;break;case 6:if(!s){t.next=10;break}o=s.map(function(e){return{label:e,value:e}}),t.next=14;break;case 10:if(!n){t.next=14;break}return t.next=13,n();case 13:o=t.sent;case 14:return t.abrupt("return",o);case 15:case"end":return t.stop()}},t,e)}))()}}},l={render:function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("el-select",e._g(e._b({staticClass:"emq-select",attrs:{value:e.rawValue}},"el-select",e.$attrs,!1),e.$listeners),[e._t("default",e._l(e.options,function(t,n){return r("el-option",{key:n,attrs:{value:t[e.fieldName.value],label:t[e.fieldName.label],disabled:e.isDisabled(t)}},[e._t("option",null,{item:t})],2)}))],2)},staticRenderFns:[]},c=r("VU/8")(o,l,!1,null,null,null);t.a=c.exports},"G5A+":function(e,t){},HhN8:function(e,t){!function(t){"use strict";var r,n=Object.prototype,i=n.hasOwnProperty,a="function"==typeof Symbol?Symbol:{},s=a.iterator||"@@iterator",o=a.asyncIterator||"@@asyncIterator",l=a.toStringTag||"@@toStringTag",c="object"==typeof e,u=t.regeneratorRuntime;if(u)c&&(e.exports=u);else{(u=t.regeneratorRuntime=c?e.exports:{}).wrap=_;var p="suspendedStart",h="suspendedYield",d="executing",f="completed",y={},v={};v[s]=function(){return this};var m=Object.getPrototypeOf,b=m&&m(m(C([])));b&&b!==n&&i.call(b,s)&&(v=b);var g=w.prototype=E.prototype=Object.create(v);x.prototype=g.constructor=w,w.constructor=x,w[l]=x.displayName="GeneratorFunction",u.isGeneratorFunction=function(e){var t="function"==typeof e&&e.constructor;return!!t&&(t===x||"GeneratorFunction"===(t.displayName||t.name))},u.mark=function(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,w):(e.__proto__=w,l in e||(e[l]="GeneratorFunction")),e.prototype=Object.create(g),e},u.awrap=function(e){return{__await:e}},O($.prototype),$.prototype[o]=function(){return this},u.AsyncIterator=$,u.async=function(e,t,r,n){var i=new $(_(e,t,r,n));return u.isGeneratorFunction(t)?i:i.next().then(function(e){return e.done?e.value:i.next()})},O(g),g[l]="Generator",g[s]=function(){return this},g.toString=function(){return"[object Generator]"},u.keys=function(e){var t=[];for(var r in e)t.push(r);return t.reverse(),function r(){for(;t.length;){var n=t.pop();if(n in e)return r.value=n,r.done=!1,r}return r.done=!0,r}},u.values=C,I.prototype={constructor:I,reset:function(e){if(this.prev=0,this.next=0,this.sent=this._sent=r,this.done=!1,this.delegate=null,this.method="next",this.arg=r,this.tryEntries.forEach(R),!e)for(var t in this)"t"===t.charAt(0)&&i.call(this,t)&&!isNaN(+t.slice(1))&&(this[t]=r)},stop:function(){this.done=!0;var e=this.tryEntries[0].completion;if("throw"===e.type)throw e.arg;return this.rval},dispatchException:function(e){if(this.done)throw e;var t=this;function n(n,i){return o.type="throw",o.arg=e,t.next=n,i&&(t.method="next",t.arg=r),!!i}for(var a=this.tryEntries.length-1;a>=0;--a){var s=this.tryEntries[a],o=s.completion;if("root"===s.tryLoc)return n("end");if(s.tryLoc<=this.prev){var l=i.call(s,"catchLoc"),c=i.call(s,"finallyLoc");if(l&&c){if(this.prev=0;--r){var n=this.tryEntries[r];if(n.tryLoc<=this.prev&&i.call(n,"finallyLoc")&&this.prev=0;--t){var r=this.tryEntries[t];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),R(r),y}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.tryLoc===e){var n=r.completion;if("throw"===n.type){var i=n.arg;R(r)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,n){return this.delegate={iterator:C(e),resultName:t,nextLoc:n},"next"===this.method&&(this.arg=r),y}}}function _(e,t,r,n){var i=t&&t.prototype instanceof E?t:E,a=Object.create(i.prototype),s=new I(n||[]);return a._invoke=function(e,t,r){var n=p;return function(i,a){if(n===d)throw new Error("Generator is already running");if(n===f){if("throw"===i)throw a;return A()}for(r.method=i,r.arg=a;;){var s=r.delegate;if(s){var o=S(s,r);if(o){if(o===y)continue;return o}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if(n===p)throw n=f,r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n=d;var l=k(e,t,r);if("normal"===l.type){if(n=r.done?f:h,l.arg===y)continue;return{value:l.arg,done:r.done}}"throw"===l.type&&(n=f,r.method="throw",r.arg=l.arg)}}}(e,r,s),a}function k(e,t,r){try{return{type:"normal",arg:e.call(t,r)}}catch(e){return{type:"throw",arg:e}}}function E(){}function x(){}function w(){}function O(e){["next","throw","return"].forEach(function(t){e[t]=function(e){return this._invoke(t,e)}})}function $(e){var t;this._invoke=function(r,n){function a(){return new Promise(function(t,a){!function t(r,n,a,s){var o=k(e[r],e,n);if("throw"!==o.type){var l=o.arg,c=l.value;return c&&"object"==typeof c&&i.call(c,"__await")?Promise.resolve(c.__await).then(function(e){t("next",e,a,s)},function(e){t("throw",e,a,s)}):Promise.resolve(c).then(function(e){l.value=e,a(l)},s)}s(o.arg)}(r,n,t,a)})}return t=t?t.then(a,a):a()}}function S(e,t){var n=e.iterator[t.method];if(n===r){if(t.delegate=null,"throw"===t.method){if(e.iterator.return&&(t.method="return",t.arg=r,S(e,t),"throw"===t.method))return y;t.method="throw",t.arg=new TypeError("The iterator does not provide a 'throw' method")}return y}var i=k(n,e.iterator,t.arg);if("throw"===i.type)return t.method="throw",t.arg=i.arg,t.delegate=null,y;var a=i.arg;return a?a.done?(t[e.resultName]=a.value,t.next=e.nextLoc,"return"!==t.method&&(t.method="next",t.arg=r),t.delegate=null,y):a:(t.method="throw",t.arg=new TypeError("iterator result is not an object"),t.delegate=null,y)}function T(e){var t={tryLoc:e[0]};1 in e&&(t.catchLoc=e[1]),2 in e&&(t.finallyLoc=e[2],t.afterLoc=e[3]),this.tryEntries.push(t)}function R(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function I(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(T,this),this.reset(!0)}function C(e){if(e){var t=e[s];if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var n=-1,a=function t(){for(;++n0&&void 0!==arguments[0]?arguments[0]:{};var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";var n=[];var i="";var a=l()({},r,{});var o={};s()(t).forEach(function(t){var s=h()(t,2),l=s[0],c=s[1];if("$resource"!==l){var p=c.format,d=c.enum,f=c.input,y=c.order,v=c.items,m=c.title,b=c.type,g=c.description,_=c.default;"object"===(void 0===m?"undefined":u()(m))&&(m=m[E]),"object"===(void 0===g?"undefined":u()(g))&&(g=g[E]);var k=_||"";w.includes(p)&&(k={url:"http://"}[p]);var x={placeholder:k=k.toString()};if((d||"boolean"===b)&&(b="emq-select",x.field=d?{options:d.map(function(e){return{value:e,label:e}})}:{options:[{label:!0,value:!0},{label:!1,value:!1}]}),"object"!==b||_||(_={}),"array"===b&&"object"===v.type){var $=v.schema;o=e($,"config"),_.length||(_=[])}"textarea"===f&&(x.type="textarea",x.rows=5),n.push({key:l,type:b,label:m||l,prop:l,defaultValue:_,$attrs:x,description:(g||"").replace(/\n/g,"
"),order:y,oneObjOfArray:o}),r?a[r][l]=O(t):a[l]=O(t)}else i="string"});n=n.sort(function(e,t){return e.order-t.order});return{model:n,rules:a,resource:i}},t.b=function(e){if("zh"===E)return _[e];return k[e]},t.e=function(e,t,r){return new i.a(function(n,i){try{var a=e.filter(function(e){if(e[t]){var n=e[t].toLowerCase().replace(/\s+/g,""),i=r.toLocaleLowerCase().replace(/\s+/g,"");return n.match(i)}return null});return n(a)}catch(e){return i(e)}})},t.h=function(e){var t=e.replace(/\"/g,""),r=null;return["message.publish","message.deliver","message.acked","message.dropped","client.connected","client.disconnected","client.subscribe","client.unsubscribe"].forEach(function(e){var n=e.split("."),i=h()(n,2),a=i[0],s=i[1],o=new RegExp(a+"\\."+s,"gim");t.match(o)&&(r=t.match(o))}),r},t.g=function(e,t){var r={"message.publish":"","message.deliver":"$events/message_delivered","message.acked":"$events/message_acked","message.dropped":"$events/message_dropped","client.connected":"$events/client_connected","client.disconnected":"$events/client_disconnected","client.subscribe":"$events/session_subscribed","client.unsubscribe":"$events/session_unsubscribed"}[t],n=e.replace(/\"/g,""),i=m.a.parse(n);""===r&&(i.value.where=null,r="#");return i.value.from.value[0].value.value.value='"'+r+'"',m.a.stringify(i)},t.a=function(e,t){var r=(t-e)%864e5,n=Math.floor(r/36e5),i=r%36e5,a=Math.floor(i/6e4),s=i%6e4,o=Math.round(s/1e3);return n+":"+a+":"+o},r.d(t,"i",function(){return S}),r.d(t,"d",function(){return T});var E=window.localStorage.language||window.EMQX_DASHBOARD_CONFIG.lang||"en",x={is_required:{en:"is required",zh:"必填"}},w=["string","number","boolean","method","regexp","integer","float","array","object","enum","date","url","hex","email"];function O(e){var t=h()(e,2),r=t[0],n=t[1],i=(n.type,n.format,n.required),a=(n.enum,n.title);"object"===(void 0===a?"undefined":u()(a))&&(a=a[E]);var s={};i&&(s.required=!0,s.message=(a||r)+" "+x.is_required[E])}function $(e,t){var r=new y.a(t.target,{text:function(){return e}});r.on("success",function(){d.default.prototype.$message({message:d.default.prototype.$t("oper.copySuccess"),type:"success",duration:1500}),r.destroy()}),r.on("error",function(){d.default.prototype.$message({message:d.default.prototype.$t("oper.copyFailed"),type:"error"}),r.destroy()}),r.onClick(t)}var S=function(e,t,r){t?t.length>64?r(new Error(d.default.prototype.$t("rule.id_len_tip"))):/^[0-9a-zA-Z_:]{1,64}$/.test(t)?r():r(new Error(d.default.prototype.$t("rule.id_char_tip"))):r(new Error("ID "+d.default.prototype.$t("rule.is_required")))},T=function(e,t){return e&&e.length>t?e.substring(0,t)+"...":e}},SHGx:function(e,t,r){"use strict";var n=r("pFYg"),i=r.n(n),a=r("fZjL"),s=r.n(a),o=r("Dd8w"),l=r.n(o),c=r("FcGO"),u=r("d7EF"),p=r.n(u),h=r("W3Iv"),d=r.n(h),f=r("woOf"),y=r.n(f),v={name:"ArrayEditor",components:{EmqSelect:c.a},model:{prop:"value",event:"update"},props:{value:{type:Array,required:!0},notNull:{type:Boolean,default:!1},data:{type:Object,required:!0}},data:function(){return{tableData:[],headers:[],oneRow:{},defaultConfig:{}}},created:function(){this.initData()},methods:{assignValue:function(){var e=this;if(this.value.length){for(var t=0;t0&&void 0!==arguments[0])||arguments[0];this.$refs.record.validate(function(r){if(r){var n=e.record.config;s()(n).forEach(function(t){var r=n[t];"true"===r&&(e.record.config[t]=!0),"false"===r&&(e.record.config[t]=!1)});var i=t?"/resources":"/resources?test=true";e.$httpPost(i,e.record).then(function(r){t?(e.$message.success(e.$t("rule.create_success")),e.dialogVisible=!1,e.$emit("confirm",r.data)):e.$message.success(e.$t("rule.conf_test_success"))}).catch(function(){})}})},handleTypeChange:function(e){this.paramsList=[],this.resourceRules={};var t=this.resourceTypes.find(function(t){return t.name===e});if(t){var r=Object(g.f)(t.params,"config"),n=r.model,i=r.rules;this.resourceRules=i,this.paramsList=n,this.initRecord(),setTimeout(this.clearTabIndex,500)}},initRecord:function(){var e=this;0===this.paramsList.length?this.$set(this.record,"config",void 0):this.record.config||this.$set(this.record,"config",{}),this.$set(this.record,"config",{}),this.paramsList.forEach(function(t){e.$set(e.record.config,t.key,t.defaultValue)}),setTimeout(function(){e.$refs.record.clearValidate()},30)},loadResourceTypes:function(){var e=this;this.$httpGet("/resource_types").then(function(t){e.record={type:"",config:{},description:"",id:"resource:"+Math.random().toString().slice(3,9)},e.resourceType&&(e.record.type=e.resourceType),e.resourceTypes=t.data.map(function(e){return e.titleLabel="object"===i()(e.title)?e.title[_]:e.title,e}),e.handleTypeChange(e.record.type),setTimeout(function(){e.$refs.record.clearValidate()},30)})}}},E={render:function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("el-dialog",e._b({staticClass:"resource-dialog",attrs:{width:"700px",visible:e.dialogVisible,title:e.$t("rule.resource_mgmt")},on:{"update:visible":function(t){e.dialogVisible=t},close:e.close,open:e.loadResourceTypes}},"el-dialog",e.$attrs,!1),[r("el-form",{ref:"record",staticClass:"el-form--public",attrs:{model:e.record,rules:e.rules}},[r("el-row",{attrs:{gutter:20}},[r("el-col",{attrs:{span:12}},[r("el-form-item",{attrs:{prop:"type",label:e.$t("rule.resource_type")}},[r("el-select",{staticClass:"el-select--public",staticStyle:{width:"100%"},attrs:{"popper-class":"el-select--public",disabled:!!e.resourceType},on:{change:e.handleTypeChange},model:{value:e.record.type,callback:function(t){e.$set(e.record,"type",t)},expression:"record.type"}},e._l(e.resourceTypes,function(t,n){return r("div",{key:n},[0===e.enableItem.length||e.enableItem.includes(t.name)?r("el-option",{attrs:{label:t.titleLabel,value:t.name}}):e._e()],1)}),0)],1)],1),e._v(" "),r("el-col",{attrs:{span:12}},[r("el-form-item",[r("template",{slot:"label"},[e._v(" ")]),e._v(" "),r("el-button",{attrs:{type:"primary"},on:{click:function(t){return e.handleCreate(!1)}}},[e._v("\n "+e._s(e.$t("rule.conf_test"))+"\n ")])],2)],1),e._v(" "),e.record.type?[r("el-col",{attrs:{span:12}},[r("el-form-item",{attrs:{prop:"id",label:e.$t("rule.resource_id")}},[r("el-input",{model:{value:e.record.id,callback:function(t){e.$set(e.record,"id",t)},expression:"record.id"}})],1)],1),e._v(" "),r("el-col",{attrs:{span:12}},[r("el-form-item",{attrs:{prop:"description",label:e.$t("rule.resource_des")}},[r("el-input",{attrs:{type:"textarea"},model:{value:e.record.description,callback:function(t){e.$set(e.record,"description",t)},expression:"record.description"}})],1)],1),e._v(" "),e._l(e.paramsList,function(t,n){return r("el-col",{key:n,attrs:{span:"object"===t.type||"array"===t.type||"textarea"===t.$attrs.type?24:12}},[r("el-form-item",{attrs:{prop:"config."+t.prop}},[r("template",{slot:"label"},[e._v("\n "+e._s(t.label)+"\n\n "),t.description?r("el-popover",{attrs:{placement:"right",width:"200",trigger:"hover"}},[r("div",{domProps:{innerHTML:e._s(t.description)}}),e._v(" "),r("span",{staticClass:"el-icon-question",attrs:{slot:"reference",tabindex:"-1"},slot:"reference"})]):e._e()],1),e._v(" "),"object"===t.type?r("data-table",{model:{value:e.record.config[t.key],callback:function(r){e.$set(e.record.config,t.key,r)},expression:"record.config[item.key]"}}):"array"===t.type?[r("array-editor",{attrs:{data:t.oneObjOfArray},model:{value:e.record.config[t.key],callback:function(r){e.$set(e.record.config,t.key,r)},expression:"record.config[item.key]"}})]:"emq-select"===t.type?r("emq-select",e._b({staticClass:"el-select--public",attrs:{"popper-class":"el-select--public"},model:{value:e.record.config[t.key],callback:function(r){e.$set(e.record.config,t.key,r)},expression:"record.config[item.key]"}},"emq-select",t.$attrs,!1)):"number"===t.type?r("el-input",e._b({attrs:{type:"number"},model:{value:e.record.config[t.key],callback:function(r){e.$set(e.record.config,t.key,e._n(r))},expression:"record.config[item.key]"}},"el-input",t.$attrs,!1)):r("el-input",e._b({model:{value:e.record.config[t.key],callback:function(r){e.$set(e.record.config,t.key,r)},expression:"record.config[item.key]"}},"el-input",t.$attrs,!1))],2)],1)})]:e._e()],2)],1),e._v(" "),r("div",{attrs:{slot:"footer"},slot:"footer"},[r("el-button",{staticClass:"cache-btn",attrs:{type:"text"},on:{click:function(t){e.dialogVisible=!1}}},[e._v("\n "+e._s(e.$t("rule.cancel"))+"\n ")]),e._v(" "),r("el-button",{staticClass:"confirm-btn",attrs:{type:"success"},on:{click:e.handleCreate}},[e._v("\n "+e._s(e.$t("rule.create"))+"\n ")])],1)],1)},staticRenderFns:[]};var x=r("VU/8")(k,E,!1,function(e){r("G5A+")},null,null);t.a=x.exports},TQvf:function(e,t,r){ +/*! + * clipboard.js v2.0.6 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +var n;n=function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var i=t[n]={i:n,l:!1,exports:{}};return e[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)r.d(n,i,function(t){return e[t]}.bind(null,i));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=6)}([function(e,t){e.exports=function(e){var t;if("SELECT"===e.nodeName)e.focus(),t=e.value;else if("INPUT"===e.nodeName||"TEXTAREA"===e.nodeName){var r=e.hasAttribute("readonly");r||e.setAttribute("readonly",""),e.select(),e.setSelectionRange(0,e.value.length),r||e.removeAttribute("readonly"),t=e.value}else{e.hasAttribute("contenteditable")&&e.focus();var n=window.getSelection(),i=document.createRange();i.selectNodeContents(e),n.removeAllRanges(),n.addRange(i),t=n.toString()}return t}},function(e,t){function r(){}r.prototype={on:function(e,t,r){var n=this.e||(this.e={});return(n[e]||(n[e]=[])).push({fn:t,ctx:r}),this},once:function(e,t,r){var n=this;function i(){n.off(e,i),t.apply(r,arguments)}return i._=t,this.on(e,i,r)},emit:function(e){for(var t=[].slice.call(arguments,1),r=((this.e||(this.e={}))[e]||[]).slice(),n=0,i=r.length;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=e.action,this.container=e.container,this.emitter=e.emitter,this.target=e.target,this.text=e.text,this.trigger=e.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var e=this,t="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return e.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[t?"right":"left"]="-9999px";var r=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=r+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=i()(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=i()(this.target),this.copyText()}},{key:"copyText",value:function(){var e=void 0;try{e=document.execCommand(this.action)}catch(t){e=!1}this.handleResult(e)}},{key:"handleResult",value:function(e){this.emitter.emit(e?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),document.activeElement.blur(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=e,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(e){if(void 0!==e){if(!e||"object"!==(void 0===e?"undefined":a(e))||1!==e.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&e.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(e.hasAttribute("readonly")||e.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=e}},get:function(){return this._target}}]),e}(),l=r(1),c=r.n(l),u=r(2),p=r.n(u),h="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},d=function(){function e(e,t){for(var r=0;r0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof e.action?e.action:this.defaultAction,this.target="function"==typeof e.target?e.target:this.defaultTarget,this.text="function"==typeof e.text?e.text:this.defaultText,this.container="object"===h(e.container)?e.container:document.body}},{key:"listenClick",value:function(e){var t=this;this.listener=p()(e,"click",function(e){return t.onClick(e)})}},{key:"onClick",value:function(e){var t=e.delegateTarget||e.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new o({action:this.action(t),target:this.target(t),text:this.text(t),container:this.container,trigger:t,emitter:this})}},{key:"defaultAction",value:function(e){return y("action",e)}},{key:"defaultTarget",value:function(e){var t=y("target",e);if(t)return document.querySelector(t)}},{key:"defaultText",value:function(e){return y("text",e)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],t="string"==typeof e?[e]:e,r=!!document.queryCommandSupported;return t.forEach(function(e){r=r&&!!document.queryCommandSupported(e)}),r}}]),t}();function y(e,t){var r="data-clipboard-"+e;if(t.hasAttribute(r))return t.getAttribute(r)}t.default=f}]).default},e.exports=n()},Xxa5:function(e,t,r){e.exports=r("1H6C")},cMrt:function(e,t){e.exports={en:{emqx_auth_clientid:"https://docs.emqx.io/broker/latest/en/advanced/auth-clientid.html",emqx_auth_username:"https://docs.emqx.io/broker/latest/en/advanced/auth-username.html",emqx_auth_http:"https://docs.emqx.io/broker/latest/en/advanced/auth-http.html",emqx_auth_jwt:"https://docs.emqx.io/broker/latest/en/advanced/auth-jwt.html",emqx_auth_ldap:"https://docs.emqx.io/broker/latest/en/advanced/auth-ldap.html",emqx_auth_mnesia:"https://docs.emqx.io/broker/latest/en/advanced/auth-mnesia.html",emqx_auth_mongo:"https://docs.emqx.io/broker/latest/en/advanced/auth-mongodb.html",emqx_auth_mysql:"https://docs.emqx.io/broker/latest/en/advanced/auth-mysql.html",emqx_auth_pgsql:"https://docs.emqx.io/broker/latest/en/advanced/auth-postgresql.html",emqx_auth_redis:"https://docs.emqx.io/broker/latest/en/advanced/auth-redis.html",emqx_dashboard:"https://docs.emqx.io/broker/latest/en/getting-started/dashboard.html",emqx_extension_hook:"https://docs.emqx.io/broker/latest/en/advanced/multiple-language-support.html",emqx_rule_engine:"https://docs.emqx.io/broker/latest/en/rule/rule-engine.html"},zh:{emqx_auth_clientid:"https://docs.emqx.io/broker/latest/cn/advanced/auth-clientid.html",emqx_auth_username:"https://docs.emqx.io/broker/latest/cn/advanced/auth-username.html",emqx_auth_http:"https://docs.emqx.io/broker/latest/cn/advanced/auth-http.html",emqx_auth_jwt:"https://docs.emqx.io/broker/latest/cn/advanced/auth-jwt.html",emqx_auth_ldap:"https://docs.emqx.io/broker/latest/cn/advanced/auth-ldap.html",emqx_auth_mnesia:"https://docs.emqx.io/broker/latest/cn/advanced/auth-mnesia.html",emqx_auth_mongo:"https://docs.emqx.io/broker/latest/cn/advanced/auth-mongodb.html",emqx_auth_mysql:"https://docs.emqx.io/broker/latest/cn/advanced/auth-mysql.html",emqx_auth_pgsql:"https://docs.emqx.io/broker/latest/cn/advanced/auth-postgresql.html",emqx_auth_redis:"https://docs.emqx.io/broker/latest/cn/advanced/auth-redis.html",emqx_dashboard:"https://docs.emqx.io/broker/latest/cn/getting-started/dashboard.html",emqx_extension_hook:"https://docs.emqx.io/broker/latest/cn/advanced/multiple-language-support.html",emqx_rule_engine:"https://docs.emqx.io/broker/latest/cn/rule/rule-engine.html"}}},dmxg:function(e,t,r){var n,i=function(){var e=function(e,t,r,n){for(r=r||{},n=e.length;n--;r[e[n]]=t);return r},t=[1,8],r=[1,4],n=[2,4],i=[1,11],a=[1,10],s=[2,16],o=[1,14],l=[1,15],c=[1,16],u=[6,8],p=[2,144],h=[1,19],d=[1,20],f=[16,33,35,36,37,38,39,40,41,42,45,46,50,52,53,55,56,58,59,61,76,79,81,82,83,84,86,87,88,101],y=[16,18,32,33,35,36,37,38,39,40,41,42,45,46,50,52,53,55,56,58,59,61,76,79,81,82,83,84,86,87,88,101],v=[2,158],m=[1,29],b=[6,8,14,17,146,150,152,154],g=[1,42],_=[1,60],k=[1,51],E=[1,58],x=[1,59],w=[1,61],O=[1,62],$=[1,63],S=[1,64],T=[1,65],R=[1,57],I=[1,52],C=[1,53],A=[1,54],L=[1,55],N=[1,56],q=[1,43],F=[1,44],K=[1,45],P=[1,34],D=[16,35,36,37,38,39,40,41,42,45,46,50,52,53,55,56,58,59,61,76,79,81,82,83,84,86,87,88,101],j=[6,8,14,17,150,152,154],B=[2,141],U=[1,74],H=[1,75],M=[6,8,14,17,43,133,138,144,146,150,152,154],G=[1,80],V=[1,77],Q=[1,78],W=[1,79],X=[1,81],J=[6,8,14,17,36,43,49,50,71,72,74,77,89,107,124,125,126,127,129,133,135,138,141,142,144,146,150,152,154,157,164,165,167,168,173,177,179,180,182],z=[6,8,14,17,34,36,43,49,50,71,72,74,77,89,107,112,113,114,115,116,117,121,124,125,126,127,129,133,135,138,141,142,144,146,150,152,154,157,164,165,167,168,173,177,179,180,182],Y=[1,102],Z=[1,100],ee=[1,101],te=[1,96],re=[1,97],ne=[1,98],ie=[1,99],ae=[1,103],se=[1,104],oe=[1,105],le=[1,106],ce=[1,107],ue=[1,108],pe=[2,101],he=[6,8,14,17,34,36,43,45,49,50,71,72,74,77,79,81,89,91,92,93,94,95,96,97,98,99,101,105,106,107,108,109,110,112,113,114,115,116,117,121,124,125,126,127,129,133,135,138,141,142,144,146,150,152,154,157,164,165,167,168,173,177,179,180,182],de=[6,8,14,17,34,36,43,45,49,50,71,72,74,77,79,81,89,91,92,93,94,95,96,97,98,99,101,103,105,106,107,108,109,110,112,113,114,115,116,117,121,124,125,126,127,129,133,135,138,141,142,144,146,150,152,154,157,164,165,167,168,173,177,179,180,182],fe=[1,109],ye=[1,116],ve=[2,62],me=[1,117],be=[16,35,37,38,39,40,41,42,45,46,50,52,53,55,56,58,59,61,76,79,81,82,83,84,86,87,88,101],ge=[16,29,35,50,52,53,55,56,58,59,61,76,79,81,82,83,84,86,87,88,119],_e=[1,163],ke=[17,43],Ee=[2,57],xe=[1,172],we=[1,170],Oe=[1,171],$e=[6,8,138,146],Se=[16,35,38,39,40,41,42,45,46,50,52,53,55,56,58,59,61,76,79,81,82,83,84,86,87,88,101],Te=[6,8,14,17,138,144,146,150,152,154],Re=[6,8,14,17,36,43,49,50,71,72,74,77,89,125,126,127,129,133,135,138,141,142,144,146,150,152,154,157,164,165,167,168,173,177,179,180,182],Ie=[6,8,14,17,34,36,43,49,50,71,72,74,77,89,91,92,93,94,99,101,105,106,107,108,109,110,112,113,114,115,116,117,121,124,125,126,127,129,133,135,138,141,142,144,146,150,152,154,157,164,165,167,168,173,177,179,180,182],Ce=[6,8,14,17,34,36,43,49,50,71,72,74,77,79,81,89,91,92,93,94,99,101,105,106,107,108,109,110,112,113,114,115,116,117,121,124,125,126,127,129,133,135,138,141,142,144,146,150,152,154,157,164,165,167,168,173,177,179,180,182],Ae=[16,35,39,40,41,42,45,46,50,52,53,55,56,58,59,61,76,79,81,82,83,84,86,87,88,101],Le=[16,35,40,41,42,45,46,50,52,53,55,56,58,59,61,76,79,81,82,83,84,86,87,88,101],Ne=[16,35,42,45,46,50,52,53,55,56,58,59,61,76,79,81,82,83,84,86,87,88,101],qe=[71,74,77],Fe=[16,35,45,46,50,52,53,55,56,58,59,61,76,79,81,82,83,84,86,87,88,101],Ke=[1,232],Pe=[1,233],De=[6,8,14,17],je=[6,8,14,17,43,157],Be=[1,249],Ue=[1,245],He=[2,195],Me=[1,252],Ge=[1,253],Ve=[6,8,14,17,43,129,135,138,144,146,150,152,154,182],Qe=[1,255],We=[1,258],Xe=[1,259],Je=[1,260],ze=[1,261],Ye=[2,172],Ze=[1,257],et=[6,8,14,17,36,43,89,129,135,138,144,146,150,152,154,164,165,167,168,173,177,179,180,182],tt=[6,8,14,17,135,138,144,146,150,152,154],rt=[1,273],nt=[2,177],it=[170,173],at=[6,8,14,17,36,43,89,129,135,138,144,146,150,152,154,164,165,167,168,173,177,179,180,182,192,193,194],st=[2,197],ot=[1,278],lt=[1,290],ct=[1,298],ut=[1,299],pt=[1,300],ht=[6,8,14,17,138,146,150,152,154],dt=[1,310],ft=[1,316],yt=[1,317],vt=[2,202],mt=[1,328],bt=[16,152],gt=[6,8,14,17,152,154],_t=[1,344],kt={trace:function(){},yy:{},symbols_:{error:2,main:3,selectClause:4,semicolonOpt:5,EOF:6,unionClause:7,";":8,unionClauseNotParenthesized:9,unionClauseParenthesized:10,order_by_opt:11,limit_opt:12,selectClauseParenthesized:13,UNION:14,distinctOpt:15,"(":16,")":17,SELECT:18,highPriorityOpt:19,maxStateMentTimeOpt:20,straightJoinOpt:21,sqlSmallResultOpt:22,sqlBigResultOpt:23,sqlBufferResultOpt:24,sqlCacheOpt:25,sqlCalcFoundRowsOpt:26,selectExprList:27,selectDataSetOpt:28,ALL:29,DISTINCT:30,DISTINCTROW:31,HIGH_PRIORITY:32,MAX_STATEMENT_TIME:33,"=":34,NUMERIC:35,STRAIGHT_JOIN:36,SQL_SMALL_RESULT:37,SQL_BIG_RESULT:38,SQL_BUFFER_RESULT:39,SQL_CACHE:40,SQL_NO_CACHE:41,SQL_CALC_FOUND_ROWS:42,",":43,selectExpr:44,"*":45,SELECT_EXPR_STAR:46,expr:47,selectExprAliasOpt:48,AS:49,IDENTIFIER:50,string:51,QUOTED_IDENTIFIER:52,STRING:53,number:54,EXPONENT_NUMERIC:55,HEX_NUMERIC:56,boolean:57,TRUE:58,FALSE:59,null:60,NULL:61,literal:62,function_call:63,function_call_param_list:64,function_call_param:65,identifier:66,DOT:67,identifier_list:68,case_expr_opt:69,when_then_list:70,WHEN:71,THEN:72,case_when_else:73,ELSE:74,case_when:75,CASE:76,END:77,simple_expr_prefix:78,"+":79,simple_expr:80,"-":81,"~":82,"!":83,BINARY:84,expr_list:85,ROW:86,EXISTS:87,"{":88,"}":89,bit_expr:90,"|":91,"&":92,"<<":93,">>":94,"/":95,DIV:96,MOD:97,"%":98,"^":99,not_opt:100,NOT:101,escape_opt:102,ESCAPE:103,predicate:104,IN:105,BETWEEN:106,AND:107,SOUNDS:108,LIKE:109,REGEXP:110,comparison_operator:111,">=":112,">":113,"<=":114,"<":115,"<>":116,"!=":117,sub_query_data_set_opt:118,ANY:119,boolean_primary:120,IS:121,boolean_extra:122,UNKNOWN:123,"&&":124,"||":125,OR:126,XOR:127,where_opt:128,WHERE:129,group_by_opt:130,group_by:131,roll_up_opt:132,WITH:133,ROLLUP:134,GROUP_BY:135,group_by_order_by_item_list:136,order_by:137,ORDER_BY:138,group_by_order_by_item:139,sort_opt:140,ASC:141,DESC:142,having_opt:143,HAVING:144,limit:145,LIMIT:146,OFFSET:147,procedure_opt:148,procedure:149,PROCEDURE:150,for_update_lock_in_share_mode_opt:151,FOR:152,UPDATE:153,LOCK:154,SHARE:155,MODE:156,FROM:157,table_references:158,partitionOpt:159,escaped_table_reference:160,table_reference:161,OJ:162,join_inner_cross:163,INNER:164,CROSS:165,left_right:166,LEFT:167,RIGHT:168,out_opt:169,OUTER:170,left_right_out_opt:171,join_table:172,JOIN:173,table_factor:174,join_condition:175,on_join_condition:176,NATURAL:177,join_condition_opt:178,ON:179,USING:180,partition_names:181,PARTITION:182,aliasOpt:183,index_or_key:184,INDEX:185,KEY:186,for_opt:187,identifier_list_opt:188,index_hint_list_opt:189,index_hint_list:190,index_hint:191,USE:192,IGNORE:193,FORCE:194,$accept:0,$end:1},terminals_:{2:"error",6:"EOF",8:";",14:"UNION",16:"(",17:")",18:"SELECT",29:"ALL",30:"DISTINCT",31:"DISTINCTROW",32:"HIGH_PRIORITY",33:"MAX_STATEMENT_TIME",34:"=",35:"NUMERIC",36:"STRAIGHT_JOIN",37:"SQL_SMALL_RESULT",38:"SQL_BIG_RESULT",39:"SQL_BUFFER_RESULT",40:"SQL_CACHE",41:"SQL_NO_CACHE",42:"SQL_CALC_FOUND_ROWS",43:",",45:"*",46:"SELECT_EXPR_STAR",49:"AS",50:"IDENTIFIER",52:"QUOTED_IDENTIFIER",53:"STRING",55:"EXPONENT_NUMERIC",56:"HEX_NUMERIC",58:"TRUE",59:"FALSE",61:"NULL",67:"DOT",71:"WHEN",72:"THEN",74:"ELSE",76:"CASE",77:"END",79:"+",81:"-",82:"~",83:"!",84:"BINARY",86:"ROW",87:"EXISTS",88:"{",89:"}",91:"|",92:"&",93:"<<",94:">>",95:"/",96:"DIV",97:"MOD",98:"%",99:"^",101:"NOT",103:"ESCAPE",105:"IN",106:"BETWEEN",107:"AND",108:"SOUNDS",109:"LIKE",110:"REGEXP",112:">=",113:">",114:"<=",115:"<",116:"<>",117:"!=",119:"ANY",121:"IS",123:"UNKNOWN",124:"&&",125:"||",126:"OR",127:"XOR",129:"WHERE",133:"WITH",134:"ROLLUP",135:"GROUP_BY",138:"ORDER_BY",141:"ASC",142:"DESC",144:"HAVING",146:"LIMIT",147:"OFFSET",150:"PROCEDURE",152:"FOR",153:"UPDATE",154:"LOCK",155:"SHARE",156:"MODE",157:"FROM",162:"OJ",164:"INNER",165:"CROSS",167:"LEFT",168:"RIGHT",170:"OUTER",173:"JOIN",177:"NATURAL",179:"ON",180:"USING",182:"PARTITION",185:"INDEX",186:"KEY",192:"USE",193:"IGNORE",194:"FORCE"},productions_:[0,[3,3],[3,3],[5,1],[5,0],[7,1],[7,3],[10,4],[10,4],[13,3],[9,4],[9,4],[4,12],[15,1],[15,1],[15,1],[15,0],[19,1],[19,0],[20,3],[20,0],[21,1],[21,0],[22,1],[22,0],[23,1],[23,0],[24,1],[24,0],[25,0],[25,1],[25,1],[26,1],[26,0],[27,3],[27,1],[44,1],[44,1],[44,2],[48,0],[48,2],[48,1],[51,1],[51,1],[54,1],[54,1],[54,1],[57,1],[57,1],[60,1],[62,1],[62,1],[62,1],[62,1],[63,4],[64,3],[64,1],[65,0],[65,1],[65,1],[65,2],[65,1],[66,1],[66,3],[68,1],[68,3],[69,0],[69,1],[70,4],[70,5],[73,0],[73,2],[75,5],[78,2],[78,2],[78,2],[78,2],[78,2],[80,1],[80,1],[80,1],[80,1],[80,3],[80,4],[80,3],[80,4],[80,4],[80,1],[90,1],[90,3],[90,3],[90,3],[90,3],[90,3],[90,3],[90,3],[90,3],[90,3],[90,3],[90,3],[90,3],[100,0],[100,1],[102,0],[102,2],[104,1],[104,6],[104,6],[104,6],[104,4],[104,5],[104,4],[111,1],[111,1],[111,1],[111,1],[111,1],[111,1],[111,1],[118,1],[118,1],[120,1],[120,4],[120,3],[120,6],[122,1],[122,1],[47,1],[47,4],[47,2],[47,3],[47,3],[47,3],[47,3],[47,3],[85,1],[85,3],[128,0],[128,2],[130,0],[130,1],[132,0],[132,2],[131,3],[11,0],[11,1],[137,3],[136,1],[136,3],[139,2],[140,0],[140,1],[140,1],[143,0],[143,2],[145,2],[145,4],[145,4],[12,0],[12,1],[148,0],[148,1],[149,2],[151,0],[151,2],[151,4],[28,0],[28,10],[158,1],[158,3],[160,1],[160,4],[163,0],[163,1],[163,1],[166,1],[166,1],[169,0],[169,1],[171,0],[171,2],[172,4],[172,5],[172,4],[172,6],[172,5],[178,0],[178,1],[176,2],[175,1],[175,4],[161,1],[161,1],[181,1],[181,3],[159,0],[159,4],[183,0],[183,2],[183,1],[184,1],[184,1],[187,0],[187,2],[187,2],[187,2],[188,0],[188,1],[189,0],[189,1],[190,1],[190,3],[191,6],[191,6],[191,6],[174,4],[174,4],[174,3]],performAction:function(e,t,r,n,i,a,s){var o=a.length-1;switch(i){case 1:case 2:return{nodeType:"Main",value:a[o-2],hasSemicolon:a[o-1]};case 3:case 142:this.$=!0;break;case 4:this.$=!1;break;case 5:case 13:case 14:case 15:case 17:case 19:case 21:case 23:case 25:case 27:case 30:case 31:case 32:case 50:case 51:case 52:case 53:case 58:case 59:case 61:case 67:case 71:case 78:case 79:case 80:case 81:case 87:case 88:case 102:case 104:case 105:case 112:case 113:case 114:case 115:case 116:case 117:case 118:case 119:case 120:case 121:case 125:case 127:case 138:case 140:case 145:case 151:case 152:case 154:case 159:case 161:case 162:case 173:case 174:case 175:case 176:case 178:case 187:case 189:case 191:case 192:case 200:case 201:case 207:case 209:this.$=a[o];break;case 6:this.$=a[o-2],this.$.orderBy=a[o-1],this.$.limit=a[o];break;case 7:case 8:this.$={type:"Union",left:a[o-3],distinctOpt:a[o-1],right:a[o]};break;case 9:this.$={type:"SelectParenthesized",value:a[o-1]};break;case 10:case 11:this.$={type:"Union",left:a[o-3],distinctOpt:a[o-1],right:a[o]};break;case 12:this.$={type:"Select",distinctOpt:a[o-10],highPriorityOpt:a[o-9],maxStateMentTimeOpt:a[o-8],straightJoinOpt:a[o-7],sqlSmallResultOpt:a[o-6],sqlBigResultOpt:a[o-5],sqlBufferResultOpt:a[o-4],sqlCacheOpt:a[o-3],sqlCalcFoundRowsOpt:a[o-2],selectItems:a[o-1],from:a[o].from,partition:a[o].partition,where:a[o].where,groupBy:a[o].groupBy,having:a[o].having,orderBy:a[o].orderBy,limit:a[o].limit,procedure:a[o].procedure,updateLockMode:a[o].updateLockMode};break;case 16:case 18:case 20:case 22:case 24:case 26:case 28:case 29:case 33:case 57:case 66:case 70:case 101:case 103:case 137:case 139:case 141:case 144:case 150:case 153:case 158:case 160:case 163:case 172:case 177:case 186:case 195:case 202:case 206:case 208:this.$=null;break;case 34:a[o-2].value.push(a[o]);break;case 35:this.$={type:"SelectExpr",value:[a[o]]};break;case 36:case 37:case 62:this.$={type:"Identifier",value:a[o]};break;case 38:this.$=a[o-1],this.$.alias=a[o].alias,this.$.hasAs=a[o].hasAs;break;case 39:case 197:this.$={alias:null,hasAs:null};break;case 40:this.$={alias:a[o],hasAs:!0};break;case 41:this.$={alias:a[o],hasAs:!1};break;case 42:case 43:this.$={type:"String",value:a[o]};break;case 44:case 45:case 46:this.$={type:"Number",value:a[o]};break;case 47:this.$={type:"Boolean",value:"TRUE"};break;case 48:this.$={type:"Boolean",value:"FALSE"};break;case 49:this.$={type:"Null",value:"null"};break;case 54:this.$={type:"FunctionCall",name:a[o-3],params:a[o-1]};break;case 55:a[o-2].push(a[o]),this.$=a[o-2];break;case 56:this.$=[a[o]];break;case 60:this.$={type:"FunctionCallParam",distinctOpt:a[o-1],value:a[o]};break;case 63:this.$=a[o-2],a[o-2].value+="."+a[o];break;case 64:this.$={type:"IdentifierList",value:[a[o]]};break;case 65:case 169:this.$=a[o-2],a[o-2].value.push(a[o]);break;case 68:this.$={type:"WhenThenList",value:[{when:a[o-2],then:a[o]}]};break;case 69:this.$=a[o-4],this.$.value.push({when:a[o-2],then:a[o]});break;case 72:this.$={type:"CaseWhen",caseExprOpt:a[o-3],whenThenList:a[o-2],else:a[o-1]};break;case 73:case 74:case 75:case 76:case 77:this.$={type:"Prefix",prefix:a[o-1],value:a[o]};break;case 82:this.$={type:"SimpleExprParentheses",value:a[o-1]};break;case 83:this.$={type:"SimpleExprParentheses",value:a[o-2],hasRow:!0};break;case 84:this.$={type:"SubQuery",value:a[o-1]};break;case 85:this.$={type:"SubQuery",value:a[o-1],hasExists:!0};break;case 86:this.$={type:"IdentifierExpr",identifier:a[o-2],value:a[o-1]};break;case 89:this.$={type:"BitExpression",operator:"|",left:a[o-2],right:a[o]};break;case 90:this.$={type:"BitExpression",operator:"&",left:a[o-2],right:a[o]};break;case 91:this.$={type:"BitExpression",operator:"<<",left:a[o-2],right:a[o]};break;case 92:this.$={type:"BitExpression",operator:">>",left:a[o-2],right:a[o]};break;case 93:this.$={type:"BitExpression",operator:"+",left:a[o-2],right:a[o]};break;case 94:this.$={type:"BitExpression",operator:"-",left:a[o-2],right:a[o]};break;case 95:this.$={type:"BitExpression",operator:"*",left:a[o-2],right:a[o]};break;case 96:this.$={type:"BitExpression",operator:"/",left:a[o-2],right:a[o]};break;case 97:this.$={type:"BitExpression",operator:"DIV",left:a[o-2],right:a[o]};break;case 98:this.$={type:"BitExpression",operator:"MOD",left:a[o-2],right:a[o]};break;case 99:this.$={type:"BitExpression",operator:"%",left:a[o-2],right:a[o]};break;case 100:this.$={type:"BitExpression",operator:"^",left:a[o-2],right:a[o]};break;case 106:this.$={type:"InSubQueryPredicate",hasNot:a[o-4],left:a[o-5],right:a[o-1]};break;case 107:this.$={type:"InExpressionListPredicate",hasNot:a[o-4],left:a[o-5],right:a[o-1]};break;case 108:this.$={type:"BetweenPredicate",hasNot:a[o-4],left:a[o-5],right:{left:a[o-2],right:a[o]}};break;case 109:this.$={type:"SoundsLikePredicate",hasNot:!1,left:a[o-3],right:a[o]};break;case 110:this.$={type:"LikePredicate",hasNot:a[o-3],left:a[o-4],right:a[o-1],escape:a[o]};break;case 111:this.$={type:"RegexpPredicate",hasNot:a[o-2],left:a[o-3],right:a[o]};break;case 122:this.$={type:"IsNullBooleanPrimary",hasNot:a[o-1],value:a[o-3]};break;case 123:this.$={type:"ComparisonBooleanPrimary",left:a[o-2],operator:a[o-1],right:a[o]};break;case 124:this.$={type:"ComparisonSubQueryBooleanPrimary",operator:a[o-4],subQueryOpt:a[o-3],left:a[o-5],right:a[o-1]};break;case 126:this.$={type:"BooleanExtra",value:a[o]};break;case 128:this.$={type:"IsExpression",hasNot:a[o-1],left:a[o-3],right:a[o]};break;case 129:this.$={type:"NotExpression",value:a[o]};break;case 130:case 133:this.$={type:"AndExpression",operator:a[o-1],left:a[o-2],right:a[o]};break;case 131:case 132:this.$={type:"OrExpression",operator:a[o-1],left:a[o-2],right:a[o]};break;case 134:this.$={type:"XORExpression",left:a[o-2],right:a[o]};break;case 135:this.$={type:"ExpressionList",value:[a[o]]};break;case 136:case 211:this.$=a[o-2],this.$.value.push(a[o]);break;case 143:this.$={type:"GroupBy",value:a[o-1],rollUp:a[o]};break;case 146:this.$={type:"OrderBy",value:a[o-1],rollUp:a[o]};break;case 147:case 193:this.$=[a[o]];break;case 148:this.$=a[o-2],a[o-2].push(a[o]);break;case 149:this.$={type:"GroupByOrderByItem",value:a[o-1],sortOpt:a[o]};break;case 155:this.$={type:"Limit",value:[a[o]]};break;case 156:this.$={type:"Limit",value:[a[o-2],a[o]]};break;case 157:this.$={type:"Limit",value:[a[o],a[o-2]],offsetMode:!0};break;case 164:this.$=a[o-1]+" "+a[o];break;case 165:this.$=a[o-3]+" "+a[o-2]+" "+a[o-1]+" "+a[o];break;case 166:this.$={};break;case 167:this.$={from:a[o-8],partition:a[o-7],where:a[o-6],groupBy:a[o-5],having:a[o-4],orderBy:a[o-3],limit:a[o-2],procedure:a[o-1],updateLockMode:a[o]};break;case 168:this.$={type:"TableReferences",value:[a[o]]};break;case 170:this.$={type:"TableReference",value:a[o]};break;case 171:this.$={type:"TableReference",hasOj:!0,value:a[o-1]};break;case 179:this.$={leftRight:null,outOpt:null};break;case 180:this.$={leftRight:a[o-1],outOpt:a[o]};break;case 181:this.$={type:"InnerCrossJoinTable",innerCrossOpt:a[o-2],left:a[o-3],right:a[o],condition:null};break;case 182:this.$={type:"InnerCrossJoinTable",innerCrossOpt:a[o-3],left:a[o-4],right:a[o-1],condition:a[o]};break;case 183:this.$={type:"StraightJoinTable",left:a[o-3],right:a[o-1],condition:a[o]};break;case 184:this.$={type:"LeftRightJoinTable",leftRight:a[o-4],outOpt:a[o-3],left:a[o-5],right:a[o-1],condition:a[o]};break;case 185:this.$={type:"NaturalJoinTable",leftRight:a[o-2].leftRight,outOpt:a[o-2].outOpt,left:a[o-4],right:a[o]};break;case 188:this.$={type:"OnJoinCondition",value:a[o]};break;case 190:this.$={type:"UsingJoinCondition",value:a[o-1]};break;case 194:this.$=a[o-2],a[o-2].push(a[o]);break;case 196:this.$={type:"Partitions",value:a[o-1]};break;case 198:this.$={hasAs:!0,alias:a[o]};break;case 199:this.$={hasAs:!1,alias:a[o]};break;case 203:case 204:case 205:this.$={type:"ForOptIndexHint",value:a[o]};break;case 210:this.$={type:"IndexHintList",value:[a[o]]};break;case 212:this.$={type:"UseIndexHint",value:a[o-1],forOpt:a[o-3],indexOrKey:a[o-4]};break;case 213:this.$={type:"IgnoreIndexHint",value:a[o-1],forOpt:a[o-3],indexOrKey:a[o-4]};break;case 214:this.$={type:"ForceIndexHint",value:a[o-1],forOpt:a[o-3],indexOrKey:a[o-4]};break;case 215:this.$={type:"TableFactor",value:a[o-3],partition:a[o-2],alias:a[o-1].alias,hasAs:a[o-1].hasAs,indexHintOpt:a[o]};break;case 216:this.$={type:"SubQuery",value:a[o-2],alias:a[o].alias,hasAs:a[o].hasAs};break;case 217:this.$=a[o-1],this.$.hasParentheses=!0}},table:[{3:1,4:2,7:3,9:5,10:6,13:7,16:t,18:r},{1:[3]},{5:9,6:n,8:i,14:a},{5:12,6:n,8:i},e([16,32,33,35,36,37,38,39,40,41,42,45,46,50,52,53,55,56,58,59,61,76,79,81,82,83,84,86,87,88,101],s,{15:13,29:o,30:l,31:c}),e(u,[2,5]),e([6,8,146],p,{11:17,137:18,138:h}),{14:d},{4:21,18:r},{6:[1,22]},{15:23,18:s,29:o,30:l,31:c},{6:[2,3]},{6:[1,24]},e(f,[2,18],{19:25,32:[1,26]}),e(y,[2,13]),e(y,[2,14]),e(y,[2,15]),e(u,v,{12:27,145:28,146:m}),e(b,[2,145]),{16:g,35:_,47:32,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33,136:30,139:31},{15:66,16:s,29:o,30:l,31:c},{17:[1,67]},{1:[2,1]},{4:68,9:69,18:r},{1:[2,2]},e(D,[2,20],{20:70,33:[1,71]}),e(f,[2,17]),e(u,[2,6]),e(j,[2,159]),{35:[1,72]},e(b,B,{132:73,43:U,133:H}),e(M,[2,147]),e(M,[2,150],{140:76,107:G,124:V,125:Q,126:W,127:X,141:[1,82],142:[1,83]}),e(J,[2,127],{111:85,34:[1,86],112:[1,87],113:[1,88],114:[1,89],115:[1,90],116:[1,91],117:[1,92],121:[1,84]}),{16:g,35:_,47:93,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},e(z,[2,121]),e(z,[2,105],{100:94,45:Y,79:Z,81:ee,91:te,92:re,93:ne,94:ie,95:ae,96:se,97:oe,98:le,99:ce,101:ue,105:pe,106:pe,109:pe,110:pe,108:[1,95]}),e(he,[2,88]),e(de,[2,78]),e(de,[2,79],{67:fe}),e(de,[2,80]),e(de,[2,81]),{4:111,16:g,18:r,35:_,47:112,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,85:110,86:q,87:F,88:K,90:36,101:P,104:35,120:33},{16:[1,113]},{16:[1,114]},{50:ye,66:115},e(de,[2,87]),e(de,[2,50]),e(de,[2,51]),e(de,[2,52]),e(de,[2,53]),e([6,8,14,17,34,36,43,45,49,50,67,71,72,74,77,79,81,89,91,92,93,94,95,96,97,98,99,101,103,105,106,107,108,109,110,112,113,114,115,116,117,121,124,125,126,127,129,133,135,138,141,142,144,146,150,152,154,157,164,165,167,168,173,177,179,180,182],ve,{16:me}),{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:118,81:C,82:A,83:L,84:N,86:q,87:F,88:K},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:119,81:C,82:A,83:L,84:N,86:q,87:F,88:K},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:120,81:C,82:A,83:L,84:N,86:q,87:F,88:K},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:121,81:C,82:A,83:L,84:N,86:q,87:F,88:K},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:122,81:C,82:A,83:L,84:N,86:q,87:F,88:K},{16:g,35:_,47:124,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,69:123,71:[2,66],75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},e(de,[2,42]),e(de,[2,43]),e(de,[2,44]),e(de,[2,45]),e(de,[2,46]),e(de,[2,47]),e(de,[2,48]),e(de,[2,49]),{10:126,13:125,16:t},e([6,8,14,138,146],[2,9]),e(u,[2,10],{14:a}),e(u,[2,11]),e(be,[2,22],{21:127,36:[1,128]}),{34:[1,129]},e(j,[2,155],{43:[1,130],147:[1,131]}),e(b,[2,146]),{16:g,35:_,47:32,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33,139:132},{134:[1,133]},e(M,[2,149]),{16:g,35:_,47:134,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},{16:g,35:_,47:135,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},{16:g,35:_,47:136,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},{16:g,35:_,47:137,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},{16:g,35:_,47:138,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},e(M,[2,151]),e(M,[2,152]),e([58,59,61,123],pe,{100:139,101:ue}),{16:g,29:[1,142],35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,104:140,118:141,119:[1,143]},e(ge,[2,112]),e(ge,[2,113]),e(ge,[2,114]),e(ge,[2,115]),e(ge,[2,116]),e(ge,[2,117]),e(ge,[2,118]),e(J,[2,129]),{105:[1,144],106:[1,145],109:[1,146],110:[1,147]},{109:[1,148]},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:149},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:150},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:151},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:152},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:153},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:154},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:155},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:156},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:157},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:158},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:159},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:160},e([58,59,61,105,106,109,110,123],[2,102]),{50:[1,161]},{17:[1,162],43:_e},{17:[1,164]},e(ke,[2,135],{107:G,124:V,125:Q,126:W,127:X}),{16:g,35:_,47:112,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,85:165,86:q,87:F,88:K,90:36,101:P,104:35,120:33},{4:166,18:r},{16:g,35:_,47:167,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,67:fe,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},e([6,8,14,16,17,35,36,43,49,50,52,53,55,56,58,59,61,67,76,79,81,82,83,84,86,87,88,89,101,129,135,138,144,146,150,152,154,164,165,167,168,173,177,179,180,182,192,193,194],ve),e(ke,Ee,{120:33,104:35,90:36,80:37,62:38,66:39,63:40,78:41,75:46,51:47,54:48,57:49,60:50,64:168,65:169,47:173,16:g,30:xe,35:_,45:we,46:Oe,50:k,52:E,53:x,55:w,56:O,58:$,59:S,61:T,76:R,79:I,81:C,82:A,83:L,84:N,86:q,87:F,88:K,101:P}),e(de,[2,73]),e(de,[2,74]),e(de,[2,75]),e(de,[2,76]),e(de,[2,77]),{70:174,71:[1,175]},{71:[2,67],107:G,124:V,125:Q,126:W,127:X},e($e,[2,7],{14:d}),e($e,[2,8]),e(Se,[2,24],{22:176,37:[1,177]}),e(be,[2,21]),{35:[1,178]},{35:[1,179]},{35:[1,180]},e(M,[2,148]),e(Te,[2,142]),e(J,[2,130]),e(Re,[2,131],{107:G,124:V}),e(Re,[2,132],{107:G,124:V}),e(J,[2,133]),e(Re,[2,134],{107:G,124:V}),{57:183,58:$,59:S,61:[1,182],122:181,123:[1,184]},e(z,[2,123]),{16:[1,185]},{16:[2,119]},{16:[2,120]},{16:[1,186]},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:187},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:188,81:C,82:A,83:L,84:N,86:q,87:F,88:K},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:189},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:190},e([6,8,14,17,34,36,43,49,50,71,72,74,77,89,91,101,105,106,107,108,109,110,112,113,114,115,116,117,121,124,125,126,127,129,133,135,138,141,142,144,146,150,152,154,157,164,165,167,168,173,177,179,180,182],[2,89],{45:Y,79:Z,81:ee,92:re,93:ne,94:ie,95:ae,96:se,97:oe,98:le,99:ce}),e([6,8,14,17,34,36,43,49,50,71,72,74,77,89,91,92,99,101,105,106,107,108,109,110,112,113,114,115,116,117,121,124,125,126,127,129,133,135,138,141,142,144,146,150,152,154,157,164,165,167,168,173,177,179,180,182],[2,90],{45:Y,79:Z,81:ee,93:ne,94:ie,95:ae,96:se,97:oe,98:le}),e(Ie,[2,91],{45:Y,79:Z,81:ee,95:ae,96:se,97:oe,98:le}),e(Ie,[2,92],{45:Y,79:Z,81:ee,95:ae,96:se,97:oe,98:le}),e(Ce,[2,93],{45:Y,95:ae,96:se,97:oe,98:le}),e(Ce,[2,94],{45:Y,95:ae,96:se,97:oe,98:le}),e(he,[2,95]),e(he,[2,96]),e(he,[2,97]),e(he,[2,98]),e(he,[2,99]),e([6,8,14,17,34,36,43,49,50,71,72,74,77,89,91,99,101,105,106,107,108,109,110,112,113,114,115,116,117,121,124,125,126,127,129,133,135,138,141,142,144,146,150,152,154,157,164,165,167,168,173,177,179,180,182],[2,100],{45:Y,79:Z,81:ee,92:re,93:ne,94:ie,95:ae,96:se,97:oe,98:le}),e([6,8,14,16,17,34,35,36,43,45,49,50,52,53,55,56,58,59,61,67,71,72,74,76,77,79,81,82,83,84,86,87,88,89,91,92,93,94,95,96,97,98,99,101,103,105,106,107,108,109,110,112,113,114,115,116,117,121,124,125,126,127,129,133,135,138,141,142,144,146,150,152,154,157,164,165,167,168,173,177,179,180,182,192,193,194],[2,63]),e(de,[2,82]),{16:g,35:_,47:191,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},e(de,[2,84]),{17:[1,192],43:_e},{17:[1,193]},{89:[1,194],107:G,124:V,125:Q,126:W,127:X},{17:[1,195],43:[1,196]},e(ke,[2,56]),e(ke,[2,58]),e(ke,[2,59]),{16:g,35:_,47:197,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},e(ke,[2,61],{107:G,124:V,125:Q,126:W,127:X}),{71:[1,199],73:198,74:[1,200],77:[2,70]},{16:g,35:_,47:201,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},e(Ae,[2,26],{23:202,38:[1,203]}),e(Se,[2,23]),e(D,[2,19]),e(j,[2,156]),e(j,[2,157]),e(J,[2,128]),e(z,[2,122]),e(J,[2,125]),e(J,[2,126]),{4:204,18:r},{4:205,16:g,18:r,35:_,47:112,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,85:206,86:q,87:F,88:K,90:36,101:P,104:35,120:33},{45:Y,79:Z,81:ee,91:te,92:re,93:ne,94:ie,95:ae,96:se,97:oe,98:le,99:ce,107:[1,207]},e(z,[2,103],{102:208,103:[1,209]}),e(z,[2,111],{45:Y,79:Z,81:ee,91:te,92:re,93:ne,94:ie,95:ae,96:se,97:oe,98:le,99:ce}),e(z,[2,109],{45:Y,79:Z,81:ee,91:te,92:re,93:ne,94:ie,95:ae,96:se,97:oe,98:le,99:ce}),e(ke,[2,136],{107:G,124:V,125:Q,126:W,127:X}),e(de,[2,83]),e(de,[2,85]),e(de,[2,86]),e(de,[2,54]),e(ke,Ee,{120:33,104:35,90:36,80:37,62:38,66:39,63:40,78:41,75:46,51:47,54:48,57:49,60:50,47:173,65:210,16:g,30:xe,35:_,45:we,46:Oe,50:k,52:E,53:x,55:w,56:O,58:$,59:S,61:T,76:R,79:I,81:C,82:A,83:L,84:N,86:q,87:F,88:K,101:P}),e(ke,[2,60],{107:G,124:V,125:Q,126:W,127:X}),{77:[1,211]},{16:g,35:_,47:212,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},{16:g,35:_,47:213,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},{72:[1,214],107:G,124:V,125:Q,126:W,127:X},e(Le,[2,28],{24:215,39:[1,216]}),e(Ae,[2,25]),{17:[1,217]},{17:[1,218]},{17:[1,219],43:_e},{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,104:220},e(z,[2,110]),{16:g,35:_,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:221,81:C,82:A,83:L,84:N,86:q,87:F,88:K},e(ke,[2,55]),e(de,[2,72]),{72:[1,222],107:G,124:V,125:Q,126:W,127:X},{77:[2,71],107:G,124:V,125:Q,126:W,127:X},{16:g,35:_,47:223,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},e(Ne,[2,29],{25:224,40:[1,225],41:[1,226]}),e(Le,[2,27]),e(z,[2,124]),e(z,[2,106]),e(z,[2,107]),e(z,[2,108]),e(z,[2,104]),{16:g,35:_,47:227,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},e(qe,[2,68],{107:G,124:V,125:Q,126:W,127:X}),e(Fe,[2,33],{26:228,42:[1,229]}),e(Ne,[2,30]),e(Ne,[2,31]),e(qe,[2,69],{107:G,124:V,125:Q,126:W,127:X}),{16:g,27:230,35:_,44:231,45:Ke,46:Pe,47:234,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},e(Fe,[2,32]),e(De,[2,166],{28:235,43:[1,236],157:[1,237]}),e(je,[2,35]),e(je,[2,36]),e(je,[2,37]),e(je,[2,39],{48:238,49:[1,239],50:[1,240],107:G,124:V,125:Q,126:W,127:X}),e(De,[2,12]),{16:g,35:_,44:241,45:Ke,46:Pe,47:234,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},{16:Be,50:ye,66:248,88:Ue,158:242,160:243,161:244,172:247,174:246},e(je,[2,38]),{50:[1,250]},e(je,[2,41]),e(je,[2,34]),e([6,8,14,17,129,135,138,144,146,150,152,154],He,{159:251,43:Me,182:Ge}),e(Ve,[2,168]),e(Ve,[2,170],{163:254,166:256,36:Qe,164:We,165:Xe,167:Je,168:ze,173:Ye,177:Ze}),{162:[1,262]},e(et,[2,191]),e(et,[2,192]),e([6,8,14,17,36,43,49,50,89,129,135,138,144,146,150,152,154,164,165,167,168,173,177,179,180,192,193,194],He,{159:263,67:fe,182:Ge}),{4:264,16:Be,18:r,50:ye,66:248,88:Ue,158:265,160:243,161:244,172:247,174:246},e(je,[2,40]),e(tt,[2,137],{128:266,129:[1,267]}),{16:Be,50:ye,66:248,88:Ue,160:268,161:244,172:247,174:246},{16:[1,269]},{173:[1,270]},{16:Be,50:ye,66:248,174:271},{169:272,170:rt,173:nt},{166:275,167:Je,168:ze,171:274,173:[2,179]},{173:[2,173]},{173:[2,174]},e(it,[2,175]),e(it,[2,176]),{16:Be,50:ye,66:248,161:276,172:247,174:246},e(at,st,{183:277,66:279,49:ot,50:ye}),{17:[1,280]},{17:[1,281],43:Me},e(Te,[2,139],{130:282,131:283,135:[1,284]}),{16:g,35:_,47:285,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},e(Ve,[2,169]),{50:ye,66:287,181:286},{16:Be,50:ye,66:248,174:288},{176:289,179:lt},{173:[1,291]},{173:[2,178]},{173:[1,292]},{169:293,170:rt,173:nt},{36:Qe,89:[1,294],163:254,164:We,165:Xe,166:256,167:Je,168:ze,173:Ye,177:Ze},e(et,[2,208],{189:295,190:296,191:297,192:ct,193:ut,194:pt}),{50:ye,66:301},e(at,[2,199],{67:fe}),e(et,st,{66:279,183:302,49:ot,50:ye}),e(et,[2,217]),e(ht,[2,153],{143:303,144:[1,304]}),e(Te,[2,140]),{16:g,35:_,47:32,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33,136:305,139:31},e(tt,[2,138],{107:G,124:V,125:Q,126:W,127:X}),{17:[1,306],43:[1,307]},e(ke,[2,193],{67:fe}),e([6,8,14,17,36,43,89,129,135,138,144,146,150,152,154,164,165,167,168,173,177,182],[2,181],{175:308,176:309,179:lt,180:dt}),e(et,[2,183]),{16:g,35:_,47:311,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},{16:Be,50:ye,66:248,161:312,172:247,174:246},{16:Be,50:ye,66:248,174:313},{173:[2,180]},e(Ve,[2,171]),e(et,[2,215]),e(et,[2,209]),e(et,[2,210]),{184:315,185:ft,186:yt},{184:318,185:ft,186:yt},{184:319,185:ft,186:yt},e(at,[2,198],{67:fe}),e(et,[2,216]),e(b,p,{137:18,11:320,138:h}),{16:g,35:_,47:321,50:k,51:47,52:E,53:x,54:48,55:w,56:O,57:49,58:$,59:S,60:50,61:T,62:38,63:40,66:39,75:46,76:R,78:41,79:I,80:37,81:C,82:A,83:L,84:N,86:q,87:F,88:K,90:36,101:P,104:35,120:33},e(Te,B,{132:322,43:U,133:H}),e([6,8,14,17,36,43,49,50,89,129,135,138,144,146,150,152,154,164,165,167,168,173,177,179,180,182,192,193,194],[2,196]),{50:ye,66:323},e(et,[2,182]),e(et,[2,189]),{16:[1,324]},e(et,[2,188],{107:G,124:V,125:Q,126:W,127:X}),{36:Qe,163:254,164:We,165:Xe,166:256,167:Je,168:ze,173:Ye,175:325,176:309,177:Ze,179:lt,180:dt},e(et,[2,185]),{191:326,192:ct,193:ut,194:pt},{16:vt,152:mt,187:327},e(bt,[2,200]),e(bt,[2,201]),{16:vt,152:mt,187:329},{16:vt,152:mt,187:330},e(j,v,{145:28,12:331,146:m}),e(ht,[2,154],{107:G,124:V,125:Q,126:W,127:X}),e(Te,[2,143]),e(ke,[2,194],{67:fe}),{50:ye,66:333,68:332},e(et,[2,184]),e(et,[2,211]),{16:[1,334]},{135:[1,337],138:[1,336],173:[1,335]},{16:[1,338]},{16:[1,339]},e(gt,[2,160],{148:340,149:341,150:[1,342]}),{17:[1,343],43:_t},e(ke,[2,64],{67:fe}),{17:[2,206],50:ye,66:333,68:346,188:345},{16:[2,203]},{16:[2,204]},{16:[2,205]},{50:ye,66:333,68:347},{50:ye,66:333,68:348},e(De,[2,163],{151:349,152:[1,350],154:[1,351]}),e(gt,[2,161]),{50:[1,353],63:352},e(et,[2,190]),{50:ye,66:354},{17:[1,355]},{17:[2,207],43:_t},{17:[1,356],43:_t},{17:[1,357],43:_t},e(De,[2,167]),{153:[1,358]},{105:[1,359]},e(gt,[2,162]),{16:me},e(ke,[2,65],{67:fe}),e(et,[2,212]),e(et,[2,213]),e(et,[2,214]),e(De,[2,164]),{155:[1,360]},{156:[1,361]},e(De,[2,165])],defaultActions:{11:[2,3],22:[2,1],24:[2,2],142:[2,119],143:[2,120],258:[2,173],259:[2,174],273:[2,178],293:[2,180],335:[2,203],336:[2,204],337:[2,205]},parseError:function(e,t){if(!t.recoverable){var r=new Error(e);throw r.hash=t,r}this.trace(e)},parse:function(e){var t=this,r=[0],n=[null],i=[],a=this.table,s="",o=0,l=0,c=0,u=i.slice.call(arguments,1),p=Object.create(this.lexer),h={yy:{}};for(var d in this.yy)Object.prototype.hasOwnProperty.call(this.yy,d)&&(h.yy[d]=this.yy[d]);p.setInput(e,h.yy),h.yy.lexer=p,h.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var f=p.yylloc;i.push(f);var y=p.options&&p.options.ranges;"function"==typeof h.yy.parseError?this.parseError=h.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var v,m,b,g,_,k,E,x,w,O=function(){var e;return"number"!=typeof(e=p.lex()||1)&&(e=t.symbols_[e]||e),e},$={};;){if(b=r[r.length-1],this.defaultActions[b]?g=this.defaultActions[b]:(null!==v&&void 0!==v||(v=O()),g=a[b]&&a[b][v]),void 0===g||!g.length||!g[0]){var S="";for(k in w=[],a[b])this.terminals_[k]&&k>2&&w.push("'"+this.terminals_[k]+"'");S=p.showPosition?"Parse error on line "+(o+1)+":\n"+p.showPosition()+"\nExpecting "+w.join(", ")+", got '"+(this.terminals_[v]||v)+"'":"Parse error on line "+(o+1)+": Unexpected "+(1==v?"end of input":"'"+(this.terminals_[v]||v)+"'"),this.parseError(S,{text:p.match,token:this.terminals_[v]||v,line:p.yylineno,loc:f,expected:w})}if(g[0]instanceof Array&&g.length>1)throw new Error("Parse Error: multiple actions possible at state: "+b+", token: "+v);switch(g[0]){case 1:r.push(v),n.push(p.yytext),i.push(p.yylloc),r.push(g[1]),v=null,m?(v=m,m=null):(l=p.yyleng,s=p.yytext,o=p.yylineno,f=p.yylloc,c>0&&c--);break;case 2:if(E=this.productions_[g[1]][1],$.$=n[n.length-E],$._$={first_line:i[i.length-(E||1)].first_line,last_line:i[i.length-1].last_line,first_column:i[i.length-(E||1)].first_column,last_column:i[i.length-1].last_column},y&&($._$.range=[i[i.length-(E||1)].range[0],i[i.length-1].range[1]]),void 0!==(_=this.performAction.apply($,[s,l,o,h.yy,g[1],n,i].concat(u))))return _;E&&(r=r.slice(0,-1*E*2),n=n.slice(0,-1*E),i=i.slice(0,-1*E)),r.push(this.productions_[g[1]][0]),n.push($.$),i.push($._$),x=a[r[r.length-2]][r[r.length-1]],r.push(x);break;case 3:return!0}}return!0}},Et={EOF:1,parseError:function(e,t){if(!this.yy.parser)throw new Error(e);this.yy.parser.parseError(e,t)},setInput:function(e,t){return this.yy=t||this.yy||{},this._input=e,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var e=this._input[0];return this.yytext+=e,this.yyleng++,this.offset++,this.match+=e,this.matched+=e,e.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),e},unput:function(e){var t=e.length,r=e.split(/(?:\r\n?|\n)/g);this._input=e+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-t),this.offset-=t;var n=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),r.length-1&&(this.yylineno-=r.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:r?(r.length===n.length?this.yylloc.first_column:0)+n[n.length-r.length].length-r[0].length:this.yylloc.first_column-t},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-t]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(e){this.unput(this.match.slice(e))},pastInput:function(){var e=this.matched.substr(0,this.matched.length-this.match.length);return(e.length>20?"...":"")+e.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var e=this.match;return e.length<20&&(e+=this._input.substr(0,20-e.length)),(e.substr(0,20)+(e.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var e=this.pastInput(),t=new Array(e.length+1).join("-");return e+this.upcomingInput()+"\n"+t+"^"},test_match:function(e,t){var r,n,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(n=e[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=n.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:n?n[n.length-1].length-n[n.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+e[0].length},this.yytext+=e[0],this.match+=e[0],this.matches=e,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(e[0].length),this.matched+=e[0],r=this.performAction.call(this,this.yy,this,t,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),r)return r;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var e,t,r,n;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;at[0].length)){if(t=r,n=a,this.options.backtrack_lexer){if(!1!==(e=this.test_match(r,i[a])))return e;if(this._backtrack){t=!1;continue}return!1}if(!this.options.flex)break}return t?!1!==(e=this.test_match(t,i[n]))&&e:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var e=this.next();return e||this.lex()},begin:function(e){this.conditionStack.push(e)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(e){return(e=this.conditionStack.length-1-Math.abs(e||0))>=0?this.conditionStack[e]:"INITIAL"},pushState:function(e){this.begin(e)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(e,t,r,n){switch(r){case 0:case 1:case 2:case 3:break;case 4:case 5:case 6:return 50;case 7:return 18;case 8:return 29;case 9:return 119;case 10:return 30;case 11:return 31;case 12:return 32;case 13:return 33;case 14:return 36;case 15:return 37;case 16:return 38;case 17:return 39;case 18:return 40;case 19:return 41;case 20:return 42;case 21:return 46;case 22:return 49;case 23:return 58;case 24:return 59;case 25:return 61;case 26:return"COLLATE";case 27:return 84;case 28:return 86;case 29:return 87;case 30:return 76;case 31:return 71;case 32:return 72;case 33:return 74;case 34:return 77;case 35:return 96;case 36:return 97;case 37:return 101;case 38:return 106;case 39:return 105;case 40:return 108;case 41:return 109;case 42:return 103;case 43:return 110;case 44:return 121;case 45:return 123;case 46:return 107;case 47:return 126;case 48:return 127;case 49:return 157;case 50:return 182;case 51:return 192;case 52:return 185;case 53:return 186;case 54:return 152;case 55:return 173;case 56:return 138;case 57:return 135;case 58:return 193;case 59:return 194;case 60:return 164;case 61:return 165;case 62:return 179;case 63:return 180;case 64:return 167;case 65:return 168;case 66:return 170;case 67:return 177;case 68:return 129;case 69:return 141;case 70:return 142;case 71:return 133;case 72:return 134;case 73:return 144;case 74:return 147;case 75:return 150;case 76:return 153;case 77:return 154;case 78:return 155;case 79:return 156;case 80:return 162;case 81:return 146;case 82:return 14;case 83:return 43;case 84:return 34;case 85:return 16;case 86:return 17;case 87:return 82;case 88:return 117;case 89:return 83;case 90:return 91;case 91:return 92;case 92:return 79;case 93:return 81;case 94:return 45;case 95:return 95;case 96:return 98;case 97:return 99;case 98:return 94;case 99:return 112;case 100:return 113;case 101:return 93;case 102:return"<=>";case 103:return 114;case 104:return 116;case 105:return 115;case 106:return 88;case 107:return 89;case 108:return 8;case 109:case 110:return 53;case 111:return 56;case 112:return 35;case 113:return 55;case 114:return 50;case 115:return 67;case 116:return 52;case 117:return 6;case 118:return"INVALID"}},rules:[/^(?:[\/][*](.|\n)*?[*][\/])/i,/^(?:[-][-]\s.*\n)/i,/^(?:[#]\s.*\n)/i,/^(?:\s+)/i,/^(?:[`][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*[`])/i,/^(?:[\w]+[\u4e00-\u9fa5]+[0-9a-zA-Z_\u4e00-\u9fa5]*)/i,/^(?:[\u4e00-\u9fa5][0-9a-zA-Z_\u4e00-\u9fa5]*)/i,/^(?:SELECT\b)/i,/^(?:ALL\b)/i,/^(?:ANY\b)/i,/^(?:DISTINCT\b)/i,/^(?:DISTINCTROW\b)/i,/^(?:HIGH_PRIORITY\b)/i,/^(?:MAX_STATEMENT_TIME\b)/i,/^(?:STRAIGHT_JOIN\b)/i,/^(?:SQL_SMALL_RESULT\b)/i,/^(?:SQL_BIG_RESULT\b)/i,/^(?:SQL_BUFFER_RESULT\b)/i,/^(?:SQL_CACHE\b)/i,/^(?:SQL_NO_CACHE\b)/i,/^(?:SQL_CALC_FOUND_ROWS\b)/i,/^(?:([a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*\.){1,2}\*)/i,/^(?:AS\b)/i,/^(?:TRUE\b)/i,/^(?:FALSE\b)/i,/^(?:NULL\b)/i,/^(?:COLLATE\b)/i,/^(?:BINARY\b)/i,/^(?:ROW\b)/i,/^(?:EXISTS\b)/i,/^(?:CASE\b)/i,/^(?:WHEN\b)/i,/^(?:THEN\b)/i,/^(?:ELSE\b)/i,/^(?:END\b)/i,/^(?:DIV\b)/i,/^(?:MOD\b)/i,/^(?:NOT\b)/i,/^(?:BETWEEN\b)/i,/^(?:IN\b)/i,/^(?:SOUNDS\b)/i,/^(?:LIKE\b)/i,/^(?:ESCAPE\b)/i,/^(?:REGEXP\b)/i,/^(?:IS\b)/i,/^(?:UNKNOWN\b)/i,/^(?:AND\b)/i,/^(?:OR\b)/i,/^(?:XOR\b)/i,/^(?:FROM\b)/i,/^(?:PARTITION\b)/i,/^(?:USE\b)/i,/^(?:INDEX\b)/i,/^(?:KEY\b)/i,/^(?:FOR\b)/i,/^(?:JOIN\b)/i,/^(?:ORDER\s+BY\b)/i,/^(?:GROUP\s+BY\b)/i,/^(?:IGNORE\b)/i,/^(?:FORCE\b)/i,/^(?:INNER\b)/i,/^(?:CROSS\b)/i,/^(?:ON\b)/i,/^(?:USING\b)/i,/^(?:LEFT\b)/i,/^(?:RIGHT\b)/i,/^(?:OUTER\b)/i,/^(?:NATURAL\b)/i,/^(?:WHERE\b)/i,/^(?:ASC\b)/i,/^(?:DESC\b)/i,/^(?:WITH\b)/i,/^(?:ROLLUP\b)/i,/^(?:HAVING\b)/i,/^(?:OFFSET\b)/i,/^(?:PROCEDURE\b)/i,/^(?:UPDATE\b)/i,/^(?:LOCK\b)/i,/^(?:SHARE\b)/i,/^(?:MODE\b)/i,/^(?:OJ\b)/i,/^(?:LIMIT\b)/i,/^(?:UNION\b)/i,/^(?:,)/i,/^(?:=)/i,/^(?:\()/i,/^(?:\))/i,/^(?:~)/i,/^(?:!=)/i,/^(?:!)/i,/^(?:\|)/i,/^(?:&)/i,/^(?:\+)/i,/^(?:-)/i,/^(?:\*)/i,/^(?:\/)/i,/^(?:%)/i,/^(?:\^)/i,/^(?:>>)/i,/^(?:>=)/i,/^(?:>)/i,/^(?:<<)/i,/^(?:<=>)/i,/^(?:<=)/i,/^(?:<>)/i,/^(?:<)/i,/^(?:\{)/i,/^(?:\})/i,/^(?:;)/i,/^(?:['](\\.|[^'])*['])/i,/^(?:["](\\.|[^"])*["])/i,/^(?:[0][x][0-9a-fA-F]+)/i,/^(?:[-]?[0-9]+(\.[0-9]+)?)/i,/^(?:[-]?[0-9]+(\.[0-9]+)?[eE][-][0-9]+(\.[0-9]+)?)/i,/^(?:[a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*)/i,/^(?:\.)/i,/^(?:['"][a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*["'])/i,/^(?:$)/i,/^(?:.)/i],conditions:{INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118],inclusive:!0}}};function xt(){this.yy={}}return kt.lexer=Et,xt.prototype=kt,kt.Parser=xt,new xt}();function a(){this.buffer=""}i||(i={}),i.stringify=function(e){var t=new a;return t.travelMain(e),t.buffer},a.prototype.travel=function(e){if(e){if("string"==typeof e)return this.append(e);this["travel"+e.type].call(this,e)}};var s=!1;a.prototype.appendKeyword=function(e,t,r){s&&(t=!0,s=!1),this.buffer+=t?e.toUpperCase():" "+e.toUpperCase(),r&&(s=!0)},a.prototype.append=function(e,t,r){s&&(t=!0,s=!1),this.buffer+=t?e:" "+e,r&&(s=!0)},a.prototype.travelMain=function(e){this.travel(e.value),e.hasSemicolon&&this.append(";",!0)},a.prototype.travelSelect=function(e){this.appendKeyword("select"),e.distinctOpt&&this.appendKeyword(e.distinctOpt),e.highPriorityOpt&&this.appendKeyword(e.highPriorityOpt),e.maxStateMentTimeOpt&&this.append("MAX_STATEMENT_TIME = "+e.maxStateMentTimeOpt),e.straightJoinOpt&&this.appendKeyword(e.straightJoinOpt),e.sqlSmallResultOpt&&this.appendKeyword(e.sqlSmallResultOpt),e.sqlBigResultOpt&&this.appendKeyword(e.sqlBigResultOpt),e.sqlBufferResultOpt&&this.appendKeyword(e.sqlBufferResultOpt),e.sqlCacheOpt&&this.appendKeyword(e.sqlCacheOpt),e.sqlCalcFoundRowsOpt&&this.appendKeyword(e.sqlCalcFoundRowsOpt),e.selectItems&&this.travelSelectExpr(e.selectItems),e.from&&(this.appendKeyword("from"),this.travel(e.from)),e.partition&&this.travel(e.partition),e.where&&(this.appendKeyword("where"),this.travel(e.where)),e.groupBy&&this.travel(e.groupBy),e.having&&(this.appendKeyword("having"),this.travel(e.having)),e.orderBy&&this.travel(e.orderBy),e.limit&&this.travel(e.limit),e.procedure&&(this.appendKeyword("procedure"),this.travel(e.procedure)),e.updateLockMode&&this.appendKeyword(e.updateLockMode)},a.prototype.travelSelectExpr=function(e){for(var t=e.value,r=0;r0&&void 0!==arguments[0]?arguments[0]:void 0;if(this.action&&this.action.params&&this.action.params.$resource){this.$set(this.record.params,"$resource",t),this.$set(this.record,"resource",t);var r=this.action.types,n=void 0===r?[]:r;return this.$httpGet("/resources").then(function(r){var i=r.data;e.resourcesOptions=i.filter(function(e){return n.includes(e.type)}),e.$set(e.record,"resource",t)})}},loadActions:function(){var e=this;return this.$httpGet("/actions",this.params).then(function(t){e.actionsList=t.data.map(function(e){return e.label=(e.title||{})[E],e.descriptionLabel=(e.description||{})[E],e})})},renderForm:function(e){var t=this;return f()(u.a.mark(function r(){var n,i,a,s;return u.a.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:if(t.formData){r.next=2;break}return r.abrupt("return");case 2:return n=t.formData||e,i=n.name,a=n.params,s=void 0===a?{}:a,r.next=5,t.handleActionChange(i);case 5:t.fillData(s);case 6:case"end":return r.stop()}},r,t)}))()},fillData:function(e){var t=this;l()(e).forEach(function(e){var r=s()(e,2),n=r[0],i=r[1];t.$set(t.record,n,i)})}},created:function(){var e=this;return f()(u.a.mark(function t(){return u.a.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,e.loadActions();case 2:return t.next=4,e.renderForm();case 4:case"end":return t.stop()}},t,e)}))()}},w={render:function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("el-dialog",e._b({staticClass:"action-dialog",attrs:{width:"500px","append-to-body":"",visible:e.dialogVisible,title:e.$t("rule.actions")},on:{"update:visible":function(t){e.dialogVisible=t},open:e.open,close:e.close}},"el-dialog",e.$attrs,!1),[r("el-form",{ref:"record",staticClass:"el-form--public",attrs:{model:e.record,rules:e.rules}},[r("el-row",{attrs:{gutter:20}},[r("el-col",{attrs:{span:12}},[r("el-form-item",{attrs:{prop:"action"}},[r("template",{slot:"label"},[e._v("\n "+e._s(e.$t("rule.action"))+"\n "),r("el-popover",{attrs:{placement:"top-start",width:"200",trigger:"hover"}},[r("div",{domProps:{innerHTML:e._s(e.action.descriptionLabel||e.$t("rule.action_type"))}}),e._v(" "),r("i",{staticClass:"el-icon-question",attrs:{slot:"reference",tabindex:"-1"},slot:"reference"})])],1),e._v(" "),r("el-select",{staticClass:"el-select--public",staticStyle:{width:"100%"},attrs:{"popper-class":"el-select--public"},on:{change:e.handleActionChange},model:{value:e.record.action,callback:function(t){e.$set(e.record,"action",t)},expression:"record.action"}},e._l(e.actionsList,function(e,t){return r("el-option",{key:t,attrs:{label:e.label,value:e.name}})}),1)],2)],1),e._v(" "),e.action.params&&e.action.params.$resource?r("el-col",{attrs:{span:12}},[r("el-form-item",{staticClass:"resource-item",attrs:{prop:"params.$resource"}},[r("template",{slot:"label"},[e._v("\n "+e._s(e.$t("rule.resource"))+"\n "),r("span",{staticClass:"btn",staticStyle:{float:"right","font-size":"12px"},on:{click:e.createResource}},[e._v("\n "+e._s(e.$t("rule.new_resource"))+"\n ")])]),e._v(" "),r("el-select",{staticClass:"el-select--public",staticStyle:{width:"100%"},attrs:{"popper-class":"el-select--public"},model:{value:e.record.params.$resource,callback:function(t){e.$set(e.record.params,"$resource",t)},expression:"record.params.$resource"}},e._l(e.resourcesOptions,function(e,t){return r("el-option",{key:t,attrs:{label:e.id,value:e.id}})}),1)],2)],1):e._e(),e._v(" "),e._l(e.paramsList,function(t,n){return r("el-col",{key:n,attrs:{span:"object"===t.type||"textarea"===t.$attrs.type?24:12}},[r("el-form-item",{attrs:{prop:"params."+t.prop}},[r("template",{slot:"label"},[e._v("\n "+e._s(t.label)+"\n\n "),t.description?r("el-popover",{attrs:{placement:"right",width:"200",trigger:"hover"}},[r("div",{domProps:{innerHTML:e._s(t.description)}}),e._v(" "),r("i",{staticClass:"el-icon-question",attrs:{slot:"reference",tabindex:"-1"},slot:"reference"})]):e._e()],1),e._v(" "),"object"===t.type?r("data-table",{model:{value:e.record.params[t.key],callback:function(r){e.$set(e.record.params,t.key,r)},expression:"record.params[item.key]"}}):"emq-select"===t.type?r("emq-select",e._b({staticClass:"el-select--public",attrs:{"popper-class":"el-select--public"},model:{value:e.record.params[t.key],callback:function(r){e.$set(e.record.params,t.key,r)},expression:"record.params[item.key]"}},"emq-select",t.$attrs,!1)):"number"===t.type?r("el-input",e._b({attrs:{type:"number"},model:{value:e.record.params[t.key],callback:function(r){e.$set(e.record.params,t.key,e._n(r))},expression:"record.params[item.key]"}},"el-input",t.$attrs,!1)):r("el-input",e._b({model:{value:e.record.params[t.key],callback:function(r){e.$set(e.record.params,t.key,r)},expression:"record.params[item.key]"}},"el-input",t.$attrs,!1))],2)],1)})],2)],1),e._v(" "),r("div",{attrs:{slot:"footer"},slot:"footer"},[r("el-button",{staticClass:"cache-btn",attrs:{type:"text"},on:{click:function(t){e.dialogVisible=!1}}},[e._v("\n "+e._s(e.$t("rule.cancel"))+"\n ")]),e._v(" "),r("el-button",{staticClass:"confirm-btn",attrs:{type:"success"},on:{click:e.handleAdd}},[e._v("\n "+e._s(e.$t("rule.confirm"))+"\n ")])],1),e._v(" "),r("resource-dialog",{attrs:{visible:e.resourceDialogVisible,"resource-type":e.resourceType,"enable-item":e.enableItem,"append-to-body":""},on:{"update:visible":function(t){e.resourceDialogVisible=t},confirm:e.handleResourceCreate}})],1)},staticRenderFns:[]};var O={name:"rule-actions",components:{ActionDialog:r("VU/8")(x,w,!1,function(e){r("yoEm")},null,null).exports},props:{record:{type:Object,required:!0},inDialog:{type:Boolean,default:!1},operations:{type:Array,default:function(){return["create","edit","delete"]}},params:{type:Object,default:function(){return{}}}},watch:{dialogVisible:function(e){e||(this.editForm=null,this.editIndex=null,this.currentAction={},this.isFallBacks=!1)}},computed:{has:function(){var e=[];return this.operations.forEach(function(t){e[t]=!0}),e}},data:function(){return{dialogVisible:!1,editForm:null,editIndex:null,isFallBacks:!1,currentAction:{}}},filters:{jsonFormat:function(e){return i()(e,null,2)}},methods:{getSum:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(0===e.length||!t)return 0;var r=0;return e.forEach(function(e){var n=e[t]||0;r+=n}),r},handleActionAdd:function(e,t){if(this.isFallBacks)return null!==this.editIndex&&(this.currentAction.fallbacks=[]),void this.currentAction.fallbacks.push(e);null!==t?this.record.actions.splice(t,1,e):this.record.actions.push(e)},handleActionRemove:function(e){var t=e;this.record.actions=this.record.actions.filter(function(e,r){return r!==t})},handleActionEdit:function(e,t){this.editIndex=t,this.editForm=e,this.dialogVisible=!0},handleAddFallbacks:function(e){this.currentAction=e,this.isFallBacks=!0,this.dialogVisible=!0},handleFallbackRemove:function(e){e.fallbacks=[]},handleFallbackEdit:function(e,t,r){this.currentAction=t,this.isFallBacks=!0,this.editIndex=r,this.editForm=e,this.dialogVisible=!0}}},$={render:function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("div",{staticClass:"rule-actions"},[e._l(e.record.actions,function(t,n){return r("div",{key:n,staticClass:"action-card"},[r("el-row",{staticClass:"action-body",attrs:{type:"flex"}},[r("el-col",{attrs:{span:12}},[r("div",{staticClass:"filed-item"},[r("label",{staticClass:"title"},[e._v(e._s(e.$t("rule.type"))+": ")]),e._v(" "),r("span",{staticClass:"desc"},[e._v(e._s(t.name))])]),e._v(" "),e._l(Object.entries(t.params),function(t,n){return r("div",{key:n,staticClass:"filed-item"},[r("label",{staticClass:"title"},[e._v(" "+e._s("$resource"===t[0]?e.$t("rule.rely_resource"):t[0])+": ")]),e._v(" "),r("span",{staticClass:"desc"},[e._v(e._s(t[1]))])])})],2),e._v(" "),e.has.delete||e.has.edit?r("el-col",{staticClass:"action-oper",attrs:{span:12}},[e.has.edit?r("el-button",{attrs:{type:"text"},on:{click:function(r){return e.handleActionEdit(t,n)}}},[e._v("\n "+e._s(e.$t("rule.edit"))+"\n ")]):e._e(),e._v(" "),e.has.delete?r("el-button",{staticClass:"delete-btn",attrs:{type:"text"},on:{click:function(t){return e.handleActionRemove(n)}}},[e._v("\n "+e._s(e.$t("rule.delete"))+"\n ")]):e._e(),e._v(" "),t.fallbacks.length?e._e():r("div",{staticClass:"fallbacks"},[r("el-popover",{attrs:{placement:"top-start",trigger:"hover",content:e.$t("rule.fallbackActionCreate")}},[r("el-button",{attrs:{slot:"reference",type:"text",icon:"el-icon-plus"},on:{click:function(r){return e.handleAddFallbacks(t)}},slot:"reference"},[e._v("\n "+e._s(e.$t("rule.fallbackAction"))+"\n ")])],1)],1)],1):e._e(),e._v(" "),e.has.delete||e.has.edit?e._e():r("el-col",{attrs:{span:12}},[r("div",{staticClass:"status-wrapper filed-item"},[e._l(t.metrics||[],function(t,n){return r("div",{key:n,staticClass:"status-item"},[r("div",{staticClass:"title"},[e._v(e._s(e.$t("rule.metrics"))+":")]),e._v(" "),r("span",{staticClass:"key"},[e._v("\n "+e._s(t.node)+"\n ")]),e._v(" "),r("span",{attrs:{type:"info"}},[e._v("\n "+e._s(e.$t("rule.success"))+":\n "),r("span",[e._v(e._s(t.success))])]),e._v(" "),r("span",{attrs:{type:"info"}},[e._v("\n "+e._s(e.$t("rule.failed"))+":\n "),r("span",[e._v(e._s(t.failed))])])])}),e._v(" "),r("div",{staticClass:"status-item"},[r("span",{staticClass:"key"},[e._v("\n "+e._s(e.$t("rule.all"))+"\n ")]),e._v(" "),r("span",{attrs:{type:"info"}},[e._v("\n "+e._s(e.$t("rule.success"))+":\n "),r("span",[e._v(e._s(e.getSum(t.metrics,"success")))])]),e._v(" "),r("span",{attrs:{type:"info"}},[e._v("\n "+e._s(e.$t("rule.failed"))+":\n "),r("span",[e._v(e._s(e.getSum(t.metrics,"failed")))])])])],2)])],1),e._v(" "),t.fallbacks&&t.fallbacks.length?e._l(t.fallbacks,function(n,i){return r("el-row",{key:i,staticClass:"action-footer",attrs:{type:"flex"}},[r("el-col",{attrs:{span:12}},[r("div",{staticClass:"filed-item"},[r("label",{staticClass:"title"},[e._v(e._s(e.$t("rule.type"))+": ")]),e._v(" "),r("span",{staticClass:"desc"},[e._v(e._s(n.name))])]),e._v(" "),e._l(Object.entries(n.params),function(t,n){return r("div",{key:n,staticClass:"filed-item"},[r("label",{staticClass:"title"},[e._v(" "+e._s("$resource"===t[0]?e.$t("rule.rely_resource"):t[0])+": ")]),e._v(" "),r("span",{staticClass:"desc"},[e._v(e._s(t[1]))])])})],2),e._v(" "),e.has.delete||e.has.edit?r("el-col",{staticClass:"action-oper",attrs:{span:12}},[e.has.edit?r("el-button",{attrs:{type:"text"},on:{click:function(r){return e.handleFallbackEdit(n,t,i)}}},[e._v("\n "+e._s(e.$t("rule.edit"))+"\n ")]):e._e(),e._v(" "),e.has.delete?r("el-button",{staticClass:"delete-btn",attrs:{type:"text"},on:{click:function(r){return e.handleFallbackRemove(t,i)}}},[e._v("\n "+e._s(e.$t("rule.delete"))+"\n ")]):e._e(),e._v(" "),r("div",{staticClass:"fallbacks"},[r("el-popover",{attrs:{placement:"top-start",trigger:"hover",content:e.$t("rule.fallbackActionTip")}},[r("span",{attrs:{slot:"reference"},slot:"reference"},[e._v("\n "+e._s(e.$t("rule.fallbackAction"))+"\n ")])])],1)],1):e._e(),e._v(" "),e.has.delete||e.has.edit?e._e():r("el-col",{attrs:{span:12}},[r("div",{staticClass:"status-wrapper filed-item"},[e._l(n.metrics||[],function(t,n){return r("div",{key:n,staticClass:"status-item"},[r("div",{staticClass:"title"},[e._v(e._s(e.$t("rule.metrics"))+":")]),e._v(" "),r("span",{staticClass:"key"},[e._v("\n "+e._s(t.node)+"\n ")]),e._v(" "),r("span",{attrs:{type:"info"}},[e._v("\n "+e._s(e.$t("rule.success"))+":\n "),r("span",[e._v(e._s(t.success))])]),e._v(" "),r("span",{attrs:{type:"info"}},[e._v("\n "+e._s(e.$t("rule.failed"))+":\n "),r("span",[e._v(e._s(t.failed))])])])}),e._v(" "),r("div",{staticClass:"status-item"},[r("span",{staticClass:"key"},[e._v("\n "+e._s(e.$t("rule.all"))+"\n ")]),e._v(" "),r("span",{attrs:{type:"info"}},[e._v("\n "+e._s(e.$t("rule.success"))+":\n "),r("span",[e._v(e._s(e.getSum(n.metrics,"success")))])]),e._v(" "),r("span",{attrs:{type:"info"}},[e._v("\n "+e._s(e.$t("rule.failed"))+":\n "),r("span",[e._v(e._s(e.getSum(n.metrics,"failed")))])])])],2)])],1)}):e._e()],2)}),e._v(" "),e.has.create?r("el-button",{staticStyle:{"min-width":"80px"},attrs:{type:"success",plain:"",icon:"el-icon-plus",size:"small"},on:{click:function(t){e.dialogVisible=!0}}},[e._v("\n "+e._s(e.$t("rule.add"))+"\n ")]):e._e(),e._v(" "),r("action-dialog",{attrs:{visible:e.dialogVisible,currentActions:e.record.actions,recordIndex:e.editIndex,editRecord:e.editForm,params:e.params},on:{"update:visible":function(t){e.dialogVisible=t},confirm:e.handleActionAdd}})],2)},staticRenderFns:[]};var S=r("VU/8")(O,$,!1,function(e){r("lqjQ")},null,null);t.a=S.exports},exGp:function(e,t,r){"use strict";t.__esModule=!0;var n,i=r("//Fk"),a=(n=i)&&n.__esModule?n:{default:n};t.default=function(e){return function(){var t=e.apply(this,arguments);return new a.default(function(e,r){return function n(i,s){try{var o=t[i](s),l=o.value}catch(e){return void r(e)}if(!o.done)return a.default.resolve(l).then(function(e){n("next",e)},function(e){n("throw",e)});e(l)}("next")})}}},lqjQ:function(e,t){},yoEm:function(e,t){}}); \ No newline at end of file diff --git a/apps/emqx_dashboard/priv/www/static/js/1.fcd6fde8b053e80bc68f.js b/apps/emqx_dashboard/priv/www/static/js/1.fcd6fde8b053e80bc68f.js new file mode 100644 index 000000000..0a49cb931 --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/js/1.fcd6fde8b053e80bc68f.js @@ -0,0 +1 @@ +webpackJsonp([1],{g2pX:function(e,t){},zXyA:function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=a("Dd8w"),i=a.n(s),l=a("zL8q"),n=a("NYxO"),o={name:"data-view",components:{"el-pagination":l.Pagination,"el-input":l.Input,"el-select":l.Select,"el-option":l.Option,"el-table":l.Table,"el-table-column":l.TableColumn,"el-date-picker":l.DatePicker},data:function(){return{searchView:!1,cluster:!1,popoverVisible:!1,count:0,hasnext:!1,params:{_page:1,_limit:10},nodeName:"",nodes:[],activeTab:"clients",searchKey:"",searchValue:"",searchPlaceholder:this.$t("clients.clientId"),clients:[],fuzzyParams:{comparator:"_gte",match:"_match_topic"},topics:[],subscriptions:[],showMoreQuery:!1,protoNames:["MQTT","MQTT-SN","CoAP","LwM2M"]}},watch:{$route:"init",activeTab:function(){this.fuzzyParams={comparator:"_gte",match:"_match_topic"}}},computed:{iconStatus:function(){return this.searchView?"el-icon-close":"el-icon-search"}},methods:i()({},Object(n.b)(["CURRENT_NODE"]),{stashNode:function(){this.cluster="cluster"===this.nodeName,this.cluster||this.CURRENT_NODE(this.nodeName)},init:function(){switch(this.activeTab=this.$route.path.split("/")[1],this.params._page=1,this.activeTab){case"topics":this.searchPlaceholder="Topic";break;default:this.searchPlaceholder=this.$t("clients.clientId")}this.loadData()},loadData:function(){var e=this;this.searchValue="",this.$httpGet("/nodes").then(function(t){var a=e.$store.state.nodeName||t.data[0].node;e.nodeName=e.cluster?"cluster":a,e.nodes=t.data,e.loadChild()}).catch(function(t){e.$message.error(t||e.$t("error.networkError"))})},loadChild:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]&&arguments[0],a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;if(this.stashNode(),this.searchView=!1,this.searchValue="",!0===t&&(this.params._page=1),this.nodeName||"topics"===this.activeTab){var s="/nodes/"+this.nodeName+"/"+this.activeTab;("topics"===this.activeTab||this.cluster)&&(s="topics"===this.activeTab?"routes":this.activeTab);var l={};l=a?i()({},a,this.params):i()({},this.params),this.$httpGet(s,l).then(function(t){e[e.activeTab]=t.data.items,e.count=t.data.meta.count||0,e.hasnext=t.data.meta.hasnext}).catch(function(t){e.$message.error(t||e.$t("error.networkError"))})}},searchChild:function(){var e=this;if(this.searchView)this.loadChild();else if(this.searchValue){var t="/nodes/"+this.nodeName+"/"+this.activeTab+"/"+encodeURIComponent(this.searchValue);if("topics"===this.activeTab||this.cluster)t="/"+("topics"===this.activeTab?"routes":this.activeTab)+"/"+encodeURIComponent(this.searchValue);this.$httpGet(t).then(function(t){e.count=0,e.params={_page:1,_limit:10},e.searchView=!0,e[e.activeTab]=t.data}).catch(function(t){e.$message.error(t||e.$t("error.networkError"))})}else this.loadData()},handleSizeChange:function(e){this.params._limit=e,this.loadChild(!0)},handlePrevClick:function(){if(1!==this.params._page){this.params._page-=1;var e=this.genQueryParams(this.fuzzyParams);this.loadChild(!1,e)}},handleNextClick:function(){if(this.hasnext){this.params._page+=1;var e=this.genQueryParams(this.fuzzyParams);this.loadChild(!1,e)}},handleDisconnect:function(e,t,a){var s=this;this.$httpDelete("/clients/"+encodeURIComponent(e.clientid)).then(function(){s.loadData(),a.$refs["popover-"+t].doClose()}).catch(function(e){s.$message.error(e||s.$t("error.networkError"))})},genQueryParams:function(e){var t={};if("clients"===this.activeTab){var a=e._like_clientid,s=e._like_username,i=e.ip_address,l=e.conn_state,n=e.proto_name,o=e.comparator,r=e._connected_at;if(t={_like_clientid:a||void 0,_like_username:s||void 0,ip_address:i||void 0,conn_state:l||void 0,proto_name:n||void 0},r)t[o+"_connected_at"]=Math.floor(r/1e3)}else if("subscriptions"===this.activeTab){var c=e._like_clientid,p=e.topic,u=e.qos,d=e.share,m=e.match;t={clientid:c||void 0,qos:""===u?void 0:u,share:d||void 0},p&&(t[m]=p)}return t},clientQuerySearch:function(){var e=this.genQueryParams(this.fuzzyParams);this.loadChild(!0,e)},resetClientQuerySearch:function(){this.fuzzyParams={comparator:">=",match:"_match_topic"},this.init()}}),created:function(){this.init()}},r={render:function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"data-view"},[a("div",{staticClass:"page-title"},[e._v("\n "+e._s(e.$t("leftbar."+e.activeTab))+"\n "),a("div",{staticStyle:{float:"right"},nativeOn:{keyup:function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")?null:e.searchChild(t)}}},["topics"!==e.activeTab?a("el-select",{staticClass:"select-radius",attrs:{placeholder:e.$t("select.placeholder"),disabled:e.$store.state.loading},on:{change:function(t){return e.loadChild(!0)}},model:{value:e.nodeName,callback:function(t){e.nodeName=t},expression:"nodeName"}},e._l(e.nodes,function(e){return a("el-option",{key:e.node,attrs:{label:e.node,value:e.node}})}),1):e._e()],1)]),e._v(" "),"topics"!==e.activeTab?a("el-card",{staticClass:"el-card--self search-card"},[a("el-form",{ref:"fuzzyParams",attrs:{model:e.fuzzyParams,"label-position":"left","label-width":"110px"}},[a("el-row",{attrs:{gutter:20}},[a("el-col",{attrs:{span:8}},[a("el-form-item",{attrs:{label:e.$t("clients.clientId")}},[a("el-input",{attrs:{type:"text",size:"small"},model:{value:e.fuzzyParams._like_clientid,callback:function(t){e.$set(e.fuzzyParams,"_like_clientid",t)},expression:"fuzzyParams._like_clientid"}})],1)],1),e._v(" "),"clients"===e.activeTab?a("el-col",{attrs:{span:8}},[a("el-form-item",{attrs:{label:e.$t("clients.username")}},[a("el-input",{attrs:{type:"text",size:"small"},model:{value:e.fuzzyParams._like_username,callback:function(t){e.$set(e.fuzzyParams,"_like_username",t)},expression:"fuzzyParams._like_username"}})],1)],1):"subscriptions"===e.activeTab?a("el-col",{attrs:{span:8}},[a("el-form-item",{attrs:{label:e.$t("topics.topic")}},[a("el-row",{staticClass:"form-item-row"},[a("el-col",{attrs:{span:9}},[a("el-select",{staticClass:"match",model:{value:e.fuzzyParams.match,callback:function(t){e.$set(e.fuzzyParams,"match",t)},expression:"fuzzyParams.match"}},[a("el-option",{attrs:{label:"filter",value:"_match_topic"}}),e._v(" "),a("el-option",{attrs:{label:"topic",value:"topic"}})],1)],1),e._v(" "),a("el-col",{attrs:{span:15}},[a("el-input",{attrs:{type:"text",size:"small"},model:{value:e.fuzzyParams.topic,callback:function(t){e.$set(e.fuzzyParams,"topic",t)},expression:"fuzzyParams.topic"}})],1)],1)],1)],1):e._e(),e._v(" "),e.showMoreQuery?["clients"===e.activeTab?[a("el-col",{attrs:{span:8}},[a("el-form-item",{attrs:{label:e.$t("clients.ipAddr")}},[a("el-input",{attrs:{type:"text",size:"small"},model:{value:e.fuzzyParams.ip_address,callback:function(t){e.$set(e.fuzzyParams,"ip_address",t)},expression:"fuzzyParams.ip_address"}})],1)],1),e._v(" "),a("el-col",{attrs:{span:8}},[a("el-form-item",{attrs:{label:e.$t("clients.connected")}},[a("el-select",{model:{value:e.fuzzyParams.conn_state,callback:function(t){e.$set(e.fuzzyParams,"conn_state",t)},expression:"fuzzyParams.conn_state"}},[a("el-option",{attrs:{value:"connected"}}),e._v(" "),a("el-option",{attrs:{value:"disconnected"}})],1)],1)],1),e._v(" "),a("el-col",{attrs:{span:8}},[a("el-form-item",{attrs:{label:e.$t("clients.createdAt")}},[a("el-row",{staticClass:"form-item-row"},[a("el-col",{attrs:{span:8}},[a("el-select",{staticClass:"comparator",model:{value:e.fuzzyParams.comparator,callback:function(t){e.$set(e.fuzzyParams,"comparator",t)},expression:"fuzzyParams.comparator"}},[a("el-option",{attrs:{label:">=",value:"_gte"}}),e._v(" "),a("el-option",{attrs:{label:"<=",value:"_lte"}})],1)],1),e._v(" "),a("el-col",{attrs:{span:16}},[a("el-date-picker",{staticClass:"datatime",attrs:{type:"datetime","value-format":"timestamp"},model:{value:e.fuzzyParams._connected_at,callback:function(t){e.$set(e.fuzzyParams,"_connected_at",t)},expression:"fuzzyParams._connected_at"}})],1)],1)],1)],1),e._v(" "),a("el-col",{attrs:{span:8}},[a("el-form-item",{attrs:{label:e.$t("clients.protoName")}},[a("el-select",{model:{value:e.fuzzyParams.proto_name,callback:function(t){e.$set(e.fuzzyParams,"proto_name",t)},expression:"fuzzyParams.proto_name"}},e._l(e.protoNames,function(e){return a("el-option",{key:e,attrs:{value:e}})}),1)],1)],1)]:"subscriptions"===e.activeTab?[a("el-col",{attrs:{span:8}},[a("el-form-item",{attrs:{label:"QoS"}},[a("el-select",{attrs:{clearable:""},model:{value:e.fuzzyParams.qos,callback:function(t){e.$set(e.fuzzyParams,"qos",t)},expression:"fuzzyParams.qos"}},[a("el-option",{attrs:{value:0}}),e._v(" "),a("el-option",{attrs:{value:1}}),e._v(" "),a("el-option",{attrs:{value:2}})],1)],1)],1),e._v(" "),a("el-col",{staticClass:"col-share",attrs:{span:8}},[a("el-form-item",{attrs:{label:e.$t("subscriptions.share")}},[a("el-input",{attrs:{type:"text",size:"small",placeholder:"group_name"},model:{value:e.fuzzyParams.share,callback:function(t){e.$set(e.fuzzyParams,"share",t)},expression:"fuzzyParams.share"}})],1)],1)]:e._e()]:e._e(),e._v(" "),a("span",{staticClass:"col-oper"},[a("el-button",{attrs:{size:"small",type:"primary",plain:""},on:{click:e.clientQuerySearch}},[e._v("\n "+e._s(e.$t("oper.search"))+"\n ")]),e._v(" "),a("el-button",{attrs:{size:"small",plain:""},on:{click:e.resetClientQuerySearch}},[e._v("\n "+e._s(e.$t("oper.reset"))+"\n ")]),e._v(" "),a("a",{staticClass:"show-more",attrs:{href:"javascript:;"},on:{click:function(t){e.showMoreQuery=!e.showMoreQuery}}},[e._v("\n "+e._s(e.showMoreQuery?e.$t("oper.collapse"):e.$t("oper.expand"))+"\n "),a("i",{class:e.showMoreQuery?"el-icon-arrow-up":"el-icon-arrow-down"})])],1)],2)],1)],1):e._e(),e._v(" "),a("el-table",{directives:[{name:"show",rawName:"v-show",value:"clients"===e.activeTab,expression:"activeTab === 'clients'"},{name:"loading",rawName:"v-loading",value:e.$store.state.loading,expression:"$store.state.loading"}],attrs:{border:"",data:e.clients}},[a("el-table-column",{attrs:{prop:"clientid",label:e.$t("clients.clientId"),width:"160px","show-overflow-tooltip":""},scopedSlots:e._u([{key:"default",fn:function(t){var s=t.row;return[a("a",{attrs:{href:"javascript:;"},on:{click:function(t){e.$router.push({path:"/clients/"+encodeURIComponent(s.clientid)})}}},[e._v("\n "+e._s(s.clientid)+"\n ")])]}}])}),e._v(" "),a("el-table-column",{attrs:{prop:"username","min-width":"100px",label:e.$t("clients.username"),"show-overflow-tooltip":""}}),e._v(" "),a("el-table-column",{attrs:{prop:"ip_address",label:e.$t("clients.ipAddr"),"min-width":"140px","show-overflow-tooltip":""},scopedSlots:e._u([{key:"default",fn:function(t){var a=t.row;return[e._v("\n "+e._s(a.ip_address)+":"+e._s(a.port)+"\n ")]}}])}),e._v(" "),a("el-table-column",{attrs:{prop:"keepalive","min-width":"100px",label:e.$t("clients.keepalive")}}),e._v(" "),a("el-table-column",{attrs:{prop:"expiry_interval","min-width":"150px",label:e.$t("clients.expiryInterval")}}),e._v(" "),a("el-table-column",{attrs:{prop:"subscriptions_cnt","min-width":"160px",label:e.$t("clients.subscriptionsCount")}}),e._v(" "),a("el-table-column",{attrs:{prop:"connected","min-width":"140px",label:e.$t("clients.connected")},scopedSlots:e._u([{key:"default",fn:function(t){var s=t.row;return[a("span",{class:[s.connected?"connected":"disconnected","status-circle"]}),e._v("\n "+e._s(s.connected?e.$t("websocket.connected"):e.$t("websocket.disconnected"))+"\n ")]}}])}),e._v(" "),a("el-table-column",{attrs:{prop:"created_at",label:e.$t("clients.createdAt"),"min-width":"160px"}}),e._v(" "),a("el-table-column",{attrs:{fixed:"right",width:"120px",label:e.$t("oper.oper")},scopedSlots:e._u([{key:"default",fn:function(t){var s=t.row,i=t.$index,l=t._self;return[a("el-popover",{ref:"popover-"+i,attrs:{placement:"right",trigger:"click"}},[a("p",[e._v(e._s(s.connected?e.$t("oper.confirmKickOut"):e.$t("oper.confirmCleanSession")))]),e._v(" "),a("div",{staticStyle:{"text-align":"right"}},[a("el-button",{staticClass:"cache-btn",attrs:{size:"mini",type:"text"},on:{click:function(e){l.$refs["popover-"+i].doClose()}}},[e._v("\n "+e._s(e.$t("oper.cancel"))+"\n ")]),e._v(" "),a("el-button",{attrs:{size:"mini",type:"success"},on:{click:function(t){return e.handleDisconnect(s,i,l)}}},[e._v("\n "+e._s(e.$t("oper.confirm"))+"\n ")])],1),e._v(" "),a("el-button",{attrs:{slot:"reference",size:"mini",type:"danger",plain:""},slot:"reference"},[e._v("\n "+e._s(s.connected?e.$t("clients.kickOut"):e.$t("websocket.cleanSession"))+"\n ")])],1)]}}])})],1),e._v(" "),a("el-table",{directives:[{name:"show",rawName:"v-show",value:"topics"===e.activeTab,expression:"activeTab === 'topics'"},{name:"loading",rawName:"v-loading",value:e.$store.state.loading,expression:"$store.state.loading"}],attrs:{border:"",data:e.topics}},[a("el-table-column",{attrs:{prop:"topic",label:e.$t("topics.topic")}}),e._v(" "),a("el-table-column",{attrs:{prop:"node",label:e.$t("topics.node")}})],1),e._v(" "),a("el-table",{directives:[{name:"show",rawName:"v-show",value:"subscriptions"===e.activeTab,expression:"activeTab === 'subscriptions'"},{name:"loading",rawName:"v-loading",value:e.$store.state.loading,expression:"$store.state.loading"}],attrs:{border:"",data:e.subscriptions}},[e.cluster?a("el-table-column",{attrs:{prop:"node","min-width":"160",label:e.$t("clients.node")}}):e._e(),e._v(" "),a("el-table-column",{attrs:{prop:"clientid",label:e.$t("subscriptions.clientId")}}),e._v(" "),a("el-table-column",{attrs:{prop:"topic",label:e.$t("subscriptions.topic")}}),e._v(" "),a("el-table-column",{attrs:{prop:"qos",label:e.$t("subscriptions.qoS")}})],1),e._v(" "),a("div",{staticClass:"center-align"},[e.count>10?a("el-pagination",{attrs:{background:"",layout:"total, sizes, prev, pager, next","page-sizes":[10,50,100,300,500],"current-page":e.params._page,"page-size":e.params._limit,total:e.count},on:{"update:currentPage":function(t){return e.$set(e.params,"_page",t)},"update:current-page":function(t){return e.$set(e.params,"_page",t)},"size-change":e.handleSizeChange,"current-change":e.loadChild}}):e._e(),e._v(" "),-1===e.count&&(e.clients.length||e.subscriptions.length)?a("div",{staticClass:"custom-pagination"},[a("a",{class:["prev",1===e.params._page?"disabled":""],attrs:{href:"javascript:;"},on:{click:e.handlePrevClick}},[a("i",{staticClass:"el-icon-arrow-left"}),e._v("\n "+e._s(e.$t("oper.prev"))+"\n ")]),e._v(" "),a("a",{class:["next",e.hasnext?"":"disabled"],attrs:{href:"javascript:;"},on:{click:e.handleNextClick}},[e._v("\n "+e._s(e.$t("oper.next"))+"\n "),a("i",{staticClass:"el-icon-arrow-right"})])]):e._e()],1)],1)},staticRenderFns:[]};var c=a("VU/8")(o,r,!1,function(e){a("g2pX")},null,null);t.default=c.exports}}); \ No newline at end of file diff --git a/apps/emqx_dashboard/priv/www/static/js/10.188c5e479f887d471dde.js b/apps/emqx_dashboard/priv/www/static/js/10.188c5e479f887d471dde.js new file mode 100644 index 000000000..5480c6f23 --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/js/10.188c5e479f887d471dde.js @@ -0,0 +1 @@ +webpackJsonp([10],{QSR2:function(e,s,r){"use strict";Object.defineProperty(s,"__esModule",{value:!0});var t=r("Dd8w"),a=r.n(t),o=r("zL8q"),n=r("NYxO"),i={name:"users-view",components:{"el-dialog":o.Dialog,"el-input":o.Input,"el-button":o.Button,"el-table":o.Table,"el-table-column":o.TableColumn,"el-popover":o.Popover,"el-form":o.Form,"el-form-item":o.FormItem,"el-row":o.Row,"el-col":o.Col},data:function(){var e=this;return{changePassword:!1,dialogVisible:!1,oper:"new",users:[],record:{username:"",password:"",newPassword:"",repeatPassword:"",tags:""},rules:{username:[{required:!0,message:this.$t("users.usernameRequired")},{min:2,max:32,message:this.$t("users.usernameIllegal"),trigger:"change"}],tags:[{required:!0,message:this.$t("users.remarkRequired")}],password:[{required:!0,message:this.$t("users.passwordRequired")},{min:3,max:255,message:this.$t("users.passwordIllegal"),trigger:"change"}],newPassword:[{required:!0,message:this.$t("users.passwordRequired")},{min:3,max:255,message:this.$t("users.passwordIllegal"),trigger:"change"}],repeatPassword:[{required:!0,message:this.$t("users.passwordRequired")},{validator:function(s,r,t){r!==e.record.newPassword?t(new Error(e.$t("users.passwordInconsistent"))):t()},trigger:"change"}]}}},computed:{username:function(){return this.$store.state.user.username}},methods:a()({},Object(n.b)(["USER_LOGIN"]),{handleOperation:function(){var e=this,s=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],r=arguments[1];this.changePassword=!1,this.dialogVisible=!0,s?(this.oper="new",this.record={username:"",password:"",newPassword:"",repeatPassword:"",tags:"viewer"},setTimeout(function(){e.$refs.record.clearValidate()},10)):(this.oper="edit",this.record=a()({},r),this.$set(this.record,"password",""))},loadData:function(){var e=this;this.$httpGet("/users").then(function(s){e.users=s.data}).catch(function(s){e.$message.error(s||e.$t("error.networkError"))})},createUser:function(){var e=this;"edit"!==this.oper?this.$refs.record.validate(function(s){s&&e.$httpPost("/users",e.record).then(function(){e.$message.success(""+e.$t("users.createUser")),e.loadData(),e.dialogVisible=!1}).catch(function(s){e.$message.error(s||e.$t("error.networkError"))})}):this.updateUser()},updateUser:function(){var e=this;this.$refs.record.validate(function(s){if(s)if(e.changePassword){var r={old_pwd:e.record.password,new_pwd:e.record.newPassword};e.$httpPut("/users/"+e.record.username,e.record).then(function(){e.$httpPut("/change_pwd/"+e.record.username,r).then(function(){e.$store.state.user.username===e.record.username&&e.record.password!==e.record.newPassword?(e.$message.error(e.$t("users.authenticate")),e.USER_LOGIN({isLogOut:!0}),e.$router.push("/login")):(e.$message.success(""+e.$t("oper.edit")+e.$t("alert.success")),e.dialogVisible=!1,e.loadData())}).catch(function(s){e.$message.error(s||e.$t("error.networkError"))})})}else e.$httpPut("/users/"+e.record.username,e.record).then(function(){e.$message.success(""+e.$t("oper.edit")+e.$t("alert.success")),e.dialogVisible=!1,e.loadData()}).catch(function(s){e.$message.error(s||e.$t("error.networkError"))})})},deleteUser:function(e,s,r){var t=this;this.$httpDelete("/users/"+e.username).then(function(){t.$message.success(""+t.$t("oper.delete")+t.$t("alert.success")),t.loadData(),r.$refs["popover-"+s].doClose()}).catch(function(e){t.$message.error(e||t.$t("error.networkError"))})}}),created:function(){this.loadData()}},l={render:function(){var e=this,s=e.$createElement,r=e._self._c||s;return r("div",{staticClass:"users-view"},[r("div",{staticClass:"page-title"},[e._v("\n "+e._s(e.$t("leftbar.users"))+"\n "),r("el-button",{staticClass:"confirm-btn",staticStyle:{float:"right"},attrs:{round:"",plain:"",type:"success",icon:"el-icon-plus",size:"medium",disabled:e.$store.state.loading},on:{click:function(s){return e.handleOperation(!0)}}},[e._v("\n "+e._s(e.$t("users.newUser"))+"\n ")])],1),e._v(" "),r("el-table",{directives:[{name:"loading",rawName:"v-loading",value:e.$store.state.loading,expression:"$store.state.loading"}],attrs:{border:"",data:e.users}},[r("el-table-column",{attrs:{prop:"username",label:e.$t("users.username")}}),e._v(" "),r("el-table-column",{attrs:{prop:"tags",label:e.$t("users.remark")}}),e._v(" "),r("el-table-column",{attrs:{width:"140",label:e.$t("oper.oper")},scopedSlots:e._u([{key:"default",fn:function(s){var t=s.row,a=s.$index,o=s._self;return[r("el-button",{attrs:{size:"mini",type:"warning",plain:""},on:{click:function(s){return e.handleOperation(!1,t)}}},[e._v("\n "+e._s(e.$t("oper.edit"))+"\n ")]),e._v(" "),r("el-popover",{ref:"popover-"+a,attrs:{placement:"right",trigger:"click"}},[r("p",[e._v(e._s(e.$t("oper.confirmDelete")))]),e._v(" "),r("div",{staticStyle:{"text-align":"right"}},[r("el-button",{staticClass:"cache-btn",attrs:{size:"mini",type:"text"},on:{click:function(e){o.$refs["popover-"+a].doClose()}}},[e._v("\n "+e._s(e.$t("oper.cancel"))+"\n ")]),e._v(" "),r("el-button",{attrs:{size:"mini",type:"success"},on:{click:function(s){return e.deleteUser(t,a,o)}}},[e._v("\n "+e._s(e.$t("oper.confirm"))+"\n ")])],1),e._v(" "),r("el-button",{directives:[{name:"show",rawName:"v-show",value:"admin"!==t.username&&e.username!==t.username,expression:"row.username !== 'admin' && username !== row.username"}],attrs:{slot:"reference",size:"mini",type:"danger",plain:""},slot:"reference"},[e._v("\n "+e._s(e.$t("oper.delete"))+"\n ")])],1)]}}])})],1),e._v(" "),r("el-dialog",{attrs:{width:"500px",visible:e.dialogVisible,title:"new"===e.oper?e.$t("users.newUser"):e.$t("users.editUser")},on:{"update:visible":function(s){e.dialogVisible=s}},nativeOn:{keyup:function(s){return!s.type.indexOf("key")&&e._k(s.keyCode,"enter",13,s.key,"Enter")?null:e.createUser(s)}}},[r("el-form",{ref:"record",staticClass:"el-form--public",attrs:{"label-position":"top",size:"medium",model:e.record,rules:e.rules}},[r("el-row",{attrs:{gutter:20}},[r("el-col",{attrs:{span:24}},[r("el-form-item",{attrs:{prop:"username",label:e.$t("users.username")}},[r("el-input",{attrs:{disabled:"edit"===e.oper},model:{value:e.record.username,callback:function(s){e.$set(e.record,"username",s)},expression:"record.username"}})],1)],1),e._v(" "),"new"===e.oper?r("el-col",{attrs:{span:24}},[r("el-form-item",{attrs:{prop:"password",label:e.$t("users.password")}},[r("el-input",{attrs:{type:"password"},model:{value:e.record.password,callback:function(s){e.$set(e.record,"password",s)},expression:"record.password"}})],1)],1):e._e(),e._v(" "),e.changePassword&&"edit"===e.oper?r("div",[r("el-col",{attrs:{span:24}},[r("el-form-item",{attrs:{prop:"password",label:e.$t("users.oldPassword")}},[r("el-input",{attrs:{type:"password"},model:{value:e.record.password,callback:function(s){e.$set(e.record,"password",s)},expression:"record.password"}})],1)],1),e._v(" "),r("el-col",{attrs:{span:24}},[r("el-form-item",{attrs:{prop:"newPassword",label:e.$t("users.newPassword")}},[r("el-input",{attrs:{type:"password"},model:{value:e.record.newPassword,callback:function(s){e.$set(e.record,"newPassword",s)},expression:"record.newPassword"}})],1)],1),e._v(" "),r("el-col",{attrs:{span:24}},[r("el-form-item",{attrs:{prop:"repeatPassword",label:e.$t("users.confirmNewPassword")}},[r("el-input",{attrs:{type:"password"},model:{value:e.record.repeatPassword,callback:function(s){e.$set(e.record,"repeatPassword",s)},expression:"record.repeatPassword"}})],1)],1)],1):e._e(),e._v(" "),r("el-col",{attrs:{span:24}},[r("el-form-item",{attrs:{prop:"tags",label:e.$t("users.remark")}},[r("el-input",{model:{value:e.record.tags,callback:function(s){e.$set(e.record,"tags",s)},expression:"record.tags"}})],1)],1),e._v(" "),r("el-col",{attrs:{span:24}},[r("el-form-item",["edit"===e.oper?r("el-button",{staticClass:"cache-btn change-password",attrs:{type:"text"},on:{click:function(s){e.changePassword=!e.changePassword}}},[e._v("\n "+e._s(e.changePassword?e.$t("users.dontChangePassword"):e.$t("users.changePassword"))+"\n ")]):e._e()],1)],1)],1)],1),e._v(" "),r("div",{attrs:{slot:"footer"},slot:"footer"},[r("el-button",{staticClass:"cache-btn",attrs:{type:"text"},on:{click:function(s){e.dialogVisible=!1}}},[e._v("\n "+e._s(e.$t("oper.cancel"))+"\n ")]),e._v(" "),r("el-button",{staticClass:"confirm-btn",attrs:{type:"success",loading:e.$store.state.loading},on:{click:e.createUser}},[e._v("\n "+e._s(e.$t("oper.save"))+"\n ")])],1)],1)],1)},staticRenderFns:[]};var c=r("VU/8")(i,l,!1,function(e){r("gHf8")},null,null);s.default=c.exports},gHf8:function(e,s){}}); \ No newline at end of file diff --git a/apps/emqx_dashboard/priv/www/static/js/11.3861aeb3036b8f41a6e8.js b/apps/emqx_dashboard/priv/www/static/js/11.3861aeb3036b8f41a6e8.js new file mode 100644 index 000000000..3aae89ad8 --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/js/11.3861aeb3036b8f41a6e8.js @@ -0,0 +1 @@ +webpackJsonp([11],{DoQ2:function(t,e){},GQ4E:function(t,e,s){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var a=s("woOf"),n=s.n(a),o={name:"topic-metrics",components:{},props:{},watch:{currentExpandRow:{deep:!0,handler:function(){clearInterval(this.timer)}}},data:function(){return{expands:[],addVisible:!1,popoverVisible:!1,modClosed:!1,topicQos:"all",timer:0,topics:[],currentExpandRow:{},currentTopic:{},record:{},rules:{topic:{required:!0,message:this.$t("oper.pleaseEnter")}}}},methods:{getRowKeys:function(t){return t.topic},loadData:function(){var t=this;this.$httpGet("/topic-metrics").then(function(e){var s=e.data;t.topics=s.map(function(t){var e=t.metrics;return{topic:t.topic,messageIn:e["messages.in.count"],messageOut:e["messages.out.count"],messageDrop:e["messages.dropped.count"]}}),t.modClosed=!1}).catch(function(e){t.$message.warning(t.$t("error."+e.message)),t.modClosed=!0})},hidePopover:function(){var t=this;this.popoverVisible=!0,setTimeout(function(){t.popoverVisible=!1},0)},handleOperation:function(){this.addVisible=!0},handleModLoad:function(){var t=this;this.$httpPut("/modules/emqx_mod_topic_metrics/load").then(function(){t.$message.success(t.$t("oper.enableSuccess")),t.loadData(),t.modClosed=!1}).catch(function(e){t.$message.error(e||t.$t("error.networkError"))})},deleteTopicMetric:function(t){var e=this;this.$httpDelete("/topic-metrics/"+encodeURIComponent(t.topic)).then(function(){e.loadData(),e.hidePopover()}).catch(function(t){e.$message.error(t||e.$t("error.networkError"))})},handleAdd:function(){var t=this;this.$refs.record.validate(function(e){if(e){var s={};n()(s,t.record),t.$httpPost("/topic-metrics",s).then(function(){t.handleClose(),t.loadData()}).catch(function(){})}})},handleClose:function(){this.addVisible=!1,this.$refs.record.resetFields()},viewTopicDetails:function(t,e){var s=document.querySelectorAll(".el-table__expand-icon")[e];s&&s.click()},loadDetail:function(){var t=this;this.$httpGet("/topic-metrics/"+encodeURIComponent(this.currentTopic.topic)).then(function(e){t.currentTopic=e.data,t.loadData()}).catch(function(){})},setLoadDetailInterval:function(){var t=this;this.timer=setInterval(function(){t.$httpGet("/topic-metrics/"+encodeURIComponent(t.currentExpandRow.topic)).then(function(e){t.currentTopic=e.data}).catch(function(){})},1e4)},handleExpandChange:function(t,e){var s=this;if(!e.length)return this.currentExpandRow={},void clearInterval(this.timer);this.currentExpandRow=t,this.currentTopic={},this.$httpGet("/topic-metrics/"+encodeURIComponent(t.topic)).then(function(a){s.currentTopic=a.data,s.$refs.crudTable.store.states.expandRows=e.length?[t]:[],s.loadData(),s.setLoadDetailInterval()}).catch(function(){})},getCurrentTopicData:function(t,e){var s={all:"messages",qos0:"messages.qos0",qos1:"messages.qos1",qos2:"messages.qos2"}[this.topicQos],a=this.currentTopic[s+"."+t+"."+e];return"rate"===e&&a?a.toFixed(2):a},getCurrentTopicDropRate:function(t){return t?t.toFixed(2):t}},created:function(){this.loadData()},beforeRouteLeave:function(t,e,s){clearInterval(this.timer),s()},beforeDestroy:function(){clearInterval(this.timer)}},i={render:function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"topic-metrics"},[s("div",{staticClass:"page-title"},[t._v("\n "+t._s(t.$t("analysis.topicMetrics"))+"\n "),s("span",{staticClass:"sub-tip"},[t._v(t._s(t.$t("analysis.metricsTip")))]),t._v(" "),t.modClosed?s("el-button",{staticClass:"confirm-btn",staticStyle:{float:"right"},attrs:{round:"",plain:"",type:"success",size:"medium",disable:t.$store.state.loading},on:{click:t.handleModLoad}},[t._v("\n "+t._s(t.$t("modules.enable"))+"\n ")]):s("el-button",{staticClass:"confirm-btn",staticStyle:{float:"right"},attrs:{round:"",plain:"",type:"success",icon:"el-icon-plus",size:"medium",disable:t.$store.state.loading},on:{click:t.handleOperation}},[t._v("\n "+t._s(t.$t("rule.create"))+"\n ")])],1),t._v(" "),s("el-table",{directives:[{name:"loading",rawName:"v-loading",value:t.$store.state.loading,expression:"$store.state.loading"}],ref:"crudTable",attrs:{border:"",data:t.topics,"row-key":t.getRowKeys,"expand-row-keys":t.expands},on:{"expand-change":t.handleExpandChange}},[s("el-table-column",{attrs:{type:"expand"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("div",{staticClass:"expand-header"},[t._v("\n "+t._s(t.$t("analysis.details"))+"\n "),s("el-radio-group",{staticClass:"topic-qos-radio",attrs:{prop:e,size:"mini"},model:{value:t.topicQos,callback:function(e){t.topicQos=e},expression:"topicQos"}},[s("el-radio-button",{attrs:{label:"all"}},[t._v(t._s(t.$t("analysis.all")))]),t._v(" "),s("el-radio-button",{attrs:{label:"qos0"}},[t._v("QoS 0")]),t._v(" "),s("el-radio-button",{attrs:{label:"qos1"}},[t._v("QoS 1")]),t._v(" "),s("el-radio-button",{attrs:{label:"qos2"}},[t._v("QoS 2")])],1)],1),t._v(" "),s("el-row",{staticClass:"expand-body",attrs:{gutter:20}},[s("el-col",{attrs:{span:8}},[s("div",{staticClass:"message-card in"},[s("div",[t._v("\n "+t._s(t.$t("analysis.messageIn"))+"\n "),s("span",{staticClass:"message-rate"},[t._v("\n "+t._s(t.$t("analysis.rateItem",[t.getCurrentTopicData("in","rate")]))+"\n "+t._s(t.$t("analysis.rate"))+"\n ")])]),t._v(" "),s("div",{staticClass:"message-card--body"},[t._v("\n "+t._s(t.getCurrentTopicData("in","count"))+"\n ")])])]),t._v(" "),s("el-col",{attrs:{span:8}},[s("div",{staticClass:"message-card out"},[s("div",[t._v("\n "+t._s(t.$t("analysis.messageOut"))+"\n "),s("span",{staticClass:"message-rate"},[t._v("\n "+t._s(t.$t("analysis.rateItem",[t.getCurrentTopicData("out","rate")]))+"\n "+t._s(t.$t("analysis.rate"))+"\n ")])]),t._v(" "),s("div",{staticClass:"message-card--body"},[t._v("\n "+t._s(t.getCurrentTopicData("out","count"))+"\n ")])])]),t._v(" "),s("el-col",{attrs:{span:8}},[s("div",{staticClass:"message-card drop"},[s("div",[t._v("\n "+t._s(t.$t("analysis.messageDrop"))+"\n "),s("span",{staticClass:"message-rate"},[t._v("\n "+t._s(t.$t("analysis.rateItem",[t.getCurrentTopicDropRate(t.currentTopic["messages.dropped.rate"])]))+"\n "+t._s(t.$t("analysis.rate"))+"\n ")])]),t._v(" "),s("div",{staticClass:"message-card--body"},[t._v("\n "+t._s(t.currentTopic["messages.dropped.count"])+"\n ")])])])],1)]}}])}),t._v(" "),s("el-table-column",{attrs:{prop:"topic",label:t.$t("topics.topic")}}),t._v(" "),s("el-table-column",{attrs:{prop:"messageIn",label:t.$t("analysis.messageIn")}}),t._v(" "),s("el-table-column",{attrs:{prop:"messageOut",label:t.$t("analysis.messageOut")}}),t._v(" "),s("el-table-column",{attrs:{prop:"messageDrop",label:t.$t("analysis.messageDrop")}}),t._v(" "),s("el-table-column",{attrs:{width:"180px",label:t.$t("oper.oper")},scopedSlots:t._u([{key:"default",fn:function(e){return[s("el-button",{attrs:{type:"success",size:"mini",plain:""},on:{click:function(s){return t.viewTopicDetails(e.row,e.$index)}}},[t._v("\n "+t._s(t.$t("oper.view"))+"\n ")]),t._v(" "),s("el-popover",{attrs:{placement:"right",trigger:"click",value:t.popoverVisible}},[s("p",[t._v(t._s(t.$t("oper.confirmDelete")))]),t._v(" "),s("div",{staticStyle:{"text-align":"right"}},[s("el-button",{staticClass:"cache-btn",attrs:{size:"mini",type:"text"},on:{click:t.hidePopover}},[t._v("\n "+t._s(t.$t("oper.cancel"))+"\n ")]),t._v(" "),s("el-button",{attrs:{size:"mini",type:"success"},on:{click:function(s){return t.deleteTopicMetric(e.row)}}},[t._v("\n "+t._s(t.$t("oper.confirm"))+"\n ")])],1),t._v(" "),s("el-button",{attrs:{slot:"reference",size:"mini",type:"danger",plain:""},slot:"reference"},[t._v("\n "+t._s(t.$t("oper.delete"))+"\n ")])],1)]}}])})],1),t._v(" "),s("el-dialog",{staticClass:"create-subscribe",attrs:{title:t.$t("analysis.addTopic"),width:"400px",visible:t.addVisible},on:{"update:visible":function(e){t.addVisible=e}},nativeOn:{keyup:function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:t.handleAdd(e)}}},[s("el-form",{ref:"record",staticClass:"el-form--public",attrs:{model:t.record,rules:t.rules,size:"small","label-position":"top"}},[s("el-form-item",{attrs:{prop:"topic",label:t.$t("subscriptions.topic")}},[s("el-input",{attrs:{placeholder:"Topic"},model:{value:t.record.topic,callback:function(e){t.$set(t.record,"topic",e)},expression:"record.topic"}})],1)],1),t._v(" "),s("div",{attrs:{slot:"footer"},slot:"footer"},[s("el-button",{staticClass:"cache-btn",attrs:{type:"text"},on:{click:t.handleClose}},[t._v("\n "+t._s(t.$t("oper.cancel"))+"\n ")]),t._v(" "),s("el-button",{staticClass:"confirm-btn",attrs:{type:"success",loading:t.$store.state.loading},on:{click:t.handleAdd}},[t._v("\n "+t._s(t.$t("oper.add"))+"\n ")])],1)],1)],1)},staticRenderFns:[]};var r=s("VU/8")(o,i,!1,function(t){s("DoQ2")},null,null);e.default=r.exports}}); \ No newline at end of file diff --git a/apps/emqx_dashboard/priv/www/static/js/12.43feccc8f1584bdba5c2.js b/apps/emqx_dashboard/priv/www/static/js/12.43feccc8f1584bdba5c2.js new file mode 100644 index 000000000..ae12b78ce --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/js/12.43feccc8f1584bdba5c2.js @@ -0,0 +1 @@ +webpackJsonp([12],{BYOx:function(t,e){},VKKr:function(t,e,s){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var a=s("mvHQ"),o=s.n(a),i=s("zL8q"),n=s("VOAv"),l={name:"settings-view",components:{"el-radio":i.Radio,"el-radio-group":i.RadioGroup,"el-button":i.Button,"el-form":i.Form,"el-form-item":i.FormItem,"el-row":i.Row,"el-col":i.Col,"el-card":i.Card},data:function(){return{options:{themes:"",language:""},defaultConfig:"",defaultThemes:"",defaultLanguage:""}},computed:{notChanged:function(){return this.defaultConfig===o()(this.options)}},methods:{init:function(){var t=window.localStorage.getItem("themes")||"dark-themes";t="light-themes"===t?"light-themes":"dark-themes",this.options.themes=t,this.defaultThemes=t,this.options.language=window.localStorage.getItem("language")||"en",this.options.language=["zh","en","ja"].includes(this.options.language)?this.options.language:"en",this.defaultLanguage=this.options.language,this.defaultConfig=o()(this.options)},themesToggle:function(){Object(n.b)(this.options.themes)},applySetting:function(){this.$message.success(this.$t("settings.success")),this.themesToggle(),this.defaultThemes=this.options.themes,window.localStorage.setItem("language",this.options.language),window.localStorage.setItem("themes",this.options.themes),this.defaultLanguage!==this.options.language&&setTimeout(function(){location.reload()},600),this.defaultConfig=o()(this.options)}},created:function(){this.init()},beforeRouteLeave:function(t,e,s){this.defaultThemes!==this.options.themes&&Object(n.b)(this.defaultThemes),s()}},r={render:function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"settings-view"},[s("div",{staticClass:"page-title"},[t._v(t._s(t.$t("leftbar.settings")))]),t._v(" "),s("el-card",{staticClass:"el-card--self"},[s("el-row",{attrs:{gutter:20}},[s("el-form",{ref:"options",attrs:{model:t.options,"label-width":"100px","label-position":"top"}},[s("el-col",{attrs:{span:12}},[s("el-form-item",{attrs:{label:t.$t("settings.themes")}},[s("el-radio-group",{on:{change:t.themesToggle},model:{value:t.options.themes,callback:function(e){t.$set(t.options,"themes",e)},expression:"options.themes"}},[s("el-radio",{attrs:{label:"dark-themes"}},[t._v("Dark")]),t._v(" "),s("el-radio",{attrs:{label:"light-themes"}},[t._v("Light")])],1)],1)],1),t._v(" "),s("el-col",{attrs:{span:12}},[s("el-form-item",{attrs:{label:t.$t("settings.language")}},[s("el-radio-group",{model:{value:t.options.language,callback:function(e){t.$set(t.options,"language",e)},expression:"options.language"}},[s("el-radio",{attrs:{label:"en"}},[t._v("EN")]),t._v(" "),s("el-radio",{attrs:{label:"zh"}},[t._v("中文")]),t._v(" "),s("el-radio",{attrs:{label:"ja"}},[t._v("日本語")])],1)],1)],1),t._v(" "),s("el-col",{staticClass:"operation-area",attrs:{span:24}},[s("el-form-item",[s("el-button",{staticClass:"confirm-btn",attrs:{type:"success",disabled:t.notChanged},on:{click:t.applySetting}},[t._v("\n "+t._s(t.$t("settings.apply"))+"\n ")])],1)],1)],1)],1)],1)],1)},staticRenderFns:[]};var g=s("VU/8")(l,r,!1,function(t){s("BYOx")},null,null);e.default=g.exports}}); \ No newline at end of file diff --git a/apps/emqx_dashboard/priv/www/static/js/13.026a13a2a59abd354bd5.js b/apps/emqx_dashboard/priv/www/static/js/13.026a13a2a59abd354bd5.js new file mode 100644 index 000000000..22e38efdb --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/js/13.026a13a2a59abd354bd5.js @@ -0,0 +1 @@ +webpackJsonp([13],{IvP6:function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=i("Xxa5"),a=i.n(n),s=i("exGp"),l=i.n(s),r={name:"rules-view",components:{RuleActions:i("eDC2").a},props:{},data:function(){return{ruleDialogLoading:!1,timer:0,rule:{for:[],metrics:{}},dialogVisible:!1,tableData:[]}},methods:{getMatchedCount:function(){var t=(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}).metrics,e=0;return(void 0===t?[]:t).forEach(function(t){var i=t.matched;e+=i}),e},getHitRate:function(t){var e=t.matched,i=void 0===e?0:e,n=t.nomatch,a=i/(i+(void 0===n?0:n))*100;return a.toString().split(".")[1]&&a.toString().split(".")[1].length>2?a.toFixed(2):a},viewRule:function(t){var e=this;return l()(a.a.mark(function i(){var n;return a.a.wrap(function(i){for(;;)switch(i.prev=i.next){case 0:if(!t.id){i.next=3;break}return e.$router.push("/rules/"+t.id),i.abrupt("return");case 3:return i.next=5,e.$httpGet("/rules/"+t.id);case 5:if(i.t1=i.sent,i.t1){i.next=8;break}i.t1={};case 8:if(i.t0=i.t1.data,i.t0){i.next=11;break}i.t0={};case 11:n=i.t0,e.rule=n||t,e.dialogVisible=!0,clearTimeout(e.timer),e.timer=setTimeout(function(){e.viewRule(t)},1e4);case 16:case"end":return i.stop()}},i,e)}))()},editRule:function(t){this.$router.push("/rules/create?rule="+t.id)},loadDetails:function(t){var e=this;this.ruleDialogLoading=!0,this.$httpGet("/rules/"+t).then(function(t){var i=t.data;e.rule=i,setTimeout(function(){e.ruleDialogLoading=!1},500)}).catch(function(){e.ruleDialogLoading=!1})},closeDialog:function(){clearInterval(this.timer),this.loadData()},handleDelete:function(t){var e=this;this.$confirm(this.$t("rule.confirm_stop_delete"),"Notice",{confirmButtonClass:"confirm-btn",confirmButtonText:this.$t("oper.confirm"),cancelButtonClass:"cache-btn el-button--text",cancelButtonText:this.$t("oper.cancel"),type:"warning"}).then(function(){e.$httpDelete("/rules/"+t.id).then(function(){e.$message.success(e.$t("rule.delete_success")),e.loadData()})}).catch()},handleOperation:function(){this.$router.push("/rules/create")},loadData:function(){var t=this;this.$httpGet("/rules").then(function(e){t.tableData=e.data;var i=t.tableData.find(function(e){return e.id===t.rule.id});i&&(t.rule=i)})},updateRule:function(t){var e=this,i=t.id,n=t.enabled;this.$httpPut("/rules/"+i,{enabled:n}).then(function(){e.$message.success(e.$t("oper.editSuccess"))})}},filters:{actionsFilter:function(t){return t.map(function(t){return t.name}).join(", ")}},created:function(){this.loadData(),clearInterval(this.timer)},beforeRouteLeave:function(t,e,i){clearInterval(this.timer),i()}},o={render:function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",{staticClass:"rules-view"},[i("div",{staticClass:"page-title"},[t._v("\n "+t._s(t.$t("rule.message_rule"))+"\n "),i("el-button",{staticClass:"confirm-btn",staticStyle:{float:"right"},attrs:{round:"",plain:"",type:"success",icon:"el-icon-plus",size:"medium",disable:t.$store.state.loading},on:{click:t.handleOperation}},[t._v("\n "+t._s(t.$t("rule.create"))+"\n ")])],1),t._v(" "),i("el-table",{directives:[{name:"loading",rawName:"v-loading",value:t.$store.state.loading,expression:"$store.state.loading"}],attrs:{border:"",data:t.tableData}},[i("el-table-column",{attrs:{prop:"id",label:t.$t("rule.id")},scopedSlots:t._u([{key:"default",fn:function(e){var n=e.row;return[i("span",{staticClass:"btn",on:{click:function(e){return t.viewRule(n)}}},[t._v("\n "+t._s(n.id)+"\n ")])]}}])}),t._v(" "),i("el-table-column",{attrs:{prop:"for",label:t.$t("rule.topic")}}),t._v(" "),i("el-table-column",{attrs:{prop:"rawsql","min-width":"150px",label:"SQL"}}),t._v(" "),i("el-table-column",{attrs:{prop:"actions",label:t.$t("rule.actions")},scopedSlots:t._u([{key:"default",fn:function(e){var n=e.row;return t._l(n.actions,function(e,n){return i("div",{key:n,staticClass:"action-item"},[t._v("\n "+t._s(e.name)+"\n ")])})}}])}),t._v(" "),i("el-table-column",{attrs:{prop:"metrics.matched","min-width":"110px",label:t.$t("rule.rule_matched_1"),formatter:t.getMatchedCount}}),t._v(" "),i("el-table-column",{attrs:{label:t.$t("rule.viewStates")},scopedSlots:t._u([{key:"default",fn:function(e){return[i("el-tooltip",{attrs:{content:e.row.enabled?t.$t("rule.ruleEnabled"):t.$t("rule.ruleDisabled"),placement:"left"}},[i("el-switch",{attrs:{"active-text":"","inactive-text":"","active-color":"#13ce66","inactive-color":"#ff4949"},on:{change:function(i){return t.updateRule(e.row)}},model:{value:e.row.enabled,callback:function(i){t.$set(e.row,"enabled",i)},expression:"props.row.enabled"}})],1)]}}])}),t._v(" "),i("el-table-column",{attrs:{label:t.$t("rule.oper"),"min-width":"120px"},scopedSlots:t._u([{key:"default",fn:function(e){var n=e.row;return[i("el-button",{attrs:{type:"success",size:"mini",plain:""},on:{click:function(e){return t.editRule(n)}}},[t._v("\n "+t._s(t.$t("rule.edit"))+"\n ")]),t._v(" "),i("el-button",{attrs:{size:"mini",type:"danger",plain:""},on:{click:function(e){return t.handleDelete(n)}}},[t._v("\n "+t._s(t.$t("rule.delete"))+"\n ")])]}}])})],1),t._v(" "),i("el-dialog",{attrs:{title:t.$t("rule.rule_details"),visible:t.dialogVisible},on:{"update:visible":function(e){t.dialogVisible=e},close:t.closeDialog}},[i("div",{staticClass:"dialog-preview"},[i("div",{staticClass:"option-item"},[i("div",{staticClass:"option-title"},[t._v(t._s(t.$t("rule.id")))]),t._v(" "),i("div",{staticClass:"option-value"},[t._v(t._s(t.rule.id))])]),t._v(" "),i("div",{staticClass:"option-item"},[i("div",{staticClass:"option-title"},[t._v(t._s(t.$t("rule.trigger_events")))]),t._v(" "),i("div",{staticClass:"option-value"},[t._v(t._s((t.rule.for||[]).join(",")))])]),t._v(" "),i("div",{staticClass:"option-item"},[i("div",{staticClass:"option-title"},[t._v(t._s(t.$t("rule.rule_desc")))]),t._v(" "),i("div",{staticClass:"option-value"},[t._v(t._s(t.rule.description))])]),t._v(" "),i("div",{staticClass:"option-item"},[i("div",{staticClass:"option-title"},[t._v("SQL")]),t._v(" "),i("div",{staticClass:"option-all"},[i("code",[t._v("\n "+t._s(t.rule.rawsql)+"\n ")])])]),t._v(" "),i("div",{staticClass:"option-item"},[i("div",{staticClass:"option-title"},[t._v("\n "+t._s(t.$t("rule.metrics"))+"\n "),i("i",{directives:[{name:"show",rawName:"v-show",value:t.ruleDialogLoading,expression:"ruleDialogLoading"}],staticClass:"el-icon-loading"})]),t._v(" "),i("div",{staticClass:"option-all"},[i("span",{attrs:{size:"mini",type:"info"}},[t._v("\n "+t._s(t.$t("rule.rule_matched_1"))+": "),i("span",[t._v(t._s(t.rule.metrics.matched))]),t._v(" "+t._s(t.$t("rule.match_unit"))+"\n ")]),t._v(" "),i("span",{attrs:{size:"mini",type:"info"}},[t._v("\n "+t._s(t.$t("rule.speed_current"))+": "),i("span",[t._v(t._s(t.rule.metrics.speed))]),t._v(" "+t._s(t.$t("rule.speed_unit"))+"\n ")]),t._v(" "),i("span",{attrs:{size:"mini",type:"info"}},[t._v("\n "+t._s(t.$t("rule.speed_max_1"))+": "),i("span",[t._v(t._s(t.rule.metrics.speed_max))]),t._v(" "+t._s(t.$t("rule.speed_unit"))+"\n ")]),t._v(" "),i("span",{attrs:{size:"mini",type:"info"}},[t._v("\n "+t._s(t.$t("rule.speed_last5m_1"))+": "),i("span",[t._v(t._s(t.rule.metrics.speed_last5m))]),t._v(" "+t._s(t.$t("rule.speed_unit"))+"\n ")])])]),t._v(" "),i("el-table-column",{attrs:{prop:"description",label:t.$t("rule.description")}}),t._v(" "),i("div",{staticClass:"option-item"},[i("div",{staticClass:"option-title"},[t._v("\n "+t._s(t.$t("rule.actions"))+"\n "),i("i",{directives:[{name:"show",rawName:"v-show",value:t.ruleDialogLoading,expression:"ruleDialogLoading"}],staticClass:"el-icon-loading"})]),t._v(" "),i("div",{staticClass:"option-all"},[i("rule-actions",{attrs:{"in-dialog":"",record:t.rule,operations:[]}})],1)])],1),t._v(" "),i("div",{attrs:{slot:"footer"},slot:"footer"},[i("el-button",{staticClass:"confirm-btn",attrs:{type:"success"},on:{click:function(e){t.dialogVisible=!1}}},[t._v("\n "+t._s(t.$t("rule.confirm"))+"\n ")])],1)])],1)},staticRenderFns:[]};var u=i("VU/8")(r,o,!1,function(t){i("SVmr")},null,null);e.default=u.exports},SVmr:function(t,e){}}); \ No newline at end of file diff --git a/apps/emqx_dashboard/priv/www/static/js/14.0342a1a3d29f1adca947.js b/apps/emqx_dashboard/priv/www/static/js/14.0342a1a3d29f1adca947.js new file mode 100644 index 000000000..4f2f11065 --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/js/14.0342a1a3d29f1adca947.js @@ -0,0 +1 @@ +webpackJsonp([14],{JWuK:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=r("Xxa5"),s=r.n(a),l=r("exGp"),n=r.n(l),o={name:"RuleView",components:{RuleActions:r("eDC2").a},props:{},data:function(){return{record:{actions:[{id:"inspect_1562305995013447740",metrics:[{failed:0,node:"emqx@127.0.0.1",success:0}],name:"inspect",params:{}}],description:"",enabled:!0,for:["message.publish"],id:"rule:b35e3e59",metrics:[{matched:0,node:"emqx@127.0.0.1",speed:0,speed_last5m:0,speed_max:0}],rawsql:"SELECT\n *\nFROM\n \"message.publish\"\nWHERE\n topic =~ '#'"}}},methods:{loadData:function(){var e=this;return n()(s.a.mark(function t(){var r;return s.a.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:if(e.id){t.next=2;break}return t.abrupt("return");case 2:return t.next=4,e.$httpGet("/rules/"+e.id);case 4:r=t.sent,e.record=r.data;case 6:case"end":return t.stop()}},t,e)}))()}},created:function(){this.loadData()},computed:{id:function(){return this.$route.params.id}}},c={render:function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("div",{staticClass:"rule-view"},[r("div",{staticClass:"page-title"},[r("el-breadcrumb",{attrs:{separator:"/"}},[r("el-breadcrumb-item",{attrs:{to:{path:"/rules"}}},[e._v("\n "+e._s(e.$t("rule.message_rule"))+"\n ")]),e._v(" "),r("el-breadcrumb-item",{staticClass:"breadcrumb-name"},[e._v(e._s(e.id))])],1)],1),e._v(" "),r("el-card",{staticClass:"el-card--self"},[r("div",{staticClass:"config-dialog",attrs:{slot:"header"},slot:"header"},[e._v("\n "+e._s(e.$t("rule.basic_info"))+"\n ")]),e._v(" "),r("el-form",{attrs:{model:e.record,"label-position":"left","label-width":"100px","label-suffix":":"}},[r("el-form-item",{attrs:{label:e.$t("rule.topic")}},[r("span",[e._v(e._s(e.record.for.join(",")))])]),e._v(" "),r("el-form-item",{attrs:{label:e.$t("rule.description")}},[r("span",[e._v(e._s(e.record.description))])]),e._v(" "),r("el-form-item",{attrs:{label:e.$t("rule.rule_sql")}},[r("code",[e._v(e._s(e.record.rawsql))])])],1)],1),e._v(" "),r("el-card",{staticClass:"el-card--self"},[r("div",{staticClass:"config-dialog",attrs:{slot:"header"},slot:"header"},[e._v("\n "+e._s(e.$t("rule.metrics"))+"\n ")]),e._v(" "),r("el-table",{attrs:{border:"",data:e.record.metrics}},[r("el-table-column",{attrs:{prop:"node",label:e.$t("rule.node")}}),e._v(" "),r("el-table-column",{attrs:{prop:"matched",sortable:"",label:e.$t("rule.rule_matched_1")}}),e._v(" "),r("el-table-column",{attrs:{prop:"speed",sortable:"",label:e.$t("rule.speed_current")}}),e._v(" "),r("el-table-column",{attrs:{prop:"speed_max",label:e.$t("rule.speed_max_1")}}),e._v(" "),r("el-table-column",{attrs:{prop:"speed_last5m",label:e.$t("rule.speed_last5m_1")}})],1)],1),e._v(" "),r("el-card",{staticClass:"el-card--self"},[r("div",{staticClass:"config-dialog",attrs:{slot:"header"},slot:"header"},[e._v("\n "+e._s(e.$t("rule.set_action"))+"\n ")]),e._v(" "),r("rule-actions",{attrs:{record:e.record,operations:[]}})],1)],1)},staticRenderFns:[]};var i=r("VU/8")(o,c,!1,function(e){r("g3JU")},null,null);t.default=i.exports},g3JU:function(e,t){}}); \ No newline at end of file diff --git a/apps/emqx_dashboard/priv/www/static/js/15.7d11711536eb5b2ca561.js b/apps/emqx_dashboard/priv/www/static/js/15.7d11711536eb5b2ca561.js new file mode 100644 index 000000000..54486f22f --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/js/15.7d11711536eb5b2ca561.js @@ -0,0 +1 @@ +webpackJsonp([15],{DGQ0:function(t,e){},xPbZ:function(t,e,s){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var i=s("Dd8w"),n=s.n(i),a={name:"resources-view",components:{ResourceDialog:s("SHGx").a},props:{},data:function(){return{dialogVisible:!1,viewDialogVisible:!1,tableData:[],res:{},reloadLoading:!1,currentResource:""}},methods:{viewRunningStatus:function(t,e){var s=document.querySelectorAll(".el-table__expand-icon")[e];s&&s.click&&s.click()},handleReconnect:function(t,e){var s=this;this.reloadLoading=!0,this.currentResource=t.id,this.$httpPost("/resources/"+t.id).then(function(){setTimeout(function(){s.reloadLoading=!1,s.$message.success(s.$t("rule.connectSuccess"));try{t.status[e].is_alive=!0}catch(t){console.log(t)}},300)}).catch(function(){s.reloadLoading=!1})},handleDelete:function(t){var e=this;this.$confirm(this.$t("rule.confirm_stop_delete"),"Notice",{confirmButtonClass:"confirm-btn",confirmButtonText:this.$t("oper.confirm"),cancelButtonClass:"cache-btn el-button--text",cancelButtonText:this.$t("oper.cancel"),type:"warning"}).then(function(){e.$httpDelete("/resources/"+t.id).then(function(){e.$message.success(e.$t("rule.delete_success")),e.loadData()})}).catch()},viewResource:function(t){this.res=n()({},t),this.viewDialogVisible=!0},handleOperation:function(){this.dialogVisible=!0},loadData:function(){var t=this;this.$httpGet("/resources").then(function(e){var s=e.data;t.tableData=s.map(function(t){return t.status=t.status||[],t})})},handExpand:function(t){var e=this;t.status&&t.status.length>0||this.$httpGet("/resources/"+t.id).then(function(s){e.$set(t,"status",s.data.status)})}},created:function(){this.loadData()}},o={render:function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"resources-view"},[s("div",{staticClass:"page-title"},[t._v("\n "+t._s(t.$t("rule.resource_title"))+"\n "),s("el-button",{staticClass:"confirm-btn",staticStyle:{float:"right"},attrs:{round:"",plain:"",type:"success",icon:"el-icon-plus",size:"medium",disable:t.$store.state.loading},on:{click:t.handleOperation}},[t._v("\n "+t._s(t.$t("rule.create"))+"\n ")])],1),t._v(" "),s("el-table",{attrs:{border:"",data:t.tableData},on:{"expand-change":t.handExpand}},[s("el-table-column",{attrs:{prop:"id",type:"expand","class-name":"expand-column",width:"1px"},scopedSlots:t._u([{key:"default",fn:function(e){var i=e.row;return[s("ul",{staticClass:"status-wrapper"},t._l(i.status||[],function(e,n){return s("li",{key:n,staticClass:"status-item"},[s("span",{staticClass:"key"},[t._v("\n "+t._s(e.node)+"\n ")]),t._v(" "),s("span",{class:[e.is_alive?"running":"stopped danger","status"]},[t._v("\n "+t._s(e.is_alive?t.$t("rule.enabled"):t.$t("rule.disabled"))+"\n ")]),t._v(" "),e.is_alive?t._e():s("el-button",{attrs:{loading:t.reloadLoading&&t.currentResource===i.id,plain:"",type:"success",size:"mini"},on:{click:function(e){return t.handleReconnect(i,n)}}},[t._v("\n "+t._s(t.$t("rule.reconnect"))+"\n ")])],1)}),0)]}}])}),t._v(" "),s("el-table-column",{attrs:{prop:"id",label:t.$t("rule.id")},scopedSlots:t._u([{key:"default",fn:function(e){var i=e.row;return[s("span",{on:{click:function(e){return t.viewResource(i)}}},[t._v("\n "+t._s(i.id)+"\n ")])]}}])}),t._v(" "),s("el-table-column",{attrs:{prop:"description",label:t.$t("rule.resource_des")}}),t._v(" "),s("el-table-column",{attrs:{prop:"type",label:t.$t("rule.resource_type")}}),t._v(" "),s("el-table-column",{attrs:{label:t.$t("rule.oper")},scopedSlots:t._u([{key:"default",fn:function(e){var i=e.row,n=e.$index;return[s("el-button",{attrs:{plain:"",type:"success",size:"mini"},on:{click:function(e){return t.viewResource(i)}}},[t._v("\n "+t._s(t.$t("rule.view"))+"\n ")]),t._v(" "),s("el-button",{attrs:{plain:"",size:"mini",type:"warning"},on:{click:function(e){return t.handleDelete(i)}}},[t._v("\n "+t._s(t.$t("rule.delete"))+"\n ")]),t._v(" "),s("el-button",{attrs:{plain:"",type:"success",size:"mini"},on:{click:function(e){return t.viewRunningStatus(i,n)}}},[t._v("\n "+t._s(t.$t("rule.viewStates"))+"\n ")])]}}])})],1),t._v(" "),s("resource-dialog",{ref:"resourceDialog",attrs:{visible:t.dialogVisible},on:{"update:visible":function(e){t.dialogVisible=e},confirm:t.loadData}}),t._v(" "),s("el-dialog",{attrs:{title:t.$t("rule.resource_details"),visible:t.viewDialogVisible},on:{"update:visible":function(e){t.viewDialogVisible=e}}},[s("div",{staticClass:"dialog-preview"},[s("div",{staticClass:"option-item"},[s("div",{staticClass:"option-title"},[t._v("\n "+t._s(t.$t("rule.id"))+"\n ")]),t._v(" "),s("div",{staticClass:"option-value"},[t._v(t._s(t.res.id))])]),t._v(" "),s("div",{staticClass:"option-item"},[s("div",{staticClass:"option-title"},[t._v("\n "+t._s(t.$t("rule.resource_type"))+"\n ")]),t._v(" "),s("div",{staticClass:"option-value"},[t._v(t._s(t.res.type))])]),t._v(" "),s("div",{staticClass:"option-item"},[s("div",{staticClass:"option-title"},[t._v("\n "+t._s(t.$t("rule.resource_des"))+"\n ")]),t._v(" "),s("div",{staticClass:"option-value"},[t._v(t._s(t.res.description))])]),t._v(" "),t.res.config&&Object.keys(t.res.config).length>0?s("div",{staticClass:"option-item"},[s("div",{staticClass:"option-title"},[t._v("\n "+t._s(t.$t("rule.config_info"))+"\n ")]),t._v(" "),s("div",{staticClass:"option-all"},t._l(Object.entries(t.res.config),function(e,i){return s("div",{key:i,staticClass:"option-item"},["object"!=typeof e[1]||Array.isArray(e[1])?[s("div",{staticClass:"option-title"},[t._v("\n "+t._s(e[0])+"\n ")]),t._v(" "),s("div",{staticClass:"option-value"},[t._v("\n "+t._s(e[1])+"\n ")])]:[s("div",{staticClass:"option-title"},[t._v("\n "+t._s(e[0])+"\n ")]),t._v(" "),s("div",{staticClass:"option-value"},[e[1]&&0!==Object.keys(e[1]).length?s("data-table",{staticStyle:{"margin-top":"0"},attrs:{disabled:""},model:{value:e[1],callback:function(s){t.$set(e,1,s)},expression:"item[1]"}}):s("span",[t._v("\n N/A\n ")])],1)]],2)}),0)]):t._e()]),t._v(" "),s("div",{attrs:{slot:"footer"},slot:"footer"},[s("el-button",{staticClass:"confirm-btn",attrs:{type:"success"},on:{click:function(e){t.viewDialogVisible=!1}}},[t._v("\n "+t._s(t.$t("rule.confirm"))+"\n ")])],1)])],1)},staticRenderFns:[]};var l=s("VU/8")(a,o,!1,function(t){s("DGQ0")},null,null);e.default=l.exports}}); \ No newline at end of file diff --git a/apps/emqx_dashboard/priv/www/static/js/16.6bfd6f3eb9216e73149c.js b/apps/emqx_dashboard/priv/www/static/js/16.6bfd6f3eb9216e73149c.js new file mode 100644 index 000000000..7ed55e988 --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/js/16.6bfd6f3eb9216e73149c.js @@ -0,0 +1 @@ +webpackJsonp([16],{RjBg:function(e,t,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=s("fZjL"),l=s.n(a),o=s("d7EF"),n=s.n(o),r=s("W3Iv"),i=s.n(r),c=s("Dd8w"),d=s.n(c),v=s("zL8q"),u=s("NYxO"),m={name:"overview-view",components:{"el-select":v.Select,"el-option":v.Option,"el-table":v.Table,"el-table-column":v.TableColumn,"el-row":v.Row,"el-col":v.Col},data:function(){return{nodeName:"",brokers:{},nodes:[],stats:[],timer:0,metrics:{packets:[],messages:[],bytes:[],client:[],session:[],delivery:[]}}},methods:d()({},Object(u.b)(["CURRENT_NODE"]),{init:function(){var e=this;this.$httpGet("/nodes").then(function(t){e.nodeName=e.$store.state.nodeName||t.data[0].node,e.nodes=t.data,e.CURRENT_NODE(e.nodeName),e.refreshInterval()}).catch(function(t){e.$message.error(t||e.$t("error.networkError")),setTimeout(function(){e.init()},2e4)})},refreshInterval:function(){this.loadData(),clearInterval(this.timer),this.timer=setInterval(this.loadData,1e4)},loadData:function(){var e=this;this.CURRENT_NODE(this.nodeName),this.$httpGet("/nodes").then(function(t){e.nodes=t.data.sort(function(t,s){return t.node===e.nodeName?-1:t.uptime>s.uptime?-1:1})}),this.$httpGet("/stats").then(function(t){var s=t.data;s.forEach(function(e){var t=d()({node:e.node},e.stats);i()(t).forEach(function(t){var s=n()(t,2),a=s[0],l=s[1],o=a.replace(/\./g,"_");e[o]=l,a.includes(".")&&delete e[a]})}),e.stats=s}),this.$httpGet("/brokers/"+this.nodeName).then(function(t){e.brokers=t.data}),this.$httpGet("/nodes/"+this.nodeName+"/metrics").then(function(t){e.metrics={packets:[],messages:[],bytes:[],client:[],session:[],delivery:[]};var s=d()({},t.data),a={packets:["received","sent","connect","connack","auth","disconnect.sent","disconnect.received","pingreq","pingresp","publish.received","publish.sent","puback.received","puback.sent","puback.missed","pubcomp.received","pubcomp.sent","pubcomp.missed","pubrec.received","pubrec.sent","pubrec.missed","pubrel.received","pubrel.sent","pubrel.missed","subscribe","suback","unsubscribe","unsuback"],messages:["received","sent","dropped","retained","qos0.received","qos0.sent","qos1.received","qos1.sent","qos2.received","qos2.expired","qos2.sent","qos2.dropped"],bytes:["received","sent"],client:["connected","authenticate","auth.anonymous","check_acl","subscribe","unsubscribe","disconnected"],session:["created","resumed","takeovered","discarded","terminated"],delivery:["dropped","dropped.no_local","dropped.too_large","dropped.qos0_msg","dropped.queue_full","dropped.expired"]};l()(a).forEach(function(l){a[l].forEach(function(a){var o=l+"."+a;delete s[o],void 0!==t.data[o]&&e.metrics[l].push({key:a,value:t.data[o]})})}),l()(s).forEach(function(t){var a=t.split(".")[0];e.metrics[a]&&void 0!==s[t]&&e.metrics[a].push({key:t.split(".").slice(1).join("."),value:s[t]})})})}}),created:function(){this.init()},beforeRouteLeave:function(e,t,s){clearInterval(this.timer),s()},beforeDestroy:function(){clearInterval(this.timer)}},p={render:function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("div",{staticClass:"overview-view"},[s("div",{staticClass:"page-title"},[e._v("\n "+e._s(e.$t("leftbar.overview"))+"\n "),s("el-select",{staticClass:"select-radius",attrs:{placeholder:e.$t("select.placeholder")},on:{change:e.loadData},model:{value:e.nodeName,callback:function(t){e.nodeName=t},expression:"nodeName"}},e._l(e.nodes,function(e){return s("el-option",{key:e.node,attrs:{label:e.node,value:e.node}})}),1)],1),e._v(" "),s("div",{staticClass:"card-box",staticStyle:{"margin-top":"54px"}},[s("div",{staticClass:"card-title"},[e._v(e._s(e.$t("overview.broker")))]),e._v(" "),s("el-row",{staticClass:"broker-card",attrs:{gutter:10}},[s("el-col",{attrs:{span:6}},[s("div",{staticClass:"card-item"},[s("div",{staticClass:"icon"},[s("i",{staticClass:"iconfont icon-systemname"})]),e._v(" "),s("div",{staticClass:"desc"},[s("h3",[e._v(e._s(e.$t("overview.systemName")))]),e._v(" "),s("p",[e._v(e._s(e.brokers.sysdescr))])])])]),e._v(" "),s("el-col",{attrs:{span:6}},[s("div",{staticClass:"card-item"},[s("div",{staticClass:"icon"},[s("i",{staticClass:"iconfont icon-version",staticStyle:{"font-weight":"600"}})]),e._v(" "),s("div",{staticClass:"desc"},[s("h3",[e._v(e._s(e.$t("overview.version")))]),e._v(" "),s("p",[e._v(e._s(e.brokers.version))])])])]),e._v(" "),s("el-col",{attrs:{span:6}},[s("div",{staticClass:"card-item"},[s("div",{staticClass:"icon"},[s("i",{staticClass:"iconfont icon-uptime"})]),e._v(" "),s("div",{staticClass:"desc"},[s("h3",[e._v(e._s(e.$t("overview.uptime")))]),e._v(" "),s("p",[e._v(e._s(e.brokers.uptime))])])])]),e._v(" "),s("el-col",{attrs:{span:6}},[s("div",{staticClass:"card-item"},[s("div",{staticClass:"icon",staticStyle:{"line-height":"46px"}},[s("i",{staticClass:"iconfont icon-Systemtime",staticStyle:{"font-size":"36px",top:"2px"}})]),e._v(" "),s("div",{staticClass:"desc"},[s("h3",[e._v(e._s(e.$t("overview.systemTime")))]),e._v(" "),s("p",[e._v(e._s(e.brokers.datetime))])])])])],1)],1),e._v(" "),s("div",{staticClass:"card-box"},[s("div",{staticClass:"card-title"},[e._v(e._s(e.$t("overview.nodes"))+"("+e._s(e.nodes.length)+")")]),e._v(" "),s("el-table",{attrs:{data:e.nodes,border:""}},[s("el-table-column",{attrs:{prop:"node","min-width":"200",label:e.$t("overview.name")}}),e._v(" "),s("el-table-column",{attrs:{prop:"otp_release","min-width":"200",label:e.$t("overview.erlangOTPRelease")}}),e._v(" "),s("el-table-column",{attrs:{label:e.$t("overview.erlangProcesses")}},[s("el-table-column",{attrs:{"min-width":"150",prop:"process",label:"(used/avaliable)"},scopedSlots:e._u([{key:"default",fn:function(t){return[e._v("\n "+e._s(t.row.process_used+" / "+t.row.process_available)+"\n ")]}}])})],1),e._v(" "),s("el-table-column",{attrs:{label:e.$t("overview.cpuInfo")}},[s("el-table-column",{attrs:{"min-width":"180",label:" (1load/5load/15load)"},scopedSlots:e._u([{key:"default",fn:function(t){return[e._v("\n "+e._s(t.row.load1+" / "+t.row.load5+" / "+t.row.load15)+"\n ")]}}])})],1),e._v(" "),s("el-table-column",{attrs:{"min-width":"200",label:e.$t("overview.memoryInfo")}},[s("el-table-column",{attrs:{"min-width":"180",label:" (used/total)"},scopedSlots:e._u([{key:"default",fn:function(t){return[e._v("\n "+e._s(t.row.memory_used+" / "+t.row.memory_total)+"\n ")]}}])})],1),e._v(" "),s("el-table-column",{attrs:{prop:"max_fds","min-width":"120",label:e.$t("overview.maxFds")}}),e._v(" "),s("el-table-column",{attrs:{"min-width":"120",label:e.$t("overview.status")},scopedSlots:e._u([{key:"default",fn:function(t){return[s("span",{class:["Running"===t.row.node_status?"running":"stopped","status"]},[e._v("\n "+e._s(t.row.node_status)+"\n ")])]}}])})],1)],1),e._v(" "),s("div",{staticClass:"card-box"},[s("div",{staticClass:"card-title"},[e._v(e._s(e.$t("overview.stats"))+"("+e._s(e.stats.length)+")")]),e._v(" "),s("el-table",{staticClass:"stats-table",attrs:{data:e.stats,border:""}},[s("el-table-column",{attrs:{prop:"node","min-width":"150",label:e.$t("overview.name")}}),e._v(" "),s("el-table-column",{attrs:{label:e.$t("overview.connectionsCount")}},[s("el-table-column",{attrs:{"min-width":"150",label:"(count/max)"},scopedSlots:e._u([{key:"default",fn:function(t){var s=t.row;return[e._v("\n "+e._s(s.connections_count)+" / "+e._s(s.connections_max)+"\n ")]}}])})],1),e._v(" "),s("el-table-column",{attrs:{label:e.$t("overview.topicsCount")}},[s("el-table-column",{attrs:{"min-width":"150",label:"(count/max)"},scopedSlots:e._u([{key:"default",fn:function(t){var s=t.row;return[e._v("\n "+e._s(s.topics_count)+" / "+e._s(s.topics_max)+"\n ")]}}])})],1),e._v(" "),s("el-table-column",{attrs:{label:e.$t("overview.retainedCount")}},[s("el-table-column",{attrs:{"min-width":"150",label:"(count/max)"},scopedSlots:e._u([{key:"default",fn:function(t){var s=t.row;return[e._v("\n "+e._s(s.retained_count)+" / "+e._s(s.retained_max)+"\n ")]}}])})],1),e._v(" "),s("el-table-column",{attrs:{label:e.$t("overview.sessionsCount")}},[s("el-table-column",{attrs:{"min-width":"150",label:"(count/max)"},scopedSlots:e._u([{key:"default",fn:function(t){var s=t.row;return[e._v("\n "+e._s(s.sessions_count)+" / "+e._s(s.sessions_max)+"\n ")]}}])})],1),e._v(" "),s("el-table-column",{attrs:{label:e.$t("overview.subscriptionsCount")}},[s("el-table-column",{attrs:{"min-width":"150",label:"(count/max)"},scopedSlots:e._u([{key:"default",fn:function(t){var s=t.row;return[e._v("\n "+e._s(s.subscriptions_count)+" / "+e._s(s.subscriptions_max)+"\n ")]}}])})],1),e._v(" "),s("el-table-column",{attrs:{label:e.$t("overview.subscriptionsSharedCount")}},[s("el-table-column",{attrs:{"min-width":"150",label:"(count/max)"},scopedSlots:e._u([{key:"default",fn:function(t){var s=t.row;return[e._v("\n "+e._s(s.subscriptions_shared_count)+" / "+e._s(s.subscriptions_shared_max)+"\n ")]}}])})],1)],1)],1),e._v(" "),s("div",{staticClass:"card-box"},[s("div",{staticClass:"card-title"},[e._v(e._s(e.$t("overview.metrics")))]),e._v(" "),s("el-row",{attrs:{gutter:20}},[s("el-col",{attrs:{span:8}},[s("el-table",{attrs:{data:e.metrics.client}},[s("el-table-column",{attrs:{"min-width":"200",prop:"key",label:e.$t("overview.client")}}),e._v(" "),s("el-table-column",{attrs:{sortable:"",prop:"value",label:""}})],1)],1),e._v(" "),s("el-col",{attrs:{span:8}},[s("el-table",{attrs:{data:e.metrics.delivery}},[s("el-table-column",{attrs:{"min-width":"160",prop:"key",label:e.$t("overview.delivery")}}),e._v(" "),s("el-table-column",{attrs:{sortable:"",prop:"value",label:""}})],1)],1),e._v(" "),s("el-col",{attrs:{span:8}},[s("el-table",{attrs:{data:e.metrics.session}},[s("el-table-column",{attrs:{"min-width":"200",prop:"key",label:e.$t("overview.session")}}),e._v(" "),s("el-table-column",{attrs:{sortable:"",prop:"value",label:""}})],1)],1)],1),e._v(" "),s("el-row",{attrs:{gutter:20}},[s("el-col",{attrs:{span:8}},[s("el-table",{attrs:{data:e.metrics.packets}},[s("el-table-column",{attrs:{"min-width":"200",prop:"key",label:e.$t("overview.packetsData")}}),e._v(" "),s("el-table-column",{attrs:{sortable:"",prop:"value",label:""}})],1)],1),e._v(" "),s("el-col",{attrs:{span:8}},[s("el-table",{attrs:{data:e.metrics.messages}},[s("el-table-column",{attrs:{"min-width":"200",prop:"key",label:e.$t("overview.messagesData")}}),e._v(" "),s("el-table-column",{attrs:{sortable:"",prop:"value",label:""}})],1)],1),e._v(" "),s("el-col",{attrs:{span:8}},[s("el-table",{attrs:{data:e.metrics.bytes}},[s("el-table-column",{attrs:{"min-width":"160",prop:"key",label:e.$t("overview.bytesData")}}),e._v(" "),s("el-table-column",{attrs:{sortable:"",prop:"value",label:""}})],1)],1)],1)],1)])},staticRenderFns:[]};var b=s("VU/8")(m,p,!1,function(e){s("Xk+3")},null,null);t.default=b.exports},"Xk+3":function(e,t){}}); \ No newline at end of file diff --git a/apps/emqx_dashboard/priv/www/static/js/17.1d56280c16e6e2b81cff.js b/apps/emqx_dashboard/priv/www/static/js/17.1d56280c16e6e2b81cff.js new file mode 100644 index 000000000..b73d3de57 --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/js/17.1d56280c16e6e2b81cff.js @@ -0,0 +1 @@ +webpackJsonp([17],{EsSr:function(t,e){},wkqA:function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r={render:function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"not-found"},[n("p",[t._v("404 Not Found")]),t._v(" "),n("router-link",{attrs:{to:"/"}},[t._v("Homepage")]),t._v(" "),n("router-link",{attrs:{to:"#"},on:{click:function(e){return t.$router.go(-2)}}},[t._v("Back up")])],1)},staticRenderFns:[]};var o=n("VU/8")({name:"NotFound"},r,!1,function(t){n("EsSr")},null,null);e.default=o.exports}}); \ No newline at end of file diff --git a/apps/emqx_dashboard/priv/www/static/js/18.a0c394cb4b55bee2fa82.js b/apps/emqx_dashboard/priv/www/static/js/18.a0c394cb4b55bee2fa82.js new file mode 100644 index 000000000..48c45305b --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/js/18.a0c394cb4b55bee2fa82.js @@ -0,0 +1 @@ +webpackJsonp([18],{"6xQH":function(e,r){},lmfZ:function(e,r,s){"use strict";Object.defineProperty(r,"__esModule",{value:!0});var o=s("Dd8w"),t=s.n(o),n=s("zL8q"),l=s("NYxO"),i={name:"login-view",components:{"el-col":n.Col,"el-row":n.Row,"el-card":n.Card,"el-form":n.Form,"el-form-item":n.FormItem,"el-input":n.Input,"el-checkbox":n.Checkbox,"el-button":n.Button},data:function(){return{remember:!1,user:{username:"",password:""},loginError:{username:"",password:""}}},methods:t()({},Object(l.b)(["USER_LOGIN"]),{login:function(){var e=this;return this.user.username?this.user.password?void this.$axios.post("/auth",this.user).then(function(){e.USER_LOGIN({user:e.user,remember:e.remember}),e.$router.push(e.$route.query.redirect||"/")}).catch(function(){e.loginError.username=e.$t("login.error"),e.user={username:"",password:""}}):(this.loginError.password=this.$t("login.passwordRequired"),!1):(this.loginError.username=this.$t("login.usernameRequired"),!1)}})},a={render:function(){var e=this,r=e.$createElement,s=e._self._c||r;return s("div",{staticClass:"login-view"},[s("el-card",[s("div",{attrs:{slot:"header"},slot:"header"},[e._v("\n "+e._s(e.$t("login.title"))+"\n ")]),e._v(" "),s("el-form",{staticClass:"el-form--public",attrs:{size:"medium","label-position":"top",model:e.user},nativeOn:{keyup:function(r){return!r.type.indexOf("key")&&e._k(r.keyCode,"enter",13,r.key,"Enter")?null:e.login(r)}}},[s("el-form-item",{attrs:{label:e.$t("login.username")}},[s("el-input",{class:{error:e.loginError.username},attrs:{placeholder:e.loginError.username},on:{focus:function(r){e.loginError.username=""}},model:{value:e.user.username,callback:function(r){e.$set(e.user,"username",r)},expression:"user.username"}})],1),e._v(" "),s("el-form-item",{attrs:{label:e.$t("login.password")}},[s("el-input",{class:{error:e.loginError.password},attrs:{type:"password",placeholder:e.loginError.password},on:{focus:function(r){e.loginError.password=""}},model:{value:e.user.password,callback:function(r){e.$set(e.user,"password",r)},expression:"user.password"}})],1)],1),e._v(" "),s("div",{staticClass:"login-footer"},[s("el-checkbox",{model:{value:e.remember,callback:function(r){e.remember=r},expression:"remember"}},[e._v("\n "+e._s(e.$t("login.remember"))+"\n ")]),e._v(" "),s("el-button",{staticClass:"confirm-btn",attrs:{type:"success",loading:e.$store.state.loading,disabled:e.$store.state.loading},on:{click:e.login}},[e._v(e._s(e.$t("login.loginButton"))+"\n ")])],1),e._v(" "),s("div",{staticClass:"clear-fix"})],1)],1)},staticRenderFns:[]};var u=s("VU/8")(i,a,!1,function(e){s("6xQH")},null,null);r.default=u.exports}}); \ No newline at end of file diff --git a/apps/emqx_dashboard/priv/www/static/js/19.060521bb4ba4f7a81ac0.js b/apps/emqx_dashboard/priv/www/static/js/19.060521bb4ba4f7a81ac0.js new file mode 100644 index 000000000..beb98a621 --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/js/19.060521bb4ba4f7a81ac0.js @@ -0,0 +1 @@ +webpackJsonp([19],{IHTQ:function(e,t){},uuOo:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=n("Dd8w"),a=n.n(s),o=n("NYxO"),l=n("zL8q"),r={name:"listeners-view",components:{"el-select":l.Select,"el-option":l.Option,"el-table":l.Table,"el-table-column":l.TableColumn},data:function(){return{nodeName:"",nodes:[],listeners:[]}},methods:a()({},Object(o.b)(["CURRENT_NODE"]),{loadData:function(){var e=this;this.$httpGet("/nodes").then(function(t){e.nodeName=e.$store.state.nodeName||t.data[0].node,e.nodes=t.data,e.loadListeners()}).catch(function(t){e.$message.error(t||e.$t("error.networkError"))})},loadListeners:function(){var e=this;this.CURRENT_NODE(this.nodeName),this.$httpGet("/nodes/"+this.nodeName+"/listeners").then(function(t){e.listeners=t.data}).catch(function(t){e.$message.error(t||e.$t("error.networkError"))})}}),created:function(){this.loadData()}},i={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"listeners-view"},[n("div",{staticClass:"page-title"},[e._v("\n "+e._s(e.$t("leftbar.listeners"))+"\n "),n("el-select",{staticClass:"select-radius",attrs:{placeholder:e.$t("select.placeholder"),disabled:e.$store.state.loading},on:{change:e.loadListeners},model:{value:e.nodeName,callback:function(t){e.nodeName=t},expression:"nodeName"}},e._l(e.nodes,function(e){return n("el-option",{key:e.node,attrs:{label:e.node,value:e.node}})}),1)],1),e._v(" "),n("el-table",{directives:[{name:"loading",rawName:"v-loading",value:e.$store.state.loading,expression:"$store.state.loading"}],attrs:{border:"",data:e.listeners}},[n("el-table-column",{attrs:{prop:"protocol",width:"240",label:e.$t("listeners.protocol")}}),e._v(" "),n("el-table-column",{attrs:{prop:"listen_on","min-width":"240",label:e.$t("listeners.listenOn")}}),e._v(" "),n("el-table-column",{attrs:{prop:"max_conns","min-width":"180",label:e.$t("listeners.maxConnections")}}),e._v(" "),n("el-table-column",{attrs:{prop:"current_conns","min-width":"120",label:e.$t("listeners.currentConnections")}})],1)],1)},staticRenderFns:[]};var c=n("VU/8")(r,i,!1,function(e){n("IHTQ")},null,null);t.default=c.exports}}); \ No newline at end of file diff --git a/apps/emqx_dashboard/priv/www/static/js/2.0aed9f93bfe094e9099c.js b/apps/emqx_dashboard/priv/www/static/js/2.0aed9f93bfe094e9099c.js new file mode 100644 index 000000000..9d6fb1400 --- /dev/null +++ b/apps/emqx_dashboard/priv/www/static/js/2.0aed9f93bfe094e9099c.js @@ -0,0 +1,8 @@ +webpackJsonp([2],{"+0Qw":function(e,t){},"+AxE":function(e,t){},"+HRN":function(e,t,n){"use strict";var i=n("kkc6").Buffer,r=n(2);e.exports=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.head=null,this.tail=null,this.length=0}return e.prototype.push=function(e){var t={data:e,next:null};this.length>0?this.tail.next=t:this.head=t,this.tail=t,++this.length},e.prototype.unshift=function(e){var t={data:e,next:this.head};0===this.length&&(this.tail=t),this.head=t,++this.length},e.prototype.shift=function(){if(0!==this.length){var e=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,e}},e.prototype.clear=function(){this.head=this.tail=null,this.length=0},e.prototype.join=function(e){if(0===this.length)return"";for(var t=this.head,n=""+t.data;t=t.next;)n+=e+t.data;return n},e.prototype.concat=function(e){if(0===this.length)return i.alloc(0);if(1===this.length)return this.head.data;for(var t,n,r,o=i.allocUnsafe(e>>>0),s=this.head,a=0;s;)t=s.data,n=o,r=a,t.copy(n,r),a+=s.data.length,s=s.next;return o},e}(),r&&r.inspect&&r.inspect.custom&&(e.exports.prototype[r.inspect.custom]=function(){var e=r.inspect({length:this.length});return this.constructor.name+" "+e})},"+Tn7":function(e,t){},"+e0g":function(e,t,n){"use strict";var i=n("3PYz"),r=n("lZ6o"),o=r.utils,s=o.assert,a=o.parseBytes,u=n("RzOE"),c=n("hkfz");function l(e){if(s("ed25519"===e,"only tested with ed25519 so far"),!(this instanceof l))return new l(e);e=r.curves[e].curve;this.curve=e,this.g=e.g,this.g.precompute(e.n.bitLength()+1),this.pointClass=e.point().constructor,this.encodingLength=Math.ceil(e.n.bitLength()/8),this.hash=i.sha512}e.exports=l,l.prototype.sign=function(e,t){e=a(e);var n=this.keyFromSecret(t),i=this.hashInt(n.messagePrefix(),e),r=this.g.mul(i),o=this.encodePoint(r),s=this.hashInt(o,n.pubBytes(),e).mul(n.priv()),u=i.add(s).umod(this.curve.n);return this.makeSignature({R:r,S:u,Rencoded:o})},l.prototype.verify=function(e,t,n){e=a(e),t=this.makeSignature(t);var i=this.keyFromPublic(n),r=this.hashInt(t.Rencoded(),i.pubBytes(),e),o=this.g.mul(t.S());return t.R().add(i.pub().mul(r)).eq(o)},l.prototype.hashInt=function(){for(var e=this.hash(),t=0;t16)throw new Error("unable to decrypt data");var n=-1;for(;++n16)return t=this.cache.slice(0,16),this.cache=this.cache.slice(16),t}else if(this.cache.length>=16)return t=this.cache.slice(0,16),this.cache=this.cache.slice(16),t;return null},d.prototype.flush=function(){if(this.cache.length)return this.cache},t.createDecipher=function(e,t){var n=o[e.toLowerCase()];if(!n)throw new TypeError("invalid suite type");var i=c(t,!1,n.key,n.iv);return h(e,i.key,i.iv)},t.createDecipheriv=h},"+jct":function(e,t,n){"use strict";n.d(t,"b",function(){return i}),n.d(t,"a",function(){return r}),t.c=function(e){var t=r;if(e&&e instanceof RegExp)if(e.global)t=e;else{var n="g";e.ignoreCase&&(n+="i"),e.multiline&&(n+="m"),e.unicode&&(n+="u"),t=new RegExp(e.source,n)}return t.lastIndex=0,t},t.d=function(e,t,n,i){t.lastIndex=0;var r=t.exec(n);if(!r)return null;var o=r[0].indexOf(" ")>=0?function(e,t,n,i){var r,o=e-1-i;t.lastIndex=0;for(;r=t.exec(n);){var s=r.index||0;if(s>o)return null;if(t.lastIndex>=o)return{word:r[0],startColumn:i+1+s,endColumn:i+1+t.lastIndex}}return null}(e,t,n,i):function(e,t,n,i){var r,o=e-1-i,s=n.lastIndexOf(" ",o-1)+1;t.lastIndex=s;for(;r=t.exec(n);){var a=r.index||0;if(a<=o&&t.lastIndex>=o)return{word:r[0],startColumn:i+1+a,endColumn:i+1+t.lastIndex}}return null}(e,t,n,i);return t.lastIndex=0,o};var i="`~!@#$%^&*()-=+[{]}\\|;:'\",.<>/?";var r=function(e){void 0===e&&(e="");for(var t="(-?\\d*\\.\\d\\w*)|([^",n=0,r=i;n=0||(t+="\\"+o)}return t+="\\s]+)",new RegExp(t,"g")}()},"+oh4":function(e,t,n){"use strict";var i;n.d(t,"a",function(){return i}),n.d(t,"b",function(){return r}),function(e){e[e.None=0]="None",e[e.Indent=1]="Indent",e[e.IndentOutdent=2]="IndentOutdent",e[e.Outdent=3]="Outdent"}(i||(i={}));var r=function(){function e(e){if(this.open=e.open,this.close=e.close,this._standardTokenMask=0,Array.isArray(e.notIn))for(var t=0,n=e.notIn.length;t200)return t;if("object"==typeof t){switch(t.$mid){case 1:return i.a.revive(t);case 2:return new RegExp(t.source,t.flags)}for(var r in t)Object.hasOwnProperty.call(t,r)&&(t[r]=e(t[r],n+1))}return t}(t,0)};var i=n("mrx5")},"/9db":function(e,t,n){"use strict";n.d(t,"a",function(){return i});var i,r=n("7g0X");!function(e){e.editorTextFocus=new r.d("editorTextFocus",!1),e.focus=new r.d("editorFocus",!1),e.textInputFocus=new r.d("textInputFocus",!1),e.readOnly=new r.d("editorReadonly",!1),e.writable=e.readOnly.toNegated(),e.hasNonEmptySelection=new r.d("editorHasSelection",!1),e.hasOnlyEmptySelection=e.hasNonEmptySelection.toNegated(),e.hasMultipleSelections=new r.d("editorHasMultipleSelections",!1),e.hasSingleSelection=e.hasMultipleSelections.toNegated(),e.tabMovesFocus=new r.d("editorTabMovesFocus",!1),e.tabDoesNotMoveFocus=e.tabMovesFocus.toNegated(),e.isInEmbeddedEditor=new r.d("isInEmbeddedEditor",!1),e.canUndo=new r.d("canUndo",!1),e.canRedo=new r.d("canRedo",!1),e.languageId=new r.d("editorLangId",""),e.hasCompletionItemProvider=new r.d("editorHasCompletionItemProvider",!1),e.hasCodeActionsProvider=new r.d("editorHasCodeActionsProvider",!1),e.hasCodeLensProvider=new r.d("editorHasCodeLensProvider",!1),e.hasDefinitionProvider=new r.d("editorHasDefinitionProvider",!1),e.hasDeclarationProvider=new r.d("editorHasDeclarationProvider",!1),e.hasImplementationProvider=new r.d("editorHasImplementationProvider",!1),e.hasTypeDefinitionProvider=new r.d("editorHasTypeDefinitionProvider",!1),e.hasHoverProvider=new r.d("editorHasHoverProvider",!1),e.hasDocumentHighlightProvider=new r.d("editorHasDocumentHighlightProvider",!1),e.hasDocumentSymbolProvider=new r.d("editorHasDocumentSymbolProvider",!1),e.hasReferenceProvider=new r.d("editorHasReferenceProvider",!1),e.hasRenameProvider=new r.d("editorHasRenameProvider",!1),e.hasSignatureHelpProvider=new r.d("editorHasSignatureHelpProvider",!1),e.hasDocumentFormattingProvider=new r.d("editorHasDocumentFormattingProvider",!1),e.hasDocumentSelectionFormattingProvider=new r.d("editorHasDocumentSelectionFormattingProvider",!1),e.hasMultipleDocumentFormattingProvider=new r.d("editorHasMultipleDocumentFormattingProvider",!1),e.hasMultipleDocumentSelectionFormattingProvider=new r.d("editorHasMultipleDocumentSelectionFormattingProvider",!1)}(i||(i={}))},"/MLu":function(e,t,n){e.exports=n("cSWu").PassThrough},"/bUF":function(module,exports){var indexOf=function(e,t){if(e.indexOf)return e.indexOf(t);for(var n=0;n=s&&e<=u||e>=a&&e<=c}function _(e,t,n,i){for(var r,o="",s=0,a=-1,u=0,c=0;c<=e.length;++c){if(c2){var h=o.lastIndexOf(n);-1===h?(o="",s=0):s=(o=o.slice(0,h)).length-1-o.lastIndexOf(n),a=c,u=0;continue}if(2===o.length||1===o.length){o="",s=0,a=c,u=0;continue}}t&&(o.length>0?o+=n+"..":o="..",s=2)}else o.length>0?o+=n+e.slice(a+1,c):o=e.slice(a+1,c),s=c-a-1;a=c,u=0}else r===l&&-1!==u?++u:u=-1}return o}function b(e,t){var n=t.dir||t.root,i=t.base||(t.name||"")+(t.ext||"");return n?n===t.root?n+i:n+e+i:i}var y={resolve:function(){for(var e=[],t=0;t=-1;s--){var a=void 0;if(s>=0?a=e[s]:n?void 0!==(a=r.b["="+n]||r.a())&&a.slice(0,3).toLowerCase()===n.toLowerCase()+"\\"||(a=n+"\\"):a=r.a(),p(a,"path"),0!==a.length){var u=a.length,c=0,l="",d=!1,h=a.charCodeAt(0);if(u>1)if(g(h))if(d=!0,g(a.charCodeAt(1))){for(var f=2,m=f;f2&&g(a.charCodeAt(2))&&(d=!0,c=3));else g(h)&&(c=1,d=!0);if(!(l.length>0&&n.length>0&&l.toLowerCase()!==n.toLowerCase())&&(0===n.length&&l.length>0&&(n=l),o||(i=a.slice(c)+"\\"+i,o=d),n.length>0&&o))break}}return i=_(i,!o,"\\",g),n+(o?"\\":"")+i||"."},normalize:function(e){p(e,"path");var t=e.length;if(0===t)return".";var n,i,r=0,o=!1,s=e.charCodeAt(0);if(t>1)if(g(s))if(o=!0,g(e.charCodeAt(1))){for(var a=2,u=a;a2&&g(e.charCodeAt(2))&&(o=!0,r=3));else if(g(s))return"\\";return 0!==(i=r0&&g(e.charCodeAt(t-1))&&(i+="\\"),void 0===n?o?i.length>0?"\\"+i:"\\":i.length>0?i:"":o?i.length>0?n+"\\"+i:n+"\\":i.length>0?n+i:n},isAbsolute:function(e){p(e,"path");var t=e.length;if(0===t)return!1;var n=e.charCodeAt(0);return!!g(n)||!!(v(n)&&t>2&&58===e.charCodeAt(1)&&g(e.charCodeAt(2)))},join:function(){for(var e,t,n=[],i=0;i0&&(void 0===e?e=t=o:e+="\\"+o)}if(void 0===e)return".";var s=!0,a=0;if("string"==typeof t&&g(t.charCodeAt(0))){++a;var u=t.length;u>1&&g(t.charCodeAt(1))&&(++a,u>2&&(g(t.charCodeAt(2))?++a:s=!1))}if(s){for(;a=2&&(e="\\"+e.slice(a))}return y.normalize(e)},relative:function(e,t){if(p(e,"from"),p(t,"to"),e===t)return"";var n=y.resolve(e),i=y.resolve(t);if(n===i)return"";if((e=n.toLowerCase())===(t=i.toLowerCase()))return"";for(var r=0;rr&&e.charCodeAt(o-1)===h;--o);for(var s=o-r,a=0;aa&&t.charCodeAt(u-1)===h;--u);for(var c=u-a,l=sl){if(t.charCodeAt(a+f)===h)return i.slice(a+f+1);if(2===f)return i.slice(a+f)}s>l&&(e.charCodeAt(r+f)===h?d=f:2===f&&(d=3));break}var g=e.charCodeAt(r+f);if(g!==t.charCodeAt(a+f))break;g===h&&(d=f)}if(f!==l&&-1===d)return i;var m="";for(-1===d&&(d=0),f=r+d+1;f<=o;++f)f!==o&&e.charCodeAt(f)!==h||(0===m.length?m+="..":m+="\\..");return m.length>0?m+i.slice(a+d,u):(a+=d,i.charCodeAt(a)===h&&++a,i.slice(a,u))},toNamespacedPath:function(e){if("string"!=typeof e)return e;if(0===e.length)return"";var t=y.resolve(e);if(t.length>=3)if(t.charCodeAt(0)===h){if(t.charCodeAt(1)===h){var n=t.charCodeAt(2);if(63!==n&&n!==l)return"\\\\?\\UNC\\"+t.slice(2)}}else if(v(t.charCodeAt(0))&&58===t.charCodeAt(1)&&t.charCodeAt(2)===h)return"\\\\?\\"+t;return e},dirname:function(e){p(e,"path");var t=e.length;if(0===t)return".";var n=-1,i=-1,r=!0,o=0,s=e.charCodeAt(0);if(t>1)if(g(s)){if(n=o=1,g(e.charCodeAt(1))){for(var a=2,u=a;a2&&g(e.charCodeAt(2))&&(n=o=3));else if(g(s))return e;for(var c=t-1;c>=o;--c)if(g(e.charCodeAt(c))){if(!r){i=c;break}}else r=!1;if(-1===i){if(-1===n)return".";i=n}return e.slice(0,i)},basename:function(e,t){void 0!==t&&p(t,"ext"),p(e,"path");var n,i=0,r=-1,o=!0;e.length>=2&&(v(e.charCodeAt(0))&&58===e.charCodeAt(1)&&(i=2));if(void 0!==t&&t.length>0&&t.length<=e.length){if(t.length===e.length&&t===e)return"";var s=t.length-1,a=-1;for(n=e.length-1;n>=i;--n){var u=e.charCodeAt(n);if(g(u)){if(!o){i=n+1;break}}else-1===a&&(o=!1,a=n+1),s>=0&&(u===t.charCodeAt(s)?-1==--s&&(r=n):(s=-1,r=a))}return i===r?r=a:-1===r&&(r=e.length),e.slice(i,r)}for(n=e.length-1;n>=i;--n)if(g(e.charCodeAt(n))){if(!o){i=n+1;break}}else-1===r&&(o=!1,r=n+1);return-1===r?"":e.slice(i,r)},extname:function(e){p(e,"path");var t=0,n=-1,i=0,r=-1,o=!0,s=0;e.length>=2&&58===e.charCodeAt(1)&&v(e.charCodeAt(0))&&(t=i=2);for(var a=e.length-1;a>=t;--a){var u=e.charCodeAt(a);if(g(u)){if(!o){i=a+1;break}}else-1===r&&(o=!1,r=a+1),u===l?-1===n?n=a:1!==s&&(s=1):-1!==n&&(s=-1)}return-1===n||-1===r||0===s||1===s&&n===r-1&&n===i+1?"":e.slice(n,r)},format:function(e){if(null===e||"object"!=typeof e)throw new f("pathObject","Object",e);return b("\\",e)},parse:function(e){p(e,"path");var t={root:"",dir:"",base:"",ext:"",name:""};if(0===e.length)return t;var n=e.length,i=0,r=e.charCodeAt(0);if(n>1){if(g(r)){if(i=1,g(e.charCodeAt(1))){for(var o=2,s=o;o2))return t.root=t.dir=e,t;if(g(e.charCodeAt(2))){if(3===n)return t.root=t.dir=e,t;i=3}}}else if(g(r))return t.root=t.dir=e,t;i>0&&(t.root=e.slice(0,i));for(var a=-1,u=i,c=-1,d=!0,h=e.length-1,f=0;h>=i;--h)if(g(r=e.charCodeAt(h))){if(!d){u=h+1;break}}else-1===c&&(d=!1,c=h+1),r===l?-1===a?a=h:1!==f&&(f=1):-1!==a&&(f=-1);return-1===a||-1===c||0===f||1===f&&a===c-1&&a===u+1?-1!==c&&(t.base=t.name=e.slice(u,c)):(t.name=e.slice(u,a),t.base=e.slice(u,c),t.ext=e.slice(a,c)),t.dir=u>0&&u!==i?e.slice(0,u-1):t.root,t},sep:"\\",delimiter:";",win32:null,posix:null},w={resolve:function(){for(var e=[],t=0;t=-1&&!i;o--){var s=void 0;p(s=o>=0?e[o]:r.a(),"path"),0!==s.length&&(n=s+"/"+n,i=s.charCodeAt(0)===d)}return n=_(n,!i,"/",m),i?n.length>0?"/"+n:"/":n.length>0?n:"."},normalize:function(e){if(p(e,"path"),0===e.length)return".";var t=e.charCodeAt(0)===d,n=e.charCodeAt(e.length-1)===d;return 0!==(e=_(e,!t,"/",m)).length||t||(e="."),e.length>0&&n&&(e+="/"),t?"/"+e:e},isAbsolute:function(e){return p(e,"path"),e.length>0&&e.charCodeAt(0)===d},join:function(){for(var e,t=[],n=0;n0&&(void 0===e?e=r:e+="/"+r)}return void 0===e?".":w.normalize(e)},relative:function(e,t){if(p(e,"from"),p(t,"to"),e===t)return"";if((e=w.resolve(e))===(t=w.resolve(t)))return"";for(var n=1;na){if(t.charCodeAt(o+c)===d)return t.slice(o+c+1);if(0===c)return t.slice(o+c)}else r>a&&(e.charCodeAt(n+c)===d?u=c:0===c&&(u=0));break}var l=e.charCodeAt(n+c);if(l!==t.charCodeAt(o+c))break;l===d&&(u=c)}var h="";for(c=n+u+1;c<=i;++c)c!==i&&e.charCodeAt(c)!==d||(0===h.length?h+="..":h+="/..");return h.length>0?h+t.slice(o+u):(o+=u,t.charCodeAt(o)===d&&++o,t.slice(o))},toNamespacedPath:function(e){return e},dirname:function(e){if(p(e,"path"),0===e.length)return".";for(var t=e.charCodeAt(0)===d,n=-1,i=!0,r=e.length-1;r>=1;--r)if(e.charCodeAt(r)===d){if(!i){n=r;break}}else i=!1;return-1===n?t?"/":".":t&&1===n?"//":e.slice(0,n)},basename:function(e,t){void 0!==t&&p(t,"ext"),p(e,"path");var n,i=0,r=-1,o=!0;if(void 0!==t&&t.length>0&&t.length<=e.length){if(t.length===e.length&&t===e)return"";var s=t.length-1,a=-1;for(n=e.length-1;n>=0;--n){var u=e.charCodeAt(n);if(u===d){if(!o){i=n+1;break}}else-1===a&&(o=!1,a=n+1),s>=0&&(u===t.charCodeAt(s)?-1==--s&&(r=n):(s=-1,r=a))}return i===r?r=a:-1===r&&(r=e.length),e.slice(i,r)}for(n=e.length-1;n>=0;--n)if(e.charCodeAt(n)===d){if(!o){i=n+1;break}}else-1===r&&(o=!1,r=n+1);return-1===r?"":e.slice(i,r)},extname:function(e){p(e,"path");for(var t=-1,n=0,i=-1,r=!0,o=0,s=e.length-1;s>=0;--s){var a=e.charCodeAt(s);if(a!==d)-1===i&&(r=!1,i=s+1),a===l?-1===t?t=s:1!==o&&(o=1):-1!==t&&(o=-1);else if(!r){n=s+1;break}}return-1===t||-1===i||0===o||1===o&&t===i-1&&t===n+1?"":e.slice(t,i)},format:function(e){if(null===e||"object"!=typeof e)throw new f("pathObject","Object",e);return b("/",e)},parse:function(e){p(e,"path");var t={root:"",dir:"",base:"",ext:"",name:""};if(0===e.length)return t;var n,i=e.charCodeAt(0)===d;i?(t.root="/",n=1):n=0;for(var r=-1,o=0,s=-1,a=!0,u=e.length-1,c=0;u>=n;--u){var h=e.charCodeAt(u);if(h!==d)-1===s&&(a=!1,s=u+1),h===l?-1===r?r=u:1!==c&&(c=1):-1!==r&&(c=-1);else if(!a){o=u+1;break}}return-1===r||-1===s||0===c||1===c&&r===s-1&&r===o+1?-1!==s&&(t.base=t.name=0===o&&i?e.slice(1,s):e.slice(o,s)):(0===o&&i?(t.name=e.slice(1,r),t.base=e.slice(1,s)):(t.name=e.slice(o,r),t.base=e.slice(o,s)),t.ext=e.slice(r,s)),o>0?t.dir=e.slice(0,o-1):i&&(t.dir="/"),t},sep:"/",delimiter:":",win32:null,posix:null};w.win32=y.win32=y,w.posix=y.posix=w;var C="win32"===r.c?y.normalize:w.normalize,S="win32"===r.c?y.join:w.join,x="win32"===r.c?y.relative:w.relative,L="win32"===r.c?y.dirname:w.dirname,O="win32"===r.c?y.basename:w.basename,k="win32"===r.c?y.extname:w.extname,N="win32"===r.c?y.sep:w.sep},"/vd3":function(e,t,n){t.pbkdf2=n("GUE9"),t.pbkdf2Sync=n("Zq1s")},"/y0r":function(e,t,n){var i=n("BEbT"),r=n("X3l8").Buffer,o=n("z+8S"),s=n("LC74"),a=n("UPHp"),u=n("H2Pp"),c=n("4sPJ");function l(e,t,n,s){o.call(this);var u=r.alloc(4,0);this._cipher=new i.AES(t);var l=this._cipher.encryptBlock(u);this._ghash=new a(l),n=function(e,t,n){if(12===t.length)return e._finID=r.concat([t,r.from([0,0,0,1])]),r.concat([t,r.from([0,0,0,2])]);var i=new a(n),o=t.length,s=o%16;i.update(t),s&&(s=16-s,i.update(r.alloc(s,0))),i.update(r.alloc(8,0));var u=8*o,l=r.alloc(8);l.writeUIntBE(u,0,8),i.update(l),e._finID=i.state;var d=r.from(e._finID);return c(d),d}(this,n,l),this._prev=r.from(n),this._cache=r.allocUnsafe(0),this._secCache=r.allocUnsafe(0),this._decrypt=s,this._alen=0,this._len=0,this._mode=e,this._authTag=null,this._called=!1}s(l,o),l.prototype._update=function(e){if(!this._called&&this._alen){var t=16-this._alen%16;t<16&&(t=r.alloc(t,0),this._ghash.update(t))}this._called=!0;var n=this._mode.encrypt(this,e);return this._decrypt?this._ghash.update(e):this._ghash.update(n),this._len+=e.length,n},l.prototype._final=function(){if(this._decrypt&&!this._authTag)throw new Error("Unsupported state or unable to authenticate data");var e=u(this._ghash.final(8*this._alen,8*this._len),this._cipher.encryptBlock(this._finID));if(this._decrypt&&function(e,t){var n=0;e.length!==t.length&&n++;for(var i=Math.min(e.length,t.length),r=0;r0&&r[r.length-1])&&(6===o[0]||2===o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},k=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}};!function(e){e.Hidden=new(function(){return function(){this.type=0}}());var t=function(){return function(e,t,n){this.actions=e,this.editorPosition=t,this.widgetPosition=n,this.type=1}}();e.Showing=t}(r||(r={}));var N,E=function(e){function t(t,n,i){var o=e.call(this)||this;return o._editor=t,o._quickFixActionId=n,o._keybindingService=i,o._onClick=o._register(new C.a),o.onClick=o._onClick.event,o._state=r.Hidden,o._domNode=document.createElement("div"),o._domNode.className="lightbulb-glyph",o._editor.addContentWidget(o),o._register(o._editor.onDidChangeModelContent(function(e){var t=o._editor.getModel();(1!==o._state.type||!t||o._state.editorPosition.lineNumber>=t.getLineCount())&&o.hide()})),o._register(p.k(o._domNode,"mousedown",function(e){if(1===o._state.type){o._editor.focus(),e.preventDefault();var t=p.x(o._domNode),n=t.top,i=t.height,r=o._editor.getConfiguration().lineHeight,s=Math.floor(r/3);null!==o._state.widgetPosition.position&&o._state.widgetPosition.position.lineNumber2&&i._editor.getTopForLineNumber(e)===i._editor.getTopForLineNumber(e-1)},f=s;if(!(o.fontInfo.spaceWidth*d>22))if(s>1&&!h(s-1))f-=1;else if(h(s+1)){if(a*o.fontInfo.spaceWidth<22)return this.hide()}else f+=1;this._state=new r.Showing(e,n,{position:{lineNumber:f,column:1},preference:t._posPref}),p.R(this._domNode,"autofixable",e.hasAutoFix),this._editor.layoutContentWidget(this)},Object.defineProperty(t.prototype,"title",{set:function(e){this._domNode.title=e},enumerable:!0,configurable:!0}),t.prototype.hide=function(){this._state=r.Hidden,this._editor.layoutContentWidget(this)},t.prototype._updateLightBulbTitle=function(){var e,t=this._keybindingService.lookupKeybinding(this._quickFixActionId);e=t?x.a("quickFixWithKb","Show Fixes ({0})",t.getLabel()):x.a("quickFix","Show Fixes"),this.title=e},t._posPref=[0],t=O([k(2,f.a)],t)}(o.a),I=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),D=this&&this.__decorate||function(e,t,n,i){var r,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,i);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},M=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},T=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(r,o){function s(e){try{u(i.next(e))}catch(e){o(e)}}function a(e){try{u(i.throw(e))}catch(e){o(e)}}function u(e){e.done?r(e.value):new n(function(t){t(e.value)}).then(s,a)}u((i=i.apply(e,t||[])).next())})},P=this&&this.__generator||function(e,t){var n,i,r,o,s={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,i&&(r=2&o[0]?i.return:o[0]?i.throw||((r=i.return)&&r.call(i),0):i.next)&&!(r=r.call(i,o[1])).done)return r;switch(i=0,r&&(o=[2&o[0],r.value]),o[0]){case 0:case 1:r=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,i=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(r=(r=s.trys).length>0&&r[r.length-1])&&(6===o[0]||2===o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]0))return[3,9];if(1!==e.trigger.autoApply&&(0!==e.trigger.autoApply||1!==t.actions.length))return[3,9];i.label=5;case 5:return i.trys.push([5,,7,8]),[4,this.delegate.applyCodeAction(t.actions[0],!1)];case 6:return i.sent(),[3,8];case 7:return t.dispose(),[7];case 8:return[2];case 9:return this._activeCodeActions.value=t,this._codeActionWidget.show(t,e.position),[3,11];case 10:this._codeActionWidget.isVisible?t.dispose():this._activeCodeActions.value=t,i.label=11;case 11:return[2]}})})},t.prototype.showCodeActionList=function(e,t){return T(this,void 0,void 0,function(){return P(this,function(n){return this._codeActionWidget.show(e,t),[2]})})},t.prototype._handleLightBulbSelect=function(e){this._codeActionWidget.show(e.actions,e)},t=D([M(3,h.a),M(4,f.a)],t)}(o.a),R=n("ItKl"),F=n("7g0X"),j=n("OHx0"),W=n("DBt1"),B=n("odeJ"),V=n("vTy2"),H=n("PCC9"),z=n("OBuU"),U=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),K=new F.d("supportedCodeAction",""),q=function(e){function t(t,n,i,r){void 0===r&&(r=250);var o=e.call(this)||this;return o._editor=t,o._markerService=n,o._signalChange=i,o._delay=r,o._autoTriggerTimer=o._register(new B.e),o._register(o._markerService.onMarkerChanged(function(e){return o._onMarkerChanges(e)})),o._register(o._editor.onDidChangeCursorPosition(function(){return o._onCursorChange()})),o}return U(t,e),t.prototype.trigger=function(e){var t=this._getRangeOfSelectionUnlessWhitespaceEnclosed(e);return this._createEventAndSignalChange(e,t)},t.prototype._onMarkerChanges=function(e){var t=this,n=this._editor.getModel();n&&e.some(function(e){return e.toString()===n.uri.toString()})&&this._autoTriggerTimer.cancelAndSet(function(){t.trigger({type:"auto"})},this._delay)},t.prototype._onCursorChange=function(){var e=this;this._autoTriggerTimer.cancelAndSet(function(){e.trigger({type:"auto"})},this._delay)},t.prototype._getRangeOfMarker=function(e){var t=this._editor.getModel();if(t)for(var n=0,i=this._markerService.read({resource:t.uri});n=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},$=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},J=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(r,o){function s(e){try{u(i.next(e))}catch(e){o(e)}}function a(e){try{u(i.throw(e))}catch(e){o(e)}}function u(e){e.done?r(e.value):new n(function(t){t(e.value)}).then(s,a)}u((i=i.apply(e,t||[])).next())})},Q=this&&this.__generator||function(e,t){var n,i,r,o,s={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,i&&(r=2&o[0]?i.return:o[0]?i.throw||((r=i.return)&&r.call(i),0):i.next)&&!(r=r.call(i,o[1])).done)return r;switch(i=0,r&&(o=[2&o[0],r.value]),o[0]){case 0:case 1:r=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,i=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(r=(r=s.trys).length>0&&r[r.length-1])&&(6===o[0]||2===o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]=0;t--)this.editOperations[t]={operations:e.applyEdits(this.editOperations[t].operations)}},e.prototype.redo=function(e){for(var t=0;t0){var e=this.past.pop();try{e.undo(this.model)}catch(e){return Object(i.e)(e),this.clear(),null}return this.future.push(e),{selections:e.beforeCursorState,recordedVersionId:e.beforeVersionId}}return null},e.prototype.canUndo=function(){return this.past.length>0||null!==this.currentOpenStackElement},e.prototype.redo=function(){if(this.future.length>0){var e=this.future.pop();try{e.redo(this.model)}catch(e){return Object(i.e)(e),this.clear(),null}return this.past.push(e),{selections:e.afterCursorState,recordedVersionId:e.afterVersionId}}return null},e.prototype.canRedo=function(){return this.future.length>0},e}(),v=function(){return function(){this.spacesDiff=0,this.looksLikeAlignment=!1}}();function _(e,t,n,i,r){var o;for(r.spacesDiff=0,r.looksLikeAlignment=!1,o=0;o0&&a>0||c>0&&l>0)){var d=Math.abs(a-l),h=Math.abs(s-c);if(0===d)return r.spacesDiff=h,void(h>0&&0<=c-1&&c-10?r++:m>1&&o++,_(s,a,h,g,c),!c.looksLikeAlignment||n&&t===c.spacesDiff)){var S=c.spacesDiff;S<=8&&u[S]++,s=h,a=g}}var x=n;r!==o&&(x=rO&&(O=t,L=e)}),4===L&&u[4]>0&&u[2]>0&&u[2]>=u[4]/2&&(L=2)}return{insertSpaces:x,tabSize:L}}function y(e){return(1&e.metadata)>>>0}function w(e,t){e.metadata=254&e.metadata|t<<0}function C(e){return(2&e.metadata)>>>1==1}function S(e,t){e.metadata=253&e.metadata|(t?1:0)<<1}function x(e){return(4&e.metadata)>>>2==1}function L(e,t){e.metadata=251&e.metadata|(t?1:0)<<2}function O(e){return(8&e.metadata)>>>3==1}function k(e,t){e.metadata=247&e.metadata|(t?1:0)<<3}function N(e,t){e.metadata=207&e.metadata|t<<4}function E(e,t){e.metadata=191&e.metadata|(t?1:0)<<6}var I=function(){function e(e,t,n){this.metadata=0,this.parent=this,this.left=this,this.right=this,w(this,1),this.start=t,this.end=n,this.delta=0,this.maxEnd=n,this.id=e,this.ownerId=0,this.options=null,L(this,!1),N(this,1),k(this,!1),E(this,!1),this.cachedVersionId=0,this.cachedAbsoluteStart=t,this.cachedAbsoluteEnd=n,this.range=null,S(this,!1)}return e.prototype.reset=function(e,t,n,i){this.start=t,this.end=n,this.maxEnd=n,this.cachedVersionId=e,this.cachedAbsoluteStart=t,this.cachedAbsoluteEnd=n,this.range=i},e.prototype.setOptions=function(e){this.options=e;var t=this.options.className;L(this,"squiggly-error"===t||"squiggly-warning"===t||"squiggly-info"===t),N(this,this.options.stickiness),k(this,!(!this.options.overviewRuler||!this.options.overviewRuler.color)),E(this,this.options.collapseOnReplaceEdit)},e.prototype.setCachedOffsets=function(e,t,n){this.cachedVersionId!==n&&(this.range=null),this.cachedVersionId=n,this.cachedAbsoluteStart=e,this.cachedAbsoluteEnd=t},e.prototype.detach=function(){this.parent=null,this.left=null,this.right=null},e}(),D=new I(null,0,0);D.parent=D,D.left=D,D.right=D,w(D,0);var M=function(){function e(){this.root=D,this.requestNormalizeDelta=!1}return e.prototype.intervalSearch=function(e,t,n,i,r){return this.root===D?[]:function(e,t,n,i,r,o){var s=e.root,a=0,u=0,c=0,l=[],d=0;for(;s!==D;)if(C(s))S(s.left,!1),S(s.right,!1),s===s.parent.right&&(a-=s.parent.delta),s=s.parent;else{if(!C(s.left)){if(a+s.maxEndn)S(s,!0);else{if((c=a+s.end)>=t){s.setCachedOffsets(u,c,o);var h=!0;i&&s.ownerId&&s.ownerId!==i&&(h=!1),r&&x(s)&&(h=!1),h&&(l[d++]=s)}S(s,!0),s.right===D||C(s.right)||(a+=s.delta,s=s.right)}}return S(e.root,!1),l}(this,e,t,n,i,r)},e.prototype.search=function(e,t,n){return this.root===D?[]:function(e,t,n,i){var r=e.root,o=0,s=0,a=0,u=[],c=0;for(;r!==D;)if(C(r))S(r.left,!1),S(r.right,!1),r===r.parent.right&&(o-=r.parent.delta),r=r.parent;else if(r.left===D||C(r.left)){s=o+r.start,a=o+r.end,r.setCachedOffsets(s,a,i);var l=!0;t&&r.ownerId&&r.ownerId!==t&&(l=!1),n&&x(r)&&(l=!1),l&&(u[c++]=r),S(r,!0),r.right===D||C(r.right)||(o+=r.delta,r=r.right)}else r=r.left;return S(e.root,!1),u}(this,e,t,n)},e.prototype.collectNodesFromOwner=function(e){return function(e,t){var n=e.root,i=[],r=0;for(;n!==D;)C(n)?(S(n.left,!1),S(n.right,!1),n=n.parent):n.left===D||C(n.left)?(n.ownerId===t&&(i[r++]=n),S(n,!0),n.right===D||C(n.right)||(n=n.right)):n=n.left;return S(e.root,!1),i}(this,e)},e.prototype.collectNodesPostOrder=function(){return function(e){var t=e.root,n=[],i=0;for(;t!==D;)C(t)?(S(t.left,!1),S(t.right,!1),t=t.parent):t.left===D||C(t.left)?t.right===D||C(t.right)?(n[i++]=t,S(t,!0)):t=t.right:t=t.left;return S(e.root,!1),n}(this)},e.prototype.insert=function(e){A(this,e),this._normalizeDeltaIfNecessary()},e.prototype.delete=function(e){R(this,e),this._normalizeDeltaIfNecessary()},e.prototype.resolveNode=function(e,t){for(var n=e,i=0;e!==this.root;)e===e.parent.right&&(i+=e.parent.delta),e=e.parent;var r=n.start+i,o=n.end+i;n.setCachedOffsets(r,o,t)},e.prototype.acceptReplace=function(e,t,n,i){for(var r=function(e,t,n){var i=e.root,r=0,o=0,s=0,a=[],u=0;for(;i!==D;)if(C(i))S(i.left,!1),S(i.right,!1),i===i.parent.right&&(r-=i.parent.delta),i=i.parent;else{if(!C(i.left)){if(r+i.maxEndn?S(i,!0):((s=r+i.end)>=t&&(i.setCachedOffsets(o,s,0),a[u++]=i),S(i,!0),i.right===D||C(i.right)||(r+=i.delta,i=i.right))}return S(e.root,!1),a}(this,e,e+t),o=0,s=r.length;on?(r.start+=s,r.end+=s,r.delta+=s,(r.delta<-1073741824||r.delta>1073741824)&&(e.requestNormalizeDelta=!0),S(r,!0)):(S(r,!0),r.right===D||C(r.right)||(o+=r.delta,r=r.right))}S(e.root,!1)}(this,e,e+t,n),this._normalizeDeltaIfNecessary();for(o=0,s=r.length;on)&&(1!==i&&(2===i||t))}function P(e,t,n,i,r){var o=function(e){return(48&e.metadata)>>>4}(e),s=0===o||2===o,a=1===o||2===o,u=n-t,c=i,l=Math.min(u,c),d=e.start,h=!1,f=e.end,p=!1;t<=d&&f<=n&&function(e){return(64&e.metadata)>>>6==1}(e)&&(e.start=t,h=!0,e.end=t,p=!0);var g=r?1:u>0?2:0;if(!h&&T(d,s,t,g)&&(h=!0),!p&&T(f,a,t,g)&&(p=!0),l>0&&!r){g=u>c?2:0;!h&&T(d,s,t+l,g)&&(h=!0),!p&&T(f,a,t+l,g)&&(p=!0)}g=r?1:0;!h&&T(d,s,n,g)&&(e.start=t+c,h=!0),!p&&T(f,a,n,g)&&(e.end=t+c,p=!0);var m=c-u;h||(e.start=Math.max(0,d+m)),p||(e.end=Math.max(0,f+m)),e.start>e.end&&(e.end=e.start)}function A(e,t){if(e.root===D)return t.parent=D,t.left=D,t.right=D,w(t,0),e.root=t,e.root;!function(e,t){var n=0,i=e.root,r=t.start,o=t.end;for(;;){var s=z(r,o,i.start+n,i.end+n);if(s<0){if(i.left===D){t.start-=n,t.end-=n,t.maxEnd-=n,i.left=t;break}i=i.left}else{if(i.right===D){t.start-=n+i.delta,t.end-=n+i.delta,t.maxEnd-=n+i.delta,i.right=t;break}n+=i.delta,i=i.right}}t.parent=i,t.left=D,t.right=D,w(t,1)}(e,t),H(t.parent);for(var n=t;n!==e.root&&1===y(n.parent);){var i;if(n.parent===n.parent.parent.left)1===y(i=n.parent.parent.right)?(w(n.parent,0),w(i,0),w(n.parent.parent,1),n=n.parent.parent):(n===n.parent.right&&j(e,n=n.parent),w(n.parent,0),w(n.parent.parent,1),W(e,n.parent.parent));else 1===y(i=n.parent.parent.left)?(w(n.parent,0),w(i,0),w(n.parent.parent,1),n=n.parent.parent):(n===n.parent.left&&W(e,n=n.parent),w(n.parent,0),w(n.parent.parent,1),j(e,n.parent.parent))}return w(e.root,0),t}function R(e,t){var n,i;if(t.left===D?(i=t,(n=t.right).delta+=t.delta,(n.delta<-1073741824||n.delta>1073741824)&&(e.requestNormalizeDelta=!0),n.start+=t.delta,n.end+=t.delta):t.right===D?(n=t.left,i=t):((n=(i=function(e){for(;e.left!==D;)e=e.left;return e}(t.right)).right).start+=i.delta,n.end+=i.delta,n.delta+=i.delta,(n.delta<-1073741824||n.delta>1073741824)&&(e.requestNormalizeDelta=!0),i.start+=t.delta,i.end+=t.delta,i.delta=t.delta,(i.delta<-1073741824||i.delta>1073741824)&&(e.requestNormalizeDelta=!0)),i===e.root)return e.root=n,w(n,0),t.detach(),F(),V(n),void(e.root.parent=D);var r,o=1===y(i);if(i===i.parent.left?i.parent.left=n:i.parent.right=n,i===t?n.parent=i.parent:(i.parent===t?n.parent=i:n.parent=i.parent,i.left=t.left,i.right=t.right,i.parent=t.parent,w(i,y(t)),t===e.root?e.root=i:t===t.parent.left?t.parent.left=i:t.parent.right=i,i.left!==D&&(i.left.parent=i),i.right!==D&&(i.right.parent=i)),t.detach(),o)return H(n.parent),i!==t&&(H(i),H(i.parent)),void F();for(H(n),H(n.parent),i!==t&&(H(i),H(i.parent));n!==e.root&&0===y(n);)n===n.parent.left?(1===y(r=n.parent.right)&&(w(r,0),w(n.parent,1),j(e,n.parent),r=n.parent.right),0===y(r.left)&&0===y(r.right)?(w(r,1),n=n.parent):(0===y(r.right)&&(w(r.left,0),w(r,1),W(e,r),r=n.parent.right),w(r,y(n.parent)),w(n.parent,0),w(r.right,0),j(e,n.parent),n=e.root)):(1===y(r=n.parent.left)&&(w(r,0),w(n.parent,1),W(e,n.parent),r=n.parent.left),0===y(r.left)&&0===y(r.right)?(w(r,1),n=n.parent):(0===y(r.left)&&(w(r.right,0),w(r,1),j(e,r),r=n.parent.left),w(r,y(n.parent)),w(n.parent,0),w(r.left,0),W(e,n.parent),n=e.root));w(n,0),F()}function F(){D.parent=D,D.delta=0,D.start=0,D.end=0}function j(e,t){var n=t.right;n.delta+=t.delta,(n.delta<-1073741824||n.delta>1073741824)&&(e.requestNormalizeDelta=!0),n.start+=t.delta,n.end+=t.delta,t.right=n.left,n.left!==D&&(n.left.parent=t),n.parent=t.parent,t.parent===D?e.root=n:t===t.parent.left?t.parent.left=n:t.parent.right=n,n.left=t,t.parent=n,V(t),V(n)}function W(e,t){var n=t.left;t.delta-=n.delta,(t.delta<-1073741824||t.delta>1073741824)&&(e.requestNormalizeDelta=!0),t.start-=n.delta,t.end-=n.delta,t.left=n.right,n.right!==D&&(n.right.parent=t),n.parent=t.parent,t.parent===D?e.root=n:t===t.parent.right?t.parent.right=n:t.parent.left=n,n.right=t,t.parent=n,V(t),V(n)}function B(e){var t=e.end;if(e.left!==D){var n=e.left.maxEnd;n>t&&(t=n)}if(e.right!==D){var i=e.right.maxEnd+e.delta;i>t&&(t=i)}return t}function V(e){e.maxEnd=B(e)}function H(e){for(;e!==D;){var t=B(e);if(e.maxEnd===t)return;e.maxEnd=t,e=e.parent}}function z(e,t,n,i){return e===n?t-i:e-n}var U=function(){function e(e,t){this.piece=e,this.color=t,this.size_left=0,this.lf_left=0,this.parent=this,this.left=this,this.right=this}return e.prototype.next=function(){if(this.right!==K)return q(this.right);for(var e=this;e.parent!==K&&e.parent.left!==e;)e=e.parent;return e.parent===K?K:e.parent},e.prototype.prev=function(){if(this.left!==K)return G(this.left);for(var e=this;e.parent!==K&&e.parent.right!==e;)e=e.parent;return e.parent===K?K:e.parent},e.prototype.detach=function(){this.parent=null,this.left=null,this.right=null},e}(),K=new U(null,0);function q(e){for(;e.left!==K;)e=e.left;return e}function G(e){for(;e.right!==K;)e=e.right;return e}function Z(e){return e===K?0:e.size_left+e.piece.length+Z(e.right)}function Y(e){return e===K?0:e.lf_left+e.piece.lineFeedCnt+Y(e.right)}function X(){K.parent=K}function $(e,t){var n=t.right;n.size_left+=t.size_left+(t.piece?t.piece.length:0),n.lf_left+=t.lf_left+(t.piece?t.piece.lineFeedCnt:0),t.right=n.left,n.left!==K&&(n.left.parent=t),n.parent=t.parent,t.parent===K?e.root=n:t.parent.left===t?t.parent.left=n:t.parent.right=n,n.left=t,t.parent=n}function J(e,t){var n=t.left;t.left=n.right,n.right!==K&&(n.right.parent=t),n.parent=t.parent,t.size_left-=n.size_left+(n.piece?n.piece.length:0),t.lf_left-=n.lf_left+(n.piece?n.piece.lineFeedCnt:0),t.parent===K?e.root=n:t===t.parent.right?t.parent.right=n:t.parent.left=n,n.right=t,t.parent=n}function Q(e,t){var n,i;if(n=t.left===K?(i=t).right:t.right===K?(i=t).left:(i=q(t.right)).right,i===e.root)return e.root=n,n.color=0,t.detach(),X(),void(e.root.parent=K);var r=1===i.color;if(i===i.parent.left?i.parent.left=n:i.parent.right=n,i===t?(n.parent=i.parent,ne(e,n)):(i.parent===t?n.parent=i:n.parent=i.parent,ne(e,n),i.left=t.left,i.right=t.right,i.parent=t.parent,i.color=t.color,t===e.root?e.root=i:t===t.parent.left?t.parent.left=i:t.parent.right=i,i.left!==K&&(i.left.parent=i),i.right!==K&&(i.right.parent=i),i.size_left=t.size_left,i.lf_left=t.lf_left,ne(e,i)),t.detach(),n.parent.left===n){var o=Z(n),s=Y(n);if(o!==n.parent.size_left||s!==n.parent.lf_left){var a=o-n.parent.size_left,u=s-n.parent.lf_left;n.parent.size_left=o,n.parent.lf_left=s,te(e,n.parent,a,u)}}if(ne(e,n.parent),r)X();else{for(var c;n!==e.root&&0===n.color;)n===n.parent.left?(1===(c=n.parent.right).color&&(c.color=0,n.parent.color=1,$(e,n.parent),c=n.parent.right),0===c.left.color&&0===c.right.color?(c.color=1,n=n.parent):(0===c.right.color&&(c.left.color=0,c.color=1,J(e,c),c=n.parent.right),c.color=n.parent.color,n.parent.color=0,c.right.color=0,$(e,n.parent),n=e.root)):(1===(c=n.parent.left).color&&(c.color=0,n.parent.color=1,J(e,n.parent),c=n.parent.left),0===c.left.color&&0===c.right.color?(c.color=1,n=n.parent):(0===c.left.color&&(c.right.color=0,c.color=1,$(e,c),c=n.parent.left),c.color=n.parent.color,n.parent.color=0,c.left.color=0,J(e,n.parent),n=e.root));n.color=0,X()}}function ee(e,t){for(ne(e,t);t!==e.root&&1===t.parent.color;){var n;if(t.parent===t.parent.parent.left)1===(n=t.parent.parent.right).color?(t.parent.color=0,n.color=0,t.parent.parent.color=1,t=t.parent.parent):(t===t.parent.right&&$(e,t=t.parent),t.parent.color=0,t.parent.parent.color=1,J(e,t.parent.parent));else 1===(n=t.parent.parent.left).color?(t.parent.color=0,n.color=0,t.parent.parent.color=1,t=t.parent.parent):(t===t.parent.left&&J(e,t=t.parent),t.parent.color=0,t.parent.parent.color=1,$(e,t.parent.parent))}e.root.color=0}function te(e,t,n,i){for(;t!==e.root&&t!==K;)t.parent.left===t&&(t.parent.size_left+=n,t.parent.lf_left+=i),t=t.parent}function ne(e,t){var n=0,i=0;if(t!==e.root){if(0===n){for(;t!==e.root&&t===t.parent.right;)t=t.parent;if(t===e.root)return;n=Z((t=t.parent).left)-t.size_left,i=Y(t.left)-t.lf_left,t.size_left+=n,t.lf_left+=i}for(;t!==e.root&&(0!==n||0!==i);)t.parent.left===t&&(t.parent.size_left+=n,t.parent.lf_left+=i),t=t.parent}}K.parent=K,K.left=K,K.right=K,K.color=0;var ie=n("IErJ");function re(e){var t;return(t=e[e.length-1]<65536?new Uint16Array(e.length):new Uint32Array(e.length)).set(e,0),t}var oe=function(){return function(e,t,n,i,r){this.lineStarts=e,this.cr=t,this.lf=n,this.crlf=i,this.isBasicASCII=r}}();function se(e,t){void 0===t&&(t=!0);for(var n=[0],i=1,r=0,o=e.length;r=0;t--){var n=this._cache[t];if(n.nodeStartOffset<=e&&n.nodeStartOffset+n.node.piece.length>=e)return n}return null},e.prototype.get2=function(e){for(var t=this._cache.length-1;t>=0;t--){var n=this._cache[t];if(n.nodeStartLineNumber&&n.nodeStartLineNumber=e)return n}return null},e.prototype.set=function(e){this._cache.length>=this._limit&&this._cache.shift(),this._cache.push(e)},e.prototype.valdiate=function(e){for(var t=!1,n=this._cache,i=0;i=e)&&(n[i]=null,t=!0)}if(t){for(var o=[],s=0,a=n;s0){e[r].lineStarts||(e[r].lineStarts=se(e[r].buffer));var s=new ae(r+1,{line:0,column:0},{line:e[r].lineStarts.length-1,column:e[r].buffer.length-e[r].lineStarts[e[r].lineStarts.length-1]},e[r].lineStarts.length-1,e[r].buffer.length);this._buffers.push(e[r]),i=this.rbInsertRight(i,s)}this._searchCache=new ce(1),this._lastVisitedLine={lineNumber:0,value:""},this.computeBufferMetadata()},e.prototype.normalizeEOL=function(e){var t=this,n=65535-Math.floor(21845),i=2*n,r="",o=0,s=[];if(this.iterate(this.root,function(a){var u=t.getNodeContent(a),c=u.length;if(o<=n||o+c0){var a=r.replace(/\r\n|\r|\n/g,e);s.push(new ue(a,se(a)))}this.create(s,e,!0)},e.prototype.getEOL=function(){return this._EOL},e.prototype.setEOL=function(e){this._EOL=e,this._EOLLength=this._EOL.length,this.normalizeEOL(e)},e.prototype.getOffsetAt=function(e,t){for(var n=0,i=this.root;i!==K;)if(i.left!==K&&i.lf_left+1>=e)i=i.left;else{if(i.lf_left+i.piece.lineFeedCnt+1>=e)return(n+=i.size_left)+(this.getAccumulatedValue(i,e-i.lf_left-2)+t-1);e-=i.lf_left+i.piece.lineFeedCnt,n+=i.size_left+i.piece.length,i=i.right}return n},e.prototype.getPositionAt=function(e){e=Math.floor(e),e=Math.max(0,e);for(var t=this.root,n=0,i=e;t!==K;)if(0!==t.size_left&&t.size_left>=e)t=t.left;else{if(t.size_left+t.piece.length>=e){var r=this.getIndexOf(t,e-t.size_left);if(n+=t.lf_left+r.index,0===r.index){var o=i-this.getOffsetAt(n+1,1);return new c.a(n+1,o+1)}return new c.a(n+1,r.remainder+1)}if(e-=t.size_left+t.piece.length,n+=t.lf_left+t.piece.lineFeedCnt,t.right===K){o=i-e-this.getOffsetAt(n+1,1);return new c.a(n+1,o+1)}t=t.right}return new c.a(1,1)},e.prototype.getValueInRange=function(e,t){if(e.startLineNumber===e.endLineNumber&&e.startColumn===e.endColumn)return"";var n=this.nodeAt2(e.startLineNumber,e.startColumn),i=this.nodeAt2(e.endLineNumber,e.endColumn),r=this.getValueInRange2(n,i);return t?t===this._EOL&&this._EOLNormalized&&t===this.getEOL()&&this._EOLNormalized?r:r.replace(/\r\n|\r|\n/g,t):r},e.prototype.getValueInRange2=function(e,t){if(e.node===t.node){var n=e.node,i=this._buffers[n.piece.bufferIndex].buffer,r=this.offsetInBuffer(n.piece.bufferIndex,n.piece.start);return i.substring(r+e.remainder,r+t.remainder)}var o=e.node,s=this._buffers[o.piece.bufferIndex].buffer,a=this.offsetInBuffer(o.piece.bufferIndex,o.piece.start),u=s.substring(a+e.remainder,a+o.piece.length);for(o=o.next();o!==K;){var c=this._buffers[o.piece.bufferIndex].buffer,l=this.offsetInBuffer(o.piece.bufferIndex,o.piece.start);if(o===t.node){u+=c.substring(l,l+t.remainder);break}u+=c.substr(l,o.piece.length),o=o.next()}return u},e.prototype.getLinesContent=function(){return this.getContentOfSubTree(this.root).split(/\r\n|\r|\n/)},e.prototype.getLength=function(){return this._length},e.prototype.getLineCount=function(){return this._lineCnt},e.prototype.getLineContent=function(e){return this._lastVisitedLine.lineNumber===e?this._lastVisitedLine.value:(this._lastVisitedLine.lineNumber=e,e===this._lineCnt?this._lastVisitedLine.value=this.getLineRawContent(e):this._EOLNormalized?this._lastVisitedLine.value=this.getLineRawContent(e,this._EOLLength):this._lastVisitedLine.value=this.getLineRawContent(e).replace(/(\r\n|\r|\n)$/,""),this._lastVisitedLine.value)},e.prototype.getLineCharCode=function(e,t){var n=this.nodeAt2(e,t+1);if(n.remainder===n.node.piece.length){var i=n.node.next();if(!i)return 0;var r=this._buffers[i.piece.bufferIndex],o=this.offsetInBuffer(i.piece.bufferIndex,i.piece.start);return r.buffer.charCodeAt(o)}r=this._buffers[n.node.piece.bufferIndex];var s=(o=this.offsetInBuffer(n.node.piece.bufferIndex,n.node.piece.start))+n.remainder;return r.buffer.charCodeAt(s)},e.prototype.getLineLength=function(e){if(e===this.getLineCount()){var t=this.getOffsetAt(e,1);return this.getLength()-t}return this.getOffsetAt(e+1,1)-this.getOffsetAt(e,1)-this._EOLLength},e.prototype.findMatchesInNode=function(e,t,n,i,r,o,s,a,u,c,d){var h,f=this._buffers[e.piece.bufferIndex],p=this.offsetInBuffer(e.piece.bufferIndex,e.piece.start),g=this.offsetInBuffer(e.piece.bufferIndex,r),m=this.offsetInBuffer(e.piece.bufferIndex,o);t.reset(g);var v={line:0,column:0};do{if(h=t.next(f.buffer)){if(h.index>=m)return c;this.positionInBuffer(e,h.index-p,v);var _=this.getLineFeedCnt(e.piece.bufferIndex,r,v),b=v.line===r.line?v.column-r.column+i:v.column+1,y=b+h[0].length;if(d[c++]=Object(ie.d)(new l.a(n+_,b,n+_,y),h,a),h.index+h[0].length>=m)return c;if(c>=u)return c}}while(h);return c},e.prototype.findMatchesLineByLine=function(e,t,n,i){var r=[],o=0,s=new ie.b(t.wordSeparators,t.regex),a=this.nodeAt2(e.startLineNumber,e.startColumn);if(null===a)return[];var u=this.nodeAt2(e.endLineNumber,e.endColumn);if(null===u)return[];var c=this.positionInBuffer(a.node,a.remainder),l=this.positionInBuffer(u.node,u.remainder);if(a.node===u.node)return this.findMatchesInNode(a.node,s,e.startLineNumber,e.startColumn,c,l,t,n,i,o,r),r;for(var d=e.startLineNumber,h=a.node;h!==u.node;){var f=this.getLineFeedCnt(h.piece.bufferIndex,c,h.piece.end);if(f>=1){var p=this._buffers[h.piece.bufferIndex].lineStarts,g=this.offsetInBuffer(h.piece.bufferIndex,h.piece.start),m=p[c.line+f],v=d===e.startLineNumber?e.startColumn:1;if((o=this.findMatchesInNode(h,s,d,v,c,this.positionInBuffer(h,m-g),t,n,i,o,r))>=i)return r;d+=f}var _=d===e.startLineNumber?e.startColumn-1:0;if(d===e.endLineNumber){var b=this.getLineContent(d).substring(_,e.endColumn-1);return o=this._findMatchesInLine(t,s,b,e.endLineNumber,_,o,r,n,i),r}if((o=this._findMatchesInLine(t,s,this.getLineContent(d).substr(_),d,_,o,r,n,i))>=i)return r;d++,h=(a=this.nodeAt2(d,1)).node,c=this.positionInBuffer(a.node,a.remainder)}if(d===e.endLineNumber){var y=d===e.startLineNumber?e.startColumn-1:0;b=this.getLineContent(d).substring(y,e.endColumn-1);return o=this._findMatchesInLine(t,s,b,e.endLineNumber,y,o,r,n,i),r}var w=d===e.startLineNumber?e.startColumn:1;return o=this.findMatchesInNode(u.node,s,d,w,c,l,t,n,i,o,r),r},e.prototype._findMatchesInLine=function(e,t,n,i,r,o,s,a,u){var c,d=e.wordSeparators;if(!a&&e.simpleSearch){for(var f=e.simpleSearch,p=f.length,g=n.length,m=-p;-1!==(m=n.indexOf(f,m+p));)if((!d||Object(ie.e)(d,n,g,m,p))&&(s[o++]=new h.b(new l.a(i,m+1+r,i,m+1+p+r),null),o>=u))return o;return o}t.reset(0);do{if((c=t.next(n))&&(s[o++]=Object(ie.d)(new l.a(i,c.index+1+r,i,c.index+1+c[0].length+r),c,a),o>=u))return o}while(c);return o},e.prototype.insert=function(e,t,n){if(void 0===n&&(n=!1),this._EOLNormalized=this._EOLNormalized&&n,this._lastVisitedLine.lineNumber=0,this._lastVisitedLine.value="",this.root!==K){var i=this.nodeAt(e),r=i.node,o=i.remainder,s=i.nodeStartOffset,a=r.piece,u=a.bufferIndex,c=this.positionInBuffer(r,o);if(0===r.piece.bufferIndex&&a.end.line===this._lastChangeBufferPos.line&&a.end.column===this._lastChangeBufferPos.column&&s+a.length===e&&t.length<65535)return this.appendToNode(r,t),void this.computeBufferMetadata();if(s===e)this.insertContentToNodeLeft(t,r),this._searchCache.valdiate(e);else if(s+r.piece.length>e){var l=[],d=new ae(a.bufferIndex,c,a.end,this.getLineFeedCnt(a.bufferIndex,c,a.end),this.offsetInBuffer(u,a.end)-this.offsetInBuffer(u,c));if(this.shouldCheckCRLF()&&this.endWithCR(t))if(10===this.nodeCharCodeAt(r,o)){var h={line:d.start.line+1,column:0};d=new ae(d.bufferIndex,h,d.end,this.getLineFeedCnt(d.bufferIndex,h,d.end),d.length-1),t+="\n"}if(this.shouldCheckCRLF()&&this.startWithLF(t))if(13===this.nodeCharCodeAt(r,o-1)){var f=this.positionInBuffer(r,o-1);this.deleteNodeTail(r,f),t="\r"+t,0===r.piece.length&&l.push(r)}else this.deleteNodeTail(r,c);else this.deleteNodeTail(r,c);var p=this.createNewPieces(t);d.length>0&&this.rbInsertRight(r,d);for(var g=r,m=0;m=0;u--)a=this.rbInsertLeft(a,s[u]);this.validateCRLFWithPrevNode(a),this.deleteNodes(n)},e.prototype.insertContentToNodeRight=function(e,t){this.adjustCarriageReturnFromNext(e,t)&&(e+="\n");for(var n=this.createNewPieces(e),i=this.rbInsertRight(t,n[0]),r=i,o=1;o=l))break;a=c+1}return n?(n.line=c,n.column=s-d,null):{line:c,column:s-d}},e.prototype.getLineFeedCnt=function(e,t,n){if(0===n.column)return n.line-t.line;var i=this._buffers[e].lineStarts;if(n.line===i.length-1)return n.line-t.line;var r=i[n.line+1],o=i[n.line]+n.column;if(r>o+1)return n.line-t.line;var s=o-1;return 13===this._buffers[e].buffer.charCodeAt(s)?n.line-t.line+1:n.line-t.line},e.prototype.offsetInBuffer=function(e,t){return this._buffers[e].lineStarts[t.line]+t.column},e.prototype.deleteNodes=function(e){for(var t=0;t65535){for(var t=[];e.length>65535;){var n=e.charCodeAt(65534),i=void 0;13===n||n>=55296&&n<=56319?(i=e.substring(0,65534),e=e.substring(65534)):(i=e.substring(0,65535),e=e.substring(65535));var r=se(i);t.push(new ae(this._buffers.length,{line:0,column:0},{line:r.length-1,column:i.length-r[r.length-1]},r.length-1,i.length)),this._buffers.push(new ue(i,r))}var o=se(e);return t.push(new ae(this._buffers.length,{line:0,column:0},{line:o.length-1,column:e.length-o[o.length-1]},o.length-1,e.length)),this._buffers.push(new ue(e,o)),t}var s=this._buffers[0].buffer.length,a=se(e,!1),u=this._lastChangeBufferPos;if(this._buffers[0].lineStarts[this._buffers[0].lineStarts.length-1]===s&&0!==s&&this.startWithLF(e)&&this.endWithCR(this._buffers[0].buffer)){this._lastChangeBufferPos={line:this._lastChangeBufferPos.line,column:this._lastChangeBufferPos.column+1},u=this._lastChangeBufferPos;for(var c=0;c=e-1)n=n.left;else{if(n.lf_left+n.piece.lineFeedCnt>e-1){o=this.getAccumulatedValue(n,e-n.lf_left-2),u=this.getAccumulatedValue(n,e-n.lf_left-1),s=this._buffers[n.piece.bufferIndex].buffer,a=this.offsetInBuffer(n.piece.bufferIndex,n.piece.start);return c+=n.size_left,this._searchCache.set({node:n,nodeStartOffset:c,nodeStartLineNumber:l-(e-1-n.lf_left)}),s.substring(a+o,a+u-t)}if(n.lf_left+n.piece.lineFeedCnt===e-1){o=this.getAccumulatedValue(n,e-n.lf_left-2),s=this._buffers[n.piece.bufferIndex].buffer,a=this.offsetInBuffer(n.piece.bufferIndex,n.piece.start);i=s.substring(a+o,a+n.piece.length);break}e-=n.lf_left+n.piece.lineFeedCnt,c+=n.size_left+n.piece.length,n=n.right}for(n=n.next();n!==K;){s=this._buffers[n.piece.bufferIndex].buffer;if(n.piece.lineFeedCnt>0){u=this.getAccumulatedValue(n,0),a=this.offsetInBuffer(n.piece.bufferIndex,n.piece.start);return i+=s.substring(a,a+u-t)}a=this.offsetInBuffer(n.piece.bufferIndex,n.piece.start);i+=s.substr(a,n.piece.length),n=n.next()}return i},e.prototype.computeBufferMetadata=function(){for(var e=this.root,t=1,n=0;e!==K;)t+=e.lf_left+e.piece.lineFeedCnt,n+=e.size_left+e.piece.length,e=e.right;this._lineCnt=t,this._length=n,this._searchCache.valdiate(this._length)},e.prototype.getIndexOf=function(e,t){var n=e.piece,i=this.positionInBuffer(e,t),r=i.line-n.start.line;if(this.offsetInBuffer(n.bufferIndex,n.end)-this.offsetInBuffer(n.bufferIndex,n.start)===t){var o=this.getLineFeedCnt(e.piece.bufferIndex,n.start,i);if(o!==r)return{index:o,remainder:0}}return{index:r,remainder:i.column}},e.prototype.getAccumulatedValue=function(e,t){if(t<0)return 0;var n=e.piece,i=this._buffers[n.bufferIndex].lineStarts,r=n.start.line+t+1;return r>n.end.line?i[n.end.line]+n.end.column-i[n.start.line]-n.start.column:i[r]-i[n.start.line]-n.start.column},e.prototype.deleteNodeTail=function(e,t){var n=e.piece,i=n.lineFeedCnt,r=this.offsetInBuffer(n.bufferIndex,n.end),o=t,s=this.offsetInBuffer(n.bufferIndex,o),a=this.getLineFeedCnt(n.bufferIndex,n.start,o),u=a-i,c=s-r,l=n.length+c;e.piece=new ae(n.bufferIndex,n.start,o,a,l),te(this,e,c,u)},e.prototype.deleteNodeHead=function(e,t){var n=e.piece,i=n.lineFeedCnt,r=this.offsetInBuffer(n.bufferIndex,n.start),o=t,s=this.getLineFeedCnt(n.bufferIndex,o,n.end),a=s-i,u=r-this.offsetInBuffer(n.bufferIndex,o),c=n.length+u;e.piece=new ae(n.bufferIndex,o,n.end,s,c),te(this,e,u,a)},e.prototype.shrinkNode=function(e,t,n){var i=e.piece,r=i.start,o=i.end,s=i.length,a=i.lineFeedCnt,u=t,c=this.getLineFeedCnt(i.bufferIndex,i.start,u),l=this.offsetInBuffer(i.bufferIndex,t)-this.offsetInBuffer(i.bufferIndex,r);e.piece=new ae(i.bufferIndex,i.start,u,c,l),te(this,e,l-s,c-a);var d=new ae(i.bufferIndex,n,o,this.getLineFeedCnt(i.bufferIndex,n,o),this.offsetInBuffer(i.bufferIndex,o)-this.offsetInBuffer(i.bufferIndex,n)),h=this.rbInsertRight(e,d);this.validateCRLFWithPrevNode(h)},e.prototype.appendToNode=function(e,t){this.adjustCarriageReturnFromNext(t,e)&&(t+="\n");var n=this.shouldCheckCRLF()&&this.startWithLF(t)&&this.endWithCR(e),i=this._buffers[0].buffer.length;this._buffers[0].buffer+=t;for(var r=se(t,!1),o=0;oe)t=t.left;else{if(t.size_left+t.piece.length>=e){i+=t.size_left;var r={node:t,remainder:e-t.size_left,nodeStartOffset:i};return this._searchCache.set(r),r}e-=t.size_left+t.piece.length,i+=t.size_left+t.piece.length,t=t.right}return null},e.prototype.nodeAt2=function(e,t){for(var n=this.root,i=0;n!==K;)if(n.left!==K&&n.lf_left>=e-1)n=n.left;else{if(n.lf_left+n.piece.lineFeedCnt>e-1){var r=this.getAccumulatedValue(n,e-n.lf_left-2),o=this.getAccumulatedValue(n,e-n.lf_left-1);return i+=n.size_left,{node:n,remainder:Math.min(r+t-1,o),nodeStartOffset:i}}if(n.lf_left+n.piece.lineFeedCnt===e-1){if((r=this.getAccumulatedValue(n,e-n.lf_left-2))+t-1<=n.piece.length)return{node:n,remainder:r+t-1,nodeStartOffset:i};t-=n.piece.length-r;break}e-=n.lf_left+n.piece.lineFeedCnt,i+=n.size_left+n.piece.length,n=n.right}for(n=n.next();n!==K;){if(n.piece.lineFeedCnt>0){o=this.getAccumulatedValue(n,0);var s=this.offsetOfNode(n);return{node:n,remainder:Math.min(t-1,o),nodeStartOffset:s}}if(n.piece.length>=t-1)return{node:n,remainder:t-1,nodeStartOffset:this.offsetOfNode(n)};t-=n.piece.length,n=n.next()}return null},e.prototype.nodeCharCodeAt=function(e,t){if(e.piece.lineFeedCnt<1)return-1;var n=this._buffers[e.piece.bufferIndex],i=this.offsetInBuffer(e.piece.bufferIndex,e.piece.start)+t;return n.buffer.charCodeAt(i)},e.prototype.offsetOfNode=function(e){if(!e)return 0;for(var t=e.size_left;e!==this.root;)e.parent.right===e&&(t+=e.parent.size_left+e.parent.piece.length),e=e.parent;return t},e.prototype.shouldCheckCRLF=function(){return!(this._EOLNormalized&&"\n"===this._EOL)},e.prototype.startWithLF=function(e){if("string"==typeof e)return 10===e.charCodeAt(0);if(e===K||0===e.piece.lineFeedCnt)return!1;var t=e.piece,n=this._buffers[t.bufferIndex].lineStarts,i=t.start.line,r=n[i]+t.start.column;return i!==n.length-1&&(!(n[i+1]>r+1)&&10===this._buffers[t.bufferIndex].buffer.charCodeAt(r))},e.prototype.endWithCR=function(e){return"string"==typeof e?13===e.charCodeAt(e.length-1):e!==K&&0!==e.piece.lineFeedCnt&&13===this.nodeCharCodeAt(e,e.piece.length-1)},e.prototype.validateCRLFWithPrevNode=function(e){if(this.shouldCheckCRLF()&&this.startWithLF(e)){var t=e.prev();this.endWithCR(t)&&this.fixCRLF(t,e)}},e.prototype.validateCRLFWithNextNode=function(e){if(this.shouldCheckCRLF()&&this.endWithCR(e)){var t=e.next();this.startWithLF(t)&&this.fixCRLF(e,t)}},e.prototype.fixCRLF=function(e,t){var n,i=[],r=this._buffers[e.piece.bufferIndex].lineStarts;n=0===e.piece.end.column?{line:e.piece.end.line-1,column:r[e.piece.end.line]-r[e.piece.end.line-1]-1}:{line:e.piece.end.line,column:e.piece.end.column-1};var o=e.piece.length-1,s=e.piece.lineFeedCnt-1;e.piece=new ae(e.piece.bufferIndex,e.piece.start,n,s,o),te(this,e,-1,-1),0===e.piece.length&&i.push(e);var a={line:t.piece.start.line+1,column:0},u=t.piece.length-1,c=this.getLineFeedCnt(t.piece.bufferIndex,a,t.piece.end);t.piece=new ae(t.piece.bufferIndex,a,t.piece.end,c,u),te(this,t,-1,-1),0===t.piece.length&&i.push(t);var l=this.createNewPieces("\r\n");this.rbInsertRight(e,l[0]);for(var d=0;d0){v.sort(function(e,t){return t.lineNumber-e.lineNumber}),S=[];u=0;for(var x=v.length;u0&&v[u-1].lineNumber===b)){var L=v[u].oldContent,O=this.getLineContent(b);0!==O.length&&O!==L&&-1===s.q(O)&&S.push(b)}}}return new h.a(w,C,S)},e.prototype._reduceOperations=function(e){return e.length<1e3?e:[this._toSingleEditOperation(e)]},e.prototype._toSingleEditOperation=function(e){for(var t=!1,n=e[0].range,i=e[e.length-1].range,r=new l.a(n.startLineNumber,n.startColumn,i.endLineNumber,i.endColumn),o=n.startLineNumber,s=n.startColumn,a=[],u=0,c=e.length;u0){var h=a.lines.length,f=a.lines[0],p=a.lines[h-1];d=1===h?new l.a(u,c,u,c+f.length):new l.a(u,c,u+h-1,p.length+1)}else d=new l.a(u,c,u,c);n=d.endLineNumber,i=d.endColumn,t.push(d),r=a}return t},e._sortOpsAscending=function(e,t){var n=l.a.compareRangesUsingEnds(e.range,t.range);return 0===n?e.sortIndex-t.sortIndex:n},e._sortOpsDescending=function(e,t){var n=l.a.compareRangesUsingEnds(e.range,t.range);return 0===n?t.sortIndex-e.sortIndex:-n},e}(),he=function(){function e(e,t,n,i,r,o,s,a){this._chunks=e,this._bom=t,this._cr=n,this._lf=i,this._crlf=r,this._containsRTL=o,this._isBasicASCII=s,this._normalizeEOL=a}return e.prototype._getEOL=function(e){var t=this._cr+this._lf+this._crlf,n=this._cr+this._crlf;return 0===t?1===e?"\n":"\r\n":n>t/2?"\r\n":"\n"},e.prototype.create=function(e){var t=this._getEOL(e),n=this._chunks;if(this._normalizeEOL&&("\r\n"===t&&(this._cr>0||this._lf>0)||"\n"===t&&(this._cr>0||this._crlf>0)))for(var i=0,r=n.length;i=55296&&t<=56319?(this._acceptChunk1(e.substr(0,e.length-1),!1),this._hasPreviousChar=!0,this._previousChar=t):(this._acceptChunk1(e,!1),this._hasPreviousChar=!1,this._previousChar=t)}},e.prototype._acceptChunk1=function(e,t){(t||0!==e.length)&&(this._hasPreviousChar?this._acceptChunk2(String.fromCharCode(this._previousChar)+e):this._acceptChunk2(e))},e.prototype._acceptChunk2=function(e){var t=function(e,t){e.length=0,e[0]=0;for(var n=1,i=0,r=0,o=0,s=!0,a=0,u=t.length;a126)&&(s=!1)}var l=new oe(re(e),i,r,o,s);return e.length=0,l}(this._tmpLineStarts,e);this.chunks.push(new ue(e,t.lineStarts)),this.cr+=t.cr,this.lf+=t.lf,this.crlf+=t.crlf,this.isBasicASCII&&(this.isBasicASCII=t.isBasicASCII),this.isBasicASCII||this.containsRTL||(this.containsRTL=s.h(e))},e.prototype.finish=function(e){return void 0===e&&(e=!0),this._finish(),new he(this.chunks,this.BOM,this.cr,this.lf,this.crlf,this.containsRTL,this.isBasicASCII,e)},e.prototype._finish=function(){if(0===this.chunks.length&&this._acceptChunk1("",!0),this._hasPreviousChar){this._hasPreviousChar=!1;var e=this.chunks[this.chunks.length-1];e.buffer+=String.fromCharCode(this._previousChar);var t=se(e.buffer);e.lineStarts=t,13===this._previousChar&&this.cr++}},e}(),pe=function(){return function(){this.changeType=1}}(),ge=function(){return function(e,t){this.changeType=2,this.lineNumber=e,this.detail=t}}(),me=function(){return function(e,t){this.changeType=3,this.fromLineNumber=e,this.toLineNumber=t}}(),ve=function(){return function(e,t,n){this.changeType=4,this.fromLineNumber=e,this.toLineNumber=t,this.detail=n}}(),_e=function(){return function(){this.changeType=5}}(),be=function(){function e(e,t,n,i){this.changes=e,this.versionId=t,this.isUndoing=n,this.isRedoing=i}return e.prototype.containsEvent=function(e){for(var t=0,n=this.changes.length;t>>0}var Ne=new Uint32Array(0).buffer,Ee=function(){function e(){this.tokens=[]}return e.prototype.add=function(e,t){if(this.tokens.length>0){var n=this.tokens[this.tokens.length-1];if(n.startLineNumber+n.tokens.length-1+1===e)return void n.tokens.push(t)}this.tokens.push(new Ie(e,[t]))},e}(),Ie=function(){return function(e,t){this.startLineNumber=e,this.tokens=t}}();function De(e){return e instanceof Uint32Array?e:new Uint32Array(e)}var Me,Te=function(){function e(){this._lineTokens=[],this._len=0}return e.prototype.flush=function(){this._lineTokens=[],this._len=0},e.prototype.getTokens=function(e,t,n){var i=null;if(t1&&(r=Se.x.getLanguageId(i[1])!==e),!r)return Ne}if(!i||0===i.length){var o=new Uint32Array(2);return o[0]=t,o[1]=ke(e),o.buffer}return i[i.length-2]=t,0===i.byteOffset&&i.byteLength===i.buffer.byteLength?i.buffer:i},e.prototype._ensureLine=function(e){for(;e>=this._len;)this._lineTokens[this._len]=null,this._len++},e.prototype._deleteLines=function(e,t){0!==t&&(e+t>this._len&&(t=this._len-e),this._lineTokens.splice(e,t),this._len-=t)},e.prototype._insertLines=function(e,t){if(0!==t){for(var n=[],i=0;i=this._len))if(t.startLineNumber!==t.endLineNumber){this._lineTokens[n]=e._deleteEnding(this._lineTokens[n],t.startColumn-1);var i=t.endLineNumber-1,r=null;i=this._len||(0!==n?(this._lineTokens[r]=e._deleteEnding(this._lineTokens[r],t.column-1),this._lineTokens[r]=e._insert(this._lineTokens[r],t.column-1,i),this._insertLines(t.lineNumber,n)):this._lineTokens[r]=e._insert(this._lineTokens[r],t.column-1,i))}},e._deleteBeginning=function(t,n){return null===t||t===Ne?t:e._delete(t,0,n)},e._deleteEnding=function(t,n){if(null===t||t===Ne)return t;var i=De(t),r=i[i.length-2];return e._delete(t,n,r)},e._delete=function(e,t,n){if(null===e||e===Ne||t===n)return e;var i=De(e),r=i.length>>>1;if(0===t&&i[i.length-2]===n)return Ne;var o,s,a=Ce.a.findIndexInTokensArray(i,t),u=a>0?i[a-1<<1]:0;if(ns&&(i[o++]=f,i[o++]=i[1+(h<<1)],s=f)}if(o===i.length)return e;var p=new Uint32Array(o);return p.set(i.subarray(0,o),0),p.buffer},e._append=function(e,t){if(t===Ne)return e;if(e===Ne)return t;if(null===e)return e;if(null===t)return null;var n=De(e),i=De(t),r=i.length>>>1,o=new Uint32Array(n.length+i.length);o.set(n,0);for(var s=n.length,a=n[n.length-2],u=0;u>>1,o=Ce.a.findIndexInTokensArray(i,t);o>0&&(i[o-1<<1]===t&&o--);for(var s=o;s=this._len;)this._beginState[this._len]=null,this._valid[this._len]=!1,this._len++},e.prototype._deleteLines=function(e,t){0!==t&&(e+t>this._len&&(t=this._len-e),this._beginState.splice(e,t),this._valid.splice(e,t),this._len-=t)},e.prototype._insertLines=function(e,t){if(0!==t){for(var n=[],i=[],r=0;r=0;r--)this._invalidateLine(e.startLineNumber+r-1);this._acceptDeleteRange(e),this._acceptInsertText(new c.a(e.startLineNumber,e.startColumn),t)},e.prototype._acceptDeleteRange=function(e){e.startLineNumber-1>=this._len||this._deleteLines(e.startLineNumber,e.endLineNumber-e.startLineNumber)},e.prototype._acceptInsertText=function(e,t){e.lineNumber-1>=this._len||this._insertLines(e.lineNumber,t)},e}(),Re=function(e){function t(t){var n=e.call(this)||this;return n._textModel=t,n._tokenizationStateStore=new Ae,n._revalidateTokensTimeout=-1,n._tokenizationSupport=null,n._register(Se.y.onDidChange(function(e){var t=n._textModel.getLanguageIdentifier();-1!==e.changedLanguages.indexOf(t.language)&&(n._resetTokenizationState(),n._textModel.clearTokens())})),n._register(n._textModel.onDidChangeRawContentFast(function(e){e.containsEvent(1)&&n._resetTokenizationState()})),n._register(n._textModel.onDidChangeContentFast(function(e){for(var t=0,i=e.changes.length;t20);){if(this._tokenizeOneInvalidLine(t)>=e)break}this._beginBackgroundTokenization(),this._textModel.setTokens(t.tokens)},t.prototype.tokenizeViewport=function(e,t){var n=new Ee;this._tokenizeViewport(n,e,t),this._textModel.setTokens(n.tokens)},t.prototype.reset=function(){this._resetTokenizationState(),this._textModel.clearTokens()},t.prototype.forceTokenization=function(e){var t=new Ee;this._updateTokensUntilLine(t,e),this._textModel.setTokens(t.tokens)},t.prototype.isCheapToTokenize=function(e){if(!this._tokenizationSupport)return!0;var t=this._tokenizationStateStore.invalidLineStartIndex+1;return!(e>t)&&(e0&&s>=1;s--){var a=this._textModel.getLineFirstNonWhitespaceColumn(s);if(0!==a&&a=0;s--){c=(h=Fe(u,this._tokenizationSupport,r[s],c)).endState}for(var l=t;l<=n;l++){var d=this._textModel.getLineContent(l),h=Fe(u,this._tokenizationSupport,d,c);e.add(l,h.tokens),this._tokenizationStateStore.setFakeTokens(l-1),c=h.endState}}},t}(o.a);function Fe(e,t,n,r){var o=null;if(t)try{o=t.tokenize2(n,r.clone(),0)}catch(e){Object(i.e)(e)}return o||(o=Object(xe.e)(e.id,n,r,0)),Ce.a.convertToEndOffset(o.tokens,n.length),o}var je=n("+jct"),We=n("Fllr"),Be=n("Eeyw"),Ve=n("iNUG"),He=n("KIxu"),ze=n("TNPA");n.d(t,"b",function(){return Ye}),n.d(t,"a",function(){return tt});var Ue=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}();function Ke(e){var t=new fe;return t.acceptChunk(e),t.finish()}function qe(e,t){return("string"==typeof e?Ke(e):e).create(t)}var Ge=0,Ze=function(){throw new Error("Invalid change accessor")},Ye=function(e){function t(n,i,o,u){void 0===u&&(u=null);var c=e.call(this)||this;c._onWillDispose=c._register(new r.a),c.onWillDispose=c._onWillDispose.event,c._onDidChangeDecorations=c._register(new rt),c.onDidChangeDecorations=c._onDidChangeDecorations.event,c._onDidChangeLanguage=c._register(new r.a),c.onDidChangeLanguage=c._onDidChangeLanguage.event,c._onDidChangeLanguageConfiguration=c._register(new r.a),c.onDidChangeLanguageConfiguration=c._onDidChangeLanguageConfiguration.event,c._onDidChangeTokens=c._register(new r.a),c.onDidChangeTokens=c._onDidChangeTokens.event,c._onDidChangeOptions=c._register(new r.a),c.onDidChangeOptions=c._onDidChangeOptions.event,c._onDidChangeAttached=c._register(new r.a),c.onDidChangeAttached=c._onDidChangeAttached.event,c._eventEmitter=c._register(new ot),Ge++,c.id="$model"+Ge,c.isForSimpleWidget=i.isForSimpleWidget,c._associatedResource=void 0===u||null===u?a.a.parse("inmemory://model/"+Ge):u,c._attachedEditorCount=0,c._buffer=qe(n,i.defaultEOL),c._options=t.resolveOptions(c._buffer,i);var d=c._buffer.getLineCount(),h=c._buffer.getValueLengthInRange(new l.a(1,1,d,c._buffer.getLineLength(d)+1),0);return i.largeFileOptimizations?c._isTooLargeForTokenization=h>t.LARGE_FILE_SIZE_THRESHOLD||d>t.LARGE_FILE_LINE_COUNT_THRESHOLD:c._isTooLargeForTokenization=!1,c._isTooLargeForSyncing=h>t.MODEL_SYNC_LIMIT,c._versionId=1,c._alternativeVersionId=1,c._isDisposed=!1,c._isDisposing=!1,c._languageIdentifier=o||xe.a,c._languageRegistryListener=We.a.onDidChange(function(e){e.languageIdentifier.id===c._languageIdentifier.id&&c._onDidChangeLanguageConfiguration.fire({})}),c._instanceId=s.I(Ge),c._lastDecorationId=0,c._decorations=Object.create(null),c._decorationsTree=new Xe,c._commandManager=new m(c),c._isUndoing=!1,c._isRedoing=!1,c._trimAutoWhitespaceLines=null,c._tokens=new Te,c._tokenization=new Re(c),c}return Ue(t,e),t.createFromString=function(e,n,i,r){return void 0===n&&(n=t.DEFAULT_CREATION_OPTIONS),void 0===i&&(i=null),void 0===r&&(r=null),new t(e,n,i,r)},t.resolveOptions=function(e,t){if(t.detectIndentation){var n=b(e,t.tabSize,t.insertSpaces);return new h.e({tabSize:n.tabSize,indentSize:n.tabSize,insertSpaces:n.insertSpaces,trimAutoWhitespace:t.trimAutoWhitespace,defaultEOL:t.defaultEOL})}return new h.e({tabSize:t.tabSize,indentSize:t.indentSize,insertSpaces:t.insertSpaces,trimAutoWhitespace:t.trimAutoWhitespace,defaultEOL:t.defaultEOL})},t.prototype.onDidChangeRawContentFast=function(e){return this._eventEmitter.fastEvent(function(t){return e(t.rawContentChangedEvent)})},t.prototype.onDidChangeRawContent=function(e){return this._eventEmitter.slowEvent(function(t){return e(t.rawContentChangedEvent)})},t.prototype.onDidChangeContentFast=function(e){return this._eventEmitter.fastEvent(function(t){return e(t.contentChangedEvent)})},t.prototype.onDidChangeContent=function(e){return this._eventEmitter.slowEvent(function(t){return e(t.contentChangedEvent)})},t.prototype.dispose=function(){this._isDisposing=!0,this._onWillDispose.fire(),this._languageRegistryListener.dispose(),this._tokenization.dispose(),this._isDisposed=!0,e.prototype.dispose.call(this),this._isDisposing=!1},t.prototype._assertNotDisposed=function(){if(this._isDisposed)throw new Error("Model is disposed!")},t.prototype._emitContentChangedEvent=function(e,t){this._isDisposing||this._eventEmitter.fire(new ye(e,t))},t.prototype.setValue=function(e){if(this._assertNotDisposed(),null!==e){var t=qe(e,this._options.defaultEOL);this.setValueFromTextBuffer(t)}},t.prototype._createContentChanged2=function(e,t,n,i,r,o,s){return{changes:[{range:e,rangeOffset:t,rangeLength:n,text:i}],eol:this._buffer.getEOL(),versionId:this.getVersionId(),isUndoing:r,isRedoing:o,isFlush:s}},t.prototype.setValueFromTextBuffer=function(e){if(this._assertNotDisposed(),null!==e){var t=this.getFullModelRange(),n=this.getValueLengthInRange(t),i=this.getLineCount(),r=this.getLineMaxColumn(i);this._buffer=e,this._increaseVersionId(),this._tokens.flush(),this._decorations=Object.create(null),this._decorationsTree=new Xe,this._commandManager=new m(this),this._trimAutoWhitespaceLines=null,this._emitContentChangedEvent(new be([new pe],this._versionId,!1,!1),this._createContentChanged2(new l.a(1,1,i,r),0,n,this.getValue(),!1,!1,!0))}},t.prototype.setEOL=function(e){this._assertNotDisposed();var t=1===e?"\r\n":"\n";if(this._buffer.getEOL()!==t){var n=this.getFullModelRange(),i=this.getValueLengthInRange(n),r=this.getLineCount(),o=this.getLineMaxColumn(r);this._onBeforeEOLChange(),this._buffer.setEOL(t),this._increaseVersionId(),this._onAfterEOLChange(),this._emitContentChangedEvent(new be([new _e],this._versionId,!1,!1),this._createContentChanged2(new l.a(1,1,r,o),0,i,this.getValue(),!1,!1,!1))}},t.prototype._onBeforeEOLChange=function(){var e=this.getVersionId(),t=this._decorationsTree.search(0,!1,!1,e);this._ensureNodesHaveRanges(t)},t.prototype._onAfterEOLChange=function(){for(var e=this.getVersionId(),t=this._decorationsTree.collectNodesPostOrder(),n=0,i=t.length;n0},t.prototype.getAttachedEditorCount=function(){return this._attachedEditorCount},t.prototype.isTooLargeForSyncing=function(){return this._isTooLargeForSyncing},t.prototype.isTooLargeForTokenization=function(){return this._isTooLargeForTokenization},t.prototype.isDisposed=function(){return this._isDisposed},t.prototype.isDominatedByLongLines=function(){if(this._assertNotDisposed(),this.isTooLargeForTokenization())return!1;for(var e=0,t=0,n=this._buffer.getLineCount(),i=1;i<=n;i++){var r=this._buffer.getLineLength(i);r>=1e4?t+=r:e+=r}return t>e},Object.defineProperty(t.prototype,"uri",{get:function(){return this._associatedResource},enumerable:!0,configurable:!0}),t.prototype.getOptions=function(){return this._assertNotDisposed(),this._options},t.prototype.getFormattingOptions=function(){return{tabSize:this._options.indentSize,insertSpaces:this._options.insertSpaces}},t.prototype.updateOptions=function(e){this._assertNotDisposed();var t=void 0!==e.tabSize?e.tabSize:this._options.tabSize,n=void 0!==e.indentSize?e.indentSize:this._options.indentSize,i=void 0!==e.insertSpaces?e.insertSpaces:this._options.insertSpaces,r=void 0!==e.trimAutoWhitespace?e.trimAutoWhitespace:this._options.trimAutoWhitespace,o=new h.e({tabSize:t,indentSize:n,insertSpaces:i,defaultEOL:this._options.defaultEOL,trimAutoWhitespace:r});if(!this._options.equals(o)){var s=this._options.createChangeEvent(o);this._options=o,this._onDidChangeOptions.fire(s)}},t.prototype.detectIndentation=function(e,t){this._assertNotDisposed();var n=b(this._buffer,t,e);this.updateOptions({insertSpaces:n.insertSpaces,tabSize:n.tabSize,indentSize:n.tabSize})},t._normalizeIndentationFromWhitespace=function(e,t,n){for(var i=0,r=0;rthis.getLineCount())throw new Error("Illegal value for lineNumber");return this._buffer.getLineContent(e)},t.prototype.getLineLength=function(e){if(this._assertNotDisposed(),e<1||e>this.getLineCount())throw new Error("Illegal value for lineNumber");return this._buffer.getLineLength(e)},t.prototype.getLinesContent=function(){return this._assertNotDisposed(),this._buffer.getLinesContent()},t.prototype.getEOL=function(){return this._assertNotDisposed(),this._buffer.getEOL()},t.prototype.getLineMinColumn=function(e){return this._assertNotDisposed(),1},t.prototype.getLineMaxColumn=function(e){if(this._assertNotDisposed(),e<1||e>this.getLineCount())throw new Error("Illegal value for lineNumber");return this._buffer.getLineLength(e)+1},t.prototype.getLineFirstNonWhitespaceColumn=function(e){if(this._assertNotDisposed(),e<1||e>this.getLineCount())throw new Error("Illegal value for lineNumber");return this._buffer.getLineFirstNonWhitespaceColumn(e)},t.prototype.getLineLastNonWhitespaceColumn=function(e){if(this._assertNotDisposed(),e<1||e>this.getLineCount())throw new Error("Illegal value for lineNumber");return this._buffer.getLineLastNonWhitespaceColumn(e)},t.prototype._validateRangeRelaxedNoAllocations=function(e){var t,n,i=this._buffer.getLineCount(),r=e.startLineNumber,o=e.startColumn;if(r<1)t=1,n=1;else if(r>i)t=i,n=this.getLineMaxColumn(t);else{if(t=0|r,o<=1)n=1;else n=o>=(h=this.getLineMaxColumn(t))?h:0|o}var s,a,u=e.endLineNumber,c=e.endColumn;if(u<1)s=1,a=1;else if(u>i)s=i,a=this.getLineMaxColumn(s);else{var h;if(s=0|u,c<=1)a=1;else a=c>=(h=this.getLineMaxColumn(s))?h:0|c}return r===t&&o===n&&u===s&&c===a&&e instanceof l.a&&!(e instanceof d.a)?e:new l.a(t,n,s,a)},t.prototype._isValidPosition=function(e,t,n){if("number"!=typeof e||"number"!=typeof t)return!1;if(isNaN(e)||isNaN(t))return!1;if(e<1||t<1)return!1;if((0|e)!==e||(0|t)!==t)return!1;if(e>this._buffer.getLineCount())return!1;if(t>this.getLineMaxColumn(e))return!1;if(n&&t>1){var i=this._buffer.getLineCharCode(e,t-2);if(s.w(i))return!1}return!0},t.prototype._validatePosition=function(e,t,n){var i=Math.floor("number"!=typeof e||isNaN(e)?1:e),r=Math.floor("number"!=typeof t||isNaN(t)?1:t),o=this._buffer.getLineCount();if(i<1)return new c.a(1,1);if(i>o)return new c.a(o,this.getLineMaxColumn(o));if(r<=1)return new c.a(i,1);var a=this.getLineMaxColumn(i);if(r>=a)return new c.a(i,a);if(n){var u=this._buffer.getLineCharCode(i,r-2);if(s.w(u))return new c.a(i,r-1)}return new c.a(i,r)},t.prototype.validatePosition=function(e){return this._assertNotDisposed(),e instanceof c.a&&this._isValidPosition(e.lineNumber,e.column,!0)?e:this._validatePosition(e.lineNumber,e.column,!0)},t.prototype._isValidRange=function(e,t){var n=e.startLineNumber,i=e.startColumn,r=e.endLineNumber,o=e.endColumn;if(!this._isValidPosition(n,i,!1))return!1;if(!this._isValidPosition(r,o,!1))return!1;if(t){var a=i>1?this._buffer.getLineCharCode(n,i-2):0,u=o>1&&o<=this._buffer.getLineLength(r)?this._buffer.getLineCharCode(r,o-2):0,c=s.w(a),l=s.w(u);return!c&&!l}return!0},t.prototype.validateRange=function(e){if(this._assertNotDisposed(),e instanceof l.a&&!(e instanceof d.a)&&this._isValidRange(e,!0))return e;var t=this._validatePosition(e.startLineNumber,e.startColumn,!1),n=this._validatePosition(e.endLineNumber,e.endColumn,!1),i=t.lineNumber,r=t.column,o=n.lineNumber,a=n.column,u=r>1?this._buffer.getLineCharCode(i,r-2):0,c=a>1&&a<=this._buffer.getLineLength(o)?this._buffer.getLineCharCode(o,a-2):0,h=s.w(u),f=s.w(c);return h||f?i===o&&r===a?new l.a(i,r-1,o,a-1):h&&f?new l.a(i,r-1,o,a+1):h?new l.a(i,r-1,o,a):new l.a(i,r,o,a+1):new l.a(i,r,o,a)},t.prototype.modifyPosition=function(e,t){this._assertNotDisposed();var n=this.getOffsetAt(e)+t;return this.getPositionAt(Math.min(this._buffer.getLength(),Math.max(0,n)))},t.prototype.getFullModelRange=function(){this._assertNotDisposed();var e=this.getLineCount();return new l.a(1,1,e,this.getLineMaxColumn(e))},t.prototype.findMatchesLineByLine=function(e,t,n,i){return this._buffer.findMatchesLineByLine(e,t,n,i)},t.prototype.findMatches=function(e,t,n,i,r,o,s){var a;if(void 0===s&&(s=999),this._assertNotDisposed(),a=l.a.isIRange(t)?this.validateRange(t):this.getFullModelRange(),!n&&e.indexOf("\n")<0){var u=new ie.a(e,n,i,r).parseSearchRequest();return u?this.findMatchesLineByLine(a,u,o,s):[]}return ie.c.findMatches(this,new ie.a(e,n,i,r),a,o,s)},t.prototype.findNextMatch=function(e,t,n,i,r,o){this._assertNotDisposed();var s=this.validatePosition(t);if(!n&&e.indexOf("\n")<0){var a=new ie.a(e,n,i,r).parseSearchRequest();if(!a)return null;var u=this.getLineCount(),c=new l.a(s.lineNumber,s.column,u,this.getLineMaxColumn(u)),d=this.findMatchesLineByLine(c,a,o,1);return ie.c.findNextMatch(this,new ie.a(e,n,i,r),s,o),d.length>0?d[0]:(c=new l.a(1,1,s.lineNumber,this.getLineMaxColumn(s.lineNumber)),(d=this.findMatchesLineByLine(c,a,o,1)).length>0?d[0]:null)}return ie.c.findNextMatch(this,new ie.a(e,n,i,r),s,o)},t.prototype.findPreviousMatch=function(e,t,n,i,r,o){this._assertNotDisposed();var s=this.validatePosition(t);return ie.c.findPreviousMatch(this,new ie.a(e,n,i,r),s,o)},t.prototype.pushStackElement=function(){this._commandManager.pushStackElement()},t.prototype.pushEOL=function(e){if(("\n"===this.getEOL()?0:1)!==e)try{this._onDidChangeDecorations.beginDeferredEmit(),this._eventEmitter.beginDeferredEmit(),this._commandManager.pushEOL(e)}finally{this._eventEmitter.endDeferredEmit(),this._onDidChangeDecorations.endDeferredEmit()}},t.prototype.pushEditOperations=function(e,t,n){try{return this._onDidChangeDecorations.beginDeferredEmit(),this._eventEmitter.beginDeferredEmit(),this._pushEditOperations(e,t,n)}finally{this._eventEmitter.endDeferredEmit(),this._onDidChangeDecorations.endDeferredEmit()}},t.prototype._pushEditOperations=function(e,t,n){var i=this;if(this._options.trimAutoWhitespace&&this._trimAutoWhitespaceLines){for(var r=t.map(function(e){return{range:i.validateRange(e.range),text:e.text}}),o=!0,s=0,a=e.length;su.endLineNumber,p=u.startLineNumber>_.endLineNumber;if(!f&&!p){c=!0;break}}if(!c){o=!1;break}}if(o)for(s=0,a=this._trimAutoWhitespaceLines.length;s_.endLineNumber)&&!(g===_.startLineNumber&&_.startColumn===m&&_.isEmpty()&&b&&b.length>0&&"\n"===b.charAt(0)||g===_.startLineNumber&&1===_.startColumn&&_.isEmpty()&&b&&b.length>0&&"\n"===b.charAt(b.length-1))){v=!1;break}}v&&t.push({range:new l.a(g,1,g,m),text:null})}this._trimAutoWhitespaceLines=null}return this._commandManager.pushEditOperation(e,t,n)},t.prototype.applyEdits=function(e){try{return this._onDidChangeDecorations.beginDeferredEmit(),this._eventEmitter.beginDeferredEmit(),this._applyEdits(e)}finally{this._eventEmitter.endDeferredEmit(),this._onDidChangeDecorations.endDeferredEmit()}},t.prototype._applyEdits=function(e){for(var t=0,n=e.length;t=0;b--){var y=f+b,w=o-u-_+y;a.push(new ge(y,this.getLineContent(w)))}if(vthis.getLineCount()?[]:this.getLinesDecorations(e,e,t,n)},t.prototype.getLinesDecorations=function(e,t,n,i){void 0===n&&(n=0),void 0===i&&(i=!1);var r=this.getLineCount(),o=Math.min(r,Math.max(1,e)),s=Math.min(r,Math.max(1,t)),a=this.getLineMaxColumn(s);return this._getDecorationsInRange(new l.a(o,1,s,a),n,i)},t.prototype.getDecorationsInRange=function(e,t,n){void 0===t&&(t=0),void 0===n&&(n=!1);var i=this.validateRange(e);return this._getDecorationsInRange(i,t,n)},t.prototype.getOverviewRulerDecorations=function(e,t){void 0===e&&(e=0),void 0===t&&(t=!1);var n=this.getVersionId(),i=this._decorationsTree.search(e,t,!0,n);return this._ensureNodesHaveRanges(i)},t.prototype.getAllDecorations=function(e,t){void 0===e&&(e=0),void 0===t&&(t=!1);var n=this.getVersionId(),i=this._decorationsTree.search(e,t,!1,n);return this._ensureNodesHaveRanges(i)},t.prototype._getDecorationsInRange=function(e,t,n){var i=this._buffer.getOffsetAt(e.startLineNumber,e.startColumn),r=this._buffer.getOffsetAt(e.endLineNumber,e.endColumn),o=this.getVersionId(),s=this._decorationsTree.intervalSearch(i,r,t,n,o);return this._ensureNodesHaveRanges(s)},t.prototype._ensureNodesHaveRanges=function(e){for(var t=0,n=e.length;tthis.getLineCount())throw new Error("Illegal value for lineNumber");this._tokens.setTokens(this._languageIdentifier.id,e-1,this._buffer.getLineLength(e),t)},t.prototype.setTokens=function(e){if(0!==e.length){for(var t=[],n=0,i=e.length;nthis.getLineCount())throw new Error("Illegal value for lineNumber");this._tokenization.forceTokenization(e)},t.prototype.isCheapToTokenize=function(e){return this._tokenization.isCheapToTokenize(e)},t.prototype.tokenizeIfCheap=function(e){this.isCheapToTokenize(e)&&this.forceTokenization(e)},t.prototype.getLineTokens=function(e){if(e<1||e>this.getLineCount())throw new Error("Illegal value for lineNumber");return this._getLineTokens(e)},t.prototype._getLineTokens=function(e){var t=this.getLineContent(e);return this._tokens.getTokens(this._languageIdentifier.id,e-1,t)},t.prototype.getLanguageIdentifier=function(){return this._languageIdentifier},t.prototype.getModeId=function(){return this._languageIdentifier.language},t.prototype.setMode=function(e){if(this._languageIdentifier.id!==e.id){var t={oldLanguage:this._languageIdentifier.language,newLanguage:e.language};this._languageIdentifier=e,this._onDidChangeLanguage.fire(t),this._onDidChangeLanguageConfiguration.fire({})}},t.prototype.getLanguageIdAtPosition=function(e,t){var n=this.validatePosition(new c.a(e,t)),i=this.getLineTokens(n.lineNumber);return i.getLanguageId(i.findTokenIndexAtOffset(n.column-1))},t.prototype.getWordAtPosition=function(e){this._assertNotDisposed();var n=this.validatePosition(e),i=this.getLineContent(n.lineNumber),r=this._getLineTokens(n.lineNumber),o=r.findTokenIndexAtOffset(n.column-1),s=t._findLanguageBoundaries(r,o),a=s[0],u=s[1],c=Object(je.d)(n.column,We.a.getWordDefinition(r.getLanguageId(o)),i.substring(a,u),a);if(c&&c.startColumn<=e.column&&e.column<=c.endColumn)return c;if(o>0&&a===n.column-1){var l=t._findLanguageBoundaries(r,o-1),d=l[0],h=l[1],f=Object(je.d)(n.column,We.a.getWordDefinition(r.getLanguageId(o-1)),i.substring(d,h),d);if(f&&f.startColumn<=e.column&&e.column<=f.endColumn)return f}return null},t._findLanguageBoundaries=function(e,t){for(var n=e.getLanguageId(t),i=0,r=t;r>=0&&e.getLanguageId(r)===n;r--)i=e.getStartOffset(r);for(var o=e.getLineContent().length,s=(r=t,e.getCount());r0&&n.getStartOffset(r)===e.column-1){a=n.getStartOffset(r);r--;var c=We.a.getBracketsSupport(n.getLanguageId(r));if(c&&!Object(Be.b)(n.getStandardTokenType(r))){var l,d,h;s=Math.max(n.getStartOffset(r),e.column-1-c.maxBracketLength);if((l=Ve.a.findPrevBracketInToken(c.reversedRegex,t,i,s,a))&&l.startColumn<=e.column&&e.column<=l.endColumn)if(d=(d=i.substring(l.startColumn-1,l.endColumn-1)).toLowerCase(),h=this._matchFoundBracket(l,c.textIsBracket[d],c.textIsOpenBracket[d]))return h}}return null},t.prototype._matchFoundBracket=function(e,t,n){if(!t)return null;var i;if(n){if(i=this._findMatchingBracketDown(t,e.getEndPosition()))return[e,i]}else if(i=this._findMatchingBracketUp(t,e.getStartPosition()))return[e,i];return null},t.prototype._findMatchingBracketUp=function(e,t){for(var n=e.languageIdentifier.id,i=e.reversedRegex,r=-1,o=t.lineNumber;o>=1;o--){var s=this._getLineTokens(o),a=s.getCount(),u=this._buffer.getLineContent(o),c=a-1,l=-1;for(o===t.lineNumber&&(c=s.findTokenIndexAtOffset(t.column-1),l=t.column-1);c>=0;c--){var d=s.getLanguageId(c),h=s.getStandardTokenType(c),f=s.getStartOffset(c),p=s.getEndOffset(c);if(-1===l&&(l=p),d===n&&!Object(Be.b)(h))for(;;){var g=Ve.a.findPrevBracketInToken(i,o,u,f,l);if(!g)break;var m=u.substring(g.startColumn-1,g.endColumn-1);if((m=m.toLowerCase())===e.open?r++:m===e.close&&r--,0===r)return g;l=g.startColumn-1}l=-1}}return null},t.prototype._findMatchingBracketDown=function(e,t){for(var n=e.languageIdentifier.id,i=e.forwardRegex,r=1,o=t.lineNumber,s=this.getLineCount();o<=s;o++){var a=this._getLineTokens(o),u=a.getCount(),c=this._buffer.getLineContent(o),l=0,d=0;for(o===t.lineNumber&&(l=a.findTokenIndexAtOffset(t.column-1),d=t.column-1);l=1;r--){var o=this._getLineTokens(r),s=o.getCount(),a=this._buffer.getLineContent(r),u=s-1,c=-1;for(r===t.lineNumber&&(u=o.findTokenIndexAtOffset(t.column-1),c=t.column-1);u>=0;u--){var l=o.getLanguageId(u),d=o.getStandardTokenType(u),h=o.getStartOffset(u),f=o.getEndOffset(u);if(-1===c&&(c=f),n!==l&&(n=l,i=We.a.getBracketsSupport(n)),i&&!Object(Be.b)(d)){var p=Ve.a.findPrevBracketInToken(i.reversedRegex,r,a,h,c);if(p)return this._toFoundBracket(i,p)}c=-1}}return null},t.prototype.findNextBracket=function(e){for(var t=this.validatePosition(e),n=-1,i=null,r=t.lineNumber,o=this.getLineCount();r<=o;r++){var s=this._getLineTokens(r),a=s.getCount(),u=this._buffer.getLineContent(r),c=0,l=0;for(r===t.lineNumber&&(c=s.findTokenIndexAtOffset(t.column-1),l=t.column-1);cr)throw new Error("Illegal value for lineNumber");for(var o=We.a.getFoldingRules(this._languageIdentifier.id),s=Boolean(o&&o.offSide),a=-2,u=-1,c=-2,l=-1,d=function(e){if(-1!==a&&(-2===a||a>e-1)){a=-1,u=-1;for(var t=e-2;t>=0;t--){var n=i._computeIndentLevel(t);if(n>=0){a=t,u=n;break}}}if(-2===c){c=-1,l=-1;for(t=e;t=0){c=t,l=o;break}}}},h=-2,f=-1,p=-2,g=-1,m=function(e){if(-2===h){h=-1,f=-1;for(var t=e-2;t>=0;t--){var n=i._computeIndentLevel(t);if(n>=0){h=t,f=n;break}}}if(-1!==p&&(-2===p||p=0){p=t,g=o;break}}}},v=0,_=!0,b=0,y=!0,w=0,C=0;_||y;C++){var S=e-C,x=e+C;if(0!==C&&(S<1||Sr||x>n)&&(y=!1),C>5e4&&(_=!1,y=!1),_){var L=void 0;if((O=this._computeIndentLevel(S-1))>=0?(c=S-1,l=O,L=Math.ceil(O/this._options.indentSize)):(d(S),L=this._getIndentLevelForWhitespaceLine(s,u,l)),0===C){if(v=S,b=x,0===(w=L))return{startLineNumber:v,endLineNumber:b,indent:w};continue}L>=w?v=S:_=!1}if(y){var O,k=void 0;(O=this._computeIndentLevel(x-1))>=0?(h=x-1,f=O,k=Math.ceil(O/this._options.indentSize)):(m(x),k=this._getIndentLevelForWhitespaceLine(s,f,g)),k>=w?b=x:y=!1}}return{startLineNumber:v,endLineNumber:b,indent:w}},t.prototype.getLinesIndentGuides=function(e,t){this._assertNotDisposed();var n=this.getLineCount();if(e<1||e>n)throw new Error("Illegal value for startLineNumber");if(t<1||t>n)throw new Error("Illegal value for endLineNumber");for(var i=We.a.getFoldingRules(this._languageIdentifier.id),r=Boolean(i&&i.offSide),o=new Array(t-e+1),s=-2,a=-1,u=-2,c=-1,l=e;l<=t;l++){var d=l-e,h=this._computeIndentLevel(l-1);if(h>=0)s=l-1,a=h,o[d]=Math.ceil(h/this._options.indentSize);else{if(-2===s){s=-1,a=-1;for(var f=l-2;f>=0;f--){if((p=this._computeIndentLevel(f))>=0){s=f,a=p;break}}}if(-1!==u&&(-2===u||u=0){u=f,c=p;break}}}o[d]=this._getIndentLevelForWhitespaceLine(r,a,c)}}return o},t.prototype._getIndentLevelForWhitespaceLine=function(e,t,n){return-1===t||-1===n?0:t0?this._deferredEvent?this._deferredEvent=this._deferredEvent.merge(e):this._deferredEvent=e:(this._fastEmitter.fire(e),this._slowEmitter.fire(e))},t}(o.a)},"0u1n":function(e,t){},1:function(e,t){},"16On":function(e,t,n){var i=n("LC74");function r(e){this._reporterState={obj:null,path:[],options:e||{},errors:[]}}function o(e,t){this.path=e,this.rethrow(t)}t.Reporter=r,r.prototype.isError=function(e){return e instanceof o},r.prototype.save=function(){var e=this._reporterState;return{obj:e.obj,pathLen:e.path.length}},r.prototype.restore=function(e){var t=this._reporterState;t.obj=e.obj,t.path=t.path.slice(0,e.pathLen)},r.prototype.enterKey=function(e){return this._reporterState.path.push(e)},r.prototype.exitKey=function(e){var t=this._reporterState;t.path=t.path.slice(0,e-1)},r.prototype.leaveKey=function(e,t,n){var i=this._reporterState;this.exitKey(e),null!==i.obj&&(i.obj[t]=n)},r.prototype.path=function(){return this._reporterState.path.join("/")},r.prototype.enterObject=function(){var e=this._reporterState,t=e.obj;return e.obj={},t},r.prototype.leaveObject=function(e){var t=this._reporterState,n=t.obj;return t.obj=e,n},r.prototype.error=function(e){var t,n=this._reporterState,i=e instanceof o;if(t=i?e:new o(n.path.map(function(e){return"["+JSON.stringify(e)+"]"}).join(""),e.message||e,e.stack),!n.options.partial)throw t;return i||n.errors.push(t),t},r.prototype.wrapResult=function(e){var t=this._reporterState;return t.options.partial?{result:this.isError(e)?null:e,errors:t.errors}:e},i(o,Error),o.prototype.rethrow=function(e){if(this.message=e+" at: "+(this.path||"(shallow)"),Error.captureStackTrace&&Error.captureStackTrace(this,o),!this.stack)try{throw new Error(this.message)}catch(e){this.stack=e.stack}return this}},"19bf":function(e,t,n){"use strict";var i=n("KDHK");t.certificate=n("lQBd");var r=i.define("RSAPrivateKey",function(){this.seq().obj(this.key("version").int(),this.key("modulus").int(),this.key("publicExponent").int(),this.key("privateExponent").int(),this.key("prime1").int(),this.key("prime2").int(),this.key("exponent1").int(),this.key("exponent2").int(),this.key("coefficient").int())});t.RSAPrivateKey=r;var o=i.define("RSAPublicKey",function(){this.seq().obj(this.key("modulus").int(),this.key("publicExponent").int())});t.RSAPublicKey=o;var s=i.define("SubjectPublicKeyInfo",function(){this.seq().obj(this.key("algorithm").use(a),this.key("subjectPublicKey").bitstr())});t.PublicKey=s;var a=i.define("AlgorithmIdentifier",function(){this.seq().obj(this.key("algorithm").objid(),this.key("none").null_().optional(),this.key("curve").objid().optional(),this.key("params").seq().obj(this.key("p").int(),this.key("q").int(),this.key("g").int()).optional())}),u=i.define("PrivateKeyInfo",function(){this.seq().obj(this.key("version").int(),this.key("algorithm").use(a),this.key("subjectPrivateKey").octstr())});t.PrivateKey=u;var c=i.define("EncryptedPrivateKeyInfo",function(){this.seq().obj(this.key("algorithm").seq().obj(this.key("id").objid(),this.key("decrypt").seq().obj(this.key("kde").seq().obj(this.key("id").objid(),this.key("kdeparams").seq().obj(this.key("salt").octstr(),this.key("iters").int())),this.key("cipher").seq().obj(this.key("algo").objid(),this.key("iv").octstr()))),this.key("subjectPrivateKey").octstr())});t.EncryptedPrivateKey=c;var l=i.define("DSAPrivateKey",function(){this.seq().obj(this.key("version").int(),this.key("p").int(),this.key("q").int(),this.key("g").int(),this.key("pub_key").int(),this.key("priv_key").int())});t.DSAPrivateKey=l,t.DSAparam=i.define("DSAparam",function(){this.int()});var d=i.define("ECPrivateKey",function(){this.seq().obj(this.key("version").int(),this.key("privateKey").octstr(),this.key("parameters").optional().explicit(0).use(h),this.key("publicKey").optional().explicit(1).bitstr())});t.ECPrivateKey=d;var h=i.define("ECParameters",function(){this.choice({namedCurve:this.objid()})});t.signature=i.define("signature",function(){this.seq().obj(this.key("r").int(),this.key("s").int())})},"1LBi":function(e,t){},"1O6n":function(e,t){},"1Z8u":function(e,t){},"1lLf":function(e,t,n){"use strict";var i=n("08Lv"),r=n("LC74");function o(e,t){return 55296==(64512&e.charCodeAt(t))&&(!(t<0||t+1>=e.length)&&56320==(64512&e.charCodeAt(t+1)))}function s(e){return(e>>>24|e>>>8&65280|e<<8&16711680|(255&e)<<24)>>>0}function a(e){return 1===e.length?"0"+e:e}function u(e){return 7===e.length?"0"+e:6===e.length?"00"+e:5===e.length?"000"+e:4===e.length?"0000"+e:3===e.length?"00000"+e:2===e.length?"000000"+e:1===e.length?"0000000"+e:e}t.inherits=r,t.toArray=function(e,t){if(Array.isArray(e))return e.slice();if(!e)return[];var n=[];if("string"==typeof e)if(t){if("hex"===t)for((e=e.replace(/[^a-z0-9]+/gi,"")).length%2!=0&&(e="0"+e),r=0;r>6|192,n[i++]=63&s|128):o(e,r)?(s=65536+((1023&s)<<10)+(1023&e.charCodeAt(++r)),n[i++]=s>>18|240,n[i++]=s>>12&63|128,n[i++]=s>>6&63|128,n[i++]=63&s|128):(n[i++]=s>>12|224,n[i++]=s>>6&63|128,n[i++]=63&s|128)}else for(r=0;r>>0}return s},t.split32=function(e,t){for(var n=new Array(4*e.length),i=0,r=0;i>>24,n[r+1]=o>>>16&255,n[r+2]=o>>>8&255,n[r+3]=255&o):(n[r+3]=o>>>24,n[r+2]=o>>>16&255,n[r+1]=o>>>8&255,n[r]=255&o)}return n},t.rotr32=function(e,t){return e>>>t|e<<32-t},t.rotl32=function(e,t){return e<>>32-t},t.sum32=function(e,t){return e+t>>>0},t.sum32_3=function(e,t,n){return e+t+n>>>0},t.sum32_4=function(e,t,n,i){return e+t+n+i>>>0},t.sum32_5=function(e,t,n,i,r){return e+t+n+i+r>>>0},t.sum64=function(e,t,n,i){var r=e[t],o=i+e[t+1]>>>0,s=(o>>0,e[t+1]=o},t.sum64_hi=function(e,t,n,i){return(t+i>>>0>>0},t.sum64_lo=function(e,t,n,i){return t+i>>>0},t.sum64_4_hi=function(e,t,n,i,r,o,s,a){var u=0,c=t;return u+=(c=c+i>>>0)>>0)>>0)>>0},t.sum64_4_lo=function(e,t,n,i,r,o,s,a){return t+i+o+a>>>0},t.sum64_5_hi=function(e,t,n,i,r,o,s,a,u,c){var l=0,d=t;return l+=(d=d+i>>>0)>>0)>>0)>>0)>>0},t.sum64_5_lo=function(e,t,n,i,r,o,s,a,u,c){return t+i+o+a+c>>>0},t.rotr64_hi=function(e,t,n){return(t<<32-n|e>>>n)>>>0},t.rotr64_lo=function(e,t,n){return(e<<32-n|t>>>n)>>>0},t.shr64_hi=function(e,t,n){return e>>>n},t.shr64_lo=function(e,t,n){return(e<<32-n|t>>>n)>>>0}},"1mBN":function(e,t){},"1sup":function(e,t,n){"use strict";t.b=function(e,t,n){"string"==typeof e&&(e=i.a.file(e));if(n){var h=n.getWorkspaceFolder(e);if(h){var f=n.getWorkspace().folders.length>1,p=void 0;if(p=Object(u.e)(h.uri,e)?"":Object(u.h)(h.uri,e),f){var g=h&&h.name?h.name:Object(u.b)(h.uri);p=p?g+" • "+p:g}return p}}if(e.scheme!==s.b.file&&e.scheme!==s.b.untitled)return e.with({query:null,fragment:null}).toString(!0);if(c(e.fsPath))return Object(r.normalize)(l(e.fsPath));var m=Object(r.normalize)(e.fsPath);!a.g&&t&&(m=function(e,t){if(a.g||!e||!t)return e;var n=d.original===t?d.normalized:void 0;n||(n=""+Object(o.G)(t,r.posix.sep)+r.posix.sep,d={original:t,normalized:n});(a.c?Object(o.J)(e,n):Object(o.K)(e,n))&&(e="~/"+e.substr(n.length));return e}(m,t.userHome));return m},t.a=function(e){if(!e)return;"string"==typeof e&&(e=i.a.file(e));var t=Object(u.b)(e)||(e.scheme===s.b.file?e.fsPath:e.path);if(c(t))return l(t);return t};var i=n("mrx5"),r=n("/uRs"),o=n("aL7J"),s=n("lapT"),a=n("ZfGv"),u=n("ZYUE");function c(e){return!(!a.g||!e||":"!==e[1])}function l(e){return c(e)?e.charAt(0).toUpperCase()+e.slice(1):e}var d=Object.create(null)},"1xIj":function(e,t,n){"use strict";n.d(t,"a",function(){return r}),n.d(t,"b",function(){return o});var i=n("JVO/"),r=Object(i.c)("logService"),o=function(){function e(){}return e.prototype.trace=function(e){for(var t=[],n=1;n":""},l.prototype.isInfinity=function(){return 0===this.x.cmpn(0)&&(0===this.y.cmp(this.z)||this.zOne&&0===this.y.cmp(this.curve.c))},l.prototype._extDbl=function(){var e=this.x.redSqr(),t=this.y.redSqr(),n=this.z.redSqr();n=n.redIAdd(n);var i=this.curve._mulA(e),r=this.x.redAdd(this.y).redSqr().redISub(e).redISub(t),o=i.redAdd(t),s=o.redSub(n),a=i.redSub(t),u=r.redMul(s),c=o.redMul(a),l=r.redMul(a),d=s.redMul(o);return this.curve.point(u,c,d,l)},l.prototype._projDbl=function(){var e,t,n,i=this.x.redAdd(this.y).redSqr(),r=this.x.redSqr(),o=this.y.redSqr();if(this.curve.twisted){var s=(c=this.curve._mulA(r)).redAdd(o);if(this.zOne)e=i.redSub(r).redSub(o).redMul(s.redSub(this.curve.two)),t=s.redMul(c.redSub(o)),n=s.redSqr().redSub(s).redSub(s);else{var a=this.z.redSqr(),u=s.redSub(a).redISub(a);e=i.redSub(r).redISub(o).redMul(u),t=s.redMul(c.redSub(o)),n=s.redMul(u)}}else{var c=r.redAdd(o);a=this.curve._mulC(this.z).redSqr(),u=c.redSub(a).redSub(a);e=this.curve._mulC(i.redISub(c)).redMul(u),t=this.curve._mulC(c).redMul(r.redISub(o)),n=c.redMul(u)}return this.curve.point(e,t,n)},l.prototype.dbl=function(){return this.isInfinity()?this:this.curve.extended?this._extDbl():this._projDbl()},l.prototype._extAdd=function(e){var t=this.y.redSub(this.x).redMul(e.y.redSub(e.x)),n=this.y.redAdd(this.x).redMul(e.y.redAdd(e.x)),i=this.t.redMul(this.curve.dd).redMul(e.t),r=this.z.redMul(e.z.redAdd(e.z)),o=n.redSub(t),s=r.redSub(i),a=r.redAdd(i),u=n.redAdd(t),c=o.redMul(s),l=a.redMul(u),d=o.redMul(u),h=s.redMul(a);return this.curve.point(c,l,h,d)},l.prototype._projAdd=function(e){var t,n,i=this.z.redMul(e.z),r=i.redSqr(),o=this.x.redMul(e.x),s=this.y.redMul(e.y),a=this.curve.d.redMul(o).redMul(s),u=r.redSub(a),c=r.redAdd(a),l=this.x.redAdd(this.y).redMul(e.x.redAdd(e.y)).redISub(o).redISub(s),d=i.redMul(u).redMul(l);return this.curve.twisted?(t=i.redMul(c).redMul(s.redSub(this.curve._mulA(o))),n=u.redMul(c)):(t=i.redMul(c).redMul(s.redSub(o)),n=this.curve._mulC(u).redMul(c)),this.curve.point(d,t,n)},l.prototype.add=function(e){return this.isInfinity()?e:e.isInfinity()?this:this.curve.extended?this._extAdd(e):this._projAdd(e)},l.prototype.mul=function(e){return this._hasDoubles(e)?this.curve._fixedNafMul(this,e):this.curve._wnafMul(this,e)},l.prototype.mulAdd=function(e,t,n){return this.curve._wnafMulAdd(1,[this,t],[e,n],2,!1)},l.prototype.jmulAdd=function(e,t,n){return this.curve._wnafMulAdd(1,[this,t],[e,n],2,!0)},l.prototype.normalize=function(){if(this.zOne)return this;var e=this.z.redInvm();return this.x=this.x.redMul(e),this.y=this.y.redMul(e),this.t&&(this.t=this.t.redMul(e)),this.z=this.curve.one,this.zOne=!0,this},l.prototype.neg=function(){return this.curve.point(this.x.redNeg(),this.y,this.z,this.t&&this.t.redNeg())},l.prototype.getX=function(){return this.normalize(),this.x.fromRed()},l.prototype.getY=function(){return this.normalize(),this.y.fromRed()},l.prototype.eq=function(e){return this===e||0===this.getX().cmp(e.getX())&&0===this.getY().cmp(e.getY())},l.prototype.eqXToP=function(e){var t=e.toRed(this.curve.red).redMul(this.z);if(0===this.x.cmp(t))return!0;for(var n=e.clone(),i=this.curve.redN.redMul(this.z);;){if(n.iadd(this.curve.n),n.cmp(this.curve.p)>=0)return!1;if(t.redIAdd(i),0===this.x.cmp(t))return!0}},l.prototype.toP=l.prototype.normalize,l.prototype.mixedAdd=l.prototype.add},"2Ayt":function(e,t,n){"use strict";n.d(t,"a",function(){return u});var i=n("aL7J"),r=n("Ao9X"),o=n("6boo"),s=n("ZSmM"),a=n("vTy2"),u=function(){function e(){}return e.deleteRight=function(e,t,n,i){for(var o=[],u=3!==e,c=0,l=i.length;c1){var m=n.getLineContent(g.lineNumber),v=i.q(m),_=-1===v?m.length+1:v+1;if(g.column<=_){var b=o.a.visibleColumnFromColumn2(t,n,g),y=o.a.prevIndentTabStop(b,t.indentSize),w=o.a.columnFromVisibleColumn2(t,n,g.lineNumber,y);p=new a.a(g.lineNumber,w,g.lineNumber,g.column)}else p=new a.a(g.lineNumber,g.column-1,g.lineNumber,g.column)}else{var C=s.a.left(t,n,g.lineNumber,g.column);p=new a.a(C.lineNumber,C.column,g.lineNumber,g.column)}}p.isEmpty()?c[d]=null:(p.startLineNumber!==p.endLineNumber&&(l=!0),c[d]=new r.a(p,""))}return[l,c]},e.cut=function(e,t,n){for(var i=[],s=0,u=n.length;s1?(d=l.lineNumber-1,h=t.getLineMaxColumn(l.lineNumber-1),f=l.lineNumber,p=t.getLineMaxColumn(l.lineNumber)):(d=l.lineNumber,h=1,f=l.lineNumber,p=t.getLineMaxColumn(l.lineNumber));var g=new a.a(d,h,f,p);g.isEmpty()?i[s]=null:i[s]=new r.a(g,"")}else i[s]=null;else i[s]=new r.a(c,"")}return new o.e(0,i,{shouldPushStackElementBefore:!0,shouldPushStackElementAfter:!0})},e}()},"2JY6":function(e,t,n){(function(t){var n=Math.pow(2,30)-1;function i(e,n){if("string"!=typeof e&&!t.isBuffer(e))throw new TypeError(n+" must be a buffer or string")}e.exports=function(e,t,r,o){if(i(e,"Password"),i(t,"Salt"),"number"!=typeof r)throw new TypeError("Iterations not a number");if(r<0)throw new TypeError("Bad iterations");if("number"!=typeof o)throw new TypeError("Key length not a number");if(o<0||o>n||o!=o)throw new TypeError("Bad key length")}}).call(t,n("EuP9").Buffer)},"2LSJ":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n.d(t,"InsertCursorAbove",function(){return y}),n.d(t,"InsertCursorBelow",function(){return w}),n.d(t,"MultiCursorSessionResult",function(){return L}),n.d(t,"MultiCursorSession",function(){return O}),n.d(t,"MultiCursorSelectionController",function(){return k}),n.d(t,"MultiCursorSelectionControllerAction",function(){return N}),n.d(t,"AddSelectionToNextFindMatchAction",function(){return E}),n.d(t,"AddSelectionToPreviousFindMatchAction",function(){return I}),n.d(t,"MoveSelectionToNextFindMatchAction",function(){return D}),n.d(t,"MoveSelectionToPreviousFindMatchAction",function(){return M}),n.d(t,"SelectHighlightsAction",function(){return T}),n.d(t,"CompatChangeAll",function(){return P}),n.d(t,"SelectionHighlighter",function(){return R});var i,r=n("hK2W"),o=n("odeJ"),s=n("uNfg"),a=n("tqet"),u=n("03Zz"),c=n("HAT9"),l=n("vTy2"),d=n("iHM7"),h=n("/9db"),f=n("D2uo"),p=n("0ly5"),g=n("PCC9"),m=n("T1Qz"),v=n("L5KM"),_=n("eoic"),b=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),y=function(e){function t(){return e.call(this,{id:"editor.action.insertCursorAbove",label:r.a("mutlicursor.insertAbove","Add Cursor Above"),alias:"Add Cursor Above",precondition:void 0,kbOpts:{kbExpr:h.a.editorTextFocus,primary:2576,linux:{primary:1552,secondary:[3088]},weight:100},menubarOpts:{menuId:22,group:"3_multi",title:r.a({key:"miInsertCursorAbove",comment:["&& denotes a mnemonic"]},"&&Add Cursor Above"),order:2}})||this}return b(t,e),t.prototype.run=function(e,t,n){if(t.hasModel()){var i=n&&!0===n.logicalLine,r=t._getCursors(),o=r.context;o.config.readOnly||(o.model.pushStackElement(),r.setStates(n.source,3,c.b.addCursorUp(o,r.getAll(),i)),r.reveal(!0,1,0))}},t}(u.b),w=function(e){function t(){return e.call(this,{id:"editor.action.insertCursorBelow",label:r.a("mutlicursor.insertBelow","Add Cursor Below"),alias:"Add Cursor Below",precondition:void 0,kbOpts:{kbExpr:h.a.editorTextFocus,primary:2578,linux:{primary:1554,secondary:[3090]},weight:100},menubarOpts:{menuId:22,group:"3_multi",title:r.a({key:"miInsertCursorBelow",comment:["&& denotes a mnemonic"]},"A&&dd Cursor Below"),order:3}})||this}return b(t,e),t.prototype.run=function(e,t,n){if(t.hasModel()){var i=n&&!0===n.logicalLine,r=t._getCursors(),o=r.context;o.config.readOnly||(o.model.pushStackElement(),r.setStates(n.source,3,c.b.addCursorDown(o,r.getAll(),i)),r.reveal(!0,2,0))}},t}(u.b),C=function(e){function t(){return e.call(this,{id:"editor.action.insertCursorAtEndOfEachLineSelected",label:r.a("mutlicursor.insertAtEndOfEachLineSelected","Add Cursors to Line Ends"),alias:"Add Cursors to Line Ends",precondition:void 0,kbOpts:{kbExpr:h.a.editorTextFocus,primary:1575,weight:100},menubarOpts:{menuId:22,group:"3_multi",title:r.a({key:"miInsertCursorAtEndOfEachLineSelected",comment:["&& denotes a mnemonic"]},"Add C&&ursors to Line Ends"),order:4}})||this}return b(t,e),t.prototype.getCursorsForSelection=function(e,t,n){if(!e.isEmpty()){for(var i=e.startLineNumber;i1&&n.push(new d.a(e.endLineNumber,e.endColumn,e.endLineNumber,e.endColumn))}},t.prototype.run=function(e,t){var n=this;if(t.hasModel()){var i=t.getModel(),r=[];t.getSelections().forEach(function(e){return n.getCursorsForSelection(e,i,r)}),r.length>0&&t.setSelections(r)}},t}(u.b),S=function(e){function t(){return e.call(this,{id:"editor.action.addCursorsToBottom",label:r.a("mutlicursor.addCursorsToBottom","Add Cursors To Bottom"),alias:"Add Cursors To Bottom",precondition:void 0})||this}return b(t,e),t.prototype.run=function(e,t){if(t.hasModel()){for(var n=t.getSelections(),i=t.getModel().getLineCount(),r=[],o=n[0].startLineNumber;o<=i;o++)r.push(new d.a(o,n[0].startColumn,o,n[0].endColumn));r.length>0&&t.setSelections(r)}},t}(u.b),x=function(e){function t(){return e.call(this,{id:"editor.action.addCursorsToTop",label:r.a("mutlicursor.addCursorsToTop","Add Cursors To Top"),alias:"Add Cursors To Top",precondition:void 0})||this}return b(t,e),t.prototype.run=function(e,t){if(t.hasModel()){for(var n=t.getSelections(),i=[],r=n[0].startLineNumber;r>=1;r--)i.push(new d.a(r,n[0].startColumn,r,n[0].endColumn));i.length>0&&t.setSelections(i)}},t}(u.b),L=function(){return function(e,t,n){this.selections=e,this.revealRange=t,this.revealScrollType=n}}(),O=function(){function e(e,t,n,i,r,o,s){this._editor=e,this.findController=t,this.isDisconnectedFromFindController=n,this.searchText=i,this.wholeWord=r,this.matchCase=o,this.currentMatch=s}return e.create=function(t,n){if(!t.hasModel())return null;var i=n.getState();if(!t.hasTextFocus()&&i.isRevealed&&i.searchString.length>0)return new e(t,n,!1,i.searchString,i.wholeWord,i.matchCase,null);var r,o,s=!1,a=t.getSelections();1===a.length&&a[0].isEmpty()?(s=!0,r=!0,o=!0):(r=i.wholeWord,o=i.matchCase);var u,c=t.getSelection(),l=null;if(c.isEmpty()){var h=t.getModel().getWordAtPosition(c.getStartPosition());if(!h)return null;u=h.word,l=new d.a(c.startLineNumber,h.startColumn,c.startLineNumber,h.endColumn)}else u=t.getModel().getValueInRange(c).replace(/\r\n/g,"\n");return new e(t,n,s,u,r,o,l)},e.prototype.addSelectionToNextFindMatch=function(){if(!this._editor.hasModel())return null;var e=this._getNextMatch();if(!e)return null;var t=this._editor.getSelections();return new L(t.concat(e),e,0)},e.prototype.moveSelectionToNextFindMatch=function(){if(!this._editor.hasModel())return null;var e=this._getNextMatch();if(!e)return null;var t=this._editor.getSelections();return new L(t.slice(0,t.length-1).concat(e),e,0)},e.prototype._getNextMatch=function(){if(!this._editor.hasModel())return null;if(this.currentMatch){var e=this.currentMatch;return this.currentMatch=null,e}this.findController.highlightFindOptions();var t=this._editor.getSelections(),n=t[t.length-1],i=this._editor.getModel().findNextMatch(this.searchText,n.getEndPosition(),!1,this.matchCase,this.wholeWord?this._editor.getConfiguration().wordSeparators:null,!1);return i?new d.a(i.range.startLineNumber,i.range.startColumn,i.range.endLineNumber,i.range.endColumn):null},e.prototype.addSelectionToPreviousFindMatch=function(){if(!this._editor.hasModel())return null;var e=this._getPreviousMatch();if(!e)return null;var t=this._editor.getSelections();return new L(t.concat(e),e,0)},e.prototype.moveSelectionToPreviousFindMatch=function(){if(!this._editor.hasModel())return null;var e=this._getPreviousMatch();if(!e)return null;var t=this._editor.getSelections();return new L(t.slice(0,t.length-1).concat(e),e,0)},e.prototype._getPreviousMatch=function(){if(!this._editor.hasModel())return null;if(this.currentMatch){var e=this.currentMatch;return this.currentMatch=null,e}this.findController.highlightFindOptions();var t=this._editor.getSelections(),n=t[t.length-1],i=this._editor.getModel().findPreviousMatch(this.searchText,n.getStartPosition(),!1,this.matchCase,this.wholeWord?this._editor.getConfiguration().wordSeparators:null,!1);return i?new d.a(i.range.startLineNumber,i.range.startColumn,i.range.endLineNumber,i.range.endColumn):null},e.prototype.selectAll=function(){return this._editor.hasModel()?(this.findController.highlightFindOptions(),this._editor.getModel().findMatches(this.searchText,!0,!1,this.matchCase,this.wholeWord?this._editor.getConfiguration().wordSeparators:null,!1,1073741824)):[]},e}(),k=function(e){function t(t){var n=e.call(this)||this;return n._sessionDispose=n._register(new a.b),n._editor=t,n._ignoreSelectionChange=!1,n._session=null,n}return b(t,e),t.get=function(e){return e.getContribution(t.ID)},t.prototype.dispose=function(){this._endSession(),e.prototype.dispose.call(this)},t.prototype.getId=function(){return t.ID},t.prototype._beginSessionIfNeeded=function(e){var t=this;if(!this._session){var n=O.create(this._editor,e);if(!n)return;this._session=n;var i={searchString:this._session.searchText};this._session.isDisconnectedFromFindController&&(i.wholeWordOverride=1,i.matchCaseOverride=1,i.isRegexOverride=2),e.getState().change(i,!1),this._sessionDispose.add(this._editor.onDidChangeCursorSelection(function(e){t._ignoreSelectionChange||t._endSession()})),this._sessionDispose.add(this._editor.onDidBlurEditorText(function(){t._endSession()})),this._sessionDispose.add(e.getState().onFindReplaceStateChange(function(e){(e.matchCase||e.wholeWord)&&t._endSession()}))}},t.prototype._endSession=function(){if(this._sessionDispose.clear(),this._session&&this._session.isDisconnectedFromFindController){this._session.findController.getState().change({wholeWordOverride:0,matchCaseOverride:0,isRegexOverride:0},!1)}this._session=null},t.prototype._setSelections=function(e){this._ignoreSelectionChange=!0,this._editor.setSelections(e),this._ignoreSelectionChange=!1},t.prototype._expandEmptyToWord=function(e,t){if(!t.isEmpty())return t;var n=e.getWordAtPosition(t.getStartPosition());return n?new d.a(t.startLineNumber,n.startColumn,t.startLineNumber,n.endColumn):t},t.prototype._applySessionResult=function(e){e&&(this._setSelections(e.selections),e.revealRange&&this._editor.revealRangeInCenterIfOutsideViewport(e.revealRange,e.revealScrollType))},t.prototype.getSession=function(e){return this._session},t.prototype.addSelectionToNextFindMatch=function(e){if(this._editor.hasModel()){if(!this._session){var t=this._editor.getSelections();if(t.length>1){var n=e.getState().matchCase;if(!F(this._editor.getModel(),t,n)){for(var i=this._editor.getModel(),r=[],o=0,s=t.length;o0&&n.isRegex)t=this._editor.getModel().findMatches(n.searchString,!0,n.isRegex,n.matchCase,n.wholeWord?this._editor.getConfiguration().wordSeparators:null,!1,1073741824);else{if(this._beginSessionIfNeeded(e),!this._session)return;t=this._session.selectAll()}if(t.length>0){for(var i=this._editor.getSelection(),r=0,o=t.length;r1){var a=r.getState().matchCase;if(!F(t.getModel(),s,a))return null}o=O.create(t,r)}if(!o)return null;if(o.currentMatch)return null;if(/^[ \t]+$/.test(o.searchText))return null;if(o.searchText.length>200)return null;var u=r.getState(),c=u.matchCase;if(u.isRevealed){var l=u.searchString;c||(l=l.toLowerCase());var d=o.searchText;if(c||(d=d.toLowerCase()),l===d&&o.matchCase===u.matchCase&&o.wholeWord===u.wholeWord&&!u.isRegex)return null}return new A(o.searchText,o.matchCase,o.wholeWord?t.getConfiguration().wordSeparators:null)},t.prototype._setState=function(e){if(A.softEquals(this.state,e))this.state=e;else if(this.state=e,this.state){if(this.editor.hasModel()){var n=this.editor.getModel();if(!n.isTooLargeForTokenization()){var i=g.i.has(n),r=n.findMatches(this.state.searchText,!0,!1,this.state.matchCase,this.state.wordSeparators,!1).map(function(e){return e.range});r.sort(l.a.compareRangesUsingStarts);var o=this.editor.getSelections();o.sort(l.a.compareRangesUsingStarts);for(var s=[],a=0,u=0,c=r.length,d=o.length;a=d)s.push(h),a++;else{var f=l.a.compareRangesUsingStarts(h,o[u]);f<0?(!o[u].isEmpty()&&l.a.areIntersecting(h,o[u])||s.push(h),a++):f>0?u++:(a++,u++)}}var p=s.map(function(e){return{range:e,options:i?t._SELECTION_HIGHLIGHT:t._SELECTION_HIGHLIGHT_OVERVIEW}});this.decorations=this.editor.deltaDecorations(this.decorations,p)}}}else this.decorations=this.editor.deltaDecorations(this.decorations,[])},t.prototype.dispose=function(){this._setState(null),e.prototype.dispose.call(this)},t.ID="editor.contrib.selectionHighlighter",t._SELECTION_HIGHLIGHT_OVERVIEW=p.a.register({stickiness:1,className:"selectionHighlight",overviewRuler:{color:Object(_.g)(v._32),position:f.d.Center}}),t._SELECTION_HIGHLIGHT=p.a.register({stickiness:1,className:"selectionHighlight"}),t}(a.a);function F(e,t,n){for(var i=j(e,t[0],!n),r=1,o=t.length;r=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},w=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},C=Object(v._36)("editor.wordHighlightBackground",{dark:"#575757B8",light:"#57575740",hc:null},r.a("wordHighlight","Background color of a symbol during read-access, like reading a variable. The color must not be opaque so as not to hide underlying decorations."),!0),S=Object(v._36)("editor.wordHighlightStrongBackground",{dark:"#004972B8",light:"#0e639c40",hc:null},r.a("wordHighlightStrong","Background color of a symbol during write-access, like writing to a variable. The color must not be opaque so as not to hide underlying decorations."),!0),x=Object(v._36)("editor.wordHighlightBorder",{light:null,dark:null,hc:v.b},r.a("wordHighlightBorder","Border color of a symbol during read-access, like reading a variable.")),L=Object(v._36)("editor.wordHighlightStrongBorder",{light:null,dark:null,hc:v.b},r.a("wordHighlightStrongBorder","Border color of a symbol during write-access, like writing to a variable.")),O=Object(v._36)("editorOverviewRuler.wordHighlightForeground",{dark:"#A0A0A0CC",light:"#A0A0A0CC",hc:"#A0A0A0CC"},r.a("overviewRulerWordHighlightForeground","Overview ruler marker color for symbol highlights. The color must not be opaque so as not to hide underlying decorations."),!0),k=Object(v._36)("editorOverviewRuler.wordHighlightStrongForeground",{dark:"#C0A0C0CC",light:"#C0A0C0CC",hc:"#C0A0C0CC"},r.a("overviewRulerWordHighlightStrongForeground","Overview ruler marker color for write-access symbol highlights. The color must not be opaque so as not to hide underlying decorations."),!0),N=new m.d("hasWordHighlights",!1);function E(e,t,n){var i=g.i.ordered(e);return Object(s.h)(i.map(function(i){return function(){return Promise.resolve(i.provideDocumentHighlights(e,t,n)).then(void 0,u.f)}}),o.n)}var I=function(){function e(e,t,n){var i=this;this._wordRange=this._getCurrentWordRange(e,t),this.result=Object(s.f)(function(r){return i._compute(e,t,n,r)})}return e.prototype._getCurrentWordRange=function(e,t){var n=e.getWordAtPosition(t.getPosition());return n?new d.a(t.startLineNumber,n.startColumn,t.startLineNumber,n.endColumn):null},e.prototype.isValid=function(e,t,n){for(var i=t.startLineNumber,r=t.startColumn,o=t.endColumn,s=this._getCurrentWordRange(e,t),a=Boolean(this._wordRange&&this._wordRange.equalsRange(s)),u=0,c=n.length;!a&&u=o&&(a=!0)}return a},e.prototype.cancel=function(){this.result.cancel()},e}(),D=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return b(t,e),t.prototype._compute=function(e,t,n,i){return E(e,t.getPosition(),i).then(function(e){return e||[]})},t}(I),M=function(e){function t(t,n,i){var r=e.call(this,t,n,i)||this;return r._selectionIsEmpty=n.isEmpty(),r}return b(t,e),t.prototype._compute=function(e,t,n,i){return Object(s.l)(250,i).then(function(){if(!t.isEmpty())return[];var i=e.getWordAtPosition(t.getPosition());return i?e.findMatches(i.word,!0,!1,!0,n,!1).map(function(e){return{range:e.range,kind:g.h.Text}}):[]})},t.prototype.isValid=function(t,n,i){var r=n.isEmpty();return this._selectionIsEmpty===r&&e.prototype.isValid.call(this,t,n,i)},t}(I);Object(l.e)("_executeDocumentHighlights",function(e,t){return E(e,t,a.a.None)});var T=function(){function e(e,t){var n=this;this.toUnhook=new c.b,this.workerRequestTokenId=0,this.workerRequestCompleted=!1,this.workerRequestValue=[],this.lastCursorPositionChangeTime=0,this.renderDecorationsTimer=-1,this.editor=e,this._hasWordHighlights=N.bindTo(t),this._ignorePositionChangeEvent=!1,this.occurrencesHighlight=this.editor.getConfiguration().contribInfo.occurrencesHighlight,this.model=this.editor.getModel(),this.toUnhook.add(e.onDidChangeCursorPosition(function(e){n._ignorePositionChangeEvent||n.occurrencesHighlight&&n._onPositionChanged(e)})),this.toUnhook.add(e.onDidChangeModelContent(function(e){n._stopAll()})),this.toUnhook.add(e.onDidChangeConfiguration(function(e){var t=n.editor.getConfiguration().contribInfo.occurrencesHighlight;n.occurrencesHighlight!==t&&(n.occurrencesHighlight=t,n._stopAll())})),this._decorationIds=[],this.workerRequestTokenId=0,this.workerRequest=null,this.workerRequestCompleted=!1,this.lastCursorPositionChangeTime=0,this.renderDecorationsTimer=-1}return e.prototype.hasDecorations=function(){return this._decorationIds.length>0},e.prototype.restore=function(){this.occurrencesHighlight&&this._run()},e.prototype._getSortedHighlights=function(){var e=this;return o.d(this._decorationIds.map(function(t){return e.model.getDecorationRange(t)}).sort(d.a.compareRangesUsingStarts))},e.prototype.moveNext=function(){var e=this,t=this._getSortedHighlights(),n=t[(o.j(t,function(t){return t.containsPosition(e.editor.getPosition())})+1)%t.length];try{this._ignorePositionChangeEvent=!0,this.editor.setPosition(n.getStartPosition()),this.editor.revealRangeInCenterIfOutsideViewport(n)}finally{this._ignorePositionChangeEvent=!1}},e.prototype.moveBack=function(){var e=this,t=this._getSortedHighlights(),n=t[(o.j(t,function(t){return t.containsPosition(e.editor.getPosition())})-1+t.length)%t.length];try{this._ignorePositionChangeEvent=!0,this.editor.setPosition(n.getStartPosition()),this.editor.revealRangeInCenterIfOutsideViewport(n)}finally{this._ignorePositionChangeEvent=!1}},e.prototype._removeDecorations=function(){this._decorationIds.length>0&&(this._decorationIds=this.editor.deltaDecorations(this._decorationIds,[]),this._hasWordHighlights.set(!1))},e.prototype._stopAll=function(){this._removeDecorations(),-1!==this.renderDecorationsTimer&&(clearTimeout(this.renderDecorationsTimer),this.renderDecorationsTimer=-1),null!==this.workerRequest&&(this.workerRequest.cancel(),this.workerRequest=null),this.workerRequestCompleted||(this.workerRequestTokenId++,this.workerRequestCompleted=!0)},e.prototype._onPositionChanged=function(e){this.occurrencesHighlight&&3===e.reason?this._run():this._stopAll()},e.prototype._run=function(){var e=this,t=this.editor.getSelection();if(t.startLineNumber===t.endLineNumber){var n=t.startLineNumber,i=t.startColumn,r=t.endColumn,o=this.model.getWordAtPosition({lineNumber:n,column:i});if(!o||o.startColumn>i||o.endColumn=n?(this.renderDecorationsTimer=-1,this.renderDecorations()):this.renderDecorationsTimer=setTimeout(function(){e.renderDecorations()},n-t)},e.prototype.renderDecorations=function(){this.renderDecorationsTimer=-1;for(var t=[],n=0,i=this.workerRequestValue.length;n=6?"utf-8":"binary";e.exports=n}).call(t,n("W2nU"))},"3Clc":function(e,t){},"3PYz":function(e,t,n){var i=t;i.utils=n("1lLf"),i.common=n("YSDb"),i.sha=n("NCTB"),i.ripemd=n("CKAI"),i.hmac=n("3kRU"),i.sha1=i.sha.sha1,i.sha256=i.sha.sha256,i.sha224=i.sha.sha224,i.sha384=i.sha.sha384,i.sha512=i.sha.sha512,i.ripemd160=i.ripemd.ripemd160},"3UJ8":function(e,t,n){"use strict";n.d(t,"a",function(){return i});var i=function(){function e(){for(var e=[],t=0;te;)n.ishrn(1);if(n.isEven()&&n.iadd(a),n.testn(1)||n.iadd(u),t.cmp(u)){if(!t.cmp(c))for(;n.mod(l).cmp(d);)n.iadd(f)}else for(;n.mod(o).cmp(h);)n.iadd(f);if(m(p=n.shrn(1))&&m(n)&&v(p)&&v(n)&&s.test(p)&&s.test(n))return n}}},"3j2o":function(e,t){},"3kRU":function(e,t,n){"use strict";var i=n("1lLf"),r=n("08Lv");function o(e,t,n){if(!(this instanceof o))return new o(e,t,n);this.Hash=e,this.blockSize=e.blockSize/8,this.outSize=e.outSize/8,this.inner=null,this.outer=null,this._init(i.toArray(t,n))}e.exports=o,o.prototype._init=function(e){e.length>this.blockSize&&(e=(new this.Hash).update(e).digest()),r(e.length<=this.blockSize);for(var t=e.length;t>>3},t.g1_256=function(e){return i(e,17)^i(e,19)^e>>>10}},"3uSZ":function(e,t,n){"use strict";n.d(t,"a",function(){return r}),t.b=function e(t){return o(t)?!t.value:!Array.isArray(t)||t.every(e)},t.c=function(e,t){return!e&&!t||!(!e||!t)&&(Array.isArray(e)&&Array.isArray(t)?Object(i.g)(e,t,s):!(!o(e)||!o(t))&&s(e,t))},t.e=function(e){if(!e)return e;return e.replace(/\\([\\`*_{}[\]()#+\-.!])/g,"$1")},t.d=function(e){var t=[],n=e.split("|").map(function(e){return e.trim()});e=n[0];var i=n[1];if(i){var r=/height=(\d+)/.exec(i),o=/width=(\d+)/.exec(i),s=r?r[1]:"",a=o?o[1]:"",u=isFinite(parseInt(a)),c=isFinite(parseInt(s));u&&t.push('width="'+a+'"'),c&&t.push('height="'+s+'"')}return{href:e,dimensions:t}};var i=n("X6iQ"),r=function(){function e(e){void 0===e&&(e=""),this.value=e}return e.prototype.appendText=function(e){return this.value+=e.replace(/[\\`*_{}[\]()#+\-.!]/g,"\\$&"),this},e.prototype.appendMarkdown=function(e){return this.value+=e,this},e.prototype.appendCodeblock=function(e,t){return this.value+="\n```",this.value+=e,this.value+="\n",this.value+=t,this.value+="\n```\n",this},e}();function o(e){return e instanceof r||!(!e||"object"!=typeof e)&&("string"==typeof e.value&&("boolean"==typeof e.isTrusted||void 0===e.isTrusted))}function s(e,t){return e===t||!(!e||!t)&&(e.value===t.value&&e.isTrusted===t.isTrusted)}},4:function(e,t){},"4/4u":function(e,t,n){e.exports=n("cSWu").Transform},"44YW":function(e,t,n){"use strict";n.d(t,"a",function(){return r});var i=n("JVO/"),r=Object(i.c)("clipboardService")},"4JIo":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i,r=n("3j2o"),o=(n.n(r),n("hK2W")),s=n("lAcG"),a=n("ZfGv"),u=n("4QaN"),c=n("03Zz"),l=n("vORD"),d=n("/9db"),h=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),f="9_cutcopypaste",p=a.e||document.queryCommandSupported("cut"),g=a.e||document.queryCommandSupported("copy"),m=g&&!s.g,v=a.e||!s.e&&document.queryCommandSupported("paste"),_=function(e){function t(t,n){var i=e.call(this,n)||this;return i.browserCommand=t,i}return h(t,e),t.prototype.runCommand=function(e,t){var n=e.get(l.a).getFocusedCodeEditor();n&&n.hasTextFocus()?n.trigger("keyboard",this.id,t):document.execCommand(this.browserCommand)},t.prototype.run=function(e,t){t.focus(),document.execCommand(this.browserCommand)},t}(c.b),b=function(e){function t(){var t={kbExpr:d.a.textInputFocus,primary:2102,win:{primary:2102,secondary:[1044]},weight:100};return a.e||(t=void 0),e.call(this,"cut",{id:"editor.action.clipboardCutAction",label:o.a("actions.clipboard.cutLabel","Cut"),alias:"Cut",precondition:d.a.writable,kbOpts:t,menuOpts:{group:f,order:1},menubarOpts:{menuId:14,group:"2_ccp",title:o.a({key:"miCut",comment:["&& denotes a mnemonic"]},"Cu&&t"),order:1}})||this}return h(t,e),t.prototype.run=function(t,n){n.hasModel()&&(!n.getConfiguration().emptySelectionClipboard&&n.getSelection().isEmpty()||e.prototype.run.call(this,t,n))},t}(_),y=function(e){function t(){var t={kbExpr:d.a.textInputFocus,primary:2081,win:{primary:2081,secondary:[2067]},weight:100};return a.e||(t=void 0),e.call(this,"copy",{id:"editor.action.clipboardCopyAction",label:o.a("actions.clipboard.copyLabel","Copy"),alias:"Copy",precondition:void 0,kbOpts:t,menuOpts:{group:f,order:2},menubarOpts:{menuId:14,group:"2_ccp",title:o.a({key:"miCopy",comment:["&& denotes a mnemonic"]},"&&Copy"),order:2}})||this}return h(t,e),t.prototype.run=function(t,n){n.hasModel()&&(!n.getConfiguration().emptySelectionClipboard&&n.getSelection().isEmpty()||e.prototype.run.call(this,t,n))},t}(_),w=function(e){function t(){var t={kbExpr:d.a.textInputFocus,primary:2100,win:{primary:2100,secondary:[1043]},weight:100};return a.e||(t=void 0),e.call(this,"paste",{id:"editor.action.clipboardPasteAction",label:o.a("actions.clipboard.pasteLabel","Paste"),alias:"Paste",precondition:d.a.writable,kbOpts:t,menuOpts:{group:f,order:3},menubarOpts:{menuId:14,group:"2_ccp",title:o.a({key:"miPaste",comment:["&& denotes a mnemonic"]},"&&Paste"),order:3}})||this}return h(t,e),t}(_),C=function(e){function t(){return e.call(this,"copy",{id:"editor.action.clipboardCopyWithSyntaxHighlightingAction",label:o.a("actions.clipboard.copyWithSyntaxHighlightingLabel","Copy With Syntax Highlighting"),alias:"Copy With Syntax Highlighting",precondition:void 0,kbOpts:{kbExpr:d.a.textInputFocus,primary:0,weight:100}})||this}return h(t,e),t.prototype.run=function(t,n){n.hasModel()&&(!n.getConfiguration().emptySelectionClipboard&&n.getSelection().isEmpty()||(u.a.forceCopyWithSyntaxHighlighting=!0,e.prototype.run.call(this,t,n),u.a.forceCopyWithSyntaxHighlighting=!1))},t}(_);p&&Object(c.f)(b),g&&Object(c.f)(y),v&&Object(c.f)(w),m&&Object(c.f)(C)},"4QaN":function(e,t,n){"use strict";n.d(t,"a",function(){return p}),n.d(t,"b",function(){return g});var i,r=n("lAcG"),o=n("7/Cv"),s=n("odeJ"),a=n("Kp7x"),u=n("tqet"),c=n("ZfGv"),l=n("aL7J"),d=n("ZWAj"),h=n("iHM7"),f=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),p={forceCopyWithSyntaxHighlighting:!1},g=function(e){function t(t,n){var i=e.call(this)||this;i._onFocus=i._register(new a.a),i.onFocus=i._onFocus.event,i._onBlur=i._register(new a.a),i.onBlur=i._onBlur.event,i._onKeyDown=i._register(new a.a),i.onKeyDown=i._onKeyDown.event,i._onKeyUp=i._register(new a.a),i.onKeyUp=i._onKeyUp.event,i._onCut=i._register(new a.a),i.onCut=i._onCut.event,i._onPaste=i._register(new a.a),i.onPaste=i._onPaste.event,i._onType=i._register(new a.a),i.onType=i._onType.event,i._onCompositionStart=i._register(new a.a),i.onCompositionStart=i._onCompositionStart.event,i._onCompositionUpdate=i._register(new a.a),i.onCompositionUpdate=i._onCompositionUpdate.event,i._onCompositionEnd=i._register(new a.a),i.onCompositionEnd=i._onCompositionEnd.event,i._onSelectionChangeRequest=i._register(new a.a),i.onSelectionChangeRequest=i._onSelectionChangeRequest.event,i._host=t,i._textArea=i._register(new v(n)),i._lastTextAreaEvent=0,i._asyncTriggerCut=i._register(new s.d(function(){return i._onCut.fire()},0)),i._textAreaState=d.b.EMPTY,i._selectionChangeListener=null,i.writeScreenReaderContent("ctor"),i._hasFocus=!1,i._isDoingComposition=!1,i._nextCommand=0,i._register(o.k(n.domNode,"keydown",function(e){!i._isDoingComposition||109!==e.keyCode&&1!==e.keyCode||e.stopPropagation(),e.equals(9)&&e.preventDefault(),i._onKeyDown.fire(e)})),i._register(o.k(n.domNode,"keyup",function(e){i._onKeyUp.fire(e)})),i._register(o.h(n.domNode,"compositionstart",function(e){i._lastTextAreaEvent=1,i._isDoingComposition||(i._isDoingComposition=!0,r.g||i._setAndWriteTextAreaState("compositionstart",d.b.EMPTY),i._onCompositionStart.fire())}));var u=function(e,t){var n=i._textAreaState,r=d.b.readFromTextArea(i._textArea);return[r,d.b.deduceInput(n,r,e,t)]},h=function(e){var t=i._textAreaState,n=d.b.selectedText(e);return[n,{text:n.value,replaceCharCnt:t.selectionEnd-t.selectionStart}]},f=function(e){return!(!r.g||"ja"!==e)||!(!r.j||0!==e.indexOf("zh-Han"))};return i._register(o.h(n.domNode,"compositionupdate",function(e){if(i._lastTextAreaEvent=2,f(e.locale)){var t=u(!1,!1),n=t[0],r=t[1];return i._textAreaState=n,i._onType.fire(r),void i._onCompositionUpdate.fire(e)}var o=h(e.data),s=o[0],a=o[1];i._textAreaState=s,i._onType.fire(a),i._onCompositionUpdate.fire(e)})),i._register(o.h(n.domNode,"compositionend",function(e){if(i._lastTextAreaEvent=3,f(e.locale)){var t=u(!1,!1),n=t[0],o=t[1];i._textAreaState=n,i._onType.fire(o)}else{var s=h(e.data);n=s[0],o=s[1];i._textAreaState=n,i._onType.fire(o)}(r.g||r.e)&&(i._textAreaState=d.b.readFromTextArea(i._textArea)),i._isDoingComposition&&(i._isDoingComposition=!1,i._onCompositionEnd.fire())})),i._register(o.h(n.domNode,"input",function(){var e=8===i._lastTextAreaEvent;if(i._lastTextAreaEvent=4,i._textArea.setIgnoreSelectionChangeTime("received input event"),!i._isDoingComposition){var t=u(c.d,e&&c.d),n=t[0],r=t[1];0===r.replaceCharCnt&&1===r.text.length&&l.w(r.text.charCodeAt(0))||(i._textAreaState=n,0===i._nextCommand?""!==r.text&&i._onType.fire(r):(""!==r.text&&i._onPaste.fire({text:r.text}),i._nextCommand=0))}})),i._register(o.h(n.domNode,"cut",function(e){i._lastTextAreaEvent=5,i._textArea.setIgnoreSelectionChangeTime("received cut event"),i._ensureClipboardGetsEditorSelection(e),i._asyncTriggerCut.schedule()})),i._register(o.h(n.domNode,"copy",function(e){i._lastTextAreaEvent=6,i._ensureClipboardGetsEditorSelection(e)})),i._register(o.h(n.domNode,"paste",function(e){if(i._lastTextAreaEvent=7,i._textArea.setIgnoreSelectionChangeTime("received paste event"),m.canUseTextData(e)){var t=m.getTextData(e);""!==t&&i._onPaste.fire({text:t})}else i._textArea.getSelectionStart()!==i._textArea.getSelectionEnd()&&i._setAndWriteTextAreaState("paste",d.b.EMPTY),i._nextCommand=1})),i._register(o.h(n.domNode,"focus",function(){i._lastTextAreaEvent=8,i._setHasFocus(!0)})),i._register(o.h(n.domNode,"blur",function(){i._lastTextAreaEvent=9,i._setHasFocus(!1)})),i}return f(t,e),t.prototype._installSelectionChangeListener=function(){var e=this,t=0;return o.h(document,"selectionchange",function(n){if(e._hasFocus&&!e._isDoingComposition&&r.e&&c.g){var i=Date.now(),o=i-t;if(t=i,!(o<5)){var s=i-e._textArea.getIgnoreSelectionChangeTime();if(e._textArea.resetSelectionChangeTime(),!(s<100)&&e._textAreaState.selectionStartPosition&&e._textAreaState.selectionEndPosition){var a=e._textArea.getValue();if(e._textAreaState.value===a){var u=e._textArea.getSelectionStart(),l=e._textArea.getSelectionEnd();if(e._textAreaState.selectionStart!==u||e._textAreaState.selectionEnd!==l){var d=e._textAreaState.deduceEditorPosition(u),f=e._host.deduceModelPosition(d[0],d[1],d[2]),p=e._textAreaState.deduceEditorPosition(l),g=e._host.deduceModelPosition(p[0],p[1],p[2]),m=new h.a(f.lineNumber,f.column,g.lineNumber,g.column);e._onSelectionChangeRequest.fire(m)}}}}}})},t.prototype.dispose=function(){e.prototype.dispose.call(this),this._selectionChangeListener&&(this._selectionChangeListener.dispose(),this._selectionChangeListener=null)},t.prototype.focusTextArea=function(){this._setHasFocus(!0)},t.prototype.isFocused=function(){return this._hasFocus},t.prototype._setHasFocus=function(e){this._hasFocus!==e&&(this._hasFocus=e,this._selectionChangeListener&&(this._selectionChangeListener.dispose(),this._selectionChangeListener=null),this._hasFocus&&(this._selectionChangeListener=this._installSelectionChangeListener()),this._hasFocus&&(r.f?this._setAndWriteTextAreaState("focusgain",d.b.EMPTY):this.writeScreenReaderContent("focusgain")),this._hasFocus?this._onFocus.fire():this._onBlur.fire())},t.prototype._setAndWriteTextAreaState=function(e,t){this._hasFocus||(t=t.collapseSelection()),t.writeToTextArea(e,this._textArea,this._hasFocus),this._textAreaState=t},t.prototype.writeScreenReaderContent=function(e){this._isDoingComposition||this._setAndWriteTextAreaState(e,this._host.getScreenReaderContent(this._textAreaState))},t.prototype._ensureClipboardGetsEditorSelection=function(e){var t=this._host.getPlainTextToCopy();if(m.canUseTextData(e)){var n=null;r.d()&&(t.length<65536||p.forceCopyWithSyntaxHighlighting)&&(n=this._host.getHTMLToCopy()),m.setTextData(e,t,n)}else this._setAndWriteTextAreaState("copy or cut",d.b.selectedText(t))},t}(u.a),m=function(){function e(){}return e.canUseTextData=function(e){return!!e.clipboardData||!!window.clipboardData},e.getTextData=function(e){if(e.clipboardData)return e.preventDefault(),e.clipboardData.getData("text/plain");if(window.clipboardData)return e.preventDefault(),window.clipboardData.getData("Text");throw new Error("ClipboardEventUtils.getTextData: Cannot use text data!")},e.setTextData=function(e,t,n){if(e.clipboardData)return e.clipboardData.setData("text/plain",t),null!==n&&e.clipboardData.setData("text/html",n),void e.preventDefault();if(window.clipboardData)return window.clipboardData.setData("Text",t),void e.preventDefault();throw new Error("ClipboardEventUtils.setTextData: Cannot use text data!")},e}(),v=function(e){function t(t){var n=e.call(this)||this;return n._actual=t,n._ignoreSelectionChangeTime=0,n}return f(t,e),t.prototype.setIgnoreSelectionChangeTime=function(e){this._ignoreSelectionChangeTime=Date.now()},t.prototype.getIgnoreSelectionChangeTime=function(){return this._ignoreSelectionChangeTime},t.prototype.resetSelectionChangeTime=function(){this._ignoreSelectionChangeTime=0},t.prototype.getValue=function(){return this._actual.domNode.value},t.prototype.setValue=function(e,t){var n=this._actual.domNode;n.value!==t&&(this.setIgnoreSelectionChangeTime("setValue"),n.value=t)},t.prototype.getSelectionStart=function(){return this._actual.domNode.selectionStart},t.prototype.getSelectionEnd=function(){return this._actual.domNode.selectionEnd},t.prototype.setSelectionRange=function(e,t,n){var i=this._actual.domNode,s=document.activeElement===i,a=i.selectionStart,u=i.selectionEnd;if(s&&a===t&&u===n)r.i&&window.parent!==window&&i.focus();else{if(s)return this.setIgnoreSelectionChangeTime("setSelectionRange"),i.setSelectionRange(t,n),void(r.i&&window.parent!==window&&i.focus());try{var c=o.O(i);this.setIgnoreSelectionChangeTime("setSelectionRange"),i.focus(),i.setSelectionRange(t,n),o.M(i,c)}catch(e){}}},t}(u.a)},"4R/o":function(e,t,n){"use strict";(function(e,i){function r(){throw new Error("secure random number generation not supported by this browser\nuse chrome, FireFox or Internet Explorer 11")}var o=n("X3l8"),s=n("rOku"),a=o.Buffer,u=o.kMaxLength,c=e.crypto||e.msCrypto,l=Math.pow(2,32)-1;function d(e,t){if("number"!=typeof e||e!=e)throw new TypeError("offset must be a number");if(e>l||e<0)throw new TypeError("offset must be a uint32");if(e>u||e>t)throw new RangeError("offset out of range")}function h(e,t,n){if("number"!=typeof e||e!=e)throw new TypeError("size must be a number");if(e>l||e<0)throw new TypeError("size must be a uint32");if(e+t>n||e>u)throw new RangeError("buffer too small")}function f(e,t,n,r){if(i.browser){var o=e.buffer,a=new Uint8Array(o,t,n);return c.getRandomValues(a),r?void i.nextTick(function(){r(null,e)}):e}if(!r)return s(n).copy(e,t),e;s(n,function(n,i){if(n)return r(n);i.copy(e,t),r(null,e)})}c&&c.getRandomValues||!i.browser?(t.randomFill=function(t,n,i,r){if(!(a.isBuffer(t)||t instanceof e.Uint8Array))throw new TypeError('"buf" argument must be a Buffer or Uint8Array');if("function"==typeof n)r=n,n=0,i=t.length;else if("function"==typeof i)r=i,i=t.length-n;else if("function"!=typeof r)throw new TypeError('"cb" argument must be a function');return d(n,t.length),h(i,n,t.length),f(t,n,i,r)},t.randomFillSync=function(t,n,i){void 0===n&&(n=0);if(!(a.isBuffer(t)||t instanceof e.Uint8Array))throw new TypeError('"buf" argument must be a Buffer or Uint8Array');d(n,t.length),void 0===i&&(i=t.length-n);return h(i,n,t.length),f(t,n,i)}):(t.randomFill=r,t.randomFillSync=r)}).call(t,n("DuR2"),n("W2nU"))},"4Vh3":function(e,t){e.exports={modp1:{gen:"02",prime:"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a63a3620ffffffffffffffff"},modp2:{gen:"02",prime:"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece65381ffffffffffffffff"},modp5:{gen:"02",prime:"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca237327ffffffffffffffff"},modp14:{gen:"02",prime:"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff"},modp15:{gen:"02",prime:"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a93ad2caffffffffffffffff"},modp16:{gen:"02",prime:"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c934063199ffffffffffffffff"},modp17:{gen:"02",prime:"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec9751e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a8918da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a84031900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7474d6e694f91e6dcc4024ffffffffffffffff"},modp18:{gen:"02",prime:"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec9751e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a8918da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a84031900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7474d6e694f91e6dbe115974a3926f12fee5e438777cb6a932df8cd8bec4d073b931ba3bc832b68d9dd300741fa7bf8afc47ed2576f6936ba424663aab639c5ae4f5683423b4742bf1c978238f16cbe39d652de3fdb8befc848ad922222e04a4037c0713eb57a81a23f0c73473fc646cea306b4bcbc8862f8385ddfa9d4b7fa2c087e879683303ed5bdd3a062b3cf5b3a278a66d2a13f83f44f82ddf310ee074ab6a364597e899a0255dc164f31cc50846851df9ab48195ded7ea1b1d510bd7ee74d73faf36bc31ecfa268359046f4eb879f924009438b481c6cd7889a002ed5ee382bc9190da6fc026e479558e4475677e9aa9e3050e2765694dfc81f56e880b96e7160c980dd98edd3dfffffffffffffffff"}}},"4Yhh":function(e,t){},"4sPJ":function(e,t){e.exports=function(e){for(var t,n=e.length;n--;){if(255!==(t=e.readUInt8(n))){t++,e.writeUInt8(t,n);break}e.writeUInt8(0,n)}}},"4tuZ":function(e,t,n){"use strict";var i,r=n("aL7J"),o=n("80kS"),s=n("tqet"),a=n("03Zz"),u=n("7g0X"),c=n("EMhq"),l=n("JVO/"),d=n("8xpx"),h=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),f=Object(l.c)("IEditorCancelService"),p=new u.d("cancellableOperation",!1);Object(d.b)(f,function(){function e(){this._tokens=new WeakMap}return e.prototype.add=function(e,t){var n,i=this._tokens.get(e);return i||(i=e.invokeWithinContext(function(e){return{key:p.bindTo(e.get(u.c)),tokens:new c.a}}),this._tokens.set(e,i)),i.key.set(!0),n=i.tokens.push(t),function(){n&&(n(),i.key.set(!i.tokens.isEmpty()),n=void 0)}},e.prototype.cancel=function(e){var t=this._tokens.get(e);if(t){var n=t.tokens.pop();n&&(n.cancel(),t.key.set(!t.tokens.isEmpty()))}},e}(),!0);var g=function(e){function t(t,n){var i=e.call(this,n)||this;return i.editor=t,i._unregister=t.invokeWithinContext(function(e){return e.get(f).add(t,i)}),i}return h(t,e),t.prototype.dispose=function(){this._unregister(),e.prototype.dispose.call(this)},t}(o.b);Object(a.g)(new(function(e){function t(){return e.call(this,{id:"editor.cancelOperation",kbOpts:{weight:100,primary:9},precondition:p})||this}return h(t,e),t.prototype.runEditorCommand=function(e,t){e.get(f).cancel(t)},t}(a.c))),n.d(t,"a",function(){return v}),n.d(t,"b",function(){return _}),n.d(t,"d",function(){return b}),n.d(t,"c",function(){return y});var m=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),v=function(){function e(e,t){if(this.flags=t,0!=(1&this.flags)){var n=e.getModel();this.modelVersionId=n?r.r("{0}#{1}",n.uri.toString(),n.getVersionId()):null}else this.modelVersionId=null;0!=(4&this.flags)?this.position=e.getPosition():this.position=null,0!=(2&this.flags)?this.selection=e.getSelection():this.selection=null,0!=(8&this.flags)?(this.scrollLeft=e.getScrollLeft(),this.scrollTop=e.getScrollTop()):(this.scrollLeft=-1,this.scrollTop=-1)}return e.prototype._equals=function(t){if(!(t instanceof e))return!1;var n=t;return this.modelVersionId===n.modelVersionId&&(this.scrollLeft===n.scrollLeft&&this.scrollTop===n.scrollTop&&(!(!this.position&&n.position||this.position&&!n.position||this.position&&n.position&&!this.position.equals(n.position))&&!(!this.selection&&n.selection||this.selection&&!n.selection||this.selection&&n.selection&&!this.selection.equalsRange(n.selection))))},e.prototype.validate=function(t){return this._equals(new e(t,this.flags))},e}(),_=function(e){function t(t,n,i){var r=e.call(this,t,i)||this;return r.editor=t,r._listener=new s.b,4&n&&r._listener.add(t.onDidChangeCursorPosition(function(e){return r.cancel()})),2&n&&r._listener.add(t.onDidChangeCursorSelection(function(e){return r.cancel()})),8&n&&r._listener.add(t.onDidScrollChange(function(e){return r.cancel()})),1&n&&(r._listener.add(t.onDidChangeModel(function(e){return r.cancel()})),r._listener.add(t.onDidChangeModelContent(function(e){return r.cancel()}))),r}return m(t,e),t.prototype.dispose=function(){this._listener.dispose(),e.prototype.dispose.call(this)},t}(g),b=function(e){function t(t,n){var i=e.call(this,n)||this;return i._listener=t.onDidChangeContent(function(){return i.cancel()}),i}return m(t,e),t.prototype.dispose=function(){this._listener.dispose(),e.prototype.dispose.call(this)},t}(o.b),y=function(){function e(e,t){this._visiblePosition=e,this._visiblePositionScrollDelta=t}return e.capture=function(t){var n=null,i=0;if(0!==t.getScrollTop()){var r=t.getVisibleRanges();if(r.length>0){n=r[0].getStartPosition();var o=t.getTopForPosition(n.lineNumber,n.column);i=t.getScrollTop()-o}}return new e(n,i)},e.prototype.restore=function(e){if(this._visiblePosition){var t=e.getTopForPosition(this._visiblePosition.lineNumber,this._visiblePosition.column);e.setScrollTop(t+this._visiblePositionScrollDelta)}},e}()},"5QAX":function(e,t,n){var i=n("geuY"),r=n("X3l8").Buffer;e.exports=function(e,t){return r.from(e.toRed(i.mont(t.modulus)).redPow(new i(t.publicExponent)).fromRed().toArray())}},"5RGO":function(e,t){},"5TlO":function(e,t,n){"use strict";t.a=function(e,t){if(!e)throw new Error(t?"Assertion failed ("+t+")":"Assertion Failed")}},"5VRF":function(e,t,n){"use strict";n.d(t,"c",function(){return s}),n.d(t,"d",function(){return r}),t.f=function(e){return Array.isArray(e)?r.fromArray(e):e},n.d(t,"a",function(){return a}),n.d(t,"b",function(){return u}),n.d(t,"e",function(){return c});var i,r,o=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),s={done:!0,value:void 0};!function(e){var t={next:function(){return s}};e.empty=function(){return t},e.single=function(e){var t=!1;return{next:function(){return t?s:(t=!0,{done:!1,value:e})}}},e.fromArray=function(e,t,n){return void 0===t&&(t=0),void 0===n&&(n=e.length),{next:function(){return t>=n?s:{done:!1,value:e[t++]}}}},e.from=function(t){return t?Array.isArray(t)?e.fromArray(t):t:e.empty()},e.map=function(e,t){return{next:function(){var n=e.next();return n.done?s:{done:!1,value:t(n.value)}}}},e.filter=function(e,t){return{next:function(){for(;;){var n=e.next();if(n.done)return s;if(t(n.value))return{done:!1,value:n.value}}}}},e.forEach=function(e,t){for(var n=e.next();!n.done;n=e.next())t(n.value)},e.collect=function(e,t){void 0===t&&(t=Number.POSITIVE_INFINITY);var n=[];if(0===t)return n;for(var i=0,r=e.next();!(r.done||(n.push(r.value),++i>=t));r=e.next());return n},e.concat=function(){for(var e=[],t=0;t=e.length)return s;var t=e[n].next();return t.done?(n++,this.next()):t}}}}(r||(r={}));var a=function(){function e(e,t,n,i){void 0===t&&(t=0),void 0===n&&(n=e.length),void 0===i&&(i=t-1),this.items=e,this.start=t,this.end=n,this.index=i}return e.prototype.first=function(){return this.index=this.start,this.current()},e.prototype.next=function(){return this.index=Math.min(this.index+1,this.end),this.current()},e.prototype.current=function(){return this.index===this.start-1||this.index===this.end?null:this.items[this.index]},e}(),u=function(e){function t(t,n,i,r){return void 0===n&&(n=0),void 0===i&&(i=t.length),void 0===r&&(r=n-1),e.call(this,t,n,i,r)||this}return o(t,e),t.prototype.current=function(){return e.prototype.current.call(this)},t.prototype.previous=function(){return this.index=Math.max(this.index-1,this.start-1),this.current()},t.prototype.first=function(){return this.index=this.start,this.current()},t.prototype.last=function(){return this.index=this.end-1,this.current()},t.prototype.parent=function(){return null},t}(a),c=function(){function e(e,t){this.iterator=e,this.fn=t}return e.prototype.next=function(){return this.fn(this.iterator.next())},e}()},"5kgg":function(e,t){},"5lao":function(e,t,n){"use strict";n.d(t,"a",function(){return h}),n.d(t,"b",function(){return f});var i,r=n("ZfGv"),o=n("iXRW"),s=n("G8r4"),a=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),u=r.d?1.5:1.35;function c(e,t){if("number"==typeof e)return e;if(void 0===e)return t;var n=parseFloat(e);return isNaN(n)?t:n}function l(e,t,n){return en?n:e}function d(e,t){return"string"!=typeof e?t:e}var h=function(){function e(e){this.zoomLevel=e.zoomLevel,this.fontFamily=String(e.fontFamily),this.fontWeight=String(e.fontWeight),this.fontSize=e.fontSize,this.lineHeight=0|e.lineHeight,this.letterSpacing=e.letterSpacing}return e.createFromRawSettings=function(t,n,i){void 0===i&&(i=!1);var r=d(t.fontFamily,o.b.fontFamily),a=d(t.fontWeight,o.b.fontWeight),h=c(t.fontSize,o.b.fontSize);0===(h=l(h,0,100))?h=o.b.fontSize:h<8&&(h=8);var f=function(e,t){if("number"==typeof e)return Math.round(e);if(void 0===e)return t;var n=parseInt(e);return isNaN(n)?t:n}(t.lineHeight,0);0===(f=l(f,0,150))?f=Math.round(u*h):f<8&&(f=8);var p=c(t.letterSpacing,0);p=l(p,-5,20);var g=1+(i?0:.1*s.a.getZoomLevel());return new e({zoomLevel:n,fontFamily:r,fontWeight:a,fontSize:h*=g,lineHeight:f*=g,letterSpacing:p})},e.prototype.getId=function(){return this.zoomLevel+"-"+this.fontFamily+"-"+this.fontWeight+"-"+this.fontSize+"-"+this.lineHeight+"-"+this.letterSpacing},e.prototype.getMassagedFontFamily=function(){return/[,"']/.test(this.fontFamily)?this.fontFamily:/[+ ]/.test(this.fontFamily)?'"'+this.fontFamily+'"':this.fontFamily},e}(),f=function(e){function t(t,n){var i=e.call(this,t)||this;return i.isTrusted=n,i.isMonospace=t.isMonospace,i.typicalHalfwidthCharacterWidth=t.typicalHalfwidthCharacterWidth,i.typicalFullwidthCharacterWidth=t.typicalFullwidthCharacterWidth,i.canUseHalfwidthRightwardsArrow=t.canUseHalfwidthRightwardsArrow,i.spaceWidth=t.spaceWidth,i.maxDigitWidth=t.maxDigitWidth,i}return a(t,e),t.prototype.equals=function(e){return this.fontFamily===e.fontFamily&&this.fontWeight===e.fontWeight&&this.fontSize===e.fontSize&&this.lineHeight===e.lineHeight&&this.letterSpacing===e.letterSpacing&&this.typicalHalfwidthCharacterWidth===e.typicalHalfwidthCharacterWidth&&this.typicalFullwidthCharacterWidth===e.typicalFullwidthCharacterWidth&&this.canUseHalfwidthRightwardsArrow===e.canUseHalfwidthRightwardsArrow&&this.spaceWidth===e.spaceWidth&&this.maxDigitWidth===e.maxDigitWidth},t}(h)},"5tK6":function(e,t,n){"use strict";n.d(t,"a",function(){return m});var i,r=n("LCUL"),o=(n.n(r),n("tqet")),s=n("lAcG"),a=n("ZfGv"),u=n("KIxu"),c=n("Bug4"),l=n("b1X/"),d=n("Kp7x"),h=n("7/Cv"),f=n("Gxst"),p=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),g=!1,m=function(e){function t(t,n,i){void 0===i&&(i={});var r=e.call(this)||this;return r._state=3,r._onDidEnablementChange=r._register(new d.a),r.onDidEnablementChange=r._onDidEnablementChange.event,r._onDidStart=r._register(new d.a),r.onDidStart=r._onDidStart.event,r._onDidChange=r._register(new d.a),r.onDidChange=r._onDidChange.event,r._onDidReset=r._register(new d.a),r.onDidReset=r._onDidReset.event,r._onDidEnd=r._register(new d.a),r.onDidEnd=r._onDidEnd.event,r.linkedSash=void 0,r.orthogonalStartSashDisposables=r._register(new o.b),r.orthogonalEndSashDisposables=r._register(new o.b),r.el=Object(h.m)(t,Object(h.a)(".monaco-sash")),a.d&&Object(h.f)(r.el,"mac"),r._register(Object(f.a)(r.el,"mousedown")(r.onMouseDown,r)),r._register(Object(f.a)(r.el,"dblclick")(r.onMouseDoubleClick,r)),c.b.addTarget(r.el),r._register(Object(f.a)(r.el,c.a.Start)(r.onTouchStart,r)),s.k&&Object(h.f)(r.el,"touch"),r.setOrientation(i.orientation||0),r.hidden=!1,r.layoutProvider=n,r.orthogonalStartSash=i.orthogonalStartSash,r.orthogonalEndSash=i.orthogonalEndSash,Object(h.R)(r.el,"debug",g),r}return p(t,e),Object.defineProperty(t.prototype,"state",{get:function(){return this._state},set:function(e){this._state!==e&&(Object(h.R)(this.el,"disabled",0===e),Object(h.R)(this.el,"minimum",1===e),Object(h.R)(this.el,"maximum",2===e),this._state=e,this._onDidEnablementChange.fire(e))},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"orthogonalStartSash",{get:function(){return this._orthogonalStartSash},set:function(e){this.orthogonalStartSashDisposables.clear(),e?(this.orthogonalStartSashDisposables.add(e.onDidEnablementChange(this.onOrthogonalStartSashEnablementChange,this)),this.onOrthogonalStartSashEnablementChange(e.state)):this.onOrthogonalStartSashEnablementChange(0),this._orthogonalStartSash=e},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"orthogonalEndSash",{get:function(){return this._orthogonalEndSash},set:function(e){this.orthogonalEndSashDisposables.clear(),e?(this.orthogonalEndSashDisposables.add(e.onDidEnablementChange(this.onOrthogonalEndSashEnablementChange,this)),this.onOrthogonalEndSashEnablementChange(e.state)):this.onOrthogonalEndSashEnablementChange(0),this._orthogonalEndSash=e},enumerable:!0,configurable:!0}),t.prototype.setOrientation=function(e){this.orientation=e,1===this.orientation?(Object(h.f)(this.el,"horizontal"),Object(h.I)(this.el,"vertical")):(Object(h.I)(this.el,"horizontal"),Object(h.f)(this.el,"vertical")),this.layoutProvider&&this.layout()},t.prototype.onMouseDown=function(e){var t=this;h.c.stop(e,!1);var n=!1;if(!e.__orthogonalSashEvent){var i=this.getOrthogonalSash(e);i&&(n=!0,e.__orthogonalSashEvent=!0,i.onMouseDown(e))}if(this.linkedSash&&!e.__linkedSashEvent&&(e.__linkedSashEvent=!0,this.linkedSash.onMouseDown(e)),this.state){for(var r=Object(h.y)("iframe").concat(Object(h.y)("webview")),s=0,u=r;s=this.el.clientHeight-4)return this.orthogonalEndSash}else{if(e.offsetX<=4)return this.orthogonalStartSash;if(e.offsetX>=this.el.clientWidth-4)return this.orthogonalEndSash}},t.prototype.dispose=function(){e.prototype.dispose.call(this),this.el&&this.el.parentElement&&this.el.parentElement.removeChild(this.el),this.el=null},t}(o.a)},"5zde":function(e,t,n){n("zQR9"),n("qyJz"),e.exports=n("FeBl").Array.from},"606G":function(e,t,n){"use strict";n.d(t,"a",function(){return r});var i=n("JVO/"),r=Object(i.c)("editorWorkerService")},"67ys":function(e,t){},"6Hge":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=n("X6iQ"),r=n("80kS"),o=n("03Zz"),s=n("artP"),a=n("vTy2"),u=n("iHM7"),c=n("/9db"),l=n("PCC9"),d=n("hK2W"),h=n("tqet"),f=n("aL7J"),p=function(){function e(){}return e.prototype.provideSelectionRanges=function(e,t){for(var n=[],i=0,r=t;i=0;u--){if(95===(d=r.charCodeAt(u))||45===d)break;if(Object(f.y)(d)&&Object(f.z)(l))break;l=d}for(u+=1;c0&&0===t.getLineFirstNonWhitespaceColumn(n.lineNumber)&&0===t.getLineLastNonWhitespaceColumn(n.lineNumber)&&e.push({range:new a.a(n.lineNumber,1,n.lineNumber,t.getLineMaxColumn(n.lineNumber))})},e}(),g=n("NjuD"),m=n("ItKl"),v=n("zxiH");t.provideSelectionRanges=k;var _,b=this&&this.__extends||(_=function(e,t){return(_=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}_(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),y=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(r,o){function s(e){try{u(i.next(e))}catch(e){o(e)}}function a(e){try{u(i.throw(e))}catch(e){o(e)}}function u(e){e.done?r(e.value):new n(function(t){t(e.value)}).then(s,a)}u((i=i.apply(e,t||[])).next())})},w=this&&this.__generator||function(e,t){var n,i,r,o,s={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,i&&(r=2&o[0]?i.return:o[0]?i.throw||((r=i.return)&&r.call(i),0):i.next)&&!(r=r.call(i,o[1])).done)return r;switch(i=0,r&&(o=[2&o[0],r.value]),o[0]){case 0:case 1:r=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,i=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(r=(r=s.trys).length>0&&r[r.length-1])&&(6===o[0]||2===o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]=this.ranges.length)return this;var i=new e(n,this.ranges);return i.ranges[n].equalsRange(this.ranges[this.index])?i.mov(t):i},e}(),S=function(){function e(e){this._ignoreSelection=!1,this._editor=e}return e.get=function(t){return t.getContribution(e._id)},e.prototype.dispose=function(){Object(h.f)(this._selectionListener)},e.prototype.getId=function(){return e._id},e.prototype.run=function(e){var t=this;if(this._editor.hasModel()){var n=this._editor.getSelections(),o=this._editor.getModel();if(l.u.has(o)){var s=Promise.resolve(void 0);return this._state||(s=k(o,n.map(function(e){return e.getPosition()}),r.a.None).then(function(e){if(i.n(e)&&e.length===n.length&&t._editor.hasModel()&&i.g(t._editor.getSelections(),n,function(e,t){return e.equalsSelection(t)})){for(var r=function(t){e[t]=e[t].filter(function(e){return e.containsPosition(n[t].getStartPosition())&&e.containsPosition(n[t].getEndPosition())}),e[t].unshift(n[t])},o=0;o)?=?)";var L=u++;a[L]=a[l]+"|x|X|\\*";var O=u++;a[O]=a[c]+"|x|X|\\*";var k=u++;a[k]="[v=\\s]*("+a[O]+")(?:\\.("+a[O]+")(?:\\.("+a[O]+")(?:"+a[m]+")?"+a[b]+"?)?)?";var N=u++;a[N]="[v=\\s]*("+a[L]+")(?:\\.("+a[L]+")(?:\\.("+a[L]+")(?:"+a[v]+")?"+a[b]+"?)?)?";var E=u++;a[E]="^"+a[x]+"\\s*"+a[k]+"$";var I=u++;a[I]="^"+a[x]+"\\s*"+a[N]+"$";var D=u++;a[D]="(?:^|[^\\d])(\\d{1,16})(?:\\.(\\d{1,16}))?(?:\\.(\\d{1,16}))?(?:$|[^\\d])";var M=u++;a[M]="(?:~>?)";var T=u++;a[T]="(\\s*)"+a[M]+"\\s+",s[T]=new RegExp(a[T],"g");var P=u++;a[P]="^"+a[M]+a[k]+"$";var A=u++;a[A]="^"+a[M]+a[N]+"$";var R=u++;a[R]="(?:\\^)";var F=u++;a[F]="(\\s*)"+a[R]+"\\s+",s[F]=new RegExp(a[F],"g");var j=u++;a[j]="^"+a[R]+a[k]+"$";var W=u++;a[W]="^"+a[R]+a[N]+"$";var B=u++;a[B]="^"+a[x]+"\\s*("+C+")$|^$";var V=u++;a[V]="^"+a[x]+"\\s*("+w+")$|^$";var H=u++;a[H]="(\\s*)"+a[x]+"\\s*("+C+"|"+a[k]+")",s[H]=new RegExp(a[H],"g");var z=u++;a[z]="^\\s*("+a[k]+")\\s+-\\s+("+a[k]+")\\s*$";var U=u++;a[U]="^\\s*("+a[N]+")\\s+-\\s+("+a[N]+")\\s*$";var K=u++;a[K]="(<|>)?=?\\s*\\*";for(var q=0;qr)return null;if(!(t.loose?s[S]:s[y]).test(e))return null;try{return new Z(e,t)}catch(e){return null}}function Z(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrerelease:!1}),e instanceof Z){if(e.loose===t.loose)return e;e=e.version}else if("string"!=typeof e)throw new TypeError("Invalid Version: "+e);if(e.length>r)throw new TypeError("version is longer than "+r+" characters");if(!(this instanceof Z))return new Z(e,t);i("SemVer",e,t),this.options=t,this.loose=!!t.loose;var n=e.trim().match(t.loose?s[S]:s[y]);if(!n)throw new TypeError("Invalid Version: "+e);if(this.raw=e,this.major=+n[1],this.minor=+n[2],this.patch=+n[3],this.major>o||this.major<0)throw new TypeError("Invalid major version");if(this.minor>o||this.minor<0)throw new TypeError("Invalid minor version");if(this.patch>o||this.patch<0)throw new TypeError("Invalid patch version");n[4]?this.prerelease=n[4].split(".").map(function(e){if(/^[0-9]+$/.test(e)){var t=+e;if(t>=0&&t=0;)"number"==typeof this.prerelease[n]&&(this.prerelease[n]++,n=-2);-1===n&&this.prerelease.push(0)}t&&(this.prerelease[0]===t?isNaN(this.prerelease[1])&&(this.prerelease=[t,0]):this.prerelease=[t,0]);break;default:throw new Error("invalid increment argument: "+e)}return this.format(),this.raw=this.version,this},t.inc=function(e,t,n,i){"string"==typeof n&&(i=n,n=void 0);try{return new Z(e,n).inc(t,i).version}catch(e){return null}},t.diff=function(e,t){if(ee(e,t))return null;var n=G(e),i=G(t),r="";if(n.prerelease.length||i.prerelease.length){r="pre";var o="prerelease"}for(var s in n)if(("major"===s||"minor"===s||"patch"===s)&&n[s]!==i[s])return r+s;return o},t.compareIdentifiers=X;var Y=/^[0-9]+$/;function X(e,t){var n=Y.test(e),i=Y.test(t);return n&&i&&(e=+e,t=+t),e===t?0:n&&!i?-1:i&&!n?1:e0}function Q(e,t,n){return $(e,t,n)<0}function ee(e,t,n){return 0===$(e,t,n)}function te(e,t,n){return 0!==$(e,t,n)}function ne(e,t,n){return $(e,t,n)>=0}function ie(e,t,n){return $(e,t,n)<=0}function re(e,t,n,i){switch(t){case"===":return"object"==typeof e&&(e=e.version),"object"==typeof n&&(n=n.version),e===n;case"!==":return"object"==typeof e&&(e=e.version),"object"==typeof n&&(n=n.version),e!==n;case"":case"=":case"==":return ee(e,n,i);case"!=":return te(e,n,i);case">":return J(e,n,i);case">=":return ne(e,n,i);case"<":return Q(e,n,i);case"<=":return ie(e,n,i);default:throw new TypeError("Invalid operator: "+t)}}function oe(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrerelease:!1}),e instanceof oe){if(e.loose===!!t.loose)return e;e=e.value}if(!(this instanceof oe))return new oe(e,t);i("comparator",e,t),this.options=t,this.loose=!!t.loose,this.parse(e),this.semver===se?this.value="":this.value=this.operator+this.semver.version,i("comp",this)}t.rcompareIdentifiers=function(e,t){return X(t,e)},t.major=function(e,t){return new Z(e,t).major},t.minor=function(e,t){return new Z(e,t).minor},t.patch=function(e,t){return new Z(e,t).patch},t.compare=$,t.compareLoose=function(e,t){return $(e,t,!0)},t.rcompare=function(e,t,n){return $(t,e,n)},t.sort=function(e,n){return e.sort(function(e,i){return t.compare(e,i,n)})},t.rsort=function(e,n){return e.sort(function(e,i){return t.rcompare(e,i,n)})},t.gt=J,t.lt=Q,t.eq=ee,t.neq=te,t.gte=ne,t.lte=ie,t.cmp=re,t.Comparator=oe;var se={};function ae(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrerelease:!1}),e instanceof ae)return e.loose===!!t.loose&&e.includePrerelease===!!t.includePrerelease?e:new ae(e.raw,t);if(e instanceof oe)return new ae(e.value,t);if(!(this instanceof ae))return new ae(e,t);if(this.options=t,this.loose=!!t.loose,this.includePrerelease=!!t.includePrerelease,this.raw=e,this.set=e.split(/\s*\|\|\s*/).map(function(e){return this.parseRange(e.trim())},this).filter(function(e){return e.length}),!this.set.length)throw new TypeError("Invalid SemVer Range: "+e);this.format()}function ue(e){return!e||"x"===e.toLowerCase()||"*"===e}function ce(e,t,n,i,r,o,s,a,u,c,l,d,h){return((t=ue(n)?"":ue(i)?">="+n+".0.0":ue(r)?">="+n+"."+i+".0":">="+t)+" "+(a=ue(u)?"":ue(c)?"<"+(+u+1)+".0.0":ue(l)?"<"+u+"."+(+c+1)+".0":d?"<="+u+"."+c+"."+l+"-"+d:"<="+a)).trim()}function le(e,t,n){for(var r=0;r0){var o=e[r].semver;if(o.major===t.major&&o.minor===t.minor&&o.patch===t.patch)return!0}return!1}return!0}function de(e,t,n){try{t=new ae(t,n)}catch(e){return!1}return t.test(e)}function he(e,t,n,i){var r,o,s,a,u;switch(e=new Z(e,i),t=new ae(t,i),n){case">":r=J,o=ie,s=Q,a=">",u=">=";break;case"<":r=Q,o=ne,s=J,a="<",u="<=";break;default:throw new TypeError('Must provide a hilo val of "<" or ">"')}if(de(e,t,i))return!1;for(var c=0;c=0.0.0")),l=l||e,d=d||e,r(e.semver,l.semver,i)?l=e:s(e.semver,d.semver,i)&&(d=e)}),l.operator===a||l.operator===u)return!1;if((!d.operator||d.operator===a)&&o(e,d.semver))return!1;if(d.operator===u&&s(e,d.semver))return!1}return!0}oe.prototype.parse=function(e){var t=this.options.loose?s[B]:s[V],n=e.match(t);if(!n)throw new TypeError("Invalid comparator: "+e);this.operator=n[1],"="===this.operator&&(this.operator=""),n[2]?this.semver=new Z(n[2],this.options.loose):this.semver=se},oe.prototype.toString=function(){return this.value},oe.prototype.test=function(e){return i("Comparator.test",e,this.options.loose),this.semver===se||("string"==typeof e&&(e=new Z(e,this.options)),re(e,this.operator,this.semver,this.options))},oe.prototype.intersects=function(e,t){if(!(e instanceof oe))throw new TypeError("a Comparator is required");var n;if(t&&"object"==typeof t||(t={loose:!!t,includePrerelease:!1}),""===this.operator)return n=new ae(e.value,t),de(this.value,n,t);if(""===e.operator)return n=new ae(this.value,t),de(e.semver,n,t);var i=!(">="!==this.operator&&">"!==this.operator||">="!==e.operator&&">"!==e.operator),r=!("<="!==this.operator&&"<"!==this.operator||"<="!==e.operator&&"<"!==e.operator),o=this.semver.version===e.semver.version,s=!(">="!==this.operator&&"<="!==this.operator||">="!==e.operator&&"<="!==e.operator),a=re(this.semver,"<",e.semver,t)&&(">="===this.operator||">"===this.operator)&&("<="===e.operator||"<"===e.operator),u=re(this.semver,">",e.semver,t)&&("<="===this.operator||"<"===this.operator)&&(">="===e.operator||">"===e.operator);return i||r||o&&s||a||u},t.Range=ae,ae.prototype.format=function(){return this.range=this.set.map(function(e){return e.join(" ").trim()}).join("||").trim(),this.range},ae.prototype.toString=function(){return this.range},ae.prototype.parseRange=function(e){var t=this.options.loose;e=e.trim();var n=t?s[U]:s[z];e=e.replace(n,ce),i("hyphen replace",e),e=e.replace(s[H],"$1$2$3"),i("comparator trim",e,s[H]),e=(e=(e=e.replace(s[T],"$1~")).replace(s[F],"$1^")).split(/\s+/).join(" ");var r=t?s[B]:s[V],o=e.split(" ").map(function(e){return function(e,t){return i("comp",e,t),e=function(e,t){return e.trim().split(/\s+/).map(function(e){return function(e,t){i("caret",e,t);var n=t.loose?s[W]:s[j];return e.replace(n,function(t,n,r,o,s){var a;return i("caret",e,t,n,r,o,s),ue(n)?a="":ue(r)?a=">="+n+".0.0 <"+(+n+1)+".0.0":ue(o)?a="0"===n?">="+n+"."+r+".0 <"+n+"."+(+r+1)+".0":">="+n+"."+r+".0 <"+(+n+1)+".0.0":s?(i("replaceCaret pr",s),a="0"===n?"0"===r?">="+n+"."+r+"."+o+"-"+s+" <"+n+"."+r+"."+(+o+1):">="+n+"."+r+"."+o+"-"+s+" <"+n+"."+(+r+1)+".0":">="+n+"."+r+"."+o+"-"+s+" <"+(+n+1)+".0.0"):(i("no pr"),a="0"===n?"0"===r?">="+n+"."+r+"."+o+" <"+n+"."+r+"."+(+o+1):">="+n+"."+r+"."+o+" <"+n+"."+(+r+1)+".0":">="+n+"."+r+"."+o+" <"+(+n+1)+".0.0"),i("caret return",a),a})}(e,t)}).join(" ")}(e,t),i("caret",e),e=function(e,t){return e.trim().split(/\s+/).map(function(e){return function(e,t){var n=t.loose?s[A]:s[P];return e.replace(n,function(t,n,r,o,s){var a;return i("tilde",e,t,n,r,o,s),ue(n)?a="":ue(r)?a=">="+n+".0.0 <"+(+n+1)+".0.0":ue(o)?a=">="+n+"."+r+".0 <"+n+"."+(+r+1)+".0":s?(i("replaceTilde pr",s),a=">="+n+"."+r+"."+o+"-"+s+" <"+n+"."+(+r+1)+".0"):a=">="+n+"."+r+"."+o+" <"+n+"."+(+r+1)+".0",i("tilde return",a),a})}(e,t)}).join(" ")}(e,t),i("tildes",e),e=function(e,t){return i("replaceXRanges",e,t),e.split(/\s+/).map(function(e){return function(e,t){e=e.trim();var n=t.loose?s[I]:s[E];return e.replace(n,function(t,n,r,o,s,a){i("xRange",e,t,n,r,o,s,a);var u=ue(r),c=u||ue(o),l=c||ue(s),d=l;return"="===n&&d&&(n=""),u?t=">"===n||"<"===n?"<0.0.0":"*":n&&d?(c&&(o=0),s=0,">"===n?(n=">=",c?(r=+r+1,o=0,s=0):(o=+o+1,s=0)):"<="===n&&(n="<",c?r=+r+1:o=+o+1),t=n+r+"."+o+"."+s):c?t=">="+r+".0.0 <"+(+r+1)+".0.0":l&&(t=">="+r+"."+o+".0 <"+r+"."+(+o+1)+".0"),i("xRange return",t),t})}(e,t)}).join(" ")}(e,t),i("xrange",e),e=function(e,t){return i("replaceStars",e,t),e.trim().replace(s[K],"")}(e,t),i("stars",e),e}(e,this.options)},this).join(" ").split(/\s+/);return this.options.loose&&(o=o.filter(function(e){return!!e.match(r)})),o=o.map(function(e){return new oe(e,this.options)},this)},ae.prototype.intersects=function(e,t){if(!(e instanceof ae))throw new TypeError("a Range is required");return this.set.some(function(n){return n.every(function(n){return e.set.some(function(e){return e.every(function(e){return n.intersects(e,t)})})})})},t.toComparators=function(e,t){return new ae(e,t).set.map(function(e){return e.map(function(e){return e.value}).join(" ").trim().split(" ")})},ae.prototype.test=function(e){if(!e)return!1;"string"==typeof e&&(e=new Z(e,this.options));for(var t=0;t":0===t.prerelease.length?t.patch++:t.prerelease.push(0),t.raw=t.format();case"":case">=":n&&!J(n,t)||(n=t);break;case"<":case"<=":break;default:throw new Error("Unexpected operation: "+e.operator)}})}if(n&&e.test(n))return n;return null},t.validRange=function(e,t){try{return new ae(e,t).range||"*"}catch(e){return null}},t.ltr=function(e,t,n){return he(e,t,"<",n)},t.gtr=function(e,t,n){return he(e,t,">",n)},t.outside=he,t.prerelease=function(e,t){var n=G(e,t);return n&&n.prerelease.length?n.prerelease:null},t.intersects=function(e,t,n){return e=new ae(e,n),t=new ae(t,n),e.intersects(t)},t.coerce=function(e){if(e instanceof Z)return e;if("string"!=typeof e)return null;var t=e.match(s[D]);if(null==t)return null;return G(t[1]+"."+(t[2]||"0")+"."+(t[3]||"0"))}}).call(t,n("W2nU"))},"6TMp":function(e,t,n){"use strict";n.d(t,"a",function(){return r});var i=n("JVO/"),r=Object(i.c)("modeService")},"6ZSt":function(e,t){e.exports={"aes-128-ecb":{cipher:"AES",key:128,iv:0,mode:"ECB",type:"block"},"aes-192-ecb":{cipher:"AES",key:192,iv:0,mode:"ECB",type:"block"},"aes-256-ecb":{cipher:"AES",key:256,iv:0,mode:"ECB",type:"block"},"aes-128-cbc":{cipher:"AES",key:128,iv:16,mode:"CBC",type:"block"},"aes-192-cbc":{cipher:"AES",key:192,iv:16,mode:"CBC",type:"block"},"aes-256-cbc":{cipher:"AES",key:256,iv:16,mode:"CBC",type:"block"},aes128:{cipher:"AES",key:128,iv:16,mode:"CBC",type:"block"},aes192:{cipher:"AES",key:192,iv:16,mode:"CBC",type:"block"},aes256:{cipher:"AES",key:256,iv:16,mode:"CBC",type:"block"},"aes-128-cfb":{cipher:"AES",key:128,iv:16,mode:"CFB",type:"stream"},"aes-192-cfb":{cipher:"AES",key:192,iv:16,mode:"CFB",type:"stream"},"aes-256-cfb":{cipher:"AES",key:256,iv:16,mode:"CFB",type:"stream"},"aes-128-cfb8":{cipher:"AES",key:128,iv:16,mode:"CFB8",type:"stream"},"aes-192-cfb8":{cipher:"AES",key:192,iv:16,mode:"CFB8",type:"stream"},"aes-256-cfb8":{cipher:"AES",key:256,iv:16,mode:"CFB8",type:"stream"},"aes-128-cfb1":{cipher:"AES",key:128,iv:16,mode:"CFB1",type:"stream"},"aes-192-cfb1":{cipher:"AES",key:192,iv:16,mode:"CFB1",type:"stream"},"aes-256-cfb1":{cipher:"AES",key:256,iv:16,mode:"CFB1",type:"stream"},"aes-128-ofb":{cipher:"AES",key:128,iv:16,mode:"OFB",type:"stream"},"aes-192-ofb":{cipher:"AES",key:192,iv:16,mode:"OFB",type:"stream"},"aes-256-ofb":{cipher:"AES",key:256,iv:16,mode:"OFB",type:"stream"},"aes-128-ctr":{cipher:"AES",key:128,iv:16,mode:"CTR",type:"stream"},"aes-192-ctr":{cipher:"AES",key:192,iv:16,mode:"CTR",type:"stream"},"aes-256-ctr":{cipher:"AES",key:256,iv:16,mode:"CTR",type:"stream"},"aes-128-gcm":{cipher:"AES",key:128,iv:12,mode:"GCM",type:"auth"},"aes-192-gcm":{cipher:"AES",key:192,iv:12,mode:"GCM",type:"auth"},"aes-256-gcm":{cipher:"AES",key:256,iv:12,mode:"GCM",type:"auth"}}},"6boo":function(e,t,n){"use strict";n.d(t,"b",function(){return p}),n.d(t,"f",function(){return g}),n.d(t,"c",function(){return m}),n.d(t,"d",function(){return b}),n.d(t,"e",function(){return y}),n.d(t,"a",function(){return w}),t.g=function(e){return"'"===e||'"'===e||"`"===e};var i=n("zxiH"),r=n("aL7J"),o=n("artP"),s=n("vTy2"),a=n("iHM7"),u=n("0ly5"),c=n("Fllr"),l=function(){return!0},d=function(){return!1},h=function(e){return" "===e||"\t"===e};function f(e,t,n){e.has(t)?e.get(t).push(n):e.set(t,[n])}var p=function(){function e(t,n,i){this._languageIdentifier=t;var r=i.editor;this.readOnly=r.readOnly,this.tabSize=n.tabSize,this.indentSize=n.indentSize,this.insertSpaces=n.insertSpaces,this.pageSize=Math.max(1,Math.floor(r.layoutInfo.height/r.fontInfo.lineHeight)-2),this.lineHeight=r.lineHeight,this.useTabStops=r.useTabStops,this.wordSeparators=r.wordSeparators,this.emptySelectionClipboard=r.emptySelectionClipboard,this.copyWithSyntaxHighlighting=r.copyWithSyntaxHighlighting,this.multiCursorMergeOverlapping=r.multiCursorMergeOverlapping,this.autoClosingBrackets=r.autoClosingBrackets,this.autoClosingQuotes=r.autoClosingQuotes,this.autoClosingOvertype=r.autoClosingOvertype,this.autoSurround=r.autoSurround,this.autoIndent=r.autoIndent,this.autoClosingPairsOpen2=new Map,this.autoClosingPairsClose2=new Map,this.surroundingPairs={},this._electricChars=null,this.shouldAutoCloseBefore={quote:e._getShouldAutoClose(t,this.autoClosingQuotes),bracket:e._getShouldAutoClose(t,this.autoClosingBrackets)};var o=e._getAutoClosingPairs(t);if(o)for(var s=0,a=o;s=i.length)&&r.x(i.charCodeAt(n))},e.isHighSurrogate=function(e,t,n){var i=e.getLineContent(t);return!(n<0||n>=i.length)&&r.w(i.charCodeAt(n))},e.isInsideSurrogatePair=function(e,t,n){return this.isHighSurrogate(e,t,n-2)},e.visibleColumnFromColumn=function(e,t,n){var i=e.length;i>t-1&&(i=t-1);for(var o=0,s=0;s=t)return u-ts?s:r},e.nextRenderTabStop=function(e,t){return e+t-e%t},e.nextIndentTabStop=function(e,t){return e+t-e%t},e.prevRenderTabStop=function(e,t){return e-1-(e-1)%t},e.prevIndentTabStop=function(e,t){return e-1-(e-1)%t},e}()},"6hW9":function(e,t,n){var i=n("BEbT"),r=n("X3l8").Buffer,o=n("z+8S");function s(e,t,n,s){o.call(this),this._cipher=new i.AES(t),this._prev=r.from(n),this._cache=r.allocUnsafe(0),this._secCache=r.allocUnsafe(0),this._decrypt=s,this._mode=e}n("LC74")(s,o),s.prototype._update=function(e){return this._mode.encrypt(this,e,this._decrypt)},s.prototype._final=function(){this._cipher.scrub()},e.exports=s},"6jTg":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=n("odeJ"),r=n("zxiH"),o=n("tqet"),s=n("4tuZ"),a=n("03Zz"),u=n("PCC9"),c=n("X6iQ"),l=n("80kS"),d=n("mrx5"),h=n("jIdl"),f=function(){function e(){this.lenses=[],this._dispoables=new o.b}return e.prototype.dispose=function(){this._dispoables.dispose()},e.prototype.add=function(e,t){this._dispoables.add(e);for(var n=0,i=e.lenses;nt.symbol.range.startLineNumber?1:i.get(e.provider)i.get(t.provider)?1:e.symbol.range.startColumnt.symbol.range.startColumn?1:0}),o})}Object(a.j)("_executeCodeLensProvider",function(e,t){var n=t.resource,i=t.itemResolveCount;if(!(n instanceof d.a))throw Object(r.b)();var s=e.get(h.a).getModel(n);if(!s)throw Object(r.b)();var a=[],u=new o.b;return p(s,l.a.None).then(function(e){u.add(e);for(var t=[],n=function(e){void 0===i||Boolean(e.symbol.command)?a.push(e.symbol):i-- >0&&e.provider.resolveCodeLens&&t.push(Promise.resolve(e.provider.resolveCodeLens(s,e.symbol,l.a.None)).then(function(t){return a.push(t||e.symbol)}))},r=0,o=e.lenses;rno commands";else{for(var i=[],r=0;r"+s+"",this._commands.set(String(r),o)):a=""+s+"",i.push(a)}}var u=""===this._domNode.innerHTML||" "===this._domNode.innerHTML;this._domNode.innerHTML=i.join(" | "),this._editor.layoutContentWidget(this),u&&t&&g.f(this._domNode,"fadein")}},e.prototype.getCommand=function(e){return e.parentElement===this._domNode?this._commands.get(e.id):void 0},e.prototype.getId=function(){return this._id},e.prototype.getDomNode=function(){return this._domNode},e.prototype.setSymbolRange=function(e){if(this._editor.hasModel()){var t=e.startLineNumber,n=this._editor.getModel().getLineFirstNonWhitespaceColumn(t);this._widgetPosition={position:{lineNumber:t,column:n},preference:[1]}}},e.prototype.getPosition=function(){return this._widgetPosition||null},e.prototype.isVisible=function(){return this._domNode.hasAttribute("monaco-visible-content-widget")},e._idPool=0,e}(),x=function(){function e(){this._removeDecorations=[],this._addDecorations=[],this._addDecorationsCallbacks=[]}return e.prototype.addDecoration=function(e,t){this._addDecorations.push(e),this._addDecorationsCallbacks.push(t)},e.prototype.removeDecoration=function(e){this._removeDecorations.push(e)},e.prototype.commit=function(e){for(var t=e.deltaDecorations(this._removeDecorations,this._addDecorations),n=0,i=t.length;n a:hover { color: "+i+" !important; }")});var O=n("ItKl"),k=n("fAkY"),N=n("JVO/"),E=n("8xpx"),I=n("WTFd"),D=n("Cfmk"),M=n("dwjm"),T=this&&this.__decorate||function(e,t,n,i){var r,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,i);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},P=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},A=Object(N.c)("ICodeLensCache"),R=function(){return function(e,t){this.lineCount=e,this.data=t}}(),F=function(){function e(e){var t=this;this._fakeProvider=new(function(){function e(){}return e.prototype.provideCodeLenses=function(){throw new Error("not supported")},e}()),this._cache=new I.a(20,.75);Object(i.k)(function(){return e.remove("codelens/cache",1)});var n="codelens/cache2",r=e.get(n,1,"{}");this._deserialize(r),Object(M.a)(e.onWillSaveState)(function(i){i.reason===D.c.SHUTDOWN&&e.store(n,t._serialize(),1)})}return e.prototype.put=function(e,t){var n=new f;n.add({lenses:t.lenses.map(function(e){return e.symbol}),dispose:function(){}},this._fakeProvider);var i=new R(e.getLineCount(),n);this._cache.set(e.uri.toString(),i)},e.prototype.get=function(e){var t=this._cache.get(e.uri.toString());return t&&t.lineCount===e.getLineCount()?t.data:void 0},e.prototype.delete=function(e){this._cache.delete(e.uri.toString())},e.prototype._serialize=function(){var e=Object.create(null);return this._cache.forEach(function(t,n){for(var i=new Set,r=0,o=t.data.lenses;r=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},W=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},B=function(){function e(e,t,n,i){var r=this;this._editor=e,this._commandService=t,this._notificationService=n,this._codeLensCache=i,this._globalToDispose=new o.b,this._localToDispose=new o.b,this._lenses=[],this._oldCodeLensModels=new o.b,this._modelChangeCounter=0,this._isEnabled=this._editor.getConfiguration().contribInfo.codeLens,this._globalToDispose.add(this._editor.onDidChangeModel(function(){return r._onModelChange()})),this._globalToDispose.add(this._editor.onDidChangeModelLanguage(function(){return r._onModelChange()})),this._globalToDispose.add(this._editor.onDidChangeConfiguration(function(){var e=r._isEnabled;r._isEnabled=r._editor.getConfiguration().contribInfo.codeLens,e!==r._isEnabled&&r._onModelChange()})),this._globalToDispose.add(u.b.onDidChange(this._onModelChange,this)),this._onModelChange()}return e.prototype.dispose=function(){this._localDispose(),this._globalToDispose.dispose(),this._oldCodeLensModels.dispose(),Object(o.f)(this._currentCodeLensModel)},e.prototype._localDispose=function(){this._currentFindCodeLensSymbolsPromise&&(this._currentFindCodeLensSymbolsPromise.cancel(),this._currentFindCodeLensSymbolsPromise=void 0,this._modelChangeCounter++),this._currentResolveCodeLensSymbolsPromise&&(this._currentResolveCodeLensSymbolsPromise.cancel(),this._currentResolveCodeLensSymbolsPromise=void 0),this._localToDispose.clear(),this._oldCodeLensModels.clear(),Object(o.f)(this._currentCodeLensModel)},e.prototype.getId=function(){return e.ID},e.prototype._onModelChange=function(){var e=this;this._localDispose();var t=this._editor.getModel();if(t&&this._isEnabled){var n=this._codeLensCache.get(t);if(n&&this._renderCodeLensSymbols(n),u.b.has(t)){for(var a=0,c=u.b.all(t);a0&&h.schedule()})),this._localToDispose.add(this._editor.onDidLayoutChange(function(){h.schedule()})),this._localToDispose.add(Object(o.h)(function(){if(e._editor.getModel()){var t=s.c.capture(e._editor);e._editor.changeDecorations(function(t){e._editor.changeViewZones(function(n){e._disposeAllLenses(t,n)})}),t.restore(e._editor)}else e._disposeAllLenses(void 0,void 0)})),this._localToDispose.add(this._editor.onDidChangeConfiguration(function(t){if(t.fontInfo)for(var n=0,i=e._lenses;ni||(n&&n[n.length-1].symbol.range.startLineNumber===c?n.push(u):(n=[u],r.push(n)))}var l=s.c.capture(this._editor);this._editor.changeDecorations(function(e){t._editor.changeViewZones(function(n){for(var i=new x,o=0,s=0;s=0;r--)t.sheet.deleteRule(i[r])},t.F=function(e){if("object"==typeof HTMLElement)return e instanceof HTMLElement;return e&&"object"==typeof e&&1===e.nodeType&&"string"==typeof e.nodeName},n.d(t,"d",function(){return Y}),n.d(t,"c",function(){return X}),t.O=function(e){for(var t=[],n=0;e&&e.nodeType===e.ELEMENT_NODE;n++)t[n]=e.scrollTop,e=e.parentNode;return t},t.M=function(e,t){for(var n=0;e&&e.nodeType===e.ELEMENT_NODE;n++)e.scrollTop!==t[n]&&(e.scrollTop=t[n]),e=e.parentNode},t.S=function(e){return new $(e)},t.m=function(e){for(var t=[],n=1;n=0;){if(o=s+r,(0===s||32===n.charCodeAt(s-1))&&32===n.charCodeAt(o))return this._lastStart=s,void(this._lastEnd=o+1);if(s>0&&32===n.charCodeAt(s-1)&&o===i)return this._lastStart=s-1,void(this._lastEnd=o);if(0===s&&o===i)return this._lastStart=0,void(this._lastEnd=o)}this._lastStart=-1}else this._lastStart=-1}else this._lastStart=-1},e.prototype.hasClass=function(e,t){return this._findClassName(e,t),-1!==this._lastStart},e.prototype.addClasses=function(e){for(var t=this,n=[],i=1;i0;){T.sort(F.sort),T.shift().execute()}A=!1},I=function(e,t){void 0===t&&(t=0);var n,i=new F(e,t);return M.push(i),P||(P=!0,n=R,D||(D=self.requestAnimationFrame||self.msRequestAnimationFrame||self.webkitRequestAnimationFrame||self.mozRequestAnimationFrame||self.oRequestAnimationFrame||function(e){return setTimeout(function(){return e((new Date).getTime())},0)}),D.call(self,n)),i},E=function(e,t){if(A){var n=new F(e,t);return T.push(n),n}return I(e,t)};var j=16,W=function(e,t){return t},B=function(e){function t(t,n,i,r,o){void 0===r&&(r=W),void 0===o&&(o=j);var s=e.call(this)||this,a=null,c=0,l=s._register(new u.e),d=function(){c=(new Date).getTime(),i(a),a=null};return s._register(k(t,n,function(e){a=r(a,e);var t=(new Date).getTime()-c;t>=o?(l.cancel(),d()):l.setIfNotSet(d,o-t)})),s}return g(t,e),t}(d.a);function V(e){return document.defaultView.getComputedStyle(e,null)}var H=function(){function e(){}return e.convertToPixels=function(e,t){return parseFloat(t)||0},e.getDimension=function(t,n,i){var r=V(t),o="0";return r&&(o=r.getPropertyValue?r.getPropertyValue(n):r.getAttribute(i)),e.convertToPixels(t,o)},e.getBorderLeftWidth=function(t){return e.getDimension(t,"border-left-width","borderLeftWidth")},e.getBorderRightWidth=function(t){return e.getDimension(t,"border-right-width","borderRightWidth")},e.getBorderTopWidth=function(t){return e.getDimension(t,"border-top-width","borderTopWidth")},e.getBorderBottomWidth=function(t){return e.getDimension(t,"border-bottom-width","borderBottomWidth")},e.getPaddingLeft=function(t){return e.getDimension(t,"padding-left","paddingLeft")},e.getPaddingRight=function(t){return e.getDimension(t,"padding-right","paddingRight")},e.getPaddingTop=function(t){return e.getDimension(t,"padding-top","paddingTop")},e.getPaddingBottom=function(t){return e.getDimension(t,"padding-bottom","paddingBottom")},e.getMarginLeft=function(t){return e.getDimension(t,"margin-left","marginLeft")},e.getMarginTop=function(t){return e.getDimension(t,"margin-top","marginTop")},e.getMarginRight=function(t){return e.getDimension(t,"margin-right","marginRight")},e.getMarginBottom=function(t){return e.getDimension(t,"margin-bottom","marginBottom")},e}(),z=function(){return function(e,t){this.width=e,this.height=t}}();var U=new(function(){function e(){}return Object.defineProperty(e.prototype,"scrollX",{get:function(){return"number"==typeof window.scrollX?window.scrollX:document.body.scrollLeft+document.documentElement.scrollLeft},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"scrollY",{get:function(){return"number"==typeof window.scrollY?window.scrollY:document.body.scrollTop+document.documentElement.scrollTop},enumerable:!0,configurable:!0}),e}());function K(e,t){for(;e;){if(e===t)return!0;e=e.parentNode}return!1}function q(e){void 0===e&&(e=document.getElementsByTagName("head")[0]);var t=document.createElement("style");return t.type="text/css",t.media="screen",e.appendChild(t),t}var G=null;function Z(){return G||(G=q()),G}var Y={CLICK:"click",DBLCLICK:"dblclick",MOUSE_UP:"mouseup",MOUSE_DOWN:"mousedown",MOUSE_OVER:"mouseover",MOUSE_MOVE:"mousemove",MOUSE_OUT:"mouseout",MOUSE_ENTER:"mouseenter",MOUSE_LEAVE:"mouseleave",CONTEXT_MENU:"contextmenu",WHEEL:"wheel",KEY_DOWN:"keydown",KEY_PRESS:"keypress",KEY_UP:"keyup",LOAD:"load",BEFORE_UNLOAD:"beforeunload",UNLOAD:"unload",ABORT:"abort",ERROR:"error",RESIZE:"resize",SCROLL:"scroll",FULLSCREEN_CHANGE:"fullscreenchange",WK_FULLSCREEN_CHANGE:"webkitfullscreenchange",SELECT:"select",CHANGE:"change",SUBMIT:"submit",RESET:"reset",FOCUS:"focus",FOCUS_IN:"focusin",FOCUS_OUT:"focusout",BLUR:"blur",INPUT:"input",STORAGE:"storage",DRAG_START:"dragstart",DRAG:"drag",DRAG_ENTER:"dragenter",DRAG_LEAVE:"dragleave",DRAG_OVER:"dragover",DROP:"drop",DRAG_END:"dragend",ANIMATION_START:r.m?"webkitAnimationStart":"animationstart",ANIMATION_END:r.m?"webkitAnimationEnd":"animationend",ANIMATION_ITERATION:r.m?"webkitAnimationIteration":"animationiteration"},X={stop:function(e,t){e.preventDefault?e.preventDefault():e.returnValue=!1,t&&(e.stopPropagation?e.stopPropagation():e.cancelBubble=!0)}};var $=function(e){function t(t){var n=e.call(this)||this;n._onDidFocus=n._register(new l.a),n.onDidFocus=n._onDidFocus.event,n._onDidBlur=n._register(new l.a),n.onDidBlur=n._onDidBlur.event;var i=K(document.activeElement,t),r=!1;return n._register(Object(o.a)(t,Y.FOCUS,!0)(function(){r=!1,i||(i=!0,n._onDidFocus.fire())})),n._register(Object(o.a)(t,Y.BLUR,!0)(function(){i&&(r=!0,window.setTimeout(function(){r&&(r=!1,i=!1,n._onDidBlur.fire())},0))})),n}return g(t,e),t}(d.a);var J,Q=/([\w\-]+)?(#([\w\-]+))?((.([\w\-]+))*)/;function ee(e,t,n){for(var i=[],r=3;r=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},S=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},x=function(){function e(){}return e.prototype.select=function(e,t,n){if(0===n.length)return 0;for(var i=n[0].score[0],r=1;ru&&d.type===i[c].completion.kind&&d.insertText===i[c].completion.insertText&&(u=d.touch,a=c),i[c].completion.preselect&&-1===s)return c}return-1!==a?a:-1!==s?s:0},t.prototype.toJSON=function(){var e=[];return this._cache.forEach(function(t,n){e.push([n,t])}),e},t.prototype.fromJSON=function(e){this._cache.clear();for(var t=0,n=e;t0){this._seq=e[0][1].touch+1;for(var t=0,n=e;t=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},R=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},F=function(){function e(t,n){this._editor=t,this._index=0,this._ckOtherSuggestions=e.OtherSuggestions.bindTo(n)}return e.prototype.dispose=function(){this.reset()},e.prototype.reset=function(){this._ckOtherSuggestions.reset(),Object(a.f)(this._listener),this._model=void 0,this._acceptNext=void 0,this._ignore=!1},e.prototype.set=function(t,n){var i=this,r=t.model,o=t.index;0!==r.items.length?e._moveIndex(!0,r,o)!==o?(this._acceptNext=n,this._model=r,this._index=o,this._listener=this._editor.onDidChangeCursorPosition(function(){i._ignore||i.reset()}),this._ckOtherSuggestions.set(!0)):this.reset():this.reset()},e._moveIndex=function(e,t,n){for(var i=n;(i=(i+t.items.length+(e?1:-1))%t.items.length)!==n&&t.items[i].completion.additionalTextEdits;);return i},e.prototype.next=function(){this._move(!0)},e.prototype.prev=function(){this._move(!1)},e.prototype._move=function(t){if(this._model)try{this._ignore=!0,this._index=e._moveIndex(t,this._model,this._index),this._acceptNext({index:this._index,item:this._model.items[this._index],model:this._model})}finally{this._ignore=!1}},e.OtherSuggestions=new T.d("hasOtherSuggestions",!1),e=A([R(1,T.c)],e)}(),j=n("Kp7x"),W=n("iHM7"),B=n("GYOr"),V=n("iXRW"),H=n("aL7J"),z=(function(){}(),function(){function e(t,n,i,r,o){void 0===o&&(o=V.a.contribInfo.suggest),this._snippetCompareFn=e._compareCompletionItems,this._items=t,this._column=n,this._wordDistance=r,this._options=o,this._refilterKind=1,this._lineContext=i,"top"===o.snippets?this._snippetCompareFn=e._compareCompletionItemsSnippetsUp:"bottom"===o.snippets&&(this._snippetCompareFn=e._compareCompletionItemsSnippetsDown)}return Object.defineProperty(e.prototype,"lineContext",{get:function(){return this._lineContext},set:function(e){this._lineContext.leadingLineContent===e.leadingLineContent&&this._lineContext.characterCountDelta===e.characterCountDelta||(this._refilterKind=this._lineContext.characterCountDelta2e3?B.d:B.e,u=0;u=d)c.score=B.a.Default;else if("string"==typeof c.completion.filterText){if(!(p=a(i,r,h,c.completion.filterText,c.filterTextLow,0,!1)))continue;0===Object(H.e)(c.completion.filterText,c.completion.label)?c.score=p:(c.score=Object(B.b)(i,r,h,c.completion.label,c.labelLow,0),c.score[0]=p[0])}else{var p;if(!(p=a(i,r,h,c.completion.label,c.labelLow,0,!1)))continue;c.score=p}}switch(c.idx=u,c.distance=this._wordDistance.distance(c.position,c.completion),s.push(c),this._stats.suggestionCount++,c.completion.kind){case 25:this._stats.snippetCount++;break;case 18:this._stats.textCount++}}this._filteredItems=s.sort(this._snippetCompareFn),this._refilterKind=0},e._compareCompletionItems=function(e,t){return e.score[0]>t.score[0]?-1:e.score[0]t.distance?1:e.idxt.idx?1:0},e._compareCompletionItemsSnippetsDown=function(t,n){if(t.completion.kind!==n.completion.kind){if(25===t.completion.kind)return 1;if(25===n.completion.kind)return-1}return e._compareCompletionItems(t,n)},e._compareCompletionItemsSnippetsUp=function(t,n){if(t.completion.kind!==n.completion.kind){if(25===t.completion.kind)return-1;if(25===n.completion.kind)return 1}return e._compareCompletionItems(t,n)},e}()),U=n("80kS"),K=n("NjuD"),q=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),G=function(){function e(){}return e.create=function(t,n){if(!n.getConfiguration().contribInfo.suggest.localityBonus)return Promise.resolve(e.None);if(!n.hasModel())return Promise.resolve(e.None);var i=n.getModel(),r=n.getPosition();return t.canComputeWordRanges(i.uri)?(new K.a).provideSelectionRanges(i,[r]).then(function(s){return s&&0!==s.length&&0!==s[0].length?t.computeWordRanges(i.uri,s[0][0].range).then(function(t){return new(function(e){function i(){return null!==e&&e.apply(this,arguments)||this}return q(i,e),i.prototype.distance=function(e,i){if(!t||!r.equals(n.getPosition()))return 0;if(17===i.kind)return 2<<20;var a=i.label,u=t[a];if(Object(o.m)(u))return 2<<20;for(var c=Object(o.c)(u,l.a.fromPositions(e),l.a.compareRangesUsingStarts),d=c>=0?u[c]:u[Math.max(0,~c-1)],h=s.length,f=0,p=s[0];f0?{triggerKind:2}:{triggerKind:0},this._requestToken=new U.b;var h=this._editor.getConfiguration().contribInfo,f=new Set,p=1;switch(h.suggest.snippets){case"top":p=0;break;case"bottom":p=2;break;case"none":f.add(25)}for(var g in h.suggest.filteredTypes){var v=Object(m.A)(g,!0);void 0!==v&&!1===h.suggest.filteredTypes[g]&&f.add(v)}var _=G.create(this._editorWorker,this._editor),b=Object(P.e)(c,this._editor.getPosition(),new P.a(p,f,n),u,this._requestToken.token);Promise.all([b,_]).then(function(t){var n=t[0],s=t[1];if(Object(a.f)(r._requestToken),0!==r._state&&r._editor.hasModel()){var u=r._editor.getModel();if(Object(o.n)(i)){var c=Object(P.d)(p);n=n.concat(i).sort(c)}var d=new Z(u,r._editor.getPosition(),l,e.shy);r._completionModel=new z(n,r._context.column,{leadingLineContent:d.leadingLineContent,characterCountDelta:d.column-r._context.column},s,r._editor.getConfiguration().contribInfo.suggest);for(var h=0,f=n;hthis._context.column&&this._completionModel.incomplete.size>0&&0!==e.leadingWord.word.length){var t=this._completionModel.incomplete,n=this._completionModel.adopt(t);this.trigger({auto:2===this._state,shy:!1},!0,t,n)}else{var i=this._completionModel.lineContext,r=!1;if(this._completionModel.lineContext={leadingLineContent:e.leadingLineContent,characterCountDelta:e.column-this._context.column},0===this._completionModel.items.length){if(Z.shouldAutoTrigger(this._editor)&&this._context.leadingWord.endColumn0)&&0===e.leadingWord.word.length)return void this.cancel()}this._onDidSuggest.fire({completionModel:this._completionModel,auto:this._context.auto,shy:this._context.shy,isFrozen:r})}}else this.cancel()},e}(),X=(n("YUwp"),n("7/Cv")),$=n("SWdJ"),J=n("qecS"),Q=n("NqM+"),ee=n("3ciN"),te=n("Yqb6"),ne=n("eoic"),ie=n("L5KM"),re=n("VBCr"),oe=n("6TMp"),se=n("GsV8"),ae=n("tpa8"),ue=n("lapT"),ce=n("ZYUE"),le=n("9XyG");function de(e,t,n,i){var r=i===I.ROOT_FOLDER?["rootfolder-icon"]:i===I.FOLDER?["folder-icon"]:["file-icon"];if(n){var o;if(n.scheme===ue.b.data)o=ce.a.parseMetaData(n).get(ce.a.META_DATA_LABEL);else o=he(Object(ce.c)(n).toLowerCase());if(i===I.FOLDER)r.push(o+"-name-folder-icon");else{if(o){r.push(o+"-name-file-icon");for(var s=o.split("."),a=1;a=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},_e=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},be=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(r,o){function s(e){try{u(i.next(e))}catch(e){o(e)}}function a(e){try{u(i.throw(e))}catch(e){o(e)}}function u(e){e.done?r(e.value):new n(function(t){t(e.value)}).then(s,a)}u((i=i.apply(e,t||[])).next())})},ye=this&&this.__generator||function(e,t){var n,i,r,o,s={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,i&&(r=2&o[0]?i.return:o[0]?i.throw||((r=i.return)&&r.call(i),0):i.next)&&!(r=r.call(i,o[1])).done)return r;switch(i=0,r&&(o=[2&o[0],r.value]),o[0]){case 0:case 1:r=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,i=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(r=(r=s.trys).length>0&&r[r.length-1])&&(6===o[0]||2===o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]=0&&(c.extraClasses=(c.extraClasses||[]).concat(["deprecated"]),c.matches=[]),r.iconLabel.setLabel(s.label,void 0,c),r.typeLabel.textContent=(s.detail||"").replace(/\n.*$/m,""),ke(e)?(Object(X.Q)(r.readMore),r.readMore.onmousedown=function(e){e.stopPropagation(),e.preventDefault()},r.readMore.onclick=function(e){e.stopPropagation(),e.preventDefault(),i.widget.toggleDetails()}):(Object(X.D)(r.readMore),r.readMore.onmousedown=null,r.readMore.onclick=null)},e.prototype.disposeTemplate=function(e){e.disposables.dispose()},e=ve([_e(3,fe.a),_e(4,oe.a),_e(5,ne.c)],e)}(),Ee=function(){function e(e,t,n,i,r){var o=this;this.widget=t,this.editor=n,this.markdownRenderer=i,this.triggerKeybindingLabel=r,this.borderWidth=1,this.disposables=new a.b,this.el=Object(X.m)(e,Object(X.a)(".details")),this.disposables.add(Object(a.h)(function(){return e.removeChild(o.el)})),this.body=Object(X.a)(".body"),this.scrollbar=new J.a(this.body,{}),Object(X.m)(this.el,this.scrollbar.getDomNode()),this.disposables.add(this.scrollbar),this.header=Object(X.m)(this.body,Object(X.a)(".header")),this.close=Object(X.m)(this.header,Object(X.a)("span.close")),this.close.title=D.a("readLess","Read less...{0}",this.triggerKeybindingLabel),this.type=Object(X.m)(this.header,Object(X.a)("p.type")),this.docs=Object(X.m)(this.body,Object(X.a)("p.docs")),this.ariaLabel=null,this.configureFont(),j.b.chain(this.editor.onDidChangeConfiguration.bind(this.editor)).filter(function(e){return e.fontInfo}).on(this.configureFont,this,this.disposables),i.onDidRenderCodeBlock(function(){return o.scrollbar.scanDomNode()},this,this.disposables)}return Object.defineProperty(e.prototype,"element",{get:function(){return this.el},enumerable:!0,configurable:!0}),e.prototype.renderLoading=function(){this.type.textContent=D.a("loading","Loading..."),this.docs.textContent=""},e.prototype.renderItem=function(e,t){var n=this;this.renderDisposeable=Object(a.f)(this.renderDisposeable);var i=e.completion,r=i.documentation,o=i.detail;if(t){var s="";s+="score: "+e.score[0]+(e.word?", compared '"+(e.completion.filterText&&e.completion.filterText+" (filterText)"||e.completion.label)+"' with '"+e.word+"'":" (no prefix)")+"\n",s+="distance: "+e.distance+", see localityBonus-setting\n",s+="index: "+e.idx+", based on "+(e.completion.sortText&&'sortText: "'+e.completion.sortText+'"'||"label")+"\n",r=(new ge.a).appendCodeblock("empty",s),o="Provider: "+e.provider._debugDisplayName}if(!t&&!ke(e))return this.type.textContent="",this.docs.textContent="",Object(X.f)(this.el,"no-docs"),void(this.ariaLabel=null);if(Object(X.I)(this.el,"no-docs"),"string"==typeof r)Object(X.I)(this.docs,"markdown-docs"),this.docs.textContent=r;else{Object(X.f)(this.docs,"markdown-docs"),this.docs.innerHTML="";var u=this.markdownRenderer.render(r);this.renderDisposeable=u,this.docs.appendChild(u.element)}o?(this.type.innerText=o,Object(X.Q)(this.type)):(this.type.innerText="",Object(X.D)(this.type)),this.el.style.height=this.header.offsetHeight+this.docs.offsetHeight+2*this.borderWidth+"px",this.close.onmousedown=function(e){e.preventDefault(),e.stopPropagation()},this.close.onclick=function(e){e.preventDefault(),e.stopPropagation(),n.widget.toggleDetails()},this.body.scrollTop=0,this.scrollbar.scanDomNode(),this.ariaLabel=H.r("{0}{1}",o||"",r?"string"==typeof r?r:r.value:"")},e.prototype.getAriaLabel=function(){return this.ariaLabel},e.prototype.scrollDown=function(e){void 0===e&&(e=8),this.body.scrollTop+=e},e.prototype.scrollUp=function(e){void 0===e&&(e=8),this.body.scrollTop-=e},e.prototype.scrollTop=function(){this.body.scrollTop=0},e.prototype.scrollBottom=function(){this.body.scrollTop=this.body.scrollHeight},e.prototype.pageDown=function(){this.scrollDown(80)},e.prototype.pageUp=function(){this.scrollUp(80)},e.prototype.setBorderWidth=function(e){this.borderWidth=e},e.prototype.configureFont=function(){var e=this.editor.getConfiguration(),t=e.fontInfo.fontFamily,n=e.contribInfo.suggestFontSize||e.fontInfo.fontSize,i=e.contribInfo.suggestLineHeight||e.fontInfo.lineHeight,r=e.fontInfo.fontWeight,o=n+"px",s=i+"px";this.el.style.fontSize=o,this.el.style.fontWeight=r,this.type.style.fontFamily=t,this.close.style.height=s,this.close.style.width=s},e.prototype.dispose=function(){this.disposables.dispose(),this.renderDisposeable=Object(a.f)(this.renderDisposeable)},e}(),Ie=function(){function e(e,t,n,i,r,o,s,u,c){var l=this;this.editor=e,this.telemetryService=t,this.allowEditorOverflow=!0,this.suppressMouseDown=!0,this.state=null,this.isAuto=!1,this.loadingTimeout=a.a.None,this.currentSuggestionDetails=null,this.ignoreFocusEvents=!1,this.completionModel=null,this.showTimeout=new v.e,this.toDispose=new a.b,this.onDidSelectEmitter=new j.a,this.onDidFocusEmitter=new j.a,this.onDidHideEmitter=new j.a,this.onDidShowEmitter=new j.a,this.onDidSelect=this.onDidSelectEmitter.event,this.onDidFocus=this.onDidFocusEmitter.event,this.onDidHide=this.onDidHideEmitter.event,this.onDidShow=this.onDidShowEmitter.event,this.maxWidgetWidth=660,this.listWidth=330,this.firstFocusInCurrentList=!1,this.preferDocPositionTop=!1,this.docsPositionPreviousWidgetY=null,this.explainMode=!1,this._lastAriaAlertLabel=null;var d=o.lookupKeybinding("editor.action.triggerSuggest"),h=d?" ("+d.getLabel()+")":"",f=this.toDispose.add(new re.a(e,s,u));this.isAuto=!1,this.focusedItem=null,this.storageService=r,this.element=Object(X.a)(".editor-widget.suggest-widget"),this.toDispose.add(Object(X.h)(this.element,"click",function(e){e.target===l.element&&l.hideWidget()})),this.messageElement=Object(X.m)(this.element,Object(X.a)(".message")),this.listElement=Object(X.m)(this.element,Object(X.a)(".tree")),this.details=c.createInstance(Ee,this.element,this,this.editor,f,h);var p=function(){return Object(X.R)(l.element,"no-icons",!l.editor.getConfiguration().contribInfo.suggest.showIcons)};p();var g=c.createInstance(Ne,this,this.editor,h);this.list=new $.b(this.listElement,this,[g],{useShadows:!1,openController:{shouldOpen:function(){return!1}},mouseSupport:!1}),this.toDispose.add(Object(te.b)(this.list,i,{listInactiveFocusBackground:xe,listInactiveFocusOutline:ie.b})),this.toDispose.add(i.onThemeChange(function(e){return l.onThemeChange(e)})),this.toDispose.add(e.onDidLayoutChange(function(){return l.onEditorLayoutChange()})),this.toDispose.add(this.list.onMouseDown(function(e){return l.onListMouseDown(e)})),this.toDispose.add(this.list.onSelectionChange(function(e){return l.onListSelection(e)})),this.toDispose.add(this.list.onFocusChange(function(e){return l.onListFocus(e)})),this.toDispose.add(this.editor.onDidChangeCursorSelection(function(){return l.onCursorSelectionChanged()})),this.toDispose.add(this.editor.onDidChangeConfiguration(function(e){return e.contribInfo&&p()})),this.suggestWidgetVisible=P.b.Visible.bindTo(n),this.suggestWidgetMultipleSuggestions=P.b.MultipleSuggestions.bindTo(n),this.editor.addContentWidget(this),this.setState(0),this.onThemeChange(i.getTheme())}return e.prototype.onCursorSelectionChanged=function(){0!==this.state&&this.editor.layoutContentWidget(this)},e.prototype.onEditorLayoutChange=function(){3!==this.state&&5!==this.state||!this.expandDocsSettingFromStorage()||this.expandSideOrBelow()},e.prototype.onListMouseDown=function(e){void 0!==e.element&&void 0!==e.index&&(e.browserEvent.preventDefault(),e.browserEvent.stopPropagation(),this.select(e.element,e.index))},e.prototype.onListSelection=function(e){e.elements.length&&this.select(e.elements[0],e.indexes[0])},e.prototype.select=function(e,t){var n=this.completionModel;n&&(this.onDidSelectEmitter.fire({item:e,index:t,model:n}),this.editor.focus())},e.prototype._getSuggestionAriaAlertLabel=function(e){return this.expandDocsSettingFromStorage()?D.a("ariaCurrenttSuggestionReadDetails","Item {0}, docs: {1}",e.completion.label,this.details.getAriaLabel()):e.completion.label},e.prototype._ariaAlert=function(e){this._lastAriaAlertLabel!==e&&(this._lastAriaAlertLabel=e,this._lastAriaAlertLabel&&Object(r.a)(this._lastAriaAlertLabel,!0))},e.prototype.onThemeChange=function(e){var t=e.getColor(we);t&&(this.listElement.style.backgroundColor=t.toString(),this.details.element.style.backgroundColor=t.toString(),this.messageElement.style.backgroundColor=t.toString());var n=e.getColor(Ce);n&&(this.listElement.style.borderColor=n.toString(),this.details.element.style.borderColor=n.toString(),this.messageElement.style.borderColor=n.toString(),this.detailsBorderColor=n.toString());var i=e.getColor(ie.S);i&&(this.detailsFocusBorderColor=i.toString()),this.details.setBorderWidth("hc"===e.type?2:1)},e.prototype.onListFocus=function(e){var t=this;if(!this.ignoreFocusEvents){if(!e.elements.length)return this.currentSuggestionDetails&&(this.currentSuggestionDetails.cancel(),this.currentSuggestionDetails=null,this.focusedItem=null),void this._ariaAlert(null);if(this.completionModel){var n=e.elements[0],i=e.indexes[0];this.firstFocusInCurrentList=!this.focusedItem,n!==this.focusedItem&&(this.currentSuggestionDetails&&(this.currentSuggestionDetails.cancel(),this.currentSuggestionDetails=null),this.focusedItem=n,this.list.reveal(i),this.currentSuggestionDetails=Object(v.f)(function(e){return be(t,void 0,void 0,function(){var t,i,r=this;return ye(this,function(o){switch(o.label){case 0:return t=Object(v.g)(function(){return r.showDetails(!0)},250),e.onCancellationRequested(function(){return t.dispose()}),[4,n.resolve(e)];case 1:return i=o.sent(),t.dispose(),[2,i]}})})}),this.currentSuggestionDetails.then(function(){i>=t.list.length||n!==t.list.element(i)||(t.ignoreFocusEvents=!0,t.list.splice(i,1,[n]),t.list.setFocus([i]),t.ignoreFocusEvents=!1,t.expandDocsSettingFromStorage()?t.showDetails(!1):Object(X.I)(t.element,"docs-side"),t._ariaAlert(t._getSuggestionAriaAlertLabel(n)))}).catch(s.e)),this.onDidFocusEmitter.fire({item:n,index:i,model:this.completionModel})}}},e.prototype.setState=function(t){if(this.element){var n=this.state!==t;switch(this.state=t,Object(X.R)(this.element,"frozen",4===t),t){case 0:Object(X.D)(this.messageElement,this.details.element,this.listElement),this.hide(),this.listHeight=0,n&&this.list.splice(0,this.list.length),this.focusedItem=null;break;case 1:this.messageElement.textContent=e.LOADING_MESSAGE,Object(X.D)(this.listElement,this.details.element),Object(X.Q)(this.messageElement),Object(X.I)(this.element,"docs-side"),this.show(),this.focusedItem=null;break;case 2:this.messageElement.textContent=e.NO_SUGGESTIONS_MESSAGE,Object(X.D)(this.listElement,this.details.element),Object(X.Q)(this.messageElement),Object(X.I)(this.element,"docs-side"),this.show(),this.focusedItem=null;break;case 3:case 4:Object(X.D)(this.messageElement),Object(X.Q)(this.listElement),this.show();break;case 5:Object(X.D)(this.messageElement),Object(X.Q)(this.details.element,this.listElement),this.show(),this._ariaAlert(this.details.getAriaLabel())}}},e.prototype.showTriggered=function(e,t){var n=this;0===this.state&&(this.isAuto=!!e,this.isAuto||(this.loadingTimeout=Object(v.g)(function(){return n.setState(1)},t)))},e.prototype.showSuggestions=function(e,t,n,i){if(this.preferDocPositionTop=!1,this.docsPositionPreviousWidgetY=null,this.loadingTimeout.dispose(),this.currentSuggestionDetails&&(this.currentSuggestionDetails.cancel(),this.currentSuggestionDetails=null),this.completionModel!==e&&(this.completionModel=e),n&&2!==this.state&&0!==this.state)this.setState(4);else{var r=this.completionModel.items.length,o=0===r;if(this.suggestWidgetMultipleSuggestions.set(r>1),o)i?this.setState(0):this.setState(2),this.completionModel=null;else{if(3!==this.state){var s=this.completionModel.stats;s.wasAutomaticallyTriggered=!!i,this.telemetryService.publicLog("suggestWidget",me({},s))}this.focusedItem=null,this.list.splice(0,this.list.length,this.completionModel.items),n?this.setState(4):this.setState(3),this.list.reveal(t,0),this.list.setFocus([t]),this.detailsBorderColor&&(this.details.element.style.borderColor=this.detailsBorderColor)}}},e.prototype.selectNextPage=function(){switch(this.state){case 0:return!1;case 5:return this.details.pageDown(),!0;case 1:return!this.isAuto;default:return this.list.focusNextPage(),!0}},e.prototype.selectNext=function(){switch(this.state){case 0:return!1;case 1:return!this.isAuto;default:return this.list.focusNext(1,!0),!0}},e.prototype.selectLast=function(){switch(this.state){case 0:return!1;case 5:return this.details.scrollBottom(),!0;case 1:return!this.isAuto;default:return this.list.focusLast(),!0}},e.prototype.selectPreviousPage=function(){switch(this.state){case 0:return!1;case 5:return this.details.pageUp(),!0;case 1:return!this.isAuto;default:return this.list.focusPreviousPage(),!0}},e.prototype.selectPrevious=function(){switch(this.state){case 0:return!1;case 1:return!this.isAuto;default:return this.list.focusPrevious(1,!0),!1}},e.prototype.selectFirst=function(){switch(this.state){case 0:return!1;case 5:return this.details.scrollTop(),!0;case 1:return!this.isAuto;default:return this.list.focusFirst(),!0}},e.prototype.getFocusedItem=function(){if(0!==this.state&&2!==this.state&&1!==this.state&&this.completionModel)return{item:this.list.getFocusedElements()[0],index:this.list.getFocus()[0],model:this.completionModel}},e.prototype.toggleDetailsFocus=function(){5===this.state?(this.setState(3),this.detailsBorderColor&&(this.details.element.style.borderColor=this.detailsBorderColor)):3===this.state&&this.expandDocsSettingFromStorage()&&(this.setState(5),this.detailsFocusBorderColor&&(this.details.element.style.borderColor=this.detailsFocusBorderColor)),this.telemetryService.publicLog2("suggestWidget:toggleDetailsFocus")},e.prototype.toggleDetails=function(){if(ke(this.list.getFocusedElements()[0]))if(this.expandDocsSettingFromStorage())this.updateExpandDocsSetting(!1),Object(X.D)(this.details.element),Object(X.I)(this.element,"docs-side"),Object(X.I)(this.element,"docs-below"),this.editor.layoutContentWidget(this),this.telemetryService.publicLog2("suggestWidget:collapseDetails");else{if(3!==this.state&&5!==this.state&&4!==this.state)return;this.updateExpandDocsSetting(!0),this.showDetails(!1),this._ariaAlert(this.details.getAriaLabel()),this.telemetryService.publicLog2("suggestWidget:expandDetails")}},e.prototype.showDetails=function(e){this.expandSideOrBelow(),Object(X.Q)(this.details.element),this.details.element.style.maxHeight=this.maxWidgetHeight+"px",e?this.details.renderLoading():this.details.renderItem(this.list.getFocusedElements()[0],this.explainMode),this.listElement.style.marginTop="0px",this.editor.layoutContentWidget(this),this.adjustDocsPosition(),this.editor.focus()},e.prototype.toggleExplainMode=function(){this.list.getFocusedElements()[0]&&this.expandDocsSettingFromStorage()&&(this.explainMode=!this.explainMode,this.showDetails(!1))},e.prototype.show=function(){var e=this,t=this.updateListHeight();t!==this.listHeight&&(this.editor.layoutContentWidget(this),this.listHeight=t),this.suggestWidgetVisible.set(!0),this.showTimeout.cancelAndSet(function(){Object(X.f)(e.element,"visible"),e.onDidShowEmitter.fire(e)},100)},e.prototype.hide=function(){this.suggestWidgetVisible.reset(),this.suggestWidgetMultipleSuggestions.reset(),Object(X.I)(this.element,"visible")},e.prototype.hideWidget=function(){this.loadingTimeout.dispose(),this.setState(0),this.onDidHideEmitter.fire(this)},e.prototype.getPosition=function(){if(0===this.state)return null;var e=[2,1];return this.preferDocPositionTop&&(e=[1]),{position:this.editor.getPosition(),preference:e}},e.prototype.getDomNode=function(){return this.element},e.prototype.getId=function(){return e.ID},e.prototype.updateListHeight=function(){var e=0;if(2===this.state||1===this.state)e=this.unfocusedHeight;else{var t=this.list.contentHeight/this.unfocusedHeight,n=this.editor.getConfiguration().contribInfo.suggest.maxVisibleSuggestions;e=Math.min(t,n)*this.unfocusedHeight}return this.element.style.lineHeight=this.unfocusedHeight+"px",this.listElement.style.height=e+"px",this.list.layout(e),e},e.prototype.adjustDocsPosition=function(){if(this.editor.hasModel()){var e=this.editor.getConfiguration().fontInfo.lineHeight,t=this.editor.getScrolledVisiblePosition(this.editor.getPosition()),n=Object(X.x)(this.editor.getDomNode()),i=n.left+t.left,r=n.top+t.top+t.height,o=Object(X.x)(this.element),s=o.left,a=o.top;if(this.docsPositionPreviousWidgetY&&this.docsPositionPreviousWidgetYa&&this.details.element.offsetHeight>this.listElement.offsetHeight&&(this.listElement.style.marginTop=this.details.element.offsetHeight-this.listElement.offsetHeight+"px")}},e.prototype.expandSideOrBelow=function(){if(!ke(this.focusedItem)&&this.firstFocusInCurrentList)return Object(X.I)(this.element,"docs-side"),void Object(X.I)(this.element,"docs-below");var e=this.element.style.maxWidth.match(/(\d+)px/);!e||Number(e[1])=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},Te=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},Pe=function(e){function t(n,i){var r=e.call(this)||this;return r._editor=n,r._enabled=!1,r._ckAtEnd=t.AtEnd.bindTo(i),r._register(r._editor.onDidChangeConfiguration(function(e){return e.contribInfo&&r._update()})),r._update(),r}return De(t,e),t.prototype.dispose=function(){e.prototype.dispose.call(this),Object(a.f)(this._selectionListener),this._ckAtEnd.reset()},t.prototype._update=function(){var e=this,t="on"===this._editor.getConfiguration().contribInfo.tabCompletion;if(this._enabled!==t)if(this._enabled=t,this._enabled){var n=function(){if(e._editor.hasModel()){var t=e._editor.getModel(),n=e._editor.getSelection(),i=t.getWordAtPosition(n.getStartPosition());i?e._ckAtEnd.set(i.endColumn===n.getStartPosition().column):e._ckAtEnd.set(!1)}else e._ckAtEnd.set(!1)};this._selectionListener=this._editor.onDidChangeCursorSelection(n),n()}else this._selectionListener&&(this._ckAtEnd.reset(),this._selectionListener.dispose(),this._selectionListener=void 0)},t.AtEnd=new T.d("atEndOfWord",!1),t=Me([Te(1,T.c)],t)}(a.a),Ae=n("606G"),Re=n("KIxu"),Fe=n("GfE5"),je=function(){function e(e,t,n){var i=this;this._disposables=new a.b,this._disposables.add(t.onDidShow(function(){return i._onItem(t.getFocusedItem())})),this._disposables.add(t.onDidFocus(this._onItem,this)),this._disposables.add(t.onDidHide(this.reset,this)),this._disposables.add(e.onWillType(function(t){if(i._active){var r=t.charCodeAt(t.length-1);i._active.acceptCharacters.has(r)&&e.getConfiguration().contribInfo.acceptSuggestionOnCommitCharacter&&n(i._active.item)}}))}return e.prototype._onItem=function(e){if(e&&Object(o.n)(e.item.completion.commitCharacters)){if(!this._active||this._active.item.item!==e.item){for(var t=new Fe.b,n=0,i=e.item.completion.commitCharacters;n0&&t.add(r.charCodeAt(0))}this._active={acceptCharacters:t,item:e}}}else this.reset()},e.prototype.reset=function(){this._active=void 0},e.prototype.dispose=function(){this._disposables.dispose()},e}();n.d(t,"SuggestController",function(){return Ke}),n.d(t,"TriggerSuggestAction",function(){return qe});var We=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),Be=this&&this.__assign||function(){return(Be=Object.assign||function(e){for(var t,n=1,i=arguments.length;n=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},He=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},ze=!1,Ue=function(){function e(e,t){if(this._model=e,this._position=t,e.getLineMaxColumn(t.lineNumber)!==t.column){var n=e.getOffsetAt(t),i=e.getPositionAt(n+1);this._marker=e.deltaDecorations([],[{range:l.a.fromPositions(t,i),options:{stickiness:1}}])}}return e.prototype.dispose=function(){this._marker&&!this._model.isDisposed()&&this._model.deltaDecorations(this._marker,[])},e.prototype.delta=function(e){if(this._model.isDisposed()||this._position.lineNumber!==e.lineNumber)return 0;if(this._marker){var t=this._model.getDecorationRange(this._marker[0]);return this._model.getOffsetAt(t.getStartPosition())-this._model.getOffsetAt(e)}return this._model.getLineMaxColumn(e.lineNumber)-e.column},e}(),Ke=function(){function e(e,t,n,i,r,o){var s=this;this._editor=e,this._memoryService=n,this._commandService=i,this._contextKeyService=r,this._instantiationService=o,this._lineSuffix=new a.d,this._toDispose=new a.b,this._model=new Y(this._editor,t),this._widget=new v.b(function(){var e=s._instantiationService.createInstance(Ie,s._editor);s._toDispose.add(e),s._toDispose.add(e.onDidSelect(function(e){return s._insertSuggestion(e,!1,!0)},s));var t=new je(s._editor,e,function(e){return s._insertSuggestion(e,!1,!0)});s._toDispose.add(t),s._toDispose.add(s._model.onDidSuggest(function(e){0===e.completionModel.items.length&&t.reset()}));var n=P.b.MakesTextEdit.bindTo(s._contextKeyService);return s._toDispose.add(e.onDidFocus(function(e){var t=e.item,i=s._editor.getPosition(),r=t.completion.range.startColumn,o=i.column,a=!0;"smart"!==s._editor.getConfiguration().contribInfo.acceptSuggestionOnEnter||2!==s._model.state||t.completion.command||t.completion.additionalTextEdits||4&t.completion.insertTextRules||o-r!==t.completion.insertText.length||(a=s._editor.getModel().getValueInRange({startLineNumber:i.lineNumber,startColumn:r,endLineNumber:i.lineNumber,endColumn:o})!==t.completion.insertText);n.set(a)})),s._toDispose.add(Object(a.h)(function(){return n.reset()})),e}),this._alternatives=new v.b(function(){return s._toDispose.add(new F(s._editor,s._contextKeyService))}),this._toDispose.add(o.createInstance(Pe,e)),this._toDispose.add(this._model.onDidTrigger(function(e){s._widget.getValue().showTriggered(e.auto,e.shy?250:50),s._lineSuffix.value=new Ue(s._editor.getModel(),e.position)})),this._toDispose.add(this._model.onDidSuggest(function(e){if(!e.shy){var t=s._memoryService.select(s._editor.getModel(),s._editor.getPosition(),e.completionModel.items);s._widget.getValue().showSuggestions(e.completionModel,t,e.isFrozen,e.auto)}})),this._toDispose.add(this._model.onDidCancel(function(e){e.retrigger||s._widget.getValue().hideWidget()})),this._toDispose.add(this._editor.onDidBlurEditorWidget(function(){ze||(s._model.cancel(),s._model.clear())}));var u=P.b.AcceptSuggestionsOnEnter.bindTo(r),c=function(){var e=s._editor.getConfiguration().contribInfo.acceptSuggestionOnEnter;u.set("on"===e||"smart"===e)};this._toDispose.add(this._editor.onDidChangeConfiguration(function(){return c()})),c()}return e.get=function(t){return t.getContribution(e.ID)},e.prototype.getId=function(){return e.ID},e.prototype.dispose=function(){this._alternatives.dispose(),this._toDispose.dispose(),this._widget.dispose(),this._model.dispose(),this._lineSuffix.dispose()},e.prototype._insertSuggestion=function(e,t,n){var i,r=this;if(!e||!e.item)return this._alternatives.getValue().reset(),this._model.cancel(),void this._model.clear();if(this._editor.hasModel()){var o=this._editor.getModel(),a=o.getAlternativeVersionId(),u=e.item,d=u.completion,p=u.position,g=this._editor.getPosition().column-p.column;n&&this._editor.pushUndoStop(),Array.isArray(d.additionalTextEdits)&&this._editor.executeEdits("suggestController.additionalTextEdits",d.additionalTextEdits.map(function(e){return c.a.replace(l.a.lift(e.range),e.text)})),this._memoryService.memorize(o,this._editor.getPosition(),e.item);var m=d.insertText;4&d.insertTextRules||(m=f.c.escape(m));var v=p.column-d.range.startColumn,_=d.range.endColumn-p.column,b=this._lineSuffix.value?this._lineSuffix.value.delta(this._editor.getPosition()):0;h.SnippetController2.get(this._editor).insert(m,{overwriteBefore:v+g,overwriteAfter:_+b,undoStopBefore:!1,undoStopAfter:!1,adjustWhitespace:!(1&d.insertTextRules)}),n&&this._editor.pushUndoStop(),d.command?d.command.id===qe.id?this._model.trigger({auto:!0,shy:!1},!0):((i=this._commandService).executeCommand.apply(i,[d.command.id].concat(d.command.arguments?d.command.arguments.slice():[])).catch(s.e).finally(function(){return r._model.clear()}),this._model.cancel()):(this._model.cancel(),this._model.clear()),t&&this._alternatives.getValue().set(e,function(e){for(;o.canUndo();){a!==o.getAlternativeVersionId()&&o.undo(),r._insertSuggestion(e,!1,!1);break}}),this._alertCompletionItem(e.item)}},e.prototype._alertCompletionItem=function(e){var t=e.completion;if(Object(o.n)(t.additionalTextEdits)){var n=D.a("arai.alert.snippet","Accepting '{0}' made {1} additional edits",t.label,t.additionalTextEdits.length);Object(r.a)(n)}},e.prototype.triggerSuggest=function(e){this._editor.hasModel()&&(this._model.trigger({auto:!1,shy:!1},!1,e),this._editor.revealLine(this._editor.getPosition().lineNumber,0),this._editor.focus())},e.prototype.triggerSuggestAndAcceptBest=function(e){var t=this;if(this._editor.hasModel()){var n=this._editor.getPosition(),i=function(){n.equals(t._editor.getPosition())&&t._commandService.executeCommand(e.fallback)};j.b.once(this._model.onDidTrigger)(function(e){var n=[];j.b.any(t._model.onDidTrigger,t._model.onDidCancel)(function(){Object(a.f)(n),i()},void 0,n),t._model.onDidSuggest(function(e){var r=e.completionModel;if(Object(a.f)(n),0!==r.items.length){var o=t._memoryService.select(t._editor.getModel(),t._editor.getPosition(),r.items),s=r.items[o];!function(e){if(4&e.completion.insertTextRules||e.completion.additionalTextEdits)return!0;var n=t._editor.getPosition(),i=e.completion.range.startColumn,r=n.column;return r-i!==e.completion.insertText.length||t._editor.getModel().getValueInRange({startLineNumber:n.lineNumber,startColumn:i,endLineNumber:n.lineNumber,endColumn:r})!==e.completion.insertText}(s)?i():(t._editor.pushUndoStop(),t._insertSuggestion({index:o,item:s,model:r},!0,!1))}else i()},void 0,n)}),this._model.trigger({auto:!1,shy:!0}),this._editor.revealLine(n.lineNumber,0),this._editor.focus()}},e.prototype.acceptSelectedSuggestion=function(e){var t=this._widget.getValue().getFocusedItem();this._insertSuggestion(t,!!e,!0)},e.prototype.acceptNextSuggestion=function(){this._alternatives.getValue().next()},e.prototype.acceptPrevSuggestion=function(){this._alternatives.getValue().prev()},e.prototype.cancelSuggestWidget=function(){this._model.cancel(),this._model.clear(),this._widget.getValue().hideWidget()},e.prototype.selectNextSuggestion=function(){this._widget.getValue().selectNext()},e.prototype.selectNextPageSuggestion=function(){this._widget.getValue().selectNextPage()},e.prototype.selectLastSuggestion=function(){this._widget.getValue().selectLast()},e.prototype.selectPrevSuggestion=function(){this._widget.getValue().selectPrevious()},e.prototype.selectPrevPageSuggestion=function(){this._widget.getValue().selectPreviousPage()},e.prototype.selectFirstSuggestion=function(){this._widget.getValue().selectFirst()},e.prototype.toggleSuggestionDetails=function(){this._widget.getValue().toggleDetails()},e.prototype.toggleExplainMode=function(){this._widget.getValue().toggleExplainMode()},e.prototype.toggleSuggestionFocus=function(){this._widget.getValue().toggleDetailsFocus()},e.ID="editor.contrib.suggestController",e=Ve([He(1,Ae.a),He(2,E),He(3,M.b),He(4,T.c),He(5,_.a)],e)}(),qe=function(e){function t(){return e.call(this,{id:t.id,label:D.a("suggest.trigger.label","Trigger Suggest"),alias:"Trigger Suggest",precondition:T.a.and(d.a.writable,d.a.hasCompletionItemProvider),kbOpts:{kbExpr:d.a.textInputFocus,primary:2058,mac:{primary:266},weight:100}})||this}return We(t,e),t.prototype.run=function(e,t){var n=Ke.get(t);n&&n.triggerSuggest()},t.id="editor.action.triggerSuggest",t}(u.b);Object(u.h)(Ke),Object(u.f)(qe);var Ge=u.c.bindToContribution(Ke.get);Object(u.g)(new Ge({id:"acceptSelectedSuggestion",precondition:P.b.Visible,handler:function(e){return e.acceptSelectedSuggestion(!0)},kbOpts:{weight:190,kbExpr:d.a.textInputFocus,primary:2}})),Object(u.g)(new Ge({id:"acceptSelectedSuggestionOnEnter",precondition:P.b.Visible,handler:function(e){return e.acceptSelectedSuggestion(!1)},kbOpts:{weight:190,kbExpr:T.a.and(d.a.textInputFocus,P.b.AcceptSuggestionsOnEnter,P.b.MakesTextEdit),primary:3}})),Object(u.g)(new Ge({id:"hideSuggestWidget",precondition:P.b.Visible,handler:function(e){return e.cancelSuggestWidget()},kbOpts:{weight:190,kbExpr:d.a.textInputFocus,primary:9,secondary:[1033]}})),Object(u.g)(new Ge({id:"selectNextSuggestion",precondition:T.a.and(P.b.Visible,P.b.MultipleSuggestions),handler:function(e){return e.selectNextSuggestion()},kbOpts:{weight:190,kbExpr:d.a.textInputFocus,primary:18,secondary:[2066],mac:{primary:18,secondary:[2066,300]}}})),Object(u.g)(new Ge({id:"selectNextPageSuggestion",precondition:T.a.and(P.b.Visible,P.b.MultipleSuggestions),handler:function(e){return e.selectNextPageSuggestion()},kbOpts:{weight:190,kbExpr:d.a.textInputFocus,primary:12,secondary:[2060]}})),Object(u.g)(new Ge({id:"selectLastSuggestion",precondition:T.a.and(P.b.Visible,P.b.MultipleSuggestions),handler:function(e){return e.selectLastSuggestion()}})),Object(u.g)(new Ge({id:"selectPrevSuggestion",precondition:T.a.and(P.b.Visible,P.b.MultipleSuggestions),handler:function(e){return e.selectPrevSuggestion()},kbOpts:{weight:190,kbExpr:d.a.textInputFocus,primary:16,secondary:[2064],mac:{primary:16,secondary:[2064,302]}}})),Object(u.g)(new Ge({id:"selectPrevPageSuggestion",precondition:T.a.and(P.b.Visible,P.b.MultipleSuggestions),handler:function(e){return e.selectPrevPageSuggestion()},kbOpts:{weight:190,kbExpr:d.a.textInputFocus,primary:11,secondary:[2059]}})),Object(u.g)(new Ge({id:"selectFirstSuggestion",precondition:T.a.and(P.b.Visible,P.b.MultipleSuggestions),handler:function(e){return e.selectFirstSuggestion()}})),Object(u.g)(new Ge({id:"toggleSuggestionDetails",precondition:P.b.Visible,handler:function(e){return e.toggleSuggestionDetails()},kbOpts:{weight:190,kbExpr:d.a.textInputFocus,primary:2058,mac:{primary:266}}})),Object(u.g)(new Ge({id:"toggleExplainMode",precondition:P.b.Visible,handler:function(e){return e.toggleExplainMode()},kbOpts:{weight:100,primary:2133}})),Object(u.g)(new Ge({id:"toggleSuggestionFocus",precondition:P.b.Visible,handler:function(e){return e.toggleSuggestionFocus()},kbOpts:{weight:190,kbExpr:d.a.textInputFocus,primary:2570,mac:{primary:778}}})),Object(u.g)(new Ge({id:"insertBestCompletion",precondition:T.a.and(T.a.equals("config.editor.tabCompletion","on"),Pe.AtEnd,P.b.Visible.toNegated(),F.OtherSuggestions.toNegated(),h.SnippetController2.InSnippetMode.toNegated()),handler:function(e,t){e.triggerSuggestAndAcceptBest(Object(Re.h)(t)?Be({fallback:"tab"},t):{fallback:"tab"})},kbOpts:{weight:190,primary:2}})),Object(u.g)(new Ge({id:"insertNextSuggestion",precondition:T.a.and(T.a.equals("config.editor.tabCompletion","on"),F.OtherSuggestions,P.b.Visible.toNegated(),h.SnippetController2.InSnippetMode.toNegated()),handler:function(e){return e.acceptNextSuggestion()},kbOpts:{weight:190,kbExpr:d.a.textInputFocus,primary:2}})),Object(u.g)(new Ge({id:"insertPrevSuggestion",precondition:T.a.and(T.a.equals("config.editor.tabCompletion","on"),F.OtherSuggestions,P.b.Visible.toNegated(),h.SnippetController2.InSnippetMode.toNegated()),handler:function(e){return e.acceptPrevSuggestion()},kbOpts:{weight:190,kbExpr:d.a.textInputFocus,primary:1026}}))},"7dSG":function(e,t,n){"use strict";(function(t,i){var r=n("ypnx");function o(e){var t=this;this.next=null,this.entry=null,this.finish=function(){!function(e,t,n){var i=e.entry;e.entry=null;for(;i;){var r=i.callback;t.pendingcb--,r(n),i=i.next}t.corkedRequestsFree?t.corkedRequestsFree.next=e:t.corkedRequestsFree=e}(t,e)}}e.exports=v;var s,a=!t.browser&&["v0.10","v0.9."].indexOf(t.version.slice(0,5))>-1?setImmediate:r.nextTick;v.WritableState=m;var u=n("jOgh");u.inherits=n("LC74");var c={deprecate:n("iP15")},l=n("UcPO"),d=n("kkc6").Buffer,h=i.Uint8Array||function(){};var f,p=n("x0Ha");function g(){}function m(e,t){s=s||n("DsFX"),e=e||{};var i=t instanceof s;this.objectMode=!!e.objectMode,i&&(this.objectMode=this.objectMode||!!e.writableObjectMode);var u=e.highWaterMark,c=e.writableHighWaterMark,l=this.objectMode?16:16384;this.highWaterMark=u||0===u?u:i&&(c||0===c)?c:l,this.highWaterMark=Math.floor(this.highWaterMark),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var d=!1===e.decodeStrings;this.decodeStrings=!d,this.defaultEncoding=e.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){!function(e,t){var n=e._writableState,i=n.sync,o=n.writecb;if(function(e){e.writing=!1,e.writecb=null,e.length-=e.writelen,e.writelen=0}(n),t)!function(e,t,n,i,o){--t.pendingcb,n?(r.nextTick(o,i),r.nextTick(S,e,t),e._writableState.errorEmitted=!0,e.emit("error",i)):(o(i),e._writableState.errorEmitted=!0,e.emit("error",i),S(e,t))}(e,n,i,t,o);else{var s=w(n);s||n.corked||n.bufferProcessing||!n.bufferedRequest||y(e,n),i?a(b,e,n,s,o):b(e,n,s,o)}}(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.bufferedRequestCount=0,this.corkedRequestsFree=new o(this)}function v(e){if(s=s||n("DsFX"),!(f.call(v,this)||this instanceof s))return new v(e);this._writableState=new m(e,this),this.writable=!0,e&&("function"==typeof e.write&&(this._write=e.write),"function"==typeof e.writev&&(this._writev=e.writev),"function"==typeof e.destroy&&(this._destroy=e.destroy),"function"==typeof e.final&&(this._final=e.final)),l.call(this)}function _(e,t,n,i,r,o,s){t.writelen=i,t.writecb=s,t.writing=!0,t.sync=!0,n?e._writev(r,t.onwrite):e._write(r,o,t.onwrite),t.sync=!1}function b(e,t,n,i){n||function(e,t){0===t.length&&t.needDrain&&(t.needDrain=!1,e.emit("drain"))}(e,t),t.pendingcb--,i(),S(e,t)}function y(e,t){t.bufferProcessing=!0;var n=t.bufferedRequest;if(e._writev&&n&&n.next){var i=t.bufferedRequestCount,r=new Array(i),s=t.corkedRequestsFree;s.entry=n;for(var a=0,u=!0;n;)r[a]=n,n.isBuf||(u=!1),n=n.next,a+=1;r.allBuffers=u,_(e,t,!0,t.length,r,"",s.finish),t.pendingcb++,t.lastBufferedRequest=null,s.next?(t.corkedRequestsFree=s.next,s.next=null):t.corkedRequestsFree=new o(t),t.bufferedRequestCount=0}else{for(;n;){var c=n.chunk,l=n.encoding,d=n.callback;if(_(e,t,!1,t.objectMode?1:c.length,c,l,d),n=n.next,t.bufferedRequestCount--,t.writing)break}null===n&&(t.lastBufferedRequest=null)}t.bufferedRequest=n,t.bufferProcessing=!1}function w(e){return e.ending&&0===e.length&&null===e.bufferedRequest&&!e.finished&&!e.writing}function C(e,t){e._final(function(n){t.pendingcb--,n&&e.emit("error",n),t.prefinished=!0,e.emit("prefinish"),S(e,t)})}function S(e,t){var n=w(t);return n&&(!function(e,t){t.prefinished||t.finalCalled||("function"==typeof e._final?(t.pendingcb++,t.finalCalled=!0,r.nextTick(C,e,t)):(t.prefinished=!0,e.emit("prefinish")))}(e,t),0===t.pendingcb&&(t.finished=!0,e.emit("finish"))),n}u.inherits(v,l),m.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},function(){try{Object.defineProperty(m.prototype,"buffer",{get:c.deprecate(function(){return this.getBuffer()},"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(e){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(f=Function.prototype[Symbol.hasInstance],Object.defineProperty(v,Symbol.hasInstance,{value:function(e){return!!f.call(this,e)||this===v&&(e&&e._writableState instanceof m)}})):f=function(e){return e instanceof this},v.prototype.pipe=function(){this.emit("error",new Error("Cannot pipe, not readable"))},v.prototype.write=function(e,t,n){var i,o=this._writableState,s=!1,a=!o.objectMode&&(i=e,d.isBuffer(i)||i instanceof h);return a&&!d.isBuffer(e)&&(e=function(e){return d.from(e)}(e)),"function"==typeof t&&(n=t,t=null),a?t="buffer":t||(t=o.defaultEncoding),"function"!=typeof n&&(n=g),o.ended?function(e,t){var n=new Error("write after end");e.emit("error",n),r.nextTick(t,n)}(this,n):(a||function(e,t,n,i){var o=!0,s=!1;return null===n?s=new TypeError("May not write null values to stream"):"string"==typeof n||void 0===n||t.objectMode||(s=new TypeError("Invalid non-string/buffer chunk")),s&&(e.emit("error",s),r.nextTick(i,s),o=!1),o}(this,o,e,n))&&(o.pendingcb++,s=function(e,t,n,i,r,o){if(!n){var s=function(e,t,n){e.objectMode||!1===e.decodeStrings||"string"!=typeof t||(t=d.from(t,n));return t}(t,i,r);i!==s&&(n=!0,r="buffer",i=s)}var a=t.objectMode?1:i.length;t.length+=a;var u=t.length-1))throw new TypeError("Unknown encoding: "+e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(v.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),v.prototype._write=function(e,t,n){n(new Error("_write() is not implemented"))},v.prototype._writev=null,v.prototype.end=function(e,t,n){var i=this._writableState;"function"==typeof e?(n=e,e=null,t=null):"function"==typeof t&&(n=t,t=null),null!==e&&void 0!==e&&this.write(e,t),i.corked&&(i.corked=1,this.uncork()),i.ending||i.finished||function(e,t,n){t.ending=!0,S(e,t),n&&(t.finished?r.nextTick(n):e.once("finish",n));t.ended=!0,e.writable=!1}(this,i,n)},Object.defineProperty(v.prototype,"destroyed",{get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(e){this._writableState&&(this._writableState.destroyed=e)}}),v.prototype.destroy=p.destroy,v.prototype._undestroy=p.undestroy,v.prototype._destroy=function(e,t){this.end(),t(e)}}).call(t,n("W2nU"),n("DuR2"))},"7flL":function(e,t,n){var i=n("q5VG"),r=n("X3l8").Buffer,o=n("VI/i"),s=n("annC"),a=n("OMJi"),u="secret must be a string or buffer",c="key must be a string or a buffer",l="key must be a string, a buffer or an object",d="function"==typeof o.createPublicKey;function h(e){if(!r.isBuffer(e)&&"string"!=typeof e){if(!d)throw m(c);if("object"!=typeof e)throw m(c);if("string"!=typeof e.type)throw m(c);if("string"!=typeof e.asymmetricKeyType)throw m(c);if("function"!=typeof e.export)throw m(c)}}function f(e){if(!r.isBuffer(e)&&"string"!=typeof e&&"object"!=typeof e)throw m(l)}function p(e){return e.replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_")}function g(e){var t=4-(e=e.toString()).length%4;if(4!==t)for(var n=0;n=0){var n=e.split("!=");return d.create(n[0].trim(),this._deserializeValue(n[1],t))}if(e.indexOf("==")>=0){n=e.split("==");return l.create(n[0].trim(),this._deserializeValue(n[1],t))}if(e.indexOf("=~")>=0){n=e.split("=~");return f.create(n[0].trim(),this._deserializeRegexValue(n[1],t))}return/^\!\s*/.test(e)?h.create(e.substr(1).trim()):c.create(e)},e._deserializeValue=function(e,t){if("true"===(e=e.trim()))return!0;if("false"===e)return!1;var n=/^'([^']*)'$/.exec(e);return n?n[1].trim():e},e._deserializeRegexValue=function(e,t){if(Object(r.u)(e)){if(t)throw new Error("missing regexp-value for =~-expression");return console.warn("missing regexp-value for =~-expression"),null}var n=e.indexOf("/"),i=e.lastIndexOf("/");if(n===i||n<0){if(t)throw new Error("bad regexp-value '"+e+"', missing /-enclosure");return console.warn("bad regexp-value '"+e+"', missing /-enclosure"),null}var o=e.slice(n+1,i),s="i"===e[i+1]?"i":"";try{return new RegExp(o,s)}catch(n){if(t)throw new Error("bad regexp-value '"+e+"', parse error: "+n);return console.warn("bad regexp-value '"+e+"', parse error: "+n),null}},e}();function u(e,t){var n=e.getType(),i=t.getType();if(n!==i)return n-i;switch(n){case 1:case 2:case 3:case 4:case 6:case 7:case 5:return e.cmp(t);default:throw new Error("Unknown ContextKeyExpr!")}}var c=function(){function e(e){this.key=e}return e.create=function(t){return new e(t)},e.prototype.getType=function(){return 1},e.prototype.cmp=function(e){return this.keye.key?1:0},e.prototype.equals=function(t){return t instanceof e&&this.key===t.key},e.prototype.evaluate=function(e){return!!e.getValue(this.key)},e.prototype.keys=function(){return[this.key]},e.prototype.negate=function(){return h.create(this.key)},e}(),l=function(){function e(e,t){this.key=e,this.value=t}return e.create=function(t,n){return"boolean"==typeof n?n?c.create(t):h.create(t):new e(t,n)},e.prototype.getType=function(){return 3},e.prototype.cmp=function(e){return this.keye.key?1:this.valuee.value?1:0},e.prototype.equals=function(t){return t instanceof e&&(this.key===t.key&&this.value===t.value)},e.prototype.evaluate=function(e){return e.getValue(this.key)==this.value},e.prototype.keys=function(){return[this.key]},e.prototype.negate=function(){return d.create(this.key,this.value)},e}(),d=function(){function e(e,t){this.key=e,this.value=t}return e.create=function(t,n){return"boolean"==typeof n?n?h.create(t):c.create(t):new e(t,n)},e.prototype.getType=function(){return 4},e.prototype.cmp=function(e){return this.keye.key?1:this.valuee.value?1:0},e.prototype.equals=function(t){return t instanceof e&&(this.key===t.key&&this.value===t.value)},e.prototype.evaluate=function(e){return e.getValue(this.key)!=this.value},e.prototype.keys=function(){return[this.key]},e.prototype.negate=function(){return l.create(this.key,this.value)},e}(),h=function(){function e(e){this.key=e}return e.create=function(t){return new e(t)},e.prototype.getType=function(){return 2},e.prototype.cmp=function(e){return this.keye.key?1:0},e.prototype.equals=function(t){return t instanceof e&&this.key===t.key},e.prototype.evaluate=function(e){return!e.getValue(this.key)},e.prototype.keys=function(){return[this.key]},e.prototype.negate=function(){return c.create(this.key)},e}(),f=function(){function e(e,t){this.key=e,this.regexp=t}return e.create=function(t,n){return new e(t,n)},e.prototype.getType=function(){return 6},e.prototype.cmp=function(e){if(this.keye.key)return 1;var t=this.regexp?this.regexp.source:"",n=e.regexp?e.regexp.source:"";return tn?1:0},e.prototype.equals=function(t){if(t instanceof e){var n=this.regexp?this.regexp.source:"",i=t.regexp?t.regexp.source:"";return this.key===t.key&&n===i}return!1},e.prototype.evaluate=function(e){var t=e.getValue(this.key);return!!this.regexp&&this.regexp.test(t)},e.prototype.keys=function(){return[this.key]},e.prototype.negate=function(){return p.create(this)},e}(),p=function(){function e(e){this._actual=e}return e.create=function(t){return new e(t)},e.prototype.getType=function(){return 7},e.prototype.cmp=function(e){return this._actual.cmp(e._actual)},e.prototype.equals=function(t){return t instanceof e&&this._actual.equals(t._actual)},e.prototype.evaluate=function(e){return!this._actual.evaluate(e)},e.prototype.keys=function(){return this._actual.keys()},e.prototype.negate=function(){return this._actual},e}(),g=function(){function e(e){this.expr=e}return e.create=function(t){var n=e._normalizeArr(t);if(0!==n.length)return 1===n.length?n[0]:new e(n)},e.prototype.getType=function(){return 5},e.prototype.cmp=function(e){if(this.expr.lengthe.expr.length)return 1;for(var t=0,n=this.expr.length;t1;){for(var s=t.shift(),u=t.shift(),c=[],l=0,d=o(s);l0&&(n._decorations=n._editor.deltaDecorations(n._decorations,[])),n._updateBracketsSoon.schedule()})),n}return _(t,e),t.get=function(e){return e.getContribution(t.ID)},t.prototype.getId=function(){return t.ID},t.prototype.jumpToBracket=function(){if(this._editor.hasModel()){var e=this._editor.getModel(),t=this._editor.getSelections().map(function(t){var n=t.getStartPosition(),i=e.matchBracket(n),r=null;if(i)i[0].containsPosition(n)?r=i[1].getStartPosition():i[1].containsPosition(n)&&(r=i[0].getStartPosition());else{var o=e.findNextBracket(n);o&&o.range&&(r=o.range.getStartPosition())}return r?new l.a(r.lineNumber,r.column,r.lineNumber,r.column):new l.a(n.lineNumber,n.column,n.lineNumber,n.column)});this._editor.setSelections(t),this._editor.revealRange(t[0])}},t.prototype.selectToBracket=function(){if(this._editor.hasModel()){var e=this._editor.getModel(),t=[];this._editor.getSelections().forEach(function(n){var i=n.getStartPosition(),r=e.matchBracket(i),o=null,s=null;if(!r){var a=e.findNextBracket(i);a&&a.range&&(r=e.matchBracket(a.range.getStartPosition()))}r&&(r[0].startLineNumber===r[1].startLineNumber?(o=r[1].startColumn0&&(this._editor.setSelections(t),this._editor.revealRange(t[0]))}},t.prototype._updateBrackets=function(){if(this._matchBrackets){this._recomputeBrackets();for(var e=[],n=0,i=0,r=this._lastBracketsData.length;i1&&r.sort(c.a.compare);var l=[],d=0,h=0,f=n.length;for(s=0,a=r.length;s0&&void 0!==arguments[0]&&arguments[0];var e=[].concat(r()(this.provider));return"sql"===this.lang&&e.push.apply(e,r()(this.sqlHints)),e},registerCustomHintsProvider:function(){var e=this;this.providerDisposeID=o.languages.registerCompletionItemProvider(this.lang,{provideCompletionItems:function(t,n,i){var r=t.getWordUntilPosition(n);return{suggestions:function(e,t,n){n.word;var i=[];return e.length&&(i=e.map(function(e){return{label:e.name,kind:e.type?o.languages.CompletionItemKind[e.type]:o.languages.CompletionItemKind.Function,documentation:e.documentation,insertText:(n=e.name,n),detail:e.detail||"EMQX",range:t};var n})),i}(e.getHints(r),{startLineNumber:n.lineNumber,endLineNumber:n.lineNumber,startColumn:r.startColumn,endColumn:r.endColumn},r)}},triggerCharacters:[" "]})},registerCustomHoverProvider:function(){var e=this;o.languages.register({id:this.lang}),this.hoverDisposeID=o.languages.registerHoverProvider(this.lang,{provideHover:function(t,n){if(!t.getWordAtPosition(n))return{};var i,r,o,s=t.getWordAtPosition(n).word;return{contents:(i=s,r=e.provider,o=[],r.forEach(function(e){var t=e.name;e.name.match(/\$events\//)&&(t=e.name.split("/")[1].replace('"',"")),i===t&&o.push({value:function(e){var t=e.name,n=e.default,i=e.valueType;return i&&(t=t+": "+i),n?t+", value: "+n:t}(e)},{value:e.documentation})}),o)}}})}},mounted:function(){this.initEditor()},created:function(){var e=this;this.defineTheme(),window.onresize=function(){e.editor&&e.editor.layout()},this.provider.length&&(this.registerCustomHintsProvider(),this.registerCustomHoverProvider())},beforeDestroy:function(){this.editor&&(this.editor.getModel().dispose(),this.editor.dispose(),this.editor=null),this.providerDisposeID&&this.providerDisposeID.dispose(),this.hoverDisposeID&&this.hoverDisposeID.dispose()}},c={render:function(){var e=this.$createElement;return(this._self._c||e)("div",{staticClass:"monaco-view",attrs:{id:"monaco-"+this.id}})},staticRenderFns:[]};var l=n("VU/8")(u,c,!1,function(e){n("u9OF")},null,null);t.a=l.exports},"9Io0":function(e,t,n){var i=n("X3l8").Buffer,r=n("sDkV"),o=n("7flL"),s=n("9DG0"),a=n("nOGB"),u=/^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/;function c(e){if(function(e){return"[object Object]"===Object.prototype.toString.call(e)}(e))return e;try{return JSON.parse(e)}catch(e){return}}function l(e){var t=e.split(".",1)[0];return c(i.from(t,"base64").toString("binary"))}function d(e){return e.split(".")[2]}function h(e){return u.test(e)&&!!l(e)}function f(e,t,n){if(!t){var i=new Error("Missing algorithm parameter for jws.verify");throw i.code="MISSING_ALGORITHM",i}var r=d(e=a(e)),s=function(e){return e.split(".",2).join(".")}(e);return o(t).verify(s,r,n)}function p(e,t){if(t=t||{},!h(e=a(e)))return null;var n=l(e);if(!n)return null;var r=function(e,t){t=t||"utf8";var n=e.split(".")[1];return i.from(n,"base64").toString(t)}(e);return("JWT"===n.typ||t.json)&&(r=JSON.parse(r,t.encoding)),{header:n,payload:r,signature:d(e)}}function g(e){var t=(e=e||{}).secret||e.publicKey||e.key,n=new r(t);this.readable=!0,this.algorithm=e.algorithm,this.encoding=e.encoding,this.secret=this.publicKey=this.key=n,this.signature=new r(e.signature),this.secret.once("close",function(){!this.signature.writable&&this.readable&&this.verify()}.bind(this)),this.signature.once("close",function(){!this.secret.writable&&this.readable&&this.verify()}.bind(this))}n("OMJi").inherits(g,s),g.prototype.verify=function(){try{var e=f(this.signature.buffer,this.algorithm,this.key.buffer),t=p(this.signature.buffer,this.encoding);return this.emit("done",e,t),this.emit("data",e),this.emit("end"),this.readable=!1,e}catch(e){this.readable=!1,this.emit("error",e),this.emit("close")}},g.decode=p,g.isValid=h,g.verify=f,e.exports=g},"9P96":function(e,t,n){t.publicEncrypt=n("9hYg"),t.privateDecrypt=n("fxuI"),t.privateEncrypt=function(e,n){return t.publicEncrypt(e,n,!0)},t.publicDecrypt=function(e,n){return t.privateDecrypt(e,n,!0)}},"9XyG":function(e,t,n){"use strict";n.d(t,"a",function(){return u}),n.d(t,"c",function(){return c}),n.d(t,"b",function(){return l});var i=n("hK2W"),r=n("Kp7x"),o=n("PCC9"),s=n("Fllr"),a=n("RWr8"),u=new(function(){function e(){this._onDidChangeLanguages=new r.a,this.onDidChangeLanguages=this._onDidChangeLanguages.event,this._languages=[],this._dynamicLanguages=[]}return e.prototype.registerLanguage=function(e){this._languages.push(e),this._onDidChangeLanguages.fire(void 0)},e.prototype.getLanguages=function(){return[].concat(this._languages).concat(this._dynamicLanguages)},e}());a.a.add("editor.modesRegistry",u);var c="plaintext",l=new o.p(c,1);u.registerLanguage({id:c,extensions:[".txt",".gitignore"],aliases:[i.a("plainText.alias","Plain Text"),"text"],mimetypes:["text/plain"]}),s.a.register(l,{brackets:[["(",")"],["[","]"],["{","}"]]})},"9bHL":function(e,t,n){"use strict";var i=n("TU7t"),r=n("tqet"),o=n("Bug4"),s=n("7/Cv"),a=n("Kp7x"),u=n("Gxst"),c=n("qecS"),l=n("vbff");function d(e,t){for(var n=[],i=0,r=t;i=o.range.end)){if(e.end=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},C={useShadows:!0,verticalScrollMode:1,setRowLineHeight:!0,supportDynamicHeights:!1,dnd:{getDragElements:function(e){return[e]},getDragURI:function(){return null},onDragStart:function(){},onDragOver:function(){return!1},drop:function(){}},horizontalScrolling:!1},S=function(){function e(e){this.elements=e}return e.prototype.update=function(){},e.prototype.getData=function(){return this.elements},e}(),x=function(){function e(e){this.elements=e}return e.prototype.update=function(){},e.prototype.getData=function(){return this.elements},e}(),L=function(){function e(){this.types=[],this.files=[]}return e.prototype.update=function(e){var t;if(e.types&&(t=this.types).splice.apply(t,[0,this.types.length].concat(e.types)),e.files){this.files.splice(0,this.files.length);for(var n=0;n=this.items.length?(this.rangeMap=new f,this.rangeMap.splice(0,0,v),this.items=v,d=[]):(this.rangeMap.splice(e,t,v),d=(i=this.items).splice.apply(i,[e,t].concat(v)));var _=n.length-t,b=this.getRenderRange(this.lastRenderTop,this.lastRenderHeight),y=h(g,_),w=l.a.intersect(b,y);for(c=w.start;c=-1&&en&&(this.scrollTop+=Math.min(14,Math.floor(.3*(t-n))))}},e.prototype.teardownDragAndDropScrollTopAnimation=function(){this.dragOverAnimationStopDisposable.dispose(),this.dragOverAnimationDisposable&&(this.dragOverAnimationDisposable.dispose(),this.dragOverAnimationDisposable=void 0)},e.prototype.getItemIndexFromEventTarget=function(e){for(var t=e;t instanceof HTMLElement&&t!==this.rowsContainer;){var n=t.getAttribute("data-index");if(n){var i=Number(n);if(!isNaN(i))return i}t=t.parentElement}},e.prototype.getRenderRange=function(e,t){return{start:this.rangeMap.indexAt(e),end:this.rangeMap.indexAfter(e+t-1)}},e.prototype._rerender=function(e,t){var n,i,r=this.getRenderRange(e,t);e===this.elementTop(r.start)?(n=r.start,i=0):r.end-r.start>1&&(n=r.start+1,i=this.elementTop(n)-e);for(var o=0;;){for(var s=this.getRenderRange(e,t),a=!1,u=s.start;un-h-2)throw new Error("message too long");var f=d.alloc(n-i-h-2),p=n-l-1,g=r(l),m=a(d.concat([c,f,d.alloc(1,1),t],p),s(g,p)),v=a(g,s(m,l));return new u(d.concat([d.alloc(1),v,m],n))}(p,t);else if(1===h)f=function(e,t,n){var i,o=t.length,s=e.modulus.byteLength();if(o>s-11)throw new Error("message too long");i=n?d.alloc(s-o-3,255):function(e){var t,n=d.allocUnsafe(e),i=0,o=r(2*e),s=0;for(;i=0)throw new Error("data too long for modulus")}return n?l(f,p):c(f,p)}},"9uVW":function(e,t,n){"use strict";n.d(t,"b",function(){return l}),n.d(t,"a",function(){return h}),n.d(t,"c",function(){return f});var i=n("hK2W"),r=n("Kp7x"),o=n("ZYUE"),s=n("tqet"),a=n("aL7J"),u=n("ZNRA"),c=n("vTy2"),l=function(){function e(e,t,n){this.parent=e,this._range=t,this.isProviderFirst=n,this._onRefChanged=new r.a,this.onRefChanged=this._onRefChanged.event,this.id=u.b.nextId()}return Object.defineProperty(e.prototype,"uri",{get:function(){return this.parent.uri},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"range",{get:function(){return this._range},set:function(e){this._range=e,this._onRefChanged.fire(this)},enumerable:!0,configurable:!0}),e.prototype.getAriaMessage=function(){return Object(i.a)("aria.oneReference","symbol in {0} on line {1} at column {2}",Object(o.b)(this.uri),this.range.startLineNumber,this.range.startColumn)},e}(),d=function(){function e(e){this._modelReference=e}return e.prototype.dispose=function(){Object(s.f)(this._modelReference)},e.prototype.preview=function(e,t){void 0===t&&(t=8);var n=this._modelReference.object.textEditorModel;if(n){var i=e.startLineNumber,r=e.startColumn,o=e.endLineNumber,s=e.endColumn,u=n.getWordUntilPosition({lineNumber:i,column:r-t}),l=new c.a(i,u.startColumn,i,r),d=new c.a(o,s,o,Number.MAX_VALUE),h=n.getValueInRange(l).replace(/^\s+/,a.l),f=n.getValueInRange(e);return{value:h+f+n.getValueInRange(d).replace(/\s+$/,a.l),highlight:{start:h.length,end:h.length+f.length}}}},e}(),h=function(){function e(e,t){this._parent=e,this._uri=t,this._children=[]}return Object.defineProperty(e.prototype,"id",{get:function(){return this._uri.toString()},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"parent",{get:function(){return this._parent},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"children",{get:function(){return this._children},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"uri",{get:function(){return this._uri},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"preview",{get:function(){return this._preview},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"failure",{get:function(){return this._loadFailure},enumerable:!0,configurable:!0}),e.prototype.getAriaMessage=function(){var e=this.children.length;return 1===e?Object(i.a)("aria.fileReferences.1","1 symbol in {0}, full path {1}",Object(o.b)(this.uri),this.uri.fsPath):Object(i.a)("aria.fileReferences.N","{0} symbols in {1}, full path {2}",e,Object(o.b)(this.uri),this.uri.fsPath)},e.prototype.resolve=function(e){var t=this;return this._resolved?Promise.resolve(this):Promise.resolve(e.createModelReference(this._uri).then(function(e){if(!e.object)throw e.dispose(),new Error;return t._preview=new d(e),t._resolved=!0,t},function(e){return t._children=[],t._resolved=!0,t._loadFailure=e,t}))},e.prototype.dispose=function(){this._preview&&(this._preview.dispose(),this._preview=void 0)},e}(),f=function(){function e(t){var n=this;this._disposables=new s.b,this.groups=[],this.references=[],this._onDidChangeReferenceRange=new r.a,this.onDidChangeReferenceRange=this._onDidChangeReferenceRange.event;var i,o=t[0];t.sort(e._compareReferences);for(var a=0,u=t;a0?(i=t?(i+1)%r:(i+r-1)%r,n.children[i]):(i=n.parent.groups.indexOf(n),t?(i=(i+1)%o,n.parent.groups[i].children[0]):(i=(i+o-1)%o,n.parent.groups[i].children[n.parent.groups[i].children.length-1]))},e.prototype.nearestReference=function(e,t){var n=this.references.map(function(n,i){return{idx:i,prefixLen:a.b(n.uri.toString(),e.toString()),offsetDist:100*Math.abs(n.range.startLineNumber-t.lineNumber)+Math.abs(n.range.startColumn-t.column)}}).sort(function(e,t){return e.prefixLen>t.prefixLen?-1:e.prefixLent.offsetDist?1:0})[0];if(n)return this.references[n.idx]},e.prototype.firstReference=function(){for(var e=0,t=this.references;ei?1:c.a.compareRangesUsingStarts(e.range,t.range)},e}()},"9vcT":function(e,t){},AKCZ:function(e,t,n){"use strict";n.d(t,"a",function(){return c}),n.d(t,"b",function(){return l});var i,r=n("tqet"),o=n("Kp7x"),s=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),a=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(r,o){function s(e){try{u(i.next(e))}catch(e){o(e)}}function a(e){try{u(i.throw(e))}catch(e){o(e)}}function u(e){e.done?r(e.value):new n(function(t){t(e.value)}).then(s,a)}u((i=i.apply(e,t||[])).next())})},u=this&&this.__generator||function(e,t){var n,i,r,o,s={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,i&&(r=2&o[0]?i.return:o[0]?i.throw||((r=i.return)&&r.call(i),0):i.next)&&!(r=r.call(i,o[1])).done)return r;switch(i=0,r&&(o=[2&o[0],r.value]),o[0]){case 0:case 1:r=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,i=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(r=(r=s.trys).length>0&&r[r.length-1])&&(6===o[0]||2===o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]n)?t=("rmd160"===e?new u:c(e)).update(t).digest():t.length0;i--)t+=this._buffer(e,t),n+=this._flushBuffer(r,n);return t+=this._buffer(e,t),r},r.prototype.final=function(e){var t,n;return e&&(t=this.update(e)),n="encrypt"===this.type?this._finalEncrypt():this._finalDecrypt(),t?t.concat(n):n},r.prototype._pad=function(e,t){if(0===t)return!1;for(;t0?n.actionBar.push(o,{icon:!0,label:!1}):n.actionBar.isEmpty()||o&&0!==o.length||n.actionBar.clear(),e instanceof b&&e.getGroupLabel()?u.f(n.container,"has-group-label"):u.I(n.container,"has-group-label"),e instanceof b){var s=e,a=n;s.showBorder()?(u.f(a.container,"results-group-separator"),i.pickerGroupBorder&&(a.container.style.borderTopColor=i.pickerGroupBorder.toString())):(u.I(a.container,"results-group-separator"),a.container.style.borderTopColor=null);var c=s.getGroupLabel()||"";a.group&&(a.group.textContent=c,i.pickerGroupForeground&&(a.group.style.color=i.pickerGroupForeground.toString()))}if(e instanceof _){var l=e.getHighlights(),d=l[0],h=l[1],f=l[2],p=e.getIcon()?"quick-open-entry-icon "+e.getIcon():"";n.icon.className=p;var g=e.getLabelOptions()||Object.create(null);g.matches=d||[],g.title=e.getTooltip(),g.descriptionTitle=e.getDescriptionTooltip()||e.getDescription(),g.descriptionMatches=h||[],n.label.setLabel(r.m(e.getLabel()),e.getDescription(),g),n.detail.set(e.getDetail(),f),n.keybinding.set(e.getKeybinding())}},e.prototype.disposeTemplate=function(e,t){t.actionBar.dispose(),t.actionBar=null,t.container=null,t.entry=null,t.keybinding=null,t.detail=null,t.group=null,t.icon=null,t.label.dispose(),t.label=null},e}(),C=function(){function e(e,t){void 0===e&&(e=[]),void 0===t&&(t=new y),this._entries=e,this._dataSource=this,this._renderer=new w(t),this._filter=this,this._runner=this,this._accessibilityProvider=this}return Object.defineProperty(e.prototype,"entries",{get:function(){return this._entries},set:function(e){this._entries=e},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"dataSource",{get:function(){return this._dataSource},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"renderer",{get:function(){return this._renderer},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"filter",{get:function(){return this._filter},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"runner",{get:function(){return this._runner},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"accessibilityProvider",{get:function(){return this._accessibilityProvider},enumerable:!0,configurable:!0}),e.prototype.getId=function(e){return e.getId()},e.prototype.getLabel=function(e){return r.n(e.getLabel())},e.prototype.getAriaLabel=function(e){return e.getAriaLabel()?i.a("quickOpenAriaLabelEntry","{0}, picker",e.getAriaLabel()):i.a("quickOpenAriaLabel","picker")},e.prototype.isVisible=function(e){return!e.isHidden()},e.prototype.run=function(e,t,n){return e.run(t,n)},e}()},Ao9X:function(e,t,n){"use strict";n.d(t,"a",function(){return r}),n.d(t,"d",function(){return o}),n.d(t,"c",function(){return s}),n.d(t,"b",function(){return a});var i=n("iHM7"),r=function(){function e(e,t,n){void 0===n&&(n=!1),this._range=e,this._text=t,this.insertsAutoWhitespace=n}return e.prototype.getEditOperations=function(e,t){t.addTrackedEditOperation(this._range,this._text)},e.prototype.computeCursorState=function(e,t){var n=t.getInverseEditOperations()[0].range;return new i.a(n.endLineNumber,n.endColumn,n.endLineNumber,n.endColumn)},e}(),o=function(){function e(e,t,n){void 0===n&&(n=!1),this._range=e,this._text=t,this.insertsAutoWhitespace=n}return e.prototype.getEditOperations=function(e,t){t.addTrackedEditOperation(this._range,this._text)},e.prototype.computeCursorState=function(e,t){var n=t.getInverseEditOperations()[0].range;return new i.a(n.startLineNumber,n.startColumn,n.startLineNumber,n.startColumn)},e}(),s=function(){function e(e,t,n,i,r){void 0===r&&(r=!1),this._range=e,this._text=t,this._columnDeltaOffset=i,this._lineNumberDeltaOffset=n,this.insertsAutoWhitespace=r}return e.prototype.getEditOperations=function(e,t){t.addTrackedEditOperation(this._range,this._text)},e.prototype.computeCursorState=function(e,t){var n=t.getInverseEditOperations()[0].range;return new i.a(n.endLineNumber+this._lineNumberDeltaOffset,n.endColumn+this._columnDeltaOffset,n.endLineNumber+this._lineNumberDeltaOffset,n.endColumn+this._columnDeltaOffset)},e}(),a=function(){function e(e,t,n){this._range=e,this._text=t,this._initialSelection=n,this._selectionId=null}return e.prototype.getEditOperations=function(e,t){t.addEditOperation(this._range,this._text),this._selectionId=t.trackSelection(this._initialSelection)},e.prototype.computeCursorState=function(e,t){return t.getTrackedSelection(this._selectionId)},e}()},"B/Xy":function(e,t,n){"use strict";n.d(t,"a",function(){return r});var i=n("JVO/"),r=Object(i.c)("textModelService")},B6Bn:function(e,t,n){"use strict";var i=n("geuY"),r=n("lZ6o").utils,o=r.getNAF,s=r.getJSF,a=r.assert;function u(e,t){this.type=e,this.p=new i(t.p,16),this.red=t.prime?i.red(t.prime):i.mont(this.p),this.zero=new i(0).toRed(this.red),this.one=new i(1).toRed(this.red),this.two=new i(2).toRed(this.red),this.n=t.n&&new i(t.n,16),this.g=t.g&&this.pointFromJSON(t.g,t.gRed),this._wnafT1=new Array(4),this._wnafT2=new Array(4),this._wnafT3=new Array(4),this._wnafT4=new Array(4);var n=this.n&&this.p.div(this.n);!n||n.cmpn(100)>0?this.redN=null:(this._maxwellTrick=!0,this.redN=this.n.toRed(this.red))}function c(e,t){this.curve=e,this.type=t,this.precomputed=null}e.exports=u,u.prototype.point=function(){throw new Error("Not implemented")},u.prototype.validate=function(){throw new Error("Not implemented")},u.prototype._fixedNafMul=function(e,t){a(e.precomputed);var n=e._getDoubles(),i=o(t,1),r=(1<=u;t--)c=(c<<1)+i[t];s.push(c)}for(var l=this.jpoint(null,null,null),d=this.jpoint(null,null,null),h=r;h>0;h--){for(u=0;u=0;c--){for(t=0;c>=0&&0===s[c];c--)t++;if(c>=0&&t++,u=u.dblp(t),c<0)break;var l=s[c];a(0!==l),u="affine"===e.type?l>0?u.mixedAdd(r[l-1>>1]):u.mixedAdd(r[-l-1>>1].neg()):l>0?u.add(r[l-1>>1]):u.add(r[-l-1>>1].neg())}return"affine"===e.type?u.toP():u},u.prototype._wnafMulAdd=function(e,t,n,i,r){for(var a=this._wnafT1,u=this._wnafT2,c=this._wnafT3,l=0,d=0;d=1;d-=2){var f=d-1,p=d;if(1===a[f]&&1===a[p]){var g=[t[f],null,null,t[p]];0===t[f].y.cmp(t[p].y)?(g[1]=t[f].add(t[p]),g[2]=t[f].toJ().mixedAdd(t[p].neg())):0===t[f].y.cmp(t[p].y.redNeg())?(g[1]=t[f].toJ().mixedAdd(t[p]),g[2]=t[f].add(t[p].neg())):(g[1]=t[f].toJ().mixedAdd(t[p]),g[2]=t[f].toJ().mixedAdd(t[p].neg()));var m=[-3,-1,-5,-7,0,7,5,1,3],v=s(n[f],n[p]);l=Math.max(v[0].length,l),c[f]=new Array(l),c[p]=new Array(l);for(var _=0;_=0;d--){for(var S=0;d>=0;){var x=!0;for(_=0;_=0&&S++,w=w.dblp(S),d<0)break;for(_=0;_0?L=u[_][O-1>>1]:O<0&&(L=u[_][-O-1>>1].neg()),w="affine"===L.type?w.mixedAdd(L):w.add(L))}}for(d=0;d=Math.ceil((e.bitLength()+1)/t.step)},c.prototype._getDoubles=function(e,t){if(this.precomputed&&this.precomputed.doubles)return this.precomputed.doubles;for(var n=[this],i=this,r=0;r>>24]^l[p>>>16&255]^d[g>>>8&255]^h[255&m]^t[v++],s=c[p>>>24]^l[g>>>16&255]^d[m>>>8&255]^h[255&f]^t[v++],a=c[g>>>24]^l[m>>>16&255]^d[f>>>8&255]^h[255&p]^t[v++],u=c[m>>>24]^l[f>>>16&255]^d[p>>>8&255]^h[255&g]^t[v++],f=o,p=s,g=a,m=u;return o=(i[f>>>24]<<24|i[p>>>16&255]<<16|i[g>>>8&255]<<8|i[255&m])^t[v++],s=(i[p>>>24]<<24|i[g>>>16&255]<<16|i[m>>>8&255]<<8|i[255&f])^t[v++],a=(i[g>>>24]<<24|i[m>>>16&255]<<16|i[f>>>8&255]<<8|i[255&p])^t[v++],u=(i[m>>>24]<<24|i[f>>>16&255]<<16|i[p>>>8&255]<<8|i[255&g])^t[v++],[o>>>=0,s>>>=0,a>>>=0,u>>>=0]}var a=[0,1,2,4,8,16,32,64,128,27,54],u=function(){for(var e=new Array(256),t=0;t<256;t++)e[t]=t<128?t<<1:t<<1^283;for(var n=[],i=[],r=[[],[],[],[]],o=[[],[],[],[]],s=0,a=0,u=0;u<256;++u){var c=a^a<<1^a<<2^a<<3^a<<4;c=c>>>8^255&c^99,n[s]=c,i[c]=s;var l=e[s],d=e[l],h=e[d],f=257*e[c]^16843008*c;r[0][s]=f<<24|f>>>8,r[1][s]=f<<16|f>>>16,r[2][s]=f<<8|f>>>24,r[3][s]=f,f=16843009*h^65537*d^257*l^16843008*s,o[0][c]=f<<24|f>>>8,o[1][c]=f<<16|f>>>16,o[2][c]=f<<8|f>>>24,o[3][c]=f,0===s?s=a=1:(s=l^e[e[e[h^l]]],a^=e[e[a]])}return{SBOX:n,INV_SBOX:i,SUB_MIX:r,INV_SUB_MIX:o}}();function c(e){this._key=r(e),this._reset()}c.blockSize=16,c.keySize=32,c.prototype.blockSize=c.blockSize,c.prototype.keySize=c.keySize,c.prototype._reset=function(){for(var e=this._key,t=e.length,n=t+6,i=4*(n+1),r=[],o=0;o>>24,s=u.SBOX[s>>>24]<<24|u.SBOX[s>>>16&255]<<16|u.SBOX[s>>>8&255]<<8|u.SBOX[255&s],s^=a[o/t|0]<<24):t>6&&o%t==4&&(s=u.SBOX[s>>>24]<<24|u.SBOX[s>>>16&255]<<16|u.SBOX[s>>>8&255]<<8|u.SBOX[255&s]),r[o]=r[o-t]^s}for(var c=[],l=0;l>>24]]^u.INV_SUB_MIX[1][u.SBOX[h>>>16&255]]^u.INV_SUB_MIX[2][u.SBOX[h>>>8&255]]^u.INV_SUB_MIX[3][u.SBOX[255&h]]}this._nRounds=n,this._keySchedule=r,this._invKeySchedule=c},c.prototype.encryptBlockRaw=function(e){return s(e=r(e),this._keySchedule,u.SUB_MIX,u.SBOX,this._nRounds)},c.prototype.encryptBlock=function(e){var t=this.encryptBlockRaw(e),n=i.allocUnsafe(16);return n.writeUInt32BE(t[0],0),n.writeUInt32BE(t[1],4),n.writeUInt32BE(t[2],8),n.writeUInt32BE(t[3],12),n},c.prototype.decryptBlock=function(e){var t=(e=r(e))[1];e[1]=e[3],e[3]=t;var n=s(e,this._invKeySchedule,u.INV_SUB_MIX,u.INV_SBOX,this._nRounds),o=i.allocUnsafe(16);return o.writeUInt32BE(n[0],0),o.writeUInt32BE(n[3],4),o.writeUInt32BE(n[2],8),o.writeUInt32BE(n[1],12),o},c.prototype.scrub=function(){o(this._keySchedule),o(this._invKeySchedule),o(this._key)},e.exports.AES=c},BO8W:function(e,t,n){"use strict";t.utils=n("iNQt"),t.Cipher=n("AWjC"),t.DES=n("Icsf"),t.CBC=n("nyV4"),t.EDE=n("YePo")},BVsN:function(e,t,n){"use strict";var i=n("LC74"),r=n("eCz2"),o=n("LYGd"),s=n("JaR3"),a=n("z+8S");function u(e){a.call(this,"digest"),this._hash=e}i(u,a),u.prototype._update=function(e){this._hash.update(e)},u.prototype._final=function(){return this._hash.digest()},e.exports=function(e){return"md5"===(e=e.toLowerCase())?new r:"rmd160"===e||"ripemd160"===e?new o:new u(s(e))}},Bug4:function(e,t,n){"use strict";n.d(t,"a",function(){return r}),n.d(t,"b",function(){return d});var i,r,o=n("X6iQ"),s=n("tqet"),a=n("7/Cv"),u=n("2VYG"),c=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),l=this&&this.__decorate||function(e,t,n,i){var r,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,i);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s};!function(e){e.Tap="-monaco-gesturetap",e.Change="-monaco-gesturechange",e.Start="-monaco-gesturestart",e.End="-monaco-gesturesend",e.Contextmenu="-monaco-gesturecontextmenu"}(r||(r={}));var d=function(e){function t(){var t=e.call(this)||this;return t.dispatched=!1,t.activeTouches={},t.handle=null,t.targets=[],t._register(a.h(document,"touchstart",function(e){return t.onTouchStart(e)})),t._register(a.h(document,"touchend",function(e){return t.onTouchEnd(e)})),t._register(a.h(document,"touchmove",function(e){return t.onTouchMove(e)})),t}return c(t,e),t.addTarget=function(e){t.isTouchDevice()&&(t.INSTANCE||(t.INSTANCE=new t),t.INSTANCE.targets.push(e))},t.isTouchDevice=function(){return"ontouchstart"in window||navigator.maxTouchPoints>0||window.navigator.msMaxTouchPoints>0},t.prototype.dispose=function(){this.handle&&(this.handle.dispose(),this.handle=null),e.prototype.dispose.call(this)},t.prototype.onTouchStart=function(e){var t=Date.now();this.handle&&(this.handle.dispose(),this.handle=null);for(var n=0,i=e.targetTouches.length;n=t.HOLD_DELAY&&Math.abs(l.initialPageX-o.s(l.rollingPageX))<30&&Math.abs(l.initialPageY-o.s(l.rollingPageY))<30){var h;(h=a.newGestureEvent(r.Contextmenu,l.initialTarget)).pageX=o.s(l.rollingPageX),h.pageY=o.s(l.rollingPageY),a.dispatchEvent(h)}else if(1===i){var f=o.s(l.rollingPageX),p=o.s(l.rollingPageY),g=o.s(l.rollingTimestamps)-l.rollingTimestamps[0],m=f-l.rollingPageX[0],v=p-l.rollingPageY[0],_=a.targets.filter(function(e){return l.initialTarget instanceof Node&&e.contains(l.initialTarget)});a.inertia(_,n,Math.abs(m)/g,m>0?1:-1,f,Math.abs(v)/g,v>0?1:-1,p)}a.dispatchEvent(a.newGestureEvent(r.End,l.initialTarget)),delete a.activeTouches[c.identifier]},a=this,u=0,c=e.changedTouches.length;u0&&(g=!1,f=o*i*h),u>0&&(g=!1,p=c*u*h);var m=d.newGestureEvent(r.Change);m.translationX=f,m.translationY=p,e.forEach(function(e){return e.dispatchEvent(m)}),g||d.inertia(e,a,i,o,s+f,u,c,l+p)})},t.prototype.onTouchMove=function(e){for(var t=Date.now(),n=0,i=e.changedTouches.length;n3&&(a.rollingPageX.shift(),a.rollingPageY.shift(),a.rollingTimestamps.shift()),a.rollingPageX.push(s.pageX),a.rollingPageY.push(s.pageY),a.rollingTimestamps.push(t)}else console.warn("end of an UNKNOWN touch",s)}this.dispatched&&(e.preventDefault(),e.stopPropagation(),this.dispatched=!1)},t.SCROLL_FRICTION=-.005,t.HOLD_DELAY=700,l([u.a],t,"isTouchDevice",null),t}(s.a)},Bv73:function(e,t){},BwcV:function(e,t,n){"use strict";n.d(t,"a",function(){return c});var i,r=n("GfE5"),o=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),s=function(e){function t(t){for(var n=e.call(this,0)||this,i=0,r=t.length;i=0){for(var i=[],r=0,o=this._placeholderGroups[this._placeholderGroupsIdx];r0&&this._editor.executeEdits("snippet.placeholderTransform",i)}var d=!1;!0===t&&this._placeholderGroupsIdx0&&(this._placeholderGroupsIdx-=1,d=!0);var h=this._editor.getModel().changeDecorations(function(t){for(var i=new Set,r=[],o=0,s=n._placeholderGroups[n._placeholderGroupsIdx];o0)return!0}t=t.parent}return!1},Object.defineProperty(e.prototype,"isAtFirstPlaceholder",{get:function(){return this._placeholderGroupsIdx<=0||0===this._placeholderGroups.length},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"isAtLastPlaceholder",{get:function(){return this._placeholderGroupsIdx===this._placeholderGroups.length-1},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"hasPlaceholder",{get:function(){return this._snippet.placeholders.length>0},enumerable:!0,configurable:!0}),e.prototype.computePossibleSelections=function(){for(var e=new Map,t=0,n=this._placeholderGroups;t0&&S!==d.getLineFirstNonWhitespaceColumn(F.positionLineNumber))&&e.adjustWhitespace(d,z,H),H.resolveVariables(new x([p,new k(u,j,D.length),new L(d,F),new N(d),new E,new I(h)]));var U=d.getOffsetAt(z)+y;y+=H.toString().length-d.getValueLengthInRange(V),c[j]=f.a.replace(V,H.toString()),l[j]=new P(t,H,U)}return{edits:c,snippets:l}},e.prototype.dispose=function(){Object(i.f)(this._snippets)},e.prototype._logInfo=function(){return'template="'+this._template+'", merged_templates="'+this._templateMerges.join(" -> ")+'"'},e.prototype.insert=function(){var t=this;if(this._editor.hasModel()){var n=e.createEditsAndSnippets(this._editor,this._template,this._options.overwriteBefore,this._options.overwriteAfter,!1,this._options.adjustWhitespace,this._options.clipboardText),i=n.edits,r=n.snippets;this._snippets=r,this._editor.executeEdits("snippet",i,function(e){return t._snippets[0].hasPlaceholder?t._move(!0):e.map(function(e){return a.a.fromPositions(e.range.getEndPosition())})}),this._editor.revealRange(this._editor.getSelections()[0])}},e.prototype.merge=function(t,n){var i=this;if(void 0===n&&(n=A),this._editor.hasModel()){this._templateMerges.push([this._snippets[0]._nestingLevel,this._snippets[0]._placeholderGroupsIdx,t]);var r=e.createEditsAndSnippets(this._editor,t,n.overwriteBefore,n.overwriteAfter,!0,n.adjustWhitespace,n.clipboardText),o=r.edits,s=r.snippets;this._editor.executeEdits("snippet",o,function(e){for(var t=0,n=i._snippets;t0},e}();n.d(t,"SnippetController2",function(){return V});var F=this&&this.__assign||function(){return(F=Object.assign||function(e){for(var t,n=1,i=arguments.length;n=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},W=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},B={overwriteBefore:0,overwriteAfter:0,undoStopBefore:!0,undoStopAfter:!0,adjustWhitespace:!0,clipboardText:void 0},V=function(){function e(t,n,r){this._editor=t,this._logService=n,this._snippetListener=new i.b,this._modelVersionId=-1,this._inSnippet=e.InSnippetMode.bindTo(r),this._hasNextTabstop=e.HasNextTabstop.bindTo(r),this._hasPrevTabstop=e.HasPrevTabstop.bindTo(r)}return e.get=function(e){return e.getContribution("snippetController2")},e.prototype.dispose=function(){this._inSnippet.reset(),this._hasPrevTabstop.reset(),this._hasNextTabstop.reset(),Object(i.f)(this._session),this._snippetListener.dispose()},e.prototype.getId=function(){return"snippetController2"},e.prototype.insert=function(e,t){try{this._doInsert(e,void 0===t?B:F({},B,t))}catch(t){this.cancel(),this._logService.error(t),this._logService.error("snippet_error"),this._logService.error("insert_template=",e),this._logService.error("existing_template=",this._session?this._session._logInfo():"")}},e.prototype._doInsert=function(e,t){var n=this;this._editor.hasModel()&&(this._snippetListener.clear(),t.undoStopBefore&&this._editor.getModel().pushStackElement(),this._session?this._session.merge(e,t):(this._modelVersionId=this._editor.getModel().getAlternativeVersionId(),this._session=new R(this._editor,e,t),this._session.insert()),t.undoStopAfter&&this._editor.getModel().pushStackElement(),this._updateState(),this._snippetListener.add(this._editor.onDidChangeModelContent(function(e){return e.isFlush&&n.cancel()})),this._snippetListener.add(this._editor.onDidChangeModel(function(){return n.cancel()})),this._snippetListener.add(this._editor.onDidChangeCursorSelection(function(){return n._updateState()})))},e.prototype._updateState=function(){if(this._session&&this._editor.hasModel()){if(this._modelVersionId===this._editor.getModel().getAlternativeVersionId())return this.cancel();if(!this._session.hasPlaceholder)return this.cancel();if(this._session.isAtLastPlaceholder||!this._session.isSelectionWithinPlaceholders())return this.cancel();this._inSnippet.set(!0),this._hasPrevTabstop.set(!this._session.isAtFirstPlaceholder),this._hasNextTabstop.set(!this._session.isAtLastPlaceholder),this._handleChoice()}},e.prototype._handleChoice=function(){var e=this;if(this._session&&this._editor.hasModel()){var t=this._session.choice;if(t){if(this._currentChoice!==t){this._currentChoice=t,this._editor.setSelections(this._editor.getSelections().map(function(e){return a.a.fromPositions(e.getStartPosition())}));var n=t.options[0];Object(c.f)(this._editor,t.options.map(function(t,i){return{kind:13,label:t.value,insertText:t.value,sortText:Object(r.F)("a",i+1),range:s.a.fromPositions(e._editor.getPosition(),e._editor.getPosition().delta(0,n.value.length))}}))}}else this._currentChoice=void 0}else this._currentChoice=void 0},e.prototype.finish=function(){for(;this._inSnippet.get();)this.next()},e.prototype.cancel=function(e){void 0===e&&(e=!1),this._inSnippet.reset(),this._hasPrevTabstop.reset(),this._hasNextTabstop.reset(),this._snippetListener.clear(),Object(i.f)(this._session),this._session=void 0,this._modelVersionId=-1,e&&this._editor.setSelections([this._editor.getSelection()])},e.prototype.prev=function(){this._session&&this._session.prev(),this._updateState()},e.prototype.next=function(){this._session&&this._session.next(),this._updateState()},e.prototype.isInSnippet=function(){return Boolean(this._inSnippet.get())},e.InSnippetMode=new l.d("inSnippetMode",!1),e.HasNextTabstop=new l.d("hasNextTabstop",!1),e.HasPrevTabstop=new l.d("hasPrevTabstop",!1),e=j([W(1,d.a),W(2,l.c)],e)}();Object(o.h)(V);var H=o.c.bindToContribution(V.get);Object(o.g)(new H({id:"jumpToNextSnippetPlaceholder",precondition:l.a.and(V.InSnippetMode,V.HasNextTabstop),handler:function(e){return e.next()},kbOpts:{weight:130,kbExpr:u.a.editorTextFocus,primary:2}})),Object(o.g)(new H({id:"jumpToPrevSnippetPlaceholder",precondition:l.a.and(V.InSnippetMode,V.HasPrevTabstop),handler:function(e){return e.prev()},kbOpts:{weight:130,kbExpr:u.a.editorTextFocus,primary:1026}})),Object(o.g)(new H({id:"leaveSnippet",precondition:V.InSnippetMode,handler:function(e){return e.cancel(!0)},kbOpts:{weight:130,kbExpr:u.a.editorTextFocus,primary:9,secondary:[1033]}})),Object(o.g)(new H({id:"acceptSnippet",precondition:V.InSnippetMode,handler:function(e){return e.finish()}}))},C015:function(e,t,n){var i=n("LC74"),r=n("CzQx"),o=n("X3l8").Buffer,s=[1116352408,3609767458,1899447441,602891725,3049323471,3964484399,3921009573,2173295548,961987163,4081628472,1508970993,3053834265,2453635748,2937671579,2870763221,3664609560,3624381080,2734883394,310598401,1164996542,607225278,1323610764,1426881987,3590304994,1925078388,4068182383,2162078206,991336113,2614888103,633803317,3248222580,3479774868,3835390401,2666613458,4022224774,944711139,264347078,2341262773,604807628,2007800933,770255983,1495990901,1249150122,1856431235,1555081692,3175218132,1996064986,2198950837,2554220882,3999719339,2821834349,766784016,2952996808,2566594879,3210313671,3203337956,3336571891,1034457026,3584528711,2466948901,113926993,3758326383,338241895,168717936,666307205,1188179964,773529912,1546045734,1294757372,1522805485,1396182291,2643833823,1695183700,2343527390,1986661051,1014477480,2177026350,1206759142,2456956037,344077627,2730485921,1290863460,2820302411,3158454273,3259730800,3505952657,3345764771,106217008,3516065817,3606008344,3600352804,1432725776,4094571909,1467031594,275423344,851169720,430227734,3100823752,506948616,1363258195,659060556,3750685593,883997877,3785050280,958139571,3318307427,1322822218,3812723403,1537002063,2003034995,1747873779,3602036899,1955562222,1575990012,2024104815,1125592928,2227730452,2716904306,2361852424,442776044,2428436474,593698344,2756734187,3733110249,3204031479,2999351573,3329325298,3815920427,3391569614,3928383900,3515267271,566280711,3940187606,3454069534,4118630271,4000239992,116418474,1914138554,174292421,2731055270,289380356,3203993006,460393269,320620315,685471733,587496836,852142971,1086792851,1017036298,365543100,1126000580,2618297676,1288033470,3409855158,1501505948,4234509866,1607167915,987167468,1816402316,1246189591],a=new Array(160);function u(){this.init(),this._w=a,r.call(this,128,112)}function c(e,t,n){return n^e&(t^n)}function l(e,t,n){return e&t|n&(e|t)}function d(e,t){return(e>>>28|t<<4)^(t>>>2|e<<30)^(t>>>7|e<<25)}function h(e,t){return(e>>>14|t<<18)^(e>>>18|t<<14)^(t>>>9|e<<23)}function f(e,t){return(e>>>1|t<<31)^(e>>>8|t<<24)^e>>>7}function p(e,t){return(e>>>1|t<<31)^(e>>>8|t<<24)^(e>>>7|t<<25)}function g(e,t){return(e>>>19|t<<13)^(t>>>29|e<<3)^e>>>6}function m(e,t){return(e>>>19|t<<13)^(t>>>29|e<<3)^(e>>>6|t<<26)}function v(e,t){return e>>>0>>0?1:0}i(u,r),u.prototype.init=function(){return this._ah=1779033703,this._bh=3144134277,this._ch=1013904242,this._dh=2773480762,this._eh=1359893119,this._fh=2600822924,this._gh=528734635,this._hh=1541459225,this._al=4089235720,this._bl=2227873595,this._cl=4271175723,this._dl=1595750129,this._el=2917565137,this._fl=725511199,this._gl=4215389547,this._hl=327033209,this},u.prototype._update=function(e){for(var t=this._w,n=0|this._ah,i=0|this._bh,r=0|this._ch,o=0|this._dh,a=0|this._eh,u=0|this._fh,_=0|this._gh,b=0|this._hh,y=0|this._al,w=0|this._bl,C=0|this._cl,S=0|this._dl,x=0|this._el,L=0|this._fl,O=0|this._gl,k=0|this._hl,N=0;N<32;N+=2)t[N]=e.readInt32BE(4*N),t[N+1]=e.readInt32BE(4*N+4);for(;N<160;N+=2){var E=t[N-30],I=t[N-30+1],D=f(E,I),M=p(I,E),T=g(E=t[N-4],I=t[N-4+1]),P=m(I,E),A=t[N-14],R=t[N-14+1],F=t[N-32],j=t[N-32+1],W=M+R|0,B=D+A+v(W,M)|0;B=(B=B+T+v(W=W+P|0,P)|0)+F+v(W=W+j|0,j)|0,t[N]=B,t[N+1]=W}for(var V=0;V<160;V+=2){B=t[V],W=t[V+1];var H=l(n,i,r),z=l(y,w,C),U=d(n,y),K=d(y,n),q=h(a,x),G=h(x,a),Z=s[V],Y=s[V+1],X=c(a,u,_),$=c(x,L,O),J=k+G|0,Q=b+q+v(J,k)|0;Q=(Q=(Q=Q+X+v(J=J+$|0,$)|0)+Z+v(J=J+Y|0,Y)|0)+B+v(J=J+W|0,W)|0;var ee=K+z|0,te=U+H+v(ee,K)|0;b=_,k=O,_=u,O=L,u=a,L=x,a=o+Q+v(x=S+J|0,S)|0,o=r,S=C,r=i,C=w,i=n,w=y,n=Q+te+v(y=J+ee|0,J)|0}this._al=this._al+y|0,this._bl=this._bl+w|0,this._cl=this._cl+C|0,this._dl=this._dl+S|0,this._el=this._el+x|0,this._fl=this._fl+L|0,this._gl=this._gl+O|0,this._hl=this._hl+k|0,this._ah=this._ah+n+v(this._al,y)|0,this._bh=this._bh+i+v(this._bl,w)|0,this._ch=this._ch+r+v(this._cl,C)|0,this._dh=this._dh+o+v(this._dl,S)|0,this._eh=this._eh+a+v(this._el,x)|0,this._fh=this._fh+u+v(this._fl,L)|0,this._gh=this._gh+_+v(this._gl,O)|0,this._hh=this._hh+b+v(this._hl,k)|0},u.prototype._hash=function(){var e=o.allocUnsafe(64);function t(t,n,i){e.writeInt32BE(t,i),e.writeInt32BE(n,i+4)}return t(this._ah,this._al,0),t(this._bh,this._bl,8),t(this._ch,this._cl,16),t(this._dh,this._dl,24),t(this._eh,this._el,32),t(this._fh,this._fl,40),t(this._gh,this._gl,48),t(this._hh,this._hl,56),e},e.exports=u},C1C2:function(e,t,n){var i=n("TnCn");t.tagClass={0:"universal",1:"application",2:"context",3:"private"},t.tagClassByName=i._reverse(t.tagClass),t.tag={0:"end",1:"bool",2:"int",3:"bitstr",4:"octstr",5:"null_",6:"objid",7:"objDesc",8:"external",9:"real",10:"enum",11:"embed",12:"utf8str",13:"relativeOid",16:"seq",17:"set",18:"numstr",19:"printstr",20:"t61str",21:"videostr",22:"ia5str",23:"utctime",24:"gentime",25:"graphstr",26:"iso646str",27:"genstr",28:"unistr",29:"charstr",30:"bmpstr"},t.tagByName=i._reverse(t.tag)},C3c5:function(e,t,n){"use strict";t.e=h,n.d(t,"a",function(){return f}),n.d(t,"c",function(){return p}),n.d(t,"d",function(){return m}),n.d(t,"b",function(){return v});var i,r=n("AKCZ"),o=n("JVO/"),s=n("7g0X"),a=n("ItKl"),u=n("Kp7x"),c=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),l=this&&this.__decorate||function(e,t,n,i){var r,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,i);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},d=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}};function h(e){return void 0!==e.command}var f=Object(o.c)("menuService"),p=new(function(){function e(){this._commands=new Map,this._menuItems=new Map,this._onDidChangeMenu=new u.a,this.onDidChangeMenu=this._onDidChangeMenu.event}return e.prototype.addCommand=function(e){var t=this;return this._commands.set(e.id,e),this._onDidChangeMenu.fire(0),{dispose:function(){t._commands.delete(e.id)&&t._onDidChangeMenu.fire(0)}}},e.prototype.getCommand=function(e){return this._commands.get(e)},e.prototype.getCommands=function(){var e=new Map;return this._commands.forEach(function(t,n){return e.set(n,t)}),e},e.prototype.appendMenuItem=function(e,t){var n=this,i=this._menuItems.get(e);return i?i.push(t):(i=[t],this._menuItems.set(e,i)),this._onDidChangeMenu.fire(e),{dispose:function(){var r=i.indexOf(t);r>=0&&(i.splice(r,1),n._onDidChangeMenu.fire(e))}}},e.prototype.getMenuItems=function(e){var t=(this._menuItems.get(e)||[]).slice(0);return 0===e&&this._appendImplicitItems(t),t},e.prototype._appendImplicitItems=function(e){for(var t=new Set,n=0,i=e.filter(function(e){return h(e)});n=0;r--){var o=e.charCodeAt(r),s=t.get(o);if(0===s){if(2===i)return this._createWord(e,i,s,r+1,this._findEndOfWord(e,t,i,r+1));i=1}else if(2===s){if(1===i)return this._createWord(e,i,s,r+1,this._findEndOfWord(e,t,i,r+1));i=2}else if(1===s&&0!==i)return this._createWord(e,i,s,r+1,this._findEndOfWord(e,t,i,r+1))}return 0!==i?this._createWord(e,i,1,0,this._findEndOfWord(e,t,i,0)):null},e._findEndOfWord=function(e,t,n,i){for(var r=e.length,o=i;o=0;r--){var o=e.charCodeAt(r),s=t.get(o);if(1===s)return r+1;if(1===n&&2===s)return r+1;if(2===n&&0===s)return r+1}return 0},e.moveWordLeft=function(t,n,i,r){var o=i.lineNumber,s=i.column,u=!1;1===s&&o>1&&(u=!0,o-=1,s=n.getLineMaxColumn(o));var c=e._findPreviousWordOnLine(t,n,new a.a(o,s));if(0===r){if(c&&!u)if(n.getLineLastNonWhitespaceColumn(o)1?new a.a(n-1,e.getLineMaxColumn(n-1)):t;for(var o=e.getLineContent(n),s=t.column-1;s>1;s--){var u=o.charCodeAt(s-2),c=o.charCodeAt(s-1);if(95!==u&&95===c)return new a.a(n,s);if(r.y(u)&&r.z(c))return new a.a(n,s);if(r.z(u)&&r.z(c)&&s+1=c.start+1&&(c=e._findNextWordOnLine(t,n,new a.a(o,c.end+1))),s=c?c.start+1:n.getLineMaxColumn(o);return new a.a(o,s)},e._moveWordPartRight=function(e,t){var n=t.lineNumber,i=e.getLineMaxColumn(n);if(t.column===i)return n1?l=1:(c--,l=n.getLineMaxColumn(c)):(h&&l<=h.end+1&&(h=e._findPreviousWordOnLine(t,n,new a.a(c,h.start+1))),h?l=h.end+1:l>1?l=1:(c--,l=n.getLineMaxColumn(c))),new u.a(c,l,s.lineNumber,s.column)},e._deleteWordPartLeft=function(t,n){if(!n.isEmpty())return n;var i=n.getPosition(),r=e._moveWordPartLeft(t,i);return new u.a(i.lineNumber,i.column,r.lineNumber,r.column)},e._findFirstNonWhitespaceChar=function(e,t){for(var n=e.length,i=t;i=p.start+1&&(p=e._findNextWordOnLine(t,n,new a.a(c,p.end+1))),p?l=p.start+1:l255)return 255;return 0|e},t.b=r,t.c=function(e){for(var t=e.length,n=new Uint32Array(t),i=0;i4294967295?4294967295:0|e}},CX1u:function(e,t,n){"use strict";t.c=function(e,t){void 0===t&&(t={});var n=r(t);return n.textContent=e,n},t.b=function(e,t){void 0===t&&(t={});var n=r(t);return function e(t,n,r){var o;if(2===n.type)o=document.createTextNode(n.content||"");else if(3===n.type)o=document.createElement("b");else if(4===n.type)o=document.createElement("i");else if(5===n.type&&r){var s=document.createElement("a");s.href="#",r.disposeables.add(i.k(s,"click",function(e){r.callback(String(n.index),e)})),o=s}else 7===n.type?o=document.createElement("br"):1===n.type&&(o=t);o&&t!==o&&t.appendChild(o),o&&Array.isArray(n.children)&&n.children.forEach(function(t){e(o,t,r)})}(n,function(e){for(var t={type:1,children:[]},n=0,i=t,r=[],a=new o(e);!a.eos();){var u=a.next(),c="\\"===u&&0!==s(a.peek());if(c&&(u=a.next()),c||0===s(u)||u!==a.peek())if("\n"===u)2===i.type&&(i=r.pop()),i.children.push({type:7});else if(2!==i.type){var l={type:2,content:u};i.children.push(l),r.push(i),i=l}else i.content+=u;else{a.advance(),2===i.type&&(i=r.pop());var d=s(u);if(i.type===d||5===i.type&&6===d)i=r.pop();else{var h={type:d,children:[]};5===d&&(h.index=n,n++),i.children.push(h),r.push(i),i=h}}}return 2===i.type&&(i=r.pop()),r.length,t}(e),t.actionHandler),n},t.a=r;var i=n("7/Cv");function r(e){var t=e.inline?"span":"div",n=document.createElement(t);return e.className&&(n.className=e.className),n}var o=function(){function e(e){this.source=e,this.index=0}return e.prototype.eos=function(){return this.index>=this.source.length},e.prototype.next=function(){var e=this.peek();return this.advance(),e},e.prototype.peek=function(){return this.source[this.index]},e.prototype.advance=function(){this.index++},e}();function s(e){switch(e){case"*":return 3;case"_":return 4;case"[":return 5;case"]":return 6;default:return 0}}},Cfmk:function(e,t,n){"use strict";n.d(t,"a",function(){return l}),n.d(t,"c",function(){return r}),n.d(t,"b",function(){return d});var i,r,o=n("JVO/"),s=n("Kp7x"),a=n("tqet"),u=n("KIxu"),c=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),l=Object(o.c)("storageService");!function(e){e[e.NONE=0]="NONE",e[e.SHUTDOWN=1]="SHUTDOWN"}(r||(r={}));var d=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t._serviceBrand=null,t._onDidChangeStorage=t._register(new s.a),t.onDidChangeStorage=t._onDidChangeStorage.event,t.onWillSaveState=s.b.None,t.globalCache=new Map,t.workspaceCache=new Map,t}return c(t,e),t.prototype.getCache=function(e){return 0===e?this.globalCache:this.workspaceCache},t.prototype.get=function(e,t,n){var i=this.getCache(t).get(e);return Object(u.k)(i)?n:i},t.prototype.getBoolean=function(e,t,n){var i=this.getCache(t).get(e);return Object(u.k)(i)?n:"true"===i},t.prototype.store=function(e,t,n){if(Object(u.k)(t))return this.remove(e,n);var i=String(t);return this.getCache(n).get(e)===i?Promise.resolve():(this.getCache(n).set(e,i),this._onDidChangeStorage.fire({scope:n,key:e}),Promise.resolve())},t.prototype.remove=function(e,t){return this.getCache(t).delete(e)?(this._onDidChangeStorage.fire({scope:t,key:e}),Promise.resolve()):Promise.resolve()},t}(a.a)},Cgw8:function(e,t,n){var i=n("X3l8").Buffer,r=n("eCz2");e.exports=function(e,t,n,o){if(i.isBuffer(e)||(e=i.from(e,"binary")),t&&(i.isBuffer(t)||(t=i.from(t,"binary")),8!==t.length))throw new RangeError("salt should be Buffer with 8 byte length");for(var s=n/8,a=i.alloc(s),u=i.alloc(o||0),c=i.alloc(0);s>0||o>0;){var l=new r;l.update(c),l.update(e),t&&l.update(t),c=l.digest();var d=0;if(s>0){var h=a.length-s;d=Math.min(s,c.length),c.copy(a,h,0,d),s-=d}if(d0){var f=u.length-o,p=Math.min(o,c.length-d);c.copy(u,f,d,d+p),o-=p}}return c.fill(0),{key:a,iv:u}}},Crnc:function(e,t,n){"use strict";n.d(t,"a",function(){return a});var i=!1,r=null;function o(e){if(!e.parent||e.parent===e)return null;try{var t=e.location,n=e.parent.location;if(t.protocol!==n.protocol||t.hostname!==n.hostname||t.port!==n.port)return i=!0,null}catch(e){return i=!0,null}return e.parent}function s(e,t){for(var n,i=e.document.getElementsByTagName("iframe"),r=0,o=i.length;r=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},m=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},v=function(e){function t(n,i){var r=e.call(this)||this;return r.closeTimeout=3e3,r._messageWidget=r._register(new a.d),r._messageListeners=r._register(new a.b),r._editor=n,r._visible=t.MESSAGE_VISIBLE.bindTo(i),r._register(r._editor.onDidAttemptReadOnlyEdit(function(){return r._onDidAttemptReadOnlyEdit()})),r}return p(t,e),t.get=function(e){return e.getContribution(t._id)},t.prototype.getId=function(){return t._id},t.prototype.dispose=function(){e.prototype.dispose.call(this),this._visible.reset()},t.prototype.showMessage=function(e,t){var n,i=this;Object(u.a)(e),this._visible.set(!0),this._messageWidget.clear(),this._messageListeners.clear(),this._messageWidget.value=new b(this._editor,t,e),this._messageListeners.add(this._editor.onDidBlurEditorText(function(){return i.closeMessage()})),this._messageListeners.add(this._editor.onDidChangeCursorPosition(function(){return i.closeMessage()})),this._messageListeners.add(this._editor.onDidDispose(function(){return i.closeMessage()})),this._messageListeners.add(this._editor.onDidChangeModel(function(){return i.closeMessage()})),this._messageListeners.add(new s.e(function(){return i.closeMessage()},this.closeTimeout)),this._messageListeners.add(this._editor.onMouseMove(function(e){e.target.position&&(n?n.containsPosition(e.target.position)||i.closeMessage():n=new c.a(t.lineNumber-3,1,e.target.position.lineNumber+3,1))}))},t.prototype.closeMessage=function(){this._visible.reset(),this._messageListeners.clear(),this._messageWidget.value&&this._messageListeners.add(b.fadeOut(this._messageWidget.value))},t.prototype._onDidAttemptReadOnlyEdit=function(){this._editor.hasModel()&&this.showMessage(o.a("editor.readonly","Cannot edit in read-only editor"),this._editor.getPosition())},t._id="editor.contrib.messageController",t.MESSAGE_VISIBLE=new d.d("messageVisible",!1),t=g([m(1,d.c)],t)}(a.a),_=l.c.bindToContribution(v.get);Object(l.g)(new _({id:"leaveEditorMessage",precondition:v.MESSAGE_VISIBLE,handler:function(e){return e.closeMessage()},kbOpts:{weight:130,primary:9}}));var b=function(){function e(e,t,n){var i=t.lineNumber,r=t.column;this.allowEditorOverflow=!0,this.suppressMouseDown=!1,this._editor=e,this._editor.revealLinesInCenterIfOutsideViewport(i,i,0),this._position={lineNumber:i,column:r-1},this._domNode=document.createElement("div"),this._domNode.classList.add("monaco-editor-overlaymessage");var o=document.createElement("div");o.classList.add("message"),o.textContent=n,this._domNode.appendChild(o);var s=document.createElement("div");s.classList.add("anchor"),this._domNode.appendChild(s),this._editor.addContentWidget(this),this._domNode.classList.add("fadeIn")}return e.fadeOut=function(e){var t,n=function(){e.dispose(),clearTimeout(t),e.getDomNode().removeEventListener("animationend",n)};return t=setTimeout(n,110),e.getDomNode().addEventListener("animationend",n),e.getDomNode().classList.add("fadeOut"),{dispose:n}},e.prototype.dispose=function(){this._editor.removeContentWidget(this)},e.prototype.getId=function(){return"messageoverlay"},e.prototype.getDomNode=function(){return this._domNode},e.prototype.getPosition=function(){return{position:this._position,preference:[1]}},e}();Object(l.h)(v),Object(h.f)(function(e,t){var n=e.getColor(f._3);if(n){var i=e.type===h.b?2:1;t.addRule(".monaco-editor .monaco-editor-overlaymessage .anchor { border-top-color: "+n+"; }"),t.addRule(".monaco-editor .monaco-editor-overlaymessage .message { border: "+i+"px solid "+n+"; }")}var r=e.getColor(f._2);r&&t.addRule(".monaco-editor .monaco-editor-overlaymessage .message { background-color: "+r+"; }");var o=e.getColor(f._4);o&&t.addRule(".monaco-editor .monaco-editor-overlaymessage .message { color: "+o+"; }")})},CzQx:function(e,t,n){var i=n("X3l8").Buffer;function r(e,t){this._block=i.alloc(e),this._finalSize=t,this._blockSize=e,this._len=0}r.prototype.update=function(e,t){"string"==typeof e&&(t=t||"utf8",e=i.from(e,t));for(var n=this._block,r=this._blockSize,o=e.length,s=this._len,a=0;a=this._finalSize&&(this._update(this._block),this._block.fill(0));var n=8*this._len;if(n<=4294967295)this._block.writeUInt32BE(n,this._blockSize-4);else{var i=(4294967295&n)>>>0,r=(n-i)/4294967296;this._block.writeUInt32BE(r,this._blockSize-8),this._block.writeUInt32BE(i,this._blockSize-4)}this._update(this._block);var o=this._hash();return e?o.toString(e):o},r.prototype._update=function(){throw new Error("_update must be implemented by subclass")},e.exports=r},D1Va:function(e,t,n){"use strict";e.exports=o;var i=n("DsFX"),r=n("jOgh");function o(e){if(!(this instanceof o))return new o(e);i.call(this,e),this._transformState={afterTransform:function(e,t){var n=this._transformState;n.transforming=!1;var i=n.writecb;if(!i)return this.emit("error",new Error("write callback called multiple times"));n.writechunk=null,n.writecb=null,null!=t&&this.push(t),i(e);var r=this._readableState;r.reading=!1,(r.needReadable||r.length0?i-4:i,d=0;d>16&255,a[u++]=t>>8&255,a[u++]=255&t;2===s&&(t=r[e.charCodeAt(d)]<<2|r[e.charCodeAt(d+1)]>>4,a[u++]=255&t);1===s&&(t=r[e.charCodeAt(d)]<<10|r[e.charCodeAt(d+1)]<<4|r[e.charCodeAt(d+2)]>>2,a[u++]=t>>8&255,a[u++]=255&t);return a},t.fromByteArray=function(e){for(var t,n=e.length,r=n%3,o=[],s=0,a=n-r;sa?a:s+16383));1===r?(t=e[n-1],o.push(i[t>>2]+i[t<<4&63]+"==")):2===r&&(t=(e[n-2]<<8)+e[n-1],o.push(i[t>>10]+i[t>>4&63]+i[t<<2&63]+"="));return o.join("")};for(var i=[],r=[],o="undefined"!=typeof Uint8Array?Uint8Array:Array,s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",a=0,u=s.length;a0)throw new Error("Invalid string. Length must be a multiple of 4");var n=e.indexOf("=");return-1===n&&(n=t),[n,n===t?0:4-n%4]}function l(e,t,n){for(var r,o,s=[],a=t;a>18&63]+i[o>>12&63]+i[o>>6&63]+i[63&o]);return s.join("")}r["-".charCodeAt(0)]=62,r["_".charCodeAt(0)]=63},EMDP:function(e,t,n){"use strict";n.d(t,"a",function(){return c}),n.d(t,"b",function(){return l});var i,r,o=n("mrx5"),s=n("ZYUE"),a=n("JVO/"),u=n("WTFd"),c=Object(a.c)("contextService");!function(e){e.isIWorkspace=function(e){return e&&"object"==typeof e&&"string"==typeof e.id&&Array.isArray(e.folders)}}(i||(i={})),function(e){e.isIWorkspaceFolder=function(e){return e&&"object"==typeof e&&o.a.isUri(e.uri)&&"string"==typeof e.name&&"function"==typeof e.toResource}}(r||(r={}));!function(){function e(e,t,n){void 0===t&&(t=[]),void 0===n&&(n=null),this._id=e,this._configuration=n,this._foldersMap=u.c.forPaths(),this.folders=t}Object.defineProperty(e.prototype,"folders",{get:function(){return this._folders},set:function(e){this._folders=e,this.updateFoldersMap()},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"id",{get:function(){return this._id},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"configuration",{get:function(){return this._configuration},set:function(e){this._configuration=e},enumerable:!0,configurable:!0}),e.prototype.getFolder=function(e){return e&&this._foldersMap.findSubstr(e.with({scheme:e.scheme,authority:e.authority,path:e.path}).toString())||null},e.prototype.updateFoldersMap=function(){this._foldersMap=u.c.forPaths();for(var e=0,t=this.folders;e=1.5*n;return Math.round(e/n)+" "+i+(r?"s":"")}e.exports=function(e,t){t=t||{};var c=typeof e;if("string"===c&&e.length>0)return function(e){if((e=String(e)).length>100)return;var t=/^((?:\d+)?\-?\d?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!t)return;var u=parseFloat(t[1]);switch((t[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return u*a;case"weeks":case"week":case"w":return u*s;case"days":case"day":case"d":return u*o;case"hours":case"hour":case"hrs":case"hr":case"h":return u*r;case"minutes":case"minute":case"mins":case"min":case"m":return u*i;case"seconds":case"second":case"secs":case"sec":case"s":return u*n;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return u;default:return}}(e);if("number"===c&&!1===isNaN(e))return t.long?function(e){var t=Math.abs(e);if(t>=o)return u(e,t,o,"day");if(t>=r)return u(e,t,r,"hour");if(t>=i)return u(e,t,i,"minute");if(t>=n)return u(e,t,n,"second");return e+" ms"}(e):function(e){var t=Math.abs(e);if(t>=o)return Math.round(e/o)+"d";if(t>=r)return Math.round(e/r)+"h";if(t>=i)return Math.round(e/i)+"m";if(t>=n)return Math.round(e/n)+"s";return e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},Eawl:function(e,t){},Eeyw:function(e,t,n){"use strict";t.a=function(e,t){var n=e.getCount(),r=e.findTokenIndexAtOffset(t),o=e.getLanguageId(r),s=r;for(;s+10&&e.getLanguageId(a-1)===o;)a--;return new i(e,o,a,s+1,e.getStartOffset(a),e.getEndOffset(s))},t.b=function(e){return 0!=(7&e)};var i=function(){function e(e,t,n,i,r,o){this._actual=e,this.languageId=t,this._firstTokenIndex=n,this._lastTokenIndex=i,this.firstCharOffset=r,this._lastCharOffset=o}return e.prototype.getLineContent=function(){return this._actual.getLineContent().substring(this.firstCharOffset,this._lastCharOffset)},e.prototype.getTokenCount=function(){return this._lastTokenIndex-this._firstTokenIndex},e.prototype.findTokenIndexAtOffset=function(e){return this._actual.findTokenIndexAtOffset(e+this.firstCharOffset)-this._firstTokenIndex},e.prototype.getStandardTokenType=function(e){return this._actual.getStandardTokenType(e+this._firstTokenIndex)},e}()},EfIu:function(e,t,n){"use strict";n.d(t,"a",function(){return i}),n.d(t,"c",function(){return r}),n.d(t,"b",function(){return o}),n.d(t,"d",function(){return s}),n.d(t,"e",function(){return a}),n.d(t,"g",function(){return u}),n.d(t,"h",function(){return c}),n.d(t,"f",function(){return l});var i,r,o,s,a,u,c,l,d=n("hK2W");!function(e){e.noSelection=d.a("noSelection","No selection"),e.singleSelectionRange=d.a("singleSelectionRange","Line {0}, Column {1} ({2} selected)"),e.singleSelection=d.a("singleSelection","Line {0}, Column {1}"),e.multiSelectionRange=d.a("multiSelectionRange","{0} selections ({1} characters selected)"),e.multiSelection=d.a("multiSelection","{0} selections"),e.emergencyConfOn=d.a("emergencyConfOn","Now changing the setting `accessibilitySupport` to 'on'."),e.openingDocs=d.a("openingDocs","Now opening the Editor Accessibility documentation page."),e.readonlyDiffEditor=d.a("readonlyDiffEditor"," in a read-only pane of a diff editor."),e.editableDiffEditor=d.a("editableDiffEditor"," in a pane of a diff editor."),e.readonlyEditor=d.a("readonlyEditor"," in a read-only code editor"),e.editableEditor=d.a("editableEditor"," in a code editor"),e.changeConfigToOnMac=d.a("changeConfigToOnMac","To configure the editor to be optimized for usage with a Screen Reader press Command+E now."),e.changeConfigToOnWinLinux=d.a("changeConfigToOnWinLinux","To configure the editor to be optimized for usage with a Screen Reader press Control+E now."),e.auto_on=d.a("auto_on","The editor is configured to be optimized for usage with a Screen Reader."),e.auto_off=d.a("auto_off","The editor is configured to never be optimized for usage with a Screen Reader, which is not the case at this time."),e.tabFocusModeOnMsg=d.a("tabFocusModeOnMsg","Pressing Tab in the current editor will move focus to the next focusable element. Toggle this behavior by pressing {0}."),e.tabFocusModeOnMsgNoKb=d.a("tabFocusModeOnMsgNoKb","Pressing Tab in the current editor will move focus to the next focusable element. The command {0} is currently not triggerable by a keybinding."),e.tabFocusModeOffMsg=d.a("tabFocusModeOffMsg","Pressing Tab in the current editor will insert the tab character. Toggle this behavior by pressing {0}."),e.tabFocusModeOffMsgNoKb=d.a("tabFocusModeOffMsgNoKb","Pressing Tab in the current editor will insert the tab character. The command {0} is currently not triggerable by a keybinding."),e.openDocMac=d.a("openDocMac","Press Command+H now to open a browser window with more information related to editor accessibility."),e.openDocWinLinux=d.a("openDocWinLinux","Press Control+H now to open a browser window with more information related to editor accessibility."),e.outroMsg=d.a("outroMsg","You can dismiss this tooltip and return to the editor by pressing Escape or Shift+Escape."),e.showAccessibilityHelpAction=d.a("showAccessibilityHelpAction","Show Accessibility Help")}(i||(i={})),function(e){e.inspectTokensAction=d.a("inspectTokens","Developer: Inspect Tokens")}(r||(r={})),function(e){e.gotoLineLabelValidLineAndColumn=d.a("gotoLineLabelValidLineAndColumn","Go to line {0} and character {1}"),e.gotoLineLabelValidLine=d.a("gotoLineLabelValidLine","Go to line {0}"),e.gotoLineLabelEmptyWithLineLimit=d.a("gotoLineLabelEmptyWithLineLimit","Type a line number between 1 and {0} to navigate to"),e.gotoLineLabelEmptyWithLineAndColumnLimit=d.a("gotoLineLabelEmptyWithLineAndColumnLimit","Type a character between 1 and {0} to navigate to"),e.gotoLineAriaLabel=d.a("gotoLineAriaLabel","Current Line: {0}. Go to line {1}."),e.gotoLineActionInput=d.a("gotoLineActionInput","Type a line number, followed by an optional colon and a character number to navigate to"),e.gotoLineActionLabel=d.a("gotoLineActionLabel","Go to Line...")}(o||(o={})),function(e){e.ariaLabelEntryWithKey=d.a("ariaLabelEntryWithKey","{0}, {1}, commands"),e.ariaLabelEntry=d.a("ariaLabelEntry","{0}, commands"),e.quickCommandActionInput=d.a("quickCommandActionInput","Type the name of an action you want to execute"),e.quickCommandActionLabel=d.a("quickCommandActionLabel","Command Palette")}(s||(s={})),function(e){e.entryAriaLabel=d.a("entryAriaLabel","{0}, symbols"),e.quickOutlineActionInput=d.a("quickOutlineActionInput","Type the name of an identifier you wish to navigate to"),e.quickOutlineActionLabel=d.a("quickOutlineActionLabel","Go to Symbol..."),e._symbols_=d.a("symbols","symbols ({0})"),e._modules_=d.a("modules","modules ({0})"),e._class_=d.a("class","classes ({0})"),e._interface_=d.a("interface","interfaces ({0})"),e._method_=d.a("method","methods ({0})"),e._function_=d.a("function","functions ({0})"),e._property_=d.a("property","properties ({0})"),e._variable_=d.a("variable","variables ({0})"),e._variable2_=d.a("variable2","variables ({0})"),e._constructor_=d.a("_constructor","constructors ({0})"),e._call_=d.a("call","calls ({0})")}(a||(a={})),function(e){e.editorViewAccessibleLabel=d.a("editorViewAccessibleLabel","Editor content"),e.accessibilityHelpMessageIE=d.a("accessibilityHelpMessageIE","Press Ctrl+F1 for Accessibility Options."),e.accessibilityHelpMessage=d.a("accessibilityHelpMessage","Press Alt+F1 for Accessibility Options.")}(u||(u={})),function(e){e.toggleHighContrast=d.a("toggleHighContrast","Toggle High Contrast Theme")}(c||(c={})),function(e){e.bulkEditServiceSummary=d.a("bulkEditServiceSummary","Made {0} edits in {1} files")}(l||(l={}))},EfRI:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n.d(t,"DeleteWordPartLeft",function(){return d}),n.d(t,"DeleteWordPartRight",function(){return h}),n.d(t,"WordPartLeftCommand",function(){return f}),n.d(t,"CursorWordPartLeft",function(){return p}),n.d(t,"CursorWordPartLeftSelect",function(){return g}),n.d(t,"WordPartRightCommand",function(){return m}),n.d(t,"CursorWordPartRight",function(){return v}),n.d(t,"CursorWordPartRightSelect",function(){return _});var i,r=n("03Zz"),o=n("CIBl"),s=n("vTy2"),a=n("/9db"),u=n("I8T6"),c=n("ItKl"),l=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),d=function(e){function t(){return e.call(this,{whitespaceHeuristics:!0,wordNavigationType:0,id:"deleteWordPartLeft",precondition:a.a.writable,kbOpts:{kbExpr:a.a.textInputFocus,primary:0,mac:{primary:769},weight:100}})||this}return l(t,e),t.prototype._delete=function(e,t,n,i,r){var a=o.b.deleteWordPartLeft(e,t,n,i);return a||new s.a(1,1,1,1)},t}(u.DeleteWordCommand),h=function(e){function t(){return e.call(this,{whitespaceHeuristics:!0,wordNavigationType:2,id:"deleteWordPartRight",precondition:a.a.writable,kbOpts:{kbExpr:a.a.textInputFocus,primary:0,mac:{primary:788},weight:100}})||this}return l(t,e),t.prototype._delete=function(e,t,n,i,r){var a=o.b.deleteWordPartRight(e,t,n,i);if(a)return a;var u=t.getLineCount(),c=t.getLineMaxColumn(u);return new s.a(u,c,u,c)},t}(u.DeleteWordCommand),f=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return l(t,e),t.prototype._move=function(e,t,n,i){return o.b.moveWordPartLeft(e,t,n)},t}(u.MoveWordCommand),p=function(e){function t(){return e.call(this,{inSelectionMode:!1,wordNavigationType:0,id:"cursorWordPartLeft",precondition:void 0,kbOpts:{kbExpr:a.a.textInputFocus,primary:0,mac:{primary:783},weight:100}})||this}return l(t,e),t}(f);c.a.registerCommandAlias("cursorWordPartStartLeft","cursorWordPartLeft");var g=function(e){function t(){return e.call(this,{inSelectionMode:!0,wordNavigationType:0,id:"cursorWordPartLeftSelect",precondition:void 0,kbOpts:{kbExpr:a.a.textInputFocus,primary:0,mac:{primary:1807},weight:100}})||this}return l(t,e),t}(f);c.a.registerCommandAlias("cursorWordPartStartLeftSelect","cursorWordPartLeftSelect");var m=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return l(t,e),t.prototype._move=function(e,t,n,i){return o.b.moveWordPartRight(e,t,n)},t}(u.MoveWordCommand),v=function(e){function t(){return e.call(this,{inSelectionMode:!1,wordNavigationType:2,id:"cursorWordPartRight",precondition:void 0,kbOpts:{kbExpr:a.a.textInputFocus,primary:0,mac:{primary:785},weight:100}})||this}return l(t,e),t}(m),_=function(e){function t(){return e.call(this,{inSelectionMode:!0,wordNavigationType:2,id:"cursorWordPartRightSelect",precondition:void 0,kbOpts:{kbExpr:a.a.textInputFocus,primary:0,mac:{primary:1809},weight:100}})||this}return l(t,e),t}(m);Object(r.g)(new d),Object(r.g)(new h),Object(r.g)(new p),Object(r.g)(new g),Object(r.g)(new v),Object(r.g)(new _)},EuP9:function(e,t,n){"use strict";(function(e){ +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +var i=n("EKta"),r=n("ujcs"),o=n("sOR5");function s(){return u.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function a(e,t){if(s()=s())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+s().toString(16)+" bytes");return 0|e}function p(e,t){if(u.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var n=e.length;if(0===n)return 0;for(var i=!1;;)switch(t){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":case void 0:return B(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return V(e).length;default:if(i)return B(e).length;t=(""+t).toLowerCase(),i=!0}}function g(e,t,n){var i=e[t];e[t]=e[n],e[n]=i}function m(e,t,n,i,r){if(0===e.length)return-1;if("string"==typeof n?(i=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),n=+n,isNaN(n)&&(n=r?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(r)return-1;n=e.length-1}else if(n<0){if(!r)return-1;n=0}if("string"==typeof t&&(t=u.from(t,i)),u.isBuffer(t))return 0===t.length?-1:v(e,t,n,i,r);if("number"==typeof t)return t&=255,u.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?r?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):v(e,[t],n,i,r);throw new TypeError("val must be string, number or Buffer")}function v(e,t,n,i,r){var o,s=1,a=e.length,u=t.length;if(void 0!==i&&("ucs2"===(i=String(i).toLowerCase())||"ucs-2"===i||"utf16le"===i||"utf-16le"===i)){if(e.length<2||t.length<2)return-1;s=2,a/=2,u/=2,n/=2}function c(e,t){return 1===s?e[t]:e.readUInt16BE(t*s)}if(r){var l=-1;for(o=n;oa&&(n=a-u),o=n;o>=0;o--){for(var d=!0,h=0;hr&&(i=r):i=r;var o=t.length;if(o%2!=0)throw new TypeError("Invalid hex string");i>o/2&&(i=o/2);for(var s=0;s>8,r=n%256,o.push(r),o.push(i);return o}(t,e.length-n),e,n,i)}function x(e,t,n){return 0===t&&n===e.length?i.fromByteArray(e):i.fromByteArray(e.slice(t,n))}function L(e,t,n){n=Math.min(e.length,n);for(var i=[],r=t;r239?4:c>223?3:c>191?2:1;if(r+d<=n)switch(d){case 1:c<128&&(l=c);break;case 2:128==(192&(o=e[r+1]))&&(u=(31&c)<<6|63&o)>127&&(l=u);break;case 3:o=e[r+1],s=e[r+2],128==(192&o)&&128==(192&s)&&(u=(15&c)<<12|(63&o)<<6|63&s)>2047&&(u<55296||u>57343)&&(l=u);break;case 4:o=e[r+1],s=e[r+2],a=e[r+3],128==(192&o)&&128==(192&s)&&128==(192&a)&&(u=(15&c)<<18|(63&o)<<12|(63&s)<<6|63&a)>65535&&u<1114112&&(l=u)}null===l?(l=65533,d=1):l>65535&&(l-=65536,i.push(l>>>10&1023|55296),l=56320|1023&l),i.push(l),r+=d}return function(e){var t=e.length;if(t<=O)return String.fromCharCode.apply(String,e);var n="",i=0;for(;ithis.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return E(this,t,n);case"utf8":case"utf-8":return L(this,t,n);case"ascii":return k(this,t,n);case"latin1":case"binary":return N(this,t,n);case"base64":return x(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return I(this,t,n);default:if(i)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),i=!0}}.apply(this,arguments)},u.prototype.equals=function(e){if(!u.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e||0===u.compare(this,e)},u.prototype.inspect=function(){var e="",n=t.INSPECT_MAX_BYTES;return this.length>0&&(e=this.toString("hex",0,n).match(/.{2}/g).join(" "),this.length>n&&(e+=" ... ")),""},u.prototype.compare=function(e,t,n,i,r){if(!u.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===n&&(n=e?e.length:0),void 0===i&&(i=0),void 0===r&&(r=this.length),t<0||n>e.length||i<0||r>this.length)throw new RangeError("out of range index");if(i>=r&&t>=n)return 0;if(i>=r)return-1;if(t>=n)return 1;if(t>>>=0,n>>>=0,i>>>=0,r>>>=0,this===e)return 0;for(var o=r-i,s=n-t,a=Math.min(o,s),c=this.slice(i,r),l=e.slice(t,n),d=0;dr)&&(n=r),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");i||(i="utf8");for(var o=!1;;)switch(i){case"hex":return _(this,e,t,n);case"utf8":case"utf-8":return b(this,e,t,n);case"ascii":return y(this,e,t,n);case"latin1":case"binary":return w(this,e,t,n);case"base64":return C(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return S(this,e,t,n);default:if(o)throw new TypeError("Unknown encoding: "+i);i=(""+i).toLowerCase(),o=!0}},u.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var O=4096;function k(e,t,n){var i="";n=Math.min(e.length,n);for(var r=t;ri)&&(n=i);for(var r="",o=t;on)throw new RangeError("Trying to access beyond buffer length")}function M(e,t,n,i,r,o){if(!u.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>r||te.length)throw new RangeError("Index out of range")}function T(e,t,n,i){t<0&&(t=65535+t+1);for(var r=0,o=Math.min(e.length-n,2);r>>8*(i?r:1-r)}function P(e,t,n,i){t<0&&(t=4294967295+t+1);for(var r=0,o=Math.min(e.length-n,4);r>>8*(i?r:3-r)&255}function A(e,t,n,i,r,o){if(n+i>e.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function R(e,t,n,i,o){return o||A(e,0,n,4),r.write(e,t,n,i,23,4),n+4}function F(e,t,n,i,o){return o||A(e,0,n,8),r.write(e,t,n,i,52,8),n+8}u.prototype.slice=function(e,t){var n,i=this.length;if(e=~~e,t=void 0===t?i:~~t,e<0?(e+=i)<0&&(e=0):e>i&&(e=i),t<0?(t+=i)<0&&(t=0):t>i&&(t=i),t0&&(r*=256);)i+=this[e+--t]*r;return i},u.prototype.readUInt8=function(e,t){return t||D(e,1,this.length),this[e]},u.prototype.readUInt16LE=function(e,t){return t||D(e,2,this.length),this[e]|this[e+1]<<8},u.prototype.readUInt16BE=function(e,t){return t||D(e,2,this.length),this[e]<<8|this[e+1]},u.prototype.readUInt32LE=function(e,t){return t||D(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},u.prototype.readUInt32BE=function(e,t){return t||D(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},u.prototype.readIntLE=function(e,t,n){e|=0,t|=0,n||D(e,t,this.length);for(var i=this[e],r=1,o=0;++o=(r*=128)&&(i-=Math.pow(2,8*t)),i},u.prototype.readIntBE=function(e,t,n){e|=0,t|=0,n||D(e,t,this.length);for(var i=t,r=1,o=this[e+--i];i>0&&(r*=256);)o+=this[e+--i]*r;return o>=(r*=128)&&(o-=Math.pow(2,8*t)),o},u.prototype.readInt8=function(e,t){return t||D(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},u.prototype.readInt16LE=function(e,t){t||D(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},u.prototype.readInt16BE=function(e,t){t||D(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},u.prototype.readInt32LE=function(e,t){return t||D(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},u.prototype.readInt32BE=function(e,t){return t||D(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},u.prototype.readFloatLE=function(e,t){return t||D(e,4,this.length),r.read(this,e,!0,23,4)},u.prototype.readFloatBE=function(e,t){return t||D(e,4,this.length),r.read(this,e,!1,23,4)},u.prototype.readDoubleLE=function(e,t){return t||D(e,8,this.length),r.read(this,e,!0,52,8)},u.prototype.readDoubleBE=function(e,t){return t||D(e,8,this.length),r.read(this,e,!1,52,8)},u.prototype.writeUIntLE=function(e,t,n,i){(e=+e,t|=0,n|=0,i)||M(this,e,t,n,Math.pow(2,8*n)-1,0);var r=1,o=0;for(this[t]=255&e;++o=0&&(o*=256);)this[t+r]=e/o&255;return t+n},u.prototype.writeUInt8=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,1,255,0),u.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},u.prototype.writeUInt16LE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,2,65535,0),u.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):T(this,e,t,!0),t+2},u.prototype.writeUInt16BE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,2,65535,0),u.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):T(this,e,t,!1),t+2},u.prototype.writeUInt32LE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,4,4294967295,0),u.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):P(this,e,t,!0),t+4},u.prototype.writeUInt32BE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,4,4294967295,0),u.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):P(this,e,t,!1),t+4},u.prototype.writeIntLE=function(e,t,n,i){if(e=+e,t|=0,!i){var r=Math.pow(2,8*n-1);M(this,e,t,n,r-1,-r)}var o=0,s=1,a=0;for(this[t]=255&e;++o>0)-a&255;return t+n},u.prototype.writeIntBE=function(e,t,n,i){if(e=+e,t|=0,!i){var r=Math.pow(2,8*n-1);M(this,e,t,n,r-1,-r)}var o=n-1,s=1,a=0;for(this[t+o]=255&e;--o>=0&&(s*=256);)e<0&&0===a&&0!==this[t+o+1]&&(a=1),this[t+o]=(e/s>>0)-a&255;return t+n},u.prototype.writeInt8=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,1,127,-128),u.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},u.prototype.writeInt16LE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,2,32767,-32768),u.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):T(this,e,t,!0),t+2},u.prototype.writeInt16BE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,2,32767,-32768),u.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):T(this,e,t,!1),t+2},u.prototype.writeInt32LE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,4,2147483647,-2147483648),u.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):P(this,e,t,!0),t+4},u.prototype.writeInt32BE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),u.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):P(this,e,t,!1),t+4},u.prototype.writeFloatLE=function(e,t,n){return R(this,e,t,!0,n)},u.prototype.writeFloatBE=function(e,t,n){return R(this,e,t,!1,n)},u.prototype.writeDoubleLE=function(e,t,n){return F(this,e,t,!0,n)},u.prototype.writeDoubleBE=function(e,t,n){return F(this,e,t,!1,n)},u.prototype.copy=function(e,t,n,i){if(n||(n=0),i||0===i||(i=this.length),t>=e.length&&(t=e.length),t||(t=0),i>0&&i=this.length)throw new RangeError("sourceStart out of bounds");if(i<0)throw new RangeError("sourceEnd out of bounds");i>this.length&&(i=this.length),e.length-t=0;--r)e[r+t]=this[r+n];else if(o<1e3||!u.TYPED_ARRAY_SUPPORT)for(r=0;r>>=0,n=void 0===n?this.length:n>>>0,e||(e=0),"number"==typeof e)for(o=t;o55295&&n<57344){if(!r){if(n>56319){(t-=3)>-1&&o.push(239,191,189);continue}if(s+1===i){(t-=3)>-1&&o.push(239,191,189);continue}r=n;continue}if(n<56320){(t-=3)>-1&&o.push(239,191,189),r=n;continue}n=65536+(r-55296<<10|n-56320)}else r&&(t-=3)>-1&&o.push(239,191,189);if(r=null,n<128){if((t-=1)<0)break;o.push(n)}else if(n<2048){if((t-=2)<0)break;o.push(n>>6|192,63&n|128)}else if(n<65536){if((t-=3)<0)break;o.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;o.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return o}function V(e){return i.toByteArray(function(e){if((e=function(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}(e).replace(j,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function H(e,t,n,i){for(var r=0;r=t.length||r>=e.length);++r)t[r+n]=e[r];return r}}).call(t,n("DuR2"))},Evjx:function(e,t,n){"use strict";n.d(t,"d",function(){return u}),n.d(t,"b",function(){return l}),n.d(t,"a",function(){return d}),n.d(t,"c",function(){return v});var i,r,o=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),s=function(){function e(){this.value="",this.pos=0}return e.isDigitCharacter=function(e){return e>=48&&e<=57},e.isVariableCharacter=function(e){return 95===e||e>=97&&e<=122||e>=65&&e<=90},e.prototype.text=function(e){this.value=e,this.pos=0},e.prototype.tokenText=function(e){return this.value.substr(e.pos,e.len)},e.prototype.next=function(){if(this.pos>=this.value.length)return{type:14,pos:this.pos,len:0};var t,n=this.pos,i=0,r=this.value.charCodeAt(n);if("number"==typeof(t=e._table[r]))return this.pos+=1,{type:t,pos:n,len:1};if(e.isDigitCharacter(r)){t=8;do{i+=1,r=this.value.charCodeAt(n+i)}while(e.isDigitCharacter(r));return this.pos+=i,{type:t,pos:n,len:i}}if(e.isVariableCharacter(r)){t=9;do{r=this.value.charCodeAt(n+ ++i)}while(e.isVariableCharacter(r)||e.isDigitCharacter(r));return this.pos+=i,{type:t,pos:n,len:i}}t=10;do{i+=1,r=this.value.charCodeAt(n+i)}while(!isNaN(r)&&void 0===e._table[r]&&!e.isDigitCharacter(r)&&!e.isVariableCharacter(r));return this.pos+=i,{type:t,pos:n,len:i}},e._table=((r={})[36]=0,r[58]=1,r[44]=2,r[123]=3,r[125]=4,r[92]=5,r[47]=6,r[124]=7,r[43]=11,r[45]=12,r[63]=13,r),e}(),a=function(){function e(){this._children=[]}return e.prototype.appendChild=function(e){return e instanceof u&&this._children[this._children.length-1]instanceof u?this._children[this._children.length-1].value+=e.value:(e.parent=this,this._children.push(e)),this},e.prototype.replace=function(e,t){var n=e.parent,i=n.children.indexOf(e),r=n.children.slice(0);r.splice.apply(r,[i,1].concat(t)),n._children=r,function e(t,n){for(var i=0,r=t;it.index?1:0},Object.defineProperty(t.prototype,"isFinalTabstop",{get:function(){return 0===this.index},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"choice",{get:function(){return 1===this._children.length&&this._children[0]instanceof d?this._children[0]:void 0},enumerable:!0,configurable:!0}),t.prototype.clone=function(){var e=new t(this.index);return this.transform&&(e.transform=this.transform.clone()),e._children=this.children.map(function(e){return e.clone()}),e},t}(c),d=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.options=[],t}return o(t,e),t.prototype.appendChild=function(e){return e instanceof u&&(e.parent=this,this.options.push(e)),this},t.prototype.toString=function(){return this.options[0].value},t.prototype.len=function(){return this.options[0].len()},t.prototype.clone=function(){var e=new t;return this.options.forEach(e.appendChild,e),e},t}(a),h=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.regexp=new RegExp(""),t}return o(t,e),t.prototype.resolve=function(e){var t=this,n=!1,i=e.replace(this.regexp,function(){return n=!0,t._replace(Array.prototype.slice.call(arguments,0,-2))});return!n&&this._children.some(function(e){return e instanceof f&&Boolean(e.elseValue)})&&(i=this._replace([])),i},t.prototype._replace=function(e){for(var t="",n=0,i=this._children;n0;){var i=n.shift();if(!t(i))break;n.unshift.apply(n,i.children)}}var m=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return o(t,e),Object.defineProperty(t.prototype,"placeholderInfo",{get:function(){if(!this._placeholders){var e,t=[];this.walk(function(n){return n instanceof l&&(t.push(n),e=!e||e.index0?r.set(e.index,e.children):o.push(e)),!0});for(var a=0,u=o;a0&&t),!r.has(0)&&n&&i.appendChild(new l(0)),i},e.prototype._accept=function(e,t){if(void 0===e||this._token.type===e){var n=!t||this._scanner.tokenText(this._token);return this._token=this._scanner.next(),n}return!1},e.prototype._backTo=function(e){return this._scanner.pos=e.pos+e.len,this._token=e,!1},e.prototype._until=function(e){if(14===this._token.type)return!1;for(var t="",n=this._token.pos,i={type:14,pos:0,len:0};this._token.type!==e||5===i.type;)if(this._token.type===e&&(t+=this._scanner.value.substring(n,i.pos),n=this._token.pos),i=this._token,this._token=this._scanner.next(),14===this._token.type)return!1;return t+=this._scanner.value.substring(n,this._token.pos),this._token=this._scanner.next(),t},e.prototype._parse=function(e){return this._parseEscaped(e)||this._parseTabstopOrVariableName(e)||this._parseComplexPlaceholder(e)||this._parseComplexVariable(e)||this._parseAnything(e)},e.prototype._parseEscaped=function(e){var t;return!!(t=this._accept(5,!0))&&(t=this._accept(0,!0)||this._accept(4,!0)||this._accept(5,!0)||t,e.appendChild(new u(t)),!0)},e.prototype._parseTabstopOrVariableName=function(e){var t,n=this._token;return this._accept(0)&&(t=this._accept(9,!0)||this._accept(8,!0))?(e.appendChild(/^\d+$/.test(t)?new l(Number(t)):new p(t)),!0):this._backTo(n)},e.prototype._parseComplexPlaceholder=function(e){var t,n=this._token;if(!(this._accept(0)&&this._accept(3)&&(t=this._accept(8,!0))))return this._backTo(n);var i=new l(Number(t));if(this._accept(1))for(;;){if(this._accept(4))return e.appendChild(i),!0;if(!this._parse(i))return e.appendChild(new u("${"+t+":")),i.children.forEach(e.appendChild,e),!0}else{if(!(i.index>0&&this._accept(7)))return this._accept(6)?this._parseTransform(i)?(e.appendChild(i),!0):(this._backTo(n),!1):this._accept(4)?(e.appendChild(i),!0):this._backTo(n);for(var r=new d;;){if(this._parseChoiceElement(r)){if(this._accept(2))continue;if(this._accept(7)&&(i.appendChild(r),this._accept(4)))return e.appendChild(i),!0}return this._backTo(n),!1}}},e.prototype._parseChoiceElement=function(e){for(var t=this._token,n=[];2!==this._token.type&&7!==this._token.type;){var i=void 0;if(!(i=(i=this._accept(5,!0))?this._accept(2,!0)||this._accept(7,!0)||this._accept(5,!0)||i:this._accept(void 0,!0)))return this._backTo(t),!1;n.push(i)}return 0===n.length?(this._backTo(t),!1):(e.appendChild(new u(n.join(""))),!0)},e.prototype._parseComplexVariable=function(e){var t,n=this._token;if(!(this._accept(0)&&this._accept(3)&&(t=this._accept(9,!0))))return this._backTo(n);var i=new p(t);if(!this._accept(1))return this._accept(6)?this._parseTransform(i)?(e.appendChild(i),!0):(this._backTo(n),!1):this._accept(4)?(e.appendChild(i),!0):this._backTo(n);for(;;){if(this._accept(4))return e.appendChild(i),!0;if(!this._parse(i))return e.appendChild(new u("${"+t+":")),i.children.forEach(e.appendChild,e),!0}},e.prototype._parseTransform=function(e){for(var t=new h,n="",i="";!this._accept(6);){var r=void 0;if(r=this._accept(5,!0))n+=r=this._accept(6,!0)||r;else{if(14===this._token.type)return!1;n+=this._accept(void 0,!0)}}for(;!this._accept(6);){r=void 0;if(r=this._accept(5,!0))r=this._accept(5,!0)||this._accept(6,!0)||r,t.appendChild(new u(r));else if(!this._parseFormatString(t)&&!this._parseAnything(t))return!1}for(;!this._accept(4);){if(14===this._token.type)return!1;i+=this._accept(void 0,!0)}try{t.regexp=new RegExp(n,i)}catch(e){return!1}return e.transform=t,!0},e.prototype._parseFormatString=function(e){var t=this._token;if(!this._accept(0))return!1;var n=!1;this._accept(3)&&(n=!0);var i=this._accept(8,!0);if(!i)return this._backTo(t),!1;if(!n)return e.appendChild(new f(Number(i))),!0;if(this._accept(4))return e.appendChild(new f(Number(i))),!0;if(!this._accept(1))return this._backTo(t),!1;if(this._accept(6)){var r=this._accept(9,!0);return r&&this._accept(4)?(e.appendChild(new f(Number(i),r)),!0):(this._backTo(t),!1)}if(this._accept(11)){if(o=this._until(4))return e.appendChild(new f(Number(i),void 0,o,void 0)),!0}else if(this._accept(12)){if(s=this._until(4))return e.appendChild(new f(Number(i),void 0,void 0,s)),!0}else if(this._accept(13)){var o;if(o=this._until(1))if(s=this._until(4))return e.appendChild(new f(Number(i),void 0,o,s)),!0}else{var s;if(s=this._until(4))return e.appendChild(new f(Number(i),void 0,void 0,s)),!0}return this._backTo(t),!1},e.prototype._parseAnything=function(e){return 14!==this._token.type&&(e.appendChild(new u(this._scanner.tokenText(this._token))),this._accept(void 0),!0)},e}()},F11g:function(e,t,n){"use strict";var i=n("geuY"),r=n("HzeT"),o=n("lZ6o"),s=o.utils.assert,a=n("yMmo"),u=n("NMED");function c(e){if(!(this instanceof c))return new c(e);"string"==typeof e&&(s(o.curves.hasOwnProperty(e),"Unknown curve "+e),e=o.curves[e]),e instanceof o.curves.PresetCurve&&(e={curve:e}),this.curve=e.curve.curve,this.n=this.curve.n,this.nh=this.n.ushrn(1),this.g=this.curve.g,this.g=e.curve.g,this.g.precompute(e.curve.n.bitLength()+1),this.hash=e.hash||e.curve.hash}e.exports=c,c.prototype.keyPair=function(e){return new a(this,e)},c.prototype.keyFromPrivate=function(e,t){return a.fromPrivate(this,e,t)},c.prototype.keyFromPublic=function(e,t){return a.fromPublic(this,e,t)},c.prototype.genKeyPair=function(e){e||(e={});for(var t=new r({hash:this.hash,pers:e.pers,persEnc:e.persEnc||"utf8",entropy:e.entropy||o.rand(this.hash.hmacStrength),entropyEnc:e.entropy&&e.entropyEnc||"utf8",nonce:this.n.toArray()}),n=this.n.byteLength(),s=this.n.sub(new i(2));;){var a=new i(t.generate(n));if(!(a.cmp(s)>0))return a.iaddn(1),this.keyFromPrivate(a)}},c.prototype._truncateToN=function(e,t){var n=8*e.byteLength()-this.n.bitLength();return n>0&&(e=e.ushrn(n)),!t&&e.cmp(this.n)>=0?e.sub(this.n):e},c.prototype.sign=function(e,t,n,o){"object"==typeof n&&(o=n,n=null),o||(o={}),t=this.keyFromPrivate(t,n),e=this._truncateToN(new i(e,16));for(var s=this.n.byteLength(),a=t.getPrivate().toArray("be",s),c=e.toArray("be",s),l=new r({hash:this.hash,entropy:a,nonce:c,pers:o.pers,persEnc:o.persEnc||"utf8"}),d=this.n.sub(new i(1)),h=0;;h++){var f=o.k?o.k(h):new i(l.generate(this.n.byteLength()));if(!((f=this._truncateToN(f,!0)).cmpn(1)<=0||f.cmp(d)>=0)){var p=this.g.mul(f);if(!p.isInfinity()){var g=p.getX(),m=g.umod(this.n);if(0!==m.cmpn(0)){var v=f.invm(this.n).mul(m.mul(t.getPrivate()).iadd(e));if(0!==(v=v.umod(this.n)).cmpn(0)){var _=(p.getY().isOdd()?1:0)|(0!==g.cmp(m)?2:0);return o.canonical&&v.cmp(this.nh)>0&&(v=this.n.sub(v),_^=1),new u({r:m,s:v,recoveryParam:_})}}}}}},c.prototype.verify=function(e,t,n,r){e=this._truncateToN(new i(e,16)),n=this.keyFromPublic(n,r);var o=(t=new u(t,"hex")).r,s=t.s;if(o.cmpn(1)<0||o.cmp(this.n)>=0)return!1;if(s.cmpn(1)<0||s.cmp(this.n)>=0)return!1;var a,c=s.invm(this.n),l=c.mul(e).umod(this.n),d=c.mul(o).umod(this.n);return this.curve._maxwellTrick?!(a=this.g.jmulAdd(l,n.getPublic(),d)).isInfinity()&&a.eqXToP(o):!(a=this.g.mulAdd(l,n.getPublic(),d)).isInfinity()&&0===a.getX().umod(this.n).cmp(o)},c.prototype.recoverPubKey=function(e,t,n,r){s((3&n)===n,"The recovery param is more than two bits"),t=new u(t,r);var o=this.n,a=new i(e),c=t.r,l=t.s,d=1&n,h=n>>1;if(c.cmp(this.curve.p.umod(this.curve.n))>=0&&h)throw new Error("Unable to find sencond key candinate");c=h?this.curve.pointFromX(c.add(this.curve.n),d):this.curve.pointFromX(c,d);var f=t.r.invm(o),p=o.sub(a).mul(f).umod(o),g=l.mul(f).umod(o);return this.g.mulAdd(p,c,g)},c.prototype.getKeyRecoveryParam=function(e,t,n,i){if(null!==(t=new u(t,i)).recoveryParam)return t.recoveryParam;for(var r=0;r<4;r++){var o;try{o=this.recoverPubKey(e,t,r)}catch(e){continue}if(o.eq(n))return r}throw new Error("Unable to find valid recovery factor")}},F5mM:function(e,t){},Fllr:function(e,t,n){"use strict";var i=n("zxiH"),r=n("Kp7x"),o=n("tqet"),s=n("aL7J"),a=n("vTy2"),u=n("+jct"),c=n("+oh4"),l=n("Eeyw"),d=function(){function e(t){if(t.autoClosingPairs?this._autoClosingPairs=t.autoClosingPairs.map(function(e){return new c.b(e)}):t.brackets?this._autoClosingPairs=t.brackets.map(function(e){return new c.b({open:e[0],close:e[1]})}):this._autoClosingPairs=[],t.__electricCharacterSupport&&t.__electricCharacterSupport.docComment){var n=t.__electricCharacterSupport.docComment;this._autoClosingPairs.push(new c.b({open:n.open,close:n.close||""}))}this._autoCloseBefore="string"==typeof t.autoCloseBefore?t.autoCloseBefore:e.DEFAULT_AUTOCLOSE_BEFORE_LANGUAGE_DEFINED,this._surroundingPairs=t.surroundingPairs||this._autoClosingPairs}return e.prototype.getAutoClosingPairs=function(){return this._autoClosingPairs},e.prototype.getAutoCloseBeforeSet=function(){return this._autoCloseBefore},e.shouldAutoClosePair=function(e,t,n){if(0===t.getTokenCount())return!0;var i=t.findTokenIndexAtOffset(n-2),r=t.getStandardTokenType(i);return e.isOK(r)},e.prototype.getSurroundingPairs=function(){return this._surroundingPairs},e.DEFAULT_AUTOCLOSE_BEFORE_LANGUAGE_DEFINED=";:.,=}])> \n\t",e}(),h=n("iNUG"),f=function(){function e(e){this._richEditBrackets=e}return e.prototype.getElectricCharacters=function(){var e=[];if(this._richEditBrackets)for(var t=0,n=this._richEditBrackets.brackets.length;t0&&n.length>0)for(i=0,r=this._brackets.length;i0)for(i=0,r=this._brackets.length;i1){var r=void 0,o=-1;for(r=t-1;r>=1;r--){if(e.getLanguageIdAtPosition(r,0)!==i)return o;var s=e.getLineContent(r);if(!n.shouldIgnore(s)&&!/^\s+$/.test(s)&&""!==s)return r;o=r}}return-1},e.prototype.getInheritIndentForLine=function(e,t,n){void 0===n&&(n=!0);var i=this.getIndentRulesSupport(e.getLanguageIdentifier().id);if(!i)return null;if(t<=1)return{indentation:"",action:null};var r=this.getPrecedingValidLine(e,t,i);if(r<0)return null;if(r<1)return{indentation:"",action:null};var o=e.getLineContent(r);if(i.shouldIncrease(o)||i.shouldIndentNextLine(o))return{indentation:s.s(o),action:c.a.Indent,line:r};if(i.shouldDecrease(o))return{indentation:s.s(o),action:null,line:r};if(1===r)return{indentation:s.s(e.getLineContent(r)),action:null,line:r};var a=r-1,u=i.getIndentMetadata(e.getLineContent(a));if(!(3&u)&&4&u){for(var l=0,d=a-1;d>0;d--)if(!i.shouldIndentNextLine(e.getLineContent(d))){l=d;break}return{indentation:s.s(e.getLineContent(l+1)),action:null,line:l+1}}if(n)return{indentation:s.s(e.getLineContent(r)),action:null,line:r};for(d=r;d>0;d--){var h=e.getLineContent(d);if(i.shouldIncrease(h))return{indentation:s.s(h),action:c.a.Indent,line:d};if(i.shouldIndentNextLine(h)){l=0;for(var f=d-1;f>0;f--)if(!i.shouldIndentNextLine(e.getLineContent(d))){l=f;break}return{indentation:s.s(e.getLineContent(l+1)),action:null,line:l+1}}if(i.shouldDecrease(h))return{indentation:s.s(h),action:null,line:d}}return{indentation:s.s(e.getLineContent(1)),action:null,line:1}},e.prototype.getGoodIndentForLine=function(e,t,n,r){var o=this.getIndentRulesSupport(t);if(!o)return null;var a=this.getInheritIndentForLine(e,n),u=e.getLineContent(n);if(a){var l=a.line;if(void 0!==l){var d=this._getOnEnterSupport(t),h=null;try{d&&(h=d.onEnter("",e.getLineContent(l),""))}catch(e){Object(i.e)(e)}if(h){var f=s.s(e.getLineContent(l));return h.removeText&&(f=f.substring(0,f.length-h.removeText)),h.indentAction===c.a.Indent||h.indentAction===c.a.IndentOutdent?f=r.shiftIndent(f):h.indentAction===c.a.Outdent&&(f=r.unshiftIndent(f)),o.shouldDecrease(u)&&(f=r.unshiftIndent(f)),h.appendText&&(f+=h.appendText),s.s(f)}}return o.shouldDecrease(u)?a.action===c.a.Indent?a.indentation:r.unshiftIndent(a.indentation):a.action===c.a.Indent?r.shiftIndent(a.indentation):a.indentation}return null},e.prototype.getIndentForEnter=function(e,t,n,i){e.forceTokenization(t.startLineNumber);var r,o,a=e.getLineTokens(t.startLineNumber),u=Object(l.a)(a,t.startColumn-1),d=u.getLineContent(),h=!1;(u.firstCharOffset>0&&a.getLanguageId(0)!==u.languageId?(h=!0,r=d.substr(0,t.startColumn-1-u.firstCharOffset)):r=a.getLineContent().substring(0,t.startColumn-1),t.isEmpty())?o=d.substr(t.startColumn-1-u.firstCharOffset):o=this.getScopedLineTokens(e,t.endLineNumber,t.endColumn).getLineContent().substr(t.endColumn-1-u.firstCharOffset);var f=this.getIndentRulesSupport(u.languageId);if(!f)return null;var p=r,g=s.s(r);if(!i&&!h){var m=this.getInheritIndentForLine(e,t.startLineNumber);f.shouldDecrease(r)&&m&&(g=m.indentation,m.action!==c.a.Indent&&(g=n.unshiftIndent(g))),p=g+s.B(s.B(r," "),"\t")}var v={getLineTokens:function(t){return e.getLineTokens(t)},getLanguageIdentifier:function(){return e.getLanguageIdentifier()},getLanguageIdAtPosition:function(t,n){return e.getLanguageIdAtPosition(t,n)},getLineContent:function(n){return n===t.startLineNumber?p:e.getLineContent(n)}},_=s.s(a.getLineContent()),b=this.getInheritIndentForLine(v,t.startLineNumber+1);if(!b){var y=h?_:g;return{beforeEnter:y,afterEnter:y}}var w=h?_:b.indentation;return b.action===c.a.Indent&&(w=n.shiftIndent(w)),f.shouldDecrease(o)&&(w=n.unshiftIndent(w)),{beforeEnter:h?_:g,afterEnter:w}},e.prototype.getIndentActionForType=function(e,t,n,i){var r=this.getScopedLineTokens(e,t.startLineNumber,t.startColumn),o=this.getIndentRulesSupport(r.languageId);if(!o)return null;var s,a=r.getLineContent(),u=a.substr(0,t.startColumn-1-r.firstCharOffset);t.isEmpty()?s=a.substr(t.startColumn-1-r.firstCharOffset):s=this.getScopedLineTokens(e,t.endLineNumber,t.endColumn).getLineContent().substr(t.endColumn-1-r.firstCharOffset);if(!o.shouldDecrease(u+s)&&o.shouldDecrease(u+n+s)){var l=this.getInheritIndentForLine(e,t.startLineNumber,!1);if(!l)return null;var d=l.indentation;return l.action!==c.a.Indent&&(d=i.unshiftIndent(d)),d}return null},e.prototype.getIndentMetadata=function(e,t){var n=this.getIndentRulesSupport(e.getLanguageIdentifier().id);return n?t<1||t>e.getLineCount()?null:n.getIndentMetadata(e.getLineContent(t)):null},e.prototype._getOnEnterSupport=function(e){var t=this._getRichEditSupport(e);return t&&t.onEnter||null},e.prototype.getRawEnterActionAtPosition=function(e,t,n){var i=this.getEnterAction(e,new a.a(t,n,t,n));return i?i.enterAction:null},e.prototype.getEnterAction=function(e,t){var n=this.getIndentationAtPosition(e,t.startLineNumber,t.startColumn),r=this.getScopedLineTokens(e,t.startLineNumber,t.startColumn),o=this._getOnEnterSupport(r.languageId);if(!o)return null;var s,a=r.getLineContent(),u=a.substr(0,t.startColumn-1-r.firstCharOffset);t.isEmpty()?s=a.substr(t.startColumn-1-r.firstCharOffset):s=this.getScopedLineTokens(e,t.endLineNumber,t.endColumn).getLineContent().substr(t.endColumn-1-r.firstCharOffset);var l=t.startLineNumber,d="";if(l>1&&0===r.firstCharOffset){var h=this.getScopedLineTokens(e,l-1);h.languageId===r.languageId&&(d=h.getLineContent())}var f=null;try{f=o.onEnter(d,u,s)}catch(e){Object(i.e)(e)}return f?(f.appendText||(f.indentAction===c.a.Indent||f.indentAction===c.a.IndentOutdent?f.appendText="\t":f.appendText=""),f.removeText&&(n=n.substring(0,n.length-f.removeText)),{enterAction:f,indentation:n}):null},e.prototype.getIndentationAtPosition=function(e,t,n){var i=e.getLineContent(t),r=s.s(i);return r.length>n-1&&(r=r.substring(0,n-1)),r},e.prototype.getScopedLineTokens=function(e,t,n){e.forceTokenization(t);var i=e.getLineTokens(t),r=void 0===n?e.getLineMaxColumn(t)-1:n-1;return Object(l.a)(i,r)},e.prototype.getBracketsSupport=function(e){var t=this._getRichEditSupport(e);return t&&t.brackets||null},e}())},G7Ib:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=n("03Zz"),r=n("0Td8");Object(i.h)(r.f),Object(i.f)(r.e),Object(i.f)(r.g),Object(i.f)(r.h),Object(i.f)(r.d),Object(i.f)(r.a),Object(i.f)(r.c),Object(i.g)(new r.b)},G8r4:function(e,t,n){"use strict";n.d(t,"a",function(){return r});var i=n("Kp7x"),r=new(function(){function e(){this._zoomLevel=0,this._onDidChangeZoomLevel=new i.a,this.onDidChangeZoomLevel=this._onDidChangeZoomLevel.event}return e.prototype.getZoomLevel=function(){return this._zoomLevel},e.prototype.setZoomLevel=function(e){e=Math.min(Math.max(-5,e),20),this._zoomLevel!==e&&(this._zoomLevel=e,this._onDidChangeZoomLevel.fire(this._zoomLevel))},e}())},GUE9:function(e,t,n){(function(t,i){var r,o=n("2JY6"),s=n("35aj"),a=n("Zq1s"),u=n("X3l8").Buffer,c=t.crypto&&t.crypto.subtle,l={sha:"SHA-1","sha-1":"SHA-1",sha1:"SHA-1",sha256:"SHA-256","sha-256":"SHA-256",sha384:"SHA-384","sha-384":"SHA-384","sha-512":"SHA-512",sha512:"SHA-512"},d=[];function h(e,t,n,i,r){return c.importKey("raw",e,{name:"PBKDF2"},!1,["deriveBits"]).then(function(e){return c.deriveBits({name:"PBKDF2",salt:t,iterations:n,hash:{name:r}},e,i<<3)}).then(function(e){return u.from(e)})}e.exports=function(e,n,f,p,g,m){"function"==typeof g&&(m=g,g=void 0);var v=l[(g=g||"sha1").toLowerCase()];if(!v||"function"!=typeof t.Promise)return i.nextTick(function(){var t;try{t=a(e,n,f,p,g)}catch(e){return m(e)}m(null,t)});if(o(e,n,f,p),"function"!=typeof m)throw new Error("No callback provided to pbkdf2");u.isBuffer(e)||(e=u.from(e,s)),u.isBuffer(n)||(n=u.from(n,s)),function(e,t){e.then(function(e){i.nextTick(function(){t(null,e)})},function(e){i.nextTick(function(){t(e)})})}(function(e){if(t.process&&!t.process.browser)return Promise.resolve(!1);if(!c||!c.importKey||!c.deriveBits)return Promise.resolve(!1);if(void 0!==d[e])return d[e];var n=h(r=r||u.alloc(8),r,10,128,e).then(function(){return!0}).catch(function(){return!1});return d[e]=n,n}(v).then(function(t){return t?h(e,n,f,p,v):a(e,n,f,p,g)}),m)}}).call(t,n("DuR2"),n("W2nU"))},GV5w:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n.d(t,"GotoLineEntry",function(){return g}),n.d(t,"GotoLineAction",function(){return m});var i,r=n("wtJh"),o=(n.n(r),n("aL7J")),s=n("Al6Q"),a=n("P1SM"),u=n("03Zz"),c=n("artP"),l=n("vTy2"),d=n("/9db"),h=n("zwZj"),f=n("EfIu"),p=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),g=function(e){function t(t,n,i){var r=e.call(this)||this;return r.editor=n,r.decorator=i,r.parseResult=r.parseInput(t),r}return p(t,e),t.prototype.parseInput=function(e){var t,n,i=e.split(",").map(function(e){return parseInt(e,10)}).filter(function(e){return!isNaN(e)});if(t=0===i.length?new c.a(-1,-1):1===i.length?new c.a(i[0],1):new c.a(i[0],i[1]),Object(a.a)(this.editor))n=this.editor.getModel();else{var r=this.editor.getModel();n=r?r.modified:null}var s=!!n&&n.validatePosition(t).equals(t);return{position:t,isValid:s,label:s?t.column&&t.column>1?o.r(f.b.gotoLineLabelValidLineAndColumn,t.lineNumber,t.column):o.r(f.b.gotoLineLabelValidLine,t.lineNumber):t.lineNumber<1||t.lineNumber>(n?n.getLineCount():0)?o.r(f.b.gotoLineLabelEmptyWithLineLimit,n?n.getLineCount():0):o.r(f.b.gotoLineLabelEmptyWithLineAndColumnLimit,n?n.getLineMaxColumn(t.lineNumber):0)}},t.prototype.getLabel=function(){return this.parseResult.label},t.prototype.getAriaLabel=function(){var e=this.editor.getPosition(),t=e?e.lineNumber:0;return o.r(f.b.gotoLineAriaLabel,t,this.parseResult.label)},t.prototype.run=function(e,t){return 1===e?this.runOpen():this.runPreview()},t.prototype.runOpen=function(){if(!this.parseResult.isValid)return!1;var e=this.toSelection();return this.editor.setSelection(e),this.editor.revealRangeInCenter(e,0),this.editor.focus(),!0},t.prototype.runPreview=function(){if(!this.parseResult.isValid)return this.decorator.clearDecorations(),!1;var e=this.toSelection();return this.editor.revealRangeInCenter(e,0),this.decorator.decorateLine(e,this.editor),!1},t.prototype.toSelection=function(){return new l.a(this.parseResult.position.lineNumber,this.parseResult.position.column,this.parseResult.position.lineNumber,this.parseResult.position.column)},t}(s.a),m=function(e){function t(){return e.call(this,f.b.gotoLineActionInput,{id:"editor.action.gotoLine",label:f.b.gotoLineActionLabel,alias:"Go to Line...",precondition:void 0,kbOpts:{kbExpr:d.a.focus,primary:2085,mac:{primary:293},weight:100}})||this}return p(t,e),t.prototype.run=function(e,t){var n=this;this._show(this.getController(t),{getModel:function(e){return new s.c([new g(e,t,n.getController(t))])},getAutoFocus:function(e){return{autoFocusFirstEntry:e.length>0}}})},t}(h.a);Object(u.f)(m)},GYOr:function(e,t,n){"use strict";n.d(t,"g",function(){return s}),t.f=function(e,t,n){void 0===n&&(n=!1);if("string"!=typeof e||"string"!=typeof t)return null;var i=b.get(e);i||(i=new RegExp(r.j(e),"i"),b.set(e,i));var o=i.exec(t);if(o)return[{start:o.index,end:o.index+o[0].length}];return n?_(e,t):v(e,t)},t.b=function(e,t,n,i,r,o){var s=I(e,t,0,i,r,0,!0);if(s)return s;for(var a=0,u=0,c=o,l=0;l=0)u+=1,a+=Math.pow(2,d),c=d+1;else if(0!==a)break}return[u,a,o]},t.c=function(e){if(void 0===e)return[];for(var t=e[1].toString(2),n=[],i=e[2];i=3)for(var c=Math.min(7,e.length-1),l=n+1;lu[0])&&(u=h))}}return u}(e,t,n,i,r,o,!0,s)};var i=n("WTFd"),r=n("aL7J");function o(){for(var e=[],t=0;t0?[{start:0,end:t.length}]:[]}.bind(void 0,!0);function a(e){return 97<=e&&e<=122}function u(e){return 65<=e&&e<=90}function c(e){return 48<=e&&e<=57}function l(e){return 32===e||9===e||10===e||13===e}var d=new Set;function h(e){return a(e)||u(e)||c(e)}function f(e,t){return 0===t.length?t=[e]:e.end===t[0].start?t[0].start=e.start:t.unshift(e),t}function p(e,t){for(var n=t;n0&&!h(e.charCodeAt(n-1)))return n}return e.length}function g(e,t,n,i){if(n===e.length)return[];if(i===t.length)return null;if(e[n]!==t[i].toLowerCase())return null;var r=null,o=i+1;for(r=g(e,t,n+1,i+1);!r&&(o=p(t,o))60)return null;var n=function(e){for(var t=0,n=0,i=0,r=0,o=0,s=0;s.2&&t<.8&&i>.6&&r<.2}(n)){if(!function(e){var t=e.upperPercent;return 0===e.lowerPercent&&t>.6}(n))return null;t=t.toLowerCase()}var i=null,r=0;for(e=e.toLowerCase();r/?".split("").forEach(function(e){return d.add(e.charCodeAt(0))});var v=o(s,m,function(e,t){var n=t.toLowerCase().indexOf(e.toLowerCase());return-1===n?null:[{start:n,end:n+e.length}]}),_=o(s,m,function(e,t){return function e(t,n,i,r){if(i===t.length)return[];if(r===n.length)return null;if(t[i]===n[r]){var o=null;return(o=e(t,n,i+1,r+1))?f({start:r,end:r+1},o):null}return e(t,n,i,r+1)}(e.toLowerCase(),t.toLowerCase(),0,0)}),b=new i.a(1e4);var y=128;function w(){for(var e=[],t=[0],n=1;n<=y;n++)t.push(-n);for(n=0;n<=y;n++){var i=t.slice(0);i[0]=-n,e.push(i)}return e}var C,S=w(),x=w(),L=w(),O=!1;function k(e,t,n,i,r){function o(e,t,n){for(void 0===n&&(n=" ");e.length=e.length)return!1;switch(e.charCodeAt(t)){case 95:case 45:case 46:case 32:case 47:case 92:case 39:case 34:case 58:case 36:return!0;default:return!1}}function E(e,t,n){return t[e]!==n[e]}function I(e,t,n,i,r,o,s){var a=e.length>y?y:e.length,u=i.length>y?y:i.length;if(!(n>=a||o>=u||a>u)&&function(e,t,n,i,r,o){for(;t1?1:f),g=S[c-1][l]+-1,m=S[c][l-1]+-1;m>=g?m>p?(S[c][l]=m,L[c][l]=4):m===p?(S[c][l]=m,L[c][l]=6):(S[c][l]=p,L[c][l]=2):g>p?(S[c][l]=g,L[c][l]=1):g===p?(S[c][l]=g,L[c][l]=3):(S[c][l]=p,L[c][l]=2)}if(O&&function(e,t,n,i){e=e.substr(t),n=n.substr(i),console.log(k(S,e,e.length,n,n.length)),console.log(k(L,e,e.length,n,n.length)),console.log(k(x,e,e.length,n,n.length))}(e,n,i,o),M=0,P=-100,A=o,R=s,function e(t,n,i,r,o){if(M>=10||i<-25)return;var s=0;for(;t>0&&n>0;){var a=x[t][n],u=L[t][n];if(4===u)n-=1,o?i-=5:0!==r&&(i-=1),o=!1,s=0;else{if(!(2&u))return;if(4&u&&e(t,n-1,0!==r?i-1:i,r,o),i+=a,t-=1,n-=1,o=!0,r+=Math.pow(2,n+A),1===a){if(s+=1,0===t&&!R)return}else i+=1+s*(a-1),s=0}}i-=n>=3?9:3*n;M+=1;i>P&&(P=i,T=r)}(c-1,l-1,a===u?1:0,0,!1),0!==M)return[P,T,o]}}function D(e,t,n,i,r,o,s){return t[n]!==o[s]?-1:s===n-i?e[n]===r[s]?7:5:!E(s,r,o)||0!==s&&E(s-1,r,o)?!N(o,s)||0!==s&&N(o,s-1)?N(o,s-1)||function(e,t){if(t<0||t>=e.length)return!1;switch(e.charCodeAt(t)){case 32:case 9:return!0;default:return!1}}(o,s-1)?5:1:5:e[n]===r[s]?7:5}!function(e){e.Default=Object.freeze([-100,0,0]),e.isDefault=function(e){return!e||-100===e[0]&&0===e[1]&&0===e[2]}}(C||(C={}));var M=0,T=0,P=0,A=0,R=!1;function F(e,t){if(!(t+1>=e.length)){var n=e[t],i=e[t+1];if(n!==i)return e.slice(0,t)+i+n+e.slice(t+2)}}},GZKt:function(e,t){},GfE5:function(e,t,n){"use strict";n.d(t,"a",function(){return r}),n.d(t,"b",function(){return o});var i=n("CQAd"),r=function(){function e(t){var n=Object(i.d)(t);this._defaultValue=n,this._asciiMap=e._createAsciiMap(n),this._map=new Map}return e._createAsciiMap=function(e){for(var t=new Uint8Array(256),n=0;n<256;n++)t[n]=e;return t},e.prototype.set=function(e,t){var n=Object(i.d)(t);e>=0&&e<256?this._asciiMap[e]=n:this._map.set(e,n)},e.prototype.get=function(e){return e>=0&&e<256?this._asciiMap[e]:this._map.get(e)||this._defaultValue},e}(),o=function(){function e(){this._actual=new r(0)}return e.prototype.add=function(e){this._actual.set(e,1)},e.prototype.has=function(e){return 1===this._actual.get(e)},e}()},GgF8:function(e,t){var n="[object String]",i=Object.prototype.toString,r=Array.isArray;e.exports=function(e){return"string"==typeof e||!r(e)&&function(e){return!!e&&"object"==typeof e}(e)&&i.call(e)==n}},GsV8:function(e,t,n){"use strict";n.d(t,"a",function(){return r}),n.d(t,"b",function(){return o});var i=n("JVO/"),r=Object(i.c)("openerService"),o=Object.freeze({_serviceBrand:void 0,registerOpener:function(){return{dispose:function(){}}},registerValidator:function(){return{dispose:function(){}}},open:function(){return Promise.resolve(!1)}})},Gu5N:function(e,t){},Gu7T:function(e,t,n){"use strict";t.__esModule=!0;var i,r=n("c/Tr"),o=(i=r)&&i.__esModule?i:{default:i};t.default=function(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);tl&&(d=l,h=e.model.getLineMaxColumn(d)),o.d.fromModelState(new o.f(new c.a(s.lineNumber,1,d,h),0,new u.a(d,h),0))}var f=t.modelState.selectionStart.getStartPosition().lineNumber;if(s.lineNumberf){l=e.viewModel.getLineCount();var p=a.lineNumber+1,g=1;return p>l&&(p=l,g=e.viewModel.getLineMaxColumn(p)),o.d.fromViewState(t.viewState.move(t.modelState.hasSelection(),p,g,0))}var m=t.modelState.selectionStart.getEndPosition();return o.d.fromModelState(t.modelState.move(t.modelState.hasSelection(),m.lineNumber,m.column,0))},e.word=function(e,t,n,i){var r=e.model.validatePosition(i);return o.d.fromModelState(a.a.word(e.config,e.model,t.modelState,n,r))},e.cancelSelection=function(e,t){if(!t.modelState.hasSelection())return new o.d(t.modelState,t.viewState);var n=t.viewState.position.lineNumber,i=t.viewState.position.column;return o.d.fromViewState(new o.f(new c.a(n,i,n,i),0,new u.a(n,i),0))},e.moveTo=function(e,t,n,i,r){var s=e.model.validatePosition(i),a=r?e.validateViewPosition(new u.a(r.lineNumber,r.column),s):e.convertModelPositionToViewPosition(s);return o.d.fromViewState(t.viewState.move(n,a.lineNumber,a.column,0))},e.move=function(e,t,n){var i=n.select,r=n.value;switch(n.direction){case 0:return 4===n.unit?this._moveHalfLineLeft(e,t,i):this._moveLeft(e,t,i,r);case 1:return 4===n.unit?this._moveHalfLineRight(e,t,i):this._moveRight(e,t,i,r);case 2:return 2===n.unit?this._moveUpByViewLines(e,t,i,r):this._moveUpByModelLines(e,t,i,r);case 3:return 2===n.unit?this._moveDownByViewLines(e,t,i,r):this._moveDownByModelLines(e,t,i,r);case 4:return this._moveToViewMinColumn(e,t,i);case 5:return this._moveToViewFirstNonWhitespaceColumn(e,t,i);case 6:return this._moveToViewCenterColumn(e,t,i);case 7:return this._moveToViewMaxColumn(e,t,i);case 8:return this._moveToViewLastNonWhitespaceColumn(e,t,i);case 9:var o=t[0],s=e.getCompletelyVisibleModelRange(),a=this._firstLineNumberInRange(e.model,s,r),u=e.model.getLineFirstNonWhitespaceColumn(a);return[this._moveToModelPosition(e,o,i,a,u)];case 11:o=t[0],s=e.getCompletelyVisibleModelRange(),a=this._lastLineNumberInRange(e.model,s,r),u=e.model.getLineFirstNonWhitespaceColumn(a);return[this._moveToModelPosition(e,o,i,a,u)];case 10:o=t[0],s=e.getCompletelyVisibleModelRange(),a=Math.round((s.startLineNumber+s.endLineNumber)/2),u=e.model.getLineFirstNonWhitespaceColumn(a);return[this._moveToModelPosition(e,o,i,a,u)];case 12:for(var c=e.getCompletelyVisibleViewRange(),l=[],d=0,h=t.length;dn.endLineNumber-1&&(r=n.endLineNumber-1),rr,d=i>o,h=io)continue;if(bi)continue;if(_1&&r--,e.columnSelect(t,n,i.fromViewLineNumber,i.fromViewVisualColumn,i.toViewLineNumber,r)},e.columnSelectRight=function(e,t,n){for(var i=0,r=Math.min(n.fromViewLineNumber,n.toViewLineNumber),o=Math.max(n.fromViewLineNumber,n.toViewLineNumber),s=r;s<=o;s++){var c=t.getLineMaxColumn(s),l=a.a.visibleColumnFromColumn2(e,t,new u.a(s,c));i=Math.max(i,l)}var d=n.toViewVisualColumn;return d1)for(var o=n.modelState?n.modelState.position:null,s=n.viewState?n.viewState.position:null,a=0,u=r.length;ar&&(i=r);var o=new c.a(i,1,i,e.context.model.getLineMaxColumn(i)),s=0;if(n.at)switch(n.at){case b.RawAtArgument.Top:s=3;break;case b.RawAtArgument.Center:s=1;break;case b.RawAtArgument.Bottom:s=4}var a=e.context.convertModelRangeToViewRange(o);e.revealRange(!1,a,s,0)},t}(k))),e.SelectAll=Object(o.g)(new(function(e){function t(){return e.call(this,{id:"selectAll",precondition:void 0})||this}return L(t,e),t.prototype.runCoreEditorCommand=function(e,t){e.context.model.pushStackElement(),e.setStates(t.source,3,[h.b.selectAll(e.context,e.getPrimaryCursor())])},t}(k))),e.SetSelection=Object(o.g)(new(function(e){function t(){return e.call(this,{id:"setSelection",precondition:void 0})||this}return L(t,e),t.prototype.runCoreEditorCommand=function(e,t){e.context.model.pushStackElement(),e.setStates(t.source,3,[a.d.fromModelSelection(t.selection)])},t}(k)))}(w||(w={})),S=C||(C={}),x=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return L(t,e),t.prototype.runEditorCommand=function(e,t,n){var i=t._getCursors();i&&this.runCoreEditingCommand(t,i,n||{})},t}(o.c),S.CoreEditingCommand=x,S.LineBreakInsert=Object(o.g)(new(function(e){function t(){return e.call(this,{id:"lineBreakInsert",precondition:g.a.writable,kbOpts:{weight:O,kbExpr:g.a.textInputFocus,primary:0,mac:{primary:301}}})||this}return L(t,e),t.prototype.runCoreEditingCommand=function(e,t,n){e.pushUndoStop(),e.executeCommands(this.id,f.a.lineBreakInsert(t.context.config,t.context.model,t.getAll().map(function(e){return e.modelState.selection})))},t}(x))),S.Outdent=Object(o.g)(new(function(e){function t(){return e.call(this,{id:"outdent",precondition:g.a.writable,kbOpts:{weight:O,kbExpr:m.a.and(g.a.editorTextFocus,g.a.tabDoesNotMoveFocus),primary:1026}})||this}return L(t,e),t.prototype.runCoreEditingCommand=function(e,t,n){e.pushUndoStop(),e.executeCommands(this.id,f.a.outdent(t.context.config,t.context.model,t.getAll().map(function(e){return e.modelState.selection}))),e.pushUndoStop()},t}(x))),S.Tab=Object(o.g)(new(function(e){function t(){return e.call(this,{id:"tab",precondition:g.a.writable,kbOpts:{weight:O,kbExpr:m.a.and(g.a.editorTextFocus,g.a.tabDoesNotMoveFocus),primary:2}})||this}return L(t,e),t.prototype.runCoreEditingCommand=function(e,t,n){e.pushUndoStop(),e.executeCommands(this.id,f.a.tab(t.context.config,t.context.model,t.getAll().map(function(e){return e.modelState.selection}))),e.pushUndoStop()},t}(x))),S.DeleteLeft=Object(o.g)(new(function(e){function t(){return e.call(this,{id:"deleteLeft",precondition:g.a.writable,kbOpts:{weight:O,kbExpr:g.a.textInputFocus,primary:1,secondary:[1025],mac:{primary:1,secondary:[1025,294,257]}}})||this}return L(t,e),t.prototype.runCoreEditingCommand=function(e,t,n){var i=d.a.deleteLeft(t.getPrevEditOperationType(),t.context.config,t.context.model,t.getAll().map(function(e){return e.modelState.selection})),r=i[0],o=i[1];r&&e.pushUndoStop(),e.executeCommands(this.id,o),t.setPrevEditOperationType(2)},t}(x))),S.DeleteRight=Object(o.g)(new(function(e){function t(){return e.call(this,{id:"deleteRight",precondition:g.a.writable,kbOpts:{weight:O,kbExpr:g.a.textInputFocus,primary:20,mac:{primary:20,secondary:[290,276]}}})||this}return L(t,e),t.prototype.runCoreEditingCommand=function(e,t,n){var i=d.a.deleteRight(t.getPrevEditOperationType(),t.context.config,t.context.model,t.getAll().map(function(e){return e.modelState.selection})),r=i[0],o=i[1];r&&e.pushUndoStop(),e.executeCommands(this.id,o),t.setPrevEditOperationType(3)},t}(x)));var E=function(e){function t(t){var n=e.call(this,t)||this;return n._editorHandler=t.editorHandler,n._inputHandler=t.inputHandler,n}return L(t,e),t.prototype.runCommand=function(e,t){var n=e.get(s.a).getFocusedCodeEditor();if(n&&n.hasTextFocus())return this._runEditorHandler(e,n,t);var i=document.activeElement;if(!(i&&["input","textarea"].indexOf(i.tagName.toLowerCase())>=0)){var r=e.get(s.a).getActiveCodeEditor();return r?(r.focus(),this._runEditorHandler(e,r,t)):void 0}document.execCommand(this._inputHandler)},t.prototype._runEditorHandler=function(e,t,n){var i=this._editorHandler;"string"==typeof i?t.trigger("keyboard",i,n):((n=n||{}).source="keyboard",i.runEditorCommand(e,t,n))},t}(o.a),I=function(e){function t(t,n,i){var r=e.call(this,{id:t,precondition:void 0,description:i})||this;return r._handlerId=n,r}return L(t,e),t.prototype.runCommand=function(e,t){var n=e.get(s.a).getFocusedCodeEditor();n&&n.trigger("keyboard",this._handlerId,t)},t}(o.a);function D(e,t){N(new I("default:"+e,e)),N(new I(e,e,t))}N(new E({editorHandler:w.SelectAll,inputHandler:"selectAll",id:"editor.action.selectAll",precondition:g.a.textInputFocus,kbOpts:{weight:O,kbExpr:null,primary:2079},menubarOpts:{menuId:22,group:"1_basic",title:i.a({key:"miSelectAll",comment:["&& denotes a mnemonic"]},"&&Select All"),order:1}})),N(new E({editorHandler:p.b.Undo,inputHandler:"undo",id:p.b.Undo,precondition:g.a.writable,kbOpts:{weight:O,kbExpr:g.a.textInputFocus,primary:2104},menubarOpts:{menuId:14,group:"1_do",title:i.a({key:"miUndo",comment:["&& denotes a mnemonic"]},"&&Undo"),order:1}})),N(new I("default:"+p.b.Undo,p.b.Undo)),N(new E({editorHandler:p.b.Redo,inputHandler:"redo",id:p.b.Redo,precondition:g.a.writable,kbOpts:{weight:O,kbExpr:g.a.textInputFocus,primary:2103,secondary:[3128],mac:{primary:3128}},menubarOpts:{menuId:14,group:"1_do",title:i.a({key:"miRedo",comment:["&& denotes a mnemonic"]},"&&Redo"),order:2}})),N(new I("default:"+p.b.Redo,p.b.Redo)),D(p.b.Type,{description:"Type",args:[{name:"args",schema:{type:"object",required:["text"],properties:{text:{type:"string"}}}}]}),D(p.b.ReplacePreviousChar),D(p.b.CompositionStart),D(p.b.CompositionEnd),D(p.b.Paste),D(p.b.Cut)},Hv4S:function(e,t){},HzeT:function(e,t,n){"use strict";var i=n("3PYz"),r=n("tpuU"),o=n("08Lv");function s(e){if(!(this instanceof s))return new s(e);this.hash=e.hash,this.predResist=!!e.predResist,this.outLen=this.hash.outSize,this.minEntropy=e.minEntropy||this.hash.hmacStrength,this._reseed=null,this.reseedInterval=null,this.K=null,this.V=null;var t=r.toArray(e.entropy,e.entropyEnc||"hex"),n=r.toArray(e.nonce,e.nonceEnc||"hex"),i=r.toArray(e.pers,e.persEnc||"hex");o(t.length>=this.minEntropy/8,"Not enough entropy. Minimum is: "+this.minEntropy+" bits"),this._init(t,n,i)}e.exports=s,s.prototype._init=function(e,t,n){var i=e.concat(t).concat(n);this.K=new Array(this.outLen/8),this.V=new Array(this.outLen/8);for(var r=0;r=this.minEntropy/8,"Not enough entropy. Minimum is: "+this.minEntropy+" bits"),this._update(e.concat(n||[])),this._reseed=1},s.prototype.generate=function(e,t,n,i){if(this._reseed>this.reseedInterval)throw new Error("Reseed is required");"string"!=typeof t&&(i=n,n=t,t=null),n&&(n=r.toArray(n,i||"hex"),this._update(n));for(var o=[];o.length=n)break;var r=e.charCodeAt(t);if(110===r||114===r||87===r)return!0}}return!1}(this.searchString):this.searchString.indexOf("\n")>=0;var t=null;try{t=i.k(this.searchString,this.isRegex,{matchCase:this.matchCase,wholeWord:!1,multiline:e,global:!0})}catch(e){return null}if(!t)return null;var n=!this.isRegex&&!e;return n&&this.searchString.toLowerCase()!==this.searchString.toUpperCase()&&(n=this.matchCase),new c(t,this.wordSeparators?Object(r.a)(this.wordSeparators):null,n?this.searchString:null)},e}();var c=function(){return function(e,t,n){this.regex=e,this.wordSeparators=t,this.simpleSearch=n}}();function l(e,t,n){if(!n)return new a.b(e,null);for(var i=[],r=0,o=t.length;r>0);t[r]>=e?i=r-1:t[r+1]>=e?(n=r,i=r):n=r+1}return n+1},e}(),h=function(){function e(){}return e.findMatches=function(e,t,n,i,r){var o=t.parseSearchRequest();return o?o.regex.multiline?this._doFindMatchesMultiline(e,n,new p(o.wordSeparators,o.regex),i,r):this._doFindMatchesLineByLine(e,n,o,i,r):[]},e._getMultilineMatchRange=function(e,t,n,i,r,o){var a,u,c=0;if(a=i?t+r+(c=i.findLineFeedCountBeforeOffset(r)):t+r,i){var l=i.findLineFeedCountBeforeOffset(r+o.length)-c;u=a+o.length+l}else u=a+o.length;var d=e.getPositionAt(a),h=e.getPositionAt(u);return new s.a(d.lineNumber,d.column,h.lineNumber,h.column)},e._doFindMatchesMultiline=function(e,t,n,i,r){var o,s=e.getOffsetAt(t.getStartPosition()),a=e.getValueInRange(t,1),u="\r\n"===e.getEOL()?new d(a):null,c=[],h=0;for(n.reset(0);o=n.next(a);)if(c[h++]=l(this._getMultilineMatchRange(e,s,a,u,o.index,o[0]),o,i),h>=r)return c;return c},e._doFindMatchesLineByLine=function(e,t,n,i,r){var o=[],s=0;if(t.startLineNumber===t.endLineNumber){var a=e.getLineContent(t.startLineNumber).substring(t.startColumn-1,t.endColumn-1);return s=this._findMatchesInLine(n,a,t.startLineNumber,t.startColumn-1,s,o,i,r),o}var u=e.getLineContent(t.startLineNumber).substring(t.startColumn-1);s=this._findMatchesInLine(n,u,t.startLineNumber,t.startColumn-1,s,o,i,r);for(var c=t.startLineNumber+1;c=c))return r;return r}var _,b=new p(e.wordSeparators,e.regex);b.reset(0);do{if((_=b.next(t))&&(o[r++]=l(new s.a(n,_.index+1+i,n,_.index+1+_[0].length+i),_,u),r>=c))return r}while(_);return r},e.findNextMatch=function(e,t,n,i){var r=t.parseSearchRequest();if(!r)return null;var o=new p(r.wordSeparators,r.regex);return r.regex.multiline?this._doFindNextMatchMultiline(e,n,o,i):this._doFindNextMatchLineByLine(e,n,o,i)},e._doFindNextMatchMultiline=function(e,t,n,i){var r=new o.a(t.lineNumber,1),a=e.getOffsetAt(r),u=e.getLineCount(),c=e.getValueInRange(new s.a(r.lineNumber,r.column,u,e.getLineMaxColumn(u)),1),h="\r\n"===e.getEOL()?new d(c):null;n.reset(t.column-1);var f=n.next(c);return f?l(this._getMultilineMatchRange(e,a,c,h,f.index,f[0]),f,i):1!==t.lineNumber||1!==t.column?this._doFindNextMatchMultiline(e,new o.a(1,1),n,i):null},e._doFindNextMatchLineByLine=function(e,t,n,i){var r=e.getLineCount(),o=t.lineNumber,s=e.getLineContent(o),a=this._findFirstMatchInLine(n,s,o,t.column,i);if(a)return a;for(var u=1;u<=r;u++){var c=(o+u-1)%r,l=e.getLineContent(c+1),d=this._findFirstMatchInLine(n,l,c+1,1,i);if(d)return d}return null},e._findFirstMatchInLine=function(e,t,n,i,r){e.reset(i-1);var o=e.next(t);return o?l(new s.a(n,o.index+1,n,o.index+1+o[0].length),o,r):null},e.findPreviousMatch=function(e,t,n,i){var r=t.parseSearchRequest();if(!r)return null;var o=new p(r.wordSeparators,r.regex);return r.regex.multiline?this._doFindPreviousMatchMultiline(e,n,o,i):this._doFindPreviousMatchLineByLine(e,n,o,i)},e._doFindPreviousMatchMultiline=function(e,t,n,i){var r=this._doFindMatchesMultiline(e,new s.a(1,1,t.lineNumber,t.column),n,i,9990);if(r.length>0)return r[r.length-1];var a=e.getLineCount();return t.lineNumber!==a||t.column!==e.getLineMaxColumn(a)?this._doFindPreviousMatchMultiline(e,new o.a(a,e.getLineMaxColumn(a)),n,i):null},e._doFindPreviousMatchLineByLine=function(e,t,n,i){var r=e.getLineCount(),o=t.lineNumber,s=e.getLineContent(o).substring(0,t.column-1),a=this._findLastMatchInLine(n,s,o,i);if(a)return a;for(var u=1;u<=r;u++){var c=(r+o-u-1)%r,l=e.getLineContent(c+1),d=this._findLastMatchInLine(n,l,c+1,i);if(d)return d}return null},e._findLastMatchInLine=function(e,t,n,i){var r,o=null;for(e.reset(0);r=e.next(t);)o=l(new s.a(n,r.index+1,n,r.index+1+r[0].length),r,i);return o},e}();function f(e,t,n,i,r){return function(e,t,n,i,r){if(0===i)return!0;var o=t.charCodeAt(i-1);if(0!==e.get(o))return!0;if(13===o||10===o)return!0;if(r>0){var s=t.charCodeAt(i);if(0!==e.get(s))return!0}return!1}(e,t,0,i,r)&&function(e,t,n,i,r){if(i+r===n)return!0;var o=t.charCodeAt(i+r);if(0!==e.get(o))return!0;if(13===o||10===o)return!0;if(r>0){var s=t.charCodeAt(i+r-1);if(0!==e.get(s))return!0}return!1}(e,t,n,i,r)}var p=function(){function e(e,t){this._wordSeparators=e,this._searchRegex=t,this._prevMatchStartIndex=-1,this._prevMatchLength=0}return e.prototype.reset=function(e){this._searchRegex.lastIndex=e,this._prevMatchStartIndex=-1,this._prevMatchLength=0},e.prototype.next=function(e){var t,n=e.length;do{if(this._prevMatchStartIndex+this._prevMatchLength===n)return null;if(!(t=this._searchRegex.exec(e)))return null;var i=t.index,r=t[0].length;if(i===this._prevMatchStartIndex&&r===this._prevMatchLength){if(0===r){this._searchRegex.lastIndex+=1;continue}return null}if(this._prevMatchStartIndex=i,this._prevMatchLength=r,!this._wordSeparators||f(this._wordSeparators,e,n,i,r))return t}while(t);return null},e}()},IG52:function(e,t,n){"use strict";n.d(t,"c",function(){return g}),n.d(t,"d",function(){return m}),n.d(t,"b",function(){return v}),n.d(t,"a",function(){return b});var i,r=n("LC7R"),o=(n.n(r),n("ZfGv")),s=n("hK2W"),a=n("tqet"),u=n("AKCZ"),c=n("7/Cv"),l=n("KIxu"),d=n("Bug4"),h=n("gzF+"),f=n("Kp7x"),p=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),g=function(e){function t(t,n,i){var r=e.call(this)||this;return r.options=i,r._context=t||r,r._action=n,n instanceof u.a&&r._register(n.onDidChange(function(e){r.element&&r.handleActionChangeEvent(e)})),r}return p(t,e),t.prototype.handleActionChangeEvent=function(e){void 0!==e.enabled&&this.updateEnabled(),void 0!==e.checked&&this.updateChecked(),void 0!==e.class&&this.updateClass(),void 0!==e.label&&(this.updateLabel(),this.updateTooltip()),void 0!==e.tooltip&&this.updateTooltip()},Object.defineProperty(t.prototype,"actionRunner",{get:function(){return this._actionRunner},set:function(e){this._actionRunner=e},enumerable:!0,configurable:!0}),t.prototype.getAction=function(){return this._action},t.prototype.isEnabled=function(){return this._action.enabled},t.prototype.setActionContext=function(e){this._context=e},t.prototype.render=function(e){var t=this;this.element=e,d.b.addTarget(e);var n=this.options&&this.options.draggable;n&&(e.draggable=!0),this._register(c.h(this.element,d.a.Tap,function(e){return t.onClick(e)})),this._register(c.h(this.element,c.d.MOUSE_DOWN,function(e){n||c.c.stop(e,!0),t._action.enabled&&0===e.button&&t.element&&c.f(t.element,"active")})),this._register(c.h(this.element,c.d.CLICK,function(e){c.c.stop(e,!0),t.options&&t.options.isMenu?t.onClick(e):o.h(function(){return t.onClick(e)})})),this._register(c.h(this.element,c.d.DBLCLICK,function(e){c.c.stop(e,!0)})),[c.d.MOUSE_UP,c.d.MOUSE_OUT].forEach(function(e){t._register(c.h(t.element,e,function(e){c.c.stop(e),c.I(t.element,"active")}))})},t.prototype.onClick=function(e){var t;c.c.stop(e,!0),l.k(this._context)?t=e:(t=this._context,l.h(t)&&(t.event=e)),this._actionRunner.run(this._action,t)},t.prototype.focus=function(){this.element&&(this.element.focus(),c.f(this.element,"focused"))},t.prototype.blur=function(){this.element&&(this.element.blur(),c.I(this.element,"focused"))},t.prototype.updateEnabled=function(){},t.prototype.updateLabel=function(){},t.prototype.updateTooltip=function(){},t.prototype.updateClass=function(){},t.prototype.updateChecked=function(){},t.prototype.dispose=function(){this.element&&(c.K(this.element),this.element=void 0),e.prototype.dispose.call(this)},t}(a.a),m=function(e){function t(n){var i=e.call(this,t.ID,n,n?"separator text":"separator")||this;return i.checked=!1,i.radio=!1,i.enabled=!1,i}return p(t,e),t.ID="vs.actions.separator",t}(u.a),v=function(e){function t(t,n,i){void 0===i&&(i={});var r=e.call(this,t,n,i)||this;return r.options=i,r.options.icon=void 0!==i.icon&&i.icon,r.options.label=void 0===i.label||i.label,r.cssClass="",r}return p(t,e),t.prototype.render=function(t){e.prototype.render.call(this,t),this.element&&(this.label=c.m(this.element,c.a("a.action-label"))),this._action.id===m.ID?this.label.setAttribute("role","presentation"):this.options.isMenu?this.label.setAttribute("role","menuitem"):this.label.setAttribute("role","button"),this.options.label&&this.options.keybinding&&this.element&&(c.m(this.element,c.a("span.keybinding")).textContent=this.options.keybinding),this.updateClass(),this.updateLabel(),this.updateTooltip(),this.updateEnabled(),this.updateChecked()},t.prototype.focus=function(){e.prototype.focus.call(this),this.label.focus()},t.prototype.updateLabel=function(){this.options.label&&(this.label.textContent=this.getAction().label)},t.prototype.updateTooltip=function(){var e=null;this.getAction().tooltip?e=this.getAction().tooltip:!this.options.label&&this.getAction().label&&this.options.icon&&(e=this.getAction().label,this.options.keybinding&&(e=s.a({key:"titleLabel",comment:["action title","action keybinding"]},"{0} ({1})",e,this.options.keybinding))),e&&(this.label.title=e)},t.prototype.updateClass=function(){this.cssClass&&c.J(this.label,this.cssClass),this.options.icon?(this.cssClass=this.getAction().class,c.f(this.label,"icon"),this.cssClass&&c.g(this.label,this.cssClass),this.updateEnabled()):c.I(this.label,"icon")},t.prototype.updateEnabled=function(){this.getAction().enabled?(this.label.removeAttribute("aria-disabled"),this.element&&c.I(this.element,"disabled"),c.I(this.label,"disabled"),this.label.tabIndex=0):(this.label.setAttribute("aria-disabled","true"),this.element&&c.f(this.element,"disabled"),c.f(this.label,"disabled"),c.L(this.label))},t.prototype.updateChecked=function(){this.getAction().checked?c.f(this.label,"checked"):c.I(this.label,"checked")},t}(g),_={orientation:0,context:null,triggerKeys:{keys:[3,10],keyDown:!1}},b=function(e){function t(t,n){void 0===n&&(n=_);var i,r,o=e.call(this)||this;switch(o._onDidBlur=o._register(new f.a),o.onDidBlur=o._onDidBlur.event,o._onDidCancel=o._register(new f.a),o.onDidCancel=o._onDidCancel.event,o._onDidRun=o._register(new f.a),o.onDidRun=o._onDidRun.event,o._onDidBeforeRun=o._register(new f.a),o.onDidBeforeRun=o._onDidBeforeRun.event,o.options=n,o._context=n.context,o.options.triggerKeys||(o.options.triggerKeys=_.triggerKeys),o.options.actionRunner?o._actionRunner=o.options.actionRunner:(o._actionRunner=new u.b,o._register(o._actionRunner)),o._register(o._actionRunner.onDidRun(function(e){return o._onDidRun.fire(e)})),o._register(o._actionRunner.onDidBeforeRun(function(e){return o._onDidBeforeRun.fire(e)})),o.viewItems=[],o.focusedItem=void 0,o.domNode=document.createElement("div"),o.domNode.className="monaco-action-bar",!1!==n.animated&&c.f(o.domNode,"animated"),o.options.orientation){case 0:i=15,r=17;break;case 1:i=17,r=15,o.domNode.className+=" reverse";break;case 2:i=16,r=18,o.domNode.className+=" vertical";break;case 3:i=18,r=16,o.domNode.className+=" vertical reverse"}return o._register(c.h(o.domNode,c.d.KEY_DOWN,function(e){var t=new h.a(e),n=!0;t.equals(i)?o.focusPrevious():t.equals(r)?o.focusNext():t.equals(9)?o.cancel():o.isTriggerKeyEvent(t)?o.options.triggerKeys&&o.options.triggerKeys.keyDown&&o.doTrigger(t):n=!1,n&&(t.preventDefault(),t.stopPropagation())})),o._register(c.h(o.domNode,c.d.KEY_UP,function(e){var t=new h.a(e);o.isTriggerKeyEvent(t)?(o.options.triggerKeys&&!o.options.triggerKeys.keyDown&&o.doTrigger(t),t.preventDefault(),t.stopPropagation()):(t.equals(2)||t.equals(1026))&&o.updateFocusedItem()})),o.focusTracker=o._register(c.S(o.domNode)),o._register(o.focusTracker.onDidBlur(function(){document.activeElement!==o.domNode&&c.E(document.activeElement,o.domNode)||(o._onDidBlur.fire(),o.focusedItem=void 0)})),o._register(o.focusTracker.onDidFocus(function(){return o.updateFocusedItem()})),o.actionsList=document.createElement("ul"),o.actionsList.className="actions-container",o.actionsList.setAttribute("role","toolbar"),o.options.ariaLabel&&o.actionsList.setAttribute("aria-label",o.options.ariaLabel),o.domNode.appendChild(o.actionsList),t.appendChild(o.domNode),o}return p(t,e),t.prototype.isTriggerKeyEvent=function(e){var t=!1;return this.options.triggerKeys&&this.options.triggerKeys.keys.forEach(function(n){t=t||e.equals(n)}),t},t.prototype.updateFocusedItem=function(){for(var e=0;e=n.actionsList.children.length?(n.actionsList.appendChild(o),n.viewItems.push(i)):(n.actionsList.insertBefore(o,n.actionsList.children[r]),n.viewItems.splice(r,0,i),r++)})},t.prototype.clear=function(){this.viewItems=Object(a.f)(this.viewItems),c.p(this.actionsList)},t.prototype.isEmpty=function(){return 0===this.viewItems.length},t.prototype.focus=function(e){var t=!1,n=void 0;void 0===e?t=!0:"number"==typeof e?n=e:"boolean"==typeof e&&(t=e),t&&void 0===this.focusedItem?(this.focusedItem=this.viewItems.length-1,this.focusNext()):(void 0!==n&&(this.focusedItem=n),this.updateFocus())},t.prototype.focusNext=function(){void 0===this.focusedItem&&(this.focusedItem=this.viewItems.length-1);var e,t=this.focusedItem;do{this.focusedItem=(this.focusedItem+1)%this.viewItems.length,e=this.viewItems[this.focusedItem]}while(this.focusedItem!==t&&!e.isEnabled());this.focusedItem!==t||e.isEnabled()||(this.focusedItem=void 0),this.updateFocus()},t.prototype.focusPrevious=function(){void 0===this.focusedItem&&(this.focusedItem=0);var e,t=this.focusedItem;do{this.focusedItem=this.focusedItem-1,this.focusedItem<0&&(this.focusedItem=this.viewItems.length-1),e=this.viewItems[this.focusedItem]}while(this.focusedItem!==t&&!e.isEnabled());this.focusedItem!==t||e.isEnabled()||(this.focusedItem=void 0),this.updateFocus(!0)},t.prototype.updateFocus=function(e){void 0===this.focusedItem&&this.actionsList.focus();for(var t=0;t>>1];n=s.r28shl(n,a),r=s.r28shl(r,a),s.pc2(n,r,e.keys,o)}},u.prototype._update=function(e,t,n,i){var r=this._desState,o=s.readUInt32BE(e,t),a=s.readUInt32BE(e,t+4);s.ip(o,a,r.tmp,0),o=r.tmp[0],a=r.tmp[1],"encrypt"===this.type?this._encrypt(r,o,a,r.tmp,0):this._decrypt(r,o,a,r.tmp,0),o=r.tmp[0],a=r.tmp[1],s.writeUInt32BE(n,o,i),s.writeUInt32BE(n,a,i+4)},u.prototype._pad=function(e,t){for(var n=e.length-t,i=t;i>>0,o=h}s.rip(a,o,i,r)},u.prototype._decrypt=function(e,t,n,i,r){for(var o=n,a=t,u=e.keys.length-2;u>=0;u-=2){var c=e.keys[u],l=e.keys[u+1];s.expand(o,e.tmp,0),c^=e.tmp[0],l^=e.tmp[1];var d=s.substitute(c,l),h=o;o=(a^s.permute(d))>>>0,a=h}s.rip(o,a,i,r)}},IrTV:function(e,t,n){"use strict";n.d(t,"a",function(){return m});var i,r=n("TU7t"),o=n("vORD"),s=n("vZcR"),a=n("ItKl"),u=n("7g0X"),c=n("JVO/"),l=n("fAkY"),d=n("eoic"),h=n("xJaW"),f=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),p=this&&this.__decorate||function(e,t,n,i){var r,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,i);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},g=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},m=function(e){function t(t,n,i,r,o,s,a,u,c,l){var d=e.call(this,t,i.getRawConfiguration(),{},r,o,s,a,u,c,l)||this;return d._parentEditor=i,d._overwriteOptions=n,e.prototype.updateOptions.call(d,d._overwriteOptions),d._register(i.onDidChangeConfiguration(function(e){return d._onParentConfigurationChanged(e)})),d}return f(t,e),t.prototype.getParentEditor=function(){return this._parentEditor},t.prototype._onParentConfigurationChanged=function(t){e.prototype.updateOptions.call(this,this._parentEditor.getRawConfiguration()),e.prototype.updateOptions.call(this,this._overwriteOptions)},t.prototype.updateOptions=function(t){r.g(this._overwriteOptions,t,!0),e.prototype.updateOptions.call(this,this._overwriteOptions)},t=p([g(3,c.a),g(4,o.a),g(5,a.b),g(6,u.c),g(7,d.c),g(8,l.a),g(9,h.b)],t)}(s.a)},ItKl:function(e,t,n){"use strict";n.d(t,"b",function(){return c}),n.d(t,"a",function(){return l});var i=n("tqet"),r=n("KIxu"),o=n("JVO/"),s=n("Kp7x"),a=n("EMhq"),u=n("WTFd"),c=Object(o.c)("commandService"),l=new(function(){function e(){this._commands=new Map,this._onDidRegisterCommand=new s.a,this.onDidRegisterCommand=this._onDidRegisterCommand.event}return e.prototype.registerCommand=function(e,t){var n=this;if(!e)throw new Error("invalid command");if("string"==typeof e){if(!t)throw new Error("invalid command");return this.registerCommand({id:e,handler:t})}if(e.description){for(var o=[],s=0,u=e.description.args;s0&&(f=t.apply(this,arguments)),e<=1&&(t=void 0),f}}function p(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}e.exports=function(e){return f(2,e)}},"JVO/":function(e,t,n){"use strict";var i;n.d(t,"b",function(){return i}),n.d(t,"a",function(){return r}),t.c=s,t.d=function(e){return function(t,n,i){if(3!==arguments.length)throw new Error("@optional-decorator can only be used to decorate a parameter");o(e,t,i,!0)}},function(e){e.serviceIds=new Map,e.DI_TARGET="$di$target",e.DI_DEPENDENCIES="$di$dependencies",e.getServiceDependencies=function(t){return t[e.DI_DEPENDENCIES]||[]}}(i||(i={}));var r=s("instantiationService");function o(e,t,n,r){t[i.DI_TARGET]===t?t[i.DI_DEPENDENCIES].push({id:e,index:n,optional:r}):(t[i.DI_DEPENDENCIES]=[{id:e,index:n,optional:r}],t[i.DI_TARGET]=t)}function s(e){if(i.serviceIds.has(e))return i.serviceIds.get(e);var t=function(e,n,i){if(3!==arguments.length)throw new Error("@IServiceName-decorator can only be used to decorate a parameter");o(t,e,i,!1)};return t.toString=function(){return e},i.serviceIds.set(e,t),t}},JaR3:function(e,t,n){(t=e.exports=function(e){e=e.toLowerCase();var n=t[e];if(!n)throw new Error(e+" is not supported (we accept pull requests)");return new n}).sha=n("N1es"),t.sha1=n("KQ4j"),t.sha224=n("lXn8"),t.sha256=n("zvjZ"),t.sha384=n("aY2F"),t.sha512=n("C015")},JbsQ:function(e,t,n){"use strict";n.d(t,"a",function(){return r});var i=n("JVO/"),r=Object(i.c)("markerDecorationsService")},K8Am:function(e,t,n){"use strict";n.d(t,"a",function(){return o});var i=n("ZfGv"),r=i.b.performance&&"function"==typeof i.b.performance.now,o=function(){function e(e){this._highResolution=r&&e,this._startTime=this._now(),this._stopTime=-1}return e.create=function(t){return void 0===t&&(t=!0),new e(t)},e.prototype.stop=function(){this._stopTime=this._now()},e.prototype.elapsed=function(){return-1!==this._stopTime?this._stopTime-this._startTime:this._now()-this._startTime},e.prototype._now=function(){return this._highResolution?i.b.performance.now():(new Date).getTime()},e}()},KCUl:function(e,t,n){(function(t){var i=n("geuY"),r=n("lZ6o").ec,o=n("jkjm"),s=n("QDfD");function a(e,t){if(e.cmpn(0)<=0)throw new Error("invalid sig");if(e.cmp(t)>=t)throw new Error("invalid sig")}e.exports=function(e,n,u,c,l){var d=o(u);if("ec"===d.type){if("ecdsa"!==c&&"ecdsa/rsa"!==c)throw new Error("wrong public key type");return function(e,t,n){var i=s[n.data.algorithm.curve.join(".")];if(!i)throw new Error("unknown curve "+n.data.algorithm.curve.join("."));var o=new r(i),a=n.data.subjectPrivateKey.data;return o.verify(t,e,a)}(e,n,d)}if("dsa"===d.type){if("dsa"!==c)throw new Error("wrong public key type");return function(e,t,n){var r=n.data.p,s=n.data.q,u=n.data.g,c=n.data.pub_key,l=o.signature.decode(e,"der"),d=l.s,h=l.r;a(d,s),a(h,s);var f=i.mont(r),p=d.invm(s);return 0===u.toRed(f).redPow(new i(t).mul(p).mod(s)).fromRed().mul(c.toRed(f).redPow(h.mul(p).mod(s)).fromRed()).mod(r).mod(s).cmp(h)}(e,n,d)}if("rsa"!==c&&"ecdsa/rsa"!==c)throw new Error("wrong public key type");n=t.concat([l,n]);for(var h=d.modulus.byteLength(),f=[1],p=0;n.length+f.length+2>>27}function l(e){return e<<30|e>>>2}function d(e,t,n,i){return 0===e?t&n|~t&i:2===e?t&n|t&i|n&i:t^n^i}i(u,r),u.prototype.init=function(){return this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520,this},u.prototype._update=function(e){for(var t,n=this._w,i=0|this._a,r=0|this._b,o=0|this._c,a=0|this._d,u=0|this._e,h=0;h<16;++h)n[h]=e.readInt32BE(4*h);for(;h<80;++h)n[h]=(t=n[h-3]^n[h-8]^n[h-14]^n[h-16])<<1|t>>>31;for(var f=0;f<80;++f){var p=~~(f/20),g=c(i)+d(p,r,o,a)+u+n[f]+s[p]|0;u=a,a=o,o=l(r),r=i,i=g}this._a=i+this._a|0,this._b=r+this._b|0,this._c=o+this._c|0,this._d=a+this._d|0,this._e=u+this._e|0},u.prototype._hash=function(){var e=o.allocUnsafe(20);return e.writeInt32BE(0|this._a,0),e.writeInt32BE(0|this._b,4),e.writeInt32BE(0|this._c,8),e.writeInt32BE(0|this._d,12),e.writeInt32BE(0|this._e,16),e},e.exports=u},KU51:function(e,t){},KYqO:function(e,t){e.exports={name:"elliptic",version:"6.4.1",description:"EC cryptography",main:"lib/elliptic.js",files:["lib"],scripts:{jscs:"jscs benchmarks/*.js lib/*.js lib/**/*.js lib/**/**/*.js test/index.js",jshint:"jscs benchmarks/*.js lib/*.js lib/**/*.js lib/**/**/*.js test/index.js",lint:"npm run jscs && npm run jshint",unit:"istanbul test _mocha --reporter=spec test/index.js",test:"npm run lint && npm run unit",version:"grunt dist && git add dist/"},repository:{type:"git",url:"git@github.com:indutny/elliptic"},keywords:["EC","Elliptic","curve","Cryptography"],author:"Fedor Indutny ",license:"MIT",bugs:{url:"https://github.com/indutny/elliptic/issues"},homepage:"https://github.com/indutny/elliptic",devDependencies:{brfs:"^1.4.3",coveralls:"^2.11.3",grunt:"^0.4.5","grunt-browserify":"^5.0.0","grunt-cli":"^1.2.0","grunt-contrib-connect":"^1.0.0","grunt-contrib-copy":"^1.0.0","grunt-contrib-uglify":"^1.0.1","grunt-mocha-istanbul":"^3.0.1","grunt-saucelabs":"^8.6.2",istanbul:"^0.4.2",jscs:"^2.9.0",jshint:"^2.6.0",mocha:"^2.1.0"},dependencies:{"bn.js":"^4.4.0",brorand:"^1.0.1","hash.js":"^1.0.0","hmac-drbg":"^1.0.0",inherits:"^2.0.1","minimalistic-assert":"^1.0.0","minimalistic-crypto-utils":"^1.0.0"}}},"KeN/":function(e,t,n){(function(t){var i=n("BVsN"),r=n("9DG0"),o=n("LC74"),s=n("pn+s"),a=n("KCUl"),u=n("ejIc");function c(e){r.Writable.call(this);var t=u[e];if(!t)throw new Error("Unknown message digest");this._hashType=t.hash,this._hash=i(t.hash),this._tag=t.id,this._signType=t.sign}function l(e){r.Writable.call(this);var t=u[e];if(!t)throw new Error("Unknown message digest");this._hash=i(t.hash),this._tag=t.id,this._signType=t.sign}function d(e){return new c(e)}function h(e){return new l(e)}Object.keys(u).forEach(function(e){u[e].id=new t(u[e].id,"hex"),u[e.toLowerCase()]=u[e]}),o(c,r.Writable),c.prototype._write=function(e,t,n){this._hash.update(e),n()},c.prototype.update=function(e,n){return"string"==typeof e&&(e=new t(e,n)),this._hash.update(e),this},c.prototype.sign=function(e,t){this.end();var n=this._hash.digest(),i=s(n,e,this._hashType,this._signType,this._tag);return t?i.toString(t):i},o(l,r.Writable),l.prototype._write=function(e,t,n){this._hash.update(e),n()},l.prototype.update=function(e,n){return"string"==typeof e&&(e=new t(e,n)),this._hash.update(e),this},l.prototype.verify=function(e,n,i){"string"==typeof n&&(n=new t(n,i)),this.end();var r=this._hash.digest();return a(n,r,e,this._signType,this._tag)},e.exports={Sign:d,Verify:h,createSign:d,createVerify:h}}).call(t,n("EuP9").Buffer)},Kp7x:function(e,t,n){"use strict";n.d(t,"b",function(){return r}),n.d(t,"a",function(){return h}),n.d(t,"e",function(){return f}),n.d(t,"d",function(){return p}),n.d(t,"c",function(){return g}),n.d(t,"f",function(){return m});var i,r,o=n("zxiH"),s=n("dwjm"),a=n("tqet"),u=n("EMhq"),c=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)});!function(e){function t(e){return function(t,n,i){void 0===n&&(n=null);var r,o=!1;return r=e(function(e){if(!o)return r?r.dispose():o=!0,t.call(n,e)},null,i),o&&r.dispose(),r}}function n(e,t){return s(function(n,i,r){return void 0===i&&(i=null),e(function(e){return n.call(i,t(e))},null,r)})}function i(e,t){return s(function(n,i,r){return void 0===i&&(i=null),e(function(e){t(e),n.call(i,e)},null,r)})}function r(e,t){return s(function(n,i,r){return void 0===i&&(i=null),e(function(e){return t(e)&&n.call(i,e)},null,r)})}function o(e,t,i){var r=i;return n(e,function(e){return r=t(r,e)})}function s(e){var t,n=new h({onFirstListenerAdd:function(){t=e(n.fire,n)},onLastListenerRemove:function(){t.dispose()}});return n.event}function u(e){var t,n=!0;return r(e,function(e){var i=n||e!==t;return n=!1,t=e,i})}e.None=function(){return a.a.None},e.once=t,e.map=n,e.forEach=i,e.filter=r,e.signal=function(e){return e},e.any=function(){for(var e=[],t=0;t1)&&c.fire(e),u=0},n)})},onLastListenerRemove:function(){o.dispose()}});return c.event},e.stopwatch=function(e){var i=(new Date).getTime();return n(t(e),function(e){return(new Date).getTime()-i})},e.latch=u,e.buffer=function(e,t,n){void 0===t&&(t=!1),void 0===n&&(n=[]);var i=n.slice(),r=e(function(e){i?i.push(e):s.fire(e)}),o=function(){i&&i.forEach(function(e){return s.fire(e)}),i=null},s=new h({onFirstListenerAdd:function(){r||(r=e(function(e){return s.fire(e)}))},onFirstListenerDidAdd:function(){i&&(t?setTimeout(o):o())},onLastListenerRemove:function(){r&&r.dispose(),r=null}});return s.event};var c=function(){function e(e){this.event=e}return e.prototype.map=function(t){return new e(n(this.event,t))},e.prototype.forEach=function(t){return new e(i(this.event,t))},e.prototype.filter=function(t){return new e(r(this.event,t))},e.prototype.reduce=function(t,n){return new e(o(this.event,t,n))},e.prototype.latch=function(){return new e(u(this.event))},e.prototype.on=function(e,t,n){return this.event(e,t,n)},e.prototype.once=function(e,n,i){return t(this.event)(e,n,i)},e}();e.chain=function(e){return new c(e)},e.fromNodeEventEmitter=function(e,t,n){void 0===n&&(n=function(e){return e});var i=function(){for(var e=[],t=0;t0?new d(this._options&&this._options.leakWarningThreshold):void 0}return Object.defineProperty(e.prototype,"event",{get:function(){var t=this;return this._event||(this._event=function(n,i,r){t._listeners||(t._listeners=new u.a);var o=t._listeners.isEmpty();o&&t._options&&t._options.onFirstListenerAdd&&t._options.onFirstListenerAdd(t);var s,c,l=t._listeners.push(i?[n,i]:n);return o&&t._options&&t._options.onFirstListenerDidAdd&&t._options.onFirstListenerDidAdd(t),t._options&&t._options.onListenerDidAdd&&t._options.onListenerDidAdd(t,n,i),t._leakageMon&&(s=t._leakageMon.check(t._listeners.size)),c={dispose:function(){(s&&s(),c.dispose=e._noop,t._disposed)||(l(),t._options&&t._options.onLastListenerRemove&&(t._listeners&&!t._listeners.isEmpty()||t._options.onLastListenerRemove(t)))}},r instanceof a.b?r.add(c):Array.isArray(r)&&r.push(c),c}),this._event},enumerable:!0,configurable:!0}),e.prototype.fire=function(e){if(this._listeners){this._deliveryQueue||(this._deliveryQueue=new u.a);for(var t=this._listeners.iterator(),n=t.next();!n.done;n=t.next())this._deliveryQueue.push([n.value,e]);for(;this._deliveryQueue.size>0;){var i=this._deliveryQueue.shift(),r=i[0],s=i[1];try{"function"==typeof r?r.call(void 0,s):r[0].call(r[1],s)}catch(n){Object(o.e)(n)}}}},e.prototype.dispose=function(){this._listeners&&this._listeners.clear(),this._deliveryQueue&&this._deliveryQueue.clear(),this._leakageMon&&this._leakageMon.dispose(),this._disposed=!0},e._noop=function(){},e}(),f=function(e){function t(t){var n=e.call(this,t)||this;return n._isPaused=0,n._eventQueue=new u.a,n._mergeFn=t&&t.merge,n}return c(t,e),t.prototype.pause=function(){this._isPaused++},t.prototype.resume=function(){if(0!==this._isPaused&&0==--this._isPaused)if(this._mergeFn){var t=this._eventQueue.toArray();this._eventQueue.clear(),e.prototype.fire.call(this,this._mergeFn(t))}else for(;!this._isPaused&&0!==this._eventQueue.size;)e.prototype.fire.call(this,this._eventQueue.shift())},t.prototype.fire=function(t){this._listeners&&(0!==this._isPaused?this._eventQueue.push(t):e.prototype.fire.call(this,t))},t}(h),p=function(){function e(){var e=this;this.hasListeners=!1,this.events=[],this.emitter=new h({onFirstListenerAdd:function(){return e.onFirstListenerAdd()},onLastListenerRemove:function(){return e.onLastListenerRemove()}})}return Object.defineProperty(e.prototype,"event",{get:function(){return this.emitter.event},enumerable:!0,configurable:!0}),e.prototype.add=function(e){var t=this,n={event:e,listener:null};this.events.push(n),this.hasListeners&&this.hook(n);return Object(a.h)(Object(s.a)(function(){t.hasListeners&&t.unhook(n);var e=t.events.indexOf(n);t.events.splice(e,1)}))},e.prototype.onFirstListenerAdd=function(){var e=this;this.hasListeners=!0,this.events.forEach(function(t){return e.hook(t)})},e.prototype.onLastListenerRemove=function(){var e=this;this.hasListeners=!1,this.events.forEach(function(t){return e.unhook(t)})},e.prototype.hook=function(e){var t=this;e.listener=e.event(function(e){return t.emitter.fire(e)})},e.prototype.unhook=function(e){e.listener&&e.listener.dispose(),e.listener=null},e.prototype.dispose=function(){this.emitter.dispose()},e}(),g=function(){function e(){this.buffers=[]}return e.prototype.wrapEvent=function(e){var t=this;return function(n,i,r){return e(function(e){var r=t.buffers[t.buffers.length-1];r?r.push(function(){return n.call(i,e)}):n.call(i,e)},void 0,r)}},e.prototype.bufferEvents=function(e){var t=[];this.buffers.push(t);var n=e();return this.buffers.pop(),t.forEach(function(e){return e()}),n},e}(),m=function(){function e(){var e=this;this.listening=!1,this.inputEvent=r.None,this.inputEventListener=a.a.None,this.emitter=new h({onFirstListenerDidAdd:function(){e.listening=!0,e.inputEventListener=e.inputEvent(e.emitter.fire,e.emitter)},onLastListenerRemove:function(){e.listening=!1,e.inputEventListener.dispose()}}),this.event=this.emitter.event}return Object.defineProperty(e.prototype,"input",{set:function(e){this.inputEvent=e,this.listening&&(this.inputEventListener.dispose(),this.inputEventListener=e(this.emitter.fire,this.emitter))},enumerable:!0,configurable:!0}),e.prototype.dispose=function(){this.inputEventListener.dispose(),this.emitter.dispose()},e}()},Kx4b:function(e,t,n){"use strict";n.d(t,"a",function(){return a});var i=n("uNfg"),r=n("ZfGv"),o=n("ItKl"),s=n("RWr8"),a=new(function(){function e(){this._coreKeybindings=[],this._extensionKeybindings=[],this._cachedMergedKeybindings=null}return e.bindToCurrentPlatform=function(e){if(1===r.a){if(e&&e.win)return e.win}else if(2===r.a){if(e&&e.mac)return e.mac}else if(e&&e.linux)return e.linux;return e},e.prototype.registerKeybindingRule=function(t){var n=e.bindToCurrentPlatform(t);n&&n.primary&&((a=Object(i.f)(n.primary,r.a))&&this._registerDefaultKeybinding(a,t.id,void 0,t.weight,0,t.when));if(n&&Array.isArray(n.secondary))for(var o=0,s=n.secondary.length;o=21&&e<=30||(e>=31&&e<=56||(80===e||81===e||82===e||83===e||84===e||85===e||86===e||110===e||111===e||87===e||88===e||89===e||90===e||91===e||92===e))},e.prototype._assertNoCtrlAlt=function(t,n){t.ctrlKey&&t.altKey&&!t.metaKey&&e._mightProduceChar(t.keyCode)&&console.warn("Ctrl+Alt+ keybindings should not be used by default under Windows. Offender: ",t," for ",n)},e.prototype._registerDefaultKeybinding=function(e,t,n,i,o,s){1===r.a&&this._assertNoCtrlAlt(e.parts[0],t),this._coreKeybindings.push({keybinding:e,command:t,commandArgs:n,when:s,weight1:i,weight2:o}),this._cachedMergedKeybindings=null},e.prototype.getDefaultKeybindings=function(){return this._cachedMergedKeybindings||(this._cachedMergedKeybindings=[].concat(this._coreKeybindings).concat(this._extensionKeybindings),this._cachedMergedKeybindings.sort(u)),this._cachedMergedKeybindings.slice(0)},e}());function u(e,t){return e.weight1!==t.weight1?e.weight1-t.weight1:e.commandt.command?1:e.weight2-t.weight2}s.a.add("platform.keybindingsRegistry",a)},L5KM:function(e,t,n){"use strict";n.d(t,"a",function(){return c}),t._36=d,n.d(t,"T",function(){return p}),n.d(t,"R",function(){return g}),n.d(t,"S",function(){return m}),n.d(t,"e",function(){return v}),n.d(t,"b",function(){return _}),n.d(t,"_46",function(){return b}),n.d(t,"_45",function(){return y}),n.d(t,"_48",function(){return w}),n.d(t,"W",function(){return C}),n.d(t,"Y",function(){return S}),n.d(t,"X",function(){return x}),n.d(t,"V",function(){return L}),n.d(t,"U",function(){return O}),n.d(t,"_2",function(){return k}),n.d(t,"_4",function(){return N}),n.d(t,"_3",function(){return E}),n.d(t,"_5",function(){return I}),n.d(t,"_7",function(){return D}),n.d(t,"_6",function(){return M}),n.d(t,"Z",function(){return T}),n.d(t,"_1",function(){return P}),n.d(t,"_0",function(){return A}),n.d(t,"_14",function(){return j}),n.d(t,"_15",function(){return W}),n.d(t,"_8",function(){return B}),n.d(t,"_9",function(){return V}),n.d(t,"_20",function(){return H}),n.d(t,"_21",function(){return z}),n.d(t,"_19",function(){return U}),n.d(t,"_17",function(){return K}),n.d(t,"_18",function(){return q}),n.d(t,"_10",function(){return G}),n.d(t,"_16",function(){return Z}),n.d(t,"_11",function(){return Y}),n.d(t,"_13",function(){return X}),n.d(t,"_12",function(){return $}),n.d(t,"_47",function(){return J}),n.d(t,"_34",function(){return Q}),n.d(t,"_33",function(){return ee}),n.d(t,"c",function(){return te}),n.d(t,"d",function(){return ne}),n.d(t,"_37",function(){return ie}),n.d(t,"_39",function(){return re}),n.d(t,"_40",function(){return oe}),n.d(t,"_38",function(){return se}),n.d(t,"_35",function(){return ae}),n.d(t,"_23",function(){return ue}),n.d(t,"_24",function(){return ce}),n.d(t,"_22",function(){return le}),n.d(t,"_27",function(){return de}),n.d(t,"_25",function(){return he}),n.d(t,"_26",function(){return fe}),n.d(t,"_28",function(){return pe}),n.d(t,"q",function(){return ge}),n.d(t,"p",function(){return me}),n.d(t,"M",function(){return ve}),n.d(t,"L",function(){return _e}),n.d(t,"G",function(){return be}),n.d(t,"F",function(){return ye}),n.d(t,"z",function(){return we}),n.d(t,"y",function(){return Ce}),n.d(t,"o",function(){return Se}),n.d(t,"x",function(){return xe}),n.d(t,"N",function(){return Le}),n.d(t,"P",function(){return Oe}),n.d(t,"O",function(){return ke}),n.d(t,"Q",function(){return Ne}),n.d(t,"H",function(){return Ee}),n.d(t,"I",function(){return Ie}),n.d(t,"E",function(){return De}),n.d(t,"J",function(){return Me}),n.d(t,"K",function(){return Te}),n.d(t,"r",function(){return Pe}),n.d(t,"t",function(){return Ae}),n.d(t,"v",function(){return Re}),n.d(t,"s",function(){return Fe}),n.d(t,"u",function(){return je}),n.d(t,"w",function(){return We}),n.d(t,"C",function(){return Be}),n.d(t,"A",function(){return Ve}),n.d(t,"B",function(){return He}),n.d(t,"D",function(){return ze}),n.d(t,"n",function(){return Ue}),n.d(t,"g",function(){return Ke}),n.d(t,"h",function(){return qe}),n.d(t,"j",function(){return Ge}),n.d(t,"l",function(){return Ze}),n.d(t,"k",function(){return Ye}),n.d(t,"m",function(){return Xe}),n.d(t,"i",function(){return $e}),n.d(t,"_43",function(){return Je}),n.d(t,"_44",function(){return Qe}),n.d(t,"_41",function(){return et}),n.d(t,"_42",function(){return tt}),n.d(t,"_31",function(){return nt}),n.d(t,"_32",function(){return it}),n.d(t,"_29",function(){return rt}),t.f=ot,t._30=function(){for(var e=[],t=0;te.length)return!1;for(var r=0;r=65&&o<=90&&o+32===s||s>=65&&s<=90&&s+32===o))return!1}return!0},e.prototype._createOperationsForBlockComment=function(t,n,i,r,o){var s,a=t.startLineNumber,u=t.startColumn,c=t.endLineNumber,d=t.endColumn,h=r.getLineContent(a),f=r.getLineContent(c),p=h.lastIndexOf(n,u-1+n.length),g=f.indexOf(i,d-1-i.length);if(-1!==p&&-1!==g)if(a===c){h.substring(p+n.length,g).indexOf(i)>=0&&(p=-1,g=-1)}else{var m=h.substring(p+n.length),v=f.substring(0,g);(m.indexOf(i)>=0||v.indexOf(i)>=0)&&(p=-1,g=-1)}-1!==p&&-1!==g?(p+n.length0&&32===f.charCodeAt(g-1)&&(i=" "+i,g-=1),s=e._createRemoveBlockCommentOperations(new l.a(a,p+n.length+1,c,g+1),n,i)):(s=e._createAddBlockCommentOperations(t,n,i),this._usedEndToken=1===s.length?i:null);for(var _=0,b=s;_a?o-1:o}},e}(),m=this&&this.__extends||(i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),v=function(e){function t(t,n){var i=e.call(this,n)||this;return i._type=t,i}return m(t,e),t.prototype.run=function(e,t){if(t.hasModel()){for(var n=t.getModel(),i=[],r=t.getSelections(),o=n.getOptions(),s=0,a=r;s0&&"#"===n.charAt(n.length-1)?n.substring(0,n.length-1):n)]=t,this._onDidChangeSchema.fire(e)},e.prototype.notifySchemaChanged=function(e){this._onDidChangeSchema.fire(e)},e}());i.a.add(o.JSONContribution,s)},LYGd:function(e,t,n){"use strict";var i=n("EuP9").Buffer,r=n("LC74"),o=n("yDvu"),s=new Array(16),a=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13],u=[5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11],c=[11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6],l=[8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11],d=[0,1518500249,1859775393,2400959708,2840853838],h=[1352829926,1548603684,1836072691,2053994217,0];function f(){o.call(this,64),this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520}function p(e,t){return e<>>32-t}function g(e,t,n,i,r,o,s,a){return p(e+(t^n^i)+o+s|0,a)+r|0}function m(e,t,n,i,r,o,s,a){return p(e+(t&n|~t&i)+o+s|0,a)+r|0}function v(e,t,n,i,r,o,s,a){return p(e+((t|~n)^i)+o+s|0,a)+r|0}function _(e,t,n,i,r,o,s,a){return p(e+(t&i|n&~i)+o+s|0,a)+r|0}function b(e,t,n,i,r,o,s,a){return p(e+(t^(n|~i))+o+s|0,a)+r|0}r(f,o),f.prototype._update=function(){for(var e=s,t=0;t<16;++t)e[t]=this._block.readInt32LE(4*t);for(var n=0|this._a,i=0|this._b,r=0|this._c,o=0|this._d,f=0|this._e,y=0|this._a,w=0|this._b,C=0|this._c,S=0|this._d,x=0|this._e,L=0;L<80;L+=1){var O,k;L<16?(O=g(n,i,r,o,f,e[a[L]],d[0],c[L]),k=b(y,w,C,S,x,e[u[L]],h[0],l[L])):L<32?(O=m(n,i,r,o,f,e[a[L]],d[1],c[L]),k=_(y,w,C,S,x,e[u[L]],h[1],l[L])):L<48?(O=v(n,i,r,o,f,e[a[L]],d[2],c[L]),k=v(y,w,C,S,x,e[u[L]],h[2],l[L])):L<64?(O=_(n,i,r,o,f,e[a[L]],d[3],c[L]),k=m(y,w,C,S,x,e[u[L]],h[3],l[L])):(O=b(n,i,r,o,f,e[a[L]],d[4],c[L]),k=g(y,w,C,S,x,e[u[L]],h[4],l[L])),n=f,f=o,o=p(r,10),r=i,i=O,y=x,x=S,S=p(C,10),C=w,w=k}var N=this._b+r+S|0;this._b=this._c+o+x|0,this._c=this._d+f+y|0,this._d=this._e+n+w|0,this._e=this._a+i+C|0,this._a=N},f.prototype._digest=function(){this._block[this._blockOffset++]=128,this._blockOffset>56&&(this._block.fill(0,this._blockOffset,64),this._update(),this._blockOffset=0),this._block.fill(0,this._blockOffset,56),this._block.writeUInt32LE(this._length[0],56),this._block.writeUInt32LE(this._length[1],60),this._update();var e=i.alloc?i.alloc(20):new i(20);return e.writeInt32LE(this._a,0),e.writeInt32LE(this._b,4),e.writeInt32LE(this._c,8),e.writeInt32LE(this._d,12),e.writeInt32LE(this._e,16),e},e.exports=f},MOjS:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i,r=n("hK2W"),o=n("Kp7x"),s=n("tqet"),a=n("7g0X"),u=n("OHx0"),c=n("vTy2"),l=n("03Zz"),d=n("eoic"),h=n("/9db"),f=(n("OV+G"),n("7/Cv")),p=n("L5KM"),g=n("TNPA"),m=n("qecS"),v=n("1sup"),_=n("X6iQ"),b=n("fw2Z"),y=n("ZYUE"),w=n("ni2T"),C=n("Nr0y"),S=encodeURIComponent(''),L=encodeURIComponent(''),k=encodeURIComponent(''),E=encodeURIComponent(''),D=encodeURIComponent(''),T=encodeURIComponent('');function A(e,t){return"."+i.className(e).split(" ").join(".")+' { background: url("data:image/svg+xml,'+i.getSVGData(e,t)+'") center center no-repeat; height: 16px; width: 16px; }'}!function(e){e.getSVGData=function(e,t){switch(e){case C.a.Ignore:var n=t.type===d.d?g.a.fromHex("#75BEFF"):g.a.fromHex("#007ACC");return t.type===d.d?D+encodeURIComponent(n.toString())+M:T+encodeURIComponent(n.toString())+P;case C.a.Info:var i=t.type===d.d?g.a.fromHex("#007ACC"):g.a.fromHex("#75BEFF");return t.type===d.d?D+encodeURIComponent(i.toString())+M:T+encodeURIComponent(i.toString())+P;case C.a.Warning:var r=t.type===d.d?g.a.fromHex("#DDB100"):g.a.fromHex("#fc0");return t.type===d.d?k+encodeURIComponent(r.toString())+N:E+encodeURIComponent(r.toString())+I;case C.a.Error:var o=t.type===d.d?g.a.fromHex("#A1260D"):g.a.fromHex("#F48771");return t.type===d.d?S+encodeURIComponent(o.toString())+x:L+encodeURIComponent(o.toString())+O}return""},e.className=function(e){switch(e){case C.a.Ignore:return"severity-icon severity-ignore";case C.a.Info:return"severity-icon severity-info";case C.a.Warning:return"severity-icon severity-warning";case C.a.Error:return"severity-icon severity-error"}return""}}(i||(i={})),Object(d.f)(function(e,t){t.addRule(A(C.a.Error,e)),t.addRule(A(C.a.Warning,e)),t.addRule(A(C.a.Info,e)),t.addRule(A(C.a.Ignore,e))});var R,F=this&&this.__extends||(R=function(e,t){return(R=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}R(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),j=function(){function e(e,t,n){var i=this;this._lines=0,this._longestLineLength=0,this._relatedDiagnostics=new WeakMap,this._disposables=[],this._editor=t;var r=document.createElement("div");r.className="descriptioncontainer",r.setAttribute("aria-live","assertive"),r.setAttribute("role","alert"),this._messageBlock=document.createElement("div"),f.f(this._messageBlock,"message"),r.appendChild(this._messageBlock),this._relatedBlock=document.createElement("div"),r.appendChild(this._relatedBlock),this._disposables.push(f.k(this._relatedBlock,"click",function(e){e.preventDefault();var t=i._relatedDiagnostics.get(e.target);t&&n(t)})),this._scrollable=new m.b(r,{horizontal:1,vertical:1,useShadows:!1,horizontalScrollbarSize:3,verticalScrollbarSize:3}),e.appendChild(this._scrollable.getDomNode()),this._disposables.push(this._scrollable.onScroll(function(e){r.style.left="-"+e.scrollLeft+"px",r.style.top="-"+e.scrollTop+"px"})),this._disposables.push(this._scrollable)}return e.prototype.dispose=function(){Object(s.f)(this._disposables)},e.prototype.update=function(e){var t=e.source,n=e.message,i=e.relatedInformation,r=e.code,o=n.split(/\r\n|\r|\n/g);this._lines=o.length,this._longestLineLength=0;for(var s=0,a=o;s1?r.a("problems","{0} of {1} problems",n,o):r.a("change","{0} of {1} problem",n,o);this.setTitle(Object(y.b)(d.uri),h)}this._icon.className=i.className(u.c.toSeverity(this._severity)),this.editor.revealPositionInCenter(l,0)},t.prototype.updateMarker=function(e){this._container.classList.remove("stale"),this._message.update(e)},t.prototype.showStale=function(){this._container.classList.add("stale"),this._relayout()},t.prototype._doLayoutBody=function(t,n){e.prototype._doLayoutBody.call(this,t,n),this._heightInPixel=t,this._message.layout(t,n),this._container.style.height=t+"px"},t.prototype._onWidth=function(e){this._message.layout(this._heightInPixel,e)},t.prototype._relayout=function(){e.prototype._relayout.call(this,this.computeRequiredHeight())},t.prototype.computeRequiredHeight=function(){return 3+this._message.getHeightInLines()},t}(b.c),B=Object(p._30)(p.q,p.p),V=Object(p._30)(p.M,p.L),H=Object(p._30)(p.G,p.F),z=Object(p._36)("editorMarkerNavigationError.background",{dark:B,light:B,hc:B},r.a("editorMarkerNavigationError","Editor marker navigation widget error color.")),U=Object(p._36)("editorMarkerNavigationWarning.background",{dark:V,light:V,hc:V},r.a("editorMarkerNavigationWarning","Editor marker navigation widget warning color.")),K=Object(p._36)("editorMarkerNavigationInfo.background",{dark:H,light:H,hc:H},r.a("editorMarkerNavigationInfo","Editor marker navigation widget info color.")),q=Object(p._36)("editorMarkerNavigation.background",{dark:"#2D2D30",light:g.a.white,hc:"#0C141F"},r.a("editorMarkerNavigationBackground","Editor marker navigation widget background."));Object(d.f)(function(e,t){var n=e.getColor(p._46);n&&t.addRule(".monaco-editor .marker-widget a { color: "+n+"; }")});var G=n("aL7J"),Z=n("vORD"),Y=n("zxiH"),X=n("C3c5"),$=n("AKCZ"),J=n("NqM+");n.d(t,"MarkerController",function(){return oe}),n.d(t,"NextMarkerAction",function(){return ae});var Q=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),ee=this&&this.__decorate||function(e,t,n,i){var r,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,i);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},te=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},ne=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(r,o){function s(e){try{u(i.next(e))}catch(e){o(e)}}function a(e){try{u(i.throw(e))}catch(e){o(e)}}function u(e){e.done?r(e.value):new n(function(t){t(e.value)}).then(s,a)}u((i=i.apply(e,t||[])).next())})},ie=this&&this.__generator||function(e,t){var n,i,r,o,s={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,i&&(r=2&o[0]?i.return:o[0]?i.throw||((r=i.return)&&r.call(i),0):i.next)&&!(r=r.call(i,o[1])).done)return r;switch(i=0,r&&(o=[2&o[0],r.value]),o[0]){case 0:case 1:r=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,i=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(r=(r=s.trys).length>0&&r[r.length-1])&&(6===o[0]||2===o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]=0?this._markers[this._nextIdx]:void 0;this._markers=e||[],this._markers.sort(se.compareMarker),this._nextIdx=t?Math.max(-1,Object(_.c)(this._markers,t,se.compareMarker)):-1,this._onMarkerSetChanged.fire(this)},e.prototype.withoutWatchingEditorPosition=function(e){this._ignoreSelectionChange=!0;try{e()}finally{this._ignoreSelectionChange=!1}},e.prototype._initIdx=function(e){for(var t=!1,n=this._editor.getPosition(),i=0;i0?this._nextIdx=(this._nextIdx-1+this._markers.length)%this._markers.length:i=!0),n!==this._nextIdx){var r=this._markers[this._nextIdx];this._onCurrentMarkerChanged.fire(r)}return i},e.prototype.canNavigate=function(){return this._markers.length>0},e.prototype.findMarkerAtPosition=function(e){for(var t=0,n=this._markers;t=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},y=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},w=function(e){function t(t,n,i){var r=e.call(this)||this;return r._editor=t,r._codeEditorService=n,r._configurationService=i,r._localToDispose=r._register(new c.b),r._decorationsIds=[],r._colorDatas=new Map,r._colorDecoratorIds=[],r._decorationsTypes=new Set,r._register(t.onDidChangeModel(function(e){r._isEnabled=r.isEnabled(),r.onModelChanged()})),r._register(t.onDidChangeModelLanguage(function(e){return r.onModelChanged()})),r._register(p.c.onDidChange(function(e){return r.onModelChanged()})),r._register(t.onDidChangeConfiguration(function(e){var t=r._isEnabled;r._isEnabled=r.isEnabled(),t!==r._isEnabled&&(r._isEnabled?r.onModelChanged():r.removeAllDecorations())})),r._timeoutTimer=null,r._computePromise=null,r._isEnabled=r.isEnabled(),r.onModelChanged(),r}return _(t,e),t.prototype.isEnabled=function(){var e=this._editor.getModel();if(!e)return!1;var t=e.getLanguageIdentifier(),n=this._configurationService.getValue(t.language);if(n){var i=n.colorDecorators;if(i&&void 0!==i.enable&&!i.enable)return i.enable}return this._editor.getConfiguration().contribInfo.colorDecorators},t.prototype.getId=function(){return t.ID},t.get=function(e){return e.getContribution(this.ID)},t.prototype.dispose=function(){this.stop(),this.removeAllDecorations(),e.prototype.dispose.call(this)},t.prototype.onModelChanged=function(){var e=this;if(this.stop(),this._isEnabled){var n=this._editor.getModel();n&&p.c.has(n)&&(this._localToDispose.add(this._editor.onDidChangeModelContent(function(n){e._timeoutTimer||(e._timeoutTimer=new i.e,e._timeoutTimer.cancelAndSet(function(){e._timeoutTimer=null,e.beginCompute()},t.RECOMPUTE_TIME))})),this.beginCompute())}},t.prototype.beginCompute=function(){var e=this;this._computePromise=Object(i.f)(function(t){var n=e._editor.getModel();return n?Object(g.b)(n,t):Promise.resolve([])}),this._computePromise.then(function(t){e.updateDecorations(t),e.updateColorDecorators(t),e._computePromise=null},o.e)},t.prototype.stop=function(){this._timeoutTimer&&(this._timeoutTimer.cancel(),this._timeoutTimer=null),this._computePromise&&(this._computePromise.cancel(),this._computePromise=null),this._localToDispose.clear()},t.prototype.updateDecorations=function(e){var t=this,n=e.map(function(e){return{range:{startLineNumber:e.colorInfo.range.startLineNumber,startColumn:e.colorInfo.range.startColumn,endLineNumber:e.colorInfo.range.endLineNumber,endColumn:e.colorInfo.range.endColumn},options:f.a.EMPTY}});this._decorationsIds=this._editor.deltaDecorations(this._decorationsIds,n),this._colorDatas=new Map,this._decorationsIds.forEach(function(n,i){return t._colorDatas.set(n,e[i])})},t.prototype.updateColorDecorators=function(e){for(var t=this,n=[],i={},o=0;o>>2}function l(e,t,n,i){return 0===e?t&n|~t&i:2===e?t&n|t&i|n&i:t^n^i}i(u,r),u.prototype.init=function(){return this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520,this},u.prototype._update=function(e){for(var t,n=this._w,i=0|this._a,r=0|this._b,o=0|this._c,a=0|this._d,u=0|this._e,d=0;d<16;++d)n[d]=e.readInt32BE(4*d);for(;d<80;++d)n[d]=n[d-3]^n[d-8]^n[d-14]^n[d-16];for(var h=0;h<80;++h){var f=~~(h/20),p=0|((t=i)<<5|t>>>27)+l(f,r,o,a)+u+n[h]+s[f];u=a,a=o,o=c(r),r=i,i=p}this._a=i+this._a|0,this._b=r+this._b|0,this._c=o+this._c|0,this._d=a+this._d|0,this._e=u+this._e|0},u.prototype._hash=function(){var e=o.allocUnsafe(20);return e.writeInt32BE(0|this._a,0),e.writeInt32BE(0|this._b,4),e.writeInt32BE(0|this._c,8),e.writeInt32BE(0|this._d,12),e.writeInt32BE(0|this._e,16),e},e.exports=u},NBYJ:function(e,t){},NCTB:function(e,t,n){"use strict";t.sha1=n("bMQ9"),t.sha224=n("fWB8"),t.sha256=n("Q48P"),t.sha384=n("EH7o"),t.sha512=n("8/0b")},NMED:function(e,t,n){"use strict";var i=n("geuY"),r=n("lZ6o").utils,o=r.assert;function s(e,t){if(e instanceof s)return e;this._importDER(e,t)||(o(e.r&&e.s,"Signature without r or s"),this.r=new i(e.r,16),this.s=new i(e.s,16),void 0===e.recoveryParam?this.recoveryParam=null:this.recoveryParam=e.recoveryParam)}function a(e,t){var n=e[t.place++];if(!(128&n))return n;for(var i=15&n,r=0,o=0,s=t.place;o>>3);for(e.push(128|n);--n;)e.push(t>>>(n<<3)&255);e.push(t)}}e.exports=s,s.prototype._importDER=function(e,t){e=r.toArray(e,t);var n=new function(){this.place=0};if(48!==e[n.place++])return!1;if(a(e,n)+n.place!==e.length)return!1;if(2!==e[n.place++])return!1;var o=a(e,n),s=e.slice(n.place,o+n.place);if(n.place+=o,2!==e[n.place++])return!1;var u=a(e,n);if(e.length!==u+n.place)return!1;var c=e.slice(n.place,u+n.place);return 0===s[0]&&128&s[1]&&(s=s.slice(1)),0===c[0]&&128&c[1]&&(c=c.slice(1)),this.r=new i(s),this.s=new i(c),this.recoveryParam=null,!0},s.prototype.toDER=function(e){var t=this.r.toArray(),n=this.s.toArray();for(128&t[0]&&(t=[0].concat(t)),128&n[0]&&(n=[0].concat(n)),t=u(t),n=u(n);!(n[0]||128&n[1]);)n=n.slice(1);var i=[2];c(i,t.length),(i=i.concat(t)).push(2),c(i,n.length);var o=i.concat(n),s=[48];return c(s,o.length),s=s.concat(o),r.encode(s,e)}},NjkW:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});n("WUwp");var i=n("tqet"),r=n("ZfGv"),o=n("03Zz"),s=n("artP"),a=n("vTy2"),u=n("iHM7"),c=function(){function e(e,t,n){this.selection=e,this.targetPosition=t,this.copy=n,this.targetSelection=null}return e.prototype.getEditOperations=function(e,t){var n=e.getValueInRange(this.selection);this.copy||t.addEditOperation(this.selection,null),t.addEditOperation(new a.a(this.targetPosition.lineNumber,this.targetPosition.column,this.targetPosition.lineNumber,this.targetPosition.column),n),!this.selection.containsPosition(this.targetPosition)||this.copy&&(this.selection.getEndPosition().equals(this.targetPosition)||this.selection.getStartPosition().equals(this.targetPosition))?this.copy?this.targetSelection=new u.a(this.targetPosition.lineNumber,this.targetPosition.column,this.selection.endLineNumber-this.selection.startLineNumber+this.targetPosition.lineNumber,this.selection.startLineNumber===this.selection.endLineNumber?this.targetPosition.column+this.selection.endColumn-this.selection.startColumn:this.selection.endColumn):this.targetPosition.lineNumber>this.selection.endLineNumber?this.targetSelection=new u.a(this.targetPosition.lineNumber-this.selection.endLineNumber+this.selection.startLineNumber,this.targetPosition.column,this.targetPosition.lineNumber,this.selection.startLineNumber===this.selection.endLineNumber?this.targetPosition.column+this.selection.endColumn-this.selection.startColumn:this.selection.endColumn):this.targetPosition.lineNumber0&&r[r.length-1])&&(6===o[0]||2===o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]=e._maxRounds){t();break}if(!r){t();break}var c=i.findNextBracket(r);if(!c){t();break}if(Date.now()-u>e._maxDuration){setTimeout(function(){return e._bracketsRightYield(t,n+1,i,r,s)});break}var l=c.close;if(c.isOpen){var d=a.has(l)?a.get(l):0;a.set(l,d+1)}else{d=a.has(l)?a.get(l):0;if(d-=1,a.set(l,Math.max(0,d)),d<0){var h=s.get(l);h||(h=new o.a,s.set(l,h)),h.push(c.range)}}r=c.range.getEndPosition()}},e._bracketsLeftYield=function(t,n,i,o,s,a){for(var u=new Map,c=Date.now();;){if(n>=e._maxRounds&&0===s.size){t();break}if(!o){t();break}var l=i.findPrevBracket(o);if(!l){t();break}if(Date.now()-c>e._maxDuration){setTimeout(function(){return e._bracketsLeftYield(t,n+1,i,o,s,a)});break}var d=l.close;if(l.isOpen){m=u.has(d)?u.get(d):0;if(m-=1,u.set(d,Math.max(0,m)),m<0){var h=s.get(d);if(h){var f=h.shift();0===h.size&&s.delete(d);var p=r.a.fromPositions(l.range.getEndPosition(),f.getStartPosition()),g=r.a.fromPositions(l.range.getStartPosition(),f.getEndPosition());a.push({range:p}),a.push({range:g}),e._addBracketLeading(i,g,a)}}}else{var m=u.has(d)?u.get(d):0;u.set(d,m+1)}o=l.range.getStartPosition()}},e._addBracketLeading=function(e,t,n){if(t.startLineNumber!==t.endLineNumber){var o=t.startLineNumber,s=e.getLineFirstNonWhitespaceColumn(o);0!==s&&s!==t.startColumn&&(n.push({range:r.a.fromPositions(new i.a(o,s),t.getEndPosition())}),n.push({range:r.a.fromPositions(new i.a(o,1),t.getEndPosition())}));var a=o-1;if(a>0){var u=e.getLineFirstNonWhitespaceColumn(a);u===t.startColumn&&u!==e.getLineLastNonWhitespaceColumn(a)&&(n.push({range:r.a.fromPositions(new i.a(a,u),t.getEndPosition())}),n.push({range:r.a.fromPositions(new i.a(a,1),t.getEndPosition())}))}}},e._maxDuration=30,e._maxRounds=2,e}()},"NqM+":function(e,t,n){"use strict";n.d(t,"a",function(){return r});var i=n("JVO/"),r=Object(i.c)("keybindingService")},Nr0y:function(e,t,n){"use strict";var i,r=n("hK2W"),o=n("aL7J");!function(e){e[e.Ignore=0]="Ignore",e[e.Info=1]="Info",e[e.Warning=2]="Warning",e[e.Error=3]="Error"}(i||(i={})),function(e){var t="error",n="warning",i="warn",s="info",a=Object.create(null);a[e.Error]=r.a("sev.error","Error"),a[e.Warning]=r.a("sev.warning","Warning"),a[e.Info]=r.a("sev.info","Info"),e.fromValue=function(r){return r?o.n(t,r)?e.Error:o.n(n,r)||o.n(i,r)?e.Warning:o.n(s,r)?e.Info:e.Ignore:e.Ignore}}(i||(i={})),t.a=i},Ny4g:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i,r,o,s,a,u,c,l,d,h,f,p,g,m,v,_,b,y,w,C,S,x,L,O,k,N,E,I,D,M,T=n("iXRW"),P=(n("tZcU"),n("80kS")),A=n("Kp7x"),R=n("uNfg"),F=n("mrx5"),j=n("artP"),W=n("vTy2"),B=n("iHM7"),V=n("c6Qy");!function(e){e[e.Unnecessary=1]="Unnecessary",e[e.Deprecated=2]="Deprecated"}(i||(i={})),function(e){e[e.Hint=1]="Hint",e[e.Info=2]="Info",e[e.Warning=4]="Warning",e[e.Error=8]="Error"}(r||(r={})),function(e){e[e.Unknown=0]="Unknown",e[e.Backspace=1]="Backspace",e[e.Tab=2]="Tab",e[e.Enter=3]="Enter",e[e.Shift=4]="Shift",e[e.Ctrl=5]="Ctrl",e[e.Alt=6]="Alt",e[e.PauseBreak=7]="PauseBreak",e[e.CapsLock=8]="CapsLock",e[e.Escape=9]="Escape",e[e.Space=10]="Space",e[e.PageUp=11]="PageUp",e[e.PageDown=12]="PageDown",e[e.End=13]="End",e[e.Home=14]="Home",e[e.LeftArrow=15]="LeftArrow",e[e.UpArrow=16]="UpArrow",e[e.RightArrow=17]="RightArrow",e[e.DownArrow=18]="DownArrow",e[e.Insert=19]="Insert",e[e.Delete=20]="Delete",e[e.KEY_0=21]="KEY_0",e[e.KEY_1=22]="KEY_1",e[e.KEY_2=23]="KEY_2",e[e.KEY_3=24]="KEY_3",e[e.KEY_4=25]="KEY_4",e[e.KEY_5=26]="KEY_5",e[e.KEY_6=27]="KEY_6",e[e.KEY_7=28]="KEY_7",e[e.KEY_8=29]="KEY_8",e[e.KEY_9=30]="KEY_9",e[e.KEY_A=31]="KEY_A",e[e.KEY_B=32]="KEY_B",e[e.KEY_C=33]="KEY_C",e[e.KEY_D=34]="KEY_D",e[e.KEY_E=35]="KEY_E",e[e.KEY_F=36]="KEY_F",e[e.KEY_G=37]="KEY_G",e[e.KEY_H=38]="KEY_H",e[e.KEY_I=39]="KEY_I",e[e.KEY_J=40]="KEY_J",e[e.KEY_K=41]="KEY_K",e[e.KEY_L=42]="KEY_L",e[e.KEY_M=43]="KEY_M",e[e.KEY_N=44]="KEY_N",e[e.KEY_O=45]="KEY_O",e[e.KEY_P=46]="KEY_P",e[e.KEY_Q=47]="KEY_Q",e[e.KEY_R=48]="KEY_R",e[e.KEY_S=49]="KEY_S",e[e.KEY_T=50]="KEY_T",e[e.KEY_U=51]="KEY_U",e[e.KEY_V=52]="KEY_V",e[e.KEY_W=53]="KEY_W",e[e.KEY_X=54]="KEY_X",e[e.KEY_Y=55]="KEY_Y",e[e.KEY_Z=56]="KEY_Z",e[e.Meta=57]="Meta",e[e.ContextMenu=58]="ContextMenu",e[e.F1=59]="F1",e[e.F2=60]="F2",e[e.F3=61]="F3",e[e.F4=62]="F4",e[e.F5=63]="F5",e[e.F6=64]="F6",e[e.F7=65]="F7",e[e.F8=66]="F8",e[e.F9=67]="F9",e[e.F10=68]="F10",e[e.F11=69]="F11",e[e.F12=70]="F12",e[e.F13=71]="F13",e[e.F14=72]="F14",e[e.F15=73]="F15",e[e.F16=74]="F16",e[e.F17=75]="F17",e[e.F18=76]="F18",e[e.F19=77]="F19",e[e.NumLock=78]="NumLock",e[e.ScrollLock=79]="ScrollLock",e[e.US_SEMICOLON=80]="US_SEMICOLON",e[e.US_EQUAL=81]="US_EQUAL",e[e.US_COMMA=82]="US_COMMA",e[e.US_MINUS=83]="US_MINUS",e[e.US_DOT=84]="US_DOT",e[e.US_SLASH=85]="US_SLASH",e[e.US_BACKTICK=86]="US_BACKTICK",e[e.US_OPEN_SQUARE_BRACKET=87]="US_OPEN_SQUARE_BRACKET",e[e.US_BACKSLASH=88]="US_BACKSLASH",e[e.US_CLOSE_SQUARE_BRACKET=89]="US_CLOSE_SQUARE_BRACKET",e[e.US_QUOTE=90]="US_QUOTE",e[e.OEM_8=91]="OEM_8",e[e.OEM_102=92]="OEM_102",e[e.NUMPAD_0=93]="NUMPAD_0",e[e.NUMPAD_1=94]="NUMPAD_1",e[e.NUMPAD_2=95]="NUMPAD_2",e[e.NUMPAD_3=96]="NUMPAD_3",e[e.NUMPAD_4=97]="NUMPAD_4",e[e.NUMPAD_5=98]="NUMPAD_5",e[e.NUMPAD_6=99]="NUMPAD_6",e[e.NUMPAD_7=100]="NUMPAD_7",e[e.NUMPAD_8=101]="NUMPAD_8",e[e.NUMPAD_9=102]="NUMPAD_9",e[e.NUMPAD_MULTIPLY=103]="NUMPAD_MULTIPLY",e[e.NUMPAD_ADD=104]="NUMPAD_ADD",e[e.NUMPAD_SEPARATOR=105]="NUMPAD_SEPARATOR",e[e.NUMPAD_SUBTRACT=106]="NUMPAD_SUBTRACT",e[e.NUMPAD_DECIMAL=107]="NUMPAD_DECIMAL",e[e.NUMPAD_DIVIDE=108]="NUMPAD_DIVIDE",e[e.KEY_IN_COMPOSITION=109]="KEY_IN_COMPOSITION",e[e.ABNT_C1=110]="ABNT_C1",e[e.ABNT_C2=111]="ABNT_C2",e[e.MAX_VALUE=112]="MAX_VALUE"}(o||(o={})),function(e){e[e.LTR=0]="LTR",e[e.RTL=1]="RTL"}(s||(s={})),function(e){e[e.Auto=1]="Auto",e[e.Hidden=2]="Hidden",e[e.Visible=3]="Visible"}(a||(a={})),function(e){e[e.Left=1]="Left",e[e.Center=2]="Center",e[e.Right=4]="Right",e[e.Full=7]="Full"}(u||(u={})),function(e){e[e.Inline=1]="Inline"}(c||(c={})),function(e){e[e.TextDefined=0]="TextDefined",e[e.LF=1]="LF",e[e.CRLF=2]="CRLF"}(l||(l={})),function(e){e[e.LF=1]="LF",e[e.CRLF=2]="CRLF"}(d||(d={})),function(e){e[e.LF=0]="LF",e[e.CRLF=1]="CRLF"}(h||(h={})),function(e){e[e.AlwaysGrowsWhenTypingAtEdges=0]="AlwaysGrowsWhenTypingAtEdges",e[e.NeverGrowsWhenTypingAtEdges=1]="NeverGrowsWhenTypingAtEdges",e[e.GrowsOnlyWhenTypingBefore=2]="GrowsOnlyWhenTypingBefore",e[e.GrowsOnlyWhenTypingAfter=3]="GrowsOnlyWhenTypingAfter"}(f||(f={})),function(e){e[e.Smooth=0]="Smooth",e[e.Immediate=1]="Immediate"}(p||(p={})),function(e){e[e.NotSet=0]="NotSet",e[e.ContentFlush=1]="ContentFlush",e[e.RecoverFromMarkers=2]="RecoverFromMarkers",e[e.Explicit=3]="Explicit",e[e.Paste=4]="Paste",e[e.Undo=5]="Undo",e[e.Redo=6]="Redo"}(g||(g={})),function(e){e[e.None=0]="None",e[e.Small=1]="Small",e[e.Large=2]="Large",e[e.SmallBlocks=3]="SmallBlocks",e[e.LargeBlocks=4]="LargeBlocks"}(m||(m={})),function(e){e[e.None=0]="None",e[e.Same=1]="Same",e[e.Indent=2]="Indent",e[e.DeepIndent=3]="DeepIndent"}(v||(v={})),function(e){e[e.Hidden=0]="Hidden",e[e.Blink=1]="Blink",e[e.Smooth=2]="Smooth",e[e.Phase=3]="Phase",e[e.Expand=4]="Expand",e[e.Solid=5]="Solid"}(_||(_={})),function(e){e[e.Line=1]="Line",e[e.Block=2]="Block",e[e.Underline=3]="Underline",e[e.LineThin=4]="LineThin",e[e.BlockOutline=5]="BlockOutline",e[e.UnderlineThin=6]="UnderlineThin"}(b||(b={})),function(e){e[e.Off=0]="Off",e[e.On=1]="On",e[e.Relative=2]="Relative",e[e.Interval=3]="Interval",e[e.Custom=4]="Custom"}(y||(y={})),function(e){e[e.EXACT=0]="EXACT",e[e.ABOVE=1]="ABOVE",e[e.BELOW=2]="BELOW"}(w||(w={})),function(e){e[e.TOP_RIGHT_CORNER=0]="TOP_RIGHT_CORNER",e[e.BOTTOM_RIGHT_CORNER=1]="BOTTOM_RIGHT_CORNER",e[e.TOP_CENTER=2]="TOP_CENTER"}(C||(C={})),function(e){e[e.UNKNOWN=0]="UNKNOWN",e[e.TEXTAREA=1]="TEXTAREA",e[e.GUTTER_GLYPH_MARGIN=2]="GUTTER_GLYPH_MARGIN",e[e.GUTTER_LINE_NUMBERS=3]="GUTTER_LINE_NUMBERS",e[e.GUTTER_LINE_DECORATIONS=4]="GUTTER_LINE_DECORATIONS",e[e.GUTTER_VIEW_ZONE=5]="GUTTER_VIEW_ZONE",e[e.CONTENT_TEXT=6]="CONTENT_TEXT",e[e.CONTENT_EMPTY=7]="CONTENT_EMPTY",e[e.CONTENT_VIEW_ZONE=8]="CONTENT_VIEW_ZONE",e[e.CONTENT_WIDGET=9]="CONTENT_WIDGET",e[e.OVERVIEW_RULER=10]="OVERVIEW_RULER",e[e.SCROLLBAR=11]="SCROLLBAR",e[e.OVERLAY_WIDGET=12]="OVERLAY_WIDGET",e[e.OUTSIDE_EDITOR=13]="OUTSIDE_EDITOR"}(S||(S={})),function(e){e[e.None=0]="None",e[e.Indent=1]="Indent",e[e.IndentOutdent=2]="IndentOutdent",e[e.Outdent=3]="Outdent"}(x||(x={})),function(e){e[e.Method=0]="Method",e[e.Function=1]="Function",e[e.Constructor=2]="Constructor",e[e.Field=3]="Field",e[e.Variable=4]="Variable",e[e.Class=5]="Class",e[e.Struct=6]="Struct",e[e.Interface=7]="Interface",e[e.Module=8]="Module",e[e.Property=9]="Property",e[e.Event=10]="Event",e[e.Operator=11]="Operator",e[e.Unit=12]="Unit",e[e.Value=13]="Value",e[e.Constant=14]="Constant",e[e.Enum=15]="Enum",e[e.EnumMember=16]="EnumMember",e[e.Keyword=17]="Keyword",e[e.Text=18]="Text",e[e.Color=19]="Color",e[e.File=20]="File",e[e.Reference=21]="Reference",e[e.Customcolor=22]="Customcolor",e[e.Folder=23]="Folder",e[e.TypeParameter=24]="TypeParameter",e[e.Snippet=25]="Snippet"}(L||(L={})),function(e){e[e.Deprecated=1]="Deprecated"}(O||(O={})),function(e){e[e.KeepWhitespace=1]="KeepWhitespace",e[e.InsertAsSnippet=4]="InsertAsSnippet"}(k||(k={})),function(e){e[e.Invoke=0]="Invoke",e[e.TriggerCharacter=1]="TriggerCharacter",e[e.TriggerForIncompleteCompletions=2]="TriggerForIncompleteCompletions"}(N||(N={})),function(e){e[e.Invoke=1]="Invoke",e[e.TriggerCharacter=2]="TriggerCharacter",e[e.ContentChange=3]="ContentChange"}(E||(E={})),function(e){e[e.Text=0]="Text",e[e.Read=1]="Read",e[e.Write=2]="Write"}(I||(I={})),function(e){e[e.File=0]="File",e[e.Module=1]="Module",e[e.Namespace=2]="Namespace",e[e.Package=3]="Package",e[e.Class=4]="Class",e[e.Method=5]="Method",e[e.Property=6]="Property",e[e.Field=7]="Field",e[e.Constructor=8]="Constructor",e[e.Enum=9]="Enum",e[e.Interface=10]="Interface",e[e.Function=11]="Function",e[e.Variable=12]="Variable",e[e.Constant=13]="Constant",e[e.String=14]="String",e[e.Number=15]="Number",e[e.Boolean=16]="Boolean",e[e.Array=17]="Array",e[e.Object=18]="Object",e[e.Key=19]="Key",e[e.Null=20]="Null",e[e.EnumMember=21]="EnumMember",e[e.Struct=22]="Struct",e[e.Event=23]="Event",e[e.Operator=24]="Operator",e[e.TypeParameter=25]="TypeParameter"}(D||(D={})),function(e){e[e.Deprecated=1]="Deprecated"}(M||(M={}));var H=function(){function e(){}return e.chord=function(e,t){return Object(R.a)(e,t)},e.CtrlCmd=2048,e.Shift=1024,e.Alt=512,e.WinCtrl=256,e}();function z(){return{editor:void 0,languages:void 0,CancellationTokenSource:P.b,Emitter:A.a,KeyCode:o,KeyMod:H,Position:j.a,Range:W.a,Selection:B.a,SelectionDirection:s,MarkerSeverity:r,MarkerTag:i,Uri:F.a,Token:V.a}}n("gvGx");var U,K=n("vORD"),q=n("7/Cv"),G=n("tqet"),Z=n("EMhq"),Y=n("+vUW"),X=n("lapT"),$=n("ZYUE"),J=n("aL7J"),Q=n("ItKl"),ee=this&&this.__extends||(U=function(e,t){return(U=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}U(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),te=this&&this.__decorate||function(e,t,n,i){var r,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,i);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},ne=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},ie=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(r,o){function s(e){try{u(i.next(e))}catch(e){o(e)}}function a(e){try{u(i.throw(e))}catch(e){o(e)}}function u(e){e.done?r(e.value):new n(function(t){t(e.value)}).then(s,a)}u((i=i.apply(e,t||[])).next())})},re=this&&this.__generator||function(e,t){var n,i,r,o,s={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return o={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function a(o){return function(a){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,i&&(r=2&o[0]?i.return:o[0]?i.throw||((r=i.return)&&r.call(i),0):i.next)&&!(r=r.call(i,o[1])).done)return r;switch(i=0,r&&(o=[2&o[0],r.value]),o[0]){case 0:case 1:r=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,i=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(r=(r=s.trys).length>0&&r[r.length-1])&&(6===o[0]||2===o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]=this.ranges.length&&(this.nextIdx=0)):(this.nextIdx-=1,this.nextIdx<0&&(this.nextIdx=this.ranges.length-1));var n=this.ranges[this.nextIdx];this.ignoreSelectionChange=!0;try{var i=n.range.getStartPosition();this._editor.setPosition(i),this._editor.revealPositionInCenter(i,t)}finally{this.ignoreSelectionChange=!1}}},t.prototype.canNavigate=function(){return this.ranges&&this.ranges.length>0},t.prototype.next=function(e){void 0===e&&(e=0),this._move(!0,e)},t.prototype.previous=function(e){void 0===e&&(e=0),this._move(!1,e)},t.prototype.dispose=function(){e.prototype.dispose.call(this),this.ranges=[],this.disposed=!0},t}(G.a),de=n("5lao"),he=n("33h2"),fe=n("D2uo"),pe=n("PCC9"),ge=n("jUH2"),me=n("606G"),ve=n("B/Xy"),_e=n("odeJ"),be=n("zxiH"),ye=n("ZfGv"),we=n("KIxu"),Ce=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),Se="$initialize",xe=!1;function Le(e){ye.f&&(xe||(xe=!0,console.warn("Could not create web worker(s). Falling back to loading web worker code in main thread, which might cause UI freezes. Please see https://github.com/Microsoft/monaco-editor#faq")),console.warn(e.message))}var Oe=function(){function e(e){this._workerId=-1,this._handler=e,this._lastSentReq=0,this._pendingReplies=Object.create(null)}return e.prototype.setWorkerId=function(e){this._workerId=e},e.prototype.sendMessage=function(e,t){var n=this,i=String(++this._lastSentReq);return new Promise(function(r,o){n._pendingReplies[i]={resolve:r,reject:o},n._send({vsWorker:n._workerId,req:i,method:e,args:t})})},e.prototype.handleMessage=function(e){e&&e.vsWorker&&(-1!==this._workerId&&e.vsWorker!==this._workerId||this._handleMessage(e))},e.prototype._handleMessage=function(e){var t=this;if(e.seq){var n=e;if(!this._pendingReplies[n.seq])return void console.warn("Got reply to unknown seq");var i=this._pendingReplies[n.seq];if(delete this._pendingReplies[n.seq],n.err){var r=n.err;return n.err.$isError&&((r=new Error).name=n.err.name,r.message=n.err.message,r.stack=n.err.stack),void i.reject(r)}i.resolve(n.res)}else{var o=e,s=o.req;this._handler.handleMessage(o.method,o.args).then(function(e){t._send({vsWorker:t._workerId,seq:s,res:e,err:void 0})},function(e){e.detail instanceof Error&&(e.detail=Object(be.g)(e.detail)),t._send({vsWorker:t._workerId,seq:s,res:void 0,err:Object(be.g)(e)})})}},e.prototype._send=function(e){var t=[];if(e.req)for(var n=e,i=0;i1&&p>1;){if(d.charCodeAt(f-2)!==h.charCodeAt(p-2))break;f--,p--}(f>1||p>1)&&this._pushTrimWhitespaceCharChange(r,o+1,1,f,s+1,1,p);for(var g=Fe._getLastNonBlankColumn(d,1),m=Fe._getLastNonBlankColumn(h,1),v=d.length+1,_=h.length+1;gt&&(t=a),s>n&&(n=s),u>n&&(n=u)}t++,n++;var c=new qe.a(n,t,0);for(i=0,r=e.length;i=this._maxCharCode?0:this._states.get(e,t)},e}(),Ze=null;var Ye=null;var Xe=function(){function e(){}return e._createLink=function(e,t,n,i,r){var o=r-1;do{var s=t.charCodeAt(o);if(2!==e.get(s))break;o--}while(o>i);if(i>0){var a=t.charCodeAt(i-1),u=t.charCodeAt(o);(40===a&&41===u||91===a&&93===u||123===a&&125===u)&&o--}return{range:{startLineNumber:n,startColumn:i+1,endLineNumber:n,endColumn:o+2},url:t.substring(i,o+1)}},e.computeLinks=function(t,n){void 0===n&&(null===Ze&&(Ze=new Ge([[1,104,2],[1,72,2],[1,102,6],[1,70,6],[2,116,3],[2,84,3],[3,116,4],[3,84,4],[4,112,5],[4,80,5],[5,115,9],[5,83,9],[5,58,10],[6,105,7],[6,73,7],[7,108,8],[7,76,8],[8,101,9],[8,69,9],[9,58,10],[10,47,11],[11,47,12]])),n=Ze);for(var i=function(){if(null===Ye){Ye=new Ke.a(0);for(var e=0;e<" \t<>'\"、。。、,.:;?!@#$%&*‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…".length;e++)Ye.set(" \t<>'\"、。。、,.:;?!@#$%&*‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…".charCodeAt(e),1);for(e=0;e<".,;".length;e++)Ye.set(".,;".charCodeAt(e),2)}return Ye}(),r=[],o=1,s=t.getLineCount();o<=s;o++){for(var a=t.getLineContent(o),u=a.length,c=0,l=0,d=0,h=1,f=!1,p=!1,g=!1;c=0?((i+=n?1:-1)<0?i=e.length-1:i%=e.length,e[i]):null},e.INSTANCE=new e,e}(),Je=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),Qe=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return Je(t,e),Object.defineProperty(t.prototype,"uri",{get:function(){return this._uri},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"version",{get:function(){return this._versionId},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"eol",{get:function(){return this._eol},enumerable:!0,configurable:!0}),t.prototype.getValue=function(){return this.getText()},t.prototype.getLinesContent=function(){return this._lines.slice(0)},t.prototype.getLineCount=function(){return this._lines.length},t.prototype.getLineContent=function(e){return this._lines[e-1]},t.prototype.getWordAtPosition=function(e,t){var n=Object(Ue.d)(e.column,Object(Ue.c)(t),this._lines[e.lineNumber-1],0);return n?new W.a(e.lineNumber,n.startColumn,e.lineNumber,n.endColumn):null},t.prototype.getWordUntilPosition=function(e,t){var n=this.getWordAtPosition(e,t);return n?{word:this._lines[e.lineNumber-1].substring(n.startColumn-1,e.column-1),startColumn:n.startColumn,endColumn:e.column}:{word:"",startColumn:e.column,endColumn:e.column}},t.prototype.createWordIterator=function(e){var t,n,i=this,r=0,o=0,s=[],a=function(){if(o=i._lines.length?Te.c:(n=i._lines[r],s=i._wordenize(n,e),o=0,r+=1,a())};return{next:a}},t.prototype.getLineWords=function(e,t){for(var n=this._lines[e-1],i=[],r=0,o=this._wordenize(n,t);rthis._lines.length)t=this._lines.length,n=this._lines[t-1].length+1,i=!0;else{var r=this._lines[t-1].length+1;n<1?(n=1,i=!0):n>r&&(n=r,i=!0)}return i?{lineNumber:t,column:n}:e},t}(ze),et=function(){function e(e,t){this._host=e,this._models=Object.create(null),this._foreignModuleFactory=t,this._foreignModule=null}return e.prototype.dispose=function(){this._models=Object.create(null)},e.prototype._getModel=function(e){return this._models[e]},e.prototype._getModels=function(){var e=this,t=[];return Object.keys(this._models).forEach(function(n){return t.push(e._models[n])}),t},e.prototype.acceptNewModel=function(e){this._models[e.url]=new Qe(F.a.parse(e.url),e.lines,e.EOL,e.versionId)},e.prototype.acceptModelChanged=function(e,t){this._models[e]&&this._models[e].onEvents(t)},e.prototype.acceptRemovedModel=function(e){this._models[e]&&delete this._models[e]},e.prototype.computeDiff=function(e,t,n){var i=this._getModel(e),r=this._getModel(t);if(!i||!r)return Promise.resolve(null);var o=i.getLinesContent(),s=r.getLinesContent(),a=new Ve(o,s,{shouldComputeCharChanges:!0,shouldPostProcessCharChanges:!0,shouldIgnoreTrimWhitespace:n,shouldMakePrettyDiff:!0}).computeDiff(),u=!(a.length>0)&&this._modelsAreIdentical(i,r);return Promise.resolve({identical:u,changes:a})},e.prototype._modelsAreIdentical=function(e,t){var n=e.getLineCount();if(n!==t.getLineCount())return!1;for(var i=1;i<=n;i++){if(e.getLineContent(i)!==t.getLineContent(i))return!1}return!0},e.prototype.computeMoreMinimalEdits=function(t,n){var i=this._getModel(t);if(!i)return Promise.resolve(n);for(var r=[],o=void 0,s=0,a=n=Object(De.o)(n,function(e,t){return e.range&&t.range?W.a.compareRangesUsingStarts(e.range,t.range):(e.range?0:1)-(t.range?0:1)});se._diffLimit)r.push({range:c,text:l});else for(var f=Object(Me.b)(h,l,!1),p=i.offsetAt(W.a.lift(c).getStartPosition()),g=0,m=f;g=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},ct=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},lt=6e4,dt=3e5;function ht(e,t){var n=e.getModel(t);return!!n&&!n.isTooLargeForSyncing()}var ft=function(e){function t(t,n,i){var r=e.call(this)||this;return r._modelService=t,r._workerManager=r._register(new gt(r._modelService)),r._logService=i,r._register(pe.q.register("*",{provideLinks:function(e,t){return ht(r._modelService,e.uri)?r._workerManager.withWorker().then(function(t){return t.computeLinks(e.uri)}).then(function(e){return e&&{links:e}}):Promise.resolve({links:[]})}})),r._register(pe.d.register("*",new pt(r._workerManager,n,r._modelService))),r}return at(t,e),t.prototype.dispose=function(){e.prototype.dispose.call(this)},t.prototype.canComputeDiff=function(e,t){return ht(this._modelService,e)&&ht(this._modelService,t)},t.prototype.computeDiff=function(e,t,n){return this._workerManager.withWorker().then(function(i){return i.computeDiff(e,t,n)})},t.prototype.computeMoreMinimalEdits=function(e,t){var n=this;if(Object(De.n)(t)){if(!ht(this._modelService,e))return Promise.resolve(t);var i=st.a.create(!0),r=this._workerManager.withWorker().then(function(n){return n.computeMoreMinimalEdits(e,t)});return r.finally(function(){return n._logService.trace("FORMAT#computeMoreMinimalEdits",e.toString(!0),i.elapsed())}),r}return Promise.resolve(void 0)},t.prototype.canNavigateValueSet=function(e){return ht(this._modelService,e)},t.prototype.navigateValueSet=function(e,t,n){return this._workerManager.withWorker().then(function(i){return i.navigateValueSet(e,t,n)})},t.prototype.canComputeWordRanges=function(e){return ht(this._modelService,e)},t.prototype.computeWordRanges=function(e,t){return this._workerManager.withWorker().then(function(n){return n.computeWordRanges(e,t)})},t=ut([ct(0,tt.a),ct(1,it),ct(2,ot.a)],t)}(G.a),pt=function(){function e(e,t,n){this._debugDisplayName="wordbasedCompletions",this._workerManager=e,this._configurationService=t,this._modelService=n}return e.prototype.provideCompletionItems=function(e,t){if(this._configurationService.getValue(e.uri,t,"editor").wordBasedSuggestions&&ht(this._modelService,e.uri))return this._workerManager.withWorker().then(function(n){return n.textualSuggest(e.uri,t)})},e}(),gt=function(e){function t(t){var n=e.call(this)||this;return n._modelService=t,n._editorWorkerClient=null,n._lastWorkerUsedTime=(new Date).getTime(),n._register(new _e.c).cancelAndSet(function(){return n._checkStopIdleWorker()},Math.round(dt/2)),n._register(n._modelService.onModelRemoved(function(e){return n._checkStopEmptyWorker()})),n}return at(t,e),t.prototype.dispose=function(){this._editorWorkerClient&&(this._editorWorkerClient.dispose(),this._editorWorkerClient=null),e.prototype.dispose.call(this)},t.prototype._checkStopEmptyWorker=function(){this._editorWorkerClient&&(0===this._modelService.getModels().length&&(this._editorWorkerClient.dispose(),this._editorWorkerClient=null))},t.prototype._checkStopIdleWorker=function(){this._editorWorkerClient&&((new Date).getTime()-this._lastWorkerUsedTime>dt&&(this._editorWorkerClient.dispose(),this._editorWorkerClient=null))},t.prototype.withWorker=function(){return this._lastWorkerUsedTime=(new Date).getTime(),this._editorWorkerClient||(this._editorWorkerClient=new bt(this._modelService,"editorWorkerService")),Promise.resolve(this._editorWorkerClient)},t}(G.a),mt=function(e){function t(t,n,i){var r=e.call(this)||this;if(r._syncedModels=Object.create(null),r._syncedModelsLastUsedTime=Object.create(null),r._proxy=t,r._modelService=n,!i){var o=new _e.c;o.cancelAndSet(function(){return r._checkStopModelSync()},Math.round(lt/2)),r._register(o)}return r}return at(t,e),t.prototype.dispose=function(){for(var t in this._syncedModels)Object(G.f)(this._syncedModels[t]);this._syncedModels=Object.create(null),this._syncedModelsLastUsedTime=Object.create(null),e.prototype.dispose.call(this)},t.prototype.ensureSyncedResources=function(e){for(var t=0,n=e;tlt&&t.push(n)}for(var i=0,r=t;i'"_]/g,"-")}function Dt(e,t){return new Error(e.languageId+": "+t)}function Mt(e,t,n,i,r){var o=null;return t.replace(/\$((\$)|(#)|(\d\d?)|[sS](\d\d?)|@(\w+))/g,function(t,s,a,u,c,l,d,h,f){return Nt(a)?Nt(u)?!Nt(c)&&c0;){var i=e.tokenizer[n];if(i)return i;var r=n.lastIndexOf(".");n=r<0?null:n.substr(0,r)}return null}var Pt=function(){function e(e){this._maxCacheDepth=e,this._entries=Object.create(null)}return e.create=function(e,t){return this._INSTANCE.create(e,t)},e.prototype.create=function(e,t){if(null!==e&&e.depth>=this._maxCacheDepth)return new At(e,t);var n=At.getStackElementId(e);n.length>0&&(n+="|"),n+=t;var i=this._entries[n];return i||(i=new At(e,t),this._entries[n]=i,i)},e._INSTANCE=new e(5),e}(),At=function(){function e(e,t){this.parent=e,this.state=t,this.depth=(this.parent?this.parent.depth:0)+1}return e.getStackElementId=function(e){for(var t="";null!==e;)t.length>0&&(t+="|"),t+=e.state,e=e.parent;return t},e._equals=function(e,t){for(;null!==e&&null!==t;){if(e===t)return!0;if(e.state!==t.state)return!1;e=e.parent,t=t.parent}return null===e&&null===t},e.prototype.equals=function(t){return e._equals(this,t)},e.prototype.push=function(e){return Pt.create(this,e)},e.prototype.pop=function(){return this.parent},e.prototype.popall=function(){for(var e=this;e.parent;)e=e.parent;return e},e.prototype.switchTo=function(e){return Pt.create(this.parent,e)},e}(),Rt=function(){function e(e,t){this.modeId=e,this.state=t}return e.prototype.equals=function(e){return this.modeId===e.modeId&&this.state.equals(e.state)},e.prototype.clone=function(){return this.state.clone()===this.state?this:new e(this.modeId,this.state)},e}(),Ft=function(){function e(e){this._maxCacheDepth=e,this._entries=Object.create(null)}return e.create=function(e,t){return this._INSTANCE.create(e,t)},e.prototype.create=function(e,t){if(null!==t)return new jt(e,t);if(null!==e&&e.depth>=this._maxCacheDepth)return new jt(e,t);var n=At.getStackElementId(e),i=this._entries[n];return i||(i=new jt(e,null),this._entries[n]=i,i)},e._INSTANCE=new e(5),e}(),jt=function(){function e(e,t){this.stack=e,this.embeddedModeData=t}return e.prototype.clone=function(){return(this.embeddedModeData?this.embeddedModeData.clone():null)===this.embeddedModeData?this:Ft.create(this.stack,this.embeddedModeData)},e.prototype.equals=function(t){return t instanceof e&&(!!this.stack.equals(t.stack)&&(null===this.embeddedModeData&&null===t.embeddedModeData||null!==this.embeddedModeData&&null!==t.embeddedModeData&&this.embeddedModeData.equals(t.embeddedModeData)))},e}(),Wt=function(){function e(){this._tokens=[],this._language=null,this._lastTokenType=null,this._lastTokenLanguage=null}return e.prototype.enterMode=function(e,t){this._language=t},e.prototype.emit=function(e,t){this._lastTokenType===t&&this._lastTokenLanguage===this._language||(this._lastTokenType=t,this._lastTokenLanguage=this._language,this._tokens.push(new V.a(e,t,this._language)))},e.prototype.nestedModeTokenize=function(e,t,n){var i=t.modeId,r=t.state,o=pe.y.get(i);if(!o)return this.enterMode(n,i),this.emit(n,""),r;var s=o.tokenize(e,r,n);return this._tokens=this._tokens.concat(s.tokens),this._lastTokenType=null,this._lastTokenLanguage=null,this._language=null,s.endState},e.prototype.finalize=function(e){return new V.b(this._tokens,e)},e}(),Bt=function(){function e(e,t){this._modeService=e,this._theme=t,this._prependTokens=null,this._tokens=[],this._currentLanguageId=0,this._lastTokenMetadata=0}return e.prototype.enterMode=function(e,t){this._currentLanguageId=this._modeService.getLanguageIdentifier(t).id},e.prototype.emit=function(e,t){var n=this._theme.match(this._currentLanguageId,t);this._lastTokenMetadata!==n&&(this._lastTokenMetadata=n,this._tokens.push(e),this._tokens.push(n))},e._merge=function(e,t,n){var i=null!==e?e.length:0,r=t.length,o=null!==n?n.length:0;if(0===i&&0===r&&0===o)return new Uint32Array(0);if(0===i&&0===r)return n;if(0===r&&0===o)return e;var s=new Uint32Array(i+r+o);null!==e&&s.set(e);for(var a=0;a0&&i.nestedModeTokenize(s,t.embeddedModeData,n);var a=e.substring(r);return this._myTokenize(a,t,n+r,i)},e.prototype._safeRuleName=function(e){return e?e.name:"(unknown)"},e.prototype._myTokenize=function(e,t,n,i){i.enterMode(n,this._modeId);for(var r,o,s=e.length,a=t.embeddedModeData,u=t.stack,c=0,l=null,d=!0;d||c=s)break;d=!1;var C=this._lexer.tokenizer[g];if(!C&&!(C=Tt(this._lexer,g)))throw Dt(this._lexer,"tokenizer state is not defined: "+g);for(var S=e.substr(c),x=0,L=C;x=this._lexer.maxStack)throw Dt(this._lexer,"maximum tokenizer stack size reached: ["+u.state+","+u.parent.state+",...]");u=u.push(g)}else if("@pop"===_.next){if(u.depth<=1)throw Dt(this._lexer,"trying to pop an empty stack in rule: "+this._safeRuleName(b));u=u.pop()}else if("@popall"===_.next)u=u.popall();else{var N;if("@"===(N=Mt(this._lexer,_.next,v,m,g))[0]&&(N=N.substr(1)),!Tt(this._lexer,N))throw Dt(this._lexer,"trying to set a next state '"+N+"' that is undefined in rule: "+this._safeRuleName(b));u=u.push(N)}}_.log&&"string"==typeof _.log&&(r=this._lexer,o=this._lexer.languageId+": "+Mt(this._lexer,_.log,v,m,g),console.log(r.languageId+": "+o))}if(null===k)throw Dt(this._lexer,"lexer rule has no well-defined action in rule: "+this._safeRuleName(b));if(Array.isArray(k)){if(l&&l.groups.length>0)throw Dt(this._lexer,"groups cannot be nested: "+this._safeRuleName(b));if(m.length!==k.length+1)throw Dt(this._lexer,"matched number of groups does not match the number of actions in rule: "+this._safeRuleName(b));for(var E=0,I=1;I=0&&a()})})},e.colorizeLine=function(e,t,n,i,r){void 0===r&&(r=4);var o=xt.d.isBasicASCII(e,t),s=xt.d.containsRTL(e,o,n);return Object(St.e)(new St.c(!1,!0,e,!1,o,s,0,i,[],r,0,-1,"none",!1,!1,null)).html},e.colorizeModelLine=function(e,t,n){void 0===n&&(n=4);var i=e.getLineContent(t);e.forceTokenization(t);var r=e.getLineTokens(t).inflate();return this.colorizeLine(i,e.mightContainNonBasicASCII(),e.mightContainRTL(),r,n)},e}();function Ut(e,t,n){return new Promise(function(i,r){var o=function(){var s=function(e,t,n){for(var i=[],r=n.getInitialState(),o=0,s=e.length;o"),r=u.endState}return i.join("")}(e,t,n);if(n instanceof Vt){var a=n.getLoadStatus();if(!1===a.loaded)return void a.promise.then(o,r)}i(s)};o()})}function Kt(e,t){var n=[],i=new Uint32Array(2);i[0]=0,i[1]=16793600;for(var r=0,o=e.length;r")}return n.join("")}var qt=n("gzF+"),Gt=n("Nr0y"),Zt=n("P1SM"),Yt=n("TeKV"),Xt=n("0WPX"),$t=n("Gzpe"),Jt=n("WTFd"),Qt=n("rHGw"),en=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),tn=function(){function e(e,t,n){void 0===e&&(e={}),void 0===t&&(t=[]),void 0===n&&(n=[]),this._contents=e,this._keys=t,this._overrides=n,this.isFrozen=!1}return Object.defineProperty(e.prototype,"contents",{get:function(){return this.checkAndFreeze(this._contents)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"overrides",{get:function(){return this.checkAndFreeze(this._overrides)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"keys",{get:function(){return this.checkAndFreeze(this._keys)},enumerable:!0,configurable:!0}),e.prototype.isEmpty=function(){return 0===this._keys.length&&0===Object.keys(this._contents).length&&0===this._overrides.length},e.prototype.getValue=function(e){return e?Object($t.d)(this.contents,e):this.contents},e.prototype.override=function(t){var n=this.getContentsForOverrideIdentifer(t);if(!n||"object"!=typeof n||!Object.keys(n).length)return this;for(var i={},r=0,o=De.e(Object.keys(this.contents).concat(Object.keys(n)));r5e3&&n._leaveChordMode():n._leaveChordMode()},500)},t.prototype._leaveChordMode=function(){this._currentChordStatusMessage&&(this._currentChordStatusMessage.dispose(),this._currentChordStatusMessage=null),this._currentChordChecker.cancel(),this._currentChord=null},t.prototype._dispatch=function(e,t){return this._doDispatch(this.resolveKeyboardEvent(e),t)},t.prototype._doDispatch=function(e,t){var n=this,i=!1;if(e.isChord())return console.warn("Unexpected keyboard event mapped to a chord"),!1;var r=e.getDispatchParts()[0];if(null===r)return i;var o=this._contextKeyService.getContext(t),s=this._currentChord?this._currentChord.keypress:null,a=e.getLabel(),u=this._getResolver().resolve(o,s,r);return u&&u.enterChord?(i=!0,this._enterChordMode(r,a),i):(this._currentChord&&(u&&u.commandId||(this._notificationService.status(on.a("missing.chord","The key combination ({0}, {1}) is not a command.",this._currentChord.label,a),{hideAfter:1e4}),i=!0)),this._leaveChordMode(),u&&u.commandId&&(u.bubble||(i=!0),void 0===u.commandArgs?this._commandService.executeCommand(u.commandId).then(void 0,function(e){return n._notificationService.warn(e)}):this._commandService.executeCommand(u.commandId,u.commandArgs).then(void 0,function(e){return n._notificationService.warn(e)}),this._telemetryService.publicLog2("workbenchActionExecuted",{id:u.commandId,from:"keybinding"})),i)},t.prototype.mightProducePrintableCharacter=function(e){return!e.ctrlKey&&!e.metaKey&&(e.keyCode>=31&&e.keyCode<=56||e.keyCode>=21&&e.keyCode<=30)},t}(G.a),un=n("7g0X"),cn=function(){function e(t,n){this._defaultKeybindings=t,this._defaultBoundCommands=new Map;for(var i=0,r=t.length;i=0;l--)this._isTargetedForRemoval(e[l],a,u,s,c)&&e.splice(l,1);else n.push(o)}return e.concat(n)},e.prototype._addKeyPress=function(t,n){var i=this._map.get(t);if(void 0===i)return this._map.set(t,[n]),void this._addToLookupMap(n);for(var r=i.length-1;r>=0;r--){var o=i[r];if(o.command!==n.command){var s=o.keypressParts.length>1,a=n.keypressParts.length>1;s&&a&&o.keypressParts[1]!==n.keypressParts[1]||e.whenIsEntirelyIncluded(o.when,n.when)&&this._removeFromLookupMap(o)}}i.push(n),this._addToLookupMap(n)},e.prototype._addToLookupMap=function(e){if(e.command){var t=this._lookupMap.get(e.command);void 0===t?(t=[e],this._lookupMap.set(e.command,t)):t.push(e)}},e.prototype._removeFromLookupMap=function(e){if(e.command){var t=this._lookupMap.get(e.command);if(void 0!==t)for(var n=0,i=t.length;n1&&null!==u.keypressParts[1]?{enterChord:!0,commandId:null,commandArgs:null,bubble:!1}:{enterChord:!1,commandId:u.command,commandArgs:u.commandArgs,bubble:u.bubble}:null},e.prototype._findCommand=function(t,n){for(var i=n.length-1;i>=0;i--){var r=n[i];if(e.contextMatchesRules(t,r.when))return r}return null},e.contextMatchesRules=function(e,t){return!t||t.evaluate(e)},e}(),ln=n("Kx4b"),dn=function(){return function(e,t,n,i,r){this.resolvedKeybinding=e,this.keypressParts=e?function(e){for(var t=[],n=0,i=e.length;n1},t.prototype.getParts=function(){var e=this;return this._parts.map(function(t){return e._getPart(t)})},t.prototype._getPart=function(e){return new R.d(e.ctrlKey,e.shiftKey,e.altKey,e.metaKey,this._getLabel(e),this._getAriaLabel(e))},t.prototype.getDispatchParts=function(){var e=this;return this._parts.map(function(t){return e._getDispatchPart(t)})},t}(R.c),gn=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),mn=function(e){function t(t,n){return e.call(this,n,t.parts)||this}return gn(t,e),t.prototype._keyCodeToUILabel=function(e){if(2===this._os)switch(e){case 15:return"←";case 16:return"↑";case 17:return"→";case 18:return"↓"}return R.b.toString(e)},t.prototype._getLabel=function(e){return e.isDuplicateModifierCase()?"":this._keyCodeToUILabel(e.keyCode)},t.prototype._getAriaLabel=function(e){return e.isDuplicateModifierCase()?"":R.b.toString(e.keyCode)},t.prototype._getDispatchPart=function(e){return t.getDispatchStr(e)},t.getDispatchStr=function(e){if(e.isModifierKey())return null;var t="";return e.ctrlKey&&(t+="ctrl+"),e.shiftKey&&(t+="shift+"),e.altKey&&(t+="alt+"),e.metaKey&&(t+="meta+"),t+=R.b.toString(e.keyCode)},t}(pn),vn=n("fAkY"),_n=n("EMDP"),bn=n("EfIu"),yn=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),wn=this&&this.__decorate||function(e,t,n,i){var r,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,i);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},Cn=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},Sn=function(){function e(e){this.model=e,this._onDispose=new A.a}return Object.defineProperty(e.prototype,"textEditorModel",{get:function(){return this.model},enumerable:!0,configurable:!0}),e.prototype.dispose=function(){this._onDispose.fire()},e}();var xn=function(){function e(){}return e.prototype.setEditor=function(e){this.editor=e},e.prototype.createModelReference=function(e){var t,n,i,r=this,o=null;return this.editor&&(t=this.editor,n=function(t){return r.findModel(t,e)},i=function(t){return r.findModel(t.getOriginalEditor(),e)||r.findModel(t.getModifiedEditor(),e)},o=Object(Zt.a)(t)?n(t):i(t)),o?Promise.resolve(new G.c(new Sn(o))):Promise.reject(new Error("Model not found"))},e.prototype.findModel=function(e,t){var n=e.getModel();return n&&n.uri.toString()!==t.toString()?null:n},e}(),Ln=function(){function e(){}return e.prototype.showWhile=function(e,t){return Promise.resolve(void 0)},e}(),On=function(){return function(){}}(),kn=function(){function e(){}return e.prototype.info=function(e){return this.notify({severity:Gt.a.Info,message:e})},e.prototype.warn=function(e){return this.notify({severity:Gt.a.Warning,message:e})},e.prototype.error=function(e){return this.notify({severity:Gt.a.Error,message:e})},e.prototype.notify=function(t){switch(t.severity){case Gt.a.Error:console.error(t.message);break;case Gt.a.Warning:console.warn(t.message);break;default:console.log(t.message)}return e.NO_OP},e.prototype.status=function(e,t){return G.a.None},e.NO_OP=new vn.b,e}(),Nn=function(){function e(e){this._onWillExecuteCommand=new A.a,this._onDidExecuteCommand=new A.a,this._instantiationService=e,this._dynamicCommands=Object.create(null)}return e.prototype.addCommand=function(e){var t=this,n=e.id;return this._dynamicCommands[n]=e,Object(G.h)(function(){delete t._dynamicCommands[n]})},e.prototype.executeCommand=function(e){for(var t=[],n=1;n0){var _=e[o-1];m=0===_.originalEndLineNumber?_.originalStartLineNumber+1:_.originalEndLineNumber+1,v=0===_.modifiedEndLineNumber?_.modifiedStartLineNumber+1:_.modifiedEndLineNumber+1}var b=p-3+1,y=g-3+1;if(bS)k+=O=S-k,N+=O;if(N>x)k+=O=x-N,N+=O;h[f++]=new ti(w,k,C,N),i[r++]=new ni(h)}var E=i[0].entries,I=[],D=0;for(o=1,s=i.length;od)&&(d=v),0!==_&&(0===h||_f)&&(f=b)}var y=document.createElement("div");y.className="diff-review-row";var w=document.createElement("div");w.className="diff-review-cell diff-review-summary";var C=d-l+1,S=f-h+1;w.appendChild(document.createTextNode(a+1+"/"+this._diffs.length+": @@ -"+l+","+C+" +"+h+","+S+" @@")),y.setAttribute("data-line",String(h));var x=function(e){return 0===e?on.a("no_lines","no lines"):1===e?on.a("one_line","1 line"):on.a("more_lines","{0} lines",e)},L=x(C),O=x(S);y.setAttribute("aria-label",on.a({key:"header",comment:["This is the ARIA label for a git diff header.","A git diff header looks like this: @@ -154,12 +159,39 @@.","That encodes that at original line 154 (which is now line 159), 12 lines were removed/changed with 39 lines.","Variables 0 and 1 refer to the diff index out of total number of diffs.","Variables 2 and 4 will be numbers (a line number).",'Variables 3 and 5 will be "no lines", "1 line" or "X lines", localized separately.']},"Difference {0} of {1}: original {2}, {3}, modified {4}, {5}",a+1,this._diffs.length,l,L,h,O)),y.appendChild(w),y.setAttribute("role","listitem"),c.appendChild(y);var k=h;for(p=0,g=u.length;p0&&r[r.length-1])&&(6===o[0]||2===o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]r.modifiedStartLineNumber?on.a("diff.clipboard.copyDeletedLinesContent.label","Copy deleted lines"):on.a("diff.clipboard.copyDeletedLinesContent.single.label","Copy deleted line"),void 0,!0,function(){return pi(a,void 0,void 0,function(){return gi(this,function(e){switch(e.label){case 0:return[4,this._clipboardService.writeText(r.originalContent.join(c)+c)];case 1:return e.sent(),[2]}})})}));var d=0,h=void 0;return r.originalEndLineNumber>r.modifiedStartLineNumber&&(h=new Yn.a("diff.clipboard.copyDeletedLineContent",on.a("diff.clipboard.copyDeletedLineContent.label","Copy deleted line ({0})",r.originalStartLineNumber),void 0,!0,function(){return pi(a,void 0,void 0,function(){return gi(this,function(e){switch(e.label){case 0:return[4,this._clipboardService.writeText(r.originalContent[d])];case 1:return e.sent(),[2]}})})}),l.push(h)),i.getConfiguration().readOnly||l.push(new Yn.a("diff.inline.revertChange",on.a("diff.inline.revertChange.label","Revert this change"),void 0,!0,function(){return pi(a,void 0,void 0,function(){var e;return gi(this,function(t){return 0===r.modifiedEndLineNumber?(e=i.getModel().getLineMaxColumn(r.modifiedStartLineNumber),i.executeEdits("diffEditor",[{range:new W.a(r.modifiedStartLineNumber,e,r.modifiedStartLineNumber,e),text:c+r.originalContent.join(c)}])):(e=i.getModel().getLineMaxColumn(r.modifiedEndLineNumber),i.executeEdits("diffEditor",[{range:new W.a(r.modifiedStartLineNumber,1,r.modifiedEndLineNumber,e),text:r.originalContent.join(c)}])),[2]})})})),a._register(q.k(a._diffActions,"mousedown",function(e){var t=q.x(a._diffActions),n=t.top,i=t.height,o=Math.floor(u/3);e.preventDefault(),a._contextMenuService.showContextMenu({getAnchor:function(){return{x:e.posx,y:n+i+o}},getActions:function(){return h&&(h.label=on.a("diff.clipboard.copyDeletedLineContent.label","Copy deleted line ({0})",r.originalStartLineNumber+d)),l},autoSelectFirstItem:!0})})),a._register(i.onMouseMove(function(e){8===e.target.type||5===e.target.type?e.target.detail.viewZoneId===a._viewZoneId?(a.visibility=!0,d=a._updateLightBulbPosition(a._marginDomNode,e.event.browserEvent.y,u)):a.visibility=!1:a.visibility=!1})),a}return fi(t,e),Object.defineProperty(t.prototype,"visibility",{get:function(){return this._visibility},set:function(e){this._visibility!==e&&(this._visibility=e,this._diffActions.style.visibility=e?"visible":"hidden")},enumerable:!0,configurable:!0}),t.prototype._updateLightBulbPosition=function(e,t,n){var i=t-q.x(e).top,r=Math.floor(i/n),o=r*n;return this._diffActions.style.top=o+"px",r},t}(G.a),vi=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),_i=this&&this.__decorate||function(e,t,n,i){var r,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,i);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},bi=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},yi=function(){function e(e,t){this._contextMenuService=e,this._clipboardService=t,this._zones=[],this.inlineDiffMargins=[],this._zonesMap={},this._decorations=[]}return e.prototype.getForeignViewZones=function(e){var t=this;return e.filter(function(e){return!t._zonesMap[String(e.id)]})},e.prototype.clean=function(e){var t=this;this._zones.length>0&&e.changeViewZones(function(e){for(var n=0,i=t._zones.length;n0?r/n:0;return{height:Math.max(0,Math.floor(e.contentHeight*o)),top:Math.floor(t*o)}},t.prototype._createDataSource=function(){var e=this;return{getWidth:function(){return e._width},getHeight:function(){return e._height-e._reviewHeight},getContainerDomNode:function(){return e._containerDomElement},relayoutEditors:function(){e._doLayout()},getOriginalEditor:function(){return e.originalEditor},getModifiedEditor:function(){return e.modifiedEditor}}},t.prototype._setStrategy=function(e){this._strategy&&this._strategy.dispose(),this._strategy=e,e.applyColors(this._themeService.getTheme()),this._diffComputationResult&&this._updateDecorations(),this._measureDomElement(!0)},t.prototype._getLineChangeAtOrBeforeLineNumber=function(e,t){var n=this._diffComputationResult?this._diffComputationResult.changes:[];if(0===n.length||e=a?i=o+1:(i=o,r=o)}return n[i]},t.prototype._getEquivalentLineForOriginalLineNumber=function(e){var t=this._getLineChangeAtOrBeforeLineNumber(e,function(e){return e.originalStartLineNumber});if(!t)return e;var n=t.originalStartLineNumber+(t.originalEndLineNumber>0?-1:0),i=t.modifiedStartLineNumber+(t.modifiedEndLineNumber>0?-1:0),r=t.originalEndLineNumber>0?t.originalEndLineNumber-t.originalStartLineNumber+1:0,o=t.modifiedEndLineNumber>0?t.modifiedEndLineNumber-t.modifiedStartLineNumber+1:0,s=e-n;return s<=r?i+Math.min(s,o):i+o-r+s},t.prototype._getEquivalentLineForModifiedLineNumber=function(e){var t=this._getLineChangeAtOrBeforeLineNumber(e,function(e){return e.modifiedStartLineNumber});if(!t)return e;var n=t.originalStartLineNumber+(t.originalEndLineNumber>0?-1:0),i=t.modifiedStartLineNumber+(t.modifiedEndLineNumber>0?-1:0),r=t.originalEndLineNumber>0?t.originalEndLineNumber-t.originalStartLineNumber+1:0,o=t.modifiedEndLineNumber>0?t.modifiedEndLineNumber-t.modifiedStartLineNumber+1:0,s=e-i;return s<=o?n+Math.min(s,r):n+r-o+s},t.prototype.getDiffLineInformationForOriginal=function(e){return this._diffComputationResult?{equivalentLineNumber:this._getEquivalentLineForOriginalLineNumber(e)}:null},t.prototype.getDiffLineInformationForModified=function(e){return this._diffComputationResult?{equivalentLineNumber:this._getEquivalentLineForModifiedLineNumber(e)}:null},t.ONE_OVERVIEW_WIDTH=15,t.ENTIRE_DIFF_OVERVIEW_WIDTH=30,t.UPDATE_DIFF_DECORATIONS_DELAY=200,t=_i([bi(3,me.a),bi(4,un.c),bi(5,nt.a),bi(6,K.a),bi(7,Qn.c),bi(8,vn.a),bi(9,hi.a)],t)}(G.a),Si=function(e){function t(t){var n=e.call(this)||this;return n._dataSource=t,n._insertColor=null,n._removeColor=null,n}return vi(t,e),t.prototype.applyColors=function(e){var t=(e.getColor(Jn.j)||Jn.g).transparent(2),n=(e.getColor(Jn.l)||Jn.h).transparent(2),i=!t.equals(this._insertColor)||!n.equals(this._removeColor);return this._insertColor=t,this._removeColor=n,i},t.prototype.getEditorsDiffDecorations=function(e,t,n,i,r,o,s){r=r.sort(function(e,t){return e.afterLineNumber-t.afterLineNumber}),i=i.sort(function(e,t){return e.afterLineNumber-t.afterLineNumber});var a=this._getViewZones(e,i,r,o,s,n),u=this._getOriginalEditorDecorations(e,t,n,o,s),c=this._getModifiedEditorDecorations(e,t,n,o,s);return{original:{decorations:u.decorations,overviewZones:u.overviewZones,zones:a.original},modified:{decorations:c.decorations,overviewZones:c.overviewZones,zones:a.modified}}},t}(G.a),xi=function(){function e(e){this._source=e,this._index=-1,this.current=null,this.advance()}return e.prototype.advance=function(){this._index++,this._index0){var n=e[e.length-1];if(n.afterLineNumber===t.afterLineNumber&&null===n.domNode)return void(n.heightInLines+=t.heightInLines)}e.push(t)},d=new xi(this.modifiedForeignVZ),h=new xi(this.originalForeignVZ),f=0,p=this.lineChanges.length;f<=p;f++){var g=f0?-1:0),s=g.modifiedStartLineNumber+(g.modifiedEndLineNumber>0?-1:0),r=g.originalEndLineNumber>0?g.originalEndLineNumber-g.originalStartLineNumber+1:0,i=g.modifiedEndLineNumber>0?g.modifiedEndLineNumber-g.modifiedStartLineNumber+1:0,a=Math.max(g.originalStartLineNumber,g.originalEndLineNumber),u=Math.max(g.modifiedStartLineNumber,g.modifiedEndLineNumber)):(a=o+=1e7+r,u=s+=1e7+i);for(var m,v=[],_=[];d.current&&d.current.afterLineNumber<=u;){var b=void 0;b=d.current.afterLineNumber<=s?o-s+d.current.afterLineNumber:a;var y=null;g&&g.modifiedStartLineNumber<=d.current.afterLineNumber&&d.current.afterLineNumber<=g.modifiedEndLineNumber&&(y=this._createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion()),v.push({afterLineNumber:b,heightInLines:d.current.heightInLines,domNode:null,marginDomNode:y}),d.advance()}for(;h.current&&h.current.afterLineNumber<=a;){b=void 0;b=h.current.afterLineNumber<=o?s-o+h.current.afterLineNumber:u,_.push({afterLineNumber:b,heightInLines:h.current.heightInLines,domNode:null}),h.advance()}if(null!==g&&Mi(g))(m=this._produceOriginalFromDiff(g,r,i))&&v.push(m);if(null!==g&&Ti(g))(m=this._produceModifiedFromDiff(g,r,i))&&_.push(m);var w=0,C=0;for(v=v.sort(c),_=_.sort(c);w=x.heightInLines?(S.heightInLines-=x.heightInLines,C++):(x.heightInLines-=S.heightInLines,w++)}for(;w2*t.MINIMUM_EDITOR_WIDTH?(in-t.MINIMUM_EDITOR_WIDTH&&(i=n-t.MINIMUM_EDITOR_WIDTH)):i=r,this._sashPosition!==i&&(this._sashPosition=i,this._sash.layout()),this._sashPosition},t.prototype.onSashDragStart=function(){this._startSashPosition=this._sashPosition},t.prototype.onSashDrag=function(e){var t=this._dataSource.getWidth()-Ci.ENTIRE_DIFF_OVERVIEW_WIDTH,n=this.layout((this._startSashPosition+(e.currentX-e.startX))/t);this._sashRatio=n/t,this._dataSource.relayoutEditors()},t.prototype.onSashDragEnd=function(){this._sash.layout()},t.prototype.onSashReset=function(){this._sashRatio=.5,this._dataSource.relayoutEditors(),this._sash.layout()},t.prototype.getVerticalSashTop=function(e){return 0},t.prototype.getVerticalSashLeft=function(e){return this._sashPosition},t.prototype.getVerticalSashHeight=function(e){return this._dataSource.getHeight()},t.prototype._getViewZones=function(e,t,n,i,r){return new Ei(e,t,n).getViewZones()},t.prototype._getOriginalEditorDecorations=function(e,t,n,i,r){for(var o=String(this._removeColor),s={decorations:[],overviewZones:[]},a=i.getModel(),u=0,c=e.length;ut?{afterLineNumber:Math.max(e.originalStartLineNumber,e.originalEndLineNumber),heightInLines:n-t,domNode:null}:null},t.prototype._produceModifiedFromDiff=function(e,t,n){return t>n?{afterLineNumber:Math.max(e.modifiedStartLineNumber,e.modifiedEndLineNumber),heightInLines:t-n,domNode:null}:null},t}(Li),Ii=function(e){function t(t,n){var i=e.call(this,t)||this;return i.decorationsLeft=t.getOriginalEditor().getLayoutInfo().decorationsLeft,i._register(t.getOriginalEditor().onDidLayoutChange(function(e){i.decorationsLeft!==e.decorationsLeft&&(i.decorationsLeft=e.decorationsLeft,t.relayoutEditors())})),i}return vi(t,e),t.prototype.setEnableSplitViewResizing=function(e){},t.prototype._getViewZones=function(e,t,n,i,r,o){return new Di(e,t,n,i,r,o).getViewZones()},t.prototype._getOriginalEditorDecorations=function(e,t,n,i,r){for(var o=String(this._removeColor),s={decorations:[],overviewZones:[]},a=0,u=e.length;a'])}h+=this.modifiedEditorConfiguration.viewInfo.scrollBeyondLastColumn;var m=document.createElement("div");m.className="view-lines line-delete",m.innerHTML=a.build(),Kn.a.applyFontInfoSlow(m,this.modifiedEditorConfiguration.fontInfo);var v=document.createElement("div");return v.className="inline-deleted-margin-view-zone",v.innerHTML=u.join(""),Kn.a.applyFontInfoSlow(v,this.modifiedEditorConfiguration.fontInfo),{shouldNotShrink:!0,afterLineNumber:0===e.modifiedEndLineNumber?e.modifiedStartLineNumber:e.modifiedStartLineNumber-1,heightInLines:t,minWidthInPx:h*d,domNode:m,marginDomNode:v,diff:{originalStartLineNumber:e.originalStartLineNumber,originalEndLineNumber:e.originalEndLineNumber,modifiedStartLineNumber:e.modifiedStartLineNumber,modifiedEndLineNumber:e.modifiedEndLineNumber,originalContent:f}}},t.prototype._renderOriginalLine=function(e,t,n,i,r,o,s){var a=t.getLineTokens(r),u=a.getLineContent(),c=li.a.filter(o,r,1,u.length+1);s.appendASCIIString('
');var l=xt.d.isBasicASCII(u,t.mightContainNonBasicASCII()),d=xt.d.containsRTL(u,l,t.mightContainRTL()),h=Object(St.d)(new St.c(n.fontInfo.isMonospace&&!n.viewInfo.disableMonospaceOptimizations,n.fontInfo.canUseHalfwidthRightwardsArrow,u,!1,l,d,0,a,c,i,n.fontInfo.spaceWidth,n.viewInfo.stopRenderingLineAfter,n.viewInfo.renderWhitespace,n.viewInfo.renderControlCharacters,n.viewInfo.fontLigatures,null),s);s.appendASCIIString("
");var f=h.characterMapping.getAbsoluteOffsets();return f.length>0?f[f.length-1]:0},t}(Li);function Mi(e){return e.modifiedEndLineNumber>0}function Ti(e){return e.originalEndLineNumber>0}Object(Qn.f)(function(e,t){var n=e.getColor(Jn.j);n&&(t.addRule(".monaco-editor .line-insert, .monaco-editor .char-insert { background-color: "+n+"; }"),t.addRule(".monaco-diff-editor .line-insert, .monaco-diff-editor .char-insert { background-color: "+n+"; }"),t.addRule(".monaco-editor .inline-added-margin-view-zone { background-color: "+n+"; }"));var i=e.getColor(Jn.l);i&&(t.addRule(".monaco-editor .line-delete, .monaco-editor .char-delete { background-color: "+i+"; }"),t.addRule(".monaco-diff-editor .line-delete, .monaco-diff-editor .char-delete { background-color: "+i+"; }"),t.addRule(".monaco-editor .inline-deleted-margin-view-zone { background-color: "+i+"; }"));var r=e.getColor(Jn.k);r&&t.addRule(".monaco-editor .line-insert, .monaco-editor .char-insert { border: 1px "+("hc"===e.type?"dashed":"solid")+" "+r+"; }");var o=e.getColor(Jn.m);o&&t.addRule(".monaco-editor .line-delete, .monaco-editor .char-delete { border: 1px "+("hc"===e.type?"dashed":"solid")+" "+o+"; }");var s=e.getColor(Jn._37);s&&t.addRule(".monaco-diff-editor.side-by-side .editor.modified { box-shadow: -6px 0 5px -5px "+s+"; }");var a=e.getColor(Jn.i);a&&t.addRule(".monaco-diff-editor.side-by-side .editor.modified { border-left: 1px solid "+a+"; }")});var Pi=n("lthF"),Ai=n("sKqm"),Ri=n("C3c5"),Fi=n("NqM+"),ji=n("xJaW"),Wi=n("44YW"),Bi=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),Vi=this&&this.__decorate||function(e,t,n,i){var r,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,i);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},Hi=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},zi=0,Ui=!1;var Ki=function(e){function t(t,n,i,r,o,s,a,u,c,l){var d=this;return(n=n||{}).ariaLabel=n.ariaLabel||bn.g.editorViewAccessibleLabel,n.ariaLabel=n.ariaLabel+";"+(Bn.j?bn.g.accessibilityHelpMessageIE:bn.g.accessibilityHelpMessage),(d=e.call(this,t,n,{},i,r,o,s,u,c,l)||this)._standaloneKeybindingService=a instanceof En?a:null,Ui||(Ui=!0,Vn.b(document.body)),d}return Bi(t,e),t.prototype.addCommand=function(e,t,n){if(!this._standaloneKeybindingService)return console.warn("Cannot add command because the editor is configured with an unrecognized KeybindingService"),null;var i="DYNAMIC_"+ ++zi,r=un.a.deserialize(n);return this._standaloneKeybindingService.addDynamicKeybinding(i,e,t,r),i},t.prototype.createContextKey=function(e,t){return this._contextKeyService.createKey(e,t)},t.prototype.addAction=function(e){var t=this;if("string"!=typeof e.id||"string"!=typeof e.label||"function"!=typeof e.run)throw new Error("Invalid action descriptor, `id`, `label` and `run` are required properties!");if(!this._standaloneKeybindingService)return console.warn("Cannot add keybinding because the editor is configured with an unrecognized KeybindingService"),G.a.None;var n=e.id,i=e.label,r=un.a.and(un.a.equals("editorId",this.getId()),un.a.deserialize(e.precondition)),o=e.keybindings,s=un.a.and(r,un.a.deserialize(e.keybindingContext)),a=e.contextMenuGroupId||null,u=e.contextMenuOrder||0,c=function(){return Promise.resolve(e.run(t))},l=new G.b,d=this.getId()+":"+n;if(l.add(Q.a.registerCommand(d,c)),a){var h={command:{id:d,title:i},when:r,group:a,order:u};l.add(Ri.c.appendMenuItem(7,h))}if(Array.isArray(o))for(var f=0,p=o;f=0}}(e);tr.push(n),n.userConfigured?ir.push(n):nr.push(n),t&&!n.userConfigured&&tr.forEach(function(e){e.mime===n.mime||e.userConfigured||(n.extension&&e.extension===n.extension&&console.warn("Overwriting extension <<"+n.extension+">> to now point to mime <<"+n.mime+">>"),n.filename&&e.filename===n.filename&&console.warn("Overwriting filename <<"+n.filename+">> to now point to mime <<"+n.mime+">>"),n.filepattern&&e.filepattern===n.filepattern&&console.warn("Overwriting filepattern <<"+n.filepattern+">> to now point to mime <<"+n.mime+">>"),n.firstline&&e.firstline===n.firstline&&console.warn("Overwriting firstline <<"+n.firstline+">> to now point to mime <<"+n.mime+">>"))})}function or(e,t){var n;if(e)switch(e.scheme){case X.b.file:n=e.fsPath;break;case X.b.data:n=$.a.parseMetaData(e).get($.a.META_DATA_LABEL);break;default:n=e.path}if(!n)return[er];n=n.toLowerCase();var i=Object($i.basename)(n),r=sr(n,i,ir);if(r)return[r,Qi];var o=sr(n,i,nr);if(o)return[o,Qi];if(t){var s=function(e){Object(J.L)(e)&&(e=e.substr(1));if(e.length>0)for(var t=tr.length-1;t>=0;t--){var n=tr[t];if(n.firstline){var i=e.match(n.firstline);if(i&&i.length>0)return n.mime}}return null}(t);if(s)return[s,Qi]}return[er]}function sr(e,t,n){for(var i=null,r=null,o=null,s=n.length-1;s>=0;s--){var a=n[s];if(t===a.filenameLowercase){i=a;break}if(a.filepattern&&(!r||a.filepattern.length>r.filepattern.length)){var u=a.filepatternOnPath?e:t;Object(Ji.a)(a.filepatternLowercase,u)&&(r=a)}a.extension&&(!o||a.extension.length>o.extension.length)&&Object(J.m)(t,a.extensionLowercase)&&(o=a)}return i?i.mime:r?r.mime:o?o.mime:null}var ar=n("9XyG"),ur=n("RWr8"),cr=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),lr=Object.prototype.hasOwnProperty,dr=function(e){function t(t,n){void 0===t&&(t=!0),void 0===n&&(n=!1);var i=e.call(this)||this;return i._onDidChange=i._register(new A.a),i.onDidChange=i._onDidChange.event,i._warnOnOverwrite=n,i._nextLanguageId2=1,i._languageIdToLanguage=[],i._languageToLanguageId=Object.create(null),i._languages={},i._mimeTypesMap={},i._nameMap={},i._lowercaseNameMap={},t&&(i._initializeFromRegistry(),i._register(ar.a.onDidChangeLanguages(function(e){return i._initializeFromRegistry()}))),i}return cr(t,e),t.prototype._initializeFromRegistry=function(){this._languages={},this._mimeTypesMap={},this._nameMap={},this._lowercaseNameMap={};var e=ar.a.getLanguages();this._registerLanguages(e)},t.prototype._registerLanguages=function(e){for(var t=this,n=0,i=e;n0&&((n=e.mimetypes).push.apply(n,t.mimetypes),r=t.mimetypes[0]),r||(r="text/x-"+i,e.mimetypes.push(r)),Array.isArray(t.extensions))for(var o=0,s=t.extensions;o0){var f=t.firstLine;"^"!==f.charAt(0)&&(f="^"+f);try{var p=new RegExp(f);J.E(p)||rr({id:i,mime:r,firstline:p},this._warnOnOverwrite)}catch(e){Object(be.e)(e)}}e.aliases.push(i);var g=null;if(void 0!==t.aliases&&Array.isArray(t.aliases)&&(g=0===t.aliases.length?[null]:t.aliases),null!==g)for(var m=0,v=g;m0;if(b&&null===g[0]);else{var y=(b?g[0]:null)||i;!b&&e.name||(e.name=y)}t.configuration&&e.configurationFiles.push(t.configuration)},t.prototype.isRegisteredMode=function(e){return!!lr.call(this._mimeTypesMap,e)||lr.call(this._languages,e)},t.prototype.getModeIdForLanguageNameLowercase=function(e){return lr.call(this._lowercaseNameMap,e)?this._lowercaseNameMap[e].language:null},t.prototype.extractModeIds=function(e){var t=this;return e?e.split(",").map(function(e){return e.trim()}).map(function(e){return lr.call(t._mimeTypesMap,e)?t._mimeTypesMap[e].language:e}).filter(function(e){return lr.call(t._languages,e)}):[]},t.prototype.getLanguageIdentifier=function(e){if(e===ge.b||0===e)return ge.a;var t;if("string"==typeof e)t=e;else if(!(t=this._languageIdToLanguage[e]))return null;return lr.call(this._languages,t)?this._languages[t].identifier:null},t.prototype.getModeIdsFromFilepathOrFirstLine=function(e,t){if(!e&&!t)return[];var n=or(e,t);return this.extractModeIds(n.join(","))},t}(G.a),hr=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),fr=function(e){function t(t,n){var i=e.call(this)||this;return i._onDidChange=i._register(new A.a),i.onDidChange=i._onDidChange.event,i._selector=n,i.languageIdentifier=i._selector(),i._register(t(function(){return i._evaluate()})),i}return hr(t,e),t.prototype._evaluate=function(){var e=this._selector();e.id!==this.languageIdentifier.id&&(this.languageIdentifier=e,this._onDidChange.fire(this.languageIdentifier))},t}(G.a),pr=function(){function e(e){var t=this;void 0===e&&(e=!1),this._onDidCreateMode=new A.a,this.onDidCreateMode=this._onDidCreateMode.event,this._onLanguagesMaybeChanged=new A.a,this.onLanguagesMaybeChanged=this._onLanguagesMaybeChanged.event,this._instantiatedModes={},this._registry=new dr(!0,e),this._registry.onDidChange(function(){return t._onLanguagesMaybeChanged.fire()})}return e.prototype.isRegisteredMode=function(e){return this._registry.isRegisteredMode(e)},e.prototype.getModeIdForLanguageName=function(e){return this._registry.getModeIdForLanguageNameLowercase(e)},e.prototype.getModeIdByFilepathOrFirstLine=function(e,t){var n=this._registry.getModeIdsFromFilepathOrFirstLine(e,t);return n.length>0?n[0]:null},e.prototype.getModeId=function(e){var t=this._registry.extractModeIds(e);return t.length>0?t[0]:null},e.prototype.getLanguageIdentifier=function(e){return this._registry.getLanguageIdentifier(e)},e.prototype.create=function(e){var t=this;return new fr(this.onLanguagesMaybeChanged,function(){var n=t.getModeId(e);return t._createModeAndGetLanguageIdentifier(n)})},e.prototype.createByFilepathOrFirstLine=function(e,t){var n=this;return new fr(this.onLanguagesMaybeChanged,function(){var i=n.getModeIdByFilepathOrFirstLine(e,t);return n._createModeAndGetLanguageIdentifier(i)})},e.prototype._createModeAndGetLanguageIdentifier=function(e){var t=this.getLanguageIdentifier(e||"plaintext")||ge.a;return this._getOrCreateMode(t.language),t},e.prototype.triggerMode=function(e){var t=this.getModeId(e);this._getOrCreateMode(t||"plaintext")},e.prototype._getOrCreateMode=function(e){if(!this._instantiatedModes.hasOwnProperty(e)){var t=this.getLanguageIdentifier(e)||ge.a;this._instantiatedModes[e]=new Xi(t),this._onDidCreateMode.fire(this._instantiatedModes[e])}return this._instantiatedModes[e]},e}(),gr=this&&this.__extends||function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),mr=this&&this.__decorate||function(e,t,n,i){var r,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,i);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},vr=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}};function _r(e){return e.toString()}var br=function(){function e(e,t,n){this._modelEventListeners=new G.b,this.model=e,this._languageSelection=null,this._languageSelectionListener=null,this._modelEventListeners.add(e.onWillDispose(function(){return t(e)})),this._modelEventListeners.add(e.onDidChangeLanguage(function(t){return n(e,t)}))}return e.prototype._disposeLanguageSelection=function(){this._languageSelectionListener&&(this._languageSelectionListener.dispose(),this._languageSelectionListener=null),this._languageSelection&&(this._languageSelection.dispose(),this._languageSelection=null)},e.prototype.dispose=function(){this._modelEventListeners.dispose(),this._disposeLanguageSelection()},e.prototype.setLanguage=function(e){var t=this;this._disposeLanguageSelection(),this._languageSelection=e,this._languageSelectionListener=this._languageSelection.onDidChange(function(){return t.model.setMode(e.languageIdentifier)}),this.model.setMode(e.languageIdentifier)},e}(),yr=ye.c||ye.d?1:2,wr=function(e){function t(t,n){var i=e.call(this)||this;return i._onModelAdded=i._register(new A.a),i.onModelAdded=i._onModelAdded.event,i._onModelRemoved=i._register(new A.a),i.onModelRemoved=i._onModelRemoved.event,i._onModelModeChanged=i._register(new A.a),i.onModelModeChanged=i._onModelModeChanged.event,i._configurationService=t,i._resourcePropertiesService=n,i._models={},i._modelCreationOptionsByLanguageAndResource=Object.create(null),i._configurationServiceSubscription=i._configurationService.onDidChangeConfiguration(function(e){return i._updateModelOptions()}),i._updateModelOptions(),i}return gr(t,e),t._readModelOptions=function(e,t){var n=T.c.tabSize;if(e.editor&&void 0!==e.editor.tabSize){var i=parseInt(e.editor.tabSize,10);isNaN(i)||(n=i),n<1&&(n=1)}var r=n;if(e.editor&&void 0!==e.editor.indentSize&&"tabSize"!==e.editor.indentSize){var o=parseInt(e.editor.indentSize,10);isNaN(o)||(r=o),r<1&&(r=1)}var s=T.c.insertSpaces;e.editor&&void 0!==e.editor.insertSpaces&&(s="false"!==e.editor.insertSpaces&&Boolean(e.editor.insertSpaces));var a=yr,u=e.eol;"\r\n"===u?a=2:"\n"===u&&(a=1);var c=T.c.trimAutoWhitespace;e.editor&&void 0!==e.editor.trimAutoWhitespace&&(c="false"!==e.editor.trimAutoWhitespace&&Boolean(e.editor.trimAutoWhitespace));var l=T.c.detectIndentation;e.editor&&void 0!==e.editor.detectIndentation&&(l="false"!==e.editor.detectIndentation&&Boolean(e.editor.detectIndentation));var d=T.c.largeFileOptimizations;return e.editor&&void 0!==e.editor.largeFileOptimizations&&(d="false"!==e.editor.largeFileOptimizations&&Boolean(e.editor.largeFileOptimizations)),{isForSimpleWidget:t,tabSize:n,indentSize:r,insertSpaces:s,detectIndentation:l,defaultEOL:a,trimAutoWhitespace:c,largeFileOptimizations:d}},t.prototype.getCreationOptions=function(e,n,i){var r=this._modelCreationOptionsByLanguageAndResource[e+n];if(!r){var o=this._configurationService.getValue("editor",{overrideIdentifier:e,resource:n}),s=this._resourcePropertiesService.getEOL(n,e);r=t._readModelOptions({editor:o,eol:s},i),this._modelCreationOptionsByLanguageAndResource[e+n]=r}return r},t.prototype._updateModelOptions=function(){var e=this._modelCreationOptionsByLanguageAndResource;this._modelCreationOptionsByLanguageAndResource=Object.create(null);for(var n=Object.keys(this._models),i=0,r=n.length;i=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},Or=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},kr=function(e){function t(t,n){void 0===n&&(n=q.s());var i=e.call(this)||this;return i._decorationOptionProviders=new Map,i._styleSheet=n,i._themeService=t,i}return xr(t,e),t.prototype.registerDecorationType=function(e,t,n){var i=this._decorationOptionProviders.get(e);if(!i){var r={styleSheet:this._styleSheet,key:e,parentTypeKey:n,options:t||Object.create(null)};i=n?new Nr(this._themeService,r):new Er(this._themeService,r),this._decorationOptionProviders.set(e,i)}i.refCount++},t.prototype.removeDecorationType=function(e){var t=this._decorationOptionProviders.get(e);t&&(t.refCount--,t.refCount<=0&&(this._decorationOptionProviders.delete(e),t.dispose(),this.listCodeEditors().forEach(function(t){return t.removeDecorations(e)})))},t.prototype.resolveDecorationOptions=function(e,t){var n=this._decorationOptionProviders.get(e);if(!n)throw new Error("Unknown decoration type key: "+e);return n.getOptions(this,t)},t=Lr([Or(0,Qn.c)],t)}(Sr),Nr=function(){function e(e,t){this._parentTypeKey=t.parentTypeKey,this.refCount=0,this._beforeContentRules=new Dr(3,t,e),this._afterContentRules=new Dr(4,t,e)}return e.prototype.getOptions=function(e,t){var n=e.resolveDecorationOptions(this._parentTypeKey,!0);return this._beforeContentRules&&(n.beforeContentClassName=this._beforeContentRules.className),this._afterContentRules&&(n.afterContentClassName=this._afterContentRules.className),n},e.prototype.dispose=function(){this._beforeContentRules&&(this._beforeContentRules.dispose(),this._beforeContentRules=null),this._afterContentRules&&(this._afterContentRules.dispose(),this._afterContentRules=null)},e}(),Er=function(){function e(e,t){var n=this;this._disposables=new G.b,this.refCount=0;var i=function(i){var r=new Dr(i,t,e);if(n._disposables.add(r),r.hasContent)return r.className};this.className=i(0);var r,o=(r=new Dr(1,t,e),n._disposables.add(r),r.hasContent?{className:r.className,hasLetterSpacing:r.hasLetterSpacing}:null);o&&(this.inlineClassName=o.className,this.inlineClassNameAffectsLetterSpacing=o.hasLetterSpacing),this.beforeContentClassName=i(3),this.afterContentClassName=i(4),this.glyphMarginClassName=i(2);var s=t.options;this.isWholeLine=Boolean(s.isWholeLine),this.stickiness=s.rangeBehavior;var a=s.light&&s.light.overviewRulerColor||s.overviewRulerColor,u=s.dark&&s.dark.overviewRulerColor||s.overviewRulerColor;void 0===a&&void 0===u||(this.overviewRuler={color:a||u,darkColor:u||a,position:s.overviewRulerLane||fe.d.Center})}return e.prototype.getOptions=function(e,t){return t?{inlineClassName:this.inlineClassName,beforeContentClassName:this.beforeContentClassName,afterContentClassName:this.afterContentClassName,className:this.className,glyphMarginClassName:this.glyphMarginClassName,isWholeLine:this.isWholeLine,overviewRuler:this.overviewRuler,stickiness:this.stickiness}:this},e.prototype.dispose=function(){this._disposables.dispose()},e}(),Ir={color:"color:{0} !important;",opacity:"opacity:{0};",backgroundColor:"background-color:{0};",outline:"outline:{0};",outlineColor:"outline-color:{0};",outlineStyle:"outline-style:{0};",outlineWidth:"outline-width:{0};",border:"border:{0};",borderColor:"border-color:{0};",borderRadius:"border-radius:{0};",borderSpacing:"border-spacing:{0};",borderStyle:"border-style:{0};",borderWidth:"border-width:{0};",fontStyle:"font-style:{0};",fontWeight:"font-weight:{0};",textDecoration:"text-decoration:{0};",cursor:"cursor:{0};",letterSpacing:"letter-spacing:{0};",gutterIconPath:"background:{0} center center no-repeat;",gutterIconSize:"background-size:{0};",contentText:"content:'{0}';",contentIconPath:"content:{0};",margin:"margin:{0};",width:"width:{0};",height:"height:{0};"},Dr=function(){function e(e,t,n){var i=this;this._theme=n.getTheme(),this._ruleType=e,this._providerArgs=t,this._usesThemeColors=!1,this._hasContent=!1,this._hasLetterSpacing=!1;var r=Mr.getClassName(this._providerArgs.key,e);this._providerArgs.parentTypeKey&&(r=r+" "+Mr.getClassName(this._providerArgs.parentTypeKey,e)),this._className=r,this._unThemedSelector=Mr.getSelector(this._providerArgs.key,this._providerArgs.parentTypeKey,e),this._buildCSS(),this._usesThemeColors?this._themeListener=n.onThemeChange(function(e){i._theme=n.getTheme(),i._removeCSS(),i._buildCSS()}):this._themeListener=null}return e.prototype.dispose=function(){this._hasContent&&(this._removeCSS(),this._hasContent=!1),this._themeListener&&(this._themeListener.dispose(),this._themeListener=null)},Object.defineProperty(e.prototype,"hasContent",{get:function(){return this._hasContent},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"hasLetterSpacing",{get:function(){return this._hasLetterSpacing},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"className",{get:function(){return this._className},enumerable:!0,configurable:!0}),e.prototype._buildCSS=function(){var e,t,n,i=this._providerArgs.options;switch(this._ruleType){case 0:e=this.getCSSTextForModelDecorationClassName(i),t=this.getCSSTextForModelDecorationClassName(i.light),n=this.getCSSTextForModelDecorationClassName(i.dark);break;case 1:e=this.getCSSTextForModelDecorationInlineClassName(i),t=this.getCSSTextForModelDecorationInlineClassName(i.light),n=this.getCSSTextForModelDecorationInlineClassName(i.dark);break;case 2:e=this.getCSSTextForModelDecorationGlyphMarginClassName(i),t=this.getCSSTextForModelDecorationGlyphMarginClassName(i.light),n=this.getCSSTextForModelDecorationGlyphMarginClassName(i.dark);break;case 3:e=this.getCSSTextForModelDecorationContentClassName(i.before),t=this.getCSSTextForModelDecorationContentClassName(i.light&&i.light.before),n=this.getCSSTextForModelDecorationContentClassName(i.dark&&i.dark.before);break;case 4:e=this.getCSSTextForModelDecorationContentClassName(i.after),t=this.getCSSTextForModelDecorationContentClassName(i.light&&i.light.after),n=this.getCSSTextForModelDecorationContentClassName(i.dark&&i.dark.after);break;default:throw new Error("Unknown rule type: "+this._ruleType)}var r=this._providerArgs.styleSheet.sheet,o=!1;e.length>0&&(r.insertRule(this._unThemedSelector+" {"+e+"}",0),o=!0),t.length>0&&(r.insertRule(".vs"+this._unThemedSelector+" {"+t+"}",0),o=!0),n.length>0&&(r.insertRule(".vs-dark"+this._unThemedSelector+", .hc-black"+this._unThemedSelector+" {"+n+"}",0),o=!0),this._hasContent=o},e.prototype._removeCSS=function(){q.H(this._unThemedSelector,this._providerArgs.styleSheet)},e.prototype.getCSSTextForModelDecorationClassName=function(e){if(!e)return"";var t=[];return this.collectCSSText(e,["backgroundColor"],t),this.collectCSSText(e,["outline","outlineColor","outlineStyle","outlineWidth"],t),this.collectBorderSettingsCSSText(e,t),t.join("")},e.prototype.getCSSTextForModelDecorationInlineClassName=function(e){if(!e)return"";var t=[];return this.collectCSSText(e,["fontStyle","fontWeight","textDecoration","cursor","color","opacity","letterSpacing"],t),e.letterSpacing&&(this._hasLetterSpacing=!0),t.join("")},e.prototype.getCSSTextForModelDecorationContentClassName=function(e){if(!e)return"";var t=[];if(void 0!==e){if(this.collectBorderSettingsCSSText(e,t),void 0!==e.contentIconPath&&t.push(J.r(Ir.contentIconPath,q.n(F.a.revive(e.contentIconPath)))),"string"==typeof e.contentText){var n=e.contentText.match(/^.*$/m)[0].replace(/['\\]/g,"\\$&");t.push(J.r(Ir.contentText,n))}this.collectCSSText(e,["fontStyle","fontWeight","textDecoration","color","opacity","backgroundColor","margin"],t),this.collectCSSText(e,["width","height"],t)&&t.push("display:inline-block;")}return t.join("")},e.prototype.getCSSTextForModelDecorationGlyphMarginClassName=function(e){if(!e)return"";var t=[];return void 0!==e.gutterIconPath&&(t.push(J.r(Ir.gutterIconPath,q.n(F.a.revive(e.gutterIconPath)))),void 0!==e.gutterIconSize&&t.push(J.r(Ir.gutterIconSize,e.gutterIconSize))),t.join("")},e.prototype.collectBorderSettingsCSSText=function(e,t){return!!this.collectCSSText(e,["border","borderColor","borderRadius","borderSpacing","borderStyle","borderWidth"],t)&&(t.push(J.r("box-sizing: border-box;")),!0)},e.prototype.collectCSSText=function(e,t,n){for(var i=n.length,r=0,o=t;rt)return 1;return 0}(e.token,t.token);return 0!==n?n:e.index-t.index});for(var n=0,i="000000",r="ffffff";e.length>=1&&""===e[0].token;){var o=e.shift();-1!==o.fontStyle&&(n=o.fontStyle),null!==o.foreground&&(i=o.foreground),null!==o.background&&(r=o.background)}for(var s=new Wr,a=0,u=t;a>>0,this._cache.set(t,n)}return(n|e<<0)>>>0},e}(),Vr=/\b(comment|string|regex|regexp)\b/;var Hr,zr,Ur,Kr=function(){function e(e,t,n){this._fontStyle=e,this._foreground=t,this._background=n,this.metadata=(this._fontStyle<<11|this._foreground<<14|this._background<<23)>>>0}return e.prototype.clone=function(){return new e(this._fontStyle,this._foreground,this._background)},e.prototype.acceptOverwrite=function(e,t,n){-1!==e&&(this._fontStyle=e),0!==t&&(this._foreground=t),0!==n&&(this._background=n),this.metadata=(this._fontStyle<<11|this._foreground<<14|this._background<<23)>>>0},e}(),qr=function(){function e(e){this._mainRule=e,this._children=new Map}return e.prototype.match=function(e){if(""===e)return this._mainRule;var t,n,i=e.indexOf(".");-1===i?(t=e,n=""):(t=e.substring(0,i),n=e.substring(i+1));var r=this._children.get(t);return void 0!==r?r.match(n):this._mainRule},e.prototype.insert=function(t,n,i,r){if(""!==t){var o,s,a=t.indexOf(".");-1===a?(o=t,s=""):(o=t.substring(0,a),s=t.substring(a+1));var u=this._children.get(o);void 0===u&&(u=new e(this._mainRule.clone()),this._children.set(o,u)),u.insert(s,n,i,r)}else this._mainRule.acceptOverwrite(n,i,r)},e}();var Gr={base:"vs",inherit:!1,rules:[{token:"",foreground:"000000",background:"fffffe"},{token:"invalid",foreground:"cd3131"},{token:"emphasis",fontStyle:"italic"},{token:"strong",fontStyle:"bold"},{token:"variable",foreground:"001188"},{token:"variable.predefined",foreground:"4864AA"},{token:"constant",foreground:"dd0000"},{token:"comment",foreground:"008000"},{token:"number",foreground:"09885A"},{token:"number.hex",foreground:"3030c0"},{token:"regexp",foreground:"800000"},{token:"annotation",foreground:"808080"},{token:"type",foreground:"008080"},{token:"delimiter",foreground:"000000"},{token:"delimiter.html",foreground:"383838"},{token:"delimiter.xml",foreground:"0000FF"},{token:"tag",foreground:"800000"},{token:"tag.id.pug",foreground:"4F76AC"},{token:"tag.class.pug",foreground:"4F76AC"},{token:"meta.scss",foreground:"800000"},{token:"metatag",foreground:"e00000"},{token:"metatag.content.html",foreground:"FF0000"},{token:"metatag.html",foreground:"808080"},{token:"metatag.xml",foreground:"808080"},{token:"metatag.php",fontStyle:"bold"},{token:"key",foreground:"863B00"},{token:"string.key.json",foreground:"A31515"},{token:"string.value.json",foreground:"0451A5"},{token:"attribute.name",foreground:"FF0000"},{token:"attribute.value",foreground:"0451A5"},{token:"attribute.value.number",foreground:"09885A"},{token:"attribute.value.unit",foreground:"09885A"},{token:"attribute.value.html",foreground:"0000FF"},{token:"attribute.value.xml",foreground:"0000FF"},{token:"string",foreground:"A31515"},{token:"string.html",foreground:"0000FF"},{token:"string.sql",foreground:"FF0000"},{token:"string.yaml",foreground:"0451A5"},{token:"keyword",foreground:"0000FF"},{token:"keyword.json",foreground:"0451A5"},{token:"keyword.flow",foreground:"AF00DB"},{token:"keyword.flow.scss",foreground:"0000FF"},{token:"operator.scss",foreground:"666666"},{token:"operator.sql",foreground:"778899"},{token:"operator.swift",foreground:"666666"},{token:"predefined.sql",foreground:"FF00FF"}],colors:(Hr={},Hr[Jn.o]="#FFFFFE",Hr[Jn.x]="#000000",Hr[Jn.E]="#E5EBF1",Hr[$n.h]="#D3D3D3",Hr[$n.a]="#939393",Hr[Jn.J]="#ADD6FF4D",Hr)},Zr={base:"vs-dark",inherit:!1,rules:[{token:"",foreground:"D4D4D4",background:"1E1E1E"},{token:"invalid",foreground:"f44747"},{token:"emphasis",fontStyle:"italic"},{token:"strong",fontStyle:"bold"},{token:"variable",foreground:"74B0DF"},{token:"variable.predefined",foreground:"4864AA"},{token:"variable.parameter",foreground:"9CDCFE"},{token:"constant",foreground:"569CD6"},{token:"comment",foreground:"608B4E"},{token:"number",foreground:"B5CEA8"},{token:"number.hex",foreground:"5BB498"},{token:"regexp",foreground:"B46695"},{token:"annotation",foreground:"cc6666"},{token:"type",foreground:"3DC9B0"},{token:"delimiter",foreground:"DCDCDC"},{token:"delimiter.html",foreground:"808080"},{token:"delimiter.xml",foreground:"808080"},{token:"tag",foreground:"569CD6"},{token:"tag.id.pug",foreground:"4F76AC"},{token:"tag.class.pug",foreground:"4F76AC"},{token:"meta.scss",foreground:"A79873"},{token:"meta.tag",foreground:"CE9178"},{token:"metatag",foreground:"DD6A6F"},{token:"metatag.content.html",foreground:"9CDCFE"},{token:"metatag.html",foreground:"569CD6"},{token:"metatag.xml",foreground:"569CD6"},{token:"metatag.php",fontStyle:"bold"},{token:"key",foreground:"9CDCFE"},{token:"string.key.json",foreground:"9CDCFE"},{token:"string.value.json",foreground:"CE9178"},{token:"attribute.name",foreground:"9CDCFE"},{token:"attribute.value",foreground:"CE9178"},{token:"attribute.value.number.css",foreground:"B5CEA8"},{token:"attribute.value.unit.css",foreground:"B5CEA8"},{token:"attribute.value.hex.css",foreground:"D4D4D4"},{token:"string",foreground:"CE9178"},{token:"string.sql",foreground:"FF0000"},{token:"keyword",foreground:"569CD6"},{token:"keyword.flow",foreground:"C586C0"},{token:"keyword.json",foreground:"CE9178"},{token:"keyword.flow.scss",foreground:"569CD6"},{token:"operator.scss",foreground:"909090"},{token:"operator.sql",foreground:"778899"},{token:"operator.swift",foreground:"909090"},{token:"predefined.sql",foreground:"FF00FF"}],colors:(zr={},zr[Jn.o]="#1E1E1E",zr[Jn.x]="#D4D4D4",zr[Jn.E]="#3A3D41",zr[$n.h]="#404040",zr[$n.a]="#707070",zr[Jn.J]="#ADD6FF26",zr)},Yr={base:"hc-black",inherit:!1,rules:[{token:"",foreground:"FFFFFF",background:"000000"},{token:"invalid",foreground:"f44747"},{token:"emphasis",fontStyle:"italic"},{token:"strong",fontStyle:"bold"},{token:"variable",foreground:"1AEBFF"},{token:"variable.parameter",foreground:"9CDCFE"},{token:"constant",foreground:"569CD6"},{token:"comment",foreground:"608B4E"},{token:"number",foreground:"FFFFFF"},{token:"regexp",foreground:"C0C0C0"},{token:"annotation",foreground:"569CD6"},{token:"type",foreground:"3DC9B0"},{token:"delimiter",foreground:"FFFF00"},{token:"delimiter.html",foreground:"FFFF00"},{token:"tag",foreground:"569CD6"},{token:"tag.id.pug",foreground:"4F76AC"},{token:"tag.class.pug",foreground:"4F76AC"},{token:"meta",foreground:"D4D4D4"},{token:"meta.tag",foreground:"CE9178"},{token:"metatag",foreground:"569CD6"},{token:"metatag.content.html",foreground:"1AEBFF"},{token:"metatag.html",foreground:"569CD6"},{token:"metatag.xml",foreground:"569CD6"},{token:"metatag.php",fontStyle:"bold"},{token:"key",foreground:"9CDCFE"},{token:"string.key",foreground:"9CDCFE"},{token:"string.value",foreground:"CE9178"},{token:"attribute.name",foreground:"569CD6"},{token:"attribute.value",foreground:"3FF23F"},{token:"string",foreground:"CE9178"},{token:"string.sql",foreground:"FF0000"},{token:"keyword",foreground:"569CD6"},{token:"keyword.flow",foreground:"C586C0"},{token:"operator.sql",foreground:"778899"},{token:"operator.swift",foreground:"909090"},{token:"predefined.sql",foreground:"FF00FF"}],colors:(Ur={},Ur[Jn.o]="#000000",Ur[Jn.x]="#FFFFFF",Ur[$n.h]="#FFFFFF",Ur[$n.a]="#FFFFFF",Ur)},Xr="vs",$r="vs-dark",Jr="hc-black",Qr=ur.a.as(Jn.a.ColorContribution),eo=ur.a.as(Qn.a.ThemingContribution),to=function(){function e(e,t){this.themeData=t;var n=t.base;e.length>0?(this.id=n+" "+e,this.themeName=e):(this.id=n,this.themeName=n),this.colors=null,this.defaultColors=Object.create(null),this._tokenTheme=null}return Object.defineProperty(e.prototype,"base",{get:function(){return this.themeData.base},enumerable:!0,configurable:!0}),e.prototype.notifyBaseUpdated=function(){this.themeData.inherit&&(this.colors=null,this._tokenTheme=null)},e.prototype.getColors=function(){if(!this.colors){var e=new Map;for(var t in this.themeData.colors)e.set(t,Ar.a.fromHex(this.themeData.colors[t]));if(this.themeData.inherit){var n=io(this.themeData.base);for(var t in n.colors)e.has(t)||e.set(t,Ar.a.fromHex(n.colors[t]))}this.colors=e}return this.colors},e.prototype.getColor=function(e,t){var n=this.getColors().get(e);return n||(!1!==t?this.getDefault(e):void 0)},e.prototype.getDefault=function(e){var t=this.defaultColors[e];return t||(t=Qr.resolveDefaultColor(e,this),this.defaultColors[e]=t,t)},e.prototype.defines=function(e){return Object.prototype.hasOwnProperty.call(this.getColors(),e)},Object.defineProperty(e.prototype,"type",{get:function(){switch(this.base){case Xr:return"light";case Jr:return"hc";default:return"dark"}},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"tokenTheme",{get:function(){if(!this._tokenTheme){var e=[],t=[];if(this.themeData.inherit){var n=io(this.themeData.base);e=n.rules,n.encodedTokensColors&&(t=n.encodedTokensColors)}e=e.concat(this.themeData.rules),this.themeData.encodedTokensColors&&(t=this.themeData.encodedTokensColors),this._tokenTheme=Br.createFromRawTokenTheme(e,t)}return this._tokenTheme},enumerable:!0,configurable:!0}),e}();function no(e){return e===Xr||e===$r||e===Jr}function io(e){switch(e){case Xr:return Gr;case $r:return Zr;case Jr:return Yr}}function ro(e){var t=io(e);return new to(e,t)}var oo=function(){function e(){this.environment=Object.create(null),this._onThemeChange=new A.a,this._onIconThemeChange=new A.a,this._knownThemes=new Map,this._knownThemes.set(Xr,ro(Xr)),this._knownThemes.set($r,ro($r)),this._knownThemes.set(Jr,ro(Jr)),this._styleElement=q.s(),this._styleElement.className="monaco-colors",this.setTheme(Xr)}return Object.defineProperty(e.prototype,"onThemeChange",{get:function(){return this._onThemeChange.event},enumerable:!0,configurable:!0}),e.prototype.defineTheme=function(e,t){if(!/^[a-z0-9\-]+$/i.test(e))throw new Error("Illegal theme name!");if(!no(t.base)&&!no(e))throw new Error("Illegal theme base!");this._knownThemes.set(e,new to(e,t)),no(e)&&this._knownThemes.forEach(function(t){t.base===e&&t.notifyBaseUpdated()}),this._theme&&this._theme.themeName===e&&this.setTheme(e)},e.prototype.getTheme=function(){return this._theme},e.prototype.setTheme=function(e){var t,n=this;if(t=this._knownThemes.has(e)?this._knownThemes.get(e):this._knownThemes.get(Xr),this._theme===t)return t.id;this._theme=t;var i=[],r={},o={addRule:function(e){r[e]||(i.push(e),r[e]=!0)}};eo.getThemingParticipants().forEach(function(e){return e(t,o,n.environment)});var s=t.tokenTheme.getColorMap();return o.addRule(function(e){for(var t=[],n=1,i=e.length;n=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},uo=this&&this.__param||function(e,t){return function(n,i){t(n,i,e)}},co="data-keybinding-context",lo=function(){function e(e,t){this._id=e,this._parent=t,this._value=Object.create(null),this._value._contextId=e}return e.prototype.setValue=function(e,t){return this._value[e]!==t&&(this._value[e]=t,!0)},e.prototype.removeValue=function(e){return e in this._value&&(delete this._value[e],!0)},e.prototype.getValue=function(e){var t=this._value[e];return void 0===t&&this._parent?this._parent.getValue(e):t},e}(),ho=function(e){function t(){return e.call(this,-1,null)||this}return so(t,e),t.prototype.setValue=function(e,t){return!1},t.prototype.removeValue=function(e){return!1},t.prototype.getValue=function(e){},t.INSTANCE=new t,t}(lo),fo=function(e){function t(t,n,i){var r=e.call(this,t,null)||this;return r._configurationService=n,r._values=new Map,r._listener=r._configurationService.onDidChangeConfiguration(function(e){if(6===e.source){var t=Object(Jt.d)(r._values);r._values.clear(),i.fire(new mo(t))}else{for(var n=[],o=0,s=e.affectedKeys;o1){var i=n.shift();i&&(r.focusItemByElement(i.container),n.push(i)),r.mnemonics.set(t,n)}}})),ye.c&&r._register(Object(q.h)(o,q.d.KEY_DOWN,function(e){var t=new qt.a(e);t.equals(14)||t.equals(11)?(r.focusedItem=r.viewItems.length-1,r.focusNext(),q.c.stop(e,!0)):(t.equals(13)||t.equals(12))&&(r.focusedItem=0,r.focusPrevious(),q.c.stop(e,!0))})),r._register(Object(q.h)(r.domNode,q.d.MOUSE_OUT,function(e){var t=e.relatedTarget;Object(q.E)(t,r.domNode)||(r.focusedItem=void 0,r.scrollTopHold=r.menuElement.scrollTop,r.updateFocus(),e.stopPropagation())})),r._register(Object(q.h)(r.domNode,q.d.MOUSE_UP,function(e){q.c.stop(e,!0)})),r._register(Object(q.h)(r.actionsList,q.d.MOUSE_OVER,function(e){var t=e.target;if(t&&Object(q.E)(t,r.actionsList)&&t!==r.actionsList){for(;t.parentElement!==r.actionsList&&null!==t.parentElement;)t=t.parentElement;if(Object(q.C)(t,"action-item")){var n=r.focusedItem;r.scrollTopHold=r.menuElement.scrollTop,r.setFocusedItem(t),n!==r.focusedItem&&r.updateFocus()}}}));var s={parent:r};return r.mnemonics=new Map,r.push(n,{icon:!0,label:!0,isMenu:!0}),r.scrollableElement=r._register(new Zn.a(o,{alwaysConsumeMouseWheel:!0,horizontal:2,vertical:3,verticalScrollbarSize:7,handleMouseWheel:!0,useShadows:!0})),r.scrollableElement.getDomNode().style.position=null,o.style.maxHeight=Math.max(10,window.innerHeight-t.getBoundingClientRect().top-30)+"px",r.menuDisposables.add(r.scrollableElement.onScroll(function(){r._onScroll.fire()},r)),r._register(Object(q.h)(r.menuElement,q.d.SCROLL,function(e){void 0!==r.scrollTopHold&&(r.menuElement.scrollTop=r.scrollTopHold,r.scrollTopHold=void 0),r.scrollableElement.scanDomNode()})),t.appendChild(r.scrollableElement.getDomNode()),r.scrollableElement.scanDomNode(),r.viewItems.filter(function(e){return!(e instanceof No)}).forEach(function(e,t,n){e.updatePositionInSet(t+1,n.length)}),r}return wo(t,e),t.prototype.style=function(e){var t=this.getContainer(),n=e.foregroundColor?""+e.foregroundColor:null,i=e.backgroundColor?""+e.backgroundColor:null,r=e.borderColor?"2px solid "+e.borderColor:null,o=e.shadowColor?"0 2px 4px "+e.shadowColor:null;t.style.border=r,this.domNode.style.color=n,this.domNode.style.backgroundColor=i,t.style.boxShadow=o,this.viewItems&&this.viewItems.forEach(function(t){(t instanceof Oo||t instanceof No)&&t.style(e)})},t.prototype.getContainer=function(){return this.scrollableElement.getDomNode()},Object.defineProperty(t.prototype,"onScroll",{get:function(){return this._onScroll.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"scrollOffset",{get:function(){return this.menuElement.scrollTop},enumerable:!0,configurable:!0}),t.prototype.focusItemByElement=function(e){var t=this.focusedItem;this.setFocusedItem(e),t!==this.focusedItem&&this.updateFocus()},t.prototype.setFocusedItem=function(e){for(var t=0;t