diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000000000000000000000000000000000..8a7039134b10b3281fce709dd551cb564ce86684
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+*.js linguist-language=Groovy
+*.css linguist-language=Groovy
+*.html linguist-language=Groovy
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..d96f3d28d1cf9c8dd52628704873e6d3c7e99e8e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,16 @@
+/build
+/classes
+/out
+.gradle/
+.settings/
+.idea/
+*.iml
+.project
+.classpath
+nohup.out
+/upload
+/log
+/lib
+/h2
+*.log
+hs_err_pid*
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..ccddb83a6913fa376d1ee115dd476f80a4d03825
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,16 @@
+FROM gradle:7.1.1-jdk8
+ENV TZ Asia/Shanghai
+
+RUN mkdir -p /srv/grule
+
+ADD bin /srv/grule/bin
+ADD conf /srv/grule/conf
+ADD src /srv/grule/src
+ADD build.gradle /srv/grule/build.gradle
+ADD start.sh /srv/grule/start.sh
+
+WORKDIR /srv/grule/
+
+RUN gradle deps
+
+ENTRYPOINT exec sh start.sh $JAVA_OPTS
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..34037536631e5c3d8426059a85e9ca833d596a84
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 23 March 2022
+
+ Copyright (C) 2007 hubert-樂xx, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
\ No newline at end of file
diff --git a/README.en.md b/README.en.md
deleted file mode 100644
index 34a316fd73ef07663b7b318f5195c4adb77f0422..0000000000000000000000000000000000000000
--- a/README.en.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# gy
-
-#### Description
-{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
-
-#### Software Architecture
-Software architecture description
-
-#### Installation
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### Instructions
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### Contribution
-
-1. Fork the repository
-2. Create Feat_xxx branch
-3. Commit your code
-4. Create Pull Request
-
-
-#### Gitee Feature
-
-1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
-2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
-3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
-4. The most valuable open source project [GVP](https://gitee.com/gvp)
-5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
-6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
\ No newline at end of file
diff --git a/README.md b/README.md
index e34b7d4751a4c660887f004ce9adc031e5946b8a..3bff63356f7ef3bc73f2265cd0c0647c069de699 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,402 @@
-# gy
+# Groovy DSL 动态规则(rule)执行引擎
-#### 介绍
-{**以下是码云平台说明,您可以替换此简介**
-码云是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
-无论是个人、团队、或是企业,都能够用码云实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
+> ___DSL___(特定领域语言): 开发 和 业务 共识的语言. 方便业务表达需求, 方便开发理解业务
-#### 软件架构
-软件架构说明
+> 一个业务需求(一般程序,一个接口)可以抽象成为: 按一定业务逻辑规则处理数据,然后返回数据
+> > + 一个人可以用成百上千个属性组成,由这些属性衍生出新的属性(例如,好人/坏人) 返回一个业务结果(0..多个属性值)
+> > + 一般接口: 查询数据库或接口,对数据进行简单逻辑计算,最后返回一些属性
+> 此框架可以执行成千上万的一般程序逻辑, 可以动态修改逻辑, 可以动态修改数据
-#### 安装教程
+> 可用于风控规则, 电商价格规则, 其它业务规则
-1. xxxx
-2. xxxx
-3. xxxx
+# 框架概念
+* 框架抽象一个业务需求(一般程序,一个接口)为一条决策,一条决策包含多个业务规则
+* 框架用自创 ___DSL___ 策略 ___规则___ 语言表达业务逻辑
+```
+ 规则 {
+ 规则名 = '芝麻分限制'
+ 拒绝 { 芝麻分 < 620 }
+ }
+```
+* 框架抽象数据为指标(字段), 即是变量
+* 指标值来源于数据源配置(收集器)包含: 接口,SQL,脚本3种类型
-#### 使用说明
+> 例子 http://39.104.28.131:9090/ test:test
-1. xxxx
-2. xxxx
-3. xxxx
+# ___规则___ 定义
+* 由 ___规则名___ 和多个 ___拒绝___, ___通过___, ___人工___, ___操作___ 组成(按顺序依次执行)
+```
+规则 {
+ 规则名 = '大小比较'
+ 拒绝 { 年龄 > 40 || 年龄 < 25 }
+}
+```
+```
+规则 {
+ 规则名 = '通过规则'
+ 通过 { 年龄 == 30 }
+}
+```
+```
+规则 {
+ 规则名 = '赋值规则'
+ 操作 { 产品代码 = 'xx' }
+}
+```
+```
+规则 { // 电商价格规则
+ 规则名 = '条件赋值规则'
+ 操作 {
+ if (当前时间 > '2021-03-02 00:00:00') {
+ 单价 = 10
+ }
+ }
+}
+```
+```
+规则 {
+ 规则名 = '列表判断规则'
-#### 参与贡献
+ 风险地 = ["成都", "巴中"]
-1. Fork 本仓库
-2. 新建 Feat_xxx 分支
-3. 提交代码
-4. 新建 Pull Request
+ 拒绝 { 工作地 in 风险地 }
+}
+```
+```
+规则 {
+ 规则名 = '包含规则'
+ 拒绝 { "$姓名".contains("xx") }
+}
+```
+# ___策略___ 定义
+* 由 ___策略名___ 和多个 ___操作___, ___条件___, ___规则___, ___评分卡___, ___决策___ 组成(按顺序依次执行)
+* ___条件___ 函数返回false, 则跳出, 继续执行下一个策略
+## ___规则___ 策略
+```
+策略 {
+ 策略名 = '004244'
+
+ 操作 { jj_代码 = '004244' }
+ 条件 { jj_名 }
+
+ 规则 {
+ 拒绝 { true }
+ }
+}
+```
+## ___评分卡___ 策略
+___模型___ : 是由多个评分条目组成
+* 普通评分条目: 由 属性名, 值匹配范围, 分值 组成
+ * 例: ['年龄', 18..24, 40]
+ * 例: ['工作成市', ['成都', '巴中'], 99]
+* 函数分条目: 一个求值函数 组成
+ * 例: [{ -> 100}]
+* 变量分条目: 一个数值变量 组成
+ * 例: ['芝麻分']
+* 动态分条目: 一个数值变量,及其值计算函数 组成
+ * 例: ['逾期次数', {次数 -> -((次数?:0) * 100)}]
+* 模型分计算: 依次遍历每个评分条目, 得到匹配的分值相加
+* 最终评分 = ___基础分___ + 模型计算得分
-#### 码云特技
+___赋值变量___ : 把评分结果赋值给的变量
+```
+策略 {
+ 策略名 = '测试评分卡'
+
+ 评分卡 {
+ 评分卡名 = "测试评分卡"
+ 基础分 = 600
+ 模型 = [
+ ['年龄', 18..24, 40],
+ ['年龄', 25..45, 80],
+ ['年龄', 46..60, 50],
+ ['性别', 'F', 50],
+ ['性别', 'M', 60],
+ [{ -> 100}], // 函数分
+ ['芝麻分'], //变量值分
+ ['逾期次数', {次数 -> -((次数?:0) * 100)}], // 动态分
+ ['工作成市', ['成都', '巴中'], 99]
+ ]
+ 赋值变量 = '评分结果'
+ }
+
+ 规则 {
+ 规则名 = "评分判断"
+ 拒绝 { 评分结果 < 60 }
+ }
+}
+```
+## 委托子 ___决策___ 策略
+* 嵌套其它决策执行(_注意不要循环嵌套_)
+* 会形成一条单独的决策执行记录(id为父决策的id为前缀)
+* ___决策id___: 指定要执行的子决策id
+* ___异步___:
+ * 不配置默认为false; 为true时, ___输出___ 配置不生效
+ * 值为false: 并且 当子决策执行结果为Reject的时,会结束当前决策执行
+* ___传参___: 共享当前决策数据给子决策使用
+* ___输出___: 会设置 到当前执行上下文, 可以使用. 没有配置时: 则使用子决策的返回属性作为 ___输出___
+```
+策略 {
+ 策略名 = '测试子决策'
+
+ 决策 {
+ 决策id = "test2"
+ // 不配置默认为false, 异步为true时, 输出则不生效
+ // 异步 = true
+
+ // 左(子决策) = 右(父决策)
+ 传参 {
+ p1 = 1
+ p2 = "aa"
+ p3 = p3 // p3的值从父决策来
+ }
+
+ // 左(父决策) = 右(子决策)
+ 输出 {
+ p111 = p1 // p1的值从子决策来
+ p222 = p3 // p3的值从子决策来
+ }
+ }
+}
+```
-1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
-2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com)
-3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目
-4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目
-5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
-6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
\ No newline at end of file
+# 决策定义
+* 由 ___决策id___, ___决策名___, ___决策描述___ 和多个 ___操作___, ___策略___ 组成(按顺序依次执行)
+* 触发当前决策: http://ip:port/decision?decisionId=test1
+* 接口返回: 3种结果(Accept, Reject, Review) 和多个返回属性值(由配置指定)
+```json
+// 返回示例
+{
+ "decideId": "647f297a2e4540dfa93991b5b6e7b44d",
+ "result": "Accept",
+ "decisionId": "jj_analyse",
+ "status": "0000",
+ "desc": null,
+ "data": {
+ "jj_code": "165525"
+ }
+}
+```
+* 依次从上往下执行
+```
+决策id = 'test1'
+决策名 = '测试1'
+决策描述 = ''
+
+// 返回的调用决策方的结果属性值
+// 返回属性 '身份证号码'
+
+// 预操作执行
+操作 {}
+
+策略 {
+ 策略名 = '预处理'
+ // 条件 { true }
+
+ 规则 {
+ 规则名 = '年龄限制'
+ 拒绝 { 年龄 > 40 || 年龄 < 25 }
+ }
+}
+```
+## ___函数定义___: 自定义决策函数
+```
+决策id = 'test1'
+决策名 = '测试1'
+
+策略 {
+ 策略名 = '测试策略'
+ 规则 {
+ 规则名 = '使用函数'
+ 操作 {
+ 钉钉消息("发个消息")
+ }
+ }
+}
+
+函数定义("钉钉消息") {String msg ->
+ Utils.http()
+ .post("https://oapi.dingtalk.com/robot/send?access_token=7e9d8d97e6b5e76a6a07b0c5d7c31e82f0fbdb8ced1ac23168f9fd5c28c57f1a")
+ .jsonBody(JSON.toJSONString(
+ [msgtype: 'text', text: [content: "Fund: " + msg], at: ['isAtAll': false]]
+ )).debug().execute()
+}
+```
+
+
+
+
+# 指标/字段/属性
+## 使用 '年龄' 指标
+```
+拒绝 { 年龄 > 50 }
+```
+
+## 指标值获取
+
+
+
+## 指标列表
+
+
+# 收集器(数据源)
+> 支持从SQL(数据库),http(接口),script(自定义groovy脚本函数)获取数据
+## 收集器执行
+
+
+
+## 决策执行过程中生产的数据收集
+
+
+# 决策执行
+## 决策执行记录
+
+
+## 决策执行详情
+
+
+# 统计图
+## 决策执行统计
+
+
+## 决策规则统计
+
+
+
+# 权限
+> 静态权限: 权限管理, 用户管理, 用户登陆, 新增用户, 删除用户, 决策创建, 查看字段, 新增字段, 更新字段, 删除字段, 查看收集器, 新增收集器, 更新收集器, 删除收集器, 查看操作历史, 查看决策结果, 查看收集记录
+
+> 动态资源(决策)权限: 每添加一个决策 就会生成3个与之对应的动态权限(读, 删, 改)
+
+## 用户分类
+* 超级用户(系统管理员): 拥有 权限管理 的用户
+* 用户管理员(组管理员: 管理组相同的用户): 拥有 用户管理 的用户
+* 普通用户: 非 权限管理 和 用户管理 的用户
+
+
+
+## 权限作用域
+* 超级用户: 可以 修改任何 用户的任何权限
+* 用户管理员:
+ * 可以 修改同组 用户的 可分配(自身拥有的)的权限
+ * 可以分配同组用户创建的动态权限
+* 普通用户: 可以 操作 拥的每个权限对的 操作
+
+
+
+# 使用说明
+> 系统基于java轻量级框架: [Tiny](https://gitee.com/xnat/tiny)
+1. git clone https://gitee.com/xnat/grule.git
+2.
+ >+ linux: sh start.sh -Xmx512m -Xms512m -Dlog.totalSizeCap=50G
+ >+ windows: ./start -Xmx512m -Xms512m
+
+* api文档地址: http://localhost:9090/api-doc/
+
+### 环境要求
+jdk8, gradle7+
+
+### 可以更换自已的数据库 (默认用的h2)
+> 数据库要求: mysql5.7+, MariaDB10.2+
+
+```sql
+-- 创建数据库
+CREATE DATABASE IF NOT EXISTS rule DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
+```
+```properties
+# 替换 conf/app.properties 中的 jpa_rule.url 配置项
+jpa_rule.url=jdbc:mysql://localhost:3306/rule?useSSL=false&user=root&password=root&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true
+```
+
+### 开发环境运行
+> 需要添加Groovy sdk 3.0.8
+
+
+IntelliJ IDEA 运行 main.groovy 启动
+
+# v1.2.10 ing...
+- [x] refactor: 用 tiny DB 替换 groovy-sql
+- [ ] upgrade: tiny:1.1.3
+- [ ] upgrade: remoter:1.0.6
+- [ ] refactor: 接口地址: 去 mnt 前缀化
+- [ ] refactor: 接口地址: decision -> decision/decide/{decisionId}
+- [ ] refactor: 接口地址: findDecideResult -> decision/decide/result/{decideId}
+- [ ] refactor: 接口地址: xx/page?page= -> xx/page/{page}
+- [ ] refactor: 用户权限关系独立一张表
+- [ ] refactor: 接口 去mnt 公用化
+- [ ] feat: 新增 DSL SPEC: 接口
+- [ ] opt: 收集器缓存时间界面函数化
+- [ ] feat: vue2 -> vue3
+- [ ] feat: 创建用户 添加默认权限
+- [ ] feat: redis 收集器
+- [ ] upgrade: groovy 3.0.9
+- [ ] feat: 字符串 in 或者 contains 操作符
+
+
+# 参与贡献
+xnatural@msn.cn
\ No newline at end of file
diff --git a/bin/grape b/bin/grape
new file mode 100755
index 0000000000000000000000000000000000000000..0a5878b159480ae6376f1eb3ad6994cb760e66ee
--- /dev/null
+++ b/bin/grape
@@ -0,0 +1,48 @@
+#!/usr/bin/env sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+
+##############################################################################
+## ##
+## Groovy JVM Bootstrap for UN*X ##
+## ##
+## use -cp or -classpath just as in java to use a custom classpath ##
+## ##
+##############################################################################
+
+export GROOVY_APP_NAME=Grape
+
+# resolve links - $0 may be a soft-link
+PRG="$0"
+
+while [ -h "$PRG" ] ; do
+ ls="$(ls -ld "$PRG")"
+ link="$(expr "$ls" : '.*-> \(.*\)$')"
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="$(dirname "$PRG")"/"$link"
+ fi
+done
+
+DIRNAME="$(dirname "$PRG")"
+
+. "$DIRNAME/startGroovy"
+
+startGroovy org.codehaus.groovy.tools.GrapeMain "$@"
diff --git a/bin/grape.bat b/bin/grape.bat
new file mode 100644
index 0000000000000000000000000000000000000000..066a116c24950f0630d3fbf21987c4b7ac9579ae
--- /dev/null
+++ b/bin/grape.bat
@@ -0,0 +1,34 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@if "%DEBUG%" == "" @echo off
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+:begin
+@rem Determine what directory it is in.
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.\
+
+"%DIRNAME%\startGroovy.bat" "%DIRNAME%" org.codehaus.groovy.tools.GrapeMain %*
+
+@rem End local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" endlocal
+%COMSPEC% /C exit /B %ERRORLEVEL%
\ No newline at end of file
diff --git a/bin/grape_completion b/bin/grape_completion
new file mode 100755
index 0000000000000000000000000000000000000000..1c59b0c1435f582401711df9985022afedb81578
--- /dev/null
+++ b/bin/grape_completion
@@ -0,0 +1,181 @@
+#!/usr/bin/env bash
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+#
+# grape Bash Completion
+# =======================
+#
+# Bash completion support for the `grape` command,
+# generated by [picocli](http://picocli.info/) version 3.0.2.
+#
+# Installation
+# ------------
+#
+# 1. Source all completion scripts in your .bash_profile
+#
+# cd $GROOVY_HOME/bin
+# for f in $(find . -name "*_completion"); do line=". $(pwd)/$f"; grep "$line" ~/.bash_profile || echo "$line" >> ~/.bash_profile; done
+#
+# 2. Open a new bash console, and type `grape [TAB][TAB]`
+#
+# Documentation
+# -------------
+# The script is called by bash whenever [TAB] or [TAB][TAB] is pressed after
+# 'grape (..)'. By reading entered command line parameters,
+# it determines possible bash completions and writes them to the COMPREPLY variable.
+# Bash then completes the user input if only one entry is listed in the variable or
+# shows the options if more than one is listed in COMPREPLY.
+#
+# References
+# ----------
+# [1] http://stackoverflow.com/a/12495480/1440785
+# [2] http://tiswww.case.edu/php/chet/bash/FAQ
+# [3] https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
+# [4] https://stackoverflow.com/questions/17042057/bash-check-element-in-array-for-elements-in-another-array/17042655#17042655
+# [5] https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html#Programmable-Completion
+#
+
+# Enable programmable completion facilities (see [3])
+shopt -s progcomp
+
+# ArrContains takes two arguments, both of which are the name of arrays.
+# It creates a temporary hash from lArr1 and then checks if all elements of lArr2
+# are in the hashtable.
+#
+# Returns zero (no error) if all elements of the 2nd array are in the 1st array,
+# otherwise returns 1 (error).
+#
+# Modified from [4]
+function ArrContains() {
+ local lArr1 lArr2
+ declare -A tmp
+ eval lArr1=("\"\${$1[@]}\"")
+ eval lArr2=("\"\${$2[@]}\"")
+ for i in "${lArr1[@]}";{ [ -n "$i" ] && ((++tmp[$i]));}
+ for i in "${lArr2[@]}";{ [ -n "$i" ] && [ -z "${tmp[$i]}" ] && return 1;}
+ return 0
+}
+
+# Bash completion entry point function.
+# _complete_grape finds which commands and subcommands have been specified
+# on the command line and delegates to the appropriate function
+# to generate possible options and subcommands for the last specified subcommand.
+function _complete_grape() {
+ CMDS0=(install)
+ CMDS1=(uninstall)
+ CMDS2=(list)
+ CMDS3=(resolve)
+ CMDS4=(help)
+
+ ArrContains COMP_WORDS CMDS4 && { _picocli_grape_help; return $?; }
+ ArrContains COMP_WORDS CMDS3 && { _picocli_grape_resolve; return $?; }
+ ArrContains COMP_WORDS CMDS2 && { _picocli_grape_list; return $?; }
+ ArrContains COMP_WORDS CMDS1 && { _picocli_grape_uninstall; return $?; }
+ ArrContains COMP_WORDS CMDS0 && { _picocli_grape_install; return $?; }
+
+ # No subcommands were specified; generate completions for the top-level command.
+ _picocli_grape; return $?;
+}
+
+# Generates completions for the options and subcommands of the `grape` command.
+function _picocli_grape() {
+ # Get completion data
+ CURR_WORD=${COMP_WORDS[COMP_CWORD]}
+ PREV_WORD=${COMP_WORDS[COMP_CWORD-1]}
+
+ COMMANDS="install uninstall list resolve help"
+ FLAG_OPTS="-q --quiet -w --warn -i --info -V --verbose -d --debug -h --help -v --version"
+ ARG_OPTS="-D --define -r --resolver"
+
+ COMPREPLY=( $(compgen -W "${FLAG_OPTS} ${ARG_OPTS} ${COMMANDS}" -- ${CURR_WORD}) )
+}
+
+# Generates completions for the options and subcommands of the `install` subcommand.
+function _picocli_grape_install() {
+ # Get completion data
+ CURR_WORD=${COMP_WORDS[COMP_CWORD]}
+ PREV_WORD=${COMP_WORDS[COMP_CWORD-1]}
+
+ COMMANDS=""
+ FLAG_OPTS="-h --help -v --version"
+ ARG_OPTS=""
+
+ COMPREPLY=( $(compgen -W "${FLAG_OPTS} ${ARG_OPTS} ${COMMANDS}" -- ${CURR_WORD}) )
+}
+
+# Generates completions for the options and subcommands of the `uninstall` subcommand.
+function _picocli_grape_uninstall() {
+ # Get completion data
+ CURR_WORD=${COMP_WORDS[COMP_CWORD]}
+ PREV_WORD=${COMP_WORDS[COMP_CWORD-1]}
+
+ COMMANDS=""
+ FLAG_OPTS="-h --help -v --version"
+ ARG_OPTS=""
+
+ COMPREPLY=( $(compgen -W "${FLAG_OPTS} ${ARG_OPTS} ${COMMANDS}" -- ${CURR_WORD}) )
+}
+
+# Generates completions for the options and subcommands of the `list` subcommand.
+function _picocli_grape_list() {
+ # Get completion data
+ CURR_WORD=${COMP_WORDS[COMP_CWORD]}
+ PREV_WORD=${COMP_WORDS[COMP_CWORD-1]}
+
+ COMMANDS=""
+ FLAG_OPTS="-h --help -v --version"
+ ARG_OPTS=""
+
+ COMPREPLY=( $(compgen -W "${FLAG_OPTS} ${ARG_OPTS} ${COMMANDS}" -- ${CURR_WORD}) )
+}
+
+# Generates completions for the options and subcommands of the `resolve` subcommand.
+function _picocli_grape_resolve() {
+ # Get completion data
+ CURR_WORD=${COMP_WORDS[COMP_CWORD]}
+ PREV_WORD=${COMP_WORDS[COMP_CWORD-1]}
+
+ COMMANDS=""
+ FLAG_OPTS="-a --ant -d --dos -s --shell -i --ivy -h --help -v --version"
+ ARG_OPTS=""
+
+ COMPREPLY=( $(compgen -W "${FLAG_OPTS} ${ARG_OPTS} ${COMMANDS}" -- ${CURR_WORD}) )
+}
+
+# Generates completions for the options and subcommands of the `help` subcommand.
+function _picocli_grape_help() {
+ # Get completion data
+ CURR_WORD=${COMP_WORDS[COMP_CWORD]}
+ PREV_WORD=${COMP_WORDS[COMP_CWORD-1]}
+
+ COMMANDS=""
+ FLAG_OPTS="-h --help"
+ ARG_OPTS=""
+
+ COMPREPLY=( $(compgen -W "${FLAG_OPTS} ${ARG_OPTS} ${COMMANDS}" -- ${CURR_WORD}) )
+}
+
+# Define a completion specification (a compspec) for the
+# `grape`, `grape.sh`, and `grape.bash` commands.
+# Uses the bash `complete` builtin (see [5]) to specify that shell function
+# `_complete_grape` is responsible for generating possible completions for the
+# current word on the command line.
+# The `-o default` option means that if the function generated no matches, the
+# default Bash completions and the Readline default filename completions are performed.
+complete -F _complete_grape -o default grape grape.sh grape.bash
diff --git a/bin/groovy b/bin/groovy
new file mode 100755
index 0000000000000000000000000000000000000000..3ecca63463190fc8ac247a6e667d9e38f932d451
--- /dev/null
+++ b/bin/groovy
@@ -0,0 +1,48 @@
+#!/usr/bin/env sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+
+##############################################################################
+## ##
+## Groovy JVM Bootstrap for UN*X ##
+## ##
+## use -cp or -classpath just as in java to use a custom classpath ##
+## ##
+##############################################################################
+
+export GROOVY_APP_NAME=Groovy
+
+# resolve links - $0 may be a soft-link
+PRG="$0"
+
+while [ -h "$PRG" ] ; do
+ ls="$(ls -ld "$PRG")"
+ link="$(expr "$ls" : '.*-> \(.*\)$')"
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="$(dirname "$PRG")"/"$link"
+ fi
+done
+
+DIRNAME="$(dirname "$PRG")"
+
+. "$DIRNAME/startGroovy"
+
+startGroovy groovy.ui.GroovyMain "$@"
diff --git a/bin/groovy.bat b/bin/groovy.bat
new file mode 100644
index 0000000000000000000000000000000000000000..836e56ae1b328ed9bb68ea92fa112f7a0f6e1313
--- /dev/null
+++ b/bin/groovy.bat
@@ -0,0 +1,34 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@if "%DEBUG%" == "" @echo off
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+:begin
+@rem Determine what directory it is in.
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.\
+
+"%DIRNAME%\startGroovy.bat" "%DIRNAME%" groovy.ui.GroovyMain %*
+
+@rem End local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" endlocal
+%COMSPEC% /C exit /B %ERRORLEVEL%
\ No newline at end of file
diff --git a/bin/groovy.ico b/bin/groovy.ico
new file mode 100644
index 0000000000000000000000000000000000000000..9e9b8d81a21db6c99daf3cd76320407a45a1b0d5
Binary files /dev/null and b/bin/groovy.ico differ
diff --git a/bin/groovyConsole b/bin/groovyConsole
new file mode 100755
index 0000000000000000000000000000000000000000..b4fe404368f6058c0225de47e000599182ddfd23
--- /dev/null
+++ b/bin/groovyConsole
@@ -0,0 +1,46 @@
+#!/usr/bin/env sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+
+##############################################################################
+## ##
+## Groovy Console script for UN*X ##
+## ##
+##############################################################################
+
+export GROOVY_APP_NAME=GroovyConsole
+
+# resolve links - $0 may be a soft-link
+PRG="$0"
+
+while [ -h "$PRG" ] ; do
+ ls="$(ls -ld "$PRG")"
+ link="$(expr "$ls" : '.*-> \(.*\)$')"
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="$(dirname "$PRG")"/"$link"
+ fi
+done
+
+DIRNAME="$(dirname "$PRG")"
+
+. "$DIRNAME/startGroovy"
+
+startGroovy groovy.console.ui.Console "$@"
diff --git a/bin/groovyConsole.bat b/bin/groovyConsole.bat
new file mode 100644
index 0000000000000000000000000000000000000000..770910266dee072720bc8e3fbb24f925eba4f8ae
--- /dev/null
+++ b/bin/groovyConsole.bat
@@ -0,0 +1,34 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@if "%DEBUG%" == "" @echo off
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+:begin
+@rem Determine what directory it is in.
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.\
+
+"%DIRNAME%\startGroovy.bat" "%DIRNAME%" groovy.console.ui.Console %*
+
+@rem End local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" endlocal
+%COMSPEC% /C exit /B %ERRORLEVEL%
\ No newline at end of file
diff --git a/bin/groovyConsole_completion b/bin/groovyConsole_completion
new file mode 100755
index 0000000000000000000000000000000000000000..9803661a71ef3d72af12becce9dca090a7e68921
--- /dev/null
+++ b/bin/groovyConsole_completion
@@ -0,0 +1,106 @@
+#!/usr/bin/env bash
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+#
+# groovyConsole Bash Completion
+# =======================
+#
+# Bash completion support for the `groovyConsole` command,
+# generated by [picocli](http://picocli.info/) version 3.0.2.
+#
+# Installation
+# ------------
+#
+# 1. Source all completion scripts in your .bash_profile
+#
+# cd $GROOVY_HOME/bin
+# for f in $(find . -name "*_completion"); do line=". $(pwd)/$f"; grep "$line" ~/.bash_profile || echo "$line" >> ~/.bash_profile; done
+#
+# 2. Open a new bash console, and type `groovyConsole [TAB][TAB]`
+#
+# Documentation
+# -------------
+# The script is called by bash whenever [TAB] or [TAB][TAB] is pressed after
+# 'groovyConsole (..)'. By reading entered command line parameters,
+# it determines possible bash completions and writes them to the COMPREPLY variable.
+# Bash then completes the user input if only one entry is listed in the variable or
+# shows the options if more than one is listed in COMPREPLY.
+#
+# References
+# ----------
+# [1] http://stackoverflow.com/a/12495480/1440785
+# [2] http://tiswww.case.edu/php/chet/bash/FAQ
+# [3] https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
+# [4] https://stackoverflow.com/questions/17042057/bash-check-element-in-array-for-elements-in-another-array/17042655#17042655
+# [5] https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html#Programmable-Completion
+#
+
+# Enable programmable completion facilities (see [3])
+shopt -s progcomp
+
+# ArrContains takes two arguments, both of which are the name of arrays.
+# It creates a temporary hash from lArr1 and then checks if all elements of lArr2
+# are in the hashtable.
+#
+# Returns zero (no error) if all elements of the 2nd array are in the 1st array,
+# otherwise returns 1 (error).
+#
+# Modified from [4]
+function ArrContains() {
+ local lArr1 lArr2
+ declare -A tmp
+ eval lArr1=("\"\${$1[@]}\"")
+ eval lArr2=("\"\${$2[@]}\"")
+ for i in "${lArr1[@]}";{ [ -n "$i" ] && ((++tmp[$i]));}
+ for i in "${lArr2[@]}";{ [ -n "$i" ] && [ -z "${tmp[$i]}" ] && return 1;}
+ return 0
+}
+
+# Bash completion entry point function.
+# _complete_groovyConsole finds which commands and subcommands have been specified
+# on the command line and delegates to the appropriate function
+# to generate possible options and subcommands for the last specified subcommand.
+function _complete_groovyConsole() {
+
+
+ # No subcommands were specified; generate completions for the top-level command.
+ _picocli_groovyConsole; return $?;
+}
+
+# Generates completions for the options and subcommands of the `groovyConsole` command.
+function _picocli_groovyConsole() {
+ # Get completion data
+ CURR_WORD=${COMP_WORDS[COMP_CWORD]}
+ PREV_WORD=${COMP_WORDS[COMP_CWORD-1]}
+
+ COMMANDS=""
+ FLAG_OPTS="-cp -classpath --classpath -h --help -V --version -pa --parameters -pr --enable-preview -i --indy"
+ ARG_OPTS="-D --define --configscript"
+
+ COMPREPLY=( $(compgen -W "${FLAG_OPTS} ${ARG_OPTS} ${COMMANDS}" -- ${CURR_WORD}) )
+}
+
+# Define a completion specification (a compspec) for the
+# `groovyConsole`, `groovyConsole.sh`, and `groovyConsole.bash` commands.
+# Uses the bash `complete` builtin (see [5]) to specify that shell function
+# `_complete_groovyConsole` is responsible for generating possible completions for the
+# current word on the command line.
+# The `-o default` option means that if the function generated no matches, the
+# default Bash completions and the Readline default filename completions are performed.
+complete -F _complete_groovyConsole -o default groovyConsole groovyConsole.sh groovyConsole.bash
diff --git a/bin/groovy_completion b/bin/groovy_completion
new file mode 100755
index 0000000000000000000000000000000000000000..9fc1fa7eac7442e1e81e5bb725bbe58082c11e17
--- /dev/null
+++ b/bin/groovy_completion
@@ -0,0 +1,106 @@
+#!/usr/bin/env bash
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+#
+# groovy Bash Completion
+# =======================
+#
+# Bash completion support for the `groovy` command,
+# generated by [picocli](http://picocli.info/) version 3.0.2.
+#
+# Installation
+# ------------
+#
+# 1. Source all completion scripts in your .bash_profile
+#
+# cd $GROOVY_HOME/bin
+# for f in $(find . -name "*_completion"); do line=". $(pwd)/$f"; grep "$line" ~/.bash_profile || echo "$line" >> ~/.bash_profile; done
+#
+# 2. Open a new bash console, and type `groovy [TAB][TAB]`
+#
+# Documentation
+# -------------
+# The script is called by bash whenever [TAB] or [TAB][TAB] is pressed after
+# 'groovy (..)'. By reading entered command line parameters,
+# it determines possible bash completions and writes them to the COMPREPLY variable.
+# Bash then completes the user input if only one entry is listed in the variable or
+# shows the options if more than one is listed in COMPREPLY.
+#
+# References
+# ----------
+# [1] http://stackoverflow.com/a/12495480/1440785
+# [2] http://tiswww.case.edu/php/chet/bash/FAQ
+# [3] https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
+# [4] https://stackoverflow.com/questions/17042057/bash-check-element-in-array-for-elements-in-another-array/17042655#17042655
+# [5] https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html#Programmable-Completion
+#
+
+# Enable programmable completion facilities (see [3])
+shopt -s progcomp
+
+# ArrContains takes two arguments, both of which are the name of arrays.
+# It creates a temporary hash from lArr1 and then checks if all elements of lArr2
+# are in the hashtable.
+#
+# Returns zero (no error) if all elements of the 2nd array are in the 1st array,
+# otherwise returns 1 (error).
+#
+# Modified from [4]
+function ArrContains() {
+ local lArr1 lArr2
+ declare -A tmp
+ eval lArr1=("\"\${$1[@]}\"")
+ eval lArr2=("\"\${$2[@]}\"")
+ for i in "${lArr1[@]}";{ [ -n "$i" ] && ((++tmp[$i]));}
+ for i in "${lArr2[@]}";{ [ -n "$i" ] && [ -z "${tmp[$i]}" ] && return 1;}
+ return 0
+}
+
+# Bash completion entry point function.
+# _complete_groovy finds which commands and subcommands have been specified
+# on the command line and delegates to the appropriate function
+# to generate possible options and subcommands for the last specified subcommand.
+function _complete_groovy() {
+
+
+ # No subcommands were specified; generate completions for the top-level command.
+ _picocli_groovy; return $?;
+}
+
+# Generates completions for the options and subcommands of the `groovy` command.
+function _picocli_groovy() {
+ # Get completion data
+ CURR_WORD=${COMP_WORDS[COMP_CWORD]}
+ PREV_WORD=${COMP_WORDS[COMP_CWORD-1]}
+
+ COMMANDS=""
+ FLAG_OPTS="-d --debug -n -p -pa --parameters -pr --enable-preview --indy -h --help -v --version"
+ ARG_OPTS="-cp -classpath --classpath -D --define --disableopt -c --encoding -e -i -l -a --autosplit --configscript -b --basescript"
+
+ COMPREPLY=( $(compgen -W "${FLAG_OPTS} ${ARG_OPTS} ${COMMANDS}" -- ${CURR_WORD}) )
+}
+
+# Define a completion specification (a compspec) for the
+# `groovy`, `groovy.sh`, and `groovy.bash` commands.
+# Uses the bash `complete` builtin (see [5]) to specify that shell function
+# `_complete_groovy` is responsible for generating possible completions for the
+# current word on the command line.
+# The `-o default` option means that if the function generated no matches, the
+# default Bash completions and the Readline default filename completions are performed.
+complete -F _complete_groovy -o default groovy groovy.sh groovy.bash
diff --git a/bin/groovyc b/bin/groovyc
new file mode 100755
index 0000000000000000000000000000000000000000..b224f3fb35a3f720ce6a34415b7890655e3e85fa
--- /dev/null
+++ b/bin/groovyc
@@ -0,0 +1,46 @@
+#!/usr/bin/env sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+
+##############################################################################
+## ##
+## Groovy JVM Bootstrap for UN*X ##
+## ##
+##############################################################################
+
+export GROOVY_APP_NAME=GroovyC
+
+# resolve links - $0 may be a soft-link
+PRG="$0"
+
+while [ -h "$PRG" ] ; do
+ ls="$(ls -ld "$PRG")"
+ link="$(expr "$ls" : '.*-> \(.*\)$')"
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="$(dirname "$PRG")"/"$link"
+ fi
+done
+
+DIRNAME="$(dirname "$PRG")"
+
+. "$DIRNAME/startGroovy"
+
+startGroovy org.codehaus.groovy.tools.FileSystemCompiler "$@"
diff --git a/bin/groovyc.bat b/bin/groovyc.bat
new file mode 100644
index 0000000000000000000000000000000000000000..df3bd1934f15fda8062cfcb92a0878109d2ec106
--- /dev/null
+++ b/bin/groovyc.bat
@@ -0,0 +1,34 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@if "%DEBUG%" == "" @echo off
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+:begin
+@rem Determine what directory it is in.
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.\
+
+"%DIRNAME%\startGroovy.bat" "%DIRNAME%" org.codehaus.groovy.tools.FileSystemCompiler %*
+
+@rem End local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" endlocal
+%COMSPEC% /C exit /B %ERRORLEVEL%
\ No newline at end of file
diff --git a/bin/groovyc_completion b/bin/groovyc_completion
new file mode 100755
index 0000000000000000000000000000000000000000..d4591f1782b2e2c1407324da37cf8800a3fd3006
--- /dev/null
+++ b/bin/groovyc_completion
@@ -0,0 +1,142 @@
+#!/usr/bin/env bash
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+#
+# groovyc Bash Completion
+# =======================
+#
+# Bash completion support for the `groovyc` command,
+# generated by [picocli](http://picocli.info/) version 3.0.2.
+#
+# Installation
+# ------------
+#
+# 1. Source all completion scripts in your .bash_profile
+#
+# cd $GROOVY_HOME/bin
+# for f in $(find . -name "*_completion"); do line=". $(pwd)/$f"; grep "$line" ~/.bash_profile || echo "$line" >> ~/.bash_profile; done
+#
+# 2. Open a new bash console, and type `groovyc [TAB][TAB]`
+#
+# Documentation
+# -------------
+# The script is called by bash whenever [TAB] or [TAB][TAB] is pressed after
+# 'groovyc (..)'. By reading entered command line parameters,
+# it determines possible bash completions and writes them to the COMPREPLY variable.
+# Bash then completes the user input if only one entry is listed in the variable or
+# shows the options if more than one is listed in COMPREPLY.
+#
+# References
+# ----------
+# [1] http://stackoverflow.com/a/12495480/1440785
+# [2] http://tiswww.case.edu/php/chet/bash/FAQ
+# [3] https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
+# [4] https://stackoverflow.com/questions/17042057/bash-check-element-in-array-for-elements-in-another-array/17042655#17042655
+# [5] https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html#Programmable-Completion
+#
+
+# Enable programmable completion facilities (see [3])
+shopt -s progcomp
+
+# ArrContains takes two arguments, both of which are the name of arrays.
+# It creates a temporary hash from lArr1 and then checks if all elements of lArr2
+# are in the hashtable.
+#
+# Returns zero (no error) if all elements of the 2nd array are in the 1st array,
+# otherwise returns 1 (error).
+#
+# Modified from [4]
+function ArrContains() {
+ local lArr1 lArr2
+ declare -A tmp
+ eval lArr1=("\"\${$1[@]}\"")
+ eval lArr2=("\"\${$2[@]}\"")
+ for i in "${lArr1[@]}";{ [ -n "$i" ] && ((++tmp[$i]));}
+ for i in "${lArr2[@]}";{ [ -n "$i" ] && [ -z "${tmp[$i]}" ] && return 1;}
+ return 0
+}
+
+# Bash completion entry point function.
+# _complete_groovyc finds which commands and subcommands have been specified
+# on the command line and delegates to the appropriate function
+# to generate possible options and subcommands for the last specified subcommand.
+function _complete_groovyc() {
+
+
+ # No subcommands were specified; generate completions for the top-level command.
+ _picocli_groovyc; return $?;
+}
+
+# Generates completions for the options and subcommands of the `groovyc` command.
+function _picocli_groovyc() {
+ # Get completion data
+ CURR_WORD=${COMP_WORDS[COMP_CWORD]}
+ PREV_WORD=${COMP_WORDS[COMP_CWORD-1]}
+
+ COMMANDS=""
+ FLAG_OPTS="-e --exception -pa --parameters -pr --enable-preview -j --jointCompilation --indy -h --help -v --version"
+ ARG_OPTS="-cp -classpath --classpath -sourcepath --sourcepath --temp --encoding -d -b --basescript -J -F --configscript"
+
+ case ${CURR_WORD} in
+ -sourcepath|--sourcepath)
+ compopt -o filenames
+ COMPREPLY=( $( compgen -f -- "" ) ) # files
+ return $?
+ ;;
+ --temp)
+ compopt -o filenames
+ COMPREPLY=( $( compgen -f -- "" ) ) # files
+ return $?
+ ;;
+ -d)
+ compopt -o filenames
+ COMPREPLY=( $( compgen -f -- "" ) ) # files
+ return $?
+ ;;
+ *)
+ case ${PREV_WORD} in
+ -sourcepath|--sourcepath)
+ compopt -o filenames
+ COMPREPLY=( $( compgen -f -- $CURR_WORD ) ) # files
+ return $?
+ ;;
+ --temp)
+ compopt -o filenames
+ COMPREPLY=( $( compgen -f -- $CURR_WORD ) ) # files
+ return $?
+ ;;
+ -d)
+ compopt -o filenames
+ COMPREPLY=( $( compgen -f -- $CURR_WORD ) ) # files
+ return $?
+ ;;
+ esac
+ esac
+
+ COMPREPLY=( $(compgen -W "${FLAG_OPTS} ${ARG_OPTS} ${COMMANDS}" -- ${CURR_WORD}) )
+}
+
+# Define a completion specification (a compspec) for the
+# `groovyc`, `groovyc.sh`, and `groovyc.bash` commands.
+# Uses the bash `complete` builtin (see [5]) to specify that shell function
+# `_complete_groovyc` is responsible for generating possible completions for the
+# current word on the command line.
+# The `-o default` option means that if the function generated no matches, the
+# default Bash completions and the Readline default filename completions are performed.
+complete -F _complete_groovyc -o default groovyc groovyc.sh groovyc.bash
diff --git a/bin/groovydoc b/bin/groovydoc
new file mode 100755
index 0000000000000000000000000000000000000000..094ac0e7e87bb960048b25a8a9e78225a1fedaeb
--- /dev/null
+++ b/bin/groovydoc
@@ -0,0 +1,46 @@
+#!/usr/bin/env sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+
+##############################################################################
+## ##
+## Groovy JVM Bootstrap for UN*X ##
+## ##
+##############################################################################
+
+export GROOVY_APP_NAME=GroovyDoc
+
+# resolve links - $0 may be a soft-link
+PRG="$0"
+
+while [ -h "$PRG" ] ; do
+ ls="$(ls -ld "$PRG")"
+ link="$(expr "$ls" : '.*-> \(.*\)$')"
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="$(dirname "$PRG")"/"$link"
+ fi
+done
+
+DIRNAME="$(dirname "$PRG")"
+
+. "$DIRNAME/startGroovy"
+
+startGroovy org.codehaus.groovy.tools.groovydoc.Main "$@"
diff --git a/bin/groovydoc.bat b/bin/groovydoc.bat
new file mode 100644
index 0000000000000000000000000000000000000000..a1ad6c37e096ddde6508e48d27c889894d6467fc
--- /dev/null
+++ b/bin/groovydoc.bat
@@ -0,0 +1,34 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@if "%DEBUG%" == "" @echo off
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+:begin
+@rem Determine what directory it is in.
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.\
+
+"%DIRNAME%\startGroovy.bat" "%DIRNAME%" org.codehaus.groovy.tools.groovydoc.Main %*
+
+@rem End local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" endlocal
+%COMSPEC% /C exit /B %ERRORLEVEL%
\ No newline at end of file
diff --git a/bin/groovydoc_completion b/bin/groovydoc_completion
new file mode 100755
index 0000000000000000000000000000000000000000..afcdfc3e3dceb583cf2a287272df45f26adeb3df
--- /dev/null
+++ b/bin/groovydoc_completion
@@ -0,0 +1,106 @@
+#!/usr/bin/env bash
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+#
+# groovydoc Bash Completion
+# =======================
+#
+# Bash completion support for the `groovydoc` command,
+# generated by [picocli](http://picocli.info/) version 3.0.2.
+#
+# Installation
+# ------------
+#
+# 1. Source all completion scripts in your .bash_profile
+#
+# cd $GROOVY_HOME/bin
+# for f in $(find . -name "*_completion"); do line=". $(pwd)/$f"; grep "$line" ~/.bash_profile || echo "$line" >> ~/.bash_profile; done
+#
+# 2. Open a new bash console, and type `groovydoc [TAB][TAB]`
+#
+# Documentation
+# -------------
+# The script is called by bash whenever [TAB] or [TAB][TAB] is pressed after
+# 'groovydoc (..)'. By reading entered command line parameters,
+# it determines possible bash completions and writes them to the COMPREPLY variable.
+# Bash then completes the user input if only one entry is listed in the variable or
+# shows the options if more than one is listed in COMPREPLY.
+#
+# References
+# ----------
+# [1] http://stackoverflow.com/a/12495480/1440785
+# [2] http://tiswww.case.edu/php/chet/bash/FAQ
+# [3] https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
+# [4] https://stackoverflow.com/questions/17042057/bash-check-element-in-array-for-elements-in-another-array/17042655#17042655
+# [5] https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html#Programmable-Completion
+#
+
+# Enable programmable completion facilities (see [3])
+shopt -s progcomp
+
+# ArrContains takes two arguments, both of which are the name of arrays.
+# It creates a temporary hash from lArr1 and then checks if all elements of lArr2
+# are in the hashtable.
+#
+# Returns zero (no error) if all elements of the 2nd array are in the 1st array,
+# otherwise returns 1 (error).
+#
+# Modified from [4]
+function ArrContains() {
+ local lArr1 lArr2
+ declare -A tmp
+ eval lArr1=("\"\${$1[@]}\"")
+ eval lArr2=("\"\${$2[@]}\"")
+ for i in "${lArr1[@]}";{ [ -n "$i" ] && ((++tmp[$i]));}
+ for i in "${lArr2[@]}";{ [ -n "$i" ] && [ -z "${tmp[$i]}" ] && return 1;}
+ return 0
+}
+
+# Bash completion entry point function.
+# _complete_groovydoc finds which commands and subcommands have been specified
+# on the command line and delegates to the appropriate function
+# to generate possible options and subcommands for the last specified subcommand.
+function _complete_groovydoc() {
+
+
+ # No subcommands were specified; generate completions for the top-level command.
+ _picocli_groovydoc; return $?;
+}
+
+# Generates completions for the options and subcommands of the `groovydoc` command.
+function _picocli_groovydoc() {
+ # Get completion data
+ CURR_WORD=${COMP_WORDS[COMP_CWORD]}
+ PREV_WORD=${COMP_WORDS[COMP_CWORD-1]}
+
+ COMMANDS=""
+ FLAG_OPTS="-h -help --help --version -verbose -quiet --debug -classpath -cp --classpath -author -noscripts -nomainforscripts -notimestamp -noversionstamp -public -protected -package -private"
+ ARG_OPTS="-d --destdir -overview -charset -fileEncoding -windowtitle -doctitle -header -footer -exclude -stylesheetfile -sourcepath"
+
+ COMPREPLY=( $(compgen -W "${FLAG_OPTS} ${ARG_OPTS} ${COMMANDS}" -- ${CURR_WORD}) )
+}
+
+# Define a completion specification (a compspec) for the
+# `groovydoc`, `groovydoc.sh`, and `groovydoc.bash` commands.
+# Uses the bash `complete` builtin (see [5]) to specify that shell function
+# `_complete_groovydoc` is responsible for generating possible completions for the
+# current word on the command line.
+# The `-o default` option means that if the function generated no matches, the
+# default Bash completions and the Readline default filename completions are performed.
+complete -F _complete_groovydoc -o default groovydoc groovydoc.sh groovydoc.bash
diff --git a/bin/groovysh b/bin/groovysh
new file mode 100755
index 0000000000000000000000000000000000000000..d1d01ba43549d107b121ccfe1e3c4b491f21a87c
--- /dev/null
+++ b/bin/groovysh
@@ -0,0 +1,46 @@
+#!/usr/bin/env sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+
+##############################################################################
+## ##
+## Groovy Shell script for UN*X ##
+## ##
+##############################################################################
+
+export GROOVY_APP_NAME=GroovyShell
+
+# resolve links - $0 may be a soft-link
+PRG="$0"
+
+while [ -h "$PRG" ] ; do
+ ls="$(ls -ld "$PRG")"
+ link="$(expr "$ls" : '.*-> \(.*\)$')"
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="$(dirname "$PRG")"/"$link"
+ fi
+done
+
+DIRNAME="$(dirname "$PRG")"
+
+. "$DIRNAME/startGroovy"
+
+startGroovy org.apache.groovy.groovysh.Main "$@"
diff --git a/bin/groovysh.bat b/bin/groovysh.bat
new file mode 100644
index 0000000000000000000000000000000000000000..e23e8a553a3f887974cd0ba36cf413a8c8ab73bb
--- /dev/null
+++ b/bin/groovysh.bat
@@ -0,0 +1,36 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@if "%DEBUG%" == "" @echo off
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+:begin
+@rem Determine what directory it is in.
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.\
+
+set CLASSNAME=org.apache.groovy.groovysh.Main
+
+"%DIRNAME%\startGroovy.bat" "%DIRNAME%" %CLASSNAME% %*
+
+@rem End local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" endlocal
+%COMSPEC% /C exit /B %ERRORLEVEL%
\ No newline at end of file
diff --git a/bin/groovysh_completion b/bin/groovysh_completion
new file mode 100755
index 0000000000000000000000000000000000000000..a175fbe59d42af000c70192c06f1ef3904e691bd
--- /dev/null
+++ b/bin/groovysh_completion
@@ -0,0 +1,103 @@
+#!/usr/bin/env bash
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+#
+# groovysh Bash Completion
+# =======================
+#
+# Bash completion support for the `groovysh` command,
+# generated by [picocli](http://picocli.info/) version 3.0.2.
+#
+# 1. Source all completion scripts in your .bash_profile
+#
+# cd $GROOVY_HOME/bin
+# for f in $(find . -name "*_completion"); do line=". $(pwd)/$f"; grep "$line" ~/.bash_profile || echo "$line" >> ~/.bash_profile; done
+#
+# 2. Open a new bash console, and type `groovysh [TAB][TAB]`
+#
+# Documentation
+# -------------
+# The script is called by bash whenever [TAB] or [TAB][TAB] is pressed after
+# 'groovysh (..)'. By reading entered command line parameters,
+# it determines possible bash completions and writes them to the COMPREPLY variable.
+# Bash then completes the user input if only one entry is listed in the variable or
+# shows the options if more than one is listed in COMPREPLY.
+#
+# References
+# ----------
+# [1] http://stackoverflow.com/a/12495480/1440785
+# [2] http://tiswww.case.edu/php/chet/bash/FAQ
+# [3] https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
+# [4] https://stackoverflow.com/questions/17042057/bash-check-element-in-array-for-elements-in-another-array/17042655#17042655
+# [5] https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html#Programmable-Completion
+#
+
+# Enable programmable completion facilities (see [3])
+shopt -s progcomp
+
+# ArrContains takes two arguments, both of which are the name of arrays.
+# It creates a temporary hash from lArr1 and then checks if all elements of lArr2
+# are in the hashtable.
+#
+# Returns zero (no error) if all elements of the 2nd array are in the 1st array,
+# otherwise returns 1 (error).
+#
+# Modified from [4]
+function ArrContains() {
+ local lArr1 lArr2
+ declare -A tmp
+ eval lArr1=("\"\${$1[@]}\"")
+ eval lArr2=("\"\${$2[@]}\"")
+ for i in "${lArr1[@]}";{ [ -n "$i" ] && ((++tmp[$i]));}
+ for i in "${lArr2[@]}";{ [ -n "$i" ] && [ -z "${tmp[$i]}" ] && return 1;}
+ return 0
+}
+
+# Bash completion entry point function.
+# _complete_groovysh finds which commands and subcommands have been specified
+# on the command line and delegates to the appropriate function
+# to generate possible options and subcommands for the last specified subcommand.
+function _complete_groovysh() {
+
+
+ # No subcommands were specified; generate completions for the top-level command.
+ _picocli_groovysh; return $?;
+}
+
+# Generates completions for the options and subcommands of the `groovysh` command.
+function _picocli_groovysh() {
+ # Get completion data
+ CURR_WORD=${COMP_WORDS[COMP_CWORD]}
+ PREV_WORD=${COMP_WORDS[COMP_CWORD-1]}
+
+ COMMANDS=""
+ FLAG_OPTS="-cp -classpath --classpath -h --help -V --version -v --verbose -q --quiet -d --debug -pr --enable-preview -pa --parameters"
+ ARG_OPTS="-e --evaluate -C --color -D --define -T --terminal"
+
+ COMPREPLY=( $(compgen -W "${FLAG_OPTS} ${ARG_OPTS} ${COMMANDS}" -- ${CURR_WORD}) )
+}
+
+# Define a completion specification (a compspec) for the
+# `groovysh`, `groovysh.sh`, and `groovysh.bash` commands.
+# Uses the bash `complete` builtin (see [5]) to specify that shell function
+# `_complete_groovysh` is responsible for generating possible completions for the
+# current word on the command line.
+# The `-o default` option means that if the function generated no matches, the
+# default Bash completions and the Readline default filename completions are performed.
+complete -F _complete_groovysh -o default groovysh groovysh.sh groovysh.bash
diff --git a/bin/java2groovy b/bin/java2groovy
new file mode 100755
index 0000000000000000000000000000000000000000..c4801933d84b581daf6bcdfea9d7c522f1d1dec3
--- /dev/null
+++ b/bin/java2groovy
@@ -0,0 +1,51 @@
+#!/usr/bin/env sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+
+##############################################################################
+## ##
+## Groovy JVM Bootstrap for UN*X ##
+## ##
+##############################################################################
+
+##
+## $Revision: 4241 $
+## $Date: 2006-11-16 17:30:40 +0000 (Thu, 16 Nov 2006) $
+##
+
+export GROOVY_APP_NAME=Java2Groovy
+
+# resolve links - $0 may be a soft-link
+PRG="$0"
+
+while [ -h "$PRG" ] ; do
+ ls="$(ls -ld "$PRG")"
+ link="$(expr "$ls" : '.*-> \(.*\)$')"
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="$(dirname "$PRG")"/"$link"
+ fi
+done
+
+DIRNAME="$(dirname "$PRG")"
+
+. "$DIRNAME/startGroovy"
+
+startGroovy org.codehaus.groovy.antlr.java.Java2GroovyMain "$@"
diff --git a/bin/java2groovy.bat b/bin/java2groovy.bat
new file mode 100644
index 0000000000000000000000000000000000000000..c137b393f001058c085763d9f027ed5f698382bb
--- /dev/null
+++ b/bin/java2groovy.bat
@@ -0,0 +1,38 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@if "%DEBUG%" == "" @echo off
+
+@rem
+@rem $Revision: 2770 $ $Date: 2005-08-29 11:49:42 +0100 (Mon, 29 Aug 2005) $
+@rem
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+:begin
+@rem Determine what directory it is in.
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.\
+
+"%DIRNAME%\startGroovy.bat" "%DIRNAME%" org.codehaus.groovy.antlr.java.Java2GroovyMain %*
+
+@rem End local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" endlocal
+%COMSPEC% /C exit /B %ERRORLEVEL%
\ No newline at end of file
diff --git a/bin/startGroovy b/bin/startGroovy
new file mode 100755
index 0000000000000000000000000000000000000000..d072626d7d6cf30d0267deb5566dc587be7cf6dc
--- /dev/null
+++ b/bin/startGroovy
@@ -0,0 +1,309 @@
+#!/usr/bin/env sh
+# -*- mode:sh -*-
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+
+##############################################################################
+## ##
+## Groovy JVM Bootstrap for UN*X ##
+## ##
+##############################################################################
+
+PROGNAME="$(basename "$0")"
+
+SCRIPT_PATH="$0"
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "${PROGNAME}: $*"
+}
+
+die ( ) {
+ warn "$*"
+ exit 1
+}
+
+earlyInit ( ) {
+ return
+}
+
+lateInit ( ) {
+ return
+}
+
+GROOVY_STARTUP="$HOME/.groovy/startup"
+if [ -r "$GROOVY_STARTUP" ] ; then
+ . "$GROOVY_STARTUP"
+fi
+
+earlyInit
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+linux=false
+case "$(uname)" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+ Linux* )
+ linux=true
+ ;;
+esac
+
+if [ "$1" = "-cp" ] || [ "$1" = "-classpath" ] || [ "$1" = "--classpath" ] ; then
+ CP=$2
+ shift 2
+fi
+
+REPLACE_PREVIEW=
+if [ "$1" = "-pr" ] || [ "$1" = "--enable-preview" ] ; then
+ JAVA_OPTS="$JAVA_OPTS --enable-preview -Dgroovy.preview.features=true"
+ # for now, also remember arg to pass through
+ REPLACE_PREVIEW="--enable-preview"
+ shift 1
+fi
+
+# Attempt to set JAVA_HOME if it's not already set.
+if [ -z "$JAVA_HOME" ] ; then
+ if $darwin ; then
+ [ -z "$JAVA_HOME" ] && [ -f "/usr/libexec/java_home" ] && JAVA_HOME="$(/usr/libexec/java_home)"
+ [ -z "$JAVA_HOME" ] && [ -d "/Library/Java/Home" ] && JAVA_HOME="/Library/Java/Home"
+ [ -z "$JAVA_HOME" ] && [ -d "/System/Library/Frameworks/JavaVM.framework/Home" ] && JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Home"
+ else
+ javaExecutable="$(which javac)"
+ [ -z "$javaExecutable" ] || [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ] && die "JAVA_HOME not set and cannot find javac to deduce location, please set JAVA_HOME."
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink="$(which readlink)"
+ [ "$(expr "$readLink" : '\([^ ]*\)')" = "no" ] && die "JAVA_HOME not set and readlink not available, please set JAVA_HOME."
+ javaExecutable="$(readlink -f "$javaExecutable")"
+ javaHome="$(dirname "$javaExecutable")"
+ javaHome="$(expr "$javaHome" : '\(.*\)/bin')"
+ JAVA_HOME="$javaHome"
+ fi
+
+ export JAVA_HOME
+fi
+
+# GROOVY-9875: backspace does not work in the SecureCRT connecting Linux
+if $linux ; then
+ export TERM=linux
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$GROOVY_HOME" ] && GROOVY_HOME="$(cygpath --unix "$GROOVY_HOME")"
+ [ -n "$JAVACMD" ] && JAVACMD="$(cygpath --unix "$JAVACMD")"
+ [ -n "$JAVA_HOME" ] && JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
+ [ -n "$CP" ] && CP=$(cygpath --path --unix "$CP")
+else
+ if [ -n "$GROOVY_HOME" ] && [ "$(expr "$GROOVY_HOME" : '\/$')" ] ; then
+ GROOVY_HOME="$(echo "$GROOVY_HOME" | sed -e 's/\/$//')"
+ fi
+fi
+
+# For MSYS, ensure paths are in appropriate format.
+if $msys
+then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME="$(cd "$JAVA_HOME" ; pwd)"
+fi
+
+# Attempt to set GROOVY_HOME if it is not already set.
+if [ -z "$GROOVY_HOME" ] || [ ! -d "$GROOVY_HOME" ] ; then
+ # Resolve links: $0 may be a link to groovy's home.
+ PRG="$0"
+ # Need this for relative symlinks.
+ while [ -h "$PRG" ] ; do
+ ls="$(ls -ld "$PRG")"
+ link="$(expr "$ls" : '.*-> \(.*\)$')"
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="$(dirname "$PRG")""/$link"
+ fi
+ done
+ SAVED="$(pwd)"
+ cd "$(dirname "$PRG")/.."
+ GROOVY_HOME="$(pwd -P)"
+ cd "$SAVED"
+fi
+
+# Set the default Groovy config if no specific one is mentioned.
+if [ -z "$GROOVY_CONF" ] ; then
+ GROOVY_CONF="$GROOVY_HOME/conf/groovy-starter.conf"
+fi
+STARTER_CLASSPATH="$GROOVY_HOME/lib/groovy-3.0.8.jar"
+
+# Create the final classpath. Setting a classpath using the -cp or -classpath option means not to use the
+# global classpath. Groovy behaves then the same as the java interpreter
+if [ -n "$CP" ] ; then
+ CP="$CP":.
+elif [ -n "$CLASSPATH" ] ; then
+ CP="$CLASSPATH":.
+else
+ CP=.
+fi
+
+# Determine the Java command to use to start the JVM.
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="java"
+ fi
+fi
+if [ ! -x "$JAVACMD" ] ; then
+ die "JAVA_HOME is not defined correctly, can not execute: $JAVACMD"
+fi
+if [ -z "$JAVA_HOME" ] ; then
+ warn "JAVA_HOME environment variable is not set"
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" ] && [ "$darwin" = "false" ] && [ "$nonstop" = "false" ] ; then
+ # disable ulimit shellcheck warnings. while only ulimit -f is technicaly
+ # POSIX correct virtually every shell on every *nix supports these options
+ # shellcheck disable=SC2039
+ MAX_FD_LIMIT="$(ulimit -H -n)"
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" ] || [ "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ # shellcheck disable=SC2039
+ ulimit -n "$MAX_FD"
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query businessSystem maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# Setup Profiler
+useprofiler=false
+if [ "$PROFILER" != "" ] ; then
+ if [ -r "$PROFILER" ] ; then
+ . "$PROFILER"
+ useprofiler=true
+ else
+ die "Profiler file not found: $PROFILER"
+ fi
+fi
+
+# For Darwin, use classes.jar for TOOLS_JAR
+TOOLS_JAR="$JAVA_HOME/lib/tools.jar"
+#if $darwin; then
+# TOOLS_JAR="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Classes/classes.jar"
+#fi
+
+# For Darwin, add GROOVY_APP_NAME to the JAVA_OPTS as -Xdock:name
+if "$darwin"; then
+ JAVA_OPTS="$JAVA_OPTS -Xdock:name=$GROOVY_APP_NAME -Xdock:icon=$GROOVY_HOME/lib/groovy.icns"
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if "$cygwin"; then
+ GROOVY_HOME="$(cygpath --mixed "$GROOVY_HOME")"
+ JAVA_HOME="$(cygpath --mixed "$JAVA_HOME")"
+ GROOVY_CONF="$(cygpath --mixed "$GROOVY_CONF")"
+ CP="$(cygpath --path --mixed "$CP")"
+ TOOLS_JAR="$(cygpath --mixed "$TOOLS_JAR")"
+ STARTER_CLASSPATH="$(cygpath --path --mixed "$STARTER_CLASSPATH")"
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW="$(find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null)"
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GROOVY_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GROOVY_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ argsstr=''
+ for arg in "$@" ; do
+ CHECK="$(echo "$arg"|egrep -c "$OURCYGPATTERN" -)"
+ if [ "$CHECK" -ne 0 ] ; then
+ patched="$(cygpath --path --ignore --mixed "$arg")"
+ else
+ patched="$arg"
+ fi
+ argsstr="$argsstr \"$patched\""
+ done
+
+ eval "set -- $argsstr"
+fi
+
+startGroovy ( ) {
+ JAVA_VERSION="$("$JAVACMD" -version 2>&1 | awk -F '"' '/version/ {print $2}' | cut -d "_" -f1)"
+ if [ "$(expr "$JAVA_VERSION" \> "1.8.0")" ]; then
+ JAVA_OPTS="$JAVA_OPTS -Dgroovy.jaxb=jaxb"
+ [ "$GROOVY_TURN_OFF_JAVA_WARNINGS" = "true" ] && JAVA_OPTS="$JAVA_OPTS --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.annotation=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.lang.module=ALL-UNNAMED --add-opens=java.base/java.lang.ref=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.math=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.net.spi=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.nio.channels=ALL-UNNAMED --add-opens=java.base/java.nio.channels.spi=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.nio.charset.spi=ALL-UNNAMED --add-opens=java.base/java.nio.file=ALL-UNNAMED --add-opens=java.base/java.nio.file.attribute=ALL-UNNAMED --add-opens=java.base/java.nio.file.spi=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.security.acl=ALL-UNNAMED --add-opens=java.base/java.security.cert=ALL-UNNAMED --add-opens=java.base/java.security.interfaces=ALL-UNNAMED --add-opens=java.base/java.security.spec=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.base/java.text.spi=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED --add-opens=java.base/java.time.chrono=ALL-UNNAMED --add-opens=java.base/java.time.format=ALL-UNNAMED --add-opens=java.base/java.time.temporal=ALL-UNNAMED --add-opens=java.base/java.time.zone=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED --add-opens=java.base/java.util.function=ALL-UNNAMED --add-opens=java.base/java.util.jar=ALL-UNNAMED --add-opens=java.base/java.util.regex=ALL-UNNAMED --add-opens=java.base/java.util.spi=ALL-UNNAMED --add-opens=java.base/java.util.stream=ALL-UNNAMED --add-opens=java.base/java.util.zip=ALL-UNNAMED --add-opens=java.datatransfer/java.awt.datatransfer=ALL-UNNAMED --add-opens=java.desktop/java.applet=ALL-UNNAMED --add-opens=java.desktop/java.awt=ALL-UNNAMED --add-opens=java.desktop/java.awt.color=ALL-UNNAMED --add-opens=java.desktop/java.awt.desktop=ALL-UNNAMED --add-opens=java.desktop/java.awt.dnd=ALL-UNNAMED --add-opens=java.desktop/java.awt.dnd.peer=ALL-UNNAMED --add-opens=java.desktop/java.awt.event=ALL-UNNAMED --add-opens=java.desktop/java.awt.font=ALL-UNNAMED --add-opens=java.desktop/java.awt.geom=ALL-UNNAMED --add-opens=java.desktop/java.awt.im=ALL-UNNAMED --add-opens=java.desktop/java.awt.im.spi=ALL-UNNAMED --add-opens=java.desktop/java.awt.image=ALL-UNNAMED --add-opens=java.desktop/java.awt.image.renderable=ALL-UNNAMED --add-opens=java.desktop/java.awt.peer=ALL-UNNAMED --add-opens=java.desktop/java.awt.print=ALL-UNNAMED --add-opens=java.desktop/java.beans=ALL-UNNAMED --add-opens=java.desktop/java.beans.beancontext=ALL-UNNAMED --add-opens=java.instrument/java.lang.instrument=ALL-UNNAMED --add-opens=java.logging/java.util.logging=ALL-UNNAMED --add-opens=java.management/java.lang.management=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.rmi/java.rmi=ALL-UNNAMED --add-opens=java.rmi/java.rmi.activation=ALL-UNNAMED --add-opens=java.rmi/java.rmi.dgc=ALL-UNNAMED --add-opens=java.rmi/java.rmi.registry=ALL-UNNAMED --add-opens=java.rmi/java.rmi.server=ALL-UNNAMED --add-opens=java.sql/java.sql=ALL-UNNAMED --add-opens=java.desktop/javax.swing=ALL-UNNAMED --add-opens=java.desktop/javax.swing.border=ALL-UNNAMED --add-opens=java.desktop/javax.swing.text=ALL-UNNAMED --add-opens=java.desktop/javax.swing.text.html=ALL-UNNAMED --add-opens=java.desktop/sun.awt=ALL-UNNAMED --add-opens=java.desktop/sun.java2d=ALL-UNNAMED --add-opens=java.desktop/sun.font=ALL-UNNAMED"
+ # sun.awt.windows just on windows platforms
+ if [ $msys = 'true' ] || [ $cygwin = 'true' ]; then
+ [ "$GROOVY_TURN_OFF_JAVA_WARNINGS" = "true" ] && JAVA_OPTS="$JAVA_OPTS --add-opens=java.desktop/sun.awt.windows=ALL-UNNAMED"
+ fi
+ fi
+
+ CLASS="$1"
+ shift
+ # Start the Profiler or the JVM
+ if [ "$useprofiler" = true ] ; then
+ runProfiler
+ else
+ # shellcheck disable=SC2086
+ exec "$JAVACMD" $JAVA_OPTS \
+ -classpath "$STARTER_CLASSPATH" \
+ -Dscript.name="$SCRIPT_PATH" \
+ -Dprogram.name="$PROGNAME" \
+ -Dgroovy.starter.conf="$GROOVY_CONF" \
+ -Dgroovy.home="$GROOVY_HOME" \
+ -Dtools.jar="$TOOLS_JAR" \
+ "$STARTER_MAIN_CLASS" \
+ --main "$CLASS" \
+ --conf "$GROOVY_CONF" \
+ --classpath "$CP" $REPLACE_PREVIEW \
+ "$@"
+ fi
+}
+
+STARTER_MAIN_CLASS=org.codehaus.groovy.tools.GroovyStarter
+
+lateInit
diff --git a/bin/startGroovy.bat b/bin/startGroovy.bat
new file mode 100644
index 0000000000000000000000000000000000000000..04d7ef6976053065cc6dcd4646dab436c855d076
--- /dev/null
+++ b/bin/startGroovy.bat
@@ -0,0 +1,309 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem ##
+@rem Groovy JVM Bootstrap for Windows ##
+@rem ##
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal enabledelayedexpansion
+
+set DIRNAME=%~1
+shift
+
+set CLASS=%~1
+shift
+
+if exist "%USERPROFILE%/.groovy/preinit.bat" call "%USERPROFILE%/.groovy/preinit.bat"
+
+@rem Determine the command interpreter to execute the "CD" later
+set COMMAND_COM="cmd.exe"
+if exist "%SystemRoot%\system32\cmd.exe" set COMMAND_COM="%SystemRoot%\system32\cmd.exe"
+if exist "%SystemRoot%\command.com" set COMMAND_COM="%SystemRoot%\command.com"
+
+@rem Use explicit find.exe to prevent cygwin and others find.exe from being used
+set FIND_EXE="find.exe"
+if exist "%SystemRoot%\system32\find.exe" set FIND_EXE="%SystemRoot%\system32\find.exe"
+if exist "%SystemRoot%\command\find.exe" set FIND_EXE="%SystemRoot%\command\find.exe"
+
+:check_JAVA_HOME
+@rem Make sure we have a valid JAVA_HOME
+if not "%JAVA_HOME%" == "" goto have_JAVA_HOME
+set PATHTMP=%PATH%
+:loop
+for /f "delims=; tokens=1*" %%i in ("!PATHTMP!") do (
+ if exist "%%i\..\bin\java.exe" (
+ set "JAVA_HOME=%%i\.."
+ goto found_JAVA_HOME
+ )
+ set PATHTMP=%%j
+ goto loop
+)
+goto check_default_JAVA_EXE
+
+:found_JAVA_HOME
+@rem Remove trailing \bin\.. from JAVA_HOME
+if "%JAVA_HOME:~-7%"=="\bin\.." SET "JAVA_HOME=%JAVA_HOME:~0,-7%"
+set JAVA_EXE=%JAVA_HOME%\bin\java.exe
+
+:check_default_JAVA_EXE
+if not "%JAVA_HOME%" == "" goto valid_JAVA_HOME
+java -version 2>NUL
+if not ERRORLEVEL 1 goto default_JAVA_EXE
+
+echo.
+echo ERROR: Environment variable JAVA_HOME has not been set.
+echo Attempting to find JAVA_HOME from PATH also failed.
+goto common_error
+
+:have_JAVA_HOME
+@rem Remove trailing slash from JAVA_HOME if found
+if "%JAVA_HOME:~-1%"=="\" SET JAVA_HOME=%JAVA_HOME:~0,-1%
+
+@rem Validate JAVA_HOME
+%COMMAND_COM% /C DIR "%JAVA_HOME%" 2>&1 | %FIND_EXE% /I /C "%JAVA_HOME%" >nul
+if not errorlevel 1 goto valid_JAVA_HOME_DIR
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+
+:common_error
+echo Please set the JAVA_HOME variable in your environment
+echo to match the location of your Java installation.
+goto end
+
+:default_JAVA_EXE
+set JAVA_EXE=java.exe
+goto check_GROOVY_HOME
+
+:valid_JAVA_HOME_DIR
+set JAVA_EXE=%JAVA_HOME%\bin\java.exe
+if exist "%JAVA_EXE%" goto valid_JAVA_HOME
+
+echo.
+echo ERROR: No java.exe found at: %JAVA_EXE%
+goto common_error
+
+:valid_JAVA_HOME
+if exist "%JAVA_HOME%\lib\tools.jar" set TOOLS_JAR=%JAVA_HOME%\lib\tools.jar
+
+:check_GROOVY_HOME
+@rem Define GROOVY_HOME if not set
+if "%GROOVY_HOME%" == "" set GROOVY_HOME=%DIRNAME%..
+
+@rem Remove trailing slash from GROOVY_HOME if found
+if "%GROOVY_HOME:~-1%"=="\" SET GROOVY_HOME=%GROOVY_HOME:~0,-1%
+
+@rem classpath handling
+set _SKIP=2
+set CP=
+if "x%~1" == "x-cp" set CP=%~2
+if "x%~1" == "x-classpath" set CP=%~2
+if "x%~1" == "x--classpath" set CP=%~2
+if "x" == "x%CP%" goto preview_check
+set _SKIP=4
+shift
+shift
+
+:preview_check
+@rem classpath handling
+set PREV=
+set REPLACE_PREVIEW=
+if "x%~1" == "x-pr" goto preview_enable
+if "x%~1" == "x--enable-preview" goto preview_enable
+goto init
+
+:preview_enable
+set JAVA_OPTS=%JAVA_OPTS% --enable-preview -Dgroovy.preview.features=true
+@rem for now, also remember arg to pass through
+set REPLACE_PREVIEW=--enable-preview
+if "%_SKIP%" == "4" set _SKIP=5
+if "%_SKIP%" == "2" set _SKIP=3
+shift
+
+:init
+@rem get name of script to launch with full path
+set GROOVY_SCRIPT_NAME=%~f1
+@rem Get command-line arguments, handling Windows variants
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+rem horrible roll your own arg processing inspired by jruby equivalent
+
+rem escape minus (-d), quotes (-q), star (-s).
+set _ARGS=%*
+if not defined _ARGS goto execute
+set _ARGS=%_ARGS:-=-d%
+set _ARGS=%_ARGS:"=-q%
+set _ARGS=%_ARGS:?=-n%
+rem Windows will try to match * with files so we escape it here
+rem but it is also a meta char for env var string substitution
+rem so it can't be first char here, hack just for common cases.
+rem If in doubt use a space or bracket before * if using -e.
+set _ARGS=%_ARGS: *= -s%
+set _ARGS=%_ARGS:)*=)-s%
+set _ARGS=%_ARGS:0*=0-s%
+set _ARGS=%_ARGS:1*=1-s%
+set _ARGS=%_ARGS:2*=2-s%
+set _ARGS=%_ARGS:3*=3-s%
+set _ARGS=%_ARGS:4*=4-s%
+set _ARGS=%_ARGS:5*=5-s%
+set _ARGS=%_ARGS:6*=6-s%
+set _ARGS=%_ARGS:7*=7-s%
+set _ARGS=%_ARGS:8*=8-s%
+set _ARGS=%_ARGS:9*=9-s%
+
+rem prequote all args for 'for' statement
+set _ARGS="%_ARGS%"
+
+set _ARG=
+:win9xME_args_loop
+rem split args by spaces into first and rest
+for /f "tokens=1,*" %%i in (%_ARGS%) do call :get_arg "%%i" "%%j"
+goto process_arg
+
+:get_arg
+rem remove quotes around first arg and don't expand wildcards
+for /f %%i in (%1) do set _ARG=%_ARG% %%~i
+rem set the remaining args
+set _ARGS=%2
+rem remove the leading space we'll add the first time
+if "x%_ARG:~0,1%" == "x " set _ARG=%_ARG:~1%
+rem return
+goto :EOF
+
+:process_arg
+if "%_ARG%" == "" goto execute
+
+rem collect all parts of a quoted argument containing spaces
+if not "%_ARG:~0,2%" == "-q" goto :argIsComplete
+if "%_ARG:~-2%" == "-q" goto :argIsComplete
+rem _ARG starts with a quote but does not end with one:
+rem add the next part to _ARG until the matching quote is found
+goto :win9xME_args_loop
+
+:argIsComplete
+if "x5" == "x%_SKIP%" goto skip_5
+if "x4" == "x%_SKIP%" goto skip_4
+if "x3" == "x%_SKIP%" goto skip_3
+if "x2" == "x%_SKIP%" goto skip_2
+if "x1" == "x%_SKIP%" goto skip_1
+
+rem now unescape -s, -q, -n, -d
+rem -d must be the last to be unescaped
+set _ARG=%_ARG:-s=*%
+set _ARG=%_ARG:-q="%
+set _ARG=%_ARG:-n=?%
+set _ARG=%_ARG:-d=-%
+
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %_ARG%
+set _ARG=
+goto win9xME_args_loop
+
+:skip_5
+set _ARG=
+set _SKIP=4
+goto win9xME_args_loop
+
+:skip_4
+set _ARG=
+set _SKIP=3
+goto win9xME_args_loop
+
+:skip_3
+set _ARG=
+set _SKIP=2
+goto win9xME_args_loop
+
+:skip_2
+set _ARG=
+set _SKIP=1
+goto win9xME_args_loop
+
+:skip_1
+set _ARG=
+set _SKIP=0
+goto win9xME_args_loop
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+set STARTER_CLASSPATH=%GROOVY_HOME%\lib\groovy-3.0.8.jar
+
+if exist "%USERPROFILE%/.groovy/init.bat" call "%USERPROFILE%/.groovy/init.bat"
+
+@rem Setting a classpath using the -cp or -classpath option means not to use
+@rem the global classpath. Groovy behaves then the same as the java
+@rem interpreter
+if "x" == "x%CP%" goto empty_cp
+:non_empty_cp
+set CP=%CP%;.
+goto after_cp
+:empty_cp
+set CP=.
+if "x" == "x%CLASSPATH%" goto after_cp
+set CP=%CLASSPATH%;%CP%
+:after_cp
+
+set STARTER_MAIN_CLASS=org.codehaus.groovy.tools.GroovyStarter
+set STARTER_CONF=%GROOVY_HOME%\conf\groovy-starter.conf
+
+set GROOVY_OPTS=-Dprogram.name="%PROGNAME%"
+set GROOVY_OPTS=%GROOVY_OPTS% -Dgroovy.home="%GROOVY_HOME%"
+if not "%TOOLS_JAR%" == "" set GROOVY_OPTS=%GROOVY_OPTS% -Dtools.jar="%TOOLS_JAR%"
+set GROOVY_OPTS=%GROOVY_OPTS% -Dgroovy.starter.conf="%STARTER_CONF%"
+set GROOVY_OPTS=%GROOVY_OPTS% -Dscript.name="%GROOVY_SCRIPT_NAME%"
+
+for /f "tokens=3" %%g in ('call "%JAVA_EXE%" -version 2^>^&1 ^| findstr /i "version"') do (
+ SET JAVA_VERSION=%%g
+)
+for /f "useback tokens=*" %%a in ('%JAVA_VERSION%') do set JAVA_VERSION=%%~a
+set JAVA_VERSION=%JAVA_VERSION:~0,5%
+set ADD_MODULES_OPT=--add-modules
+if "%JAVA_VERSION%" gtr "1.8.0" set JAVA_OPTS=%JAVA_OPTS% -Dgroovy.jaxb=jaxb
+if "%JAVA_VERSION%" gtr "1.8.0" if "%GROOVY_TURN_OFF_JAVA_WARNINGS%" == "true" set JAVA_OPTS=%JAVA_OPTS% --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.annotation=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.lang.module=ALL-UNNAMED --add-opens=java.base/java.lang.ref=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.math=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.net.spi=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.nio.channels=ALL-UNNAMED --add-opens=java.base/java.nio.channels.spi=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.nio.charset.spi=ALL-UNNAMED --add-opens=java.base/java.nio.file=ALL-UNNAMED --add-opens=java.base/java.nio.file.attribute=ALL-UNNAMED --add-opens=java.base/java.nio.file.spi=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.security.acl=ALL-UNNAMED --add-opens=java.base/java.security.cert=ALL-UNNAMED --add-opens=java.base/java.security.interfaces=ALL-UNNAMED --add-opens=java.base/java.security.spec=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.base/java.text.spi=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED --add-opens=java.base/java.time.chrono=ALL-UNNAMED --add-opens=java.base/java.time.format=ALL-UNNAMED --add-opens=java.base/java.time.temporal=ALL-UNNAMED --add-opens=java.base/java.time.zone=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED --add-opens=java.base/java.util.function=ALL-UNNAMED --add-opens=java.base/java.util.jar=ALL-UNNAMED --add-opens=java.base/java.util.regex=ALL-UNNAMED --add-opens=java.base/java.util.spi=ALL-UNNAMED --add-opens=java.base/java.util.stream=ALL-UNNAMED --add-opens=java.base/java.util.zip=ALL-UNNAMED --add-opens=java.datatransfer/java.awt.datatransfer=ALL-UNNAMED --add-opens=java.desktop/java.applet=ALL-UNNAMED --add-opens=java.desktop/java.awt=ALL-UNNAMED --add-opens=java.desktop/java.awt.color=ALL-UNNAMED --add-opens=java.desktop/java.awt.desktop=ALL-UNNAMED --add-opens=java.desktop/java.awt.dnd=ALL-UNNAMED --add-opens=java.desktop/java.awt.dnd.peer=ALL-UNNAMED --add-opens=java.desktop/java.awt.event=ALL-UNNAMED --add-opens=java.desktop/java.awt.font=ALL-UNNAMED --add-opens=java.desktop/java.awt.geom=ALL-UNNAMED --add-opens=java.desktop/java.awt.im=ALL-UNNAMED --add-opens=java.desktop/java.awt.im.spi=ALL-UNNAMED --add-opens=java.desktop/java.awt.image=ALL-UNNAMED --add-opens=java.desktop/java.awt.image.renderable=ALL-UNNAMED --add-opens=java.desktop/java.awt.peer=ALL-UNNAMED --add-opens=java.desktop/java.awt.print=ALL-UNNAMED --add-opens=java.desktop/java.beans=ALL-UNNAMED --add-opens=java.desktop/java.beans.beancontext=ALL-UNNAMED --add-opens=java.instrument/java.lang.instrument=ALL-UNNAMED --add-opens=java.logging/java.util.logging=ALL-UNNAMED --add-opens=java.management/java.lang.management=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.rmi/java.rmi=ALL-UNNAMED --add-opens=java.rmi/java.rmi.activation=ALL-UNNAMED --add-opens=java.rmi/java.rmi.dgc=ALL-UNNAMED --add-opens=java.rmi/java.rmi.registry=ALL-UNNAMED --add-opens=java.rmi/java.rmi.server=ALL-UNNAMED --add-opens=java.sql/java.sql=ALL-UNNAMED --add-opens=java.desktop/javax.swing=ALL-UNNAMED --add-opens=java.desktop/javax.swing.border=ALL-UNNAMED --add-opens=java.desktop/javax.swing.text=ALL-UNNAMED --add-opens=java.desktop/javax.swing.text.html=ALL-UNNAMED --add-opens=java.desktop/sun.awt.windows=ALL-UNNAMED --add-opens=java.desktop/sun.awt=ALL-UNNAMED --add-opens=java.desktop/sun.java2d=ALL-UNNAMED --add-opens=java.desktop/sun.font=ALL-UNNAMED
+
+if exist "%USERPROFILE%/.groovy/postinit.bat" call "%USERPROFILE%/.groovy/postinit.bat"
+
+@rem GROOVY-6453: groovysh in Windows 7/8/10 doesn't support arrow keys and Del
+set JAVA_OPTS=%JAVA_OPTS% -Djline.terminal=none
+
+@rem Execute Groovy
+"%JAVA_EXE%" %GROOVY_OPTS% %JAVA_OPTS% -classpath "%STARTER_CLASSPATH%" %STARTER_MAIN_CLASS% --main %CLASS% --conf "%STARTER_CONF%" --classpath "%CP%" %REPLACE_PREVIEW% %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" endlocal
+
+@rem Optional pause the batch file
+if "%GROOVY_BATCH_PAUSE%" == "on" pause
+%COMSPEC% /C exit /B %ERRORLEVEL%
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..9969bea162fe2241210a5118e37c6303397e45dd
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,130 @@
+repositories {
+ mavenLocal()
+ maven { url 'https://maven.aliyun.com/repository/central' }
+ maven { url 'https://maven.aliyun.com/repository/public' }
+ mavenCentral()
+}
+
+version = '1.2.10'
+
+//apply plugin: 'java'
+apply plugin: 'groovy'
+
+
+compileGroovy { // 保留方法参数的名字(非arg0/arg1...)
+ groovyOptions.parameters = true
+}
+
+
+// 修改源目录
+sourceSets {
+ main {
+ groovy {
+ srcDir 'src'
+ }
+ resources {
+ srcDir 'src/static'
+ }
+ }
+}
+
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
+
+
+ext {
+ groovyVersion = '3.0.8'
+}
+dependencies {
+ implementation fileTree(dir: 'sysLib', include: ['*.jar'])
+ implementation "org.codehaus.groovy:groovy:$groovyVersion"
+ implementation "org.codehaus.groovy:groovy-templates:$groovyVersion"
+ // implementation "org.codehaus.groovy:groovy-sql:$groovyVersion"
+ implementation "org.codehaus.groovy:groovy-json:$groovyVersion"
+ // implementation "org.codehaus.groovy:groovy-jmx:$groovyVersion"
+
+ //implementation 'com.alibaba:fastjson:1.2.73'
+
+ implementation 'cn.xnatural:tiny:1.1.2'
+ implementation 'cn.xnatural:http:1.1.0'
+ implementation 'cn.xnatural:jpa:1.0.8'
+ implementation 'cn.xnatural:sched:1.0.3'
+ implementation 'cn.xnatural:remoter:1.0.5'
+ implementation 'cn.xnatural.task:task:1.0.2'
+
+ // 连接池
+ // implementation 'com.alibaba:druid:1.1.23'
+ // implementation 'com.zaxxer:HikariCP:3.4.1'
+ // implementation 'org.apache.commons:commons-dbcp2:2.7.0'
+ implementation 'mysql:mysql-connector-java:8.0.23'
+ // implementation 'mysql:mysql-connector-java:5.1.49'
+ implementation 'org.mariadb.jdbc:mariadb-java-client:2.1.2'
+ implementation 'com.h2database:h2:1.4.199'
+
+ // implementation 'org.apache.commons:commons-email:1.5'
+ // implementation 'io.netty:netty-all:4.1.49.Final'
+ implementation 'com.squareup.okhttp3:okhttp:3.14.9'
+ // implementation('org.apache.httpcomponents:httpclient:4.5.12')
+ // implementation ('org.ehcache:ehcache:3.8.1') {exclude group:'org.slf4j'}
+ implementation 'redis.clients:jedis:3.3.0'
+ implementation 'org.jsoup:jsoup:1.13.1'
+
+ // 分布式数据一致框架
+ // implementation 'io.atomix:atomix:3.1.8'
+ // implementation 'org.apache.zookeeper:zookeeper:3.6.2'
+
+ // 工作流
+// implementation('org.activiti:activiti-engine:7.0.0.GA') {
+// exclude group: 'org.mybatis'
+// exclude group: 'org.springframework'
+// }
+
+ // 规则引擎
+// implementation('org.drools:drools-core:7.37.0.Final')
+// implementation('com.alibaba:QLExpress:3.2.4')
+
+ // jvm 脚本语言
+ // implementation('com.googlecode.aviator:aviator:5.2.3')
+}
+
+
+// 依赖copy
+task deps(type: Copy) {
+ new File('lib').deleteDir()
+ from configurations.runtimeClasspath
+ into 'lib'
+}
+
+
+task tgz(dependsOn: ['deps'], type: Tar) {
+ new File("${buildDir}/grule.tgz").deleteOnExit()
+
+ into("bin") {from("bin")}
+ into("conf") {from "conf"}
+ into("src") {from("src")}
+ into("lib") {from "lib"}
+ from "Dockerfile"
+ from "build.gradle"
+ from "start.bat"
+ from("start.sh")
+
+ archiveFileName = "grule.tgz"
+ compression(Compression.GZIP)
+ destinationDirectory = file("${buildDir}")
+}
+
+
+task tarDir(dependsOn: ['deps'], type: Copy) {
+ new File("${buildDir}/grule").deleteDir()
+
+ into("bin") {from("bin")}
+ into("conf") {from "conf"}
+ into("src") {from("src")}
+ into("lib") {from "lib"}
+ from "Dockerfile"
+ from "build.gradle"
+ from "start.bat"
+ from("start.sh")
+
+ into "${buildDir}/grule"
+}
\ No newline at end of file
diff --git a/conf/app-ali.properties b/conf/app-ali.properties
new file mode 100644
index 0000000000000000000000000000000000000000..6763038c8c1584f7d4a92ccc2aef62aeb6c0ba97
--- /dev/null
+++ b/conf/app-ali.properties
@@ -0,0 +1,38 @@
+sys.exec.corePoolSize=4
+sys.exec.maximumPoolSize=4
+
+#### 文件上传本地存放目录
+fileUploader.accessUrlPrefix=//xnatural.cn/rule/file/
+
+
+#### rule 数据源
+jpa_rule.url=jdbc:mariadb://localhost:3306/rule?useSSL=false&user=root&password=root&useUnicode=true&characterEncoding=utf-8
+jpa_rule.minIdle=1
+jpa_rule.maxActive=5
+jpa_rule.hibernate.hbm2ddl.auto=update
+#jpa_rule.hibernate.show_sql=true
+jpa_rule.druid.timeBetweenEvictionRunsMillis=7200000
+#生成sql的时候会将字段,也就是操作的列自动加上`,就不存在和mysql关键字冲突了
+#jpa_rule.hibernate.globally_quoted_identifiers=true
+
+
+#### web 配置
+web.hp=:9090
+# web.session.type=redis
+
+
+#### 集群(remoter)配置
+remoter.hp=:9001
+remoter.masterHps=xnatural.cn:8001
+#remoter.master=true
+remoter.exposeTcp=xnatural.cn${remoter.hp}
+remoter.exposeHttp=xnatural.cn${web.hp}
+
+
+#### cache配置
+# 最大缓存条数
+cacheSrv.itemLimit=100
+
+#### 历史记录清理配置
+decisionSrv.decideRecord.keepCount=70000
+decisionSrv.optimizeTable=true
\ No newline at end of file
diff --git a/conf/app.properties b/conf/app.properties
new file mode 100644
index 0000000000000000000000000000000000000000..153631ca61735e0b2b9120fab0c20e8235d390c0
--- /dev/null
+++ b/conf/app.properties
@@ -0,0 +1,55 @@
+#### 系统名字(集群时会用到)
+sys.name=grule
+
+
+#### 系统线程池(工作能力)大小配置
+sys.exec.corePoolSize=8
+sys.exec.maximumPoolSize=32
+
+
+#### rule 数据源
+jpa_rule.url=jdbc:h2:${user.dir}/../h2/data;user=root;password=root
+#jpa_rule.url=jdbc:mysql://localhost:3306/rule?useSSL=false&user=root&password=root&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true
+jpa_rule.minIdle=1
+jpa_rule.maxActive=5
+jpa_rule.testWhileIdle=true
+jpa_rule.validationQuery=select 1
+jpa_rule.hibernate.hbm2ddl.auto=update
+# druid 连接池多久过期一个连接 DruidAbstractDataSource#testConnectionInternal
+jpa_rule.druid.timeBetweenEvictionRunsMillis=1800000
+### 是否打印sql
+#jpa_rule.hibernate.show_sql=true
+
+
+#### 文件上传本地存放目录
+#fileUploader.localDir=d:/tmp/upload
+
+
+#### 事件追踪(调试)
+#ep.track=bean.get
+
+
+#### web 配置
+web.hp=:9090
+# web.session.type=redis
+# session 过期时间. 单位: 分钟
+web.session.expire=2880
+# 接口超时日志告警时间. 一般比较长. 1分钟
+web.logWarnTimeout=60000
+
+
+#### redis 相关配置
+# redis.host=localhost
+# redis.password=xxx
+# redis.database=1
+
+
+#### 集群(remoter)配置
+remoter.hp=:9001
+remoter.masterHps=
+
+
+#### 历史记录清理配置
+decisionSrv.decideRecord.keepCount=999999
+# 数据清理后 是否 optimize table
+decisionSrv.optimizeTable=false
\ No newline at end of file
diff --git a/conf/groovy-starter.conf b/conf/groovy-starter.conf
new file mode 100644
index 0000000000000000000000000000000000000000..7f9d2f3852f37c98de075c3d821a697c8da44a23
--- /dev/null
+++ b/conf/groovy-starter.conf
@@ -0,0 +1,39 @@
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements. See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You 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.
+
+##############################################################################
+## ##
+## Groovy Classloading Configuration ##
+## ##
+##############################################################################
+
+## Note: do not add classes from java.lang here. No rt.jar and on some
+## platforms no tools.jar
+##
+## See http://docs.groovy-lang.org/docs/latest/html/api/org/codehaus/groovy/tools/LoaderConfiguration.html
+## for the file format
+
+ # load required libraries
+ load !{groovy.home}/lib/*.jar
+
+ # load jaxb libraries if needed (ignored if not found)
+ load !{groovy.home}/lib/extras-${groovy.jaxb}/*.jar
+
+ # load user specific libraries
+ load !{user.home}/.groovy/lib/*.jar
+
+ # tools.jar for ant tasks
+ load ${tools.jar}
+
diff --git a/conf/openApi.json b/conf/openApi.json
new file mode 100644
index 0000000000000000000000000000000000000000..6e748d4e610a9735fd8858e6cc6f0d9c7a5ed955
--- /dev/null
+++ b/conf/openApi.json
@@ -0,0 +1,533 @@
+{
+ "openapi": "3.0.0",
+ "info": {
+ "title": "GRule API",
+ "description": "GRule",
+ "contact": {
+ "email": "xnatural@msn.cn"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ },
+ "version": "1.0.0"
+ },
+ "servers": [
+ {
+ "url": "http://localhost:9090",
+ "description": "local"
+ }
+ ],
+ "tags": [
+ {
+ "name": "rule",
+ "description": "rule api"
+ },
+ {
+ "name": "test",
+ "description": "test api"
+ },
+ {
+ "name": "main",
+ "description": "main api"
+ }
+ ],
+ "paths": {
+ "/decision": {
+ "post": {
+ "tags": ["rule"],
+ "summary": "决策执行",
+ "description": "决策执行",
+ "requestBody": {
+ "description": "表单数据",
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "decisionId": {
+ "type": "string", "required": true, "description": "决策id"
+ },
+ "async": {
+ "type": "boolean", "required": false, "description": "是否异步执行决策"
+ },
+ "callback": {
+ "type": "string", "required": false, "description": "异步执行决策完成后的回调接口地址"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "成功返回",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "$ref": "#/components/schemas/ApiResp"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/findDecideResult": {
+ "get": {
+ "tags": ["rule"],
+ "summary": "查询决策结果",
+ "description": "一般用于异步决策查询",
+ "parameters": [
+ {
+ "name": "decideId",
+ "in": "query",
+ "description": "决策结果id",
+ "required": true,
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "成功返回",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "$ref": "#/components/schemas/ApiResp"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/test/dao": {
+ "post": {
+ "tags": ["test"],
+ "summary": "数据库操作接口",
+ "description": "数据库操作接口",
+ "parameters": [
+ {
+ "name": "type",
+ "in": "query",
+ "description": "type=file 就查文件那个表",
+ "required": false,
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "成功返回",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "$ref": "#/components/schemas/ApiResp"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/test/form": {
+ "post": {
+ "tags": ["test"],
+ "summary": "表单接口",
+ "description": "接收表单提交",
+ "requestBody": {
+ "description": "表单数据",
+ "required": false,
+ "content": {
+ "application/x-www-form-urlencoded": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "param1": {
+ "type": "string"
+ },
+ "p2": {
+ "type": "number"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "成功返回",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "$ref": "#/components/schemas/ApiResp"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/test/json": {
+ "post": {
+ "tags": ["test"],
+ "summary": "json接口",
+ "description": "接收json提交",
+ "requestBody": {
+ "description": "json数据",
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "p1": {
+ "type": "number"
+ },
+ "p2": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "成功返回",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "$ref": "#/components/schemas/ApiResp"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/test/upload": {
+ "post": {
+ "tags": ["test"],
+ "summary": "文件上传",
+ "description": "文件上传",
+ "requestBody": {
+ "description": "文件",
+ "required": true,
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "file": {
+ "required": true,
+ "type": "file",
+ "format": "binary"
+ },
+ "version": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "成功返回",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "$ref": "#/components/schemas/ApiResp"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/test/pieceUpload": {
+ "post": {
+ "tags": ["test"],
+ "summary": "分片上传",
+ "description": "文件上传: 持续上传/分片上传",
+ "requestBody": {
+ "description": "文件",
+ "required": true,
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "filePiece": {
+ "required": true,
+ "type": "file",
+ "format": "binary"
+ },
+ "fileId": {
+ "type": "string"
+ },
+ "originName": {
+ "type": "string"
+ },
+ "totalPiece": {
+ "type": "number",
+ "format": "int"
+ },
+ "currentPiece": {
+ "type": "number",
+ "format": "int"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "成功返回",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "$ref": "#/components/schemas/ApiResp"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/test/remote": {
+ "get": {
+ "tags": ["test"],
+ "summary": "远程调用",
+ "description": "ep 远程调用",
+ "parameters": [
+ {
+ "name": "app",
+ "in": "query",
+ "description": "app 应用名",
+ "required": false,
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "event",
+ "in": "query",
+ "description": "事件名",
+ "required": false,
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "p1",
+ "in": "query",
+ "description": "参数p1",
+ "required": false,
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "成功返回",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "$ref": "#/components/schemas/ApiResp"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/test/http": {
+ "get": {
+ "tags": ["test"],
+ "summary": "http 代理请求",
+ "description": "http 代理请求",
+ "parameters": [
+ {
+ "name": "url",
+ "in": "query",
+ "description": "代理发送http请求的地址",
+ "required": false,
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "成功返回",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "$ref": "#/components/schemas/ApiResp"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/test/async": {
+ "get": {
+ "tags": ["test"],
+ "summary": "异步响应",
+ "description": "后台 异步响应",
+ "parameters": [
+
+ ],
+ "responses": {
+ "200": {
+ "description": "成功返回",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "$ref": "#/components/schemas/ApiResp"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/test/cus": {
+ "get": {
+ "tags": ["test"],
+ "summary": "自定义返回接口",
+ "description": "返回自定义数据结构",
+ "parameters": [
+
+ ],
+ "responses": {
+ "200": {
+ "description": "成功返回",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "$ref": "#/components/schemas/ApiResp"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/test/error": {
+ "get": {
+ "tags": ["test"],
+ "summary": "测试抛出错误",
+ "description": "抛出错误 接口",
+ "parameters": [
+
+ ],
+ "responses": {
+ "200": {
+ "description": "成功返回",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "$ref": "#/components/schemas/ApiResp"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/test/timeout": {
+ "get": {
+ "tags": ["test"],
+ "summary": "超时接口",
+ "description": "测试超时接口 让接口超时返回",
+ "parameters": [
+ {
+ "name": "timeout",
+ "in": "query",
+ "description": "设置超时时间(接口多久返回) 默认10秒",
+ "required": false,
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "integer",
+ "format": "int32"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "成功返回",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "$ref": "#/components/schemas/ApiResp"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ApiResp": {
+ "required": ["code"],
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "string",
+ "example": "00"
+ },
+ "desc": {
+ "type": "string",
+ "example": "成功"
+ },
+ "data": {
+ "type": "object"
+ },
+ "traceNo": {
+ "type": "string",
+ "format": "uuid",
+ "example": "2d59326c4ad844a6b3c866fba159a44a"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000000000000000000000000000000000..7454180f2ae8848c63b8b4dea2cb829da983f2fa
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000000000000000000000000000000000..05679dc3c18669869e21a57ce911ccfe56352d0d
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000000000000000000000000000000000000..744e882ed57263a19bf3a504977da292d009345f
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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
+#
+# https://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.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MSYS* | MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000000000000000000000000000000000000..107acd32c4e687021ef32db511e8a206129b88ec
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/src/core/HttpSrv.groovy b/src/core/HttpSrv.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..3e49ab6eec374fbf2f00e82b615940c4667162c5
--- /dev/null
+++ b/src/core/HttpSrv.groovy
@@ -0,0 +1,187 @@
+package core
+
+import cn.xnatural.app.CacheSrv
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.enet.event.EL
+import cn.xnatural.http.HttpContext
+import cn.xnatural.http.HttpServer
+import cn.xnatural.jpa.Repo
+import com.alibaba.fastjson.JSON
+import com.alibaba.fastjson.JSONObject
+import entity.UserSession
+
+import java.time.Duration
+import java.util.function.Supplier
+
+/**
+ * web 服务
+ */
+class HttpSrv extends ServerTpl {
+ @Lazy protected cacheSrv = bean(CacheSrv)
+ @Lazy protected repo = bean(Repo, 'jpa_rule_repo')
+ @Lazy protected sessionCookieName = getStr("sessionCookieName", "sessionId")
+ // 用户session过期
+ @Lazy protected expire = Duration.ofMinutes(getInteger('session.expire', 30))
+ // 用户session最迟过期时间
+ @Lazy protected continuousExpire = Duration.ofMinutes(getInteger('session.continuousExpire', 60 * 24 * 5))
+ // 用户session数据保存时间
+ @Lazy protected keep = Duration.ofMinutes(getInteger('session.keep', 60 * 24 * 180))
+ // Controller层 Class
+ protected final ctrlClzs = new LinkedList()
+ @Lazy protected server = new HttpServer(attrs(), exec()) {
+ @Override
+ protected Map sessionDelegate(HttpContext hCtx) { getSessionDelegate(hCtx) }
+ }
+
+
+ HttpSrv(String name) { super(name) }
+
+ HttpSrv() {super('web')}
+
+
+ @EL(name = 'sys.starting', async = true)
+ void start() {
+ ctrlClzs.each {server.ctrls(it)}
+ server.start()
+ server.enabled = false
+ ep.fire("${name}.started")
+ }
+
+
+ @EL(name = 'sys.started', async = true)
+ protected void started() {
+ server.ctrls.each {exposeBean(it)}
+ server.enabled = true
+ }
+
+
+ @EL(name = 'sys.stopping')
+ void stop() { server.stop() }
+
+
+ /**
+ * 提供session 数据操作
+ */
+ protected Map getSessionDelegate(HttpContext hCtx) {
+ Map sData
+ String sId = hCtx.request.getCookie(sessionCookieName)
+ if ('redis' == getStr('session.type', null)) { // session的数据, 用redis 保存 session 数据
+ def redis = bean(RedisClient)
+ String cKey
+ if (!sId || ((cKey = 'session:' + sId) && !redis.exists(cKey))) {
+ sId = UUID.randomUUID().toString().replace('-', '')
+ cKey = 'session:' + sId
+ log.info("New session '{}'", sId)
+ }
+ sData = new Map() {
+ @Override
+ int size() { redis.exec {jedis -> jedis.hkeys(cKey).size()} }
+ @Override
+ boolean isEmpty() { redis.exec {jedis -> jedis.hkeys(cKey).isEmpty()} }
+ @Override
+ boolean containsKey(Object key) { redis.hexists(cKey, key?.toString()) }
+ @Override
+ boolean containsValue(Object value) { redis.exec {jedis-> jedis.hvals(cKey).contains(value)} }
+ @Override
+ Object get(Object key) { redis.hget(cKey, key?.toString()) }
+ @Override
+ Object put(String key, Object value) { redis.hset(cKey, key?.toString(), value instanceof Collection ? value.join(',') : value?.toString(), expire.seconds.intValue()) }
+ @Override
+ Object remove(Object key) { redis.hdel(cKey, key?.toString()) }
+ @Override
+ void putAll(Map extends String, ?> m) { m?.each {e -> redis.hset(cKey, e.key, e.value.toString(), expire.seconds.intValue())} }
+ @Override
+ void clear() { redis.del(cKey) }
+ @Override
+ Set keySet() { redis.exec {jedis-> jedis.hkeys(cKey)} }
+ @Override
+ Collection values() { redis.exec {jedis-> jedis.hvals(cKey)} }
+ @Override
+ Set> entrySet() {
+ redis.exec {jedis -> jedis.hgetAll(cKey).entrySet()}
+ }
+ }
+ } else { // 默认用数据库做session 数据管理
+ return dbSessionDelegate(hCtx)
+// String cKey
+// if (!sId || ((cKey = 'session_' + sId) && (sData = cacheSrv.get(cKey)) == null)) {
+// sId = UUID.randomUUID().toString().replace('-', '')
+// cKey = 'session_' + sId
+// log.info("New session '{}'", sId)
+// }
+//
+// if (sData == null) {
+// sData = new ConcurrentHashMap<>()
+// cacheSrv.set(cKey, sData, expire)
+// } else {
+// cacheSrv.expire(cKey, expire)
+// }
+ }
+ sData.put("id", sId)
+ hCtx.response.cookie(sessionCookieName, sId, expire.seconds as Integer, null, "/", false, false)
+ return sData
+ }
+
+
+ /**
+ * 数据库session
+ */
+ protected Map dbSessionDelegate(HttpContext hCtx) {
+ Map sData
+ String sessionId = hCtx.request.getCookie(sessionCookieName)
+ UserSession session
+ if (sessionId) {
+ session = repo.findById(UserSession, sessionId)
+ if (session) {
+ // 一段时间不操作过期
+ if (System.currentTimeMillis() - session.updateTime.time > expire.toMillis()) {
+ session.valid = false
+ async { // 删除过期session数据
+ def e = repo.find(UserSession) {root, query, cb ->
+ cb.and(
+ cb.equal(root.get("valid"), false),
+ cb.lessThan(root.get("updateTime"), new Date(System.currentTimeMillis() - keep.toMillis()))
+ )
+ }
+ if (e) repo.delete(e)
+ }
+ }
+ // 如果一直操作, 则等超出continuousExpire时间后的下一天过期(在下一天第一次重新登录)
+ else if (
+ System.currentTimeMillis() - session.createTime.time > continuousExpire.toMillis() &&
+ (int) (System.currentTimeMillis() / (1000 * 60 * 60 * 24)) > (int) (session.updateTime.time / (1000 * 60 * 60 * 24))
+ ) {
+ session.valid = false
+ }
+ else {
+ session.updateTime = new Date()
+ sData = session.data ? JSON.parseObject(session.data) : new JSONObject()
+ }
+ hCtx.regFinishedFn {
+ session.data = JSON.toJSONString(sData.findAll {e -> e.value !instanceof Supplier})
+ repo.saveOrUpdate(session)
+ }
+ }
+ }
+ sessionId = session && session.valid ? session.sessionId : UUID.randomUUID().toString().replace('-', '')
+ sData = sData == null ? new JSONObject() : sData
+ sData.put("id", sessionId)
+ hCtx.response.cookie(sessionCookieName, sessionId, expire.seconds as Integer, null, "/", false, false)
+ return sData
+ }
+
+
+ /**
+ * 添加
+ * @param clzs
+ */
+ HttpSrv ctrls(Class...clzs) { clzs?.each {ctrlClzs.add(it)}; this }
+
+
+ @EL(name = "sys.heartbeat", async = true)
+ protected void clean() { server.clean() }
+
+
+ @EL(name = ['http.hp', 'web.hp'], async = false)
+ String getHp() { server.hp }
+}
diff --git a/src/core/OkHttpSrv.groovy b/src/core/OkHttpSrv.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..2fed0571743a2638e16cfb3ac69db38d2f0bea00
--- /dev/null
+++ b/src/core/OkHttpSrv.groovy
@@ -0,0 +1,328 @@
+package core
+
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.app.Utils
+import cn.xnatural.enet.event.EL
+import cn.xnatural.enet.event.EP
+import cn.xnatural.sched.Sched
+import com.alibaba.fastjson.JSON
+import okhttp3.*
+
+import java.time.Duration
+import java.util.concurrent.ConcurrentHashMap
+import java.util.function.BiConsumer
+import java.util.function.Consumer
+
+import static java.util.Collections.emptyList
+
+/**
+ * 封装常用的http请求方式:
+ * 1. get
+ * 2. post json
+ * 3. post form
+ * 4. post multi form
+ */
+class OkHttpSrv extends ServerTpl {
+
+ final Map> cookieStore = new ConcurrentHashMap<>()
+ final Map> shareCookie = new ConcurrentHashMap<>()
+
+ OkHttpSrv() {super('okHttp')}
+ OkHttpSrv(String name) {super(name)}
+
+
+ @EL(name = 'sys.starting', async = true)
+ void init() {
+ if (ep == null) {ep = new EP(); ep.addListenerSource(this)}
+ exposeBean(client, name + "_client")
+ }
+
+
+ /**
+ * OkHttpClient
+ */
+ @Lazy OkHttpClient client = {
+ new OkHttpClient.Builder()
+ .connectTimeout(Duration.ofSeconds(getLong('connectTimeout', 5L)))
+ .readTimeout(Duration.ofSeconds(getLong('readTimeout', 20L)))
+ .writeTimeout(Duration.ofSeconds(getLong('writeTimeout', 32L)))
+ .dispatcher(new Dispatcher(exec()))
+ .cookieJar(new CookieJar() {// 共享cookie
+ @Override
+ void saveFromResponse(HttpUrl url, List cookies) {
+ def cs = cookieStore.get(url.host)
+ if (cs == null) {
+ cookieStore.put(url.host, new LinkedList(cookies)) // 可更改
+ } else {// 更新cookie
+ for (def itt = cs.iterator(); itt.hasNext(); ) {
+ Cookie coo = itt.next()
+ for (Cookie c: cookies) {
+ if (c.name() == coo.name()) {
+ itt.remove()
+ break
+ }
+ }
+ }
+ cs.addAll(cookies)
+ }
+ }
+ @Override
+ List loadForRequest(HttpUrl url) {
+ List cookies = cookieStore.get(url.host)
+ if (shareCookie[(url.host)]) {
+ def rLs = new LinkedList<>((cookies?:emptyList()))
+ shareCookie[(url.host)].each {
+ rLs.addAll(cookieStore.get(it)?:emptyList())
+ }
+ return rLs
+ } else {
+ return cookies?:emptyList()
+ }
+ }
+ }).build()
+ }()
+
+
+ /**
+ * get 请求
+ * @param urlStr
+ * @return
+ */
+ OkHttp get(String urlStr) {
+ if (!urlStr) throw new IllegalArgumentException('url must not be empty')
+ def b = new Request.Builder()
+ def h = new OkHttp(urlStr, b)
+ b.method = 'GET'
+ h
+ }
+
+
+ /**
+ * post 请求
+ * @param urlStr
+ * @return
+ */
+ OkHttp post(String urlStr) {
+ if (!urlStr) throw new IllegalArgumentException('url must not be empty')
+ def b = new Request.Builder()
+ def h = new OkHttp(urlStr, b)
+ b.method = 'POST'
+ h
+ }
+
+
+ /**
+ * 创建 WebSocket
+ * @param urlStr 连接地址
+ * @param tryInterval 重连接时间间隔. 秒
+ * @param msgFn 消息处理函数
+ */
+ WebSocket ws(String urlStr, Integer tryInterval, BiConsumer msgFn = null) {
+ def req = new Request.Builder().url(urlStr).build()
+ def listener = new WebSocketListener() {
+ @Override
+ void onOpen(WebSocket webSocket, Response resp) {
+ log.info("WebSocket onOpen. url: " + urlStr)
+ }
+ @Override
+ void onMessage(WebSocket webSocket, String msg) {
+ msgFn?.accept(msg, webSocket)
+ }
+ @Override
+ void onFailure(WebSocket ws, Throwable t, Response resp) {
+ log.error("WS Error. url: " + urlStr + ", res: " + resp?.body()?.toString(), t)
+ if (tryInterval) {
+ def sched = bean(Sched)
+ if (sched) {
+ sched.after(Duration.ofSeconds(tryInterval), { client.newWebSocket(req, this) })
+ } else {
+ log.warn("重连接WebSocket, 依赖于 sched 服务")
+ }
+ }
+ }
+ }
+ client.newWebSocket(req, listener)
+ }
+
+
+ class OkHttp {
+ // 宿主
+ protected final Request.Builder builder
+ protected String urlStr
+ protected Map params
+ // 文件流:(属性名, 文件名, 文件内容)
+ protected List> fileStreams
+ protected Map cookies
+ protected String contentType
+ protected String bodyStr
+ protected boolean debug
+ protected Integer respCode
+
+ protected OkHttp(String urlStr, Request.Builder builder) {
+ if (builder == null) throw new NullPointerException('builder == null')
+ this.builder = builder
+ this.urlStr = urlStr
+ }
+ OkHttp param(String pName, Object pValue) {
+ if (pName == null) throw new NullPointerException('pName == null')
+ if (params == null) params = new LinkedHashMap<>()
+ params.put(pName, pValue)
+ if (pValue instanceof File) {
+ if (contentType == null) contentType = 'multipart/form-data;charset=utf-8'
+ }
+ this
+ }
+ OkHttp fileStream(String pName, String fileName, Object body) {
+ if (pName == null) throw new NullPointerException('pName == null')
+ if (body == null) throw new NullPointerException('body == null')
+ if (body instanceof byte[] || body instanceof String || body instanceof InputStream || body instanceof File) {
+ if (fileStreams == null) fileStreams = new LinkedList<>()
+ fileStreams.add(Tuple.tuple(pName, fileName, body))
+ if (contentType == null) contentType = 'multipart/form-data;charset=utf-8'
+ } else throw new IllegalArgumentException("Not support file body type: " + body.class.name)
+ this
+ }
+ OkHttp jsonBody(String bodyStr) {
+ this.bodyStr = bodyStr
+ if (contentType == null) contentType = 'application/json;charset=utf-8'
+ this
+ }
+ OkHttp textBody(String bodyStr) {
+ this.bodyStr = bodyStr
+ if (contentType == null) contentType = 'text/plain;charset=utf-8'
+ this
+ }
+ OkHttp contentType(String contentType) {
+ this.contentType = contentType
+ this
+ }
+ OkHttp cookie(String name, Object value) {
+ if (name == null) throw new NullPointerException('name == null')
+ if (cookies == null) cookies = new HashMap<>(7)
+ cookies.put(name, value)
+ this
+ }
+ OkHttp header(String name, Object value) {
+ if (value == null) throw new NullPointerException('value == null')
+ builder.addHeader(name, value.toString())
+ this
+ }
+ OkHttp debug() {this.debug = true; this}
+
+ Integer getRespCode() { return respCode }
+ // 请求执行
+ def execute(Consumer okFn = null, BiConsumer failFn = {throw it}) {
+ List tmpFile = null
+ if ('GET' == builder.method) { // get 请求拼装参数
+ urlStr = Utils.buildUrl(urlStr, params)
+ builder.get()
+ // okHttp get 不能body
+ }
+ else if ('POST' == builder.method) {
+ if (contentType &&
+ (contentType.containsIgnoreCase('application/json') ||
+ contentType.containsIgnoreCase('text/plain'))
+ ) {
+ if (bodyStr) builder.post(RequestBody.create(MediaType.get(contentType), (bodyStr == null ? '' : bodyStr)))
+ else if (params) builder.post(RequestBody.create(MediaType.get(contentType), JSON.toJSONString(params)))
+ } else if (contentType && contentType.containsIgnoreCase('multipart/form-data')) {
+ def b = new MultipartBody.Builder().setType(MediaType.get(contentType))
+ fileStreams?.each {
+ def v = it.v3
+ if (it.v3 instanceof InputStream) {
+ v = new File(getStr('tmpDir', Utils.baseDir('tmp').canonicalPath))
+ v.mkdirs()
+ v = new File(v, "/" + UUID.randomUUID().toString().replace("-", ''))
+ v.append((InputStream) it.v3)
+ if (tmpFile == null) tmpFile = new LinkedList<>()
+ tmpFile.add(v)
+ }
+ b.addFormDataPart(it.v1, it.v2, RequestBody.create(MediaType.get('application/octet-stream'), v))
+ }
+ params?.each {
+ if (it.value instanceof File) {
+ b.addFormDataPart(it.key, ((File) it.value).name, RequestBody.create(MediaType.get('application/octet-stream'), (File) it.value))
+ } else {
+ b.addFormDataPart(it.key, (it.value == null ? '': it.value).toString())
+ }
+ }
+ builder.post(b.build())
+ } else {
+ String bodyStr = params?.collect { it.key + '=' + URLEncoder.encode((it.value == null ? '': it.value).toString(), 'utf-8') + '&' }?.join('')?:''
+ if (bodyStr.endsWith('&') && bodyStr.length() > 2) bodyStr = bodyStr.substring(0, bodyStr.length() - 1)
+ builder.post(RequestBody.create(MediaType.get('application/x-www-form-urlencoded;charset=utf-8'), bodyStr))
+ }
+ }
+ else throw new RuntimeException("not support http method '$builder.method'")
+
+ // 删除url最后的&符号
+ if (urlStr.endsWith('&') && urlStr.length() > 2) urlStr = urlStr.substring(0, urlStr.length() - 1)
+
+ URI uri = URI.create(urlStr)
+ if (getBoolean('enabledIntervalResolve', false)) {
+ // 替换集群中的appName 对应的http地址,例: http://gy/test/cus, 找集群中对应gy的应用名所对应的http地址
+ String hp = ep.fire("resolveHttp", uri.host)
+ if (hp) {
+ String[] arr = hp.split(":")
+ uri.host = arr[0]
+ // 原始url中没有端口, 则设值
+ if (-1 == uri.port) uri.port = Integer.valueOf(arr[1])
+ uri.string = null
+ urlStr += ", hp: ${uri.host + ':' + uri.port}"
+ }
+ }
+
+ HttpUrl url = HttpUrl.get(uri)
+ // 添加cookie
+ if (cookies) {
+ def ls = cookieStore.computeIfAbsent(url.host(), {new LinkedList()})
+ cookies.each { ls.add(Cookie.parse(url, "$it.key=$it.value")) }
+ }
+
+ def result
+ def ex
+ // 日志消息
+ def logMsg = "Send http(${builder.method}): ${url.toString()}${ -> bodyStr ? ', body: ' + bodyStr : ''}${ -> params ? ', params: ' + params : ''}${ -> fileStreams ? "fileStreams: " + fileStreams.join(",") : ''}${ -> respCode == null ? '' : ' respCode: ' + respCode}${ -> result == null ? '' : ' result: ' + result}"
+ try {
+ // 发送请求
+ def call = client.newCall(builder.url(url).build())
+ if (okFn) { // 异步请求
+ call.enqueue(new Callback() {
+ @Override
+ void onFailure(Call c, IOException e) {
+ log.error(logMsg.toString(), e)
+ tmpFile?.each {it.delete()}
+ failFn?.accept(respCode, e)
+ }
+
+ @Override
+ void onResponse(Call c, Response resp) throws IOException {
+ result = resp.body()?.string()
+ respCode = resp.code()
+ if (debug) log.info(logMsg.toString())
+ tmpFile?.each {it.delete()}
+ okFn?.accept(result)
+ }
+ })
+ null
+ } else { // 同步请求
+ def resp = call.execute()
+ result = resp.body()?.string()
+ tmpFile?.each {it.delete()}
+ respCode = resp.code()
+ }
+ } catch(Exception t) {
+ ex = t
+ }
+ if (debug && !okFn) {
+ if (ex) {
+ log.error(logMsg.toString(), ex)
+ } else {
+ log.info(logMsg.toString())
+ }
+ }
+ if (ex) throw ex
+ return result
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/core/RedisClient.groovy b/src/core/RedisClient.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..4dccfce9528e547cd87bd719a0cf75f7b0ac2a51
--- /dev/null
+++ b/src/core/RedisClient.groovy
@@ -0,0 +1,143 @@
+package core
+
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.enet.event.EL
+import cn.xnatural.enet.event.EP
+import redis.clients.jedis.Jedis
+import redis.clients.jedis.JedisPool
+import redis.clients.jedis.JedisPoolConfig
+import redis.clients.jedis.Protocol
+
+import java.time.Duration
+import java.util.function.Function
+
+/**
+ * redis 客户端
+ */
+class RedisClient extends ServerTpl {
+ protected JedisPool pool
+
+ RedisClient() { super('redis') }
+ RedisClient(String name) { super(name) }
+
+
+ @EL(name = 'sys.starting', async = true)
+ void start() {
+ if (pool) throw new RuntimeException("$name is already running")
+ if (ep == null) {ep = new EP(); ep.addListenerSource(this)}
+
+ // 连接池配置
+ JedisPoolConfig poolCfg = new JedisPoolConfig(
+ minIdle: getInteger("minIdle", 1), maxIdle: getInteger("maxIdle", 5),
+ maxTotal: getInteger("maxTotal", 7), maxWaitMillis: getInteger("maxWaitMillis", 5000)
+ )
+ pool = new JedisPool(
+ poolCfg, getStr("host", "localhost"), getInteger("port", 6379),
+ getInteger("connectionTimeout", 3000),
+ getInteger("soTimeout", 7000),
+ getStr("password", null),
+ getInteger("database", Protocol.DEFAULT_DATABASE),
+ getStr("clientName", app().id())
+ )
+
+ exposeBean(pool)
+ log.info("Created '{}' Client", name)
+ ep.fire("${name}.started")
+ }
+
+
+ @EL(name = "sys.stopping", async = true, order = 2f)
+ void stop() {
+ log.info("Close '{}' Client", name)
+ pool?.close(); pool = null
+ }
+
+ @EL(name = '${name}.get')
+ String get(String key) {
+ log.trace(name + ".get. key: {}", key)
+ exec{it.get(key)}
+ }
+
+
+ @EL(name = '${name}.expire')
+ void expire(String key, Integer seconds) {
+ log.trace(name + ".hset.expire, key: {}, seconds: {}", key, seconds)
+ exec{ it.expire(key, seconds == null ? getExpire(key).seconds.intValue() : seconds)}
+ }
+
+
+ def set(String key, String value, Integer seconds) {
+ log.trace(name + ".set. key: {}, value: {}, seconds: " + seconds, key, value)
+ if (value != null) {
+ exec{c ->
+ c.set(key, value)
+ c.expire(key, seconds == null ? getExpire(key).seconds.intValue() : seconds)
+ }
+ }
+ }
+
+
+ @EL(name = '${name}.set')
+ def set(String key, String value) {
+ log.trace(name + ".set. key: {}, value: {}", key, value)
+ if (value != null) {
+ exec{it.set(key, value)}
+ }
+ }
+
+
+ def hset(String cName, String key, String value, Integer seconds) {
+ log.trace(name + ".hset. cName: "+ cName +", key: {}, value: {}, seconds: " + seconds, key, value)
+ if (value != null) {
+ exec{c ->
+ c.hset(cName, key, value)
+ c.expire(cName, seconds == null ? getExpire(cName).seconds.intValue() : seconds)
+ }
+ }
+ }
+
+
+ @EL(name = '${name}.hset')
+ def hset(String cName, String key, String value) {
+ log.trace(name + ".hset. cName: "+ cName +", key: {}, value: {}", key, value)
+ if (value != null) {
+ exec{ it.hset(cName, key, value)}
+ }
+ }
+
+
+ @EL(name = '${name}.hget', async = false)
+ String hget(String cName, String key) { exec{it.hget(cName, key)} }
+
+
+ @EL(name = '${name}.hexists', async = false)
+ boolean hexists(String cName, String key) { if (key == null) false else exec{it.hexists(cName, key)} }
+
+
+ @EL(name = '${name}.exists', async = false)
+ boolean exists(String key) { exec{it.exists(key)} }
+
+
+ @EL(name = '${name}.hdel', async = false)
+ def hdel(String cName, String key) {
+ log.debug(name + ".hdel. cName: {}, key: {}", cName, key)
+ exec{it.hdel(cName, key)}
+ }
+
+
+ @EL(name = '${name}.del')
+ def del(String key) {
+ log.info("{}.del. key: {}", name, key)
+ exec{it.del(key)}
+ }
+
+
+ @EL(name = '${name}.exec', async = false)
+ def exec(Function fn) { try (Jedis c = pool.getResource()) { fn.apply(c) } }
+
+
+ // 默认12小时过期
+ protected Duration getExpire(String cName) {
+ Duration.ofMinutes(getLong("expire." + cName, getLong("defaultExpire", 12 * 60L)))
+ }
+}
diff --git a/src/core/mode/builder/Builder.groovy b/src/core/mode/builder/Builder.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..ac304e3b9c609abe5487c45bc85c13549b52f3eb
--- /dev/null
+++ b/src/core/mode/builder/Builder.groovy
@@ -0,0 +1,59 @@
+package core.mode.builder
+
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+/**
+ * This abstract class is a template which build any kind of value and return it
+ *
+ * @param
+ * @author hubert
+ */
+abstract class Builder {
+ protected static final Logger log = LoggerFactory.getLogger(Builder.class)
+ /**
+ * Enable or disable this generator. Usually it is used for feature
+ * controlling.
+ */
+ protected boolean enabled = true
+
+
+ /**
+ * Generate any Object and return it. If isEnabled() == false. Return null.
+ * If isValid() == false return null.
+ *
+ * @param ctx . It contains the information needed to build the value.
+ */
+ T build(Map ctx = null) {
+ if (!enabled || !isValid(ctx)) return null
+ return doBuild(ctx)
+ }
+
+
+
+
+ /**
+ * do build javaBean by context.
+ *
+ * @param ctx context
+ * @return javaBean
+ */
+ protected abstract T doBuild(Map ctx)
+
+
+ /**
+ * Validate the current context. Return false if fails
+ *
+ * @param ctx GeneratorContext
+ * @return true if can continue to build
+ */
+ protected boolean isValid(Map ctx) {
+ return true
+ }
+
+
+ @Override
+ String toString() {
+ return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode())
+ }
+}
diff --git a/src/core/mode/builder/MultiPropertyBuilder.groovy b/src/core/mode/builder/MultiPropertyBuilder.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..5c15fe5ad82ff61301ed40adb1bfa4efbfc60995
--- /dev/null
+++ b/src/core/mode/builder/MultiPropertyBuilder.groovy
@@ -0,0 +1,4 @@
+package core.mode.builder
+
+abstract class MultiPropertyBuilder extends Builder> {
+}
diff --git a/src/core/mode/builder/ObjBuilder.groovy b/src/core/mode/builder/ObjBuilder.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..ab674012f3f12491babbac667f6ce1e8cac274d5
--- /dev/null
+++ b/src/core/mode/builder/ObjBuilder.groovy
@@ -0,0 +1,115 @@
+package core.mode.builder
+/**
+ * JavaBean对象构建器, 从一个运行上下文中, 构建一个结果对象.
+ * 一般用于: rest 接口, 即service 方法返回一个对象, 对象的一些属性需要复杂计算的结果
+ *
+ * @param
+ * @author hubert
+ */
+class ObjBuilder extends Builder {
+ /**
+ * 一个javaBean.
+ */
+ private Class targetClz
+ /**
+ * 属性的计算是可以依赖顺序的(先计算某个属性, 再根据这个属性的值, 计算另一个属性的值)
+ * NOTE: 尽量每个属性的计算不会相互依赖
+ */
+ private final Map> fieldGenerators = new LinkedHashMap<>()
+
+
+ static final ObjBuilder of(Class javaBeanClz) {
+ return new ObjBuilder().setJavaBeanClz(javaBeanClz)
+ }
+
+
+ @Override
+ protected boolean isValid(Map ctx) {
+ if (!super.isValid(ctx)) return false
+ if (getJavaBeanClz() == null) {
+ log.error("property javaBeanClz must not be null")
+ return false
+ }
+ if (!fieldGenerators) log.warn("fieldGenerators is empty!")
+ return true
+ }
+
+
+ @Override
+ protected T doBuild(Map ctx) {
+ T retObj = instance(ctx)
+ if (retObj == null) return null
+ fieldGenerators.each{propName, builder ->
+ if (builder == null) {
+ log.warn("属性 " + propName + " 对应的Builder为空!")
+ return
+ }
+ boolean multi = (builder instanceof MultiPropertyBuilder)
+ try {
+ if (multi) {
+ Map ps = ((MultiPropertyBuilder) builder).build(ctx)
+ log.debug("builder: {} populate propertyValues: {}", builder, ps)
+ ps.each { retObj[(it.key)] = it.value }
+ } else {
+ if (!propName) {
+ log.warn("属性名为空, 忽略!")
+ return
+ }
+ Object value = builder.build(ctx)
+ log.debug("builder: {} populate value: {} for property: " + propName, builder, value)
+ retObj[(propName)] = value
+ }
+ } catch(Exception ex) {
+ log.error("属性build错误. builder: " + builder + (multi ? (", propName: " + propName) : ""), ex)
+ }
+ }
+ return retObj
+ }
+
+
+ ObjBuilder add(String propName, Builder builder) {
+ if (builder instanceof MultiPropertyBuilder && propName != null && !propName.isEmpty()) {
+ log.warn("MultiPropertyGenerator 对应多个属性所以不需要有属性名: ({}), 请用add(MultiPropertyGenerator builder)", propName)
+ }
+ fieldGenerators.put(propName, builder)
+ return this
+ }
+
+
+ ObjBuilder add(MultiPropertyBuilder builder) {
+ fieldGenerators.put(UUID.randomUUID().toString(), builder)
+ return this
+ }
+
+
+ /**
+ * 创建目前对象
+ * @param ctx
+ * @return
+ */
+ protected T instance(Map ctx) {
+ T targetObj = null
+ Class targetClass = getJavaBeanClz()
+ if (Map.class == (targetClass)) {
+ targetObj = (T) new LinkedHashMap<>()
+ } else if (Set.class == (targetClass)) {
+ targetObj = (T) new LinkedHashSet<>()
+ } else if (List.class == (targetClass)) {
+ targetObj = (T) new ArrayList<>()
+ } else {
+ targetObj = targetClass.newInstance()
+ }
+ return targetObj
+ }
+
+
+ Class getJavaBeanClz() {
+ return targetClz
+ }
+
+
+ ObjBuilder setJavaBeanClz(Class pDTOClass) {
+ targetClz = pDTOClass
+ return this
+ }
+}
diff --git a/src/core/mode/pipeline/PipeNode.groovy b/src/core/mode/pipeline/PipeNode.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..390ec83c3e5615ed89bc4e0d56cae57dccaea2d2
--- /dev/null
+++ b/src/core/mode/pipeline/PipeNode.groovy
@@ -0,0 +1,24 @@
+package core.mode.pipeline
+
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+/**
+ * 管道节点
+ */
+abstract class PipeNode {
+ protected final Logger log = LoggerFactory.getLogger(getClass())
+
+ String key
+
+
+ abstract R run(T input)
+
+
+ String getKey() {
+ if (key == null) {
+ key = getClass().simpleName + "@" + Integer.toHexString(hashCode())
+ }
+ key
+ }
+}
diff --git a/src/core/mode/pipeline/Pipeline.groovy b/src/core/mode/pipeline/Pipeline.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..caaa1fa93f834ec48aed7730d648ddb8d355a1cd
--- /dev/null
+++ b/src/core/mode/pipeline/Pipeline.groovy
@@ -0,0 +1,36 @@
+package core.mode.pipeline
+
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+class Pipeline {
+ final Logger log = LoggerFactory.getLogger(getClass())
+ String key
+ final List> nodes = new LinkedList<>()
+
+
+ R run(T input) {
+ log.info("Start pipe '{}', input: {}", getKey(), input)
+ nodes.each{ fn ->
+ log.debug("Start pipe node '{}', input: {}", fn.key, input)
+ input = fn.run(input)
+ log.debug("End pipe node '{}', result: {}", fn.key, input)
+ }
+ log.info("End pipe '{}', result: {}", getKey(), input)
+ input
+ }
+
+
+ Pipeline add(PipeNode node) {
+ nodes.add(node)
+ this
+ }
+
+
+ String getKey() {
+ if (key == null) {
+ key = getClass().simpleName + "@" + Integer.toHexString(hashCode())
+ }
+ key
+ }
+}
diff --git a/src/core/mode/pool/ObjPool.groovy b/src/core/mode/pool/ObjPool.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..b4a448f59c6b99eef3e82ae76b5b083593215d7e
--- /dev/null
+++ b/src/core/mode/pool/ObjPool.groovy
@@ -0,0 +1,4 @@
+package core.mode.pool
+
+class ObjPool {
+}
diff --git a/src/core/mode/tree/Tree.groovy b/src/core/mode/tree/Tree.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..c8fd49e023563d827f92f7e6d7bdf01d27f52d06
--- /dev/null
+++ b/src/core/mode/tree/Tree.groovy
@@ -0,0 +1,48 @@
+package core.mode.tree
+
+class Tree {
+ String id
+ String name
+ @Lazy TNode root = new TNode(id: id, name: name)
+
+ static TNode create(String id, String name) {
+ new Tree(id: id, name: name).root
+ }
+
+
+ static TNode node(String id, String name, Closure fn = null) {new TNode(id: id, name: name, fn: fn)}
+
+
+ def run(Object...args) {
+ root
+ }
+
+ public static void main(String[] args) {
+ Tree.create("tree1", "test").sub(
+ node("n1_left", "", {println(id)}),
+ node("n1_right", "", {println(id)})
+ )
+ }
+
+ class TNode {
+ String id
+ String name
+ Closure fn
+ TNode parent
+ TNode subLeft
+ TNode subRight
+
+ TNode of(String id, String name) { new TNode(id: id, name: name) }
+
+ TNode sub(TNode left, TNode right) {
+ subLeft = left; subRight = right
+ if (subLeft) subLeft.parent = this
+ if (subRight) subRight.parent = this
+ this
+ }
+
+ def run() {
+ fn.run()
+ }
+ }
+}
diff --git a/src/ctrl/MainCtrl.groovy b/src/ctrl/MainCtrl.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..97b66ea194e608427f73616892839a7372bf8c1c
--- /dev/null
+++ b/src/ctrl/MainCtrl.groovy
@@ -0,0 +1,168 @@
+package ctrl
+
+import cn.xnatural.app.CacheSrv
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.app.Utils
+import cn.xnatural.http.*
+import cn.xnatural.jpa.Repo
+import entity.User
+import service.FileUploader
+
+import java.util.function.Supplier
+
+@Ctrl
+class MainCtrl extends ServerTpl {
+
+ @Lazy protected fileUploader = bean(FileUploader)
+ @Lazy protected cacheSrv = bean(CacheSrv)
+ @Lazy protected repo = bean(Repo, 'jpa_rule_repo')
+ // 需要登录权限的页面
+ protected final Map auth_page = [
+ "config/OpHistory.vue": "opHistory-read", "config/FieldConfig.vue": "field-read",
+ "config/DataCollectorConfig.vue": "dataCollector-read", "config/Permission.vue": "grant",
+ "data/DecideResult.vue": "decideResult-read", "data/CollectResult.vue": "collectResult-read"
+ ]
+
+ @Filter
+ void filter(HttpContext hCtx) {
+ // 需要登录权限的请求路径过虑判断
+ if (
+ (hCtx.pieces?[0] == 'mnt' && !(hCtx.pieces?[1] == 'login')) ||
+ auth_page.keySet().find {hCtx.request.path.endsWith(it)}
+ ) {
+ def uId = hCtx.getSessionAttr('uId')
+ if (uId) {
+ // 设置用户权限绑定到请求
+ hCtx.setAttr('permissions', {
+ def pIds = cacheSrv?.get("permission_" + uId)
+ if (pIds == null) {
+ def permissions = repo.findById(User, Utils.to(uId, Long)).permissions
+ pIds = permissions?.split(',')?.toList()?.toSet()?:Collections.emptySet()
+ cacheSrv?.set("permission_" + uId, pIds)
+ }
+ return pIds
+ } as Supplier)
+ } else {
+ hCtx.response.status(401)
+ hCtx.render(ApiResp.fail('用户会话已失效, 请重新登录'))
+ }
+ }
+ }
+
+
+ @Path(path = ['index.html', '/'])
+ File index(HttpContext hCtx) {
+ hCtx.response.cacheControl(10)
+ Utils.baseDir("static/index.html")
+ }
+
+ @Path(path = 'test.html')
+ File testHtml(HttpContext hCtx) {
+ hCtx.response.cacheControl(3)
+ Utils.baseDir("static/test.html")
+ }
+
+ @Path(path = 'favicon.ico')
+ File favicon(HttpContext hCtx) {
+ hCtx.response.cacheControl(3000)
+ hCtx.response.contentType("image/x-icon")
+ Utils.baseDir("static/favicon.ico")
+ }
+
+
+ @Path(path = 'health')
+ ApiResp health() {
+ ApiResp.ok(
+ ['status': 'GREEN', 'detail':
+ [
+ 'db': ['status': 'UP'],
+ ],
+ ]
+ )
+ }
+
+
+ @Path(path = 'file/{fName}')
+ File file(String fName, HttpContext hCtx) {
+ if (app().profile == 'pro') {
+ hCtx.response.cacheControl(1800)
+ }
+ fileUploader.findFile(fName)
+ }
+
+
+ // ====================== api-doc =========================
+
+ @Path(path = 'api-doc/{fName}.json', produce = 'application/json')
+ String swagger_data(String fName, HttpContext hCtx) {
+ def f = Utils.baseDir("../conf/${fName}.json")
+ if (f.exists()) {
+ return f.getText('utf-8')
+ }
+ null
+ }
+ @Path(path = 'api-doc/{fName}')
+ File swagger_ui(String fName, HttpContext hCtx) {
+ hCtx.response.cacheControl(1800)
+ Utils.baseDir("static/swagger-ui/${fName == '/' ? 'index.html' : fName}")
+ }
+
+ // ==========================js =====================
+
+ @Path(path = 'js/{fName}')
+ File js(String fName, HttpContext hCtx) {
+ if (app().profile == 'pro') {
+ hCtx.response.cacheControl(1800)
+ }
+ Utils.baseDir("static/js/$fName")
+ }
+
+ @Path(path = 'js/lib/{fName}')
+ File js_lib(String fName, HttpContext hCtx) {
+ hCtx.response.cacheControl(86400) // 一天
+ Utils.baseDir("static/js/lib/$fName")
+ }
+
+
+ @Path(path = 'components/{fName}')
+ File components(String fName, HttpContext hCtx) {
+ if (app().profile == 'pro') hCtx.response.cacheControl(1800)
+ Utils.baseDir("static/components/$fName")
+ }
+
+ @Path(path = 'views/{fName}')
+ File views(String fName, HttpContext hCtx) {
+ if (app().profile == 'pro') hCtx.response.cacheControl(1800)
+ def permission = auth_page.get(fName)
+ if (permission) hCtx.auth(permission)
+ Utils.baseDir("static/views/$fName")
+ }
+
+
+ // =======================css ========================
+
+ @Path(path = 'css/{fName}')
+ File css(String fName, HttpContext hCtx) {
+ if (app().profile == 'pro') hCtx.response.cacheControl(1800)
+ Utils.baseDir("static/css/$fName")
+ }
+
+ @Path(path = 'css/fonts/{fName}')
+ File css_fonts(String fName, HttpContext hCtx) {
+ hCtx.response.cacheControl(86400) // 一天
+ Utils.baseDir("static/css/fonts/$fName")
+ }
+
+ @Path(path = 'css/lib/{fName}')
+ File css_lib(String fName, HttpContext hCtx) {
+ hCtx.response.cacheControl(86400) // 一天
+ Utils.baseDir("static/css/lib/$fName")
+ }
+
+ // ================= 图片 ======================
+ @Path(path = 'img/{fName}')
+ File img(String fName, HttpContext hCtx) {
+ hCtx.response.cacheControl(172800)
+ Utils.baseDir("static/img/$fName")
+ }
+}
diff --git a/src/ctrl/MntCollectorCtrl.groovy b/src/ctrl/MntCollectorCtrl.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..f820adb5787d046b382fe530ab477cd6762ddec9
--- /dev/null
+++ b/src/ctrl/MntCollectorCtrl.groovy
@@ -0,0 +1,216 @@
+package ctrl
+
+import cn.xnatural.app.Inject
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.http.ApiResp
+import cn.xnatural.http.Ctrl
+import cn.xnatural.http.HttpContext
+import cn.xnatural.http.Path
+import cn.xnatural.jpa.Repo
+import entity.DataCollector
+import service.rule.CollectorManager
+import service.rule.DecisionManager
+
+
+@Ctrl(prefix = 'mnt/collector')
+class MntCollectorCtrl extends ServerTpl {
+
+ @Inject(name = 'jpa_rule_repo') protected Repo repo
+ @Inject protected DecisionManager decisionManager
+ @Inject protected CollectorManager collectorManager
+
+
+
+ @Path(path = 'page')
+ ApiResp page(HttpContext hCtx, Integer page, Integer pageSize, String kw, String id, String type) {
+ if (pageSize && pageSize > 50) return ApiResp.fail("Param pageSize <=50")
+ hCtx.auth("dataCollector-read")
+ ApiResp.ok(
+ repo.findPage(DataCollector, page, pageSize?:10) { root, query, cb ->
+ query.orderBy(cb.desc(root.get('updateTime')))
+ def ps = []
+ if (kw) {
+ ps << cb.or(
+ cb.like(root.get('name'), '%' + kw + '%'),
+ cb.like(root.get('comment'), '%' + kw + '%')
+ )
+ }
+ if (id) ps << cb.equal(root.get("id"), id)
+ if (type) ps << cb.equal(root.get('type'), type)
+ ps ? ps.inject {p1, p2 -> cb.and(p1, p2)} : null
+ }
+ )
+ }
+
+
+ @Path(path = '/', method = 'put')
+ ApiResp add(
+ HttpContext hCtx, String name, String type, String url, String bodyStr, Boolean recordResult,
+ String method, String parseScript, String contentType, String comment, String computeScript, String dataSuccessScript,
+ String sqlScript, Integer minIdle, Integer maxActive, Integer timeout, Boolean enabled, String cacheKey, String cacheTimeoutFn
+ ) {
+ hCtx.auth('dataCollector-add')
+ DataCollector collector = new DataCollector(name: name, type: type, comment: comment, enabled: (enabled == null ? true : enabled), recordResult: recordResult == null ? true : recordResult)
+ if (!collector.name) return ApiResp.fail('Param name required')
+ if (!collector.type) return ApiResp.fail('Param type required')
+ if ('http' == collector.type) {
+ if (!url) return ApiResp.fail('Param url required')
+ if (!method) return ApiResp.fail('Param method required')
+ if (!contentType && !'get'.equalsIgnoreCase(method)) return ApiResp.fail('Param contentType required')
+ if (!url.startsWith("http") && !url.startsWith('${')) return ApiResp.fail('Param url incorrect')
+ collector.parseScript = parseScript?.trim()
+ if (collector.parseScript && !collector.parseScript.startsWith("{") && !collector.parseScript.endsWith("}")) {
+ return ApiResp.fail('Param parseScript a function, must startWith {, endWith }')
+ }
+ collector.dataSuccessScript = dataSuccessScript?.trim()
+ if (collector.dataSuccessScript && !collector.dataSuccessScript.startsWith("{") && !collector.dataSuccessScript.endsWith("}")) {
+ return ApiResp.fail('Param dataSuccessScript a function, must startWith {, endWith }')
+ }
+ collector.url = url
+ collector.method = method
+ collector.bodyStr = bodyStr
+ collector.contentType = contentType
+ collector.timeout = timeout
+ } else if ('script' == collector.type) {
+ if (!computeScript) return ApiResp.fail('Param computeScript required')
+ collector.computeScript = computeScript
+ } else if ('sql' == collector.type) {
+ if (!url) return ApiResp.fail('Param url required')
+ if (!sqlScript) return ApiResp.fail('Param sqlScript required')
+ if (!url.startsWith("jdbc")) return ApiResp.fail('url incorrect')
+ if (minIdle < 0 || collector.minIdle > 20) return ApiResp.fail('Param minIdle >=0 and <= 20')
+ if (maxActive < 1 || collector.maxActive > 50) ApiResp.fail('Param maxActive >=1 and <= 50')
+ collector.url = url
+ collector.sqlScript = sqlScript
+ collector.minIdle = minIdle
+ collector.maxActive = maxActive
+ } else return ApiResp.fail('Not support type: ' + collector.type)
+ collector.creator = hCtx.getSessionAttr("uName")
+ collector.cacheKey = cacheKey
+ collector.cacheTimeoutFn = cacheTimeoutFn
+ try {
+ repo.saveOrUpdate(collector)
+ } catch (ex) {
+ def cause = ex
+ while (cause != null) {
+ if (cause.message.contains("Duplicate entry")) {
+ return ApiResp.fail("$name aleady exist")
+ }
+ cause = cause.cause
+ }
+ throw ex
+ }
+ ep.fire('dataCollectorChange', collector.id)
+ ep.fire('enHistory', collector, hCtx.getSessionAttr('uName'))
+ ApiResp.ok(collector)
+ }
+
+
+ @Path(path = ':id', method = 'post')
+ ApiResp update(
+ HttpContext hCtx, String id, String name, String url, String bodyStr, Boolean recordResult,
+ String method, String parseScript, String contentType, String comment, String computeScript, String dataSuccessScript,
+ String sqlScript, Integer minIdle, Integer maxActive, Integer timeout, Boolean enabled, String cacheKey, String cacheTimeoutFn
+ ) {
+ hCtx.auth('dataCollector-update')
+ if (!id) return ApiResp.fail("Param id required")
+ if (!name) return ApiResp.fail("Param name required")
+ def collector = repo.findById(DataCollector, id)
+ if (collector == null) return ApiResp.fail("Param id: $id not found")
+ if ('http' == collector.type) {
+ if (!url) return ApiResp.fail('Param url required')
+ if (!method) return ApiResp.fail('Param method required')
+ if ('post'.equalsIgnoreCase(method) && !contentType) {
+ return ApiResp.fail('Param contentType required')
+ }
+ if (!url.startsWith("http") && !url.startsWith('${')) return ApiResp.fail('Param url incorrect')
+ collector.parseScript = parseScript?.trim()
+ if (collector.parseScript && (!collector.parseScript.startsWith('{') || !collector.parseScript.endsWith('}'))) {
+ return ApiResp.fail('Param parseScript is function, must startWith {, endWith }')
+ }
+ collector.dataSuccessScript = dataSuccessScript?.trim()
+ if (collector.dataSuccessScript && !collector.dataSuccessScript.startsWith("{") && !collector.dataSuccessScript.endsWith("}")) {
+ return ApiResp.fail('Param dataSuccessScript is function, must startWith {, endWith }')
+ }
+ collector.url = url
+ collector.method = method
+ collector.contentType = contentType
+ collector.bodyStr = bodyStr
+ collector.timeout = timeout
+ } else if ('script' == collector.type) {
+ if (!computeScript) return ApiResp.fail('Param computeScript required')
+ collector.computeScript = computeScript?.trim()
+ if (collector.computeScript && (collector.computeScript.startsWith('{') || collector.computeScript.endsWith('}'))) {
+ return ApiResp.fail('Param computeScript is pure script. cannot startWith { or endWith }')
+ }
+ } else if ('sql' == collector.type) {
+ if (!url) return ApiResp.fail('Param url required')
+ if (!sqlScript) return ApiResp.fail('Param sqlScript required')
+ if (!url.startsWith("jdbc")) return ApiResp.fail('Param url incorrect')
+ if (minIdle < 0 || minIdle > 20) return ApiResp.fail('Param minIdle >=0 and <= 20')
+ if (maxActive < 1 || maxActive > 50) return ApiResp.fail('Param maxActive >=1 and <= 50')
+ collector.url = url
+ collector.minIdle = minIdle
+ collector.maxActive = maxActive
+ collector.sqlScript = sqlScript
+ }
+ collector.name = name
+ collector.comment = comment
+ collector.enabled = enabled == null ? true : enabled
+ collector.recordResult = recordResult == null ? true : recordResult
+ collector.cacheKey = cacheKey
+ collector.cacheTimeoutFn = cacheTimeoutFn
+ collector.updater = hCtx.getSessionAttr("uName")
+
+ try {
+ repo.saveOrUpdate(collector)
+ } catch (ex) {
+ def cause = ex
+ while (cause != null) {
+ if (cause.message.contains("Duplicate entry")) {
+ return ApiResp.fail("$name aleady exist")
+ }
+ cause = cause.cause
+ }
+ throw ex
+ }
+ ep.fire('dataCollectorChange', collector.id)
+ ep.fire('enHistory', collector, hCtx.getSessionAttr('uName'))
+ ApiResp.ok(collector)
+ }
+
+
+ @Path(path = 'enable/{id}/{enabled}', method = 'post')
+ ApiResp enable(HttpContext hCtx, String id, Boolean enabled) {
+ hCtx.auth('dataCollector-update')
+ if (!id) return ApiResp.fail("Param id required")
+ if (enabled == null) return ApiResp.fail("Param enabled required")
+ def collector = repo.findById(DataCollector, id)
+ if (collector == null) return ApiResp.fail("Param id: $id not found")
+ collector.enabled = enabled
+ repo.saveOrUpdate(collector)
+ ep.fire('dataCollectorChange', collector.id)
+ ep.fire('enHistory', collector, hCtx.getSessionAttr('uName'))
+ ApiResp.ok(collector)
+ }
+
+
+ @Path(path = '{id}', method = 'delete')
+ ApiResp del(HttpContext hCtx, String id) {
+ if (!id) return ApiResp.fail("Param id required")
+ hCtx.auth('dataCollector-del')
+ def collector = repo.findById(DataCollector, id)
+ if (collector == null) return ApiResp.fail("Param id: $id not found")
+ repo.delete(collector)
+ ep.fire('dataCollectorChange', id)
+ ep.fire('enHistory', collector, hCtx.getSessionAttr('uName'))
+ ApiResp.ok()
+ }
+
+
+ @Path(path = 'test/{id}')
+ ApiResp testCollector(String id, HttpContext hCtx) {
+ if (!id) return ApiResp.fail("Param id required")
+ ApiResp.ok(collectorManager.testCollector(id, hCtx.params()))
+ }
+}
diff --git a/src/ctrl/MntCtrl.groovy b/src/ctrl/MntCtrl.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..6c7cc5cddf9aafaa4e627efdb770c7699ed49b42
--- /dev/null
+++ b/src/ctrl/MntCtrl.groovy
@@ -0,0 +1,121 @@
+package ctrl
+
+import cn.xnatural.app.CacheSrv
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.enet.event.EL
+import cn.xnatural.http.*
+import cn.xnatural.jpa.Repo
+import com.alibaba.fastjson.JSON
+import entity.Decision
+import entity.OpHistory
+import entity.User
+import entity.UserSession
+
+import java.text.SimpleDateFormat
+import java.util.concurrent.ConcurrentHashMap
+
+@Ctrl(prefix = 'mnt')
+class MntCtrl extends ServerTpl {
+
+ @Lazy def repo = bean(Repo, 'jpa_rule_repo')
+ protected final Set wss = ConcurrentHashMap.newKeySet()
+
+
+ @EL(name = 'wsMsg_rule')
+ void wsMsgBroadcast(String msg) { wss.each {ws -> ws.send(msg)} }
+
+ @WS(path = 'ws')
+ void receiveWs(WebSocket ws) {
+ log.info('WS connect. {}', ws.session.getRemoteAddress())
+ ws.listen(new WsListener() {
+
+ @Override
+ void onClose(WebSocket wst) { wss.remove(wst) }
+
+ @Override
+ void onText(String msg) {
+ log.info('rule mnt ws receive client msg: {}', msg)
+ }
+ })
+ wss.add(ws)
+ }
+
+
+ /**
+ * 登录
+ * @param username 用户名
+ * @param password 密码
+ */
+ @Path(path = 'login')
+ ApiResp login(String username, String password, HttpContext hCtx) {
+ if (!username) return ApiResp.fail('Param username not empty')
+ if (!password) return ApiResp.fail('Param password not empty')
+ def user = repo.find(User) {root, query, cb -> cb.equal(root.get('name'), username)}
+ if (!user) return ApiResp.fail("用户不存在")
+ def pIds = user.permissions?.split(',')?.toList()?.toSet()?:Collections.emptySet()
+ hCtx.setAttr('permissions', pIds)
+ hCtx.auth("mnt-login")
+ if (password != user.password) return ApiResp.fail('密码错误')
+ bean(CacheSrv)?.set("permission_" + user.id, pIds)
+
+ hCtx.setSessionAttr('uId', user.id)
+ hCtx.setSessionAttr('uName', user.name)
+ hCtx.setSessionAttr('uGroup', user.group)
+
+ user.login = new Date()
+ repo.saveOrUpdate(user)
+ repo.saveOrUpdate(new UserSession(valid: true, sessionId: hCtx.sessionId, userId: user.id, data: JSON.toJSONString([uId: user.id, uName: user.name, uGroup: user.group])))
+ currentUser(hCtx)
+ }
+
+
+ /**
+ * 获取当前 会话 中的用户信息
+ */
+ @Path(path = 'currentUser', method = 'get')
+ ApiResp currentUser(HttpContext hCtx) {
+ return ApiResp.ok().attr('id', hCtx.getSessionAttr('uId'))
+ .attr('name', hCtx.getSessionAttr('uName'))
+ .attr('permissionIds', hCtx.getAttr('permissions', Set))
+ }
+
+
+ /**
+ * 退出会话
+ */
+ @Path(path = 'logout')
+ ApiResp logout(HttpContext hCtx) {
+ hCtx.removeSessionAttr('uId')
+ hCtx.removeSessionAttr('uName')
+ hCtx.regFinishedFn {
+ def session = repo.findById(UserSession, hCtx.sessionId)
+ if (session && session.valid) {
+ session.valid = false
+ repo.saveOrUpdate(session)
+ }
+ }
+ ApiResp.ok()
+ }
+
+
+ @Path(path = 'opHistoryPage')
+ ApiResp opHistoryPage(HttpContext hCtx, Integer page, Integer pageSize, String kw, String type, String startTime, String endTime) {
+ if (pageSize && pageSize > 20) return ApiResp.fail("Param pageSize <=20")
+ hCtx.auth("opHistory-read")
+ Date start = startTime ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(startTime) : null
+ Date end = endTime ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(endTime) : null
+ ApiResp.ok(
+ repo.findPage(OpHistory, page, pageSize?:5) { root, query, cb ->
+ query.orderBy(cb.desc(root.get('createTime')))
+ def ps = []
+ if (kw) ps << cb.like(root.get('content'), '%' + kw + '%')
+ if (start) ps << cb.greaterThanOrEqualTo(root.get('createTime'), start)
+ if (end) ps << cb.lessThanOrEqualTo(root.get('createTime'), end)
+ if (type) {
+ ps << cb.equal(root.get('tbName'), repo.tbName(Class.forName(Decision.package.name + "." + type)).replace("`", ""))
+ }
+ ps ? ps.inject {p1, p2 -> cb.and(p1, p2)} : null
+ }
+ )
+ }
+}
diff --git a/src/ctrl/MntDataCtrl.groovy b/src/ctrl/MntDataCtrl.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..bc24c6349f0578a62fc842829d0ba3ad51e5845a
--- /dev/null
+++ b/src/ctrl/MntDataCtrl.groovy
@@ -0,0 +1,335 @@
+package ctrl
+
+import cn.xnatural.app.Inject
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.app.Utils
+import cn.xnatural.http.ApiResp
+import cn.xnatural.http.Ctrl
+import cn.xnatural.http.HttpContext
+import cn.xnatural.http.Path
+import cn.xnatural.jpa.Page
+import cn.xnatural.jpa.Repo
+import com.alibaba.fastjson.JSON
+import com.alibaba.fastjson.JSONObject
+import com.alibaba.fastjson.parser.Feature
+import entity.CollectRecord
+import entity.DecideRecord
+import service.rule.*
+
+import java.text.SimpleDateFormat
+import java.util.Map.Entry
+import java.util.function.Supplier
+
+@Ctrl(prefix = 'mnt/data')
+class MntDataCtrl extends ServerTpl {
+
+ @Inject(name = "jpa_rule_repo") protected Repo repo
+ @Inject protected DecisionManager decisionManager
+ @Inject protected FieldManager fieldManager
+ @Inject protected CollectorManager collectorManager
+
+
+
+ @Path(path = 'decisionResultPage')
+ ApiResp decisionResultPage(
+ HttpContext hCtx, Integer page, Integer pageSize, String id, String decisionId, DecideResult result,
+ Long spend, String exception, String attrConditions, String startTime, String endTime
+ ) {
+ hCtx.auth("decideResult-read")
+ if (pageSize && pageSize > 10) return ApiResp.fail("Param pageSize <=10")
+ def ids = hCtx.getAttr("permissions", Set)
+ .findAll {String p -> p.startsWith("decision-read-")}
+ .findResults {String p -> p.replace("decision-read-", "")}
+ .findAll {it}
+ if (!ids) return ApiResp.ok()
+ Date start = startTime ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(startTime) : null
+ Date end = endTime ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(endTime) : null
+ ApiResp.ok(
+ repo.findPage(DecideRecord, page, pageSize?:10) { root, query, cb ->
+ def ps = []
+ if (id) ps << cb.like(root.get('id'), id + "%")
+ if (start) ps << cb.greaterThanOrEqualTo(root.get('occurTime'), start)
+ if (end) ps << cb.lessThanOrEqualTo(root.get('occurTime'), end)
+ if (decisionId) ps << cb.equal(root.get('decisionId'), decisionId)
+ ps << root.get('decisionId').in(ids)
+ if (spend) ps << cb.ge(root.get('spend'), spend)
+ if (result) ps << cb.equal(root.get('result'), result)
+ if (exception) ps << cb.like(root.get('exception'), '%' + exception + '%')
+ def orders = []
+ if (attrConditions) { // json查询 暂时只支持mysql5.7+,MariaDB 10.2.3+
+ JSON.parseArray(attrConditions).each {JSONObject jo ->
+ def fieldId = jo.getLong('fieldId')
+ if (!fieldId) return
+ def field = fieldManager.fieldHolders.find {it.value.field.id == fieldId}?.value?.field
+ if (field == null) return
+
+ def exp = cb.function("JSON_EXTRACT", String, root.get('data'), cb.literal('$.' + field.enName))
+ def op = jo['op']
+ if (op == "desc") { //降序
+ orders << cb.desc(exp.as(field.type.clzType))
+ return
+ } else if (op == 'asc') { //升序
+ orders << cb.asc(exp.as(field.type.clzType))
+ return
+ }
+ def value = jo['value']
+ if (value == null || value.empty) return
+
+ if (op == '=') {
+ ps << cb.equal(exp, value)
+ } else if (op == '>') {
+ ps << cb.gt(exp.as(field.type.clzType), Utils.to(value, field.type.clzType))
+ } else if (op == '<') {
+ ps << cb.lt(exp.as(field.type.clzType), Utils.to(value, field.type.clzType))
+ } else if (op == '>=') {
+ ps << cb.ge(exp.as(field.type.clzType), Utils.to(value, field.type.clzType))
+ } else if (op == '<=') {
+ ps << cb.le(exp.as(field.type.clzType), Utils.to(value, field.type.clzType))
+ } else if (op == 'contains') {
+ ps << cb.like(exp, '%' + value + '%')
+ } else throw new IllegalArgumentException("Param attrCondition op('$op') unknown")
+ }
+ }
+ if (orders) { // 按照data中的属性进行排序
+ query.orderBy(orders)
+ } else { // 默认时间降序
+ query.orderBy(cb.desc(root.get('occurTime')))
+ }
+ ps ? ps.inject {p1, p2 -> cb.and(p1, p2)} : null
+ }.to{record ->
+ Utils.copier(record, [:]).ignore("metaClass")
+ .addConverter('data', {String jsonStr ->
+ jsonStr == null ? null : JSON.parseObject(jsonStr, Feature.OrderedField).collect { e ->
+ [enName: e.key, cnName: fieldManager.fieldHolders.get(e.key)?.field?.cnName, value: e.value]
+ }
+ })
+ .addConverter('input', {String jsonStr ->
+ jsonStr == null ? null : JSON.parseObject(jsonStr)
+ })
+ .addConverter('dataCollectResult', {String jsonStr ->
+ jsonStr == null ? null : JSON.parseObject(jsonStr, Feature.OrderedField).collectEntries { e ->
+ // 格式为: collectorId[_数据key], 把collectorId替换为收集器名字
+ String collectorId
+ def arr = e.key.split("_")
+ if (arr.length == 1) collectorId = e.key
+ else collectorId = arr[0]
+ [
+ (collectorManager.collectorHolders.findResult {
+ it.value.collector.id == collectorId ? it.value.collector.name + (arr.length > 1 ? '_' + arr.drop(1).join('_') : '') : null
+ }?:e.key): e.value
+ ]
+ }
+ })
+ .addConverter('detail', {String detailJsonStr ->
+ if (!detailJsonStr) return null
+ def detailJo = JSON.parseObject(detailJsonStr, Feature.OrderedField)
+ // 数据转换
+ detailJo.put('data', detailJo.getJSONObject('data')?.collect { Entry e ->
+ [enName: e.key, cnName: fieldManager.fieldHolders.get(e.key)?.field?.cnName, value: e.value]
+ }?:null)
+ detailJo.getJSONArray('policies')?.each {JSONObject pJo ->
+ pJo.put('data', pJo.getJSONObject('data')?.collect { Entry e ->
+ [enName: e.key, cnName: fieldManager.fieldHolders.get(e.key)?.field?.cnName, value: e.value]
+ }?:null)
+ pJo.getJSONArray('items')?.each {JSONObject rJo ->
+ rJo.put('data', rJo.getJSONObject('data')?.collect { Entry e ->
+ [enName: e.key, cnName: fieldManager.fieldHolders.get(e.key)?.field?.cnName, value: e.value]
+ }?:null)
+ }
+ }
+ detailJo
+ })
+ .add('decisionName', {
+ decisionManager.decisionMap.find {it.value.decision.id == record.decisionId}?.value?.decision?.name
+ } as Supplier)
+ .build()
+ }
+ )
+ }
+
+
+ @Path(path = 'collectResultPage')
+ ApiResp collectResultPage(
+ HttpContext hCtx, Integer page, Integer pageSize, String decideId, String collectorType, String collector, String decisionId,
+ Long spend, Boolean success, Boolean dataSuccess, Boolean cache, String startTime, String endTime
+ ) {
+ hCtx.auth("collectResult-read")
+ def ids = hCtx.getAttr("permissions", Set)
+ .findAll {String p -> p.startsWith("decision-read-")}
+ .findResults {String p -> p.replace("decision-read-", "")}
+ .findAll {it}
+ if (!ids) return ApiResp.ok()
+ Date start = startTime ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(startTime) : null
+ Date end = endTime ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(endTime) : null
+ if (pageSize && pageSize > 50) return ApiResp.fail("Param pageSize <=50")
+ ApiResp.ok(
+ repo.findPage(CollectRecord, page, pageSize?:10) { root, query, cb ->
+ query.orderBy(cb.desc(root.get('collectDate')))
+ def ps = []
+ if (start) ps << cb.greaterThanOrEqualTo(root.get('collectDate'), start)
+ if (end) ps << cb.lessThanOrEqualTo(root.get('collectDate'), end)
+ if (decideId) ps << cb.like(root.get('decideId'), decideId + "%")
+ ps << root.get('decisionId').in(ids)
+ if (decisionId) ps << cb.equal(root.get('decisionId'), decisionId)
+ if (collectorType) ps << cb.equal(root.get('collectorType'), collectorType)
+ if (collector) ps << cb.equal(root.get('collector'), collector)
+ if (spend) ps << cb.ge(root.get('spend'), spend)
+ if (success != null) {
+ if (success) {
+ ps << cb.equal(root.get('status'), '0000')
+ } else {
+ ps << cb.notEqual(root.get('status'), '0000')
+ }
+ }
+ if (dataSuccess != null) {
+ if (dataSuccess) {
+ ps << cb.equal(root.get('dataStatus'), '0000')
+ } else {
+ ps << cb.notEqual(root.get('dataStatus'), '0000')
+ }
+ }
+ if (cache != null) {
+ if (cache) {
+ ps << cb.equal(root.get('cache'), true)
+ } else {
+ ps << cb.notEqual(root.get('cache'), true)
+ }
+ }
+ ps ? ps.inject {p1, p2 -> cb.and(p1, p2)} : null
+ }.to{record -> Utils.copier(record, [:]).ignore("metaClass")
+ .add("decisionName", {
+ decisionManager.decisionMap.find {it.value.decision.id == record.decisionId}?.value?.decision?.name
+ } as Supplier)
+ .add("collectorName", {
+ collectorManager.collectorHolders.get(record.collector)?.collector?.name
+ } as Supplier)
+ .add("collectorType", {
+ collectorManager.collectorHolders.get(record.collector)?.collector?.type
+ } as Supplier)
+ .build()
+ }
+ )
+ }
+
+
+ /**
+ * 统计一段时间的决策结果
+ * @param startTime required yyyy-MM-dd HH:mm:ss
+ * @param endTime yyyy-MM-dd HH:mm:ss
+ */
+ @Path(path = 'countDecide')
+ ApiResp countDecide(HttpContext hCtx, String startTime, String endTime) {
+ if (startTime == null) return ApiResp.fail("Param startTime required")
+ Date start = startTime ? new SimpleDateFormat('yyyy-MM-dd HH:mm:ss').parse(startTime) : null
+ Date end = endTime ? new SimpleDateFormat('yyyy-MM-dd HH:mm:ss').parse(endTime) : null
+ def ids = hCtx.getAttr("permissions", Set)
+ .findAll {String p -> p.startsWith("decision-read-")}
+ .findResults {String p -> p.replace("decision-read-", "")}
+ .findAll {it}
+ if (!ids) return ApiResp.ok()
+ hCtx.response.cacheControl(2) // 缓存2秒
+ String sql = """
+ select t1.decision_id, t2.name decisionName, t1.result, count(1) total from ${repo.tbName(DecideRecord).replace("`", '')} t1
+ left join decision t2 on t1.decision_id = t2.id
+ where t1.occur_time>=:start${end ? " and t1.occur_time<=:end" : ""} and t1.decision_id in (:ids) and t1.result is not null
+ group by t1.decision_id, t1.result
+ """.trim()
+ ApiResp.ok(end ? repo.rows(sql, start, end, ids) : repo.rows(sql, start, ids))
+ }
+
+
+ /**
+ * 统计一段时间的规则结果
+ * @param decisionId 决策id
+ * @param startTime required yyyy-MM-dd HH:mm:ss
+ * @param endTime yyyy-MM-dd HH:mm:ss
+ */
+ @Path(path = 'countRule')
+ ApiResp countRule(HttpContext hCtx, String decisionId, String startTime, String endTime) {
+ if (startTime == null) return ApiResp.fail("Param startTime required")
+ Date start = startTime ? new SimpleDateFormat('yyyy-MM-dd HH:mm:ss').parse(startTime) : null
+ Date end = endTime ? new SimpleDateFormat('yyyy-MM-dd HH:mm:ss').parse(endTime) : null
+ def ids = hCtx.getAttr("permissions", Set)
+ .findAll {String p -> p.startsWith("decision-read-")}
+ .findResults {String p -> p.replace("decision-read-", "")}
+ .findAll {it}
+ if (!ids) return ApiResp.ok().desc("无可查看的决策")
+ ids = decisionId ? ids.findAll {it == decisionId} : ids
+ if (!ids) return ApiResp.ok().desc("无可查看的决策")
+ hCtx.response.cacheControl(60) // 缓存60秒
+
+ // mysql 8.0.4+ or mariaDb 10.6+ 有json_table函数
+ def verArr = repo.getDBVersion().split("\\.")
+ if (
+ (repo.dialect.containsIgnoreCase("mysql") && verArr[0].toInteger() >= 8 && verArr[2].toInteger() >= 4) ||
+ (repo.dialect.containsIgnoreCase("maria") && verArr[0].toInteger() >= 10 && verArr[1].toInteger() >= 6)
+ ) {
+ String sql = """
+ SELECT
+ t1.decision_id decisionId, t3.name decisionName, t2.policyName, t2.ruleName, t2.result, count(1) total
+ FROM decide_record t1
+ join json_table(JSON_SET(t1.detail, '\$.id', t1.id),
+ '\$' COLUMNS (
+ id varchar(50) path '\$.id',
+ NESTED PATH '\$.policies[*]' COLUMNS (
+ policyName varchar(200) path '\$.attrs."策略名"',
+ NESTED PATH '\$.rules[*]' COLUMNS (
+ ruleName varchar(200) path '\$.attrs."规则名"',
+ result varchar(20) path '\$.result'
+ )))
+ ) t2 on t2.id=t1.id
+ left join decision t3 on t1.decision_id = t3.id
+ where
+ t1.occur_time>=:start${end ? " and t1.occur_time<=:end" : ""} and t1.decision_id in (:ids)
+ and t1.detail is not null and t1.result is not null
+ group by decisionId, policyName, ruleName, result
+ order by case t2.result when 'Reject' then 3 when 'Review' then 2 when 'Accept' then 1 when isnull(t2.result) then 0 end desc, total desc
+ limit 30
+ """
+ return ApiResp.ok(end ? repo.rows(sql, start, end, ids) : repo.rows(sql, start, ids))
+ }
+
+ String sql = """
+ select t1.decision_id decisionId, t2.name decisionName, t1.detail
+ from ${repo.tbName(DecideRecord).replace("`", '')} t1
+ left join decision t2 on t1.decision_id = t2.id
+ where
+ t1.occur_time>=:start${end ? " and t1.occur_time<=:end" : ""} and t1.decision_id in (:ids)
+ and t1.detail is not null and t1.result is not null
+ """.trim()
+
+ def ls = [] as LinkedList
+ for (int page = 1, pageSize = 100; ;page++) {
+ Page rPage = end ? repo.sqlPage(sql, page, pageSize, start, end, ids) : repo.sqlPage(sql, page, pageSize, start, ids)
+ ls.addAll(rPage.list)
+ if (page >= rPage.totalPage) break
+ }
+ // decisionId, decisionName, policyName, ruleName, result, total
+ ApiResp.ok(
+ ls.findResults {Map record ->
+ record["detail"] ? JSON.parseObject(record["detail"])?.getJSONArray("policies")?.findResults {JSONObject pJo ->
+ pJo.getJSONArray("items")?.findAll {JSONObject rJo -> rJo['attrs']['规则名']}?.findResults { JSONObject rJo ->
+ record['decisionId'] + '||' + record['decisionName'] + '||' + pJo['attrs']['策略名'] +'||' + rJo['attrs']['规则名'] + '||' + (rJo['result']?:DecideResult.Accept)
+ }?:[]
+ }?.flatten() : []
+ }.flatten().countBy {it}.findResults {e ->
+ def arr = e.key.split("\\|\\|")
+ return [decisionId: arr[0], decisionName: arr[1], policyName: arr[2], ruleName: arr[3], result: arr[4], total: e.value]
+ }.sort {o1, o2 ->
+ // 把拒绝多的排前面
+ if (o1['result'] == "Reject" && o2['result'] == "Reject") return o2['total'] - o1['total']
+ else if (o1['result'] == "Reject") return -1
+ else if (o2['result'] == "Reject") return 1
+ else return 0
+ }.takeRight(decisionId ? Integer.MAX_VALUE : 30) // 如果是指定某个决策, 则全部显示, 如果是查所有则限制显示(有可能会得多)
+ )
+ }
+
+
+ @Path(path = 'cleanExpire')
+ ApiResp cleanExpire(HttpContext hCtx) {
+ hCtx.auth("grant")
+ bean(DecisionSrv).cleanDecideRecord()
+ return ApiResp.ok().desc("等待后台清理完成...")
+ }
+}
diff --git a/src/ctrl/MntDecisionCtrl.groovy b/src/ctrl/MntDecisionCtrl.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..019d76a7422fa1e59d596b848ec333b6e9307934
--- /dev/null
+++ b/src/ctrl/MntDecisionCtrl.groovy
@@ -0,0 +1,214 @@
+package ctrl
+
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.app.Utils
+import cn.xnatural.http.ApiResp
+import cn.xnatural.http.Ctrl
+import cn.xnatural.http.HttpContext
+import cn.xnatural.http.Path
+import cn.xnatural.jpa.Page
+import cn.xnatural.jpa.Repo
+import com.alibaba.fastjson.JSON
+import com.alibaba.fastjson.JSONArray
+import com.alibaba.fastjson.JSONObject
+import entity.Decision
+import service.rule.CollectorManager
+import service.rule.spec.DecisionSpec
+
+import java.util.function.Supplier
+
+@Ctrl(prefix = 'mnt/decision')
+class MntDecisionCtrl extends ServerTpl {
+
+ @Lazy protected repo = bean(Repo, 'jpa_rule_repo')
+ @Lazy protected collectorManager = bean(CollectorManager)
+
+
+ @Path(path = 'page')
+ ApiResp decisionPage(HttpContext hCtx, Integer page, Integer pageSize, String kw, String nameLike, String decisionId) {
+ if (pageSize && pageSize > 20) return ApiResp.fail("Param pageSize <=20")
+ // 允许访问的决策id
+ def ids = hCtx.getAttr("permissions", Set)
+ .findAll {String p -> p.startsWith("decision-read-")}
+ .findResults {String p -> p.replace("decision-read-", "")}
+ .findAll {it}
+ if (!ids) return ApiResp.ok(Page.empty())
+ def delIds = hCtx.getAttr("permissions", Set)
+ .findAll {String p -> p.startsWith("decision-del-")}
+ .findResults {String p -> p.replace("decision-del-", "")}
+ .findAll {it}
+ def updateIds = hCtx.getAttr("permissions", Set)
+ .findAll {String p -> p.startsWith("decision-update-")}
+ .findResults {String p -> p.replace("decision-update-", "")}
+ .findAll {it}
+ ApiResp.ok(
+ repo.findPage(Decision, page, pageSize?:10) {root, query, cb ->
+ query.orderBy(cb.desc(root.get('updateTime')))
+ def ps = []
+ ps << root.get("id").in(ids)
+ if (decisionId) ps << cb.equal(root.get('id'), decisionId)
+ if (nameLike) ps << cb.or(cb.like(root.get('name'), '%' + nameLike + '%'), cb.like(root.get('decisionId'), '%' + nameLike + '%'))
+ if (kw) ps << cb.like(root.get('dsl'), '%' + kw + '%')
+ ps ? ps.inject {p1, p2 -> cb.and(p1, p2)} : null
+ }.to{decision ->
+ Utils.copier(decision, [:]).ignore("metaClass")
+ .add("_deletable", { delIds?.contains(decision.id) } as Supplier)
+ .add("_readonly", { !updateIds?.contains(decision.id) } as Supplier)
+ .build()
+ }
+ )
+ }
+
+
+ /**
+ * 添加一条决策
+ * @param dsl 决策DSL
+ * @param apiConfig api配置
+ * @param hCtx {@link HttpContext}
+ */
+ @Path(path = '/', method = 'put')
+ ApiResp add(HttpContext hCtx, String dsl, String apiConfig) {
+ if (!dsl) return ApiResp.fail("Param dsl required")
+ hCtx.auth('decision-add')
+ DecisionSpec spec
+ try {
+ spec = DecisionSpec.of(dsl)
+ } catch (ex) {
+ log.error("语法错误", ex)
+ return ApiResp.fail('语法错误: ' + ex.message)
+ }
+ //dsl 验证
+ if (!spec.决策id) return ApiResp.fail("决策id 不能为空")
+ if (!spec.决策名) return ApiResp.fail("决策名 不能为空")
+ // TODO 验证子决策是否存在
+
+ final decision = new Decision(
+ decisionId: spec.决策id, name: spec.决策名, comment: spec.决策描述,
+ dsl: dsl, creator: hCtx.getSessionAttr("uName")
+ )
+ adjustApiConfig(decision, apiConfig)
+
+ try {
+ repo.saveOrUpdate(decision)
+ } catch (ex) {
+ def cause = ex
+ while (cause != null) {
+ if (cause.message.contains("Duplicate entry")) {
+ if (cause.message.contains("decisionId")) {
+ return ApiResp.fail("决策id($spec.决策id)已存在")
+ }
+ }
+ cause = cause.cause
+ }
+ throw ex
+ }
+ ep.fire("decision.create", decision.id)
+ ep.fire('enHistory', decision, decision.creator)
+ ApiResp.ok(decision)
+ }
+
+
+ @Path(path = "updateDsl/{id}", method = "post")
+ ApiResp updateDsl(HttpContext hCtx, String id, String dsl) {
+ if (!id) return ApiResp.fail("Param id required")
+ if (!dsl) return ApiResp.fail("Param dsl required")
+ def decision = repo.findById(Decision, id)
+ if (!decision) return ApiResp.fail("Param id not found")
+ hCtx.auth('decision-update-' + id)
+
+ DecisionSpec spec
+ try {
+ spec = DecisionSpec.of(dsl)
+ } catch (ex) {
+ log.error("语法错误", ex)
+ return ApiResp.fail('语法错误: ' + ex.message)
+ }
+ //dsl 验证
+ if (!spec.决策id) return ApiResp.fail("决策id 不能为空")
+ if (!spec.决策名) return ApiResp.fail("决策名 不能为空")
+ decision.dsl = dsl
+ decision.name = spec.决策名
+ decision.decisionId = spec.决策id
+ decision.comment = spec.决策描述
+ decision.updater = hCtx.getSessionAttr('uName')
+
+ try {
+ repo.saveOrUpdate(decision)
+ } catch (ex) {
+ def cause = ex
+ while (cause != null) {
+ if (cause.message.contains("Duplicate entry")) {
+ if (cause.message.contains("decisionId")) {
+ return ApiResp.fail("决策id($spec.决策id)已存在")
+ }
+ }
+ cause = cause.cause
+ }
+ throw ex
+ }
+
+ ep.fire("decision.update", decision.id)
+ ep.fire('enHistory', decision, decision.updater)
+ ApiResp.ok(decision)
+ }
+
+
+ @Path(path = "updateApiConfig/{id}", method = "post")
+ ApiResp updateApiConfig(HttpContext hCtx, String id, String apiConfig) {
+ if (!id) return ApiResp.fail("Param id required")
+ def decision = repo.findById(Decision, id)
+ if (!decision) return ApiResp.fail("Param id not found")
+ hCtx.auth('decision-update-' + id)
+
+ adjustApiConfig(decision, apiConfig)
+ decision.updater = hCtx.getSessionAttr('uName')
+ repo.saveOrUpdate(decision)
+
+ ep.fire("decision.update", decision.id)
+ ep.fire('enHistory', decision, decision.updater)
+ ApiResp.ok(decision)
+ }
+
+
+ @Path(path = ':id', method = "delete")
+ ApiResp del(HttpContext hCtx, String id) {
+ if (!id) return ApiResp.fail("Param id required")
+ def decision = repo.findById(Decision, id)
+ if (!decision) return ApiResp.fail("Param id not found")
+ hCtx.auth( 'decision-del-' + id)
+ repo.delete(decision)
+
+ ep.fire('decision.delete', decision.id)
+ // ep.fire('enHistory', decision, hCtx.getSessionAttr('uName'))
+ ApiResp.ok()
+ }
+
+
+ // 更新 适配 api配置 参数
+ protected void adjustApiConfig(Decision decision, String apiConfig) {
+ if (apiConfig) {
+ def params = JSON.parseArray(apiConfig)
+ JSONObject param = params.find {JSONObject jo -> "decisionId" == jo.getString("code")}
+ if (param) {
+ param.fluentPut("fixValue", decision.decisionId).fluentPut("type", "Str").fluentPut("require", true)
+ } else {
+ params.add(0, new JSONObject().fluentPut("code", "decisionId").fluentPut("type", "Str")
+ .fluentPut("fixValue", decision.decisionId).fluentPut("name", "决策id").fluentPut("require", true)
+ )
+ }
+ for (def itt = params.iterator(); itt.hasNext(); ) {
+ JSONObject jo = itt.next()
+ if (!jo.getString("code") || !jo.getString("name")) itt.remove()
+ for (def ittt = jo.iterator(); ittt.hasNext(); ) {
+ if (ittt.next().key.startsWith('_')) ittt.remove()
+ }
+ }
+ decision.apiConfig = params.toString()
+ } else {
+ decision.apiConfig = new JSONArray().add(
+ new JSONObject().fluentPut("code", "decisionId").fluentPut("type", "Str")
+ .fluentPut("fixValue", decision.decisionId).fluentPut("name", "决策id").fluentPut("require", true)
+ ).toString()
+ }
+ }
+}
diff --git a/src/ctrl/MntFieldCtrl.groovy b/src/ctrl/MntFieldCtrl.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..2dc5370d3befd40c3e5f163523d64d17f623e915
--- /dev/null
+++ b/src/ctrl/MntFieldCtrl.groovy
@@ -0,0 +1,179 @@
+package ctrl
+
+import cn.xnatural.app.Inject
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.app.Utils
+import cn.xnatural.http.ApiResp
+import cn.xnatural.http.Ctrl
+import cn.xnatural.http.HttpContext
+import cn.xnatural.http.Path
+import cn.xnatural.jpa.Repo
+import com.alibaba.fastjson.JSON
+import com.alibaba.fastjson.JSONObject
+import entity.DataCollector
+import entity.FieldType
+import entity.RuleField
+import service.rule.CollectorManager
+import service.rule.DecisionManager
+import service.rule.FieldManager
+
+import java.util.function.Supplier
+
+
+@Ctrl(prefix = 'mnt/field')
+class MntFieldCtrl extends ServerTpl {
+
+ @Inject(name = "jpa_rule_repo") protected Repo repo
+ @Inject protected DecisionManager decisionManager
+ @Inject protected FieldManager fieldManager
+ @Inject protected CollectorManager collectorManager
+ // 指标字符限制: 字母,中文开头,可包含字数,字母,下划线,中文
+ @Lazy protected fieldNamePattern = /^[a-zA-Z\u4E00-\u9FA5]+[0-9a-zA-Z\u4E00-\u9FA5_]*$/
+
+
+ @Path(path = 'page')
+ ApiResp page(HttpContext hCtx, Integer page, Integer pageSize, String collector, String kw) {
+ if (pageSize && pageSize > 50) return ApiResp.fail("Param pageSize <=50")
+ hCtx.auth("field-read")
+ ApiResp.ok(repo.findPage(RuleField, page, (pageSize?:10)) { root, query, cb ->
+ query.orderBy(cb.desc(root.get('updateTime')))
+ def ps = []
+ if (kw) {
+ ps << cb.or(
+ cb.like(root.get('enName'), '%' + kw + '%'),
+ cb.like(root.get('cnName'), '%' + kw + '%'),
+ cb.like(root.get('comment'), '%' + kw + '%')
+ )
+ }
+ if (collector) {
+ ps << cb.function("JSON_SEARCH", String, root.get('collectorOptions'), cb.literal('one'), cb.literal(collector)).isNotNull()
+ }
+ ps ? ps.inject {p1, p2 -> cb.and(p1, p2)} : null
+ }.to{field -> Utils.copier(field, [:]).ignore("metaClass")
+ .addConverter("collectorOptions") {String opts ->
+ opts == null ? null : JSON.parseArray(opts).collect {JSONObject jo ->
+ jo.fluentPut(
+ "collectorName",
+ collectorManager.collectorHolders.find {it.key == jo['collectorId']}?.value?.collector?.name
+ )
+ }
+ }
+ .build()
+ })
+ }
+
+
+ @Path(path = '/', method = 'put')
+ ApiResp add(HttpContext hCtx, String enName, String cnName, FieldType type, String comment, String collectorOptions) {
+ hCtx.auth('field-add')
+ if (!enName) return ApiResp.fail("Param enName required")
+ if (!cnName) return ApiResp.fail("Param cnName required")
+ if (!type) return ApiResp.fail("Param type required")
+ if (!(enName ==~ fieldNamePattern)) {
+ return ApiResp.fail("Param enName illegal: 字母或中文开头,可包含字数,字母,下划线,中文")
+ }
+ if (!(cnName ==~ fieldNamePattern)) {
+ return ApiResp.fail("Param enName illegal: 字母或中文开头,可包含字数,字母,下划线,中文")
+ }
+ // 验证 collectorOptions
+ if (collectorOptions) {
+ JSON.parseArray(collectorOptions).each {JSONObject jo ->
+ if (!jo['collectorId']) throw new IllegalArgumentException("Param collectorOptions.collectorId required")
+ if (!repo.count(DataCollector) {root, query, cb -> cb.equal(root.get("id"), jo['collectorId'])}) {
+ throw new IllegalArgumentException("Param collectorOptions.collectorId not found")
+ }
+ }
+ }
+ def field = new RuleField(
+ enName: enName, cnName: cnName, type: type, comment: comment,
+ collectorOptions: collectorOptions, creator: hCtx.getSessionAttr('uName')
+ )
+ try {
+ repo.saveOrUpdate(field)
+ } catch(ex) {
+ def cause = ex
+ while (cause != null) {
+ if (cause.message.contains("Duplicate entry")) {
+ if (cause.message.contains("cnName")) {
+ return ApiResp.fail("$cnName aleady exist")
+ }
+ if (cause.message.contains("enName")) {
+ return ApiResp.fail("$enName aleady exist")
+ }
+ }
+ cause = cause.cause
+ }
+ throw ex
+ }
+ ep.fire('fieldChange', field.id)
+ ep.fire('enHistory', field, hCtx.getSessionAttr('uName'))
+ ApiResp.ok(field)
+ }
+
+
+ @Path(path = ':id', method = 'post')
+ ApiResp update(HttpContext hCtx, Long id, String enName, String cnName, FieldType type, String comment, String collectorOptions) {
+ hCtx.auth('field-update')
+ if (!id) return ApiResp.fail("Param id required")
+ if (!enName) return ApiResp.fail("Param enName required")
+ if (!cnName) return ApiResp.fail("Param cnName required")
+ if (!type) return ApiResp.fail("Param type required")
+ if (!(enName ==~ fieldNamePattern)) {
+ return ApiResp.fail("Param enName illegal: 字母或中文开头,可包含字数,字母,下划线,中文")
+ }
+ if (!(cnName ==~ fieldNamePattern)) {
+ return ApiResp.fail("Param enName illegal: 字母或中文开头,可包含字数,字母,下划线,中文")
+ }
+ def field = repo.findById(RuleField, id)
+ if (field == null) return ApiResp.fail("Param id: $id not found")
+
+ // 验证 collectorOptions
+ if (collectorOptions) {
+ JSON.parseArray(collectorOptions).each {JSONObject jo ->
+ if (!jo['collectorId']) throw new IllegalArgumentException("Param collectorOptions.collectorId required")
+ if (!repo.count(DataCollector) {root, query, cb -> cb.equal(root.get("id"), jo['collectorId'])}) {
+ throw new IllegalArgumentException("Param collectorOptions.collectorId not found")
+ }
+ }
+ }
+ field.enName = enName
+ field.cnName = cnName
+ field.type = type
+ field.comment = comment
+ field.collectorOptions = collectorOptions
+ field.updater = hCtx.getSessionAttr("uName")
+ try {
+ repo.saveOrUpdate(field)
+ } catch (ex) {
+ def cause = ex
+ while (cause != null) {
+ if (cause.message.contains("Duplicate entry")) {
+ if (cause.message.contains("cnName")) {
+ return ApiResp.fail("$cnName aleady exist")
+ }
+ if (cause.message.contains("enName")) {
+ return ApiResp.fail("$enName aleady exist")
+ }
+ }
+ cause = cause.cause
+ }
+ throw ex
+ }
+ ep.fire('fieldChange', field.id)
+ ep.fire('enHistory', field, hCtx.getSessionAttr('uName'))
+ ApiResp.ok(field)
+ }
+
+
+ @Path(path = ':id', method = "delete")
+ ApiResp del(HttpContext hCtx, Long id) {
+ if (!id) return ApiResp.fail("Param id required")
+ hCtx.auth('field-del')
+ def field = repo.findById(RuleField, id)
+ if (!field) return ApiResp.fail("Param id not found")
+ repo.delete(field)
+ ep.fire('fieldChange', id)
+ ep.fire('enHistory', field, hCtx.getSessionAttr('uName'))
+ ApiResp.ok()
+ }
+}
diff --git a/src/ctrl/MntUserCtrl.groovy b/src/ctrl/MntUserCtrl.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..5715adbe324f7e1bf72234d1b2f39dabb2e94dd5
--- /dev/null
+++ b/src/ctrl/MntUserCtrl.groovy
@@ -0,0 +1,466 @@
+package ctrl
+
+import cn.xnatural.app.CacheSrv
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.app.Utils
+import cn.xnatural.http.ApiResp
+import cn.xnatural.http.Ctrl
+import cn.xnatural.http.HttpContext
+import cn.xnatural.http.Path
+import cn.xnatural.jpa.Page
+import cn.xnatural.jpa.Repo
+import entity.Permission
+import entity.User
+
+import javax.persistence.criteria.Predicate
+import java.util.function.Supplier
+
+/**
+ * 用户, 权限相关接口
+ */
+@Ctrl(prefix = 'mnt/user')
+class MntUserCtrl extends ServerTpl {
+ @Lazy protected cacheSrv = bean(CacheSrv)
+ @Lazy protected repo = bean(Repo, 'jpa_rule_repo')
+
+
+ /**
+ * 用户分页查询接口
+ * @param hCtx
+ * @param page >= 1
+ * @param pageSize <= 50
+ * @param kw 搜索关键字
+ * @return
+ */
+ @Path(path = 'page')
+ ApiResp page(HttpContext hCtx, Integer page, Integer pageSize, String kw) {
+ if (pageSize && pageSize > 50) return ApiResp.fail("Param pageSize <=50")
+ boolean hasGrant = hCtx.hasAuth("grant")
+ boolean hasGrantUser = hCtx.hasAuth("grant-user")
+ boolean hasUserDel = hCtx.hasAuth("user-del")
+ ApiResp.ok(
+ repo.findPage(User, page, pageSize?:8) {root, query, cb ->
+ query.orderBy(cb.asc(root.get('createTime')))
+ def ps = []
+ //1. 非grant,grant-user用户: 只能看自己的信息
+ if (!hasGrant && !hasGrantUser) {
+ ps << cb.equal(root.get("id"), hCtx.getSessionAttr("uId"))
+ }
+ //2. 只grant-user用户: 能看到同一组的所有用户
+ else if (!hasGrant && hasGrantUser) {
+ if (hCtx.getSessionAttr("uGroup")) {
+ ps << cb.equal(root.get("group"), hCtx.getSessionAttr("uGroup"))
+ } else {
+ ps << cb.equal(root.get("id"), hCtx.getSessionAttr("uId"))
+ }
+ }
+ //3. grant用户: 默认可以看所有用户
+ else {}
+
+ if (kw) {
+ ps << cb.like(root.get("name"), "%" + kw + "%")
+ }
+ cb.and(ps.toArray(new Predicate[ps.size()]))
+ }.to {user ->
+ Utils.copier(user, [:]).ignore("metaClass", "password")
+ .addConverter("permissions", {String pIdStr ->
+ def pIds = pIdStr?.split(",")
+ if (!pIds) return []
+ def ps = repo.findList(Permission) {root, query, cb -> root.get("enName").in(pIds)}
+ pIds.collect {pId ->
+ if (!pId) return null
+ for (def p : ps ) {
+ if (p.enName == pId) {
+ return [(pId): p.cnName]
+ }
+ }
+ }.findAll {it}
+ })
+ .add("_readonly", {
+ if (hasGrant) return false
+ if (hasGrantUser) {
+ if (user.permissions?.split(",")?.contains("grant")) return true
+ else if (user.permissions?.contains("grant-user")) {
+ if (Utils.to(hCtx.getSessionAttr("uId"), Long) == user.id) return false
+ else return true
+ } else return false
+ }
+ return true
+ } as Supplier)
+ .add("_deletable", {
+ if (!hasUserDel) return false
+ if (hasGrant) return true
+ if (hasGrantUser) {
+ if (user.permissions?.split(",")?.contains("grant")) return false
+ else if (user.permissions?.contains("grant-user")) return false
+ else return true
+ }
+ return false
+ } as Supplier)
+ .add("_restPassword", {
+ if (hasGrant) return true
+ if (hasGrantUser) {
+ if (user.permissions?.contains("grant")) return false
+ else return true
+ }
+ return false
+ } as Supplier)
+ .build()
+ }
+ )
+ }
+
+
+ /**
+ * 添加用户
+ * @param hCtx
+ * @param name 用户名
+ * @param password 密码
+ * @param group 组名
+ * @param permissionIds 权限标识
+ */
+ @Path(path = 'add')
+ ApiResp add(HttpContext hCtx, String name, String password, String group, String[] permissionIds) {
+ //1. grant, grant-user 可以添加用户
+ hCtx.auth("grant", "grant-user", "user-add")
+ if (!name) return ApiResp.fail("Param name not empty")
+ name = name.trim()
+ if (name.length() < 2) return ApiResp.fail("Param name length >= 2")
+ if (!password) return ApiResp.fail("Param password not empty")
+ password = password.trim()
+ if (password.length() < 2) return ApiResp.fail("Param password length >= 2")
+ ApiResp resp = ApiResp.ok()
+
+ //2. 只grant-user: 不能分配grant, grant-user; 添加的用户默认同一组, 忽略group参数
+ if (!hCtx.hasAuth("grant") && hCtx.hasAuth("grant-user")) {
+ if (permissionIds?.contains("grant") || permissionIds?.contains("grant-user")) {
+ return ApiResp.fail("Not permission")
+ }
+ // 验证是否都是可以分配的权限
+ for (def pId: permissionIds) {
+ if (!(permissionPage(hCtx, 1, 1, null, pId, null).data?.list?.find {it.enName == pId})) {
+ return ApiResp.fail("Not permission, $pId")
+ }
+ }
+
+ if (group) resp.desc("Param group unnecessary, ignore")
+ group = hCtx.getSessionAttr("uGroup") // grant-user权限 创建的用户 自动同组
+ }
+
+ def eUser = repo.saveOrUpdate(
+ new User(name: name, password: password, group: group, permissions: permissionIds?.findAll {it?.trim()}?.unique()?.join(','))
+ )
+ resp.setData(eUser.id)
+ return resp
+ }
+
+
+ /**
+ * 删除用户
+ * @param hCtx
+ * @param id id
+ */
+ @Path(path = 'del/{id}')
+ ApiResp del(HttpContext hCtx, Long id) {
+ //1. grant, grant-user 可以删除用户
+ hCtx.auth("grant", "grant-user", "user-del")
+ if (!id) return ApiResp.fail("Param id required")
+
+ //2. 只grant-user: 只能删除同组用户(不包括自己,不包括grant-user,grant)
+ if (!hCtx.hasAuth("grant") && hCtx.hasAuth("grant-user")) {
+ // 不能删除自己
+ if (id == Utils.to(hCtx.getSessionAttr("uId"), Long)) {
+ ApiResp.fail("Not permission, delete self")
+ }
+ // 没有组,不能删除任何用户
+ if (!hCtx.getSessionAttr("uGroup")) {
+ ApiResp.fail("Not permission, no deletable user")
+ }
+ def eUser = repo.findById(User, id)
+ // 不能删除非同组的用户
+ if (eUser.group != hCtx.getSessionAttr("uGroup")) {
+ return ApiResp.fail("Not permission, not same group")
+ }
+ // 不能删除grant,grant-user用户
+ if (eUser.permissions?.contains("grant")) {
+ return ApiResp.fail("Not permission, has grant")
+ }
+ }
+ //3. grant用户: 随便删
+
+ cacheSrv?.remove("permission_" + id)
+ return ApiResp.ok(repo.delete(User, id))
+ }
+
+
+ /**
+ * 更新用户信息
+ * @param hCtx
+ * @param id id
+ * @param group 组
+ * @param permissionIds 权限标识
+ */
+ @Path(path = 'update')
+ ApiResp update(HttpContext hCtx, Long id, String group, String[] permissionIds) {
+ //1. grant, grant-user 可以更新用户
+ hCtx.auth("grant", "grant-user")
+ if (!id) return ApiResp.fail("Param id required")
+ def user = repo.findById(User, id)
+ if (!user) return ApiResp.fail("用户不存在")
+ ApiResp resp = ApiResp.ok()
+ //更新用户是否是自己
+ boolean targetUserIsMe = id == Utils.to(hCtx.getSessionAttr("uId"), Long)
+
+ //2. 只grant-user: 只能分配同组用户(包括自己,非grant,grant-user)的权限(自己拥有的(除grant,grant-user),同组用户产生的动态权限); 忽略group参数
+ String originGroup //原来的group
+ if (!hCtx.hasAuth("grant") && hCtx.hasAuth("grant-user")) {
+ // 没有组, 则只能为自己分配权限
+ if (!hCtx.getSessionAttr("uGroup") && !targetUserIsMe) {
+ return ApiResp.fail("Not permission, only allow self")
+ }
+ // 不能为非同组分配权限
+ if (user.group != hCtx.getSessionAttr("uGroup")) {
+ return ApiResp.fail("Not permission, not same group")
+ }
+ def targetUserPIds = user.permissions?.split(",")
+ // 不能为grant-user用户(除自己)分配权限
+ if (targetUserPIds?.contains("grant-user") && !targetUserIsMe) {
+ return ApiResp.fail("Not permission, has grant-user")
+ }
+ // 不能为grant的用户分配权限
+ if (targetUserPIds?.contains("grant")) {
+ return ApiResp.fail("Not permission, has grant")
+ }
+
+ // 不能分配grant-user权限(除自己)
+ if (permissionIds?.contains("grant-user") && !targetUserIsMe) {
+ return ApiResp.fail("Not permission, other grant-user")
+ }
+ // 不能分配grant权限
+ if (permissionIds?.contains("grant")) {
+ return ApiResp.fail("Not permission, grant")
+ }
+ // 验证是否都是可以分配的权限
+ for (def pId : permissionIds) {
+ // 自己已有的权限不必验证
+ if (targetUserPIds?.contains(pId)) continue
+
+ if (!(permissionPage(hCtx, 1, 1, null, pId, null).data?.list?.find {it.enName == pId})) {
+ return ApiResp.fail("Not permission, " + pId)
+ }
+ }
+
+ // 忽略group参数
+ if (group && group != hCtx.getSessionAttr("uGroup")) {
+ resp.desc("Param group unnecessary, ignore")
+ }
+ }
+
+ //3. grant用户: 如果修改了 组管理员(即有grant-user权限)用户 的组名, 则需要把同一组的其它用户的group同步修改
+ if (hCtx.hasAuth("grant")) {
+ if (group && user.group != group) { // 修改用户所属组
+ if (user.permissions?.contains("grant-user")) {
+ originGroup = user.group
+ }
+ user.group = group
+ }
+ }
+ user.permissions = permissionIds?.findAll {it?.trim()}?.join(',')
+
+ if (originGroup) { //如果修改了 组管理员(即有grant-user权限)用户 的组名, 则需要把同一组的其它用户的group同步修改
+ repo.trans {se ->
+ repo.saveOrUpdate(user)
+ se.createQuery("update User set group=:newGroup where group=:oldGroup")
+ .setParameter("newGroup", group).setParameter("oldGroup", originGroup)
+ .executeUpdate()
+ }
+ } else {
+ repo.saveOrUpdate(user)
+ }
+ cacheSrv?.remove("permission_" + user.id)
+ return resp.attr('id', user.id).attr('name', user.name)
+ .attr('permissions', user.permissions?.split(","))
+ }
+
+
+ @Path(path = 'changePwd')
+ ApiResp changePwd(HttpContext hCtx, Long id, String newPassword, String oldPassword) {
+ if (!id) return ApiResp.fail("Param id required")
+ if (!newPassword) return ApiResp.fail("Param newPassword required")
+ if (Utils.to(hCtx.getSessionAttr("uId"), Long) != id) {
+ return ApiResp.fail("Not permission, only allow change self password")
+ }
+ def user = repo.findById(User, id)
+ if (!user) return ApiResp.fail("用户不存在")
+ if (oldPassword != user.password) return ApiResp.fail('密码错误')
+ user.password = newPassword
+ repo.saveOrUpdate(user)
+ ApiResp.ok()
+ }
+
+
+ @Path(path = 'restPassword')
+ ApiResp restPassword(HttpContext hCtx, Long id, String newPassword) {
+ //1. grant, grant-user 可以重置密码
+ hCtx.auth("grant", "grant-user")
+ if (!id) return ApiResp.fail("Param id required")
+ if (!newPassword) return ApiResp.fail("Param newPassword required")
+ def user = repo.findById(User, id)
+ if (!user) return ApiResp.fail("用户不存在")
+
+ //2. 只grant-user: 只能重置同组(非自己,非grant,grant-user)的密码
+ if (!hCtx.hasAuth("grant") && hCtx.hasAuth("grant-user")) {
+ if (user.group != hCtx.getSessionAttr("uGroup")) {
+ return ApiResp.fail("Not permission, not same group")
+ }
+ if (user.permissions?.contains("grant")) {
+ return ApiResp.fail("Not permission, grant or grant-user")
+ }
+ }
+ user.password = newPassword
+ repo.saveOrUpdate(user)
+ ApiResp.ok()
+ }
+
+
+ @Path(path = 'delPermission/{id}', method = 'post')
+ ApiResp delPermission(HttpContext hCtx, Long id) {
+ hCtx.auth("grant")
+ if (!id) return ApiResp.fail("Param id required")
+ def permission = repo.findById(Permission, id)
+ if (permission.mark) return ApiResp.fail("Not allow delete dynamic permission")
+ repo.delete(permission)
+ ApiResp.ok(permission)
+ }
+
+
+ @Path(path = 'updatePermission', method = 'post')
+ ApiResp updatePermission(HttpContext hCtx, Long id, String enName, String cnName, String comment) {
+ hCtx.auth("grant")
+ if (!id) return ApiResp.fail("Param id required")
+ def p = repo.findById(Permission, id)
+ if (!p) return ApiResp.fail('未找到权限: ' + id)
+ if (p.enName != enName && repo.find(Permission) {root, query, cb -> cb.equal(root.get('enName'), enName)}) {
+ return ApiResp.fail("权限名'$enName'已存在")
+ }
+ if (p.cnName != cnName && repo.find(Permission) {root, query, cb -> cb.equal(root.get('cnName'), cnName)}) {
+ return ApiResp.fail("权限名'$cnName'已存在")
+ }
+ p.enName = enName
+ p.cnName = cnName
+ p.comment = comment
+ ApiResp.ok(repo.saveOrUpdate(p))
+ }
+
+
+ @Path(path = 'addPermission', method = 'post')
+ ApiResp addPermission(HttpContext hCtx, String enName, String cnName, String comment) {
+ hCtx.auth("grant")
+ if (!enName) return ApiResp.fail("Param enName required")
+ if (!cnName) return ApiResp.fail("Param cnName required")
+ def p = new Permission(enName: enName, cnName: cnName, comment: comment)
+ if (repo.find(Permission) {root, query, cb -> cb.equal(root.get('enName'), enName)}) {
+ return ApiResp.fail("权限名'$enName'已存在")
+ }
+ if (repo.find(Permission) {root, query, cb -> cb.equal(root.get('cnName'), cnName)}) {
+ return ApiResp.fail("权限名'$cnName'已存在")
+ }
+ repo.saveOrUpdate(p)
+ ApiResp.ok(p)
+ }
+
+
+ @Path(path = 'permissionPage')
+ ApiResp> permissionPage(HttpContext hCtx, Integer page, Integer pageSize, String kw, String permissionId, String[] notPermissionIds) {
+ if (page != null && page < 1) return ApiResp.fail("Param page >=1")
+ if (pageSize != null && pageSize > 20) return ApiResp.fail("Param pageSize <=20 and >=1")
+ //1. grant: 查看所有
+ if (hCtx.hasAuth("grant")) {
+ return ApiResp.ok(
+ repo.findPage(Permission, page?:1, pageSize?:10) {root, query, cb ->
+ query.orderBy(cb.desc(root.get('updateTime')))
+ def ps = []
+ if (permissionId) {
+ ps << cb.equal(root.get("enName"), permissionId)
+ }
+ if (notPermissionIds) {
+ ps << root.get("enName").in(notPermissionIds).not()
+ }
+ if (kw) {
+ ps << cb.or(cb.like(root.get("enName"), "%" + kw + "%"), cb.like(root.get("cnName"), "%" + kw + "%"))
+ }
+ cb.and(ps.toArray(new Predicate[ps.size()]))
+ }
+ )
+ }
+ //2. grant-user: 只能看到同一组所有用户创建的动态权限, 和自己的权限(除grant,grant-user)
+ else if (hCtx.hasAuth("grant-user")) { // 组管理员
+ List uNames = [hCtx.getSessionAttr("uName")]
+ if (hCtx.getSessionAttr("uGroup")) { // 组名存在, 则把组内所有用户名找出来
+ uNames = repo.trans{se -> se.createQuery("select name from User where group=:gp").setParameterList("gp", hCtx.getSessionAttr("uGroup")).list()}
+ }
+ def marks = repo.trans{se -> se.createQuery("select id from Decision where creator in (:creator)").setParameterList("creator", uNames).list()}
+ def myPIds = hCtx.getAttr("permissions", Set)?.findAll {String pId -> pId != "grant" && pId != "grant-user" && !pId.startsWith("user-")} // 当前用户的所有权限
+ if (!marks && !myPIds) return ApiResp.ok(Page.empty())
+ return ApiResp.ok(
+ repo.findPage(Permission, page?:1, pageSize?:10) {root, query, cb ->
+ query.orderBy(cb.desc(root.get('updateTime')))
+ def ps = []
+ if (marks && myPIds) {
+ ps << cb.or(root.get("mark").in(marks), root.get("enName").in(myPIds))
+ } else if (marks) {
+ ps << root.get("mark").in(marks)
+ } else if (myPIds) {
+ ps << root.get("enName").in(myPIds)
+ }
+ if (permissionId) {
+ ps << cb.equal(root.get("enName"), permissionId)
+ }
+ if (notPermissionIds) {
+ ps << root.get("enName").in(notPermissionIds).not()
+ }
+ if (kw) {
+ ps << cb.or(cb.like(root.get("enName"), "%" + kw + "%"), cb.like(root.get("cnName"), "%" + kw + "%"))
+ }
+ cb.and(ps.toArray(new Predicate[ps.size()]))
+ }
+ )
+ }
+ //3. 其他: 普通用户只能看到自己创建的动态权限
+ else {
+ def marks = repo.trans{se -> se.createQuery("select id from Decision where creator=:creator").setParameter("creator", hCtx.getSessionAttr("uName")).list()}
+ if (!marks) return ApiResp.ok(Page.empty())
+ return ApiResp.ok(
+ repo.findPage(Permission, page?:1, pageSize?:10) {root, query, cb ->
+ query.orderBy(cb.desc(root.get('updateTime')))
+ def ps = []
+ ps << root.get("mark").in(marks)
+ if (permissionId) {
+ ps << cb.equal(root.get("enName"), permissionId)
+ }
+ if (notPermissionIds) {
+ ps << root.get("enName").in(notPermissionIds).not()
+ }
+ if (kw) {
+ ps << cb.or(cb.like(root.get("enName"), "%" + kw + "%"), cb.like(root.get("cnName"), "%" + kw + "%"))
+ }
+ cb.and(ps.toArray(new Predicate[ps.size()]))
+ }
+ )
+ }
+ }
+
+
+ /**
+ * 组 分页查询
+ */
+ @Path(path = 'groupPage')
+ ApiResp> groupPage(HttpContext hCxt, Integer page, Integer pageSize, String kw) {
+ if (page != null && page < 1) return ApiResp.fail("Param page >=1")
+ if (pageSize != null && pageSize > 20) return ApiResp.fail("Param pageSize <=20 and >=1")
+ ApiResp.ok(
+ repo.sqlPage("select gp from ${repo.tbName(User)} ${kw ? 'where gp like ?' : ''} group by gp", page?:1, pageSize?:10, (kw ? ("%" + kw + "%"): null))
+ .to {it.get("gp")?:it.get("GP")}
+ )
+ }
+}
\ No newline at end of file
diff --git a/src/ctrl/RuleCtrl.groovy b/src/ctrl/RuleCtrl.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..af4f738de2e60125cbaf8f45a1282b95033c00af
--- /dev/null
+++ b/src/ctrl/RuleCtrl.groovy
@@ -0,0 +1,65 @@
+package ctrl
+
+import cn.xnatural.app.Inject
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.http.ApiResp
+import cn.xnatural.http.Ctrl
+import cn.xnatural.http.HttpContext
+import cn.xnatural.http.Path
+import cn.xnatural.jpa.Repo
+import com.alibaba.fastjson.JSON
+import entity.DecideRecord
+import service.rule.DecisionManager
+import service.rule.DecisionSrv
+import service.rule.FieldManager
+
+
+@Ctrl
+class RuleCtrl extends ServerTpl {
+
+ @Inject
+ protected FieldManager fieldManager
+ @Inject
+ protected DecisionSrv decisionSrv
+ @Inject(name = "jpa_rule_repo")
+ protected Repo repo
+
+
+ /**
+ * 执行一条决策
+ */
+ @Path(path = 'decision')
+ ApiResp decision(String decisionId, Boolean async, HttpContext ctx) {
+ def dCtx = decisionSrv.decide(decisionId, ctx.request.id, async == null ? false : async, ctx.params())
+ return ApiResp.ok(dCtx.result())
+ }
+
+
+ /**
+ * 查询决策结果
+ */
+ @Path(path = 'findDecideResult')
+ ApiResp findDecisionResult(String decideId) {
+ if (!decideId) return ApiResp.fail("decideId must required")
+ def dr = repo.findById(DecideRecord, decideId)
+ if (!dr) return ApiResp.fail("Not found decide record: " + decideId)
+ def dm = bean(DecisionManager)
+ def decisionHolder = dm.decisionMap.find {it.value.decision.id == dr.decisionId}.value
+ def attrs = dr.data ? JSON.parseObject(dr.data) : [:]
+ ApiResp.ok(
+ [
+ decideId: decideId, decision: dr.decisionId, decisionId: decisionHolder.decision.decisionId,
+ status : dr.status,
+ desc : dr.exception,
+ attrs : decisionHolder.spec.returnAttrs.collectEntries { name ->
+ def field = fieldManager.fieldHolders.get(name)?.field
+ def v = attrs.containsKey(name) ? attrs.get(name) : attrs.get(fieldManager.alias(name))
+ if (v instanceof Optional) {v = v.orElse( null)}
+ //如果key是中文, 则翻译成对应的英文名
+ if (field && field.cnName == name) return [field.enName, v]
+ else return [name, v]
+ }
+ ]
+ )
+ }
+}
diff --git a/src/ctrl/TestCtrl.groovy b/src/ctrl/TestCtrl.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..92d5fa3760192c801393aec81763ea02e2b1bf23
--- /dev/null
+++ b/src/ctrl/TestCtrl.groovy
@@ -0,0 +1,207 @@
+package ctrl
+
+import cn.xnatural.aio.AioClient
+import cn.xnatural.aio.AioServer
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.app.Utils
+import cn.xnatural.enet.event.EL
+import cn.xnatural.http.*
+import cn.xnatural.jpa.Page
+import cn.xnatural.jpa.Repo
+import core.OkHttpSrv
+import entity.Permission
+import entity.Test
+import entity.UploadFile
+import entity.VersionFile
+import service.FileUploader
+import service.TestService
+
+import java.text.SimpleDateFormat
+import java.util.concurrent.ConcurrentHashMap
+
+import static cn.xnatural.http.ApiResp.fail
+import static cn.xnatural.http.ApiResp.ok
+
+@Ctrl(prefix = 'test')
+class TestCtrl extends ServerTpl {
+ @Lazy def ts = bean(TestService)
+ @Lazy def repo = bean(Repo)
+ @Lazy def fu = bean(FileUploader)
+ @Lazy def http = bean(OkHttpSrv)
+
+ final Set wss = ConcurrentHashMap.newKeySet()
+
+
+ @Filter(order = 1)
+ void filter1(HttpContext ctx) {
+ log.info('test filter1 ============')
+ }
+
+ @Filter(order = 0)
+ void filter2(HttpContext ctx) {
+ // log.info('test filter2 ============')
+ }
+
+
+ @EL(name = 'testWsMsg')
+ void wsMsgBroadcast(String msg) {
+ wss.each {ws -> ws.send(msg)}
+ }
+
+
+ @WS(path = 'msg')
+ void wsMsg(WebSocket ws) {
+ log.info('WS connect. {}', ws.session.remoteAddress)
+ ws.listen(new WsListener() {
+
+ @Override
+ void onClose(WebSocket wst) {
+ wss.remove(wst)
+ }
+
+ @Override
+ void onText(String msg) {
+ log.info('test ws receive client msg: {}', msg)
+ }
+ })
+ wsMsgBroadcast('上线: ' + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))
+ wss.add(ws)
+ }
+
+
+ // 测试抛出错误
+ @Path(path = 'error', produce = "application/json")
+ def error() {
+ throw new Exception('错误测试')
+ }
+
+
+ // dao 测试
+ @Path(path = 'dao')
+ ApiResp dao(String type) {
+ if ('file' == type) {
+ return ok(Page.of(
+ repo.findPage(VersionFile, 1, 10, { root, query, cb -> query.orderBy(cb.desc(root.get('id')))}),
+ {UploadFile e ->
+ Utils.copier(e, [:]).ignore("metaClass")
+ .addConverter("createTime", {Date d -> d?.getTime()})
+ .build()
+ }
+ ))
+ } else {
+ return ok(ts?.findTestData())
+ }
+ return
+
+ // 非数字比较大小(时间大小比较)
+ repo.find(Test, { root, query, cb -> cb.greaterThanOrEqualTo(root.get("createTime"), new Date())})
+ }
+
+
+ // 接收form 表单提交
+ @Path(path = 'form', consumer = 'application/x-www-form-urlencoded')
+ ApiResp form(String param1, HttpContext ctx) {
+ ok(ctx.request.formParams)
+ }
+
+
+ // json 参数
+ @Path(path = 'json', consumer = 'application/json')
+ ApiResp json(String param1, HttpContext ctx) {
+ ok(ctx.request.jsonParams)
+ }
+
+
+ // 接收post string
+ @Path(path = 'string')
+ ApiResp string(HttpContext ctx) {
+ ok(ctx.request.bodyStr)
+ }
+
+
+ // 文件上传
+ @Path(path = 'upload')
+ ApiResp upload(FileData file, String version) {
+ if (file == null) return ApiResp.fail('文件未上传')
+ fu.save(file)
+ repo.saveOrUpdate(new VersionFile(version: version, finalName: file.finalName, originName: file.originName, size: file.size))
+ ok(fu.toFullUrl(file.finalName))
+ }
+
+
+ // 异步响应(手动ctx.render)
+ @Path(path = 'async')
+ void async(String p1, HttpContext ctx) {
+ ctx.render(
+ ok('p1: ' + p1 + ", " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))
+ )
+ }
+
+
+ // 测试登录
+ @Path(path = 'login')
+ ApiResp login(String username, HttpContext hCtx) {
+ hCtx.setSessionAttr('permissions', repo.findList(Permission, null).collect {it.enName}.toSet())
+ ok(hCtx.getSessionAttr("permissions"))
+ }
+
+
+ // 权限测试
+ @Path(path = 'auth')
+ ApiResp auth(String auth, HttpContext ctx) {
+ ok(ctx.auth(auth?:'auth1'))
+ }
+
+
+ // 远程事件调用
+ @Path(path = 'remote')
+ void remote(HttpContext ctx, String app, String event, String p1) {
+ ts.remote(app?:"gy", event?:'eName1', p1?:'p1',{
+ if (it instanceof Exception) ctx.render(fail(it.message))
+ else ctx.render ok(it)
+ })
+ }
+
+
+ // 远程http调用
+ @Path(path = 'http')
+ ApiResp http(String url) {
+ ok(http.get(url?:'http://gy/test/cus').debug().execute())
+ }
+
+
+ // aio 测试发送
+ @Path(path = 'aio')
+ ApiResp aio(String host, Integer port, String msg) {
+ bean(AioClient).send(
+ host?:'localhost', port?:bean(AioServer).port,
+ msg?:("send " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))
+ )
+ ok()
+ }
+
+
+ //故意超时接口
+ @Path(path = 'timeout')
+ ApiResp timeout(Long wait) {
+ Thread.sleep(wait?:1)
+ ok().desc("超时: ${wait}ms" )
+ }
+
+
+ // 下载excel文件
+ void downXlsx(HttpContext ctx) {
+ ctx.response.contentType('application/vnd.ms-excel;charset=utf-8')
+ ctx.response.header('Content-Disposition', "attachment;filename=${new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())}.xlsx")
+ def wb = org.apache.poi.ss.usermodel.WorkbookFactory.create(true)
+ def bos = new ByteArrayOutputStream(); wb.write(bos)
+ ctx.render(bos.toByteArray())
+ }
+
+
+ @Path(path = 'cus')
+ ApiResp cus(String p1) {
+ log.info("here ================")
+ return ok('p1: ' + p1 + ", " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))
+ }
+}
diff --git a/src/entity/CollectRecord.groovy b/src/entity/CollectRecord.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..ea3d0a0ea0af684c1a7a61e7edd12f0fe362b410
--- /dev/null
+++ b/src/entity/CollectRecord.groovy
@@ -0,0 +1,100 @@
+package entity
+
+import cn.xnatural.jpa.IEntity
+import org.hibernate.annotations.DynamicUpdate
+import org.hibernate.annotations.Type
+
+import javax.persistence.*
+
+/**
+ * 决策结果保存地
+ */
+@Entity
+@Table(indexes = [
+ @Index(name = "idx_collectDate", columnList = "collectDate"),
+ @Index(name = "idx_decideId", columnList = "decideId")
+])
+@DynamicUpdate
+class CollectRecord implements IEntity {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ Long id
+ /**
+ * 执行决策流水id
+ * 关联 {@link DecideRecord#id}
+ */
+ @Column(length = 36)
+ String decideId
+ /**
+ * 决策id
+ * 关联 {@link Decision#id}
+ */
+ @Column(length = 36)
+ String decisionId
+ /**
+ * 收集器id
+ * 关联 {@link DataCollector#id}
+ */
+ String collector
+ /**
+ * 执行结果状态
+ */
+ String status
+ /**
+ * http是否查得(数据是否成功)
+ */
+ String dataStatus
+ /**
+ * 收集时间
+ */
+ @Column(nullable = false)
+ Date collectDate
+ /**
+ * 执行时长
+ */
+ Long spend
+ /**
+ * 是否取的缓存
+ */
+ Boolean cache
+ /**
+ * http 调用 url
+ */
+ @Column(length = 1000)
+ String url
+ /**
+ * http 请求 body
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String body
+ /**
+ * 收集的结果
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String result
+ /**
+ * 收集器执行异常
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String exception
+ /**
+ * 解析结果
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String resolveResult
+ /**
+ * 解析异常
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String resolveException
+}
diff --git a/src/entity/DataCollector.groovy b/src/entity/DataCollector.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..9e5b324c75bd12ed7deddb15ac4e53023d28d164
--- /dev/null
+++ b/src/entity/DataCollector.groovy
@@ -0,0 +1,138 @@
+package entity
+
+
+import cn.xnatural.jpa.UUIDEntity
+import org.hibernate.annotations.DynamicUpdate
+import org.hibernate.annotations.Type
+
+import javax.persistence.*
+
+/**
+ * 数据收集器
+ */
+@Entity
+@DynamicUpdate
+@Table(indexes = [
+ @Index(name = idx_name_type, columnList = "name,type", unique = true),
+])
+class DataCollector extends UUIDEntity {
+ static final String idx_name_type = "idx_name_type"
+ /**
+ * 是否可用
+ */
+ Boolean enabled
+ /**
+ * 收集器名
+ */
+ @Column(nullable = false)
+ String name
+ /**
+ * http, script, sql
+ */
+ @Column(nullable = false)
+ String type
+ /**
+ * 备注说明
+ */
+ @Column(length = 2000)
+ String comment
+ /**
+ * 创建者
+ */
+ String creator
+ /**
+ * 更新人
+ */
+ String updater
+ /**
+ * 超时(ms)
+ */
+ Integer timeout
+ /**
+ * 缓存key
+ */
+ String cacheKey
+ /**
+ * 缓存时间计算函数
+ * 1. 返回数字: 单位分钟
+ * 2. 返回 {@link java.time.Duration}
+ * 3. 返回 {@link Date}: 过期具体时间
+ */
+ @Column(length = 1000)
+ String cacheTimeoutFn
+ /**
+ * 是否记录收集结果
+ */
+ boolean recordResult
+
+
+ /**
+ * 接口url, jdbc url
+ */
+ String url
+
+ // ======================== http =======================
+
+ /**
+ * http method
+ */
+ String method
+ /**
+ * 请求body 字符串 模板
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String bodyStr
+ /**
+ * application/json,multipart/form-data,application/x-www-form-urlencoded
+ */
+ String contentType
+ /**
+ * 值解析脚本
+ * 格式为:
+ * {String resultStr -> // 接口返回的字符串
+ *
+ * }
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String parseScript
+
+ /**
+ * 是否查得(数据是否成功)判断函数
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String dataSuccessScript
+
+
+ // ======================= script ======================
+ /**
+ * 值计算函数
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String computeScript
+
+
+ // ========================= sql ========================
+ /**
+ * groovy Sql 执行脚本
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String sqlScript
+ /**
+ * sql 连接池的最小连接
+ */
+ Integer minIdle
+ /**
+ * sql 连接池的最大连接
+ */
+ Integer maxActive
+}
diff --git a/src/entity/DecideRecord.groovy b/src/entity/DecideRecord.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..1996c61779768c45cdd310bcb3ec46091a4945ec
--- /dev/null
+++ b/src/entity/DecideRecord.groovy
@@ -0,0 +1,82 @@
+package entity
+
+
+import cn.xnatural.jpa.IEntity
+import org.hibernate.annotations.DynamicUpdate
+import org.hibernate.annotations.Type
+import service.rule.DecideResult
+
+import javax.persistence.*
+
+/**
+ * 决策记录
+ */
+@Entity
+@DynamicUpdate
+@Table(indexes = [@Index(name = "idx_occurTime", columnList = "occurTime")])
+class DecideRecord implements IEntity {
+ /**
+ * 决策流水id
+ */
+ @Id
+ @Column(length = 50)
+ String id
+ /**
+ * 决策id
+ * 关联 {@link Decision#id}
+ */
+ @Column(length = 36)
+ String decisionId
+ /**
+ * 执行结果状态
+ */
+ String status
+ /**
+ * 决策结果
+ */
+ @Column(length = 30)
+ @Enumerated(EnumType.STRING)
+ DecideResult result
+ /**
+ * 决策时间
+ */
+ @Column(nullable = false)
+ Date occurTime
+ /**
+ * 执行花费时间
+ */
+ Integer spend
+ /**
+ * 异常信息
+ */
+ @Column(length = 1000)
+ String exception
+ /**
+ * 输入参数 json 字符串
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String input
+ /**
+ * 最终数据集
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String data
+ /**
+ * 执行过程描述详情
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String detail
+ /**
+ * 数据收集结果集
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String dataCollectResult
+}
diff --git a/src/entity/Decision.groovy b/src/entity/Decision.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..b5b75659bed0c4c2deee16675dc3357321f0162c
--- /dev/null
+++ b/src/entity/Decision.groovy
@@ -0,0 +1,70 @@
+package entity
+
+import cn.xnatural.jpa.UUIDEntity
+import org.hibernate.annotations.DynamicUpdate
+import org.hibernate.annotations.Type
+
+import javax.persistence.*
+
+/**
+ * 决策
+ */
+@Entity
+@DynamicUpdate
+@Table(indexes = [
+ @Index(name = "idx_decisionId", columnList = "decisionId", unique = true),
+])
+class Decision extends UUIDEntity {
+ /**
+ * 决策id
+ */
+ @Column(nullable = false, length = 100)
+ String decisionId
+ /**
+ * 决策名
+ */
+ String name
+ /**
+ * 描述说明
+ */
+ String comment
+ /**
+ * 完整 DSL
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String dsl
+ /**
+ * api 接口配置
+ [
+ {
+ "code": "decisionId", "name": "决策id", "type": "Str", "require": true, "fixValue": "decision_1"
+ },
+ {
+ "code": "async", "name": "是否异步", "type": "Bool", "require": false, "defaultValue": false
+ },
+ {
+ "code": "idNumber", "name": "身份证", "type": "Str", "require": true, "fixLength": 18
+ },
+ {
+ "code": "mobileNo", "name": "手机号", "type": "Str", "require": true, "fixLength": 11
+ },
+ {
+ "code": "name", "name": "姓名", "type": "Str", "require": true, "maxLength": 100
+ }
+ ]
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String apiConfig
+ /**
+ * 创建者
+ */
+ String creator
+ /**
+ * 更新人
+ */
+ String updater
+}
diff --git a/src/entity/FieldType.groovy b/src/entity/FieldType.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..739f1402138d2aa3bddfb66b488e3cfc518640e0
--- /dev/null
+++ b/src/entity/FieldType.groovy
@@ -0,0 +1,13 @@
+package entity
+
+enum FieldType {
+ // 默认高精度计算
+ Str("字符串", String), Decimal("小数", BigDecimal), Int("整数", Long), Bool("布尔", Boolean), Time("时间", Date)
+ String cnName
+ Class clzType
+
+ FieldType(String cnName, Class clzType) {
+ this.cnName = cnName
+ this.clzType = clzType
+ }
+}
diff --git a/src/entity/GlobalConfig.groovy b/src/entity/GlobalConfig.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..ddb7063b6c7f4bb3e44b2807c562cdce06daf106
--- /dev/null
+++ b/src/entity/GlobalConfig.groovy
@@ -0,0 +1,30 @@
+package entity
+
+import cn.xnatural.jpa.LongIdEntity
+import org.hibernate.annotations.DynamicUpdate
+import org.hibernate.annotations.Type
+
+import javax.persistence.*
+
+/**
+ * 全局配置 key value形式
+ */
+@Entity
+@DynamicUpdate
+@Table(indexes = [
+ @Index(name = "idx_name", columnList = "name", unique = true),
+])
+class GlobalConfig extends LongIdEntity {
+ /**
+ * 属性名
+ */
+ @Column(nullable = false)
+ String name
+ /**
+ * 值
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String value
+}
diff --git a/src/entity/Lock.groovy b/src/entity/Lock.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..a80ef8ccbb3357ba47c6101c77927957ff104726
--- /dev/null
+++ b/src/entity/Lock.groovy
@@ -0,0 +1,22 @@
+package entity
+
+import cn.xnatural.jpa.LongIdEntity
+
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.Index
+import javax.persistence.Table
+
+@Entity
+@Table(name = 'tb_lock', indexes = [@Index(name = "idx_name", columnList = "name", unique = true)])
+class Lock extends LongIdEntity {
+ /**
+ * 锁 的 唯一标识
+ */
+ @Column(nullable = false)
+ String name
+ /**
+ * 描述
+ */
+ String comment
+}
diff --git a/src/entity/OpHistory.groovy b/src/entity/OpHistory.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..f51779660c962fd8a4ee4761601140ed102c8678
--- /dev/null
+++ b/src/entity/OpHistory.groovy
@@ -0,0 +1,29 @@
+package entity
+
+import cn.xnatural.jpa.LongIdEntity
+import org.hibernate.annotations.Type
+
+import javax.persistence.*
+
+/**
+ * 操作历史记录
+ */
+@Entity
+@Table(indexes = [@Index(name = "idx_createTime", columnList = "createTime")])
+class OpHistory extends LongIdEntity {
+ /**
+ * 表名
+ */
+ String tbName
+ /**
+ * 操作员
+ */
+ String operator
+ /**
+ * 保存各个字段的 json 字符串
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String content
+}
diff --git a/src/entity/Permission.groovy b/src/entity/Permission.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..8bfb54395b20f71150e79ba339c1603a1491f6c3
--- /dev/null
+++ b/src/entity/Permission.groovy
@@ -0,0 +1,39 @@
+package entity
+
+import cn.xnatural.jpa.LongIdEntity
+import org.hibernate.annotations.DynamicUpdate
+
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.Index
+import javax.persistence.Table
+
+
+/**
+ * 权限表
+ */
+@Entity
+@DynamicUpdate
+@Table(indexes = [
+ @Index(name = "idx_enName", columnList = "enName", unique = true),
+])
+class Permission extends LongIdEntity {
+ /**
+ * 权限标识
+ */
+ @Column(nullable = false)
+ String enName
+ /**
+ * 权限显示名
+ */
+ String cnName
+ /**
+ * 权限说明
+ */
+ @Column(length = 500)
+ String comment
+ /**
+ * 动态创建权限时, 此字段标识
+ */
+ String mark
+}
diff --git a/src/entity/RuleField.groovy b/src/entity/RuleField.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..a1c039b1600795291eb3dc682b2ab0d92a99cad2
--- /dev/null
+++ b/src/entity/RuleField.groovy
@@ -0,0 +1,49 @@
+package entity
+
+import cn.xnatural.jpa.LongIdEntity
+import org.hibernate.annotations.DynamicUpdate
+import org.hibernate.annotations.Type
+
+import javax.persistence.*
+
+@Table(indexes = [
+ @Index(name = "idx_enName", columnList = "enName", unique = true),
+ @Index(name = "idx_cnName", columnList = "cnName", unique = true),
+])
+@Entity
+@DynamicUpdate
+class RuleField extends LongIdEntity {
+ /**
+ * 英文名 唯一
+ */
+ @Column(nullable = false)
+ String enName
+ /**
+ * 中文名 唯一
+ */
+ @Column(nullable = false)
+ String cnName
+ /**
+ * 备注说明
+ */
+ String comment
+ @Column(nullable = false)
+ @Enumerated(EnumType.STRING)
+ FieldType type
+ /**
+ * 收集器选项
+ * [{collectorId: '收集器id', chooseFn: '选择函数,返回true则使用此收集器'}]
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String collectorOptions
+ /**
+ * 创建者
+ */
+ String creator
+ /**
+ * 更新人
+ */
+ String updater
+}
diff --git a/src/entity/Test.groovy b/src/entity/Test.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..68e7baeac566268f757e29f1207a734edee0088e
--- /dev/null
+++ b/src/entity/Test.groovy
@@ -0,0 +1,11 @@
+package entity
+
+import cn.xnatural.jpa.UUIDEntity
+
+import javax.persistence.Entity
+
+@Entity
+class Test extends UUIDEntity {
+ String name
+ Integer age
+}
\ No newline at end of file
diff --git a/src/entity/UploadFile.groovy b/src/entity/UploadFile.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..629713723ca72361b53072a369612a55cf909d4e
--- /dev/null
+++ b/src/entity/UploadFile.groovy
@@ -0,0 +1,33 @@
+package entity
+
+import cn.xnatural.jpa.LongIdEntity
+import org.hibernate.annotations.DynamicUpdate
+
+import javax.persistence.MappedSuperclass
+
+/**
+ * 文件上传基本信息实体
+ * @author xiangxb, 2018-10-07
+ */
+//@Inheritance(strategy = InheritanceType.JOINED)
+//@Entity
+@MappedSuperclass
+@DynamicUpdate
+class UploadFile extends LongIdEntity {
+ /**
+ * 系统生产的唯一文件名(可用作第3方文件服务器的文件id)
+ */
+ String finalName
+ /**
+ * 原始文件名
+ */
+ String originName
+ /**
+ * 文件大小
+ */
+ Long size
+ /**
+ * 备注
+ */
+ String comment
+}
diff --git a/src/entity/User.groovy b/src/entity/User.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..ea0679f95300e02456adbbd634e3e59f00a04c26
--- /dev/null
+++ b/src/entity/User.groovy
@@ -0,0 +1,45 @@
+package entity
+
+import cn.xnatural.jpa.LongIdEntity
+import org.hibernate.annotations.DynamicUpdate
+import org.hibernate.annotations.Type
+
+import javax.persistence.Basic
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.Index
+import javax.persistence.Lob
+import javax.persistence.Table
+
+@Entity
+@DynamicUpdate
+@Table(indexes = [
+ @Index(name = "idx_name", columnList = "name", unique = true),
+])
+class User extends LongIdEntity {
+ /**
+ * 用户登录名
+ */
+ @Column(nullable = false)
+ String name
+ /**
+ * 用户组
+ */
+ @Column(name = "gp")
+ String group
+ /**
+ * 登录的密码
+ */
+ String password
+ /**
+ * 权限标识列表, 用豆号分割
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String permissions
+ /**
+ * 上次登录时间
+ */
+ Date login
+}
diff --git a/src/entity/UserSession.groovy b/src/entity/UserSession.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..15b9b14d6ff8282b0df57965a045bec49154a288
--- /dev/null
+++ b/src/entity/UserSession.groovy
@@ -0,0 +1,46 @@
+package entity
+
+import cn.xnatural.jpa.BaseEntity
+import org.hibernate.annotations.DynamicUpdate
+import org.hibernate.annotations.Type
+
+import javax.persistence.Basic
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.Id
+import javax.persistence.Index
+import javax.persistence.Lob
+import javax.persistence.Table
+
+/**
+ * 用户会话记录
+ */
+@Entity
+@DynamicUpdate
+@Table(indexes = [
+ @Index(name = "idx_userId", columnList = "userId"),
+])
+class UserSession extends BaseEntity {
+ /**
+ * 会话id
+ */
+ @Id
+ @Column(length = 128)
+ String sessionId
+ /**
+ * 关联用户id
+ */
+ @Column(nullable = false)
+ Long userId
+ /**
+ * 是否有效. 主动退出 valid: false
+ */
+ Boolean valid
+ /**
+ * 当前会话属性
+ */
+ @Lob
+ @Basic
+ @Type(type = "text")
+ String data
+}
diff --git a/src/entity/VersionFile.groovy b/src/entity/VersionFile.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..4a104bcdd456d29d98586a7430419c530dc0fb20
--- /dev/null
+++ b/src/entity/VersionFile.groovy
@@ -0,0 +1,11 @@
+package entity
+
+import javax.persistence.Entity
+
+@Entity
+class VersionFile extends UploadFile {
+ // 1.0.1
+ String version
+ // 用于比较大小
+ Integer versionNum
+}
diff --git a/src/logback.groovy b/src/logback.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..7384c1c04688809724ffd8c027d56611f346093e
--- /dev/null
+++ b/src/logback.groovy
@@ -0,0 +1,68 @@
+import ch.qos.logback.classic.Level
+import ch.qos.logback.classic.encoder.PatternLayoutEncoder
+import ch.qos.logback.core.util.FileSize
+import cn.xnatural.app.Utils
+
+import java.nio.charset.Charset
+import java.nio.charset.StandardCharsets
+
+// 日志文件名
+def logFileName = System.getProperty('log.file.name', 'app')
+
+// 日志文件路径. 配了路径才会输出到文件. 默认输出到项目根目录下的log目录
+def logPath = System.properties.containsKey('log.path') ? System.getProperty('log.path') : Utils.baseDir("../log").canonicalPath
+
+// 去掉最后的 /
+if ('/' != logPath && logPath.endsWith('/')) logPath = logPath.substring(0, logPath.length() - 1)
+
+// 默认只输出:标准输出,文件
+def appenders = System.getProperty('log.appenders', 'console,file')
+if (appenders instanceof String) {
+ appenders = appenders.split(',').collect {it.trim()}.findAll {String s ->
+ if (s) return true
+ else false
+ }
+} else if (appenders !instanceof Collection) throw new RuntimeException('log.appenders 配置错误')
+
+if (appenders.contains('console')) { // 标准输出
+ appender('console', ConsoleAppender) {
+ encoder(PatternLayoutEncoder) {
+ // 控制台日志格式配置
+ delegate.pattern = "%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] [%-7thread] [%-40.40C :%-3L] => %m%n"
+ delegate.charset = Charset.forName("utf8")
+ }
+ }
+}
+
+if (logPath) { // 有日志输出目录配置
+ if (appenders.contains('file')) { // 日志文件
+ appender('file', RollingFileAppender) {
+ encoder(PatternLayoutEncoder) {
+ // 文件日志格式配置
+ delegate.pattern = "%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] [%-7thread] [%-40.40C :%-3L] => %m%n"
+ delegate.charset = StandardCharsets.UTF_8
+ }
+ if ("/" == logPath) file = "/${logFileName}.log"
+ else file = "$logPath/${logFileName}.log"
+ rollingPolicy(SizeAndTimeBasedRollingPolicy) {
+ if ("/" == logPath) delegate.fileNamePattern = "/${logFileName}.%d{yyyy-MM-dd}.%i.log"
+ else delegate.fileNamePattern = "${logPath}/${logFileName}.%d{yyyy-MM-dd}.%i.log"
+ delegate.maxFileSize = FileSize.valueOf(System.getProperty("log.maxFileSize", '50MB'))
+ delegate.maxHistory = Integer.valueOf(System.getProperty("log.maxHistory", '500'))
+ delegate.totalSizeCap = FileSize.valueOf(System.getProperty("log.totalSizeCap", '50GB'))
+ }
+ }
+ }
+} else appenders.remove('file')
+
+
+root(Level.valueOf(System.getProperty('log.level', 'info')), appenders)
+
+// 日志等级设置
+logger('ch.qos.logback', WARN)
+logger('org.hibernate.tool.hbm2ddl.SchemaUpdate', INFO)
+System.properties.each {String k, v->
+ if (k.startsWith("log.level.") && v) {
+ logger(k.replace('log.level.', ''), Level.valueOf((String) v))
+ }
+}
\ No newline at end of file
diff --git a/src/logback.xml b/src/logback.xml
new file mode 100644
index 0000000000000000000000000000000000000000..132f82f9881fbfbf0528a1981b667f75967c83ee
--- /dev/null
+++ b/src/logback.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] [%-7thread] [%-40.40C :%-3L] => %m%n
+
+
+
+
+ ${log_path}/${log_file_name}.log
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] [%-7thread] [%-40.40C :%-3L] => %m%n
+ utf-8
+
+
+ ${log_path}/${log_file_name}.%d{yyyyMMdd}.%i.log
+ ${log.maxHistory:-500}
+ ${log.maxFileSize:-50MB}
+ ${log.totalSizeCap:-100GB}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main.groovy b/src/main.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..74850db6e9a1019717688573443ccce30c576660
--- /dev/null
+++ b/src/main.groovy
@@ -0,0 +1,137 @@
+import cn.xnatural.app.AppContext
+import cn.xnatural.app.CacheSrv
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.enet.event.EL
+import cn.xnatural.jpa.Repo
+import cn.xnatural.remoter.Remoter
+import cn.xnatural.sched.Sched
+import core.HttpSrv
+import core.OkHttpSrv
+import core.RedisClient
+import ctrl.*
+import entity.*
+import groovy.transform.Field
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import service.FileUploader
+import service.TestService
+import service.rule.*
+
+import java.time.Duration
+
+System.setProperty("configdir", "../conf")
+@Field final Logger log = LoggerFactory.getLogger("ROOT")
+@Field final AppContext app = new AppContext() //应用上下文
+// @Lazy @Field EP ep = app.bean(EP, null)
+
+
+// 系统功能添加区
+app.addSource(
+ new OkHttpSrv(), // http 客户端
+ new CacheSrv(), // 对象缓存
+ sched(), // 时间任务调度管理
+ remoter(), // 集群分布式
+ jpa_rule(), // 数据源rule
+ new HttpSrv().ctrls( // 接口层
+ TestCtrl, MainCtrl, RuleCtrl, MntCtrl, MntUserCtrl,
+ MntDecisionCtrl, MntCollectorCtrl, MntFieldCtrl, MntDataCtrl
+ ),
+ new FileUploader(), new CollectorManager(), new TestService(),
+ new FieldManager(), new DecisionSrv(), new DecisionManager(),
+ new OpHistorySrv(), new UserSrv(), this
+).start() // 启动系统
+
+
+@EL(name = 'sys.inited')
+void sysInited() {
+ if (app.attrs('redis')) { //根据配置是否有redis,创建redis客户端工具
+ app.addSource(new RedisClient())
+ }
+}
+
+
+@EL(name = 'sys.started', async = true)
+void sysStarted() {
+ try {
+ } finally {
+ // System.exit(0)
+ }
+}
+
+
+def jpa_rule() {
+ new ServerTpl("jpa_rule") { //数据库 jpa_rule
+ Repo repo
+ @EL(name = "sys.starting", async = true)
+ void start() {
+ repo = new Repo(attrs()).entities( // jpa封装
+ Decision, RuleField, DataCollector, OpHistory, DecideRecord, CollectRecord,
+ User, Permission, GlobalConfig, Lock, UserSession
+ ).init()
+ exposeBean(repo, name + "_repo")
+ ep.fire("${name}.started")
+ }
+
+ // 数据库 最后再关闭
+ @EL(name = "sys.stopping", async = true, order = 2f)
+ void stop() { repo?.close() }
+ }
+}
+
+
+def sched() {
+ new ServerTpl("sched") { // 定时任务
+ Sched sched
+ @EL(name = "sys.starting", async = true)
+ void start() {
+ sched = new Sched(attrs(), exec()).init()
+ exposeBean(sched)
+ ep.fire("${name}.started")
+ }
+
+ @EL(name = "sched.after")
+ void after(Duration duration, Runnable fn) {sched.after(duration, fn)}
+
+ @EL(name = "sys.stopping", async = true, order = 1f)
+ void stop() { sched?.stop() }
+ }
+}
+
+
+def remoter() {
+ new ServerTpl("remoter") { // 集群配置
+ Remoter remoter
+ @EL(name = "sched.started")
+ void start() {
+ remoter = new Remoter(app.name(), app.id(), attrs(), exec(), ep, bean(Sched))
+ exposeBean(remoter)
+ exposeBean(remoter.aioClient)
+ ep.fire("${name}.started")
+ }
+
+ @EL(name = 'sys.heartbeat', async = true)
+ void heartbeat() {
+ remoter.sync()
+ remoter.aioServer.clean()
+ }
+ @EL(name = "sys.stopping", async = true)
+ void stop() { remoter.stop() }
+ }
+}
+
+
+/**
+ * 系统心跳 清理
+ */
+@EL(name = 'sys.heartbeat', async = true)
+void heartbeat() {
+ // 删除 Classloader 中只进不出的 parallelLockMap
+ def field = ClassLoader.getDeclaredField('parallelLockMap')
+ field.setAccessible(true)
+ field.get(Thread.currentThread().contextClassLoader.parent.parent).clear()
+ field.get(Thread.currentThread().contextClassLoader.parent.parent.parent).clear()
+ // 删除 Classloader 中只进不出的 classes
+ field = ClassLoader.getDeclaredField('classes')
+ field.setAccessible(true)
+ field.get(Thread.currentThread().contextClassLoader.parent).clear()
+}
\ No newline at end of file
diff --git a/src/service/EmailSrv.groovy b/src/service/EmailSrv.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..82b07c5cb9698967a88a995987ac673b90f4f413
--- /dev/null
+++ b/src/service/EmailSrv.groovy
@@ -0,0 +1,39 @@
+package service
+
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.enet.event.EL
+
+class EmailSrv extends ServerTpl {
+
+ @Lazy String host = getStr('host', 'smtp.qq.com')
+ @Lazy String sender = getStr('sender', 'xnatural@msn.cn')
+ @Lazy String password = getStr('password', null)
+
+
+ @EL(name = 'sys.starting', async = true)
+ def start() {
+
+ }
+
+
+ def email(@DelegatesTo(EmailSpec) Closure cl) {
+ def email = new EmailSpec()
+ def code = cl.rehydrate(email, this, this)
+ code.resolveStrategy = Closure.DELEGATE_ONLY
+ code()
+ send(email)
+ }
+
+
+ protected send(EmailSpec spec) {
+
+ }
+
+
+ class EmailSpec {
+ String from
+ List to
+ String subject
+ String body
+ }
+}
diff --git a/src/service/FileUploader.groovy b/src/service/FileUploader.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..e0025c5983d9cd0e37a30d822f5eb60667323b77
--- /dev/null
+++ b/src/service/FileUploader.groovy
@@ -0,0 +1,106 @@
+package service
+
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.app.Utils
+import cn.xnatural.enet.event.EL
+import cn.xnatural.http.FileData
+import core.OkHttpSrv
+
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+
+/**
+ * 文件上传
+ */
+class FileUploader extends ServerTpl {
+ /**
+ * 文件上传的 本地保存目录
+ */
+ @Lazy String localDir = new URL('file:' + (getStr("localDir", Utils.baseDir('../upload').canonicalPath))).getFile()
+ /**
+ * 文件上传 的访问url前缀
+ */
+ @Lazy URI accessUrlPrefix = URI.create(getStr("accessUrlPrefix", ("//${ep.fire('http.hp')}/file/")) + "/").normalize()
+ /**
+ * 远程文件服务器url地址
+ */
+ @Lazy String remoteUrl = getStr("remoteUrl", '')
+ @Lazy def http = bean(OkHttpSrv)
+
+
+ @EL(name = 'web.started', async = false)
+ protected init() {
+ log.info('save upload file local dir: {}', localDir)
+ log.info('access upload file url prefix: {}', accessUrlPrefix)
+ if (remoteUrl) {log.info('remote file server http url: {}', remoteUrl)}
+ }
+
+
+ /**
+ * 映射 文件名 到一个 url
+ * @param fileName 完整的文件名
+ * @return 文件访问的url
+ */
+ String toFullUrl(String fileName) {
+ accessUrlPrefix.resolve(fileName).toString()
+ }
+
+
+ /**
+ * 查找文件
+ * @param fileName 文件名
+ * @return 文件
+ */
+ File findFile(String fileName) { new File(localDir, fileName) }
+
+
+ @EL(name = 'deleteFile', async = true)
+ void delete(String fileName) {
+ File f = new File(localDir, fileName)
+ if (f.exists()) f.delete()
+ else log.warn("delete file '{}' not exists", fileName)
+ }
+
+
+ /**
+ * 保存文件
+ * @param fd
+ * @param forwardRemote
+ * @return {@link FileData}
+ */
+ FileData save(FileData fd, boolean forwardRemote = false) { save([fd])?[0] }
+
+
+ /**
+ * 多文件 多线程保存
+ * @param fds
+ */
+ // @Monitor(warnTimeOut = 7000)
+ List save(List fds, boolean forwardRemote = false) {
+ if (!fds) return fds
+
+ // 文件流copy
+ def doSave = {FileData fd ->
+ if (fd == null) return fd
+ if (forwardRemote) { // http上传到远程文件服务
+ // 1. 阿里OSS文件服务器例子
+ // def oss = new OSSClient(endpoint, accessKeyId, accessKeySecret)
+ // oss.putObject('path', fd.generatedName, fd.inputStream)
+
+ // 2. 个人http文件服务器例子
+ if (remoteUrl) http?.post(remoteUrl).fileStream('file', fd.finalName, fd.inputStream).execute()
+ } else {
+ // 创建本地文件并写入
+ def dir = new File(localDir)
+ dir.mkdirs()
+ fd.transferTo(dir)
+ log.info('Saved file: {}, originName: {}, size: ' + fd.size, dir.canonicalPath + File.separator + fd.finalName, fd.originName)
+ }
+ fd
+ }
+
+ // 并发上传
+ fds.findResults {fd -> exec().submit {doSave(fd)}}.each {it.get()}
+ fds
+ }
+}
diff --git a/src/service/TestService.groovy b/src/service/TestService.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..c5330a53f7d8c4e09574a7758a55af8968f6bf37
--- /dev/null
+++ b/src/service/TestService.groovy
@@ -0,0 +1,189 @@
+package service
+
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.app.v.VChain
+import cn.xnatural.app.v.VProcessor
+import cn.xnatural.enet.event.EL
+import cn.xnatural.jpa.Page
+import cn.xnatural.jpa.Repo
+import cn.xnatural.remoter.Remoter
+import cn.xnatural.task.TaskContext
+import cn.xnatural.task.TaskWrapper
+import com.alibaba.fastjson.JSON
+import core.OkHttpSrv
+import core.mode.builder.ObjBuilder
+import core.mode.pipeline.Pipeline
+import entity.Test
+
+import java.text.SimpleDateFormat
+import java.util.function.Consumer
+
+class TestService extends ServerTpl {
+ @Lazy def repo = bean(Repo)
+ @Lazy def http = bean(OkHttpSrv)
+
+
+ @EL(name = 'sys.heartbeat', async = true)
+ void timeNotify() {
+ // 向测试 web socket 每分钟发送消息
+ ep.fire("testWsMsg", '系统时间: ' + new SimpleDateFormat('yyyy-MM-dd HH:mm:ss').format(new Date()))
+ }
+
+
+ Page findTestData() {
+ repo.trans{ s ->
+ repo.saveOrUpdate(
+ new Test(
+ name: new SimpleDateFormat('yyyy-MM-dd HH:mm:ss').format(new Date()),
+ age: Integer.valueOf(new SimpleDateFormat('ss').format(new Date()))
+ )
+ )
+ repo.findPage(Test, 1, 10, { root, query, cb -> query.orderBy(cb.desc(root.get('createTime')))})
+ }
+ }
+
+
+ def hibernateTest() {
+ repo?.trans{s ->
+ println '============='
+// println s.createNativeQuery('select name, age from test where age>10')
+// .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP)
+// .setMaxResults(1).uniqueResult()
+// println s.createQuery('select name,age from Test where age>10')
+// // .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP)
+// .setMaxResults(1).singleResult['age']
+ println s.createQuery('from Test where age>10').setMaxResults(1).list()
+ }
+
+ findTestData()
+ println "total: " + repo.count(Test)
+ }
+
+
+ def wsClientTest() {
+ def i = 1
+ http.ws('ws://localhost:7100/test/ws', 90,{msg, ws ->
+ log.info("消息" + (i++) + ": " + msg)
+ })
+ }
+
+
+ def okHttpTest() {
+ def hp = ep.fire('http.hp')
+ // hp = '39.104.28.131/gy'
+ if (hp) {
+ // log.info '接口访问xxx: ' + okHttp.http().get("http://$hp/test/xxx").execute()
+ log.info '接口访问dao: ' + http.get("http://$hp/test/dao").cookie('sId', '222').param('type', 'file').execute()
+ log.info '接口访问form: ' + http.post("http://$hp/test/form?sss=22").param('p1', '中文').execute()
+ log.info '接口访问json: ' + http.post("http://$hp/test/json").contentType('application/json').param('p1', '中文 123').execute()
+ log.info '接口访问json: ' + http.post("http://$hp/test/json").jsonBody(JSON.toJSONString([a:'1', b:2])).execute()
+ http.post("http://$hp/test/upload?cus=11111").param('p2', 'abc 怎么')
+ .param('f1', new File('C:\\Users\\xiangxb\\Desktop\\新建文本文档.txt'))
+ .execute({log.info '接口访问upload: ' + it})
+ }
+ }
+
+
+ @EL(name = "eName11")
+ void taskTest() {
+ new TaskContext<>('test ctx', null, exec())
+ .addTask(new TaskWrapper().step {param, me -> me.info("执行任务....")})
+ .addTask(new TaskWrapper().step {param, me ->
+ me.info("执行任务")
+ }.step { param, me ->
+ me.task().ctx().addTask(new TaskWrapper().step{param1, mee -> mee.info("执行衍生任务....")})
+ })
+ .start()
+ }
+
+
+ @EL(name = "eName10")
+ def testObjBuilder() {
+ println ObjBuilder.of(Map).add("a", {"b"}).build()
+ }
+
+
+ @EL(name = "eName9")
+ def testPipe() {
+ println new Pipeline(key: 'test pipe').add({ i -> i + "xxx"}).run("qqq")
+ }
+
+
+ @EL(name = "eName8")
+ def testVChain() {
+ new VChain().add(new VProcessor() {
+ @Override
+ def pre(Object param) {
+ log.info('down1')
+ }
+
+ @Override
+ def post(Object param) {
+ log.info('up1')
+ }
+ }).add(new VProcessor() {
+ @Override
+ def pre(Object param) {
+ log.info('down2')
+ }
+
+ @Override
+ def post(Object param) {
+ log.info('up2')
+ }
+ }).run()
+ }
+
+
+ def remote(String app, String eName, String param = 'xx', Consumer fn) {
+ // 远程调用
+ fn.accept(bean(Remoter).fire(app?:'gy', eName?:'eName1', ['p1']))
+ // bean(Remoter).fireAsync(app?:'gy', eName?:'eName1', fn, [])
+ }
+
+
+ @EL(name = "eName1", async = false)
+ private String testEvent1(String p) {
+ Test e = new Test()
+ e.setName("aaaa" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
+ e.setAge(111)
+ Test t = repo.saveOrUpdate(e);
+ // if (true) throw new IllegalArgumentException("xxx");
+ return "new entity : " + t.getId()
+ }
+
+
+ @EL(name = "eName2")
+ def testEvent2(String p) {
+ repo.findPage(Test, 1, 10, { root, query, cb -> query.orderBy(cb.desc(root.get("id")))})
+ }
+
+
+ @EL(name = "eName3")
+ long testEvent3(String p) {
+ repo.count(Test, {root, query, cb -> query.orderBy(cb.desc(root.get("id")))})
+ }
+
+
+ @EL(name = "eName4")
+ def testEvent4(String p) {
+ ep.fire("cache.get","java","java")
+ }
+
+
+ @EL(name = "eName5")
+ void testEvent5(String p) {
+ ep.fire("cache.set","java","java", p?:(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())))
+ }
+
+ @EL(name = "eName6")
+ def testEvent6(String p) {
+ throw new RuntimeException("抛个错 $p")
+ }
+
+ @EL(name = "eName7")
+ def testEvent7(String i) {
+ Thread.sleep(5000L)
+ "eName7_"+System.currentTimeMillis()
+ }
+}
diff --git a/src/service/rule/CollectorManager.groovy b/src/service/rule/CollectorManager.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..cd15d0ddbf51ab9f07e29e6a68679aa22327df37
--- /dev/null
+++ b/src/service/rule/CollectorManager.groovy
@@ -0,0 +1,828 @@
+package service.rule
+
+import cn.xnatural.app.CacheSrv
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.app.Utils
+import cn.xnatural.app.util.DB
+import cn.xnatural.enet.event.EC
+import cn.xnatural.enet.event.EL
+import cn.xnatural.jpa.Repo
+import cn.xnatural.remoter.Remoter
+import com.alibaba.fastjson.JSON
+import com.alibaba.fastjson.JSONObject
+import com.alibaba.fastjson.serializer.SerializerFeature
+import core.OkHttpSrv
+import core.RedisClient
+import entity.CollectRecord
+import entity.DataCollector
+import groovy.text.GStringTemplateEngine
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.control.customizers.ImportCustomizer
+import org.slf4j.LoggerFactory
+
+import java.time.Duration
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.atomic.AtomicInteger
+import java.util.function.Function
+
+import static service.rule.spec.BaseSpec.rehydrate2
+
+/**
+ * 收集器
+ */
+class CollectorManager extends ServerTpl {
+ protected static final String DATA_COLLECTED = "data_collected"
+ @Lazy protected repo = bean(Repo, 'jpa_rule_repo')
+ @Lazy protected redis = bean(RedisClient)
+ @Lazy protected cacheSrv = bean(CacheSrv)
+ @Lazy protected fieldManager = bean(FieldManager)
+ // 是否异步保存决策结果
+ @Lazy protected asyncSave = getBoolean("asyncSave", true)
+ /**
+ * 数据获取函数. 收集器id -> 收集器
+ */
+ final Map collectorHolders = new ConcurrentHashMap(100)
+ /**
+ * 决策id -> 保存延迟计算的收集器记录函数
+ */
+ protected final Map> lazyCollectRecords = new ConcurrentHashMap<>()
+
+
+ @EL(name = 'jpa_rule.started', async = true)
+ void init() {
+ load()
+
+ if (asyncSave) {
+ Long lastWarn // 上次告警时间
+ queue(DATA_COLLECTED)
+ .failMaxKeep(getInteger(DATA_COLLECTED + ".failMaxKeep", 10000))
+ .parallel(getInteger("saveResult.parallel", 10))
+ .errorHandle {ex, me ->
+ if (lastWarn == null || (System.currentTimeMillis() - lastWarn >= Duration.ofSeconds(getLong(DATA_COLLECTED + ".warnInterval", 60 * 5L)).toMillis())) {
+ lastWarn = System.currentTimeMillis()
+ log.error("保存数据收集结果到数据库错误", ex)
+ ep.fire("globalMsg", "保存数据收集结果到数据库错误: " + (ex.message?:ex.class.simpleName))
+ }
+ // 暂停一会
+ me.suspend(Duration.ofMillis(500 + new Random().nextInt(1000)))
+ }
+ }
+
+ ep.fire("${name}.started")
+ }
+
+
+ @EL(name = 'sys.stopping', async = true)
+ protected void stop() {
+ collectorHolders.each {it.value.close()}
+ // 尽量等到 对列中的 数据都持久化完成
+ long start = System.currentTimeMillis()
+ if (queue(DATA_COLLECTED).waitingCount > 0) log.warn("等待数据收集结果数据保存完...")
+ while (queue(DATA_COLLECTED).waitingCount > 0 && System.currentTimeMillis() - start < 1000 * 60 * 2) {
+ Thread.sleep(1000)
+ }
+ }
+
+
+ @EL(name = 'decision.end')
+ void endDecision(DecisionContext ctx) {
+ if (asyncSave) async { lazyCollectRecords.remove(ctx.id)?.each { it.call() } }
+ else lazyCollectRecords.remove(ctx.id)?.each { it.call() }
+ }
+
+
+ @EL(name = ['dataCollectorChange', 'dataCollector.dataVersion'], async = true)
+ protected void listenDataCollectorChange(EC ec, String id) {
+ def collector = repo.findById(DataCollector, id)
+ if (collector == null) {
+ collectorHolders.remove(id)?.close()
+ log.info("del dataCollector: " + id)
+ } else {
+ log.info("dataCollectorChanged: " + collector.name + ", " + id)
+ initDataCollector(collector)
+ }
+ def remoter = bean(Remoter)
+ if (remoter && ec?.source() != remoter) { // 不是远程触发的事件
+ remoter.dataVersion('dataCollector').update(id, collector ? collector.updateTime.time : System.currentTimeMillis(), null)
+ }
+ }
+
+
+ def populate(String collectorId, String aName, DecisionContext ctx) {
+ // 得到收集器
+ def collectorHolder = collectorHolders.get(collectorId)
+ if (collectorHolder == null) {
+ // 重新去数据库中查找
+ initDataCollector(repo.findById(DataCollector, collectorId))
+ collectorHolder = collectorHolders.get(collectorId)
+ }
+ // 未找到收集器
+ if (!collectorHolder) {
+ log.warn(ctx.logPrefix() + "Not fund '${aName}' mapped getter function '${collectorHolder.collector.name}($collectorId)'".toString())
+ return null
+ }
+
+ return collectorHolder.populate(aName, ctx)
+ }
+
+
+ /**
+ * 测试 收集器
+ * @param id 收集器id
+ * @param param 参数
+ */
+ def testCollector(String id, Map param) { collectorHolders.get(id)?.testComputeFn?.apply(param) }
+
+
+ // 决策产生的数据收集
+ protected void dataCollected(
+ DataCollector collector, DecisionContext ctx, Boolean cache, Date collectDate, Long spend, String dataStatus,
+ Object result, Exception ex, def resolveResult, Exception resolveException, String url = null, String body = null
+ ) {
+ if (!collector.recordResult) return
+ final Runnable save = () -> {
+ // 是否有延迟计算值
+ boolean hasLazyValue
+ def resolveResultStrFn = {
+ if (resolveResult instanceof Map) {
+ return JSON.toJSONString(
+ resolveResult.findAll {e ->
+ if (e.value instanceof Closure) {
+ hasLazyValue = true
+ return false
+ }
+ else true
+ },
+ SerializerFeature.WriteMapNullValue
+ )
+ } else {
+ return resolveResult?.toString()
+ }
+ }
+
+ String status = '0000'
+ if (ex instanceof ConnectException) status = 'E001'
+ else if (ex != null) status = 'EEEE'
+ else if (resolveException != null) status = 'E002'
+ def record = new CollectRecord(
+ decideId: ctx.id, decisionId: ctx.decisionHolder.decision.id, collector: collector.id,
+ status: status, dataStatus: dataStatus,
+ collectDate: collectDate, cache: cache, spend: spend, url: url, body: body,
+ result: result, exception: ex == null ? null : ex.message?:ex.class.simpleName,
+ resolveResult: resolveResultStrFn(),
+ resolveException: resolveException == null ? null : resolveException.message?:resolveException.class.simpleName
+ )
+ if (hasLazyValue) { // 如果有延迟计算值, 则在决策结束后更新收集器结果
+ lazyCollectRecords.computeIfAbsent(ctx.id, { new LinkedList<>() }).add({
+ record.resolveResult = resolveResultStrFn()
+ repo.saveOrUpdate(record)
+ })
+ } else {
+ repo.saveOrUpdate(record)
+ }
+ }
+ if (asyncSave) queue(DATA_COLLECTED, save)
+ else save.run()
+ }
+
+
+ /**
+ * 加载所有数据收集器
+ */
+ void load() {
+ initDefaultCollector()
+ final Set ids = (collectorHolders ? ConcurrentHashMap.newKeySet(collectorHolders.size()) : null)
+ final def threshold = new AtomicInteger(1)
+ final def tryComplete = {
+ if (threshold.decrementAndGet() > 0) return
+ if (ids) { // 重新加载, 要删除内存中有, 但库中没有
+ collectorHolders.findAll {!ids.contains(it.key)}.each { e ->
+ collectorHolders.remove(e.key)
+ }
+ }
+ log.info("加载数据收集器 {}个", collectorHolders.size())
+ }
+ for (int page = 0, limit = 20; ; page++) {
+ def ls = repo.findList(DataCollector, page * limit, limit)
+ threshold.incrementAndGet()
+ async {
+ ls.each {collector ->
+ initDataCollector(collector)
+ ids?.add(collector.id)
+ }
+ tryComplete()
+ }
+ if (!ls || ls.size() < limit) {
+ tryComplete(); break
+ }
+ }
+ }
+
+
+ /**
+ * 初始化默认数据集
+ */
+ protected void initDefaultCollector() {
+ if (repo.count(DataCollector) == 0) {
+ log.info("初始化默认数据收集器")
+ repo.saveOrUpdate(new DataCollector(type: 'script', name: '星期几', enabled: true, comment: '值: 1,2,3,4,5,6,7', computeScript: """
+def week = Calendar.getInstance().get(Calendar.DAY_OF_WEEK)
+if (week == 1) 7
+else week - 1
+ """.trim()))
+
+ repo.saveOrUpdate(new DataCollector(type: 'script', name: '当前日期时间', enabled: true, comment: '值: yyyy-MM-dd HH:mm:ss', cacheKey: '${(long) (System.currentTimeMillis() / 1000)}', computeScript: """
+new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())
+ """.trim()))
+ repo.saveOrUpdate(new DataCollector(type: 'script', name: '当前日期', enabled: true, comment: '值: yyyy-MM-dd', cacheKey: '${(long) (System.currentTimeMillis() / (1000 * 60 * 60 * 24))}', computeScript: """
+new java.text.SimpleDateFormat("yyyy-MM-dd").format(new Date())
+ """.trim()))
+ repo.saveOrUpdate(new DataCollector(type: 'script', name: '当前时间', enabled: true, comment: '值: HH:mm:ss', cacheKey: '${(long) (System.currentTimeMillis() / 1000)}', computeScript: """
+new java.text.SimpleDateFormat("HH:mm:ss").format(new Date())
+ """.trim()))
+
+ repo.saveOrUpdate(new DataCollector(type: 'script', name: '性别', enabled: true, comment: '根据身份证计算. 值: F,M', computeScript: """
+if (idNumber && idNumber.length() > 17) {
+ Integer.parseInt(idNumber.substring(16, 17)) % 2 == 0 ? 'F' : 'M'
+} else null
+ """.trim()))
+
+ repo.saveOrUpdate(new DataCollector(type: 'script', name: '年龄', enabled: true, comment: '根据身份证计算', computeScript: """
+if (idNumber && idNumber.length() > 17) {
+ Calendar cal = Calendar.getInstance()
+ int yearNow = cal.get(Calendar.YEAR)
+ int monthNow = cal.get(Calendar.MONTH) + 1
+ int dayNow = cal.get(Calendar.DATE)
+
+ int birthday = Integer.valueOf(idNumber.substring(6, 14))
+ int year = Integer.valueOf(idNumber.substring(6, 10))
+ int month = Integer.valueOf(idNumber.substring(10,12))
+ int day = Integer.valueOf(idNumber.substring(12,14))
+
+ if ((month < monthNow) || (month == monthNow && day <= dayNow)) {
+ yearNow - year
+ } else {
+ yearNow - year - 1
+ }
+} else null
+ """.trim()))
+ }
+ }
+
+
+ /**
+ * 初始全数据收集器
+ */
+ void initDataCollector(DataCollector collector) {
+ if (!collector) return
+ try {
+ collectorHolders.remove(collector.id)?.close()
+ if (!collector.enabled) return
+ if ('http' == collector.type) { // http 接口
+ initHttpCollector(collector)
+ } else if ('script' == collector.type) { // groovy 脚本
+ initScriptCollector(collector)
+ } else if ('sql' == collector.type) { // 数据库查询脚本
+ initSqlCollector(collector)
+ } else throw new Exception("Not support type: $collector.type")
+ } catch (ex) {
+ log.error("初始化收集器'$collector.name($collector.id)' 错误".toString(), ex)
+ }
+ }
+
+
+ /**
+ * 初始化 sql 收集器
+ */
+ protected void initSqlCollector(DataCollector collector) {
+ if ('sql' != collector.type) return
+ if (!collector.enabled) return
+ if (!collector.url) {
+ log.warn('sql url must not be empty'); return
+ }
+ if (!collector.sqlScript) {
+ log.warn('sqlScript must not be empty'); return
+ }
+ if (collector.minIdle < 0 || collector.minIdle > 50) {
+ log.warn('0 <= minIdle <= 50'); return
+ }
+ if (collector.maxActive < 1 || collector.maxActive > 100) {
+ log.warn('1 <= minIdle <= 100'); return
+ }
+
+ def db = new DB(Repo.createDataSource([ //创建一个DB. 用于界面配置sql脚本
+ url: collector.url, jdbcUrl: collector.url,
+ minIdle: collector.minIdle, maxActive: collector.maxActive,
+ minimumIdle: collector.minIdle, maximumPoolSize: collector.maxActive
+ ]))
+
+ Binding binding = new Binding()
+ def config = new CompilerConfiguration()
+ binding.setProperty('DB', db)
+ binding.setProperty('LOG', LoggerFactory.getLogger("ROOT"))
+ def icz = new ImportCustomizer()
+ config.addCompilationCustomizers(icz)
+ icz.addImports(JSON.class.name, JSONObject.class.name, Utils.class.name)
+ Closure sqlScript = new GroovyShell(Thread.currentThread().contextClassLoader, binding, config).evaluate("{ -> $collector.sqlScript }")
+
+ // 缓存时间计算函数
+ Closure cacheTimeoutFn = buildCacheTimeoutFn(collector)
+ collectorHolders.put(collector.id, new CollectorHolder(collector: collector, db: db, dataKeyFn: { DecisionContext ctx -> //数据结果唯一性key计算逻辑
+ computeCollectDataKey(collector, ctx)
+ }, computeFn: { DecisionContext ctx -> //sql脚本执行函数
+ Object result // 结果
+ Exception exx //异常
+ String dataKey //数据缓存key
+ boolean cache = false //结果是否取自缓存
+ Date start //取数据开始时间
+ long spend
+ def logMsg = "${ctx.logPrefix()}${ -> cache ? '(缓存)' : ''}Sql收集器'${collector.name}(${collector.id})'${ -> spend ? ', spend: ' + spend : ''}, result: ${ -> result}"
+
+ //1. 先从缓存中取
+ if (cacheTimeoutFn) {
+ dataKey = computeCollectDataKey(collector, ctx)
+ if (redis) {
+ start = new Date() // 调用时间
+ result = redis.get(getStr("collectorCacheKeyPrefix", "collector") + ":" + dataKey)
+ spend = System.currentTimeMillis() - start.time
+ } else if (cacheSrv) {
+ start = new Date() // 调用时间
+ result = cacheSrv.get(getStr("collectorCacheKeyPrefix", "collector") +":"+ dataKey)?.toString()
+ spend = System.currentTimeMillis() - start.time
+ }
+ }
+ //2. 执行sql脚本
+ if (result == null || result.toString().isEmpty()) {
+ try {
+ start = new Date() // 调用时间
+ result = rehydrate2(sqlScript, ctx.data, this)()
+ spend = System.currentTimeMillis() - start.time
+ log.info(logMsg.toString())
+ } catch (ex) {
+ exx = ex
+ log.error(logMsg.toString(), ex)
+ }
+ } else {
+ cache = true
+ log.info(logMsg.toString())
+ }
+ //3. 缓存结果
+ if (result != null && !result.toString().isEmpty() && dataKey && !cache) { //缓存结果
+ setCache(ctx, cacheTimeoutFn, dataKey, result)
+ }
+ //4. 保存结果
+ dataCollected(collector, ctx, cache, start, spend, (exx ? '0001' : '0000'), result, exx, null, null)
+ return result
+ }, testComputeFn: {param -> // 测试:sql脚本执行函数
+ def result = rehydrate2(sqlScript, param, this)()
+ if (result instanceof Map) {
+ return result.collectEntries {e -> [e.key, e.value instanceof Closure ? e.value() : e.value]}
+ }
+ return result
+ }))
+ }
+
+
+ /**
+ * 初始化 script 收集器
+ */
+ protected void initScriptCollector(DataCollector collector) {
+ if ('script' != collector.type) return
+ if (!collector.enabled) return
+ if (!collector.computeScript) {
+ log.warn("Script collector'$collector.name' script must not be empty".toString()); return
+ }
+ Binding binding = new Binding()
+ binding.setProperty('LOG', LoggerFactory.getLogger("ROOT"))
+ def config = new CompilerConfiguration()
+ def icz = new ImportCustomizer()
+ config.addCompilationCustomizers(icz)
+ icz.addImports(JSON.class.name, JSONObject.class.name, Utils.class.name)
+ Closure script = new GroovyShell(Thread.currentThread().contextClassLoader, binding, config).evaluate("{ -> $collector.computeScript}")
+
+ // 缓存时间计算函数
+ Closure cacheTimeoutFn = buildCacheTimeoutFn(collector)
+ collectorHolders.put(collector.id, new CollectorHolder(collector: collector, dataKeyFn: { ctx -> //数据结果唯一性key计算逻辑
+ computeCollectDataKey(collector, ctx)
+ }, computeFn: { DecisionContext ctx ->
+ def result
+ Date start
+ long spend
+ String dataKey // 缓存key
+ boolean cache = false // 是否取自缓存
+ Exception ex
+ def logMsg = "${ctx.logPrefix()}${ -> cache ? '(缓存)' : ''}脚本收集器'${collector.name}(${collector.id})'${ -> ', spend: ' + spend}, result: ${ -> result}"
+
+ //1. 先从缓存中取
+ if (cacheTimeoutFn) {
+ dataKey = computeCollectDataKey(collector, ctx)
+ if (redis) {
+ start = new Date() // 调用时间
+ result = redis.get(getStr("collectorCacheKeyPrefix", "collector") + ":" +dataKey)
+ spend = System.currentTimeMillis() - start.time
+ } else if (cacheSrv) {
+ start = new Date() // 调用时间
+ result = cacheSrv.get(getStr("collectorCacheKeyPrefix", "collector") + ":" +dataKey)?.toString()
+ spend = System.currentTimeMillis() - start.time
+ }
+ }
+ //2. 调用脚本
+ if (!result) {
+ try {
+ start = new Date() // 调用时间
+ result = rehydrate2(script, ctx.data, this)()
+ spend = System.currentTimeMillis() - start.time
+ log.info(logMsg.toString())
+ } catch (e) {
+ ex = e
+ log.error(logMsg.toString(), ex)
+ }
+ } else {
+ cache = true
+ log.error(logMsg.toString(), ex)
+ }
+ //3. 缓存结果
+ if (!cache && result != null && !result.toString().isEmpty() && dataKey) {
+ setCache(ctx, cacheTimeoutFn, dataKey, result)
+ }
+ //4. 保存结果
+ dataCollected(collector, ctx, cache, start, spend, (ex ? '0001' : '0000'), result, ex, null, null)
+ return result
+ }, testComputeFn: {param ->
+ def result = rehydrate2(script, param, this)()
+ if (result instanceof Map) {
+ return result.collectEntries {e -> [e.key, e.value instanceof Closure ? e.value() : e.value]}
+ }
+ return result
+ }))
+ }
+
+
+ /**
+ * 初始化 http 收集器
+ */
+ protected void initHttpCollector(DataCollector collector) {
+ if ('http' != collector.type) return
+ if (!collector.enabled) return
+ // 创建 http 客户端
+ def http = new OkHttpSrv('okHttp_' + collector.id); app().inject(http)
+ http.setAttr('connectTimeout', getLong("http.connectTimeout." + collector.id, getLong('http.connectTimeout', 3L)))
+ http.setAttr('readTimeout', getLong("http.readTimeout." + collector.id, getLong('http.readTimeout', Long.valueOf(collector.timeout?:20))))
+ http.init()
+
+ // 结果解析函数
+ Closure parseFn
+ if (collector.parseScript) {
+ Binding binding = new Binding()
+ binding.setProperty('LOG', LoggerFactory.getLogger("ROOT"))
+ def config = new CompilerConfiguration()
+ def icz = new ImportCustomizer()
+ config.addCompilationCustomizers(icz)
+ icz.addImports(JSON.class.name, JSONObject.class.name, Utils.class.name)
+ parseFn = new GroovyShell(Thread.currentThread().contextClassLoader, binding, config).evaluate("$collector.parseScript")
+ }
+ // 缓存时间计算函数
+ Closure cacheTimeoutFn = buildCacheTimeoutFn(collector)
+ // 是否成功判断函数
+ Closure successFn
+ if (collector.dataSuccessScript) {
+ Binding binding = new Binding()
+ binding.setProperty('LOG', LoggerFactory.getLogger("ROOT"))
+ def config = new CompilerConfiguration()
+ def icz = new ImportCustomizer()
+ config.addCompilationCustomizers(icz)
+ icz.addImports(JSON.class.name, JSONObject.class.name, Utils.class.name)
+ successFn = new GroovyShell(Thread.currentThread().contextClassLoader, binding, config).evaluate("$collector.dataSuccessScript")
+ }
+
+ // GString 模板替换
+ def tplEngine = new GStringTemplateEngine(Thread.currentThread().contextClassLoader)
+ collectorHolders.put(collector.id, new CollectorHolder(collector: collector, dataKeyFn: { ctx -> //数据结果唯一性key计算逻辑
+ computeCollectDataKey(collector, ctx)
+ }, computeFn: { DecisionContext ctx -> // 数据集成中3方接口访问过程
+ String result // 接口返回结果字符串
+ Object resolveResult // 解析接口返回结果
+ long spend = 0 // 取数据,(网络)耗时时长
+ Date start //取数据开始时间
+ String url = collector.url // http请求 url
+ String bodyStr = collector.bodyStr // http 请求 body字符串
+ String dataKey // 数据缓存key
+ boolean cache = false //结果是否取自缓存
+ Integer respCode // http响应码
+
+ String retryMsg = '' //重试消息
+ // 日志字符串
+ def logMsg = "${ctx.logPrefix()}${ -> cache ? '(缓存)' : ''}接口收集器'$collector.name(${collector.id})'${ -> retryMsg}, url: ${ -> url}${ -> bodyStr == null ? '' : ', body: ' + bodyStr}${ -> spend ? ', spend: ' + spend : ''}${ -> respCode ? ', respCode: ' + respCode : ''}${ -> ', result: ' + result}${ -> resolveResult == null ? '' : ', resolveResult: ' + (resolveResult instanceof Map ? resolveResult.findAll {e -> !(e.value instanceof Closure)} : (resolveResult instanceof Closure ? '' : resolveResult))}"
+
+ //1. 先从缓存中取
+ if (cacheTimeoutFn) {
+ dataKey = computeCollectDataKey(collector, ctx)
+ if (redis) {
+ start = new Date() // 调用时间
+ result = redis.get(getStr("collectorCacheKeyPrefix", "collector") + ":" +dataKey)
+ spend = System.currentTimeMillis() - start.time
+ } else if (cacheSrv) {
+ start = new Date() // 调用时间
+ result = cacheSrv.get(getStr("collectorCacheKeyPrefix", "collector") + ":" +dataKey)?.toString()
+ spend = System.currentTimeMillis() - start.time
+ }
+ }
+
+ //2. 未从缓存中取到结果, 则调接口, 网络连接错误,主动重试3次
+ if (!result) {
+ // url 替换 ${} 变量
+ for (int i = 0; i < 2; i++) {
+ if (!url.contains('${')) break
+ url = tplEngine.createTemplate(url).make(new HashMap(7) {
+ int paramIndex
+ @Override
+ boolean containsKey(Object key) { return true } // 加这行是为了 防止 MissingPropertyException
+ @Override
+ Object get(Object key) {
+ paramIndex++ // 获取个数记录
+ if (super.containsKey(key)) return super.get(key)
+ def v = ctx.data.get(key)
+ // url前缀不必编码, 其它参数需要编码
+ return v == null ? '' : (paramIndex==1 && v.toString().startsWith("http") && collector.url.startsWith('${') ? v : URLEncoder.encode(v.toString(), 'utf-8'))
+ }
+ }).toString()
+ }
+
+ // body 替换 ${} 变量
+ for (int i = 0; i < 2; i++) {
+ if (!bodyStr || !bodyStr.contains('${')) break
+ bodyStr = tplEngine.createTemplate(bodyStr).make(new HashMap(7) {
+ @Override
+ boolean containsKey(Object key) { return true } // 加这行是为了 防止 MissingPropertyException
+ @Override
+ Object get(Object key) { super.containsKey(key) ? super.get(key) : (ctx.data.get(key)?:"") }
+ }).toString()
+ }
+ try {
+ start = new Date() // 调用时间
+ for (int i = 0, times = getInteger('http.retry', 2) + 1; i < times; i++) { // 接口一般遇网络错重试2次
+ try {
+ retryMsg = i > 0 ? "(重试第${i}次)" : ''
+ if ('get'.equalsIgnoreCase(collector.method)) {
+ def h = http.get(url)
+ result = h.execute()
+ respCode = h.respCode
+ } else if ('post'.equalsIgnoreCase(collector.method)) {
+ def h = http.post(url)
+ result = h.textBody(bodyStr).contentType(collector.contentType).execute()
+ respCode = h.respCode
+ } else throw new Exception("Not support http method $collector.method")
+ break
+ } catch (ex) {
+ // ConnectException | ConnectionResetException | NoRouteToHostException | UnknownHostException
+ if ((ex instanceof ConnectException) && (i + 1) < times) {
+ log.warn(logMsg.toString() + ", 异常: " + (ex.class.simpleName + ': ' + ex.message))
+ continue
+ } else throw ex
+ } finally {
+ spend = System.currentTimeMillis() - start.time
+ }
+ }
+ } catch (ex) {
+ log.error(logMsg.toString(), ex)
+ dataCollected(collector, ctx, cache, start, spend, '0001', result, ex, null, null, url, bodyStr)
+ return null
+ } finally {
+ retryMsg = ''
+ }
+ } else {
+ cache = true
+ }
+
+ //3. 判断http返回结果是否为有效数据. 默认有效(0000)
+ String dataStatus = successFn ? (rehydrate2(successFn, ctx.data, this)(result, respCode) ? '0000' : '0001') : '0000'
+
+ //4. 如果接口返回的是有效数据, 则缓存
+ if ('0000' == dataStatus && dataKey && !cache) setCache(ctx, cacheTimeoutFn, dataKey, result)
+
+ //5. 解析接口返回结果
+ if (parseFn && dataStatus == '0000') {
+ Exception ex
+ try {
+ resolveResult = rehydrate2(parseFn, ctx.data, this)(result, respCode)
+ } catch (e) {
+ ex = e
+ } finally {
+ if (ex) log.error(logMsg.toString() + ", 解析函数执行失败", ex)
+ else log.info(logMsg.toString())
+ dataCollected(collector, ctx, cache, start, spend, dataStatus, result, null, resolveResult, ex, url, bodyStr)
+ }
+ return resolveResult
+ }
+
+ log.info(logMsg.toString())
+ dataCollected(collector, ctx, cache, start, spend, dataStatus, result, null, resolveResult, null, url, bodyStr)
+ return dataStatus == '0000' ? result : null
+ }, testComputeFn: {param ->
+ // http请求 url
+ String url = collector.url
+ for (int i = 0; i < 2; i++) { // 替换 ${} 变量
+ if (!url.contains('${')) break
+ url = tplEngine.createTemplate(url).make(new HashMap(7) {
+ int paramIndex
+ @Override
+ boolean containsKey(Object key) { return true } // 加这行是为了 防止 MissingPropertyException
+ @Override
+ Object get(Object key) {
+ paramIndex++ // 获取个数记录
+ if (super.containsKey(key)) return super.get(key)
+ def v = param.get(key)
+ // url前缀不必编码, 其它参数需要编码
+ return v == null ? '' : (paramIndex==1 && v.toString().startsWith("http") && collector.url.startsWith('${') ? v : URLEncoder.encode(v.toString(), 'utf-8'))
+ }
+ }).toString()
+ }
+
+ // http 请求 body字符串
+ String bodyStr = collector.bodyStr
+ for (int i = 0; i < 2; i++) { // 替换 ${} 变量
+ if (!bodyStr || !bodyStr.contains('${')) break
+ bodyStr = tplEngine.createTemplate(bodyStr).make(new HashMap(7) {
+ @Override
+ boolean containsKey(Object key) { return true } // 加这行是为了 防止 MissingPropertyException
+ @Override
+ Object get(Object key) { super.containsKey(key) ? super.get(key) : (param.get(key)?:"") }
+ }).toString()
+ }
+ // NOTE: 如果是json 并且是,} 结尾, 则删除 最后的,(因为spring解析入参数会认为json格式错误)
+ // if (bodyStr.endsWith(',}')) bodyStr = bodyStr.substring(0, bodyStr.length() - 3) + '}'
+
+ String result // 接口返回结果字符串
+ def resolveResult // 解析接口返回结果
+
+ Integer respCode //http响应码
+ if ('get'.equalsIgnoreCase(collector.method)) {
+ def h = http.get(url)
+ result = h.debug().execute()
+ respCode = h.respCode
+ } else if ('post'.equalsIgnoreCase(collector.method)) {
+ def h = http.post(url)
+ result = h.textBody(bodyStr).contentType(collector.contentType).debug().execute()
+ respCode = h.respCode
+ } else throw new Exception("Not support http method $collector.method")
+
+ // http 返回结果成功判断. 默认成功
+ String dataStatus = successFn ? (rehydrate2(successFn, param, this)(result, respCode) ? '0000' : '0001') : '0000'
+
+ if (parseFn && dataStatus == '0000') { // 解析接口返回结果
+ resolveResult = rehydrate2(parseFn, param, this)(result, respCode)
+ if (resolveResult instanceof Map) {
+ return resolveResult.collectEntries {e -> [e.key, e.value instanceof Closure ? e.value() : e.value]}
+ }
+ return resolveResult
+ }
+ return dataStatus == '0000' ? result : null
+ }))
+ }
+
+
+ /**
+ * 计算收集器的数据缓存key
+ * @param collector 收集器
+ * @param ctx 当前执行上下文
+ * @return dataKey
+ */
+ protected String computeCollectDataKey(DataCollector collector, DecisionContext ctx) {
+ if (!collector.cacheKey) return collector.id //未配置,dataKey为收集器id
+ String dataKey = collector.cacheKey
+ if (dataKey.contains('${')) {
+ def tplEngine = new GStringTemplateEngine(Thread.currentThread().contextClassLoader)
+ for (int i = 0; i < 2; i++) { // 替换 ${} 变量
+ if (!dataKey.contains('${')) break
+ dataKey = tplEngine.createTemplate(dataKey).make(new HashMap(1) {
+ @Override
+ boolean containsKey(Object key) { true } // 加这行是为了 防止 MissingPropertyException
+ @Override
+ Object get(Object key) { super.containsKey(key) ? super.get(key) : ctx.data.get(key) }
+ }).toString()
+ }
+ }
+
+ return collector.id + '_' + dataKey
+ }
+
+
+ /**
+ * 缓存结果
+ * @param ctx 当前执行上下文
+ * @param timeoutFn 缓存时间计算函数
+ * @param dataKey 缓存数据key
+ * @param result 要缓存的数据
+ */
+ protected void setCache(DecisionContext ctx, Closure timeoutFn, String dataKey, def result) {
+ def cacheTimeout = rehydrate2(timeoutFn, ctx.data, this)()
+ if (cacheTimeout instanceof Date) {
+ long v = cacheTimeout.time - System.currentTimeMillis()
+ if (v < 1000) {
+ log.debug(ctx.logPrefix() + "缓存过期时间不能小于当前时间. dataKey: " + dataKey)
+ return
+ }
+ cacheTimeout = Duration.ofMillis(v)
+ } else if (cacheTimeout instanceof Integer) {
+ if (cacheTimeout <= 0) return
+ cacheTimeout = Duration.ofMinutes(cacheTimeout)
+ } else if (cacheTimeout instanceof Duration) {
+
+ } else throw new RuntimeException("缓存过期时间函数返回类型错误")
+ if (cacheTimeout.seconds <= 0) return
+ String key = getStr("collectorCacheKeyPrefix", "collector") +":"+ dataKey
+ if (redis) {
+ redis.set(key, result.toString())
+ redis.expire(key, cacheTimeout.seconds)
+ }
+ else if (cacheSrv) {
+ cacheSrv.set(key, result, cacheTimeout)
+ }
+ }
+
+
+ /**
+ * 构建收集器 缓存时间计算函数
+ * @param collector 收集器
+ */
+ protected Closure buildCacheTimeoutFn(DataCollector collector) {
+ if (collector.cacheTimeoutFn) {
+ Binding binding = new Binding()
+ def config = new CompilerConfiguration()
+ def icz = new ImportCustomizer()
+ config.addCompilationCustomizers(icz)
+ icz.addImports(JSON.class.name, JSONObject.class.name, Utils.class.name)
+ return new GroovyShell(Thread.currentThread().contextClassLoader, binding, config).evaluate("{ -> $collector.cacheTimeoutFn }")
+ }
+ null
+ }
+
+
+ /**
+ * 收集器 Holder
+ */
+ class CollectorHolder {
+ // 对应数据库中的实体
+ DataCollector collector
+ // 把收集器转换的执行函数
+ Function computeFn
+ // 数据收集上下文唯一key: 用于标识当前执行上下文是否需要重复计算当前收集器的结果
+ Function dataKeyFn
+ // 单元测试函数
+ Function testComputeFn
+ // DB
+ DB db
+
+ void close() {
+ db.close()
+ }
+
+
+ /**
+ * 属性值收集
+ * @param aName 属性名
+ * @param ctx 执行上下文
+ * @return 属性值
+ */
+ def populate(String aName, DecisionContext ctx) {
+ String dataKey = dataKeyFn.apply(ctx) //数据key,判断是否需要重新执行收集器的计算函数拿结果
+ // 值get函数
+ def valueGetFn = { ->
+ def collectResult = ctx.dataCollectResult.get(dataKey)
+ if (collectResult instanceof Map) { //收集器有多个属性的值
+ def value
+ String key
+ if (collectResult.containsKey(aName)) {
+ key = aName
+ value = collectResult.get(key)
+ } else {
+ key = fieldManager.alias(aName)
+ value = collectResult.get(key)
+ }
+ if (value instanceof Closure) { // 延迟计算函数,只计算一次
+ value = value()
+ collectResult.put(key, value)
+ }
+ return value
+ } else if (collectResult instanceof Closure) {
+ log.error(ctx.logPrefix() + "收集器'${collector.name}(${collector.id})'结果不能是个函数")
+ }
+ return collectResult
+ }
+ // 收集器已经执行过
+ if (ctx.dataCollectResult.containsKey(dataKey)) return valueGetFn()
+
+ ctx.dataCollectResult.put(dataKey, null) //占位,避免循环计算
+ // 函数执行
+ log.debug(ctx.logPrefix() + "Get '${aName}' value apply function: '${collector.name}(${collector.id})'".toString())
+ def collectResult = null
+ try {
+ collectResult = computeFn.apply(ctx)
+ } catch (ex) { // 接口执行报错, 默认继续往下执行规则
+ log.error(ctx.logPrefix() + "数据收集器'${collector.name}(${collector.id})' 执行错误".toString(), ex)
+ }
+ ctx.dataCollectResult.put(dataKey, collectResult)
+ return valueGetFn()
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/service/rule/DecideResult.groovy b/src/service/rule/DecideResult.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..3fb24a6567a7bd206f113a09f217b0ba9c1f6961
--- /dev/null
+++ b/src/service/rule/DecideResult.groovy
@@ -0,0 +1,18 @@
+package service.rule
+
+/**
+ * 决策结果
+ */
+enum DecideResult {
+ Accept("通过", false), Reject("拒绝", true), Review("人工审核", false)
+ String cn
+ /**
+ * 是否阻断往下执行
+ */
+ boolean block
+
+ DecideResult(String cn, boolean block) {
+ this.cn = cn
+ this.block = block
+ }
+}
\ No newline at end of file
diff --git a/src/service/rule/DecisionContext.groovy b/src/service/rule/DecisionContext.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..3279b602aa3aa64f6dfe4e41a6b89020a794773e
--- /dev/null
+++ b/src/service/rule/DecisionContext.groovy
@@ -0,0 +1,412 @@
+package service.rule
+
+import cn.xnatural.enet.event.EC
+import cn.xnatural.enet.event.EP
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import service.rule.spec.*
+
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.atomic.AtomicInteger
+
+/**
+ * 决策执行上下文
+ */
+class DecisionContext {
+ public static final Logger log = LoggerFactory.getLogger(DecisionContext)
+ // 决策执行标识(id)
+ final String id
+ // 开始时间
+ final Date startup = new Date()
+ // 当前正在执行的策略
+ final DecisionHolder decisionHolder
+ // 字段管理器
+ final FieldManager fieldManager
+ // 事件中心: 用于通知策略执行过程中的产生想事件
+ final EP ep
+ // 输入参数
+ final Map input
+
+ // 执行过程中的产生的所有数据
+ @Lazy Map data = new Data(this)
+ // 子决策 执行统计
+ @Lazy Map delegateDecisionCount = [:]
+ // 执行的策略集
+ final List policies = new LinkedList<>()
+ // 数据key -> 数据收集结果集. 不用 ConcurrentHashMap 因为不能放null值
+ final Map dataCollectResult = new LinkedHashMap<>()
+ // 过程数据: 决策(非策略,规则)
+ final Map decisionData = new LinkedHashMap<>()
+
+ // 是否已启动
+ protected final def started = new AtomicBoolean(false)
+ // 是否执行结束
+ protected final def end = new AtomicBoolean(false)
+ // 当前正在执行的策略
+ protected PassedSpec curPassedPolicy
+ // 当前正在执行的规则
+ protected PassedSpec curPassedRule
+ // 当前正在执行的评分卡
+ protected PassedSpec curPassedScorecard
+ // 当前正在执行的子决策
+ protected PassedSpec curPassedDecision
+
+ // 执行结果 异常
+ protected Throwable exception
+ // 执行状态 初始化status: 0001, 结束status: 0000, 错误status: EEEE
+ protected String status = '0001'
+ // 最终决策结果
+ DecideResult decideResult
+
+
+ DecisionContext(String id, DecisionHolder decisionHolder, Map input, FieldManager fieldManager, EP ep) {
+ this.id = id?:UUID.randomUUID().toString().replace('-', '')
+ this.decisionHolder = decisionHolder
+ this.input = input
+ this.fieldManager = fieldManager
+ this.ep = ep
+ }
+
+
+ /**
+ * 开始执行流程
+ */
+ void start() {
+ if (!started.compareAndSet(false, true)) return
+ log.info(logPrefix() + "开始")
+ trigger()
+ }
+
+
+ /**
+ * 触发执行流程: 遍历策略, 遍历规则
+ */
+ protected final void trigger() {
+ if (end.get()) return
+ try {
+ decideResult = decisionHolder.spec.compute(this)
+ end.set(true); decideResult = decideResult?:DecideResult.Accept; status = '0000'
+ log.info(logPrefix() + "结束成功. 共执行: " + (summary()['spend']) + "ms. result: " + result())
+ ep?.fire("decision.end", this)
+ } catch (Throwable ex) {
+ end.set(true); decideResult = DecideResult.Reject; status = 'EEEE'; this.exception = ex
+ log.error(logPrefix() + "结束错误. 共执行: " + (summary()['spend']) + "ms. result: " + result(), ex)
+ ep?.fire("decision.end", this)
+ }
+ }
+
+
+ /**
+ * 执行策略
+ * @param spec
+ * @return {@link DecideResult}
+ */
+ DecideResult run(PolicySpec spec) {
+ curPassedPolicy = new PassedSpec(spec: spec, attrs: [策略名: spec.策略名, *:spec.attrs])
+ policies.add(curPassedPolicy)
+ log.debug(logPrefix() + "开始执行")
+ DecideResult result = spec.compute(this)
+ curPassedPolicy.result = result
+ log.info(logPrefix() + "结束执行. result: " + result + ", data: " + (curPassedPolicy.data?:null))
+ curPassedPolicy = null
+ result
+ }
+
+
+ /**
+ * 执行规则
+ * @param spec
+ * @return {@link DecideResult}
+ */
+ DecideResult run(RuleSpec spec) {
+ curPassedRule = new PassedSpec(spec: spec, attrs: [规则名: spec.规则名, *:spec.attrs])
+ curPassedPolicy.items.add(curPassedRule)
+ log.debug(logPrefix() + "开始执行")
+ DecideResult result = spec.compute(this)
+ curPassedRule.result = result
+ log.info(logPrefix() + "结束执行. result: " + result + ", data: " + (curPassedRule.data?:null))
+ curPassedRule = null
+ result
+ }
+
+
+ /**
+ * 执行评分卡
+ * @param spec
+ * @return {@link DecideResult}
+ */
+ def run(ScorecardSpec spec) {
+ curPassedScorecard = new PassedSpec(spec: spec, attrs: [评分卡名: spec.评分卡名, *:spec.attrs])
+ curPassedPolicy.items.add(curPassedScorecard)
+ log.debug(logPrefix() + "开始执行")
+ def result = spec.compute(this)
+ curPassedScorecard.result = result
+ log.info(logPrefix() + "结束执行. result: " + result + ", data: " + (curPassedScorecard.data?:null))
+ curPassedScorecard = null
+ result
+ }
+
+
+ /**
+ * 执行子决策
+ * @param spec
+ * @return {@link DecideResult}
+ */
+ DecideResult run(DelegateDecisionSpec spec) {
+ // 根据 决策id 找到对应的 决策
+ def holder = bean(DecisionManager).findDecision(spec.决策id)
+ if (!holder) throw new IllegalArgumentException("决策id($spec.决策id) 未找到对应的决策")
+
+ curPassedDecision = new PassedSpec(spec: holder.spec, attrs: [决策id: spec.决策id, 决策名: holder.spec.决策名, *:holder.spec.attrs])
+ curPassedPolicy.items.add(curPassedDecision)
+ log.debug(logPrefix() + "开始执行")
+
+ DecideResult result = spec.compute(this)
+
+ curPassedDecision.result = result
+ curPassedDecision = null
+ result
+ }
+
+
+ /**
+ * 设置属性
+ * @param aName 属性名
+ * @param value 属性值
+ */
+ void setAttr(String aName, Object value) { data.put(aName, value) }
+
+
+ /**
+ * 过程数据记录: 规则中, 策略中, 决策中
+ * 当前如果是正在执行规则,则记录到规则数据
+ * 当前如果是正在执行评分卡,则记录到评分卡数据
+ * 当前如果是正在执行子决策,则记录到子决策数据
+ * 当前如果是正在执行策略,则记录到策略数据
+ * 当前如果是正在执行决策,则记录到决策数据
+ * @param key 属性key
+ * @param value 属性值
+ */
+ protected void recordData(String key, Object value) {
+ if (end.get()) return
+ if (curPassedRule) curPassedRule.data.put(key, value)
+ else if (curPassedScorecard) curPassedScorecard.data.put(key, value)
+ else if (curPassedDecision) curPassedDecision.data.put(key, value)
+ else if (curPassedPolicy) curPassedPolicy.data.put(key, value)
+ else decisionData.put(key, value)
+ }
+
+
+ /**
+ * 数据存放. 用于规则闭包执行上下文
+ */
+ protected class Data extends LinkedHashMap {
+ final DecisionContext ctx
+ /**
+ * 属性值来源: 为了解决优先级,及重新获取的问题
+ * 优先级: 执行上下文设置 > input > 收集器(可重复计算)
+ */
+ final Map valueFromMap = new HashMap<>()
+
+ Data(DecisionContext ctx) {this.ctx = ctx}
+
+
+ /**
+ * 查找已存在的属性
+ * @param aName 属性名
+ * @return 属性值
+ */
+ Object find(Object aName) { super.get(aName) }
+
+
+ @Override
+ Object get(Object aName) {
+ if (aName == null) return null
+ // 函数名
+ if (ctx.decisionHolder.spec.globalFunctions.containsKey(aName)) return null
+
+ if (!ctx.end.get()) {
+ // 属性值已存在
+ if (super.containsKey(aName)) {
+ // 判断是否需要重尝试新计算
+ if (valueFromMap.get(aName) == 'collector') {
+ safeSet((String) aName, ctx.getFieldManager().dataCollect(aName.toString(), ctx), 'collector')
+ }
+ } else { // 第一次获取属性值
+ safeSet(aName.toString(), null, null) // 占位. 解决 循环属性获取链
+ boolean fromInput = false
+ if (ctx.input) { //数据源1: 先从入参里面获取
+ if (ctx.input.containsKey(aName)) {
+ fromInput = true
+ safeSet((String) aName, ctx.input.get(aName), 'input')
+ } else {
+ def alias = ctx.getFieldManager().alias(aName)
+ if (alias != null && ctx.input.containsKey(alias)) {
+ fromInput = true
+ safeSet((String) aName, ctx.input.get(alias), 'input')
+ }
+ }
+ }
+ if (!fromInput) { //数据源2: 再从数据收集器获取
+ safeSet((String) aName, ctx.getFieldManager().dataCollect(aName.toString(), ctx), 'collector')
+ }
+ }
+ }
+ def value = super.get(aName)
+ if (value instanceof Optional) value = value.orElse(null)
+
+ ctx.recordData(aName.toString(), value)
+ return value
+ }
+
+ // 1. 转换成具体类型, 再保存; 2. 同时保存别名的值; 3. 保存值来源
+ protected Object safeSet(String key, Object value, String from) {
+ if (value instanceof Optional) {
+ value = value.present ? Optional.ofNullable(ctx.getFieldManager().convert(key, value.get())) : value
+ } else {
+ value = ctx.getFieldManager().convert(key, value) // 属性值类型转换
+ }
+ super.put(key, value)
+ valueFromMap.put(key, from)
+
+ def n = ctx.getFieldManager().alias(key)
+ if (n && n != key) {
+ super.put(n, value)
+ valueFromMap.put(n, from)
+ }
+
+ value
+ }
+
+ @Override
+ Object put(String key, Object value) {
+ ctx.recordData(key, safeSet(key, value, 'byHand'))
+ value
+ }
+
+ @Override
+ Object remove(Object key) { // 删除缓存
+ def r = super.remove(key)
+ def field = ctx.getFieldManager().fieldHolders.get(key)?.field
+ if (field) {
+ super.remove(field.enName)
+ super.remove(field.cnName)
+ // ctx.dataCollectResult.remove(field.dataCollector)
+ valueFromMap.remove(field.enName)
+ valueFromMap.remove(field.cnName)
+ }
+ return r
+ }
+ }
+
+
+ /**
+ * 执行过的spec模块
+ */
+ protected class PassedSpec {
+ // spec
+ transient BaseSpec spec
+ // 结果
+ def result
+ // 属性
+ Map attrs
+ // 执行过程中产生的或用到的数据集
+ final Map data = new LinkedHashMap()
+ // 子模块
+ @Lazy List items = new LinkedList<>()
+
+ @Override
+ String toString() {
+ return [attrs: attrs, result: result, data: data, items: items].toMapString()
+ }
+ }
+
+
+ // 日志前缀
+ String logPrefix() {
+ "[${id? "$id, " :''}${decisionHolder.decision.decisionId? "决策: $decisionHolder.spec.决策id" :''}${ -> curPassedPolicy? ", 策略: " + curPassedPolicy.spec.name() :''}${ -> curPassedRule ? ", 规则: " + curPassedRule.spec.name() :''}${ -> curPassedScorecard ? ", 评分卡: " + curPassedScorecard.spec.name() :''}${ -> curPassedDecision ? ", 子决策: " + curPassedDecision.spec.name() + "(" + curPassedDecision.spec.决策id + ")":''}] -> "
+ }
+
+
+ /**
+ * 整条决策 所有信息
+ */
+ private Map _summary
+ Map summary() {
+ if (this._summary && end.get()) return this._summary
+ //去重复记录(去对应的中文, 保留对应的英文)
+ def cleanData = {Map data ->
+ data ? data.collect { e ->
+ def value = e.value instanceof Optional ? e.value.orElse(null) : e.value
+ def field = fieldManager.fieldHolders.get(e.key)?.field
+ if (field && field.cnName == e.key) return [field.enName, value]
+ return [e.key, value]
+ }.collectEntries() : null
+ }
+ this._summary = [
+ id : id, //执行决策id
+ decisionId : decisionHolder.decision.decisionId, //决策id
+ spend : System.currentTimeMillis() - startup.time, //执行花费时间
+ occurTime : startup, //決策发生时间
+ result : decideResult, //结果
+ input : input, //输入参数
+ status : status, //初始化status: 0001, 结束status: 0000, 错误status: EEEE
+ exception : exception?.toString(), //异常信息
+ data : cleanData(data), //执行过程产生的最终数据集
+ detail : [ //执行详情, 过程详情
+ data : cleanData(decisionData), //只和决策相关(和具体策略,规则无关)的数据
+ attrs : decisionHolder.spec.attrs?:null, //决策自定义属性集
+ policies: policies.collect { policy -> //执行过的策略集
+ [
+ attrs: policy.attrs, result: policy.result, data: cleanData(policy.data), //只和策略相关(和具体规则无关)的数据
+ items: policy.items.collect { item ->
+ [attrs: item.attrs, result: item.result, data: cleanData(item.data) /* 和具体规则相关的数据 */]
+ }
+ ]
+ }
+ ],
+ // 执行过程中数据收集的结果集
+ dataCollectResult: dataCollectResult?.collectEntries {e ->
+ if (e.value instanceof Map) {
+ return [e.key, ((Map) e.value).findAll {!(it.value instanceof Closure)}]
+ }
+ return e
+ }?:null
+ ]
+ this._summary
+ }
+
+
+ /**
+ * 决策结果输出
+ */
+ Map result() {
+ [
+ decideId: id, result: decideResult, decisionId: decisionHolder.decision.decisionId,
+ status : status,
+ desc : exception?.toString(),
+ data : end.get() && decisionHolder.spec.returnAttrs ? decisionHolder.spec.returnAttrs.collectEntries { name ->
+ def v = data.get(name)
+ if (v instanceof Optional) {v = v.orElse(null)}
+ def field = fieldManager.fieldHolders.get(name)?.field
+ //如果key是中文, 则翻译成对应的英文名
+ if (field && field.cnName == name) return [field.enName, v]
+ else return [name, v]
+ } : null
+ ]
+ }
+
+
+ /**
+ * 全局查找 bean
+ * @param type bean类型
+ * @param name bean名字
+ * @return bean
+ */
+ def T bean(Class type, String name = null) { return ep == null ? null : (T) ep.fire("bean.get", EC.of(this).sync().args(type, name)) }
+
+
+
+ @Override
+ String toString() { result().toString() }
+}
diff --git a/src/service/rule/DecisionHolder.groovy b/src/service/rule/DecisionHolder.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..dbede5b3f90836a683f39ed3741c84f0d98677e4
--- /dev/null
+++ b/src/service/rule/DecisionHolder.groovy
@@ -0,0 +1,18 @@
+package service.rule
+
+import entity.Decision
+import service.rule.spec.DecisionSpec
+
+import java.util.function.Function
+
+/**
+ * 决策 Holder
+ */
+class DecisionHolder {
+ // 对应实体 decision
+ Decision decision
+ // dsl spec
+ DecisionSpec spec
+ // 参数验证函数
+ Function, Boolean> paramValidator
+}
diff --git a/src/service/rule/DecisionManager.groovy b/src/service/rule/DecisionManager.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..d23b60b867da0daf5e4ef627028315ccc6e9d5ba
--- /dev/null
+++ b/src/service/rule/DecisionManager.groovy
@@ -0,0 +1,262 @@
+package service.rule
+
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.app.Utils
+import cn.xnatural.enet.event.EC
+import cn.xnatural.enet.event.EL
+import cn.xnatural.jpa.Repo
+import cn.xnatural.remoter.Remoter
+import com.alibaba.fastjson.JSON
+import com.alibaba.fastjson.JSONObject
+import entity.Decision
+import entity.FieldType
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.control.customizers.ImportCustomizer
+import service.rule.spec.DecisionSpec
+
+import java.text.SimpleDateFormat
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.atomic.AtomicInteger
+import java.util.function.Function
+import java.util.regex.Pattern
+
+/**
+ * 决策管理器
+ */
+class DecisionManager extends ServerTpl {
+ @Lazy def repo = bean(Repo, 'jpa_rule_repo')
+
+ final Map decisionMap = new ConcurrentHashMap<>()
+
+
+ /**
+ * 查找 决策
+ * @param decisionId
+ * @return DecisionHolder
+ */
+ DecisionHolder findDecision(String decisionId) {
+ def holder = decisionMap.get(decisionId)
+ if (holder == null) {
+ initDecision(repo.find(Decision) { root, query, cb -> cb.equal(root.get('decisionId'), decisionId)})
+ holder = decisionMap.get(decisionId)
+ }
+ return holder
+ }
+
+
+ @EL(name = ['decision.delete', 'decision.update', 'decision.create', 'decision.dataVersion'], async = true)
+ protected void listenDecisionChange(EC ec, String id) {
+ def decision = repo.findById(Decision, id)
+ if (decision == null) {
+ decisionMap.remove(id)
+ log.info("delDecision: " + id)
+ } else {
+ log.info("decisionChanged: " + decision.name + ", " + decision.decisionId + ", " + decision.id)
+ initDecision(decision)
+ }
+ def remoter = bean(Remoter)
+ if (remoter && ec?.source() != remoter) { // 不是远程触发的事件
+ remoter.dataVersion('decision').update(id, decision ? decision.updateTime.time : System.currentTimeMillis(), null)
+ }
+ }
+
+
+ /**
+ * 加载所有决策
+ */
+ @EL(name = 'jpa_rule.started', async = true)
+ void load() {
+ final Set ids = (decisionMap ? ConcurrentHashMap.newKeySet(decisionMap.size()) : null)
+ final def threshold = new AtomicInteger(1)
+ final def tryComplete = {
+ if (threshold.decrementAndGet() > 0) return
+ if (ids) { // 删除库里面没有, 内存里面有的数据
+ decisionMap.findAll {!ids.contains(it.key)}.each {
+ decisionMap.remove(it.key)
+ }
+ }
+ }
+ for (int page = 0, limit = 10; ; page++) {
+ def ls = repo.findList(Decision, page * limit, limit)
+ threshold.incrementAndGet()
+ async { // 异步加载
+ ls.each { decision ->
+ ids?.add(decision.decisionId)
+ try {
+ initDecision(decision)
+ } catch (ex) {
+ log.error("加载决策'${decision.name}:${decision.decisionId}'错误", ex)
+ }
+ }
+ tryComplete()
+ }
+ if (!ls || ls.size() < limit) {
+ tryComplete(); break
+ }
+ }
+ }
+
+
+ /**
+ * 初始化决策
+ */
+ protected void initDecision(Decision decision) {
+ if (!decision || !decision.dsl) return
+ def spec = DecisionSpec.of(decision.dsl)
+ def apiConfig = decision.apiConfig ? JSON.parseArray(decision.apiConfig) : null
+ Map> validFunMap = [:]
+ Map patternMap = [:]
+ decisionMap.put(spec.决策id, new DecisionHolder(decision: decision, spec: spec, paramValidator: (apiConfig ? {Map params ->
+ apiConfig.each { JSONObject paramCfg ->
+ String code = paramCfg.getString('code') // 参数code
+ // TODO 忽略 async 后边改成用路径参数
+ if (code == "async") return
+ def value = params.get(code)
+ if (paramCfg.getBoolean('require') && value == null) { // 必传验证
+ throw new IllegalArgumentException("Param '$code' require")
+ }
+ def fixValue // 固定值
+ def enumValues // 枚举值
+ Pattern pattern // 正则验证
+ Function validFun // 函数验证
+ def type = FieldType.valueOf(paramCfg.getString('type')) // 参数类型. FieldType
+ if (type == null || !FieldType.enumConstants.find {it == type}) throw new Exception("参数验证类型配置错误. type: $type")
+
+ if (FieldType.Bool == type) { // Boolean 值验证
+ if (value == null) value = paramCfg.getBoolean('defaultValue')
+ if (value != null) {
+ if (!'true'.equalsIgnoreCase(value.toString()) && !'false'.equalsIgnoreCase(value.toString())) {
+ throw new IllegalArgumentException("Param '$code' type is boolean. value is 'true' or 'false'")
+ }
+ value = value instanceof Boolean ? value : Boolean.valueOf(value.toString())
+ }
+ fixValue = paramCfg.getBoolean('fixValue')
+ }
+ if (FieldType.Int == type) { // 整数验证
+ if (value == null) value = paramCfg.getLong('defaultValue')
+ if (value != null) {
+ try {
+ value = value instanceof Long ? value : Long.valueOf(value)
+ } catch (ex) {
+ throw new IllegalArgumentException("Param '$code' is not a number. value: $value", ex)
+ }
+ def min = paramCfg.getLong('min')
+ if (min != null && value < min) {
+ throw new IllegalArgumentException("Param '$code' must >= $min")
+ }
+ def max = paramCfg.getLong('max')
+ if (max != null && value > max) {
+ throw new IllegalArgumentException("Param '$code' must <= $max")
+ }
+ }
+ fixValue = paramCfg.getLong('fixValue')
+ enumValues = paramCfg.getJSONArray('enumValues')?.findAll{it}
+ }
+ if (FieldType.Decimal == type) { // 小数验证
+ if (value == null) value = paramCfg.getBigDecimal('defaultValue')
+ if (value != null) {
+ try {
+ value = value instanceof BigDecimal ? value : new BigDecimal(value.toString())
+ } catch (ex) {
+ throw new IllegalArgumentException("Param '$code' is not a decimal. value: $value", ex)
+ }
+ def min = paramCfg.getBigDecimal('min')
+ if (min != null && value < min) {
+ throw new IllegalArgumentException("Param '$code' must >= $min")
+ }
+ def max = paramCfg.getBigDecimal('max')
+ if (max != null && value > max) {
+ throw new IllegalArgumentException("Param '$code' must <= $max")
+ }
+ }
+ fixValue = paramCfg.getBigDecimal('fixValue')
+ enumValues = paramCfg.getJSONArray('enumValues')?.findAll{it}
+ }
+ if (FieldType.Str == type) { // 字符串验证
+ if (value == null) value = paramCfg.getString('defaultValue')
+ if (value != null) {
+ def maxLength = paramCfg.getInteger('maxLength')
+ if (maxLength != null && value.toString().length() > maxLength) {
+ throw new IllegalArgumentException("Param '$code' length must < $maxLength")
+ }
+ def fixLength = paramCfg.getInteger('fixLength')
+ if (fixLength != null && value.toString().length() != fixLength) {
+ throw new IllegalArgumentException("Param '$code' length must equal $fixLength")
+ }
+ }
+ fixValue = paramCfg.getString('fixValue')
+ enumValues = paramCfg.getJSONArray('enumValues')?.findAll{it}
+ if (paramCfg.getString('regex')) {
+ pattern = patternMap.get(code)
+ if (pattern == null) {
+ pattern = Pattern.compile(paramCfg.getString('regex'))
+ patternMap.put(code, pattern)
+ }
+ }
+ if (paramCfg.getString('validFun')) {
+ validFun = validFunMap.get(code)
+ if (validFun == null) {
+ Binding binding = new Binding()
+ def config = new CompilerConfiguration()
+ def icz = new ImportCustomizer()
+ config.addCompilationCustomizers(icz)
+ icz.addImports(Utils.class.name)
+ validFun = new GroovyShell(Thread.currentThread().contextClassLoader, binding, config).evaluate("${paramCfg.getString('validFun')}") as Function
+ validFunMap.put(code, validFun)
+ }
+ }
+ }
+ if (FieldType.Time == type) { // 时间验证
+ if (value == null) value = paramCfg.getString('defaultValue')
+ if (value != null) {
+ String format = paramCfg.getString('format')
+ if (!format) throw new IllegalArgumentException("Param '$code' type is Time, format must config")
+ try {
+ // if (value.toString().length() != format.length()) throw new Exception()
+ new SimpleDateFormat(format).parse(value.toString())
+ } catch (ex) {
+ throw new IllegalArgumentException("Param '$code' format error. format: $format, value: $value")
+ }
+ }
+// def min = paramCfg.getDate('min')
+// if (min != null && value < min) {
+// throw new IllegalArgumentException("Param '$code' must >= ${paramCfg.getString('min')}")
+// }
+// def max = paramCfg.getDate('max')
+// if (max != null && value > max) {
+// throw new IllegalArgumentException("Param '$code' must <= ${paramCfg.getString('max')}")
+// }
+// fixValue = paramCfg.getDate('fixValue')
+// enumValues = paramCfg.getJSONArray('enumValues')?.collect {JSONObject jo -> jo?.getDate('value')}?.findAll{it}
+ }
+
+ if (value) {
+ if (fixValue != null) { // 固定值验证
+ if (value != fixValue) {
+ throw new IllegalArgumentException("Param '$code' value fixed be '$fixValue'")
+ }
+ }
+ if (enumValues) { // 枚举值验证
+ if (!enumValues.find {it == value}) {
+ throw new IllegalArgumentException("Param '$code' enum values: '${enumValues.join(",")}'")
+ }
+ }
+ if (pattern) { // 正则验证
+ if (!pattern.matcher(value.toString()).find()) {
+ throw new IllegalArgumentException("Param '$code' regex not match: '$value'")
+ }
+ }
+ if (validFun) {
+ if (!validFun.apply(value)) {
+ throw new IllegalArgumentException("Param '$code' function valid fail: '$value'")
+ }
+ }
+ }
+
+ if (value != null || params.containsKey(code)) params.put(code, value) // 类型矫正
+ }
+ true
+ } : null)))
+ log.info("初始化决策(${decision.id}): ${decision.name}, ${decision.decisionId}".toString())
+ }
+}
diff --git a/src/service/rule/DecisionSrv.groovy b/src/service/rule/DecisionSrv.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..2787d27459009180cf272aa8ee786091dbaf91ba
--- /dev/null
+++ b/src/service/rule/DecisionSrv.groovy
@@ -0,0 +1,252 @@
+package service.rule
+
+import cn.xnatural.app.Inject
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.enet.event.EL
+import cn.xnatural.jpa.Repo
+import com.alibaba.fastjson.JSON
+import com.alibaba.fastjson.serializer.SerializerFeature
+import core.OkHttpSrv
+import entity.CollectRecord
+import entity.DecideRecord
+import entity.Lock
+
+import java.text.SimpleDateFormat
+import java.time.Duration
+
+/**
+ * 决策Service
+ */
+class DecisionSrv extends ServerTpl {
+ protected static final String SAVE_RESULT = 'save_result'
+
+ @Inject
+ protected OkHttpSrv http
+ @Inject(name = 'jpa_rule_repo')
+ protected Repo repo
+ @Inject
+ protected DecisionManager decisionManager
+ @Inject
+ protected FieldManager fieldManager
+ // 是否异步保存决策结果
+ @Lazy
+ protected asyncSave = getBoolean("asyncSave", false)
+
+
+ @EL(name = 'sys.starting', async = true)
+ protected void init() {
+ // 配置 异步保存决定结果 对列执行器
+ if (asyncSave) {
+ Long lastWarn // 上次告警时间
+ queue(SAVE_RESULT)
+ .failMaxKeep(getInteger(SAVE_RESULT + ".failMaxKeep", 10000))
+ .parallel(getInteger("saveResult.parallel", 5))
+ .errorHandle {ex, me ->
+ if (lastWarn == null || (System.currentTimeMillis() - lastWarn >= Duration.ofSeconds(getLong(SAVE_RESULT + ".warnInterval", 60 * 3L)).toMillis())) {
+ lastWarn = System.currentTimeMillis()
+ log.error("保存决策结果到数据库错误", ex)
+ ep.fire("globalMsg", "保存决策结果到数据库错误: " + (ex.message?:ex.class.simpleName))
+ }
+ // 暂停一会
+ me.suspend(Duration.ofMillis(500 + new Random().nextInt(1000)))
+ }
+ }
+ }
+
+
+ @EL(name = 'sys.stopping', async = true)
+ protected void stop() {
+ // 尽量等到 对列中的 数据都持久化完成
+ long start = System.currentTimeMillis()
+ if (asyncSave) {
+ if (queue(SAVE_RESULT).waitingCount > 0) log.warn("等待决策结果数据保存完...")
+ while (queue(SAVE_RESULT).waitingCount > 0 && System.currentTimeMillis() - start < 1000 * 60 * 2) {
+ Thread.sleep(500)
+ }
+ }
+ }
+
+
+ /**
+ * 执行一条决策
+ * @param decisionId 决策id {@link entity.Decision#decisionId}
+ * @param decideId 决策执行id, 用于记录是哪次执行
+ * @param isAsync 是否异步执行, 不等执行结果
+ * @param input 输入
+ * @return DecisionContext {@link DecisionContext}
+ */
+ DecisionContext decide(String decisionId, String decideId, boolean isAsync, Map input) {
+ if (!decisionId) {
+ throw new IllegalArgumentException('Param decisionId required')
+ }
+ def decisionHolder = decisionManager.findDecision(decisionId)
+ if (decisionHolder == null) {
+ throw new IllegalArgumentException("Not found decision: $decisionId")
+ }
+ decisionHolder.paramValidator?.apply(input) // 参数验证
+ decideId = decideId?:UUID.randomUUID().toString().replace("-", "")
+
+ log.info("Run decision. decisionId: " + decisionId + ", id: " + decideId + ", input: " + input)
+ DecisionContext dCtx = new DecisionContext(decideId, decisionHolder, input, fieldManager, ep)
+
+ // 初始化status: 0001, 结束status: 0000, 错误status: EEEE
+ repo.saveOrUpdate(new DecideRecord(
+ id: dCtx.id, decisionId: decisionHolder.decision.id, occurTime: dCtx.startup, status: '0001',
+ input: JSON.toJSONString(dCtx.input, SerializerFeature.WriteMapNullValue)
+ ))
+
+ if (isAsync) async { dCtx.start() }
+ else dCtx.start()
+ return dCtx
+ }
+
+
+
+ /**
+ * 系统全局消息
+ * @param msg
+ */
+ @EL(name = 'globalMsg', async = true)
+ void globalMsg(String msg) {
+ log.info("系统消息: " + msg)
+ ep.fire("wsMsg_rule", msg)
+ String url = getStr('ddMsgNotifyUrl', null)
+ if (url) {
+ http.post(url).jsonBody(JSON.toJSONString([
+ msgtype: "text",
+ text: ["content": "GRULE(${app().profile}): $msg".toString()],
+ at: ["isAtAll": false]
+ ])).debug().execute()
+ }
+ }
+
+
+ // 决策执行结果监听
+ @EL(name = 'decision.end')
+ void endDecision(DecisionContext ctx) {
+ log.info("end decision: " + JSON.toJSONString(ctx.summary(), SerializerFeature.WriteMapNullValue))
+
+ // 保存决策结果到数据库
+ final Runnable doSave = () -> {
+ repo.saveOrUpdate(
+ repo.findById(DecideRecord, ctx.id).tap {
+ status = ctx.status
+ exception = ctx.summary()['exception']
+ result = ctx.summary()['result']
+ spend = ctx.summary()['spend']
+ data = JSON.toJSONString(ctx.summary()['data'], SerializerFeature.WriteMapNullValue)
+ detail = JSON.toJSONString(ctx.summary()['detail'], SerializerFeature.WriteMapNullValue)
+ def dr = ctx.summary()['dataCollectResult']
+ if (dr) dataCollectResult = JSON.toJSONString(dr, SerializerFeature.WriteMapNullValue)
+ }
+ )
+ }
+ if (asyncSave) queue(SAVE_RESULT, doSave)
+ else doSave.run()
+
+ // 调用如果传了 callback(回调Url), 则会在决策执行完成后把结果回给此url
+ String cbUrl = ctx.input['callback']
+ if (cbUrl?.startsWith('http')) {
+ async {
+ def result = JSON.toJSONString(ctx.result(), SerializerFeature.WriteMapNullValue)
+ for (i in 0..< getInteger("callbackMaxTry", 2)) {
+ try {
+ http.post(cbUrl).jsonBody(result).debug().execute()
+ break
+ } catch (ex) {
+ log.error("call back result error", ex)
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * 计划清理过期DecideRecord数据
+ */
+ void cleanDecideRecord() {
+ def lock = new Lock(name: 'cleanDecideRecord', comment: "清理过期数据")
+ // 执行清理逻辑
+ def doClean = {
+ long cleanTotal = 0
+
+ // DecideRecord 清理函数
+ def clean = {DecideRecord dr ->
+ int count = repo.trans { session ->
+ // 删除关联的 收集器记录
+ session.createQuery("delete from CollectRecord where decideId=:decideId")
+ .setParameter("decideId", dr.id).executeUpdate()
+ session.createQuery("delete from DecideRecord where id=:id")
+ .setParameter("id", dr.id).executeUpdate()
+ }
+ cleanTotal += count
+ // 持续更新锁时间
+ if (cleanTotal % 20 == 0) repo.saveOrUpdate(lock)
+ log.info("Deleted expire decideRecord data: {}", JSON.toJSONString(dr))
+ }
+
+ //保留多少条数据
+ def keepCount = getLong("decideRecord.keepCount", 0)
+ if (keepCount > 0) {
+ for (long total = repo.count(DecideRecord); total > keepCount; total--) {
+ clean(repo.find(DecideRecord) { root, query, cb -> query.orderBy(cb.asc(root.get("occurTime")))})
+ // 为了防止有多个进程在删, 所以每隔10次 重新去统计总, 减少删除误差
+ if (cleanTotal % getInteger("deleteUnit", 10) == 0) {
+ total = repo.count(DecideRecord)
+ }
+ }
+ }
+
+ //保留多天的数据,如果 配置了decideRecord.keepCount 则不执行此清理
+ def keepDay = getInteger("decideRecord.keepDay", 0)
+ if (keepDay > 0 && !keepCount) {
+ def cal = Calendar.getInstance()
+ cal.add(Calendar.DAY_OF_MONTH, -keepDay)
+ do {
+ def dr = repo.find(DecideRecord) { root, query, cb -> query.orderBy(cb.asc(root.get("occurTime")))}
+ if (dr.occurTime == null || dr.occurTime < cal.time) clean(dr)
+ else break
+ } while (true)
+ }
+ return cleanTotal
+ }
+ try {
+ repo.saveOrUpdate(lock)
+ async {
+ try {
+ def total = doClean()
+ ep.fire("globalMsg", "清理过期决策数据结束. 共计: " + total)
+ } finally {
+ repo.delete(lock)
+ }
+ if (getBoolean("optimizeTable", false)) {
+ repo.execute("optimize table " + repo.tbName(DecideRecord))
+ repo.execute("optimize table " + repo.tbName(CollectRecord))
+ }
+ }
+ } catch (ex) {
+ def cause = ex
+ while (cause != null) {
+ if (cause.message.contains("Duplicate entry")) {
+ def exist = repo.find(Lock) {root, query, cb -> cb.equal(root.get("name"), lock.name)}
+ if (exist) {
+ // 当锁隔一段时间没有被更新 就会删除锁, 防止系统意外down,而锁未来的及删的情况发生
+ if (System.currentTimeMillis() - exist.updateTime.time > Duration.ofMinutes(getLong("lockTimeout", 30)).toMillis()) {
+ repo.delete(exist)
+ cleanDecideRecord() // 删除锁后重试
+ return
+ } else {
+ throw new RuntimeException("清理中... 开始时间: " + new SimpleDateFormat('yyyy-MM-dd HH:mm:ss').format(exist.createTime))
+ }
+ } else {
+ throw new RuntimeException("刚清理完")
+ }
+ }
+ cause = cause.cause
+ }
+ repo.delete(lock)
+ throw ex
+ }
+ }
+}
diff --git a/src/service/rule/FieldManager.groovy b/src/service/rule/FieldManager.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..35c096bcc29ea8bf543e450e00eb05fec48cdf3b
--- /dev/null
+++ b/src/service/rule/FieldManager.groovy
@@ -0,0 +1,222 @@
+package service.rule
+
+
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.app.Utils
+import cn.xnatural.enet.event.EC
+import cn.xnatural.enet.event.EL
+import cn.xnatural.jpa.Repo
+import cn.xnatural.remoter.Remoter
+import com.alibaba.fastjson.JSON
+import com.alibaba.fastjson.JSONObject
+import entity.DataCollector
+import entity.FieldType
+import entity.RuleField
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.control.customizers.ImportCustomizer
+import org.slf4j.LoggerFactory
+import service.rule.spec.BaseSpec
+
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.atomic.AtomicInteger
+
+/**
+ * 属性管理
+ * 属性别名
+ * 属性值函数
+ */
+class FieldManager extends ServerTpl {
+ @Lazy protected repo = bean(Repo, 'jpa_rule_repo')
+ @Lazy protected collectorManager = bean(CollectorManager)
+ /**
+ * RuleField(enName, cnName), RuleField
+ */
+ final Map fieldHolders = new ConcurrentHashMap<>(500)
+
+
+
+ /**
+ * 执行数据收集, 获取属性值
+ * @param aName 属性名
+ * @param ctx 决策执行上下文
+ * @return 当前属性的值
+ */
+ def dataCollect(String aName, DecisionContext ctx) {
+ def fieldHolder = fieldHolders.get(aName)
+ if (fieldHolder == null) {
+ log.warn(ctx.logPrefix() + "未找到属性'$aName'对应的配置".toString())
+ return null
+ }
+ String collectorId = fieldHolder.choose(ctx) // 属性对应的 值 收集器名
+ if (!collectorId) {
+ log.warn(ctx.logPrefix() + "属性'" + aName + "'没有对应的取值配置")
+ return null
+ }
+ collectorManager.populate(collectorId, aName, ctx)
+ }
+
+
+ /**
+ * 得到属性对应的别名
+ * @param aName 属性名
+ * @return null: 没有别名
+ */
+ String alias(String aName) {
+ def field = fieldHolders.get(aName)?.field
+ if (field == null) return null
+ else if (field.cnName == aName) return field.enName
+ else if (field.enName == aName) return field.cnName
+ null
+ }
+
+
+ /**
+ * 属性值类型转换
+ * @param aName 属性名
+ * @param aValue 属性值
+ * @return 转换后的值
+ */
+ Object convert(String aName, Object aValue) {
+ if (aValue == null) return aValue
+ def field = fieldHolders.get(aName)?.field
+ if (field == null) return aValue
+ Utils.to(aValue, field.type.clzType)
+ }
+
+
+ // ======================= 监听变化 ==========================
+ @EL(name = ['fieldChange', 'field.dataVersion'], async = true)
+ void listenFieldChange(EC ec, Long id) {
+ def field = repo.findById(RuleField, id)
+ if (field == null) {
+ for (def itt = fieldHolders.iterator(); itt.hasNext(); ) {
+ def e = itt.next()
+ if (e.value.field.id == id) itt.remove()
+ }
+ log.info("delField: " + id)
+ } else {
+ initField(field)
+ log.info("fieldChanged: ${field.cnName}, $id".toString())
+ }
+ def remoter = bean(Remoter)
+ if (remoter && ec?.source() != remoter) { // 不是远程触发的事件
+ remoter.dataVersion('field').update(id.toString(), field ? field.updateTime.time : System.currentTimeMillis(), null)
+ }
+ }
+
+
+ /**
+ * 加载所有指标
+ */
+ @EL(name = 'jpa_rule.started', async = true, order = 1f)
+ void load() {
+ initDefaultField()
+ final Set names = (fieldHolders ? ConcurrentHashMap.newKeySet(fieldHolders.size() / 2) : null)
+ final def threshold = new AtomicInteger(1)
+ final def tryComplete = {
+ if (threshold.decrementAndGet() > 0) return
+ if (names) { // 重新加载, 要删除内存中有, 但库中没有
+ fieldHolders.findAll {!names.contains(it.key)}.each { e ->
+ fieldHolders.remove(e.key)
+ }
+ }
+ log.info("加载字段属性 {}个", fieldHolders.size() / 2)
+ }
+ for (int page = 0, limit = 50; ; page++) {
+ def ls = repo.findList(RuleField, page * limit, limit)
+ threshold.incrementAndGet()
+ async {
+ ls.each {field ->
+ initField(field)
+ names?.add(field.enName)
+ names?.add(field.cnName)
+ }
+ tryComplete()
+ }
+ if (!ls || ls.size() < limit) {
+ tryComplete(); break
+ }
+ }
+ }
+
+
+ /**
+ * 初始化默认属性集
+ */
+ protected void initDefaultField() {
+ if (repo.count(RuleField) == 0) {
+ log.info("初始化默认属性集")
+ repo.saveOrUpdate(new RuleField(enName: 'idNumber', cnName: '身份证号码', type: FieldType.Str))
+ repo.saveOrUpdate(new RuleField(enName: 'name', cnName: '姓名', type: FieldType.Str))
+ repo.saveOrUpdate(new RuleField(enName: 'mobileNo', cnName: '手机号码', type: FieldType.Str))
+ repo.saveOrUpdate(new RuleField(enName: 'age', cnName: '年龄', type: FieldType.Int, comment: '根据身份整计算',
+ collectorOptions: JSON.toJSONString([[
+ collectorId: repo.find(DataCollector) {root, query, cb -> cb.equal(root.get("name"), "年龄")}?.id,
+ chooseFn: 'true'
+ ]])))
+ repo.saveOrUpdate(new RuleField(enName: 'gender', cnName: '性别', type: FieldType.Str, comment: '值: F(女),M(男)',
+ collectorOptions: JSON.toJSONString([[
+ collectorId: repo.find(DataCollector) {root, query, cb -> cb.equal(root.get("name"), "性别")}?.id,
+ chooseFn: 'true'
+ ]])))
+ repo.saveOrUpdate(new RuleField(enName: 'week', cnName: '星期几', type: FieldType.Int, comment: '值: 1,2,3,4,5,6,7',
+ collectorOptions: JSON.toJSONString([[
+ collectorId: repo.find(DataCollector) {root, query, cb -> cb.equal(root.get("name"), "星期几")}?.id,
+ chooseFn: 'true'
+ ]])))
+ repo.saveOrUpdate(new RuleField(enName: 'currentDateTime', cnName: '当前日期时间', type: FieldType.Str, comment: '值: yyyy-MM-dd HH:mm:ss',
+ collectorOptions: JSON.toJSONString([[
+ collectorId: repo.find(DataCollector) {root, query, cb -> cb.equal(root.get("name"), "当前日期时间")}?.id,
+ chooseFn: 'true'
+ ]])))
+ repo.saveOrUpdate(new RuleField(enName: 'currentDate', cnName: '当前日期', type: FieldType.Str, comment: '值: yyyy-MM-dd',
+ collectorOptions: JSON.toJSONString([[
+ collectorId: repo.find(DataCollector) {root, query, cb -> cb.equal(root.get("name"), "当前日期")}?.id,
+ chooseFn: 'true'
+ ]])))
+ repo.saveOrUpdate(new RuleField(enName: 'currentTime', cnName: '当前时间', type: FieldType.Str, comment: '值: HH:mm:ss',
+ collectorOptions: JSON.toJSONString([[
+ collectorId: repo.find(DataCollector) {root, query, cb -> cb.equal(root.get("name"), "当前时间")}?.id,
+ chooseFn: 'true'
+ ]])))
+ }
+ }
+
+
+ /**
+ * 初始化 {@link RuleField}
+ */
+ protected void initField(RuleField field) {
+ Binding binding = new Binding()
+ def config = new CompilerConfiguration()
+ binding.setProperty('LOG', LoggerFactory.getLogger("ROOT"))
+ def icz = new ImportCustomizer()
+ config.addCompilationCustomizers(icz)
+ icz.addImports(JSON.class.name, JSONObject.class.name, Utils.class.name)
+
+ def holder = new FieldHolder(field: field, collectorChooseMap: field.collectorOptions ? JSON.parseArray(field.collectorOptions).collectEntries {JSONObject jo ->
+ [jo['collectorId'], new GroovyShell(Thread.currentThread().contextClassLoader, binding, config).evaluate("{ -> $jo.chooseFn }")]
+ } : null)
+ fieldHolders.put(field.enName, holder)
+ fieldHolders.put(field.cnName, holder)
+ }
+
+
+ /**
+ * 字段/属性/指标 Holder
+ */
+ class FieldHolder {
+ RuleField field
+ LinkedHashMap collectorChooseMap
+
+ /**
+ * 根据当前执行上下文选择出 属性的收集器
+ * @param ctx 当前执行上下文
+ * @return 收集器id
+ */
+ String choose(DecisionContext ctx) {
+ if (collectorChooseMap == null) return null
+ collectorChooseMap.find {e -> BaseSpec.rehydrate2(e.value, ctx.data, this)()}?.key
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/service/rule/OpHistorySrv.groovy b/src/service/rule/OpHistorySrv.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..76c5175a9ed036e8f9134d43189411385535efbd
--- /dev/null
+++ b/src/service/rule/OpHistorySrv.groovy
@@ -0,0 +1,36 @@
+package service.rule
+
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.app.Utils
+import cn.xnatural.enet.event.EL
+import cn.xnatural.jpa.IEntity
+import cn.xnatural.jpa.Repo
+import com.alibaba.fastjson.JSON
+import com.alibaba.fastjson.serializer.SerializerFeature
+import entity.OpHistory
+
+import java.text.SimpleDateFormat
+
+/**
+ * 记录实体的修改历史
+ */
+class OpHistorySrv extends ServerTpl {
+
+ @Lazy def repo = bean(Repo, 'jpa_rule_repo')
+
+
+ /**
+ * 操作历史
+ * @param entity 实体
+ */
+ @EL(name = 'enHistory', async = true)
+ void enHistory(IEntity entity, String operator) {
+ def data = Utils.copier(entity, [:]).ignore("metaClass")
+ .addConverter('createTime', {v -> if (v instanceof Date) new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(v) else v})
+ .addConverter('updateTime', {v -> if (v instanceof Date) new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(v) else v})
+ .build()
+ repo.saveOrUpdate(
+ new OpHistory(tbName: repo.tbName(entity.class).replace("`", ""), operator: operator, content: JSON.toJSONString(data, SerializerFeature.WriteMapNullValue, SerializerFeature.DisableCircularReferenceDetect))
+ )
+ }
+}
diff --git a/src/service/rule/UserSrv.groovy b/src/service/rule/UserSrv.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..72e189342c0ab327641d2baf7a3459f0dcab7a84
--- /dev/null
+++ b/src/service/rule/UserSrv.groovy
@@ -0,0 +1,162 @@
+package service.rule
+
+import cn.xnatural.app.CacheSrv
+import cn.xnatural.app.ServerTpl
+import cn.xnatural.enet.event.EL
+import cn.xnatural.jpa.Repo
+import entity.Decision
+import entity.Permission
+import entity.User
+
+class UserSrv extends ServerTpl {
+
+ @Lazy def repo = bean(Repo, 'jpa_rule_repo')
+ /**
+ * 默认静态权限
+ */
+ @Lazy def staticPermission = [
+ new Permission(enName: 'grant', cnName: '权限管理'),
+ new Permission(enName: 'grant-user', cnName: '用户管理'),
+ new Permission(enName: 'mnt-login', cnName: '用户登陆'),
+ new Permission(enName: 'user-add', cnName: '新增用户'),
+ new Permission(enName: 'user-del', cnName: '删除用户'),
+ // new Permission(enName: 'password-reset', cnName: '密码重置'),
+ new Permission(enName: 'decision-add', cnName: '决策创建'),
+ new Permission(enName: 'field-read', cnName: '查看字段'),
+ new Permission(enName: 'field-add', cnName: '新增字段'),
+ new Permission(enName: 'field-update', cnName: '更新字段'),
+ new Permission(enName: 'field-del', cnName: '删除字段'),
+ new Permission(enName: 'dataCollector-read', cnName: '查看收集器'),
+ new Permission(enName: 'dataCollector-add', cnName: '新增收集器'),
+ new Permission(enName: 'dataCollector-update', cnName: '更新收集器'),
+ new Permission(enName: 'dataCollector-del', cnName: '删除收集器'),
+ new Permission(enName: 'opHistory-read', cnName: '查看操作历史'),
+ new Permission(enName: 'decideResult-read', cnName: '查看决策结果'),
+ new Permission(enName: 'collectResult-read', cnName: '查看收集记录')
+ ]
+
+
+ // 一个决策对应的所有权限
+ protected List getPermissions(Decision decision) {
+ [
+ new Permission(enName: "decision-update-" + decision.id, cnName: "更新决策:" + decision.name, mark: decision.id, comment: "动态权限-决策"),
+ new Permission(enName: "decision-del-" + decision.id, cnName: "删除决策:" + decision.name, mark: decision.id, comment: "动态权限-决策"),
+ new Permission(enName: "decision-read-" + decision.id, cnName: "查看决策:" + decision.name, mark: decision.id, comment: "动态权限-决策")
+ ]
+ }
+
+
+ /**
+ * 初始化 权限数据
+ */
+ @EL(name = "jpa_rule.started", async = true)
+ protected void initUserPermission() {
+ staticPermission.each { p ->
+ // ConstraintViolationException
+ if (!repo.count(Permission) { root, query, cb -> cb.equal(root.get("enName"), p.enName) }) {
+ repo.saveOrUpdate(p)
+ log.info("添加默认静态权限: " + p.enName + ", " + p.cnName)
+ }
+ }
+
+ // 初始化默认用户
+ if (!repo.count(User)) {
+ [
+ new User(name: 'admin', password: 'admin', group: 'admin')
+ ].each {u ->
+ u.permissions = staticPermission.collect {it.enName}.join(",")
+ def p = u.password
+ u.password = p.md5()
+ repo.saveOrUpdate(u)
+ log.info("添加默认用户. username: " + u.name + ", password: " + p)
+ }
+ }
+ }
+
+
+ /**
+ * 添加历史未添加的权限
+ */
+ @EL(name = "jpa_rule.started", async = true)
+ protected void fixDecisionPermission() {
+ for (int i = 0, limit = 10; ; i++) {
+ def ls = repo.findList(Decision, i*limit, limit)
+ ls.each { decision ->
+ getPermissions(decision).each {p ->
+ if (!repo.count(Permission) {root, query, cb -> cb.equal(root.get("enName"), p.enName)}) { // 决策权限不存在,则创建
+ repo.saveOrUpdate(p)
+ log.info("添加决策权限'$p.cnName($decision.id)'".toString())
+ }
+ }
+ }
+ if (ls.size() < limit) break
+ }
+ }
+
+
+ // 决策创建完成 更新用户权限
+ @EL(name = "decision.create", async = true)
+ protected void decisionCreate(String id) {
+ def decision = repo.findById(Decision, id)
+ def ps = getPermissions(decision)
+
+ // 创建新权限
+ ps.each {repo.saveOrUpdate(it)}
+
+ if (!decision.creator) return
+
+ // 添加创建者的权限
+ def user = repo.find(User) {root, query, cb -> cb.equal(root.get("name"), decision.creator)}
+ LinkedHashSet alreadyPs = new LinkedHashSet(user.permissions?.split(",")?.toList()?:Collections.emptyList())
+ if (!alreadyPs.containsAll(ps.findResults {it.enName})) {
+ ps.each {alreadyPs.add(it.enName)}
+ user.permissions = alreadyPs.join(",")
+ repo.saveOrUpdate(user)
+ bean(CacheSrv).remove("permission_" + user.id)
+ }
+
+ // 如果创建者是普通用户, 则需要把所有权限给所在的组管理员
+ if (user.group) {
+ async {
+ def gUser = repo.find(User) {root, query, cb -> cb.and(cb.equal(root.get("group"), user.group), cb.like(root.get("permissions"), "%grant-user%"))}
+ if (!gUser) return
+ LinkedHashSet ls = new LinkedHashSet(gUser.permissions?.split(",")?.toList()?:Collections.emptyList())
+ ps.each {ls.add(it.enName)}
+ gUser.permissions = ls.join(",")
+ repo.saveOrUpdate(gUser)
+ bean(CacheSrv)?.remove("permission_" + gUser.id)
+ }
+ }
+ }
+
+ // 更新决策关联的权限名
+ @EL(name = "decision.update", async = true)
+ protected void decisionUpdate(String id) {
+ def decision = repo.findById(Decision, id)
+ def ps = getPermissions(decision)
+ repo.findList(Permission) { root, query, cb -> cb.equal(root.get("mark"), id)}
+ ?.each {p ->
+ p.cnName = ps.find {p.enName == it.enName}.cnName
+ repo.saveOrUpdate(p)
+ }
+ }
+
+ @EL(name = "decision.delete", async = true)
+ protected void decisionDelete(String id) {
+ def ps = repo.findList(Permission) { root, query, cb -> cb.equal(root.get("mark"), id)}
+ .each {repo.delete(it)}
+ // 删除用户关联的权限
+ for (int start = 0, limit = 10;; start++) {
+ def users = repo.findList(User, start * limit, limit) {root, query, cb ->
+ ps.findResults {cb.like(root.get("permissions"), "%" + it.enName + "%")}
+ .inject {p1, p2 -> cb.or(p1, p2)}
+ }
+ users.each {u ->
+ u.permissions = u.permissions.split(",").findAll {!ps.contains(it)}.join(",")
+ repo.saveOrUpdate(u)
+ bean(CacheSrv)?.remove("permission_" + u.id)
+ }
+ if (users.size() < limit) break
+ }
+ }
+}
diff --git a/src/service/rule/spec/BaseSpec.groovy b/src/service/rule/spec/BaseSpec.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..784daf1b22e7e35a9dad7f3baaa302fe83d32ae9
--- /dev/null
+++ b/src/service/rule/spec/BaseSpec.groovy
@@ -0,0 +1,71 @@
+package service.rule.spec
+
+
+abstract class BaseSpec {
+ /**
+ * 自定义规则属性. 例: 自定义id, 描述
+ */
+ @Lazy Map attrs = new HashMap<>()
+ /**
+ * 顺序节点: 拒绝, 通过, 人工, 操作, 清除
+ * 依次遍历节点执行
+ */
+ @Lazy List> nodes = new LinkedList<>()
+
+ /**
+ * 名字
+ */
+ abstract String name()
+
+
+ /**
+ * 创建 关联执行闭包(即: 闭包的执行过程中数据存放使用传进来的执行上下文ctx)
+ * 读: 当前闭包, 当前执行上下文, 决策自定义函数
+ * 写: 当前执行上下文
+ * @param src 原闭包
+ * @param ctx 当前执行上下文
+ * @return 临时执行闭包
+ */
+ static Closure rehydrate1(Closure src, Map ctx, Object _this) {
+ final cl = src.rehydrate(new HashMap(1) {
+ @Override
+ Object put(Object key, Object value) { ctx.put(key, value); return value }
+
+ @Override
+ Object get(Object key) {
+ try {
+ return src.getProperty(key)
+ } catch (MissingPropertyException | MissingFieldException ex) {
+ return ctx.get(key)
+ }
+ }
+ }, src, _this)
+ // 不能用 DELEGATE_ONLY 不然 调不了决策中自定义函数
+ cl.setResolveStrategy(Closure.DELEGATE_FIRST)
+ return cl
+ }
+
+
+ /**
+ * 创建 独立执行闭包(即: 会为闭包单独创建一个数据存放空间)
+ * 读: 当前闭包, 独立执行空间(map), 当前执行上下文
+ * 写: 独立执行空间(map)
+ * @param src 原闭包
+ * @param ctx 当前执行上下文
+ * @return 临时执行闭包
+ */
+ static Closure rehydrate2(Closure src, Map ctx, Object _this) {
+ final cl = src.rehydrate(new LinkedHashMap(7) {
+ @Override
+ Object get(Object key) {
+ try {
+ return src.getProperty(key)
+ } catch (MissingPropertyException | MissingFieldException ex) {
+ return super.containsKey(key) ? super.get(key) : ctx.get(key)
+ }
+ }
+ }, src, _this)
+ cl.setResolveStrategy(Closure.DELEGATE_ONLY)
+ return cl
+ }
+}
diff --git a/src/service/rule/spec/DecisionSpec.groovy b/src/service/rule/spec/DecisionSpec.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..3605d94cc3236721030ac5ce035d5731251fa7e7
--- /dev/null
+++ b/src/service/rule/spec/DecisionSpec.groovy
@@ -0,0 +1,146 @@
+package service.rule.spec
+
+import cn.xnatural.app.Utils
+import com.alibaba.fastjson.JSON
+import com.alibaba.fastjson.JSONObject
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.control.customizers.ImportCustomizer
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import service.rule.DecideResult
+import service.rule.DecisionContext
+
+import java.lang.reflect.Array
+
+/**
+ * 决策 DSL
+ */
+class DecisionSpec extends BaseSpec {
+ String 决策id
+ String 决策名
+ String 决策描述
+
+ /**
+ * 接口返回属性集
+ */
+ @Lazy Set returnAttrs = new LinkedHashSet<>()
+ /**
+ * 全局决策内自定义函数
+ */
+ @Lazy Map globalFunctions = new HashMap<>()
+
+
+ DecisionSpec() {
+ Logger log = LoggerFactory.getLogger("ROOT")
+ globalFunctions.put("INFO", { String msg -> if (msg) log.info(msg.toString()) })
+ globalFunctions.put("ERROR", { String msg, Exception ex -> if (msg) log.error(msg.toString(), ex) })
+ }
+
+
+ void 操作(Closure 操作) {
+ nodes << Tuple2.tuple("Operate", { DecisionContext ctx ->
+ rehydrate1(操作, ctx.data, this)()
+ })
+ }
+
+
+ DecisionSpec 函数定义(String 函数名, Closure 函数) {
+ if (!函数名) throw new IllegalArgumentException("函数名 不能为空")
+ if (!函数) throw new IllegalArgumentException("函数 不能为空")
+ globalFunctions.put(函数名, 函数)
+ this
+ }
+
+
+ DecisionSpec 属性定义(String 属性名, Object 值) {
+ if (!属性名) throw new IllegalArgumentException("属性名 不能为空")
+ attrs.put(属性名, 值)
+ this
+ }
+
+
+ DecisionSpec 返回属性(String 属性名) { returnAttrs.add(属性名); this }
+
+
+ /**
+ * 根据 dsl 创建 策略, 并创建执行
+ */
+ void 策略(@DelegatesTo(PolicySpec) Closure cl) {
+ PolicySpec spec = new PolicySpec()
+ def code = cl.rehydrate(spec, cl, this)
+ code.resolveStrategy = Closure.DELEGATE_FIRST
+ code()
+ // 定义执行函数
+ nodes << Tuple.tuple("Policy", {DecisionContext ctx -> ctx.run(spec)})
+ }
+
+
+ /**
+ * 执行决策
+ * @param ctx {@link DecisionContext}
+ * @return {@link DecideResult}
+ */
+ DecideResult compute(DecisionContext ctx) {
+ for (def node : nodes) {
+ if ("Policy" == node.v1) {
+ DecideResult result = node.v2.call(ctx)
+ if (result && result.block) return result
+ } else if ("Operate" == node.v1) {
+ DecisionContext.log.trace(ctx.logPrefix() + "[操作]开始执行")
+ node.v2.call(ctx)
+ } else throw new IllegalArgumentException("Unknown type: " + node.v1)
+ }
+ DecideResult.Accept
+ }
+
+
+ @Override
+ String name() { return 决策名 }
+
+
+ /**
+ * 函数拦截 调用
+ * @param name 函数名
+ * @param args 参数
+ */
+ def methodMissing(String name, def args) {
+ def fn = globalFunctions.get(name)
+ if (fn) {
+ int length = Array.getLength(args)
+ if (length == 0) return fn()
+ else if (length == 1) return fn(Array.get(args, 0))
+ else if (length == 2) return fn(Array.get(args, 0), Array.get(args, 1))
+ else if (length == 3) return fn(Array.get(args, 0), Array.get(args, 1), Array.get(args, 2))
+ else if (length == 4) return fn(Array.get(args, 0), Array.get(args, 1), Array.get(args, 2), Array.get(args, 3))
+ else if (length == 5) return fn(Array.get(args, 0), Array.get(args, 1), Array.get(args, 2), Array.get(args, 3), Array.get(args, 4))
+ else if (length == 6) return fn(Array.get(args, 0), Array.get(args, 1), Array.get(args, 2), Array.get(args, 3), Array.get(args, 4), Array.get(args, 5))
+ else throw new Exception("Custom function '$name' args length: " + length)
+ }
+ else throw new MissingMethodException(name, DecisionSpec, args)
+ }
+
+
+ static DecisionSpec of(@DelegatesTo(DecisionSpec) Closure cl) {
+ def p = new DecisionSpec()
+ def code = cl.rehydrate(p, cl, this)
+ code.resolveStrategy = Closure.DELEGATE_FIRST
+ code()
+ return p
+ }
+
+
+ /**
+ * 根据dsl字符串创建 DecisionSpec 对象
+ * @param dsl dsl 字符串
+ * @return DecisionSpec
+ */
+ static DecisionSpec of(String dsl) {
+ Binding binding = new Binding()
+ def config = new CompilerConfiguration()
+ def icz = new ImportCustomizer()
+ config.addCompilationCustomizers(icz)
+ icz.addImports(DecisionSpec.class.name, JSON.class.name, JSONObject.class.name, Utils.class.name)
+ def shell = new GroovyShell(Thread.currentThread().contextClassLoader, binding, config)
+ shell.evaluate("service.rule.spec.DecisionSpec.of{$dsl}")
+ }
+}
diff --git a/src/service/rule/spec/DelegateDecisionSpec.groovy b/src/service/rule/spec/DelegateDecisionSpec.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..788b727c1c50839f8512fe565526f452d2fb94a9
--- /dev/null
+++ b/src/service/rule/spec/DelegateDecisionSpec.groovy
@@ -0,0 +1,120 @@
+package service.rule.spec
+
+import service.rule.DecideResult
+import service.rule.DecisionContext
+import service.rule.DecisionSrv
+
+import java.util.concurrent.atomic.AtomicInteger
+
+/**
+ * 用于 策略中 {@link PolicySpec} 的子决策调用
+ */
+class DelegateDecisionSpec extends BaseSpec {
+ /**
+ * 要调用的 决策id
+ */
+ String 决策id
+ /**
+ * 是否异步执行
+ * 默认同步
+ */
+ boolean 异步
+
+
+ /**
+ * 要传给决策的参数
+ * 决策 {
+ * 决策id = 'test1'
+ *
+ * // 左(子DecisionContext) = 右(父DecisionContext)
+ * 传参 {
+ * p1 = 1
+ * p2 = p2
+ * p3 = 'aa'
+ * }
+ * }
+ * @param 参数函数
+ */
+ void 传参(Closure 参数函数) {
+ nodes << Tuple.tuple('Input', { DecisionContext ctx ->
+ def input = new LinkedHashMap() {
+ @Override
+ Object get(Object key) { ctx.data.get(key) }
+ }
+ def cl = 参数函数.rehydrate(input, 参数函数, this)
+ cl.resolveStrategy = Closure.DELEGATE_FIRST
+ cl()
+ return input
+ })
+ }
+
+
+ /**
+ * 把参数输出到当前决策执行的上下文
+ * 会覆盖子决策返回配置
+ * 决策 {
+ * 决策id = 'test1'
+ *
+ * // 左(父DecisionContext) = 右(子DecisionContext)
+ * 输出 {
+ * p1 = 1
+ * p2 = p2
+ * p3 = 'aa'
+ * }
+ * }
+ *
+ * 1. 没有输出配置默认会把子决策的返回属性当输出
+ * 2. 配置了就不会输出子决策中的返回属性了
+ * @param 输出函数
+ */
+ void 输出(Closure 输出函数) {
+ nodes << Tuple.tuple('Output', { DecisionContext ctx ->
+ def output = new LinkedHashMap() {
+ @Override
+ Object get(Object key) { ctx.data.get(key) }
+ }
+ def cl = 输出函数.rehydrate(output, 输出函数, this)
+ cl.resolveStrategy = Closure.DELEGATE_FIRST
+ cl()
+ return output
+ })
+ }
+
+
+ /**
+ * 计算子决策的结果
+ * @param ctx {@link DecisionContext}
+ * @return {@link DecideResult}
+ */
+ DecideResult compute(DecisionContext ctx) {
+ Map input = [:]
+ nodes.each { node ->
+ if ("Input" == node.v1) {
+ input.putAll(node.v2.call(ctx))
+ } else if ("Output" == node.v1) {
+
+ } else throw new IllegalArgumentException("Unknown type: " + node.v1)
+ }
+ input.put("decisionId", 决策id) // 在字段选择收集器 可能会用到
+ def times = ctx.delegateDecisionCount.computeIfAbsent(决策id, { new AtomicInteger()})
+ DecisionContext ctx2 = ctx.bean(DecisionSrv).decide(决策id, ctx.id + "_" + times.getAndIncrement(), 异步, input)
+ if (异步) return null
+ Map output
+ nodes.each {node ->
+ if ("Output" == node.v1) {
+ if (output == null) output = [:]
+ output.putAll(node.v2.call(ctx2))
+ }
+ }
+ if (output == null) {
+ ctx2.result()['data']?.each { e -> ctx.data.put(e.key, e.value)} //设置返回属性
+ } else {
+ output.each { e -> ctx.data.put(e.key, e.value)}
+ }
+ return ctx2.decideResult
+ }
+
+
+ @Override
+ String name() { 决策id }
+}
diff --git a/src/service/rule/spec/PolicySpec.groovy b/src/service/rule/spec/PolicySpec.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..1d1685086b69fbd8116f3d1ce52f62a115513cc7
--- /dev/null
+++ b/src/service/rule/spec/PolicySpec.groovy
@@ -0,0 +1,160 @@
+package service.rule.spec
+
+import cn.xnatural.app.Utils
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.control.customizers.ImportCustomizer
+import service.rule.DecideResult
+import service.rule.DecisionContext
+
+/**
+ * 策略定义 spec
+ */
+class PolicySpec extends BaseSpec {
+ String 策略名
+
+
+ void 操作(Closure 操作) {
+ nodes << Tuple.tuple('Operate', { DecisionContext ctx ->
+ rehydrate1(操作, ctx.data, this)()
+ })
+ }
+
+
+ /**
+ * 执行策略条件函数. 条件函数返回false, 则跳出, 继续执行下一个策略
+ * @param 条件
+ */
+ void 条件(Closure 条件) {
+ nodes << Tuple.tuple('Condition', { DecisionContext ctx ->
+ rehydrate1(条件, ctx.data, this)()
+ })
+ }
+
+
+ void 属性定义(String 属性名, Object 值) {
+ if (!属性名) throw new IllegalArgumentException("属性名 不能为空")
+ attrs.put(属性名, 值)
+ }
+
+
+ /**
+ * 清除属性值缓存
+ * @param 属性名
+ */
+ void 清除(String... 属性名) {
+ if (!属性名) return
+ nodes << Tuple.tuple('Clear', { DecisionContext ctx ->
+ 属性名.each {ctx.data.remove(it)}
+ })
+ }
+
+
+ /**
+ * 根据 dsl 创建规则, 并创建执行
+ */
+ void 规则(@DelegatesTo(RuleSpec) Closure cl) {
+ RuleSpec spec = new RuleSpec()
+ def code = cl.rehydrate(spec, cl, this)
+ code.resolveStrategy = Closure.DELEGATE_FIRST
+ code()
+
+ if (!spec.规则名) throw new IllegalArgumentException("规则名字不能为空")
+ if (!spec.nodes) throw new IllegalArgumentException("$spec.规则名 是空规则")
+
+ // 定义执行函数
+ nodes << Tuple.tuple("Rule", {DecisionContext ctx -> ctx.run(spec)})
+ }
+
+
+ /**
+ * 根据 dls 创建 评分卡, 并创建执行
+ */
+ void 评分卡(@DelegatesTo(ScorecardSpec) Closure cl) {
+ ScorecardSpec spec = new ScorecardSpec()
+ def code = cl.rehydrate(spec, cl, this)
+ code.resolveStrategy = Closure.DELEGATE_FIRST
+ code()
+
+ if (!spec.评分卡名) throw new IllegalArgumentException("评分卡名不能为空")
+ if (!spec.赋值变量) throw new IllegalArgumentException("评分卡赋值变量不能为空")
+ if (spec.模型 == null) throw new IllegalArgumentException("$spec.评分卡名 是空评分卡")
+
+ // 定义执行函数
+ nodes << Tuple.tuple("Scorecard", {DecisionContext ctx -> ctx.run(spec)})
+ }
+
+
+ /**
+ * 根据 dls 创建 子决策, 并创建执行
+ */
+ void 决策(@DelegatesTo(DelegateDecisionSpec) Closure cl) {
+ DelegateDecisionSpec spec = new DelegateDecisionSpec()
+ def code = cl.rehydrate(spec, cl, this)
+ code.resolveStrategy = Closure.DELEGATE_FIRST
+ code()
+
+ if (!spec.决策id) throw new IllegalArgumentException("子决策id不能为空")
+
+ // 定义执行函数
+ nodes << Tuple.tuple("Decision", {DecisionContext ctx -> ctx.run(spec)})
+ }
+
+
+ /**
+ * 计算策略结果
+ * @param ctx
+ * @return {@link DecideResult}
+ */
+ DecideResult compute(DecisionContext ctx) {
+ DecideResult result
+ for (def node : nodes) {
+ if ("Rule" == node.v1) {
+ result = node.v2.call(ctx)
+ if (result && result.block) break
+ } else if ("Decision" == node.v1) {
+ result = node.v2.call(ctx)
+ if (result && result.block) break
+ } else if ("Condition" == node.v1) {
+ DecisionContext.log.trace(ctx.logPrefix() + "[条件]开始执行")
+ if (!node.v2.call(ctx)) break
+ } else if ("Operate" == node.v1) {
+ DecisionContext.log.trace(ctx.logPrefix() + "[操作]开始执行")
+ node.v2.call(ctx)
+ } else if ("Scorecard" == node.v1) {
+ node.v2.call(ctx)
+ } else throw new IllegalArgumentException("Unknown type: " + node.v1)
+ }
+ result
+ }
+
+ @Override
+ String name() { return 策略名 }
+
+
+ static PolicySpec of(@DelegatesTo(PolicySpec) Closure cl) {
+ def spec = new PolicySpec()
+ def code = cl.rehydrate(spec, cl, this)
+ code.resolveStrategy = Closure.DELEGATE_FIRST
+ code()
+ if (!spec.策略名) throw new IllegalArgumentException("策略名字不能为空")
+ if (!spec.nodes) throw new IllegalArgumentException("$spec.策略名 是空策略")
+ return spec
+ }
+
+
+ /**
+ * 根据dsl字符串创建 PolicySpec 对象
+ * @param dsl
+ * @return PolicySpec 对象
+ */
+ static PolicySpec of(String dsl) {
+ Binding binding = new Binding()
+ def config = new CompilerConfiguration()
+ def icz = new ImportCustomizer()
+ config.addCompilationCustomizers(icz)
+ icz.addImports(PolicySpec.name, Utils.name)
+ icz.addStarImports(RuleSpec.name, DecisionSpec.name, ScorecardSpec.name)
+ def shell = new GroovyShell(Thread.currentThread().contextClassLoader, binding, config)
+ shell.evaluate("service.rule.spec.PolicySpec.of{$dsl}")
+ }
+}
diff --git a/src/service/rule/spec/RuleSpec.groovy b/src/service/rule/spec/RuleSpec.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..0b10b6d4ac1c4b069e1893c3258d3d76063abfc2
--- /dev/null
+++ b/src/service/rule/spec/RuleSpec.groovy
@@ -0,0 +1,98 @@
+package service.rule.spec
+
+import service.rule.DecideResult
+import service.rule.DecisionContext
+
+/**
+ * 规则定义 spec
+ */
+class RuleSpec extends BaseSpec {
+ String 规则名
+
+
+ /**
+ * 自定义属性
+ */
+ void 属性定义(String 属性名, Object 值) {
+ if (!属性名) throw new IllegalArgumentException("属性名 不能为空")
+ attrs.put(属性名, 值)
+ }
+
+
+ /**
+ * 清除属性值缓存
+ * @param 属性名
+ */
+ void 清除(String... 属性名) {
+ if (!属性名) return
+ nodes << Tuple.tuple('Clear', { DecisionContext ctx ->
+ 属性名.each {ctx.data.remove(it)}
+ })
+ }
+
+
+ void 拒绝(Closure 条件) {
+ nodes << Tuple.tuple('Reject', { DecisionContext ctx ->
+ if (rehydrate1(条件, ctx.data, this)()) return DecideResult.Reject
+ null
+ })
+ }
+
+
+ void 通过(Closure 条件) {
+ nodes << Tuple.tuple('Accept', { DecisionContext ctx ->
+ if (rehydrate1(条件, ctx.data, this)()) return DecideResult.Accept
+ null
+ })
+ }
+
+
+ void 人工审核(Closure 条件) {
+ nodes << Tuple.tuple('Review', { DecisionContext ctx ->
+ if (rehydrate1(条件, ctx.data, this)()) return DecideResult.Review
+ null
+ })
+ }
+
+
+ void 操作(Closure 操作) {
+ nodes << Tuple.tuple('Operate', { DecisionContext ctx ->
+ rehydrate1(操作, ctx.data, this)()
+ })
+ }
+
+
+ /**
+ * 计算规则结果
+ * @param ctx {@link DecisionContext}
+ * @return {@link DecideResult}
+ */
+ DecideResult compute(DecisionContext ctx) {
+ DecideResult result
+ for (def node : nodes) {
+ if ("Reject" == node.v1) {
+ DecisionContext.log.trace(ctx.logPrefix() + "[拒绝]开始执行")
+ result = node.v2.call(ctx)
+ if (result && result.block) break
+ } else if ("Accept" == node.v1) {
+ DecisionContext.log.trace(ctx.logPrefix() + "[接受]开始执行")
+ result = node.v2.call(ctx)
+ if (result && result.block) break
+ } else if ("Review" == node.v1) {
+ DecisionContext.log.trace(ctx.logPrefix() + "[人工]开始执行")
+ result = node.v2.call(ctx)
+ if (result && result.block) break
+ } else if ("Clear" == node.v1) {
+ DecisionContext.log.trace(ctx.logPrefix() + "[清除]开始执行")
+ node.v2.call(ctx)
+ } else if ("Operate" == node.v1) {
+ DecisionContext.log.trace(ctx.logPrefix() + "[操作]开始执行")
+ node.v2.call(ctx)
+ } else throw new IllegalArgumentException("Unknown type: " + node.v1)
+ }
+ result
+ }
+
+ @Override
+ String name() { return 规则名 }
+}
diff --git a/src/service/rule/spec/ScorecardSpec.groovy b/src/service/rule/spec/ScorecardSpec.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..65ba1cfe3f4f1ad64feae8b872b34d0834ce9d89
--- /dev/null
+++ b/src/service/rule/spec/ScorecardSpec.groovy
@@ -0,0 +1,87 @@
+package service.rule.spec
+
+import service.rule.DecisionContext
+
+/**
+ * 评分卡spec
+ * 例子:
+评分卡 {
+ 评分卡名 = "测试评分卡"
+ 基础分 = 600
+ 模型 = [
+ ['年龄', 18..24, 40],
+ ['年龄', 25..45, 80],
+ ['年龄', 46..60, 50],
+ ['性别', 'F', 50],
+ ['性别', 'M', 60],
+ ['工作成市', ['成都', '巴中'], 99]
+ ]
+ 赋值变量 = '评分结果'
+}
+ */
+class ScorecardSpec extends BaseSpec {
+ /**
+ * 评分卡名字
+ */
+ String 评分卡名
+ /**
+ * 可要可不要
+ */
+ Number 基础分
+ /**
+ [
+ ['年龄', 18..24, 40],
+ ['年龄', 25..45, 80],
+ ['年龄', 46..60, 50],
+ ['性别', 'F', 50],
+ ['性别', 'M', 60]
+ ]
+ */
+ List 模型
+ /**
+ * 评分结果存放在哪个变量里面
+ */
+ String 赋值变量
+
+
+ /**
+ * 计算结果
+ * 按模型顺序依次计算结果并累加
+ */
+ Number compute(DecisionContext ctx) {
+ Number result = (基础分?:0) + 模型.findResults {item ->
+ // 3位: 变量名, 取值范围, 分值
+ if (item.size() == 3) {
+ def attrValue = ctx.data.get(item[0])
+ def enumValue = item[1]
+ if (enumValue instanceof Range || enumValue instanceof Collection) {
+ if (attrValue in enumValue) return item[2]
+ } else if (attrValue == enumValue) return item[2]
+ }
+ // 2位: 变量名, 分值计算函数
+ else if (item.size() == 2) {
+ def attrValue = ctx.data.get(item[0])
+ if (item[1] instanceof Closure) {//动态分值计算函数
+ return rehydrate2(item[1], ctx.data, this)(attrValue)
+ } else throw new IllegalArgumentException("评分卡($评分卡名)模型格式错误: 变量名, 分值计算函数")
+ }
+ // 1位: 变量名/分值计算函数
+ else if (item.size() == 1) {
+ if (item[0] instanceof String) { // 变量名
+ def v = ctx.data.get(item[0])
+ return v == null ? 0 : v //直接取值为评分
+ } else if (item[0] instanceof Closure) { // 分值计算函数
+ return rehydrate2(item[0], ctx.data, this)()
+ } else throw new IllegalArgumentException("评分卡($评分卡名)模型格式错误: 变量名 或 分值计算函数")
+ }
+ else throw new IllegalArgumentException("评分卡($评分卡名)模型格式错误: 位数")
+ 0
+ }.sum()
+ ctx.setAttr(赋值变量, result)
+ return result
+ }
+
+
+ @Override
+ String name() { return 评分卡名 }
+}
diff --git a/src/service/rule/test.decision b/src/service/rule/test.decision
new file mode 100644
index 0000000000000000000000000000000000000000..dfaf717c65376aee633bea2b5c72269a43867bdb
--- /dev/null
+++ b/src/service/rule/test.decision
@@ -0,0 +1,115 @@
+// 唯一
+决策id = 'decision1'
+决策名 = 'decision1'
+决策描述 = '测试决策1'
+
+返回属性 '身份证号码'
+返回属性 '年龄'
+
+
+操作 { // 策略执行前操作执行
+
+}
+
+策略 {
+ 策略名 = 'P_测试策略1'
+ 条件 { // 执行此策略的条件, false: 不执行, true/不配置默认 执行此策略
+ // age > 20 && age < 50
+ true
+ }
+
+ 操作 { // 策略规则执行前操作执行
+
+ }
+
+ // 空属性: 数据获取错误, 数据本身是空(其它指标)
+
+ 规则 {
+ // 关闭()
+ 规则名 = 'R_参数验证'
+
+ 拒绝 {
+ 年龄 == null || 身份证号码 == null || 姓名 == null || 年龄 > 50 || 性别 == 'M'
+ }
+ }
+
+ 规则 {
+ // 关闭()
+ 规则名 = '通过规则'
+
+ 操作 { // 顺序执行操作
+ 指标1 = 20
+ }
+
+ 通过 {
+ 身份证号码 != null
+ }
+
+ 操作 { // 顺序执行操作
+ 指标1 = 21
+ }
+ }
+
+ 规则 {
+ 规则名 = '操作规则'
+
+ 操作 { // 顺序执行操作
+ 指标1 = 20
+ }
+
+ 通过 {
+ 身份证号码 != null
+ }
+
+ 操作 { // 顺序执行操作
+ // 四则运算
+ 测试字段 = 2 * 年龄
+ }
+
+ 操作 { // 顺序执行操作
+ // 逻辑判断
+ if (年龄 > 30) {
+ 测试字段 -= 5
+ }
+ }
+ }
+
+ 规则 {
+ 规则名 = '列表规则'
+
+ 禁入区域 = [
+ '澳门特别行政区', '香港特别行政区', '新疆维吾尔自治区', '西藏自治区',
+ '内蒙古自治区', '宁夏回族自治区', '未知', '澳门', '台湾'
+ ]
+
+ 拒绝 {
+ 工作地所在省 in 禁入区域 || 居住地所在省 in 禁入区域
+ }
+ }
+
+ 规则 {
+ 规则名 = '包含规则'
+
+ 拒绝 {
+ "$姓名".contains 'xx'
+ }
+ }
+}
+
+
+策略 {
+ 策略名 = 'P_测试策略2'
+
+ 规则 {
+ 规则名 = '通过规则'
+
+ 通过 {true}
+ }
+
+ 规则 {
+ 关闭()
+ 规则名 = '拒绝规则'
+
+ 拒绝 {true}
+ }
+}
\ No newline at end of file
diff --git a/src/static/components/Upload.vue b/src/static/components/Upload.vue
new file mode 100644
index 0000000000000000000000000000000000000000..014d217436f388784197c38849fa65ab4dce7afd
--- /dev/null
+++ b/src/static/components/Upload.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/static/css/lib/bootstrap.min.4.3.1.css b/src/static/css/lib/bootstrap.min.4.3.1.css
new file mode 100644
index 0000000000000000000000000000000000000000..92e3fe871295c44f8fa58ddc7ac242463f13e6bd
--- /dev/null
+++ b/src/static/css/lib/bootstrap.min.4.3.1.css
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v4.3.1 (https://getbootstrap.com/)
+ * Copyright 2011-2019 The Bootstrap Authors
+ * Copyright 2011-2019 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip{display:block}.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip{display:block}.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:calc(1rem + .4rem);padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion>.card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion>.card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion>.card .card-header{margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-sm .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-md .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-lg .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-xl .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush .list-group-item:last-child{margin-bottom:-1px}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{margin-bottom:0;border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #dee2e6;border-bottom-right-radius:.3rem;border-bottom-left-radius:.3rem}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:0s .6s opacity}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}
+/*# sourceMappingURL=bootstrap.min.css.map */
\ No newline at end of file
diff --git a/src/static/css/lib/fonts/heyui.eot b/src/static/css/lib/fonts/heyui.eot
new file mode 100644
index 0000000000000000000000000000000000000000..bc4b5334457133cec82ba165791ff18d3e1b5a31
Binary files /dev/null and b/src/static/css/lib/fonts/heyui.eot differ
diff --git a/src/static/css/lib/fonts/heyui.svg b/src/static/css/lib/fonts/heyui.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1eef97b8e35b10aacfbce3076998d9517c4a0299
--- /dev/null
+++ b/src/static/css/lib/fonts/heyui.svg
@@ -0,0 +1,56 @@
+
+
+
+Generated by IcoMoon
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/static/css/lib/fonts/heyui.ttf b/src/static/css/lib/fonts/heyui.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..eb78e75686dc3eccb0e1d51ddc967f77c8237d68
Binary files /dev/null and b/src/static/css/lib/fonts/heyui.ttf differ
diff --git a/src/static/css/lib/fonts/heyui.woff b/src/static/css/lib/fonts/heyui.woff
new file mode 100644
index 0000000000000000000000000000000000000000..f139616640177db61d9a105327103933cab28be7
Binary files /dev/null and b/src/static/css/lib/fonts/heyui.woff differ
diff --git a/src/static/css/lib/fonts/ionicons.svg b/src/static/css/lib/fonts/ionicons.svg
new file mode 100644
index 0000000000000000000000000000000000000000..d8812552f34b46d7c1604050d7599a2a2b3cb568
--- /dev/null
+++ b/src/static/css/lib/fonts/ionicons.svg
@@ -0,0 +1,870 @@
+
+
+
+Generated by IcoMoon
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/static/css/lib/fonts/ionicons.ttf b/src/static/css/lib/fonts/ionicons.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..1caa214a3506bc3ee807eff8a24a195c50af1bf4
Binary files /dev/null and b/src/static/css/lib/fonts/ionicons.ttf differ
diff --git a/src/static/css/lib/fonts/ionicons.woff b/src/static/css/lib/fonts/ionicons.woff
new file mode 100644
index 0000000000000000000000000000000000000000..c909e51cf3e50600516fa7308f3ca75046be93ec
Binary files /dev/null and b/src/static/css/lib/fonts/ionicons.woff differ
diff --git a/src/static/css/lib/fonts/ionicons.woff2 b/src/static/css/lib/fonts/ionicons.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..1d6d9c564b3c9f7a097a8e92bde6425ccaa33704
Binary files /dev/null and b/src/static/css/lib/fonts/ionicons.woff2 differ
diff --git a/src/static/css/lib/heyui.css b/src/static/css/lib/heyui.css
new file mode 100644
index 0000000000000000000000000000000000000000..1c956b812bc22c8d50b108143a8daf1972ce6afa
--- /dev/null
+++ b/src/static/css/lib/heyui.css
@@ -0,0 +1 @@
+.h-func-list-style{list-style:none;margin:0;padding:0;outline:none;box-sizing:border-box}html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace, monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace, monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input{overflow:visible}button,select{text-transform:none}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}template{display:none}[hidden]{display:none}body{font-family:"Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased;-webkit-tap-highlight-color:rgba(255,255,255,0);-moz-osx-font-smoothing:grayscale}html,body,address,blockquote,dl,form,h1,h2,h3,h4,h5,h6,ol,table,ul,dd,dt,li,tbody,td,tfoot,th,thead,tr,button,del,ins,map,object,a,abbr,acronym,b,bdo,big,br,cite,code,dfn,em,i,img,kbd,q,samp,small,span,strong,sub,sup,tt,var,legend,fieldset,pre{margin:0;padding:0;outline:none;box-sizing:border-box}div{box-sizing:border-box}a{color:#45b984;cursor:pointer;text-decoration:none;outline:none;transition:color .2s ease}a:hover{color:#379469 !important}a:active{color:#296f4f !important}a:active,a:hover{outline:0;text-decoration:none}a[disabled]{color:#999999 !important;cursor:default}input[type=text],input[type=password],input[type=search],input[type=number],input[type=tel],input[type=url],input.h-input,textarea,.h-textarea{min-width:1px;position:relative;background-color:#ffffff;border-radius:4px;border:1px solid #eeeeee;display:inline-block;padding:4px 7.5px;line-height:1.5;font-size:13px;transition:all linear .2s;box-sizing:border-box;-webkit-appearance:none;font-family:inherit;vertical-align:top;outline:none}input[type=text]:hover,input[type=password]:hover,input[type=search]:hover,input[type=number]:hover,input[type=tel]:hover,input[type=url]:hover,input.h-input:hover,textarea:hover,.h-textarea:hover{border-color:#6ac79d}input[type=text]:focus,input[type=password]:focus,input[type=search]:focus,input[type=number]:focus,input[type=tel]:focus,input[type=url]:focus,input.h-input:focus,textarea:focus,.h-textarea:focus{border-color:#45b984;box-shadow:0 0 0 2px rgba(69,185,132,0.2)}input[type=text][disabled],input[type=password][disabled],input[type=search][disabled],input[type=number][disabled],input[type=tel][disabled],input[type=url][disabled],input.h-input[disabled],textarea[disabled],.h-textarea[disabled],input[type=text][readonly],input[type=password][readonly],input[type=search][readonly],input[type=number][readonly],input[type=tel][readonly],input[type=url][readonly],input.h-input[readonly],textarea[readonly],.h-textarea[readonly]{border-color:#d3d3d3}input[type=text][disabled]:focus,input[type=password][disabled]:focus,input[type=search][disabled]:focus,input[type=number][disabled]:focus,input[type=tel][disabled]:focus,input[type=url][disabled]:focus,input.h-input[disabled]:focus,textarea[disabled]:focus,.h-textarea[disabled]:focus,input[type=text][readonly]:focus,input[type=password][readonly]:focus,input[type=search][readonly]:focus,input[type=number][readonly]:focus,input[type=tel][readonly]:focus,input[type=url][readonly]:focus,input.h-input[readonly]:focus,textarea[readonly]:focus,.h-textarea[readonly]:focus{box-shadow:none}input[type=text][readonly],input[type=password][readonly],input[type=search][readonly],input[type=number][readonly],input[type=tel][readonly],input[type=url][readonly],input.h-input[readonly],textarea[readonly],.h-textarea[readonly]{color:#555555;box-shadow:none;cursor:default}input[type=text][disabled],input[type=password][disabled],input[type=search][disabled],input[type=number][disabled],input[type=tel][disabled],input[type=url][disabled],input.h-input[disabled],textarea[disabled],.h-textarea[disabled]{color:#999999;background-color:#f5f5f5;pointer-events:painted;cursor:not-allowed}.h-form-item-valid-error input[type=text],.h-form-item-valid-error input[type=password],.h-form-item-valid-error input[type=search],.h-form-item-valid-error input[type=number],.h-form-item-valid-error input[type=tel],.h-form-item-valid-error input[type=url],.h-form-item-valid-error input.h-input,.h-form-item-valid-error textarea,.h-form-item-valid-error .h-textarea{border-color:#E11617}.h-form-item-valid-error input[type=text]:focus,.h-form-item-valid-error input[type=password]:focus,.h-form-item-valid-error input[type=search]:focus,.h-form-item-valid-error input[type=number]:focus,.h-form-item-valid-error input[type=tel]:focus,.h-form-item-valid-error input[type=url]:focus,.h-form-item-valid-error input.h-input:focus,.h-form-item-valid-error textarea:focus,.h-form-item-valid-error .h-textarea:focus{box-shadow:0 0 0 2px rgba(225,22,23,0.2)}input[type=text],input[type=password],input[type=search],input[type=number],input[type=tel],input[type=url],input.h-input{height:30px}textarea,.h-textarea{padding:7.5px}input[type=text]::-ms-clear{display:none}input::-webkit-search-cancel-button{display:none}@font-face{font-family:'heyui';src:url('fonts/heyui.eot?p7arpe');src:url('fonts/heyui.eot?p7arpe#iefix') format('embedded-opentype'),url('fonts/heyui.ttf?p7arpe') format('truetype'),url('fonts/heyui.woff?p7arpe') format('woff'),url('fonts/heyui.svg?p7arpe#heyui') format('svg');font-weight:normal;font-style:normal}[class^="h-icon-"],[class*=" h-icon-"]{font-family:'heyui' !important;speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.h-icon-help-solid:before{content:"\e91d"}.h-icon-lock:before{content:"\e91e"}.h-icon-star:before{content:"\e922"}.h-icon-star-on:before{content:"\e923"}.h-icon-help:before{content:"\e900"}.h-icon-plus:before{content:"\e114"}.h-icon-minus:before{content:"\e115"}.h-icon-check:before{content:"\e116"}.h-icon-inbox:before{content:"\e122"}.h-icon-outbox:before{content:"\e123"}.h-icon-info:before{content:"\e901"}.h-icon-refresh:before{content:"\e902"}.h-icon-warn:before{content:"\e903"}.h-icon-success:before{content:"\e904"}.h-icon-error:before{content:"\e905"}.h-icon-close:before{content:"\e906"}.h-icon-close-min:before{content:"\e924"}.h-icon-down:before{content:"\e907"}.h-icon-angle-top:before{content:"\e917"}.h-icon-angle-left:before{content:"\e91f"}.h-icon-angle-right:before{content:"\e920"}.h-icon-angle-down:before{content:"\e921"}.h-icon-home:before{content:"\e908"}.h-icon-left:before{content:"\e909"}.h-icon-top:before{content:"\e90a"}.h-icon-user:before{content:"\e90b"}.h-icon-task:before{content:"\e90c"}.h-icon-message:before{content:"\e90d"}.h-icon-fullscreen:before{content:"\e90e"}.h-icon-menu:before{content:"\e90f"}.h-icon-right:before{content:"\e910"}.h-icon-link:before{content:"\e911"}.h-icon-calendar:before{content:"\e912"}.h-icon-upload:before{content:"\e913"}.h-icon-github:before{content:"\e914"}.h-icon-edit:before{content:"\e915"}.h-icon-location:before{content:"\e916"}.h-icon-trash:before{content:"\e918"}.h-icon-users:before{content:"\e919"}.h-icon-complete:before{content:"\e91a"}.h-icon-bell:before{content:"\e91b"}.h-icon-search:before{content:"\e91c"}.h-icon-setting:before{content:"\e926"}.h-icon-completed:before{content:"\e930"}.h-icon-loading:before{content:"\e97f"}.h-icon-spinner:before{content:"\e981"}[class^="h-icon-"],[class*=" h-icon-"]{font-size:inherit;display:inline-block;vertical-align:-1px}.h-icon-loading,.h-icon-spinner{animation:h-ani-loading-loop 1s linear infinite}@keyframes h-ani-loading-loop{from{transform:rotate(0deg)}50%{transform:rotate(180deg)}to{transform:rotate(360deg)}}ul,ol{list-style:none;margin:0;padding:0;outline:none;box-sizing:border-box}.red-color{color:#E11617 !important}.bg-red-color{background-color:#E11617 !important}.blue-color{color:#3B91FF !important}.bg-blue-color{background-color:#3B91FF !important}.green-color{color:#13CE66 !important}.bg-green-color{background-color:#13CE66 !important}.yellow-color{color:#FFAE00 !important}.bg-yellow-color{background-color:#FFAE00 !important}.white-color{color:#ffffff !important}.bg-white-color{background-color:#ffffff !important}.gray-color{color:#c1c1c1 !important}.bg-gray-color{background-color:#c1c1c1 !important}.gray1-color{color:#d3d3d3 !important}.bg-gray1-color{background-color:#d3d3d3 !important}.gray2-color{color:#eeeeee !important}.bg-gray2-color{background-color:#eeeeee !important}.gray3-color{color:#f3f3f3 !important}.bg-gray3-color{background-color:#f3f3f3 !important}.gray4-color{color:#f5f5f5 !important}.bg-gray4-color{background-color:#f5f5f5 !important}.dark-color{color:#333333 !important}.bg-dark-color{background-color:#333333 !important}.dark1-color{color:#555555 !important}.bg-dark1-color{background-color:#555555 !important}.dark2-color{color:#666666 !important}.bg-dark2-color{background-color:#666666 !important}.dark3-color{color:#777777 !important}.bg-dark3-color{background-color:#777777 !important}.dark4-color{color:#999999 !important}.bg-dark4-color{background-color:#999999 !important}.primary-color{color:#45b984 !important}.bg-primary-color{background-color:#45b984 !important}.primary1-color{color:rgba(69,185,132,0.45) !important}.bg-primary1-color{background-color:rgba(69,185,132,0.45) !important}.primary2-color{color:rgba(69,185,132,0.15) !important}.bg-primary2-color{background-color:rgba(69,185,132,0.15) !important}.link{color:#45b984;cursor:pointer;text-decoration:none;outline:none;transition:color .2s ease}.link:hover{color:#379469 !important}.link:active{color:#296f4f !important}.link:active,.link:hover{outline:0;text-decoration:none}.link[disabled]{color:#999999 !important;cursor:default}.clearfix::after{content:"";display:block;clear:both}.flex{display:flex}.flex-column{flex-direction:column}.flex1{flex:1}.middle{position:absolute;top:50%;transform:translate(0%, -50%)}.absolute{position:absolute}.relative{position:relative}.pointer{cursor:pointer}.middle-center{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%)}.middle-right{position:absolute;right:0;top:50%;transform:translate(0%, -50%)}.overflow-y{overflow-y:auto}.float-right{float:right}.text-hover{cursor:pointer;transition:color .2s ease}.text-hover:hover{color:#45b984 !important}.text-hover:active{color:#30815c !important}.text-hover.h-pop-trigger .text-hover{color:#379469 !important}.text-hover[disabled]{color:#999999 !important;cursor:default}.dark-text-hover{cursor:pointer;transition:color .2s ease;color:#999999}.dark-text-hover:hover{color:#666666 !important}.dark-text-hover:active{color:#4d4d4d !important}.dark-text-hover[disabled]{color:#999999 !important;cursor:default}.text-left{text-align:left !important}.text-center{text-align:center !important}.text-right{text-align:right !important}.bottom-line{border-bottom:1px solid #eeeeee}.h-split{display:inline-block;width:5px;height:2px}.text-ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.h-checkbox .h-checkbox-native{-webkit-appearance:none;display:inline-block;width:16px;height:16px;margin:0px;border:1px solid #d3d3d3;cursor:pointer;box-sizing:border-box;border-radius:2px;vertical-align:middle;transition:background,border linear .2s;position:relative;font-family:inherit;outline:none;background-color:#ffffff}.h-checkbox .h-checkbox-native[checked],.h-checkbox .h-checkbox-native[indeterminate]{background-color:#45b984;border:1px solid #45b984}.h-checkbox .h-checkbox-native:after{box-sizing:content-box;content:"";border:2px solid #ffffff;border-left:0;border-top:0;height:8px;left:4px;position:absolute;top:1px;width:4px;transition:all .2s;transform:rotate(45deg) scale(0)}.h-checkbox .h-checkbox-native[indeterminate]:after{content:"";width:8px;height:0px;left:2px;top:6px;transform:rotate(0deg) scale(1)}.h-checkbox .h-checkbox-native[checked]:after{transform:rotate(45deg) scale(1)}.h-checkbox .h-checkbox-native:hover{border-color:#45b984}.h-checkbox .h-checkbox-native[disabled],.h-checkbox .h-checkbox-native[readonly]{pointer-events:painted;cursor:not-allowed;border-color:#d3d3d3;background-color:#f5f5f5}.h-checkbox .h-checkbox-native[disabled]:after,.h-checkbox .h-checkbox-native[readonly]:after{border-color:#d3d3d3}.h-checkbox .h-checkbox-native[disabled][checked],.h-checkbox .h-checkbox-native[readonly][checked],.h-checkbox .h-checkbox-native[disabled][indeterminate],.h-checkbox .h-checkbox-native[readonly][indeterminate]{background-color:#d3d3d3}.h-checkbox .h-checkbox-native[disabled][checked]:after,.h-checkbox .h-checkbox-native[readonly][checked]:after,.h-checkbox .h-checkbox-native[disabled][indeterminate]:after,.h-checkbox .h-checkbox-native[readonly][indeterminate]:after{border-color:#ffffff}.h-checkbox{display:inline-block}.h-checkbox>label{margin-right:15px;display:inline-block}.h-checkbox>label:last-child{margin-right:0}.h-checkbox>label>:nth-child(2){margin-left:5px}.h-checkbox>label span{line-height:16px;vertical-align:middle}.h-checkbox>label i{line-height:16px;vertical-align:middle}.h-checkbox>label:hover>.h-checkbox-native{border-color:#45b984}.h-checkbox .h-checkbox-disabled{color:#999999;pointer-events:painted;cursor:not-allowed}.h-checkbox .h-checkbox-disabled:hover>.h-checkbox-native{border-color:#d3d3d3}.h-radio>label>.h-radio-icon{-webkit-appearance:none;display:inline-block;width:16px;height:16px;margin:0px;border:1px solid #d3d3d3;background:#FFF;border-radius:8px;cursor:pointer;position:relative;vertical-align:-3px;overflow:hidden;margin-right:5px;transition:all linear .2s;box-sizing:border-box;font-family:inherit;outline:none}.h-radio>label>.h-radio-icon:after{content:"";display:block;background-color:#45b984;transition:all linear .2s;transform:scale(0);width:8px;height:8px;border-radius:4px;position:relative;left:3px;top:3px}.h-radio>label>.h-radio-icon:checked,.h-radio>label>.h-radio-icon[checked]{border-color:#45b984}.h-radio>label>.h-radio-icon:checked:after,.h-radio>label>.h-radio-icon[checked]:after{transform:scale(1)}.h-radio>label>.h-radio-icon:hover{border-color:#45b984}.h-radio>label>.h-radio-icon[disabled]{pointer-events:painted;cursor:not-allowed;border-color:#d3d3d3;background-color:#f5f5f5}.h-radio>label>.h-radio-icon[disabled]:checked,.h-radio>label>.h-radio-icon[disabled][checked]{background-color:#f5f5f5}.h-radio>label>.h-radio-icon[disabled]:checked:after,.h-radio>label>.h-radio-icon[disabled][checked]:after{background-color:#d3d3d3}.h-radio{display:inline-block}.h-radio>label{margin-right:15px;height:30px;line-height:30px;display:inline-block}.h-radio>label:last-child{margin-right:0}.h-radio>label:hover>.h-radio-icon{border-color:#45b984}.h-radio.h-radio-disabled>label{color:#999999;pointer-events:painted;cursor:not-allowed}.h-radio.h-radio-disabled>label:hover>.h-radio-icon{border-color:#d3d3d3}.h-search{min-width:150px;position:relative;display:inline-block;vertical-align:middle}.h-search-block{display:block}.h-search .h-search-container{display:flex}.h-search .h-search-container .h-search-input{position:relative;flex:1}.h-search .h-search-container .h-search-input input{width:100%;padding-right:30px}.h-search>.h-icon-search,.h-search .h-icon-close{position:absolute;top:50%;right:8px;font-size:12px;z-index:3;cursor:pointer;transition:color .2s ease;color:#666666;margin-top:-6px}.h-search>.h-icon-search:hover,.h-search .h-icon-close:hover{color:#45b984 !important}.h-search>.h-icon-search:active,.h-search .h-icon-close:active{color:#30815c !important}.h-search>.h-icon-search.h-pop-trigger .h-search>.h-icon-search,.h-search>.h-icon-search.h-pop-trigger .h-search .h-icon-close,.h-search .h-icon-close.h-pop-trigger .h-search>.h-icon-search,.h-search .h-icon-close.h-pop-trigger .h-search .h-icon-close{color:#379469 !important}.h-search>.h-icon-search[disabled],.h-search .h-icon-close[disabled]{color:#999999 !important;cursor:default}.h-search.h-search-has-button input{border-radius:4px 0 0 4px}.h-search.h-search-has-button .h-btn{height:30px;line-height:28px;padding-top:0;padding-bottom:0;border-radius:0 4px 4px 0;margin-left:-1px}.h-search.h-search-has-button .h-icon-search-end{display:none}.h-search .h-icon-close{display:none;cursor:pointer;transition:color .2s ease}.h-search .h-icon-close:hover{color:#45b984 !important}.h-search .h-icon-close:active{color:#30815c !important}.h-search .h-icon-close.h-pop-trigger .h-search .h-icon-close{color:#379469 !important}.h-search .h-icon-close[disabled]{color:#999999 !important;cursor:default}.h-search-searching.h-search-end>.h-icon-search{display:none}.h-search-searching .h-icon-close{display:block}.h-search.h-search-front>.h-icon-search{z-index:1;left:8px;right:auto}.h-search.h-search-front input{padding-left:30px}.h-search.h-search-end>.h-icon-search{cursor:pointer;transition:color .2s ease}.h-search.h-search-end>.h-icon-search:hover{color:#45b984 !important}.h-search.h-search-end>.h-icon-search:active{color:#30815c !important}.h-search.h-search-end>.h-icon-search.h-pop-trigger .h-search.h-search-end>.h-icon-search{color:#379469 !important}.h-search.h-search-end>.h-icon-search[disabled]{color:#999999 !important;cursor:default}.h-notify{z-index:1000;position:relative}.h-notify .h-notify-body{position:fixed;overflow:auto;top:0;right:0;bottom:0;left:0;z-index:1000}.h-notify .h-notify-container{background-color:#ffffff;border-radius:3px;box-shadow:0 5px 10px rgba(0,0,0,0.1);transition:.2s;opacity:0}.h-notify .h-notify-content{position:relative}.h-notify .h-notify-close{display:block;position:absolute;right:8px;top:12px;font-size:14px;line-height:1;text-align:center;z-index:1;cursor:pointer;transition:color .2s ease;color:#999999}.h-notify .h-notify-close:hover{color:#666666 !important}.h-notify .h-notify-close:active{color:#4d4d4d !important}.h-notify .h-notify-close[disabled]{color:#999999 !important;cursor:default}.h-notify .h-notify-mask{position:fixed;top:0;bottom:0;left:0;right:0;transition:opacity .2s;background:rgba(0,0,0,0.16);z-index:1000;opacity:0}.h-notify.h-notify-show .h-notify-mask{opacity:1}.h-notify.h-notify-show .h-notify-container{opacity:1}.h-message-container{position:fixed;top:30px;z-index:1010;pointer-events:none;left:0;text-align:center;right:0}.h-message .h-notify-container{margin:0 auto;opacity:0;position:relative;overflow:hidden;display:inline-block;pointer-events:initial;max-height:0}.h-message .h-notify-content{padding:0px 18px;transition:.2s}.h-message .h-notify-content [class^="h-icon"],.h-message .h-notify-content [class*=" h-icon"]{margin-right:10px;display:inline-block}.h-message.h-notify-has-close .h-notify-content{padding-right:40px}.h-message.h-notify-show .h-notify-container{max-height:1000px;margin-top:10px}.h-message.h-notify-show .h-notify-content{padding-top:8px;padding-bottom:8px}.h-notice{transition:.7s;pointer-events:initial}.h-notice-container{position:fixed;top:50px;right:10px;z-index:1000;pointer-events:none}.h-notice .h-notify-container{min-width:350px;right:-100px;margin-bottom:20px;transform:none;position:relative;max-height:0;overflow:hidden}.h-notice .h-notify-container .h-notice-header,.h-notice .h-notify-container header:first-child{min-height:20px;line-height:20px;font-size:16px;padding:16px 0 0 16px}.h-notice .h-notify-container .h-notice-footer,.h-notice .h-notify-container footer:last-child{padding:10px 16px;text-align:left}.h-notice .h-notify-content{padding:12px 16px;color:#333333}.h-notice .h-notify-content [class^="h-icon"],.h-notice .h-notify-content [class*=" h-icon"]{margin-right:10px;display:inline-block}.h-notice.h-notice-has-icon .h-notice-header,.h-notice.h-notice-has-icon header:first-child{margin-left:40px}.h-notice.h-notice-has-icon .h-notify-content{margin-left:40px}.h-notice.h-notice-has-icon .h-notify-content [class^="h-icon"],.h-notice.h-notice-has-icon .h-notify-content [class*=" h-icon"]{margin-right:10px;float:left;font-size:30px;position:absolute;left:-25px;top:-20px}.h-notice.h-notice-has-icon headerfooter{margin-left:40px}.h-notice.h-notify-show .h-notify-container{right:0px;max-height:1000px}.h-modal{position:static}.h-modal .h-notify-body{position:relative;overflow:initial}.h-modal .h-notify-container{position:fixed}.h-modal .h-notify-container .h-modal-header,.h-modal .h-notify-container header:first-child{min-height:40px;line-height:40px;font-size:16px;padding-left:15px}.h-modal .h-notify-container .h-modal-footer,.h-modal .h-notify-container footer:last-child{padding:10px 15px;text-align:right}.h-modal .h-notify-content{padding:10px 15px}.h-modal .h-notify-content.h-notify-content-component{padding:0}.h-modal-type-default .h-notify-container{left:50%;transform:translate(-50%, 0%);top:10px;margin-bottom:10px;min-width:350px}.h-modal-full-screen .h-notify-container{margin-bottom:0;transform:none;top:30px !important;bottom:30px;left:30px;right:30px;display:flex;flex-direction:column}.h-modal-full-screen .h-notify-container .h-notify-content{flex:1;overflow:auto}.h-modal-transparent .h-notify-container{background:none;box-shadow:none}.h-modal.h-modal-has-divider .h-notify-container .h-modal-header,.h-modal.h-modal-has-divider .h-notify-container header:first-child{border-bottom:1px solid #eeeeee}.h-modal.h-modal-has-divider .h-notify-container .h-modal-footer,.h-modal.h-modal-has-divider .h-notify-container footer:last-child{border-top:1px solid #eeeeee}.h-modal.h-notify-has-mask .h-notify-body{position:fixed;overflow:auto}.h-modal.h-notify-has-mask .h-notify-container{position:absolute}.h-modal.h-notify-no-mask .h-notify-container{z-index:1000}.h-modal.h-notify-show.h-modal-type-default .h-notify-container{top:100px}.h-modal.h-notify-show.h-modal-container-center .h-notify-container{top:50%;transform:translate(-50%, -50%)}.h-modal.h-notify-show.h-modal-full-screen .h-notify-container{top:0 !important;bottom:0;left:0;right:0}.h-modal.h-notify-draggable .h-modal-header,.h-modal.h-notify-draggable header:first-child{cursor:move}.h-modal-type-drawer-right .h-notify-body,.h-modal-type-drawer-left .h-notify-body{overflow:hidden !important}.h-modal-type-drawer-right .h-notify-container,.h-modal-type-drawer-left .h-notify-container{top:0;bottom:0;display:flex;flex-direction:column}.h-modal-type-drawer-right .h-notify-container .h-notify-content,.h-modal-type-drawer-left .h-notify-container .h-notify-content{flex:1;overflow:auto}.h-modal-type-drawer-right.h-notify-show .h-notify-container,.h-modal-type-drawer-left.h-notify-show .h-notify-container{transform:translateX(0)}.h-modal-type-drawer-right .h-notify-container{transform:translateX(100%);right:0}.h-modal-type-drawer-left .h-notify-container{transform:translateX(-100%);left:0}.h-tooltip{z-index:1060;opacity:0;transition:.35s cubic-bezier(.24, .22, .015, 1.56);transition-property:transform,opacity}.h-tooltip.h-tooltip-editable{z-index:1000}.h-tooltip .h-tooltip-arrow{width:0;height:0;border-style:solid;position:absolute;border-color:transparent}.h-tooltip .h-tooltip-inner{max-width:250px;text-align:left;text-decoration:none;background:#333333;color:#ffffff;border-radius:4px}.h-tooltip .h-tooltip-inner-content{padding:4px 10px}.h-tooltip.h-tooltip-white .h-tooltip-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid;content:" ";border-width:6px;margin-left:-6px;z-index:1}.h-tooltip.h-tooltip-white .h-tooltip-inner{background:#ffffff;color:#333333;box-shadow:0 2px 10px 0 rgba(0,0,0,0.15)}.h-tooltip.h-tooltip-white[x-placement^="top"] .h-tooltip-arrow{border-top-color:#e1e1e1}.h-tooltip.h-tooltip-white[x-placement^="top"] .h-tooltip-arrow:after{bottom:1px;border-bottom-width:0;border-top-color:#ffffff}.h-tooltip.h-tooltip-white[x-placement^="right"] .h-tooltip-arrow{border-right-color:#e1e1e1}.h-tooltip.h-tooltip-white[x-placement^="right"] .h-tooltip-arrow:after{left:7px;top:-6px;border-left-width:0;border-right-color:#ffffff}.h-tooltip.h-tooltip-white[x-placement^="left"] .h-tooltip-arrow{border-left-color:#e1e1e1}.h-tooltip.h-tooltip-white[x-placement^="left"] .h-tooltip-arrow:after{right:1px;top:-6px;border-right-width:0;border-left-color:#ffffff}.h-tooltip.h-tooltip-white[x-placement^="bottom"] .h-tooltip-arrow{border-bottom-color:#e1e1e1}.h-tooltip.h-tooltip-white[x-placement^="bottom"] .h-tooltip-arrow:after{top:1px;border-top-width:0;border-bottom-color:#ffffff}.h-tooltip[x-placement^="top"]{padding:5px 0 8px 0}.h-tooltip[x-placement^="right"]{padding:0 5px 0 8px}.h-tooltip[x-placement^="bottom"]{padding:8px 0 5px 0}.h-tooltip[x-placement^="left"]{padding:0 8px 0 5px}.h-tooltip[x-placement^="top"] .h-tooltip-arrow{bottom:3px;border-width:5px 5px 0;border-top-color:#333333}.h-tooltip[x-placement="top"] .h-tooltip-arrow{left:50%;margin-left:-5px}.h-tooltip[x-placement="top-start"] .h-tooltip-arrow{left:16px}.h-tooltip[x-placement="top-end"] .h-tooltip-arrow{right:16px}.h-tooltip[x-placement^="right"] .h-tooltip-arrow{left:3px;border-width:5px 5px 5px 0;border-right-color:#333333}.h-tooltip[x-placement="right"] .h-tooltip-arrow{top:50%;margin-top:-5px}.h-tooltip[x-placement="right-start"] .h-tooltip-arrow{top:8px}.h-tooltip[x-placement="right-end"] .h-tooltip-arrow{bottom:8px}.h-tooltip[x-placement^="left"] .h-tooltip-arrow{right:3px;border-width:5px 0 5px 5px;border-left-color:#333333}.h-tooltip[x-placement="left"] .h-tooltip-arrow{top:50%;margin-top:-5px}.h-tooltip[x-placement="bottom"] .h-tooltip-arrow{left:50%;margin-left:-5px}.h-tooltip[x-placement="left-start"] .h-tooltip-arrow{top:8px}.h-tooltip[x-placement="left-end"] .h-tooltip-arrow{bottom:8px}.h-tooltip[x-placement^="bottom"] .h-tooltip-arrow{top:3px;border-width:0 5px 5px;border-bottom-color:#333333}.h-tooltip[x-placement="bottom-start"] .h-tooltip-arrow{left:16px}.h-tooltip[x-placement="bottom-end"] .h-tooltip-arrow{right:16px}.h-tooltip-show{display:inline-block}[aria-hidden=false]{transform:scale(1);opacity:1}.h-dropdown{z-index:1000}.h-dropdown-inner{transition:.25s cubic-bezier(.24, .22, .015, 1.56);transition-property:transform,opacity;transform:scale(.8);max-height:500px;opacity:0}.h-dropdown-inner>div{display:block;box-sizing:border-box;border-radius:4px;background:#ffffff;box-shadow:0 5px 10px rgba(0,0,0,0.1)}.h-dropdown[aria-hidden=false] .h-dropdown-inner{transform:scale(1);opacity:1}.h-dropdown[x-placement=top-start] .h-dropdown-inner{transform-origin:bottom left}.h-dropdown[x-placement=top] .h-dropdown-inner{transform-origin:bottom}.h-dropdown[x-placement=top-end] .h-dropdown-inner{transform-origin:bottom right}.h-dropdown[x-placement=bottom-start] .h-dropdown-inner{transform-origin:top left}.h-dropdown[x-placement=bottom-end] .h-dropdown-inner{transform-origin:top right}.h-dropdown[x-placement=bottom] .h-dropdown-inner{transform-origin:top}.h-dropdowncustom{display:inline-block;vertical-align:middle}.h-dropdowncustom-show{position:relative}.h-dropdowncustom-show-toggle .h-dropdowncustom-show-content{padding-right:26px}.h-dropdowncustom-show>.h-icon-down{color:#666666;position:absolute;right:8px;top:50%;height:10px;font-size:12px;line-height:12px;transform:translate(0, -50%) scale(.8);transition:transform .2s}.h-dropdowncustom-show.h-pop-trigger>.h-icon-down{transform:rotate(-180deg) translate(0, 50%) scale(.8)}.h-dropdowncustom-show-content{display:inline-block}.h-dropdowncustom-show.h-dropdowncustom-empty .h-icon-down{right:auto;left:50%;margin-left:-5px}.h-dropdowncustom-show.h-dropdowncustom-disabled{cursor:default;color:#999999 !important}.h-dropdowncustom-show.h-dropdowncustom-disabled>.h-icon-down{color:#999999 !important}.h-dropdowncustom.h-btn{padding:0}.h-dropdowncustom.h-btn .h-dropdowncustom-show{padding:8px 15px;padding-right:0}.h-dropdowncustom.h-btn .h-dropdowncustom-show.h-dropdowncustom-empty{width:30px}.h-dropdowncustom.h-btn .h-dropdowncustom-show .h-icon-down{color:inherit}.h-text-dropdown{cursor:pointer}.h-text-dropdown>span{vertical-align:middle}.h-text-dropdown:hover,.h-text-dropdown.h-pop-trigger{color:#45b984}.h-tabs>.h-tabs-item{cursor:pointer;display:inline-block;transition:.2s all;position:relative}.h-tabs-default{padding:0 20px;border-bottom:1px solid #eeeeee;line-height:1;font-size:15px}.h-tabs-default>.h-tabs-item{padding:10px 15px;margin-bottom:-4px;border-bottom:3px solid transparent}.h-tabs-default>.h-tabs-item+div{margin-left:20px}.h-tabs-default>.h-tabs-item:hover,.h-tabs-default>.h-tabs-item.h-tabs-item-selected{color:#45b984;border-bottom:3px solid #45b984}.h-tabs-card{border-bottom:1px solid #d3d3d3;line-height:1}.h-tabs-card>.h-tabs-item{padding:8px 16px;margin-bottom:-1px;border-radius:4px 4px 0 0;border:1px solid transparent;border-top:3px solid transparent}.h-tabs-card>.h-tabs-item+div{margin-left:5px}.h-tabs-card>.h-tabs-item:hover,.h-tabs-card>.h-tabs-item.h-tabs-item-selected{color:#45b984;background:#ffffff;border-color:#d3d3d3;border-top-color:#45b984;border-bottom-color:#ffffff}.h-tabs-menu{line-height:1;font-size:15px}.h-tabs-menu>.h-tabs-item{display:block;padding:12px 16px}.h-tabs-menu>.h-tabs-item+div{margin-top:5px}.h-tabs-menu>.h-tabs-item:hover,.h-tabs-menu>.h-tabs-item.h-tabs-item-selected{color:#45b984}input.h-input,.h-textarea{min-width:1px;position:relative;background-color:#ffffff;border-radius:4px;border:1px solid #eeeeee;display:inline-block;padding:4px 7.5px;line-height:1.5;font-size:13px;transition:all linear .2s;box-sizing:border-box;-webkit-appearance:none;font-family:inherit;vertical-align:top;outline:none}input.h-input:hover,.h-textarea:hover{border-color:#6ac79d}input.h-input:focus,.h-textarea:focus{border-color:#45b984;box-shadow:0 0 0 2px rgba(69,185,132,0.2)}input.h-input[disabled],.h-textarea[disabled],input.h-input[readonly],.h-textarea[readonly]{border-color:#d3d3d3}input.h-input[disabled]:focus,.h-textarea[disabled]:focus,input.h-input[readonly]:focus,.h-textarea[readonly]:focus{box-shadow:none}input.h-input[readonly],.h-textarea[readonly]{color:#555555;box-shadow:none;cursor:default}input.h-input[disabled],.h-textarea[disabled]{color:#999999;background-color:#f5f5f5;pointer-events:painted;cursor:not-allowed}.h-form-item-valid-error input.h-input,.h-form-item-valid-error .h-textarea{border-color:#E11617}.h-form-item-valid-error input.h-input:focus,.h-form-item-valid-error .h-textarea:focus{box-shadow:0 0 0 2px rgba(225,22,23,0.2)}input.h-input{height:30px}.h-textarea{padding:7.5px}div.h-input{position:relative;display:inline-block}div.h-input>[class^="h-icon"],div.h-input>[class*=" h-icon"]{position:absolute;top:50%;right:8px;font-size:13px;z-index:3;transform:translate(0%, -50%)}div.h-input>.h-icon-close{color:#666666;transform-origin:center;cursor:pointer;transition:color .2s ease;transform:translate(0%, -50%) scale(.9)}div.h-input>.h-icon-close:hover{color:#45b984 !important}div.h-input>.h-icon-close:active{color:#30815c !important}div.h-input>.h-icon-close.h-pop-trigger div.h-input>.h-icon-close{color:#379469 !important}div.h-input>.h-icon-close[disabled]{color:#999999 !important;cursor:default}div.h-input>input{width:100%;position:relative;z-index:2}div.h-input.h-input-prefix-icon>[class^="h-icon"],div.h-input.h-input-prefix-icon>[class*=" h-icon"]{right:auto;left:8px}div.h-input.h-input-prefix-icon>input{padding-left:25px}div.h-input.h-input-suffix-icon>input{padding-right:25px}div.h-input-group{position:relative;display:flex}div.h-input-group>.h-btn{padding-top:7px;padding-bottom:7px;border-top-left-radius:0;border-bottom-left-radius:0}div.h-input-group>.h-numberinput>.h-numberinput-show,div.h-input-group>.h-btn,div.h-input-group>.h-select>.h-select-show{border-top-left-radius:0;border-bottom-left-radius:0}div.h-input-group>.h-numberinput:not(:last-child)>.h-numberinput-show,div.h-input-group>.h-select:not(:last-child)>.h-select-show{border-top-right-radius:0;border-bottom-right-radius:0}div.h-input-group>input,div.h-input-group>div{position:relative;z-index:2;margin:0;flex:1}div.h-input-group>.h-select{overflow:hidden}div.h-input-group>div{padding:0 !important}div.h-input-group>input:not(:last-child),div.h-input-group>div:not(:last-child) input{border-top-right-radius:0;border-bottom-right-radius:0}div.h-input-group>span{line-height:28px}div.h-input-group>.h-input-addon{line-height:28px;flex:inherit;text-align:center;background-color:#f3f3f3;border-top:1px solid #eeeeee;border-bottom:1px solid #eeeeee;height:30px;vertical-align:middle;padding:0px 5px;min-width:24px;white-space:nowrap;color:#999999;display:inline-block}div.h-input-group>.h-input-addon:first-child{border-radius:4px 0 0 4px;border-left:1px solid #eeeeee}div.h-input-group>.h-input-addon:last-child,div.h-input-group>.h-input-addon.last-child{border-radius:0 4px 4px 0;border-right:1px solid #eeeeee}div.h-input-group>.h-input-addon+input,div.h-input-group>.h-input-addon+div input{border-top-left-radius:0;border-bottom-left-radius:0}div.h-input-group>.h-input-addon>.h-select{display:block}div.h-input-group>.h-input-addon>.h-select .h-select-placeholder,div.h-input-group>.h-input-addon>.h-select .h-select-value-single{height:28px;line-height:28px}.h-btn{border:none;outline:none;padding:8px 15px;font-family:inherit;display:inline-block;border-radius:4px;color:inherit;font-size:13px;line-height:1;box-sizing:border-box;cursor:pointer;transition:all .2s;background-color:transparent;border:1px solid #d3d3d3;box-shadow:0 1px 1px 0 #eeeeee;background-color:#FFF}.h-btn:hover{border-color:#7ccea9;color:#7ccea9}.h-btn:active{border-color:#30815c;color:#30815c}.h-btn>i+span{margin-left:5px}.h-btn>span+i{margin-left:5px}.h-btn.h-btn-primary{background-color:#45b984;border-color:#42b27f;color:#ffffff}.h-btn.h-btn-primary:hover{border-color:#6ac79d;background-color:#6ac79d}.h-btn.h-btn-primary:active{border-color:#3a9b6f;background-color:#3a9b6f}.h-btn.h-btn-red{background-color:#E11617;border-color:#d81516;color:#ffffff}.h-btn.h-btn-red:hover{border-color:#ec3e3f;background-color:#ec3e3f}.h-btn.h-btn-red:active{border-color:#bc1213;background-color:#bc1213}.h-btn.h-btn-gray{background-color:#c1c1c1;border-color:#bcbcbc;color:#ffffff}.h-btn.h-btn-gray:hover{border-color:#dbdbdb;background-color:#dbdbdb}.h-btn.h-btn-gray:active{border-color:#adadad;background-color:#adadad}.h-btn.h-btn-yellow{background-color:#FFAE00;border-color:#f5a700;color:#ffffff}.h-btn.h-btn-yellow:hover{border-color:#ffbe33;background-color:#ffbe33}.h-btn.h-btn-yellow:active{border-color:#d69200;background-color:#d69200}.h-btn.h-btn-green{background-color:#13CE66;border-color:#12c561;color:#ffffff}.h-btn.h-btn-green:hover{border-color:#15e572;background-color:#15e572}.h-btn.h-btn-green:active{border-color:#10a954;background-color:#10a954}.h-btn.h-btn-blue{background-color:#3B91FF;border-color:#318bff;color:#ffffff}.h-btn.h-btn-blue:hover{border-color:#6eaeff;background-color:#6eaeff}.h-btn.h-btn-blue:active{border-color:#127aff;background-color:#127aff}.h-btn.h-btn-text{box-shadow:none;border-color:transparent}.h-btn.h-btn-text-primary{border-color:#45b984;color:#45b984}.h-btn.h-btn-text-primary:hover{border-color:#7ccea9;color:#7ccea9}.h-btn.h-btn-text-primary:active{border-color:#30815c;color:#30815c}.h-btn.h-btn-text-red{border-color:#E11617;color:#E11617}.h-btn.h-btn-text-red:hover{border-color:#ee5556;color:#ee5556}.h-btn.h-btn-text-red:active{border-color:#9b0f10;color:#9b0f10}.h-btn.h-btn-text-gray{border-color:#c1c1c1;color:#c1c1c1}.h-btn.h-btn-text-gray:hover{border-color:#e7e7e7;color:#e7e7e7}.h-btn.h-btn-text-gray:active{border-color:#9b9b9b;color:#9b9b9b}.h-btn.h-btn-text-yellow{border-color:#FFAE00;color:#FFAE00}.h-btn.h-btn-text-yellow:hover{border-color:#ffc64d;color:#ffc64d}.h-btn.h-btn-text-yellow:active{border-color:#b37a00;color:#b37a00}.h-btn.h-btn-text-green{border-color:#13CE66;color:#13CE66}.h-btn.h-btn-text-green:hover{border-color:#40ed8d;color:#40ed8d}.h-btn.h-btn-text-green:active{border-color:#0d8843;color:#0d8843}.h-btn.h-btn-text-blue{border-color:#3B91FF;color:#3B91FF}.h-btn.h-btn-text-blue:hover{border-color:#88bcff;color:#88bcff}.h-btn.h-btn-text-blue:active{border-color:#0068ee;color:#0068ee}.h-btn.h-btn-no-border{box-shadow:none;border-color:transparent !important}.h-btn.h-btn-circle{border-radius:20px}.h-btn.h-btn-icon-circle{border-radius:20px;padding:8px}.h-btn.h-btn-loading{pointer-events:none;opacity:.8}.h-btn.h-btn-block{display:block;width:100%}.h-btn.h-btn-transparent{background:transparent}.h-btn[disabled]{cursor:not-allowed;background-color:#f5f5f5;border-color:#d3d3d3;color:#999999}.h-btn[disabled]:hover{background-color:#f5f5f5;border-color:#d3d3d3;color:#999999}.h-btn[disabled].h-btn-text{border-color:transparent;background-color:transparent}.h-btn+.h-btn{margin-left:4px}.h-btn.h-btn-l{padding:10px 20px}.h-btn.h-btn-m{padding:7px 16px}.h-btn.h-btn-s{padding:5px 10px}.h-btn.h-btn-xs{padding:2px 6px}.h-btn-group{display:inline-block;vertical-align:middle}.h-btn-group .h-btn{border-radius:0;float:left;position:relative}.h-btn-group .h-btn+.h-btn{margin-left:-1px}.h-btn-group .h-btn:hover{z-index:2}.h-btn-group .h-btn:first-child{border-top-left-radius:4px;border-bottom-left-radius:4px}.h-btn-group .h-btn:last-child{border-top-right-radius:4px;border-bottom-right-radius:4px}.h-btn-group.h-btn-group-circle>:first-of-type{border-top-left-radius:20px;border-bottom-left-radius:20px}.h-btn-group.h-btn-group-circle>:last-of-type{border-bottom-right-radius:20px;border-top-right-radius:20px}.h-btn-group.h-btn-group .h-btn[disabled]{z-index:-1}.h-btn-group.h-btn-group-l .h-btn{font-size:110%;padding:10px 20px}.h-btn-group.h-btn-group-s .h-btn{padding:5px 8px}.h-btn-group.h-btn-group-xs .h-btn{padding:2px 6px;font-size:90%}.h-col-24{float:left;display:block;box-sizing:border-box;width:100%}.h-col-23{float:left;display:block;box-sizing:border-box;width:95.83333333%}.h-col-22{float:left;display:block;box-sizing:border-box;width:91.66666667%}.h-col-21{float:left;display:block;box-sizing:border-box;width:87.5%}.h-col-20{float:left;display:block;box-sizing:border-box;width:83.33333333%}.h-col-19{float:left;display:block;box-sizing:border-box;width:79.16666667%}.h-col-18{float:left;display:block;box-sizing:border-box;width:75%}.h-col-17{float:left;display:block;box-sizing:border-box;width:70.83333333%}.h-col-16{float:left;display:block;box-sizing:border-box;width:66.66666667%}.h-col-15{float:left;display:block;box-sizing:border-box;width:62.5%}.h-col-14{float:left;display:block;box-sizing:border-box;width:58.33333333%}.h-col-13{float:left;display:block;box-sizing:border-box;width:54.16666667%}.h-col-12{float:left;display:block;box-sizing:border-box;width:50%}.h-col-11{float:left;display:block;box-sizing:border-box;width:45.83333333%}.h-col-10{float:left;display:block;box-sizing:border-box;width:41.66666667%}.h-col-9{float:left;display:block;box-sizing:border-box;width:37.5%}.h-col-8{float:left;display:block;box-sizing:border-box;width:33.33333333%}.h-col-7{float:left;display:block;box-sizing:border-box;width:29.16666667%}.h-col-6{float:left;display:block;box-sizing:border-box;width:25%}.h-col-5{float:left;display:block;box-sizing:border-box;width:20.83333333%}.h-col-4{float:left;display:block;box-sizing:border-box;width:16.66666667%}.h-col-3{float:left;display:block;box-sizing:border-box;width:12.5%}.h-col-2{float:left;display:block;box-sizing:border-box;width:8.33333333%}.h-col-1{float:left;display:block;box-sizing:border-box;width:4.16666667%}@media (min-width:1800px){.h-col-xl-24{float:left;display:block;box-sizing:border-box;width:100%}.h-col-xl-23{float:left;display:block;box-sizing:border-box;width:95.83333333%}.h-col-xl-22{float:left;display:block;box-sizing:border-box;width:91.66666667%}.h-col-xl-21{float:left;display:block;box-sizing:border-box;width:87.5%}.h-col-xl-20{float:left;display:block;box-sizing:border-box;width:83.33333333%}.h-col-xl-19{float:left;display:block;box-sizing:border-box;width:79.16666667%}.h-col-xl-18{float:left;display:block;box-sizing:border-box;width:75%}.h-col-xl-17{float:left;display:block;box-sizing:border-box;width:70.83333333%}.h-col-xl-16{float:left;display:block;box-sizing:border-box;width:66.66666667%}.h-col-xl-15{float:left;display:block;box-sizing:border-box;width:62.5%}.h-col-xl-14{float:left;display:block;box-sizing:border-box;width:58.33333333%}.h-col-xl-13{float:left;display:block;box-sizing:border-box;width:54.16666667%}.h-col-xl-12{float:left;display:block;box-sizing:border-box;width:50%}.h-col-xl-11{float:left;display:block;box-sizing:border-box;width:45.83333333%}.h-col-xl-10{float:left;display:block;box-sizing:border-box;width:41.66666667%}.h-col-xl-9{float:left;display:block;box-sizing:border-box;width:37.5%}.h-col-xl-8{float:left;display:block;box-sizing:border-box;width:33.33333333%}.h-col-xl-7{float:left;display:block;box-sizing:border-box;width:29.16666667%}.h-col-xl-6{float:left;display:block;box-sizing:border-box;width:25%}.h-col-xl-5{float:left;display:block;box-sizing:border-box;width:20.83333333%}.h-col-xl-4{float:left;display:block;box-sizing:border-box;width:16.66666667%}.h-col-xl-3{float:left;display:block;box-sizing:border-box;width:12.5%}.h-col-xl-2{float:left;display:block;box-sizing:border-box;width:8.33333333%}.h-col-xl-1{float:left;display:block;box-sizing:border-box;width:4.16666667%}}@media (min-width:1200px) and (max-width:1800px){.h-col-lg-24{float:left;display:block;box-sizing:border-box;width:100%}.h-col-lg-23{float:left;display:block;box-sizing:border-box;width:95.83333333%}.h-col-lg-22{float:left;display:block;box-sizing:border-box;width:91.66666667%}.h-col-lg-21{float:left;display:block;box-sizing:border-box;width:87.5%}.h-col-lg-20{float:left;display:block;box-sizing:border-box;width:83.33333333%}.h-col-lg-19{float:left;display:block;box-sizing:border-box;width:79.16666667%}.h-col-lg-18{float:left;display:block;box-sizing:border-box;width:75%}.h-col-lg-17{float:left;display:block;box-sizing:border-box;width:70.83333333%}.h-col-lg-16{float:left;display:block;box-sizing:border-box;width:66.66666667%}.h-col-lg-15{float:left;display:block;box-sizing:border-box;width:62.5%}.h-col-lg-14{float:left;display:block;box-sizing:border-box;width:58.33333333%}.h-col-lg-13{float:left;display:block;box-sizing:border-box;width:54.16666667%}.h-col-lg-12{float:left;display:block;box-sizing:border-box;width:50%}.h-col-lg-11{float:left;display:block;box-sizing:border-box;width:45.83333333%}.h-col-lg-10{float:left;display:block;box-sizing:border-box;width:41.66666667%}.h-col-lg-9{float:left;display:block;box-sizing:border-box;width:37.5%}.h-col-lg-8{float:left;display:block;box-sizing:border-box;width:33.33333333%}.h-col-lg-7{float:left;display:block;box-sizing:border-box;width:29.16666667%}.h-col-lg-6{float:left;display:block;box-sizing:border-box;width:25%}.h-col-lg-5{float:left;display:block;box-sizing:border-box;width:20.83333333%}.h-col-lg-4{float:left;display:block;box-sizing:border-box;width:16.66666667%}.h-col-lg-3{float:left;display:block;box-sizing:border-box;width:12.5%}.h-col-lg-2{float:left;display:block;box-sizing:border-box;width:8.33333333%}.h-col-lg-1{float:left;display:block;box-sizing:border-box;width:4.16666667%}}@media (min-width:992px) and (max-width:1200px){.h-col-md-24{float:left;display:block;box-sizing:border-box;width:100%}.h-col-md-23{float:left;display:block;box-sizing:border-box;width:95.83333333%}.h-col-md-22{float:left;display:block;box-sizing:border-box;width:91.66666667%}.h-col-md-21{float:left;display:block;box-sizing:border-box;width:87.5%}.h-col-md-20{float:left;display:block;box-sizing:border-box;width:83.33333333%}.h-col-md-19{float:left;display:block;box-sizing:border-box;width:79.16666667%}.h-col-md-18{float:left;display:block;box-sizing:border-box;width:75%}.h-col-md-17{float:left;display:block;box-sizing:border-box;width:70.83333333%}.h-col-md-16{float:left;display:block;box-sizing:border-box;width:66.66666667%}.h-col-md-15{float:left;display:block;box-sizing:border-box;width:62.5%}.h-col-md-14{float:left;display:block;box-sizing:border-box;width:58.33333333%}.h-col-md-13{float:left;display:block;box-sizing:border-box;width:54.16666667%}.h-col-md-12{float:left;display:block;box-sizing:border-box;width:50%}.h-col-md-11{float:left;display:block;box-sizing:border-box;width:45.83333333%}.h-col-md-10{float:left;display:block;box-sizing:border-box;width:41.66666667%}.h-col-md-9{float:left;display:block;box-sizing:border-box;width:37.5%}.h-col-md-8{float:left;display:block;box-sizing:border-box;width:33.33333333%}.h-col-md-7{float:left;display:block;box-sizing:border-box;width:29.16666667%}.h-col-md-6{float:left;display:block;box-sizing:border-box;width:25%}.h-col-md-5{float:left;display:block;box-sizing:border-box;width:20.83333333%}.h-col-md-4{float:left;display:block;box-sizing:border-box;width:16.66666667%}.h-col-md-3{float:left;display:block;box-sizing:border-box;width:12.5%}.h-col-md-2{float:left;display:block;box-sizing:border-box;width:8.33333333%}.h-col-md-1{float:left;display:block;box-sizing:border-box;width:4.16666667%}}@media (min-width:768px) and (max-width:992px){.h-col-sm-24{float:left;display:block;box-sizing:border-box;width:100%}.h-col-sm-23{float:left;display:block;box-sizing:border-box;width:95.83333333%}.h-col-sm-22{float:left;display:block;box-sizing:border-box;width:91.66666667%}.h-col-sm-21{float:left;display:block;box-sizing:border-box;width:87.5%}.h-col-sm-20{float:left;display:block;box-sizing:border-box;width:83.33333333%}.h-col-sm-19{float:left;display:block;box-sizing:border-box;width:79.16666667%}.h-col-sm-18{float:left;display:block;box-sizing:border-box;width:75%}.h-col-sm-17{float:left;display:block;box-sizing:border-box;width:70.83333333%}.h-col-sm-16{float:left;display:block;box-sizing:border-box;width:66.66666667%}.h-col-sm-15{float:left;display:block;box-sizing:border-box;width:62.5%}.h-col-sm-14{float:left;display:block;box-sizing:border-box;width:58.33333333%}.h-col-sm-13{float:left;display:block;box-sizing:border-box;width:54.16666667%}.h-col-sm-12{float:left;display:block;box-sizing:border-box;width:50%}.h-col-sm-11{float:left;display:block;box-sizing:border-box;width:45.83333333%}.h-col-sm-10{float:left;display:block;box-sizing:border-box;width:41.66666667%}.h-col-sm-9{float:left;display:block;box-sizing:border-box;width:37.5%}.h-col-sm-8{float:left;display:block;box-sizing:border-box;width:33.33333333%}.h-col-sm-7{float:left;display:block;box-sizing:border-box;width:29.16666667%}.h-col-sm-6{float:left;display:block;box-sizing:border-box;width:25%}.h-col-sm-5{float:left;display:block;box-sizing:border-box;width:20.83333333%}.h-col-sm-4{float:left;display:block;box-sizing:border-box;width:16.66666667%}.h-col-sm-3{float:left;display:block;box-sizing:border-box;width:12.5%}.h-col-sm-2{float:left;display:block;box-sizing:border-box;width:8.33333333%}.h-col-sm-1{float:left;display:block;box-sizing:border-box;width:4.16666667%}}@media (max-width:768px){.h-col-xs-24{float:left;display:block;box-sizing:border-box;width:100%}.h-col-xs-23{float:left;display:block;box-sizing:border-box;width:95.83333333%}.h-col-xs-22{float:left;display:block;box-sizing:border-box;width:91.66666667%}.h-col-xs-21{float:left;display:block;box-sizing:border-box;width:87.5%}.h-col-xs-20{float:left;display:block;box-sizing:border-box;width:83.33333333%}.h-col-xs-19{float:left;display:block;box-sizing:border-box;width:79.16666667%}.h-col-xs-18{float:left;display:block;box-sizing:border-box;width:75%}.h-col-xs-17{float:left;display:block;box-sizing:border-box;width:70.83333333%}.h-col-xs-16{float:left;display:block;box-sizing:border-box;width:66.66666667%}.h-col-xs-15{float:left;display:block;box-sizing:border-box;width:62.5%}.h-col-xs-14{float:left;display:block;box-sizing:border-box;width:58.33333333%}.h-col-xs-13{float:left;display:block;box-sizing:border-box;width:54.16666667%}.h-col-xs-12{float:left;display:block;box-sizing:border-box;width:50%}.h-col-xs-11{float:left;display:block;box-sizing:border-box;width:45.83333333%}.h-col-xs-10{float:left;display:block;box-sizing:border-box;width:41.66666667%}.h-col-xs-9{float:left;display:block;box-sizing:border-box;width:37.5%}.h-col-xs-8{float:left;display:block;box-sizing:border-box;width:33.33333333%}.h-col-xs-7{float:left;display:block;box-sizing:border-box;width:29.16666667%}.h-col-xs-6{float:left;display:block;box-sizing:border-box;width:25%}.h-col-xs-5{float:left;display:block;box-sizing:border-box;width:20.83333333%}.h-col-xs-4{float:left;display:block;box-sizing:border-box;width:16.66666667%}.h-col-xs-3{float:left;display:block;box-sizing:border-box;width:12.5%}.h-col-xs-2{float:left;display:block;box-sizing:border-box;width:8.33333333%}.h-col-xs-1{float:left;display:block;box-sizing:border-box;width:4.16666667%}}.h-row{display:block}.h-row::after{content:"";display:block;clear:both}.h-row-clear{float:left;width:100%}.h-row-flex{display:flex;flex-direction:row;flex-wrap:wrap}.h-row-flex:before,.h-row-flex:after{display:flex}.h-row-flex-start{justify-content:flex-start}.h-row-flex-center{justify-content:center}.h-row-flex-end{justify-content:flex-end}.h-row-flex-space-between{justify-content:space-between}.h-row-flex-space-around{justify-content:space-around}.h-row-flex-top{align-items:flex-start}.h-row-flex-middle{align-items:center}.h-row-flex-bottom{align-items:flex-end}.h-row-flex-row-reverse{flex-direction:row-reverse}.h-row-flex-column{flex-direction:column}.h-row-flex-column-reverse{flex-direction:column-reverse}.h-wordcount{position:absolute;right:0;top:100%;margin:0}.h-affix{position:fixed;z-index:10}.h-affix-absolute{position:absolute}.h-autocomplete .h-autocomplete-show{min-height:30px;display:flex;flex-wrap:wrap;cursor:default;background-color:#ffffff;border-radius:4px;border:1px solid #eeeeee;vertical-align:top;transition:all linear .2s;padding:2px 2px 0px 2px;font-size:13px;position:relative;box-sizing:border-box;padding-right:20px}.h-autocomplete .h-autocomplete-show:hover{border-color:#6ac79d}.h-autocomplete .h-autocomplete-show.h-pop-trigger,.h-autocomplete .h-autocomplete-show.focusing{border-color:#45b984;box-shadow:0 0 0 2px rgba(69,185,132,0.2)}.h-form-item-valid-error .h-autocomplete .h-autocomplete-show{border-color:#E11617}.h-form-item-valid-error .h-autocomplete .h-autocomplete-show:focus,.h-form-item-valid-error .h-autocomplete .h-autocomplete-show.focusing{box-shadow:0 0 0 2px rgba(225,22,23,0.2)}.h-autocomplete .h-autocomplete-show .h-icon-loading{color:#999999}.h-autocomplete .h-autocomplete-show .h-icon-loading,.h-autocomplete .h-autocomplete-show>.h-icon-close{position:absolute;top:50%;right:8px;font-size:12px;z-index:3;cursor:pointer;transition:color .2s ease;color:#666666;margin-top:-6px}.h-autocomplete .h-autocomplete-show .h-icon-loading:hover,.h-autocomplete .h-autocomplete-show>.h-icon-close:hover{color:#45b984 !important}.h-autocomplete .h-autocomplete-show .h-icon-loading:active,.h-autocomplete .h-autocomplete-show>.h-icon-close:active{color:#30815c !important}.h-autocomplete .h-autocomplete-show .h-icon-loading.h-pop-trigger .h-autocomplete .h-autocomplete-show .h-icon-loading,.h-autocomplete .h-autocomplete-show .h-icon-loading.h-pop-trigger .h-autocomplete .h-autocomplete-show>.h-icon-close,.h-autocomplete .h-autocomplete-show>.h-icon-close.h-pop-trigger .h-autocomplete .h-autocomplete-show .h-icon-loading,.h-autocomplete .h-autocomplete-show>.h-icon-close.h-pop-trigger .h-autocomplete .h-autocomplete-show>.h-icon-close{color:#379469 !important}.h-autocomplete .h-autocomplete-show .h-icon-loading[disabled],.h-autocomplete .h-autocomplete-show>.h-icon-close[disabled]{color:#999999 !important;cursor:default}.h-autocomplete .h-autocomplete-show>span{margin-bottom:2px;display:inline-block;border-radius:2px;transition:all linear .2s;margin-right:5px;border:1px solid #eeeeee;background-color:#f5f5f5;font-size:13px;padding:0px 8px}.h-autocomplete .h-autocomplete-show>span>span{line-height:22px}.h-autocomplete .h-autocomplete-show>span>.h-icon-close-min{font-size:16px;color:#666666;display:inline-block;width:14px;margin-left:2px;vertical-align:-2px;cursor:pointer;transition:color .2s ease}.h-autocomplete .h-autocomplete-show>span>.h-icon-close-min:hover{color:#45b984 !important}.h-autocomplete .h-autocomplete-show>span>.h-icon-close-min:active{color:#30815c !important}.h-autocomplete .h-autocomplete-show>span>.h-icon-close-min.h-pop-trigger .h-autocomplete .h-autocomplete-show>span>.h-icon-close-min{color:#379469 !important}.h-autocomplete .h-autocomplete-show>span>.h-icon-close-min[disabled]{color:#999999 !important;cursor:default}.h-autocomplete .h-autocomplete-show>span>.h-icon-close{line-height:22px;display:inline-block;font-size:12px;color:#666666;margin-left:5px;cursor:pointer;transition:color .2s ease}.h-autocomplete .h-autocomplete-show>span>.h-icon-close:hover{color:#45b984 !important}.h-autocomplete .h-autocomplete-show>span>.h-icon-close:active{color:#30815c !important}.h-autocomplete .h-autocomplete-show>span>.h-icon-close.h-pop-trigger .h-autocomplete .h-autocomplete-show>span>.h-icon-close{color:#379469 !important}.h-autocomplete .h-autocomplete-show>span>.h-icon-close[disabled]{color:#999999 !important;cursor:default}.h-autocomplete .h-autocomplete-input{flex:1;min-width:80px;width:auto;max-width:100%;border-color:transparent !important;line-height:13px;font-size:13px;height:24px;margin-bottom:2px;padding-left:5px}.h-autocomplete .h-autocomplete-input:focus{box-shadow:none !important}.h-autocomplete-disabled .h-autocomplete-show{background-color:#f5f5f5;color:#999999 !important;border-color:#d3d3d3}.h-autocomplete-disabled .h-autocomplete-show:hover{border-color:#d3d3d3;color:#999999 !important}.h-autocomplete-disabled .h-autocomplete-show .h-icon-down{color:#999999 !important}.h-autocomplete-ul{list-style:none;margin:0;padding:0;outline:none;box-sizing:border-box}.h-autocomplete-group{outline:none;user-select:none;position:relative;font-size:13px;background-color:#ffffff;padding:5px 0px;max-height:300px;overflow-y:auto}.h-autocomplete-group .h-autocomplete-item{cursor:pointer;padding:8px 10px;position:relative}.h-autocomplete-group .h-autocomplete-item:hover{background-color:#eeeeee}.h-autocomplete-group .h-autocomplete-item.disabled{cursor:default}.h-autocomplete-group .h-autocomplete-item.disabled:hover{background-color:transparent}.h-autocomplete-group .h-autocomplete-item.h-autocomplete-item-selected{background-color:#eeeeee}.h-autocomplete-group .h-autocomplete-empty-content{color:#999999;text-align:center}.h-avatar{position:relative;width:100%}.h-avatar-image-container{position:absolute}.h-avatar-image-container.h-avatar-middle{position:absolute;top:50%;transform:translate(0%, -50%)}.h-avatar-image-container .h-avatar-image{position:absolute;z-index:1;background-size:cover;background-position:center;height:100%;width:100%}.h-avatar.h-avatar-shape-circle .h-avatar-image{border-radius:50%}.h-avatar.h-avatar-shape-square .h-avatar-image{border-radius:4px}.h-avatar.h-avatar-no-info{display:inline-block}.h-avatar.h-avatar-fit-contain .h-avatar-image-container .h-avatar-image{background-size:contain}.h-avatar.h-avatar-fit-cover .h-avatar-image-container .h-avatar-image{background-size:cover}.h-avatar.h-avatar-fit-fill .h-avatar-image-container .h-avatar-image{background-size:100% 100%}.h-avatar.h-avatar-fit-none .h-avatar-image-container .h-avatar-image{background-size:initial}.h-backtop{z-index:10;position:fixed;cursor:pointer;display:none;right:50px;bottom:50px;animation:h-ani-backtop-hide .2s linear}.h-backtop.h-backtop-show{display:block;animation:h-ani-backtop-show .2s linear}.h-backtop-default .h-backtop-inner{background-color:rgba(0,0,0,0.6);border-radius:2px;box-shadow:0 1px 3px rgba(0,0,0,0.2);transition:all .2s ease-in-out}.h-backtop-default .h-backtop-inner:hover{background-color:rgba(0,0,0,0.7)}.h-backtop-default i{color:#fff;font-size:24px;padding:8px 12px}@keyframes h-ani-backtop-show{from{opacity:0}50%{opacity:.5}to{opacity:1}}@keyframes h-ani-backtop-hide{from{opacity:1}50%{opacity:.5}to{opacity:0}}.h-badge{position:relative;display:inline-block;vertical-align:middle;line-height:1}.h-badge-count{position:absolute;transform:scale(0);top:-10px;left:100%;margin-left:-5px;border-radius:100px;background:#E11617;color:#fff;line-height:18px;text-align:center;font-size:12px;white-space:nowrap;z-index:10;transition:.2s cubic-bezier(.24, .71, .73, 1.19);min-width:18px;height:18px;padding:0 4px}.h-badge-count-show{transform:scale(1)}.h-badge-count-dot{height:6px;width:6px;min-width:0;padding:0;top:-3px;left:auto;right:-3px}.h-badge-count a,.h-badge-count a:hover{color:#fff}.h-badge-count-alone{top:auto;display:block;position:relative;transform:translateX(0)}.h-badge-position-right{position:absolute;right:0;top:50%;transform:translateY(-50%)}.h-badge-position-right .h-badge-count{right:10px;left:auto}.h-breadcrumb-item-link{cursor:pointer;transition:color .2s ease}.h-breadcrumb-item-link:hover{color:#45b984 !important}.h-breadcrumb-item-link:active{color:#30815c !important}.h-breadcrumb-item-link.h-pop-trigger .h-breadcrumb-item-link{color:#379469 !important}.h-breadcrumb-item-link[disabled]{color:#999999 !important;cursor:default}.h-breadcrumb-item-separator{margin:0 8px;color:#d3d3d3}.h-breadcrumb-item-icon{vertical-align:middle;margin-right:4px}.h-breadcrumb-item-current{font-weight:bold}.h-carousel{position:relative}.h-carousel .h-carousel-container{position:relative;width:100%;height:100%;overflow:hidden;z-index:1}.h-carousel .h-carousel-list{position:relative;width:100%;height:100%;z-index:1;display:flex;transition-property:transform;box-sizing:content-box}.h-carousel .h-carousel-list .h-carousel-item{background-position:center;background-repeat:no-repeat;flex-shrink:0;width:100%;height:100%;position:relative}.h-carousel .h-carousel-list .h-carousel-item .h-carousel-bg{height:100%;background-position:center;background-size:cover}.h-carousel .h-carousel-list .h-carousel-item .h-carousel-bg-pointer{cursor:pointer}.h-carousel .h-carousel-list .h-carousel-item.h-carousel-effect-item{position:absolute}.h-carousel .h-carousel-arrow{position:absolute;width:100%;height:100%;top:0;left:0;transition:.3s}.h-carousel .h-carousel-arrow .h-icon-left,.h-carousel .h-carousel-arrow .h-icon-right{z-index:2;color:#fff;font-size:32px;position:absolute;top:50%;transform:translateY(-50%);cursor:pointer;opacity:.4;transition:.5s}.h-carousel .h-carousel-arrow .h-icon-left:hover,.h-carousel .h-carousel-arrow .h-icon-right:hover{opacity:1}.h-carousel .h-carousel-arrow .h-icon-left{left:2%}.h-carousel .h-carousel-arrow .h-icon-right{right:2%}.h-carousel .h-carousel-arrow.h-carousel-arrow-hover{opacity:0}.h-carousel:hover .h-carousel-arrow-hover{opacity:1}.h-carousel .h-carousel-arrow-hidden{display:none !important}.h-carousel-pagination{z-index:3;position:relative}.h-carousel-pagination-circle .h-carousel-pagination-item,.h-carousel-pagination-square .h-carousel-pagination-item{opacity:.4;display:inline-block;margin-right:10px;cursor:pointer;position:relative;height:15px;transition:.5s}.h-carousel-pagination-circle .h-carousel-pagination-item>span,.h-carousel-pagination-square .h-carousel-pagination-item>span{display:inline-block;border-radius:4px;background-color:#fff}.h-carousel-pagination-circle .h-carousel-pagination-item.active,.h-carousel-pagination-square .h-carousel-pagination-item.active{opacity:1}.h-carousel-pagination-circle .h-carousel-pagination-item:last-of-type,.h-carousel-pagination-square .h-carousel-pagination-item:last-of-type{margin-right:0}.h-carousel-pagination-circle,.h-carousel-pagination-square{position:absolute;bottom:5%;left:50%;transform:translateX(-50%);z-index:2}.h-carousel-pagination-circle .h-carousel-pagination-item>span{width:8px;height:8px}.h-carousel-pagination-square .h-carousel-pagination-item>span{width:25px;height:3px}.h-carousel-pagination-hidden{display:none}.h-carousel-effect-fade-enter-active,.h-carousel-effect-fade-leave-active{transition:opacity 1s}.h-carousel-effect-fade-enter,.h-carousel-effect-fade-leave-to{opacity:0}.h-categorypicker{display:inline-block;box-sizing:border-box;vertical-align:middle}.h-categorypicker-placeholder{color:#787878}.h-categorypicker-value-single,.h-categorypicker-placeholder{line-height:24px;height:24px;padding-left:5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.h-categorypicker-show{padding-right:25px !important;position:relative;cursor:default;line-height:1.5}.h-categorypicker-show>.h-icon-down{color:#666666;position:absolute;right:8px;top:50%;height:10px;font-size:12px;line-height:12px;transform:translate(0, -50%) scale(.8);transition:transform .2s}.h-categorypicker-show:hover>.h-icon-down{color:#45b984}.h-categorypicker-show.h-pop-trigger>.h-icon-down{transform:rotate(-180deg) translate(0, 50%) scale(.8);color:#45b984}.h-categorypicker-multiple-tags{display:flex;flex-wrap:wrap}.h-categorypicker-multiple-tags>span{margin-bottom:2px;display:inline-block;border-radius:2px;transition:all linear .2s;margin-right:5px;border:1px solid #eeeeee;background-color:#f5f5f5;font-size:13px;padding:0px 8px}.h-categorypicker-multiple-tags>span>span{line-height:22px}.h-categorypicker-multiple-tags>span>.h-icon-close-min{font-size:16px;color:#666666;display:inline-block;width:14px;margin-left:2px;vertical-align:-2px;cursor:pointer;transition:color .2s ease}.h-categorypicker-multiple-tags>span>.h-icon-close-min:hover{color:#45b984 !important}.h-categorypicker-multiple-tags>span>.h-icon-close-min:active{color:#30815c !important}.h-categorypicker-multiple-tags>span>.h-icon-close-min.h-pop-trigger .h-categorypicker-multiple-tags>span>.h-icon-close-min{color:#379469 !important}.h-categorypicker-multiple-tags>span>.h-icon-close-min[disabled]{color:#999999 !important;cursor:default}.h-categorypicker-multiple-tags>span>.h-icon-close{line-height:22px;display:inline-block;font-size:12px;color:#666666;margin-left:5px;cursor:pointer;transition:color .2s ease}.h-categorypicker-multiple-tags>span>.h-icon-close:hover{color:#45b984 !important}.h-categorypicker-multiple-tags>span>.h-icon-close:active{color:#30815c !important}.h-categorypicker-multiple-tags>span>.h-icon-close.h-pop-trigger .h-categorypicker-multiple-tags>span>.h-icon-close{color:#379469 !important}.h-categorypicker-multiple-tags>span>.h-icon-close[disabled]{color:#999999 !important;cursor:default}.h-categorypicker-input-border .h-categorypicker-show{min-height:30px;cursor:default;background-color:#ffffff;border-radius:4px;border:1px solid #eeeeee;vertical-align:top;transition:all linear .2s;padding:2px 2px 0px 2px;font-size:13px;position:relative;box-sizing:border-box}.h-categorypicker-input-border .h-categorypicker-show:hover{border-color:#6ac79d}.h-categorypicker-input-border .h-categorypicker-show.h-pop-trigger,.h-categorypicker-input-border .h-categorypicker-show.focusing{border-color:#45b984;box-shadow:0 0 0 2px rgba(69,185,132,0.2)}.h-form-item-valid-error .h-categorypicker-input-border .h-categorypicker-show{border-color:#E11617}.h-form-item-valid-error .h-categorypicker-input-border .h-categorypicker-show:focus,.h-form-item-valid-error .h-categorypicker-input-border .h-categorypicker-show.focusing{box-shadow:0 0 0 2px rgba(225,22,23,0.2)}.h-form-item-valid-error .h-categorypicker-input-border .h-categorypicker-show{border-color:#E11617}.h-form-item-valid-error .h-categorypicker-input-border .h-categorypicker-show.h-pop-trigger{box-shadow:0 0 0 2px rgba(225,22,23,0.2)}.h-categorypicker-input-border .h-categorypicker-value-single,.h-categorypicker-input-border .h-categorypicker-placeholder{margin-bottom:2px}.h-categorypicker-no-autosize{display:block}.h-categorypicker-disabled .h-categorypicker-show{background-color:#f5f5f5;color:#999999 !important;border-color:#d3d3d3}.h-categorypicker-disabled .h-categorypicker-show:hover{border-color:#d3d3d3;color:#999999 !important}.h-categorypicker-disabled .h-categorypicker-show .h-icon-down{color:#999999 !important}.h-categorypicker-value-single>.h-icon-close{position:absolute;top:50%;right:8px;font-size:12px;z-index:3;cursor:pointer;transition:color .2s ease;color:#666666;margin-top:-6px}.h-categorypicker-value-single>.h-icon-close:hover{color:#45b984 !important}.h-categorypicker-value-single>.h-icon-close:active{color:#30815c !important}.h-categorypicker-value-single>.h-icon-close.h-pop-trigger .h-categorypicker-value-single>.h-icon-close{color:#379469 !important}.h-categorypicker-value-single>.h-icon-close[disabled]{color:#999999 !important;cursor:default}.h-categorypicker-modal-multiple-tags{min-height:30px;padding-right:180px}.h-categorypicker-modal-multiple-tags>span{margin-bottom:2px;display:inline-block;border-radius:2px;transition:all linear .2s;margin-right:5px;border:1px solid #eeeeee;background-color:#f5f5f5;font-size:13px;padding:0px 8px}.h-categorypicker-modal-multiple-tags>span>span{line-height:22px}.h-categorypicker-modal-multiple-tags>span>.h-icon-close-min{font-size:16px;color:#666666;display:inline-block;width:14px;margin-left:2px;vertical-align:-2px;cursor:pointer;transition:color .2s ease}.h-categorypicker-modal-multiple-tags>span>.h-icon-close-min:hover{color:#45b984 !important}.h-categorypicker-modal-multiple-tags>span>.h-icon-close-min:active{color:#30815c !important}.h-categorypicker-modal-multiple-tags>span>.h-icon-close-min.h-pop-trigger .h-categorypicker-modal-multiple-tags>span>.h-icon-close-min{color:#379469 !important}.h-categorypicker-modal-multiple-tags>span>.h-icon-close-min[disabled]{color:#999999 !important;cursor:default}.h-categorypicker-modal-multiple-tags>span>.h-icon-close{line-height:22px;display:inline-block;font-size:12px;color:#666666;margin-left:5px;cursor:pointer;transition:color .2s ease}.h-categorypicker-modal-multiple-tags>span>.h-icon-close:hover{color:#45b984 !important}.h-categorypicker-modal-multiple-tags>span>.h-icon-close:active{color:#30815c !important}.h-categorypicker-modal-multiple-tags>span>.h-icon-close.h-pop-trigger .h-categorypicker-modal-multiple-tags>span>.h-icon-close{color:#379469 !important}.h-categorypicker-modal-multiple-tags>span>.h-icon-close[disabled]{color:#999999 !important;cursor:default}.h-categorypicker-ul{list-style:none;margin:0;padding:0;outline:none;box-sizing:border-box;min-width:300px;max-width:500px;padding:15px 10px}.h-categorypicker-ul .h-categorypicker-item{display:inline-block;padding:4px 10px}.h-categorypicker-ul .h-categorypicker-item .h-categorypicker-item-title{cursor:pointer}.h-categorypicker-ul .h-categorypicker-item{padding:2px 10px}.h-categorypicker-ul .h-categorypicker-item-title{padding:2px 5px;border-radius:4px;display:inline-block}.h-categorypicker-ul .h-categorypicker-item-title:hover{background:#eeeeee}.h-categorypicker-ul .h-categorypicker-item-selected .h-categorypicker-item-title{background:#45b984;color:#FFF}.h-categorypicker-ul.h-categorypicker-single-picker .h-categorypicker-item{padding:2px 5px}.h-categorypicker-tabs>.h-tabs-item{padding:10px 10px}.h-category{display:inline-block;box-sizing:border-box;vertical-align:middle}.h-category-placeholder{color:#787878}.h-category-value-single,.h-category-placeholder{line-height:24px;height:24px;padding-left:5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.h-category-show{padding-right:25px !important;position:relative;cursor:default;line-height:1.5}.h-category-show>.h-icon-down{color:#666666;position:absolute;right:8px;top:50%;height:10px;font-size:12px;line-height:12px;transform:translate(0, -50%) scale(.8);transition:transform .2s}.h-category-show:hover>.h-icon-down{color:#45b984}.h-category-show.h-pop-trigger>.h-icon-down{transform:rotate(-180deg) translate(0, 50%) scale(.8);color:#45b984}.h-category-multiple-tags{display:flex;flex-wrap:wrap}.h-category-multiple-tags>span{margin-bottom:2px;display:inline-block;border-radius:2px;transition:all linear .2s;margin-right:5px;border:1px solid #eeeeee;background-color:#f5f5f5;font-size:13px;padding:0px 8px}.h-category-multiple-tags>span>span{line-height:22px}.h-category-multiple-tags>span>.h-icon-close-min{font-size:16px;color:#666666;display:inline-block;width:14px;margin-left:2px;vertical-align:-2px;cursor:pointer;transition:color .2s ease}.h-category-multiple-tags>span>.h-icon-close-min:hover{color:#45b984 !important}.h-category-multiple-tags>span>.h-icon-close-min:active{color:#30815c !important}.h-category-multiple-tags>span>.h-icon-close-min.h-pop-trigger .h-category-multiple-tags>span>.h-icon-close-min{color:#379469 !important}.h-category-multiple-tags>span>.h-icon-close-min[disabled]{color:#999999 !important;cursor:default}.h-category-multiple-tags>span>.h-icon-close{line-height:22px;display:inline-block;font-size:12px;color:#666666;margin-left:5px;cursor:pointer;transition:color .2s ease}.h-category-multiple-tags>span>.h-icon-close:hover{color:#45b984 !important}.h-category-multiple-tags>span>.h-icon-close:active{color:#30815c !important}.h-category-multiple-tags>span>.h-icon-close.h-pop-trigger .h-category-multiple-tags>span>.h-icon-close{color:#379469 !important}.h-category-multiple-tags>span>.h-icon-close[disabled]{color:#999999 !important;cursor:default}.h-category-input-border .h-category-show{min-height:30px;cursor:default;background-color:#ffffff;border-radius:4px;border:1px solid #eeeeee;vertical-align:top;transition:all linear .2s;padding:2px 2px 0px 2px;font-size:13px;position:relative;box-sizing:border-box}.h-category-input-border .h-category-show:hover{border-color:#6ac79d}.h-category-input-border .h-category-show.h-pop-trigger,.h-category-input-border .h-category-show.focusing{border-color:#45b984;box-shadow:0 0 0 2px rgba(69,185,132,0.2)}.h-form-item-valid-error .h-category-input-border .h-category-show{border-color:#E11617}.h-form-item-valid-error .h-category-input-border .h-category-show:focus,.h-form-item-valid-error .h-category-input-border .h-category-show.focusing{box-shadow:0 0 0 2px rgba(225,22,23,0.2)}.h-form-item-valid-error .h-category-input-border .h-category-show{border-color:#E11617}.h-form-item-valid-error .h-category-input-border .h-category-show.h-pop-trigger{box-shadow:0 0 0 2px rgba(225,22,23,0.2)}.h-category-input-border .h-category-value-single,.h-category-input-border .h-category-placeholder{margin-bottom:2px}.h-category-no-autosize{display:block}.h-category-disabled .h-category-show{background-color:#f5f5f5;color:#999999 !important;border-color:#d3d3d3}.h-category-disabled .h-category-show:hover{border-color:#d3d3d3;color:#999999 !important}.h-category-disabled .h-category-show .h-icon-down{color:#999999 !important}.h-category-value-single>.h-icon-close{position:absolute;top:50%;right:8px;font-size:12px;z-index:3;cursor:pointer;transition:color .2s ease;color:#666666;margin-top:-6px}.h-category-value-single>.h-icon-close:hover{color:#45b984 !important}.h-category-value-single>.h-icon-close:active{color:#30815c !important}.h-category-value-single>.h-icon-close.h-pop-trigger .h-category-value-single>.h-icon-close{color:#379469 !important}.h-category-value-single>.h-icon-close[disabled]{color:#999999 !important;cursor:default}.h-category-item{font-size:90%;padding:3px 5px;cursor:pointer}.h-category-item:hover{background:#eeeeee}.h-category-modal-multiple-tags{min-height:30px;padding-right:180px}.h-category-modal-multiple-tags>span{margin-bottom:2px;display:inline-block;border-radius:2px;transition:all linear .2s;margin-right:5px;border:1px solid #eeeeee;background-color:#f5f5f5;font-size:13px;padding:0px 8px}.h-category-modal-multiple-tags>span>span{line-height:22px}.h-category-modal-multiple-tags>span>.h-icon-close-min{font-size:16px;color:#666666;display:inline-block;width:14px;margin-left:2px;vertical-align:-2px;cursor:pointer;transition:color .2s ease}.h-category-modal-multiple-tags>span>.h-icon-close-min:hover{color:#45b984 !important}.h-category-modal-multiple-tags>span>.h-icon-close-min:active{color:#30815c !important}.h-category-modal-multiple-tags>span>.h-icon-close-min.h-pop-trigger .h-category-modal-multiple-tags>span>.h-icon-close-min{color:#379469 !important}.h-category-modal-multiple-tags>span>.h-icon-close-min[disabled]{color:#999999 !important;cursor:default}.h-category-modal-multiple-tags>span>.h-icon-close{line-height:22px;display:inline-block;font-size:12px;color:#666666;margin-left:5px;cursor:pointer;transition:color .2s ease}.h-category-modal-multiple-tags>span>.h-icon-close:hover{color:#45b984 !important}.h-category-modal-multiple-tags>span>.h-icon-close:active{color:#30815c !important}.h-category-modal-multiple-tags>span>.h-icon-close.h-pop-trigger .h-category-modal-multiple-tags>span>.h-icon-close{color:#379469 !important}.h-category-modal-multiple-tags>span>.h-icon-close[disabled]{color:#999999 !important;cursor:default}.h-category-modal-single-tag{min-height:30px;line-height:30px;padding-right:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.h-circle{display:inline-block;position:relative}.h-circle::after{content:"";display:block;clear:both}.h-circle-content{width:100%;text-align:center;position:absolute;left:0;top:50%;transform:translateY(-50%);line-height:1}.h-collapse{display:block}.h-collapse-item{display:block}.h-collapse-item:last-of-type{border-bottom:1px solid #eeeeee}.h-collapse-item-header{height:40px;line-height:40px;border-top:1px solid #eeeeee;padding:0 10px;font-size:13px;transition:all .2s}.h-collapse-item-header:hover{cursor:pointer;color:#45b984}.h-collapse-item-expand{margin-top:13px;margin-right:5px;transform:rotateZ(0deg);transition:all .2s;font-size:12px}.h-collapse-item-content{word-break:break-all}.h-collapse-item-content-box{padding:10px 20px}.h-collapse-item.h-collapse-item-active .h-collapse-item-header .h-collapse-item-expand{transform:rotateZ(90deg)}.h-collapse-transition{transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out}.h-datetime-range .h-datetime-show{min-width:208px}.h-datetime-full-range .h-datetime-show{min-width:234px}.h-date-picker{border:1px solid #eeeeee}.h-date-picker .h-date-shortcut{border-radius:4px 0 0 4px;width:104px;font-size:12px;line-height:28px;background:#f8f8f8;border-right:1px solid #eeeeee;padding:10px 0}.h-date-picker .h-date-shortcut>div{padding:3px 10px;cursor:pointer}.h-date-picker .h-date-shortcut>div:hover{background-color:#eeeeee}.h-date-picker .h-date-shortcut>div.disabled{cursor:default}.h-date-picker .h-date-shortcut>div.disabled:hover{background-color:transparent}.h-date-picker .h-date-container{display:flex}.h-date-picker .h-date-full-range-container{display:block;min-width:260px}.h-date-picker .h-date-full-range-container .h-tabs{padding:0}.h-date-picker .h-date-full-range-container .h-tabs>.h-tabs-item+div{margin-left:5px;padding:10px}.h-date-picker .h-date-range-container>.h-date-content:last-child{border-left:1px solid #eeeeee}.h-date-picker .h-date-content{padding:10px}.h-date-picker .h-date-self-defined{padding:15px;text-align:center}.h-date-picker .h-date-self-defined .h-input{width:110px}.h-date-picker .h-date-footer{padding:5px 10px;text-align:right;border-top:1px solid #eeeeee}.h-date-picker .h-date-header{text-align:center;line-height:30px}.h-date-picker .h-date-header>span{cursor:pointer;transition:color .2s ease;display:inline-block}.h-date-picker .h-date-header>span:hover{color:#45b984 !important}.h-date-picker .h-date-header>span:active{color:#30815c !important}.h-date-picker .h-date-header>span.h-pop-trigger .h-date-picker .h-date-header>span{color:#379469 !important}.h-date-picker .h-date-header>span[disabled]{color:#999999 !important;cursor:default}.h-date-picker .h-date-header-show{margin:0 3px}.h-date-picker .h-date-header .h-date-year-left-picker>i+i,.h-date-picker .h-date-header .h-date-year-right-picker>i+i{margin-left:-7px}.h-date-picker .h-date-header .h-date-month-left-picker,.h-date-picker .h-date-header .h-date-year-left-picker,.h-date-picker .h-date-header .h-date-month-right-picker,.h-date-picker .h-date-header .h-date-year-right-picker{color:#999999;font-size:12px;margin:0 3px}.h-date-picker .h-date-header .h-date-month-left-picker,.h-date-picker .h-date-header .h-date-year-left-picker{float:left}.h-date-picker .h-date-header .h-date-month-right-picker,.h-date-picker .h-date-header .h-date-year-right-picker{float:right}.h-date-picker .h-date-body{margin:8px auto 0;font-size:13px;width:224px}.h-date-picker .h-date-body>div>span{width:32px;height:32px;line-height:32px;display:inline-block;text-align:center;user-select:none}.h-date-picker .h-date-body-weeks{font-weight:bold}.h-date-picker .h-date-body-pickers>span{cursor:default}.h-date-picker .h-date-body-pickers>span:hover{background-color:#eeeeee}.h-date-picker .h-date-body-pickers>span.h-date-today{color:#45b984}.h-date-picker .h-date-body-pickers>span.h-date-selected{background-color:#45b984;color:#ffffff}.h-date-picker .h-date-body-pickers>span.h-date-start-selected{border-top-right-radius:0;border-bottom-right-radius:0}.h-date-picker .h-date-body-pickers>span.h-date-end-selected{border-top-left-radius:0;border-bottom-left-radius:0}.h-date-picker .h-date-body-pickers>span.h-date-range-selected{border-radius:0;background-color:rgba(69,185,132,0.15)}.h-date-picker .h-date-body-pickers>span.h-date-not-now-day{color:#d3d3d3;background-color:#ffffff}.h-date-picker .h-date-body-pickers>span.h-date-disabled{background-color:#f5f5f5;color:#999999;pointer-events:none}.h-date-picker .h-date-body-month>div>span,.h-date-picker .h-date-body-year>div>span,.h-date-picker .h-date-body-date>div>span{border-radius:50%}.h-date-picker .h-date-body-month>div>span,.h-date-picker .h-date-body-year>div>span{height:50px;width:50px;line-height:50px;margin:3px}.h-date-picker .h-date-body-week,.h-date-picker .h-date-body-quarter{min-width:200px;width:auto}.h-date-picker .h-date-body-week>div>span,.h-date-picker .h-date-body-quarter>div>span{border-radius:5px;width:100%;display:block;padding-left:10px;padding-right:10px}.h-date-picker .h-date-body-minute>div>span,.h-date-picker .h-date-body-hour>div>span{border-radius:5px;height:30px;width:50px;line-height:30px;margin:3px}.h-datetime-input-border{display:inline-block;vertical-align:middle}.h-datetime-input-border .h-input{width:100%}.h-datetime-input-border .h-datetime-show:hover>input:not([disabled]),.h-datetime-input-border .h-pop-trigger>input:not([disabled]){border-color:#45b984;box-shadow:0 0 0 2px rgba(69,185,132,0.2)}.h-dropdownmenu{display:inline-block;vertical-align:middle}.h-dropdownmenu-group{padding:10px 0}.h-dropdownmenu-disabled{cursor:default;color:#999999 !important}.h-dropdownmenu-disabled>.h-icon-down{color:#999999 !important}.h-dropdownmenu-item{cursor:pointer;padding:5px 15px;position:relative}.h-dropdownmenu-item:hover{background-color:#eeeeee}.h-dropdownmenu-item.disabled{cursor:default}.h-dropdownmenu-item.disabled:hover{background-color:transparent}.h-dropdownmenu-item>i{margin-right:5px}.h-dropdownmenu-item-divider{height:0;padding:0;overflow:hidden;margin:3px 0;border-top:1px solid #eeeeee}.h-dropdownmenu-item.disabled{color:#999999}.form-content-width100 input[type='text'],.form-content-width100 input[type='password'],.form-content-width100 input[type='number'],.form-content-width100 textarea,.form-content-width100 .h-input,.form-content-width100 .h-select,.form-content-width100 .h-taginput,.form-content-width100 .h-autocomplete,.form-content-width100 .h-numberinput,.form-content-width100 .h-datetime-input-border,.form-content-width100 .h-input-group{width:100%}.form-content-width100 .h-radio>label,.form-content-width100 .h-checkbox>label{height:30px;line-height:30px}.h-form::after{content:"";display:block;clear:both}.h-form .h-form-item{padding-bottom:24px;position:relative}.h-form .h-form-item-no-padding{padding-bottom:0px}.h-form .h-form-item-label{display:inline-block;text-align:right;font-size:13px;color:#555555;line-height:1;padding:8.5px 15px 8.5px 0;box-sizing:border-box}.h-form .h-form-item-required .h-form-item-label::before{content:"*";color:#E11617;font-size:15px;display:inline-block;margin-right:4px;line-height:8px;vertical-align:middle}.h-form .h-form-item-content{font-size:13px}.h-form .h-form-item-content input[type='text'],.h-form .h-form-item-content input[type='password'],.h-form .h-form-item-content input[type='number'],.h-form .h-form-item-content textarea,.h-form .h-form-item-content .h-input,.h-form .h-form-item-content .h-select,.h-form .h-form-item-content .h-taginput,.h-form .h-form-item-content .h-autocomplete,.h-form .h-form-item-content .h-numberinput,.h-form .h-form-item-content .h-datetime-input-border,.h-form .h-form-item-content .h-input-group{width:100%}.h-form .h-form-item-content .h-radio>label,.h-form .h-form-item-content .h-checkbox>label{height:30px;line-height:30px}.h-form .h-form-item-wrap{position:relative}.h-form .h-form-item-wrap::after{content:"";display:block;clear:both}.h-form .h-form-item-error{position:absolute;margin-top:5px;color:#E11617;line-height:1}.h-form.h-form-label-left .h-form-item-label{text-align:left;padding:8.5px 0 8.5px 15px}.h-form.h-form-label-left .h-form-item-required .h-form-item-label:before{position:absolute;top:13px;left:6px}.h-form.h-form-block .h-form-item::after,.h-form.h-form .h-form-item-block::after{content:"";display:block;clear:both}.h-form.h-form-block .h-form-item-label,.h-form.h-form .h-form-item-block-label{display:block;text-align:left;padding-left:0}.h-form.h-form-block .h-form-item-label:empty,.h-form.h-form .h-form-item-block-label:empty{display:none}.h-form.h-form-block .h-form-item-content::after,.h-form.h-form .h-form-item-block-content::after{content:"";display:block;clear:both}.h-form.h-form-inline .h-form-item{display:inline-block}.h-form.h-form-inline .h-form-item-label{float:left}.h-form.h-form-inline .h-form-item-content{float:left;margin-right:20px}.h-form.h-form-twocolumn .h-form-item{width:50%;float:left}.h-form.h-form-twocolumn .h-form-item-label{width:80px;float:left}.h-form.h-form-threecolumn .h-form-item{width:33.3%;float:left}.h-form.h-form-threecolumn .h-form-item-label{width:80px;float:left}.h-form.h-form-single .h-form-item,.h-form.h-form .h-form-item-single{width:100%}.h-form.h-form-single .h-form-item::after,.h-form.h-form .h-form-item-single::after{content:"";display:block;clear:both}.h-form.h-form-single .h-form-item .h-form-item-label,.h-form.h-form .h-form-item-single .h-form-item-label{width:80px;float:left}.h-form.h-form-single .h-form-item .h-form-item-content,.h-form.h-form .h-form-item-single .h-form-item-content{margin-left:80px}.h-form-readonly .h-form-item{padding-bottom:3px}.h-form-readonly .h-form-item .h-form-item-wrap{line-height:30px}.h-form-item-readonly .h-form-item-content{line-height:30px}.h-image-preview{position:relative;transition:height .3s,width .3s}.h-image-preview .h-image-preview-index{color:#fff;text-shadow:0 0 10px #000;font-size:18px;position:absolute;top:10px;right:20px;z-index:2}.h-image-preview>.h-image-preview-image{max-width:800px;max-height:12000px;display:block}.h-image-preview .fade-enter-active,.h-image-preview .fade-leave-active{transition:opacity .3s}.h-image-preview .fade-enter,.h-image-preview .fade-leave-to{opacity:0}.h-image-preview .h-image-preview-icon{text-shadow:0 0 10px #000;position:absolute;top:0;bottom:0;font-size:40px;color:#fff;opacity:0;transition:.2s opacity;cursor:pointer;width:50%;display:flex;flex-direction:column;justify-content:center}.h-image-preview .h-image-preview-icon.h-image-preview-left-icon{left:10px;text-align:left}.h-image-preview .h-image-preview-icon.h-image-preview-right-icon{right:10px;text-align:right}.h-image-preview .h-image-preview-icon:hover{opacity:1}.h-image-preview-list{clear:both}.h-image-preview-list::after{content:"";display:block;clear:both}.h-image-preview-list .h-image-preview-item{position:relative;cursor:pointer;background-size:cover;background-position:center;float:left;display:inline-block;overflow:hidden}.h-image-preview-list .h-image-preview-item:hover:before{content:'';position:absolute;left:0;right:0;top:0;bottom:0;background:rgba(0,0,0,0.6)}.h-image-preview-modal.h-modal .h-notify-container{min-width:0px;padding:5px}.h-layout{position:relative;display:flex;flex-direction:column;flex:auto;min-height:0;z-index:1;overflow:hidden}.h-layout::after{content:"";display:block;clear:both}.h-layout .h-layout{overflow:hidden}.h-layout.h-layout-has-sider{flex-direction:row}.h-layout.h-layout-header-fixed{padding-top:64px}.h-layout.h-layout-header-fixed>.h-layout-header{top:0;left:0;right:0;position:fixed;z-index:2}.h-layout.h-layout-header-fixed>.h-layout-content{z-index:1}.h-layout.h-layout-header-fixed>.h-layout-sider{z-index:1}.h-layout.h-layout-sider-fixed{padding-left:200px}.h-layout.h-layout-sider-fixed>.h-layout-content{z-index:1}.h-layout.h-layout-sider-fixed>.h-layout-sider{position:fixed;bottom:0;top:0;left:0;overflow:auto;z-index:2}.h-layout.h-layout-sider-fixed.h-layout-sider-collapsed{padding-left:70px}.h-layout.h-layout-sider-collapsed>.h-layout-sider{width:70px;flex:0 0 70px;max-width:70px;min-width:70px;overflow:initial;z-index:2}.h-layout.h-layout-header-fixed>.h-layout-sider-fixed>.h-layout-sider{top:64px}.h-layout.h-layout-has-sider>.h-layout-header-fixed>.h-layout-header{left:200px}.h-layout.h-layout-sider-collapsed>.h-layout-header-fixed>.h-layout-header{left:70px}.h-layout-header{height:64px;line-height:64px;z-index:1}.h-layout-header-theme-dark{color:rgba(255,255,255,0.65);background:#333333}.h-layout-header-theme-white{background:#FFF}.h-layout-sider{transition:all .2s;position:relative;flex:0 0 200px;max-width:200px;min-width:200px;width:200px;z-index:1}.h-layout-sider-theme-dark{background:#333333}.h-layout-sider-theme-white{background:#FFF}.h-layout-content{position:relative;flex:auto}.h-loadingbar{width:100%;position:fixed;top:0;left:0;right:0;z-index:2000}.h-loadingbar-inner{height:0px;width:0;transition:width .2s linear;background-color:#45b984}.h-loadingbar-inner-failed-color-error{background-color:#E11617}.h-loadingbar-inner.loading{height:2px}.h-loadingbar-inner.error{background-color:#E11617}@keyframes loading-rotate{100%{transform:rotate(360deg)}}@keyframes loading-path-rotate{0%{stroke-dasharray:1, 200;stroke-dashoffset:0}50%{stroke-dasharray:89, 200;stroke-dashoffset:-35px}100%{stroke-dasharray:89, 200;stroke-dashoffset:-124px}}.h-loading{position:absolute;left:0;bottom:0;right:0;top:0;background-color:rgba(255,255,255,0.6);z-index:-1;visibility:hidden;text-align:center;opacity:0;transition:opacity .5s}.h-loading .h-loading-circular{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);height:42px}.h-loading .h-loading-circular>svg{opacity:0;width:42px;height:42px;transition:opacity .5s;transform-origin:center center}.h-loading .h-loading-circular>svg .circle{stroke-dasharray:90, 150;stroke-dashoffset:0;stroke-width:2;stroke:#45b984;stroke-linecap:round}.h-loading.h-loading-loading{opacity:1}.h-loading.h-loading-loading.h-loading-full-screen{z-index:2000;position:fixed}.h-loading.h-loading-visible{visibility:visible;z-index:8}.h-loading.h-loading-visible svg{opacity:1;animation:loading-rotate 2s linear infinite}.h-loading.h-loading-visible svg .circle{animation:loading-path-rotate 1.5s ease-in-out infinite}.h-loading-parent{position:relative;min-height:200px}.h-menu{width:100%;display:block;margin:0;padding:0;outline:none;list-style:none;position:relative;line-height:1}.h-menu-ul{list-style:none;margin:0;padding:0;outline:none;box-sizing:border-box}.h-menu-li{position:relative}.h-menu-show{position:relative;padding:15px 26px;cursor:pointer;text-overflow:clip;white-space:nowrap;transition:padding .2s}.h-menu-show-expand{right:20px;position:absolute;top:50%;transform:translate(0%, -50%)}.h-menu-show-desc{transition:opacity .3s cubic-bezier(.645, .045, .355, 1),width .3s cubic-bezier(.645, .045, .355, 1);opacity:1}.h-menu-show-count{transition:right .2s;z-index:2;right:60px;position:absolute;top:50%;transform:translate(0%, -50%)}.h-menu .h-menu-li a{display:block;color:inherit;font-weight:inherit;text-decoration:inherit}.h-menu-mode-normal .h-menu-ul{max-height:0;transition:max-height .3s cubic-bezier(0, 1, 0, 1);overflow:hidden}.h-menu-mode-normal .h-menu-li .h-menu-li .h-menu-show{padding-left:50px}.h-menu-mode-normal .h-menu-li .h-menu-li .h-menu-li .h-menu-show{padding-left:70px}.h-menu-mode-normal .h-menu-li-opened>.h-menu-ul{max-height:1000px;transition:max-height .8s ease-in-out}.h-menu-mode-normal .h-menu-li-opened>.h-menu-show .h-menu-show-expand>i{transform:rotate(0deg)}.h-menu-mode-normal .h-menu-show{overflow:hidden;text-overflow:ellipsis;transition:color .2s}.h-menu-mode-normal .h-menu-show-icon{margin-right:5px}.h-menu-mode-normal .h-menu-show-expand>i{transition:transform .2s;transform:rotate(-90deg)}.h-menu-dark{background:#333333;color:#b3b3b3}.h-menu-dark .h-menu-show{border-right:0px solid transparent}.h-menu-dark .h-menu-show:hover{color:#ffffff}.h-menu-dark .h-menu-ul{background:#262626}.h-menu-dark .h-menu-li-selected{background:#45b984;color:#ffffff}.h-menu-white{background:#ffffff}.h-menu-white .h-menu-show:hover{color:#45b984}.h-menu-white .h-menu-ul{background:#ffffff}.h-menu-white .h-menu-li{position:relative}.h-menu-white .h-menu-li .h-menu-show:after{position:absolute;content:'';top:0;right:0;bottom:0;width:0px;background:#45b984}.h-menu-white .h-menu-li .h-menu-li-selected{background-color:rgba(69,185,132,0.15);color:#45b984}.h-menu-white .h-menu-li .h-menu-li-selected:after{width:5px;transition:.2s}.h-menu.h-menu-size-collapse>.h-menu-li>.h-menu-show{padding-left:28px}.h-menu.h-menu-size-collapse>.h-menu-li>.h-menu-show .h-menu-show-icon{margin-right:0px;font-size:17px}.h-menu.h-menu-size-collapse>.h-menu-li>.h-menu-show .h-menu-show-desc{visibility:visible;opacity:0;display:inline-block;width:0;text-overflow:clip}.h-menu.h-menu-size-collapse>.h-menu-li>.h-menu-show .h-menu-show-count{right:10px}.h-menu.h-menu-size-collapse>.h-menu-li>.h-menu-show .h-menu-show-expand{display:none}.h-menu.h-menu-size-collapse .h-menu-ul{padding:5px 0;margin-left:5px;left:100%;top:0;width:150px;border-radius:4px;position:absolute;max-width:0;transition:.2s cubic-bezier(.78, .17, .43, 1);white-space:nowrap;display:inline-block;overflow:hidden;opacity:.8;transform:scale(.8);transform-origin:left}.h-menu.h-menu-size-collapse .h-menu-ul .h-menu-show{overflow:hidden;text-overflow:ellipsis}.h-menu.h-menu-size-collapse .h-menu-show-expand{right:10px}.h-menu.h-menu-size-collapse .h-menu-show-expand i{transform:rotate(-90deg)}.h-menu.h-menu-size-collapse .h-menu-li:hover>.h-menu-ul{max-width:250%;opacity:1;transform:scale(1);overflow:visible}.h-menu.h-menu-size-collapse .h-menu-li:hover>.h-menu-ul:before{content:'';position:absolute;height:100%;top:0;left:-10px;width:10px}.h-menu.h-menu-size-collapse .h-menu-show{padding-left:20px}.h-menu-mode-horizontal:after{content:"";display:table;clear:both}.h-menu-mode-horizontal .h-menu-li{display:inline-block}.h-menu-mode-horizontal .h-menu-li .h-menu-show.h-menu-li-selected{background-color:rgba(69,185,132,0.15);color:#45b984}.h-menu-mode-horizontal .h-menu-li .h-menu-show.h-menu-li-selected:after{content:'';position:absolute;width:100%;height:2px;background-color:#45b984;bottom:0;left:0;right:0;top:auto}.h-menu-mode-horizontal .h-menu-ul{padding:5px 0;left:0;top:100%;width:150px;max-height:0;margin-top:5px;border-radius:4px;transition:.1s cubic-bezier(.78, .17, .43, 1);transform-origin:top;opacity:.8;transform:scale(.8);visibility:hidden;overflow:hidden;position:absolute;white-space:nowrap;display:inline-block}.h-menu-mode-horizontal .h-menu-ul .h-menu-li{display:block}.h-menu-mode-horizontal .h-menu-ul .h-menu-li .h-menu-ul{padding:5px 0;left:100%;margin-left:5px;top:0;width:150px;border-radius:4px;visibility:hidden;position:absolute;max-height:0;transition:.1s cubic-bezier(.78, .17, .43, 1);transform-origin:top;opacity:.8;transform:scale(.8);white-space:nowrap;display:inline-block}.h-menu-mode-horizontal .h-menu-ul .h-menu-show{overflow:hidden;text-overflow:ellipsis;transition:color .2s}.h-menu-mode-horizontal .h-menu-ul .h-menu-show-icon{margin-right:5px}.h-menu-mode-horizontal .h-menu-ul .h-menu-show-expand>i{transition:transform .2s;transform:rotate(-90deg)}.h-menu-mode-horizontal .h-menu-ul .h-menu-show.h-menu-li-selected{background-color:rgba(69,185,132,0.15);color:#45b984}.h-menu-mode-horizontal .h-menu-ul .h-menu-show.h-menu-li-selected:after{content:'';position:absolute;width:100%;height:2px;background-color:#45b984;bottom:0;left:0;right:0;top:auto}.h-menu-mode-horizontal .h-menu-show-expand{right:10px}.h-menu-mode-horizontal .h-menu-show-expand i{transform:rotate(-90deg)}.h-menu-mode-horizontal .h-menu-li:hover>.h-menu-ul{max-height:10000px;transform:scale(1);opacity:1;visibility:visible;overflow:visible}.h-menu-mode-horizontal .h-menu-li:hover>.h-menu-ul:before{content:'';position:absolute;width:100%;top:-10px;left:0;height:10px}.h-menu-mode-horizontal .h-menu-li:hover>.h-menu-ul:after{content:'';position:absolute;height:100%;top:0;left:-10px;width:10px}.h-menu-mode-horizontal.h-menu-dark .h-menu-li .h-menu-show.h-menu-li-selected{background-color:#45b984;color:#ffffff}.h-menu-mode-horizontal.h-menu-dark .h-menu-li .h-menu-show.h-menu-li-selected:after{display:none}.h-numberinput{display:block}.h-numberinput-show{display:flex;flex-wrap:wrap;cursor:default;background-color:#ffffff;border-radius:4px;border:1px solid #eeeeee;vertical-align:top;transition:all linear .2s;padding:2px 2px 0px 2px;font-size:13px;position:relative;box-sizing:border-box}.h-numberinput-show:hover{border-color:#6ac79d}.h-numberinput-show.h-pop-trigger,.h-numberinput-show.focusing{border-color:#45b984;box-shadow:0 0 0 2px rgba(69,185,132,0.2)}.h-form-item-valid-error .h-numberinput-show{border-color:#E11617}.h-form-item-valid-error .h-numberinput-show:focus,.h-form-item-valid-error .h-numberinput-show.focusing{box-shadow:0 0 0 2px rgba(225,22,23,0.2)}.h-numberinput-show .h-numberinput-input{flex:1;min-width:80px;width:auto;max-width:100%;border-color:transparent !important;line-height:13px;font-size:13px;height:24px;margin-bottom:2px;padding-left:5px}.h-numberinput-show .h-numberinput-input:focus{box-shadow:none !important}.h-numberinput-operate{width:60px;height:28px;line-height:28px;margin-top:-2px;margin-bottom:-2px;margin-right:-2px}.h-numberinput-operate>span{display:inline-block;width:50%;text-align:center;float:left;border-left:1px solid #eeeeee;cursor:pointer;transition:color .2s ease}.h-numberinput-operate>span:hover{color:#45b984 !important}.h-numberinput-operate>span:active{color:#30815c !important}.h-numberinput-operate>span.h-pop-trigger .h-numberinput-operate>span{color:#379469 !important}.h-numberinput-operate>span[disabled]{color:#999999 !important;cursor:default}.h-numberinput-disabled .h-numberinput-show{background-color:#f5f5f5;color:#999999 !important;border-color:#d3d3d3}.h-numberinput-disabled .h-numberinput-show:hover{border-color:#d3d3d3;color:#999999 !important}.h-numberinput-disabled .h-numberinput-show .h-icon-down{color:#999999 !important}.h-numberinput-disabled .h-numberinput-operate>span{color:#999999 !important;cursor:not-allowed}.h-page{display:flex;align-items:center}.h-page::after{content:"";display:block;clear:both}.h-page>span,.h-page>input,.h-page>div{margin:0 5px}.h-page>.h-page-select-size{margin-right:0}.h-page.h-page-align-right{justify-content:flex-end;-ms-flex-pack:flex-end}.h-page.h-page-align-center{justify-content:center;-ms-flex-pack:center}.h-page-pager-container .h-page-pager{position:relative;border:1px solid #eeeeee;float:left;display:inline-block;padding:3.5px 10px;margin-left:-1px;cursor:pointer}.h-page-pager-container .h-page-pager:hover{color:#45b984}.h-page-pager-container .h-page-pager:first-child{margin-left:0}.h-page-pager-container .h-page-pager-selected{background-color:#45b984;color:#ffffff;border-color:#45b984;z-index:2}.h-page-pager-container .h-page-pager-selected:hover{color:#ffffff}.h-page-pager-container .h-page-pager-disabled{pointer-events:none;cursor:not-allowed;color:#999999}.h-page-pager-container .h-page-pager-disabled:hover{color:#999999}.h-page-ellipsis{pointer-events:none}.h-page-small{font-size:13px}.h-page-small .h-page-pager-container .h-page-pager{border:none;border-radius:4px}.h-page-small input[type=text]{height:25px;line-height:25px}.h-page-jumper-input{width:40px}.h-panel{background:#fff;border-radius:4px;position:relative;border:1px solid #eeeeee}.h-panel-no-border{border:none}.h-panel-bar{padding:10px 20px;border-bottom:1px solid #eeeeee;position:relative}.h-panel-bar>span{vertical-align:middle}.h-panel-bar .h-panel-title{line-height:34px;font-size:18px;display:inline-block;vertical-align:middle}.h-panel-bar .h-panel-right{position:absolute;top:50%;transform:translate(0%, -50%);right:20px}.h-panel-body{padding:20px}.h-poptip{width:160px;font-size:13px;padding:3px}.h-poptip-content{padding:10px 0px 18px}.h-progress{font-size:13px;position:relative;display:flex;align-items:center}.h-progress-title{width:100px;padding-right:5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.h-progress-inner{flex:1;background-color:#f3f3f3;border-radius:100px;vertical-align:middle}.h-progress-bg{border-radius:100px;background-color:#3B91FF;transition:all .2s linear;position:relative;height:100%}.h-progress-text{color:#c1c1c1;width:100px;padding-left:10px;text-align:left;vertical-align:middle;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.h-rate{background:#FFF;margin:0px;cursor:pointer;box-sizing:border-box;color:#FFAE00;display:block;line-height:30px;height:30px}.h-rate>span{font-size:18px;margin-right:3px;transition:all .2s;color:#eeeeee}.h-rate>span:hover>i{transition:transform .2s;transform:scale(1.1)}.h-rate>span.h-rate-on{color:#FFAE00}.h-rate[readonly]{pointer-events:painted;cursor:default}.h-rate[readonly]>span:hover>i{transform:none}.h-rate>.h-rate-value{font-size:15px;color:#FFAE00}.h-select{display:inline-block;box-sizing:border-box;vertical-align:middle}.h-select-placeholder{color:#787878}.h-select-value-single,.h-select-placeholder{line-height:24px;height:24px;padding-left:5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.h-select-show{padding-right:25px !important;position:relative;cursor:default;line-height:1.5}.h-select-show>.h-icon-down{color:#666666;position:absolute;right:8px;top:50%;height:10px;font-size:12px;line-height:12px;transform:translate(0, -50%) scale(.8);transition:transform .2s}.h-select-show:hover>.h-icon-down{color:#45b984}.h-select-show.h-pop-trigger>.h-icon-down{transform:rotate(-180deg) translate(0, 50%) scale(.8);color:#45b984}.h-select-multiple-tags{display:flex;flex-wrap:wrap}.h-select-multiple-tags>span{margin-bottom:2px;display:inline-block;border-radius:2px;transition:all linear .2s;margin-right:5px;border:1px solid #eeeeee;background-color:#f5f5f5;font-size:13px;padding:0px 8px}.h-select-multiple-tags>span>span{line-height:22px}.h-select-multiple-tags>span>.h-icon-close-min{font-size:16px;color:#666666;display:inline-block;width:14px;margin-left:2px;vertical-align:-2px;cursor:pointer;transition:color .2s ease}.h-select-multiple-tags>span>.h-icon-close-min:hover{color:#45b984 !important}.h-select-multiple-tags>span>.h-icon-close-min:active{color:#30815c !important}.h-select-multiple-tags>span>.h-icon-close-min.h-pop-trigger .h-select-multiple-tags>span>.h-icon-close-min{color:#379469 !important}.h-select-multiple-tags>span>.h-icon-close-min[disabled]{color:#999999 !important;cursor:default}.h-select-multiple-tags>span>.h-icon-close{line-height:22px;display:inline-block;font-size:12px;color:#666666;margin-left:5px;cursor:pointer;transition:color .2s ease}.h-select-multiple-tags>span>.h-icon-close:hover{color:#45b984 !important}.h-select-multiple-tags>span>.h-icon-close:active{color:#30815c !important}.h-select-multiple-tags>span>.h-icon-close.h-pop-trigger .h-select-multiple-tags>span>.h-icon-close{color:#379469 !important}.h-select-multiple-tags>span>.h-icon-close[disabled]{color:#999999 !important;cursor:default}.h-select-input-border .h-select-show{min-height:30px;cursor:default;background-color:#ffffff;border-radius:4px;border:1px solid #eeeeee;vertical-align:top;transition:all linear .2s;padding:2px 2px 0px 2px;font-size:13px;position:relative;box-sizing:border-box}.h-select-input-border .h-select-show:hover{border-color:#6ac79d}.h-select-input-border .h-select-show.h-pop-trigger,.h-select-input-border .h-select-show.focusing{border-color:#45b984;box-shadow:0 0 0 2px rgba(69,185,132,0.2)}.h-form-item-valid-error .h-select-input-border .h-select-show{border-color:#E11617}.h-form-item-valid-error .h-select-input-border .h-select-show:focus,.h-form-item-valid-error .h-select-input-border .h-select-show.focusing{box-shadow:0 0 0 2px rgba(225,22,23,0.2)}.h-form-item-valid-error .h-select-input-border .h-select-show{border-color:#E11617}.h-form-item-valid-error .h-select-input-border .h-select-show.h-pop-trigger{box-shadow:0 0 0 2px rgba(225,22,23,0.2)}.h-select-input-border .h-select-value-single,.h-select-input-border .h-select-placeholder{margin-bottom:2px}.h-select-no-autosize{display:block}.h-select-disabled .h-select-show{background-color:#f5f5f5;color:#999999 !important;border-color:#d3d3d3}.h-select-disabled .h-select-show:hover{border-color:#d3d3d3;color:#999999 !important}.h-select-disabled .h-select-show .h-icon-down{color:#999999 !important}.h-select-show{display:flex;flex-wrap:wrap;position:relative}.h-select-show>.h-icon-close{position:absolute;top:50%;right:8px;font-size:12px;z-index:3;cursor:pointer;transition:color .2s ease;color:#666666;margin-top:-6px}.h-select-show>.h-icon-close:hover{color:#45b984 !important}.h-select-show>.h-icon-close:active{color:#30815c !important}.h-select-show>.h-icon-close.h-pop-trigger .h-select-show>.h-icon-close{color:#379469 !important}.h-select-show>.h-icon-close[disabled]{color:#999999 !important;cursor:default}.h-select .h-select-search-input{flex:1;min-width:80px;width:auto;max-width:100%;border-color:transparent !important;line-height:13px;font-size:13px;height:24px;margin-bottom:2px;padding-left:5px}.h-select .h-select-search-input:focus{box-shadow:none !important}.h-select-filterable-value{top:1px;position:absolute;line-height:24px;left:8px;z-index:1;right:22px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.h-select-single-search-input{position:relative;z-index:2;background-color:transparent !important}.h-select-single-search-input:focus+.h-select-filterable-value{color:#c1c1c1}.h-select-disabled .h-select-single-search-input{color:#999999 !important}.h-select-group{outline:none;user-select:none;position:relative;font-size:13px;background-color:#ffffff;padding:5px 0px}.h-select-group-container .h-select-list{max-height:300px;overflow-y:auto}.h-select-group.h-select-group-has-label .h-select-item{padding:8px 20px}.h-select-group .h-select-item{cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:8px 10px;position:relative}.h-select-group .h-select-item:hover{background-color:#eeeeee}.h-select-group .h-select-item.disabled{cursor:default}.h-select-group .h-select-item.disabled:hover{background-color:transparent}.h-select-group .h-select-item-disabled{color:#999999;cursor:not-allowed}.h-select-group .h-select-item-disabled:hover{background:none}.h-select-group .h-select-item-label{color:#999999;font-size:12px;padding:8px 10px}.h-select-group .h-select-search-input{width:98%;margin:3px 1%}.h-select-group.h-select-single .h-select-item.h-select-item-picked{color:inherit;background-color:#eeeeee}.h-select-group.h-select-single .h-select-item.h-select-item-selected{background-color:rgba(69,185,132,0.15)}.h-select-group.h-select-multiple .h-select-item{padding-right:40px}.h-select-group.h-select-multiple .h-select-item .h-icon-check{display:none;position:absolute;right:10px;font-weight:bold}.h-select-group.h-select-multiple .h-select-item-selected{color:#45b984;background-color:transparent;cursor:pointer}.h-select-group.h-select-multiple .h-select-item-selected:hover{background-color:#eeeeee}.h-select-group.h-select-multiple .h-select-item-selected.disabled{cursor:default}.h-select-group.h-select-multiple .h-select-item-selected.disabled:hover{background-color:transparent}.h-select-group.h-select-multiple .h-select-item-selected .h-icon-check{display:inline-block}.h-select-group.h-select-multiple .h-select-item.h-select-item-picked{background-color:#eeeeee}.h-select-group .h-select-ul{list-style:none;margin:0;padding:0;outline:none;box-sizing:border-box}.h-select-group .h-select-ul-empty{color:#999999;line-height:24px;padding:0 10px}.h-skeleton{display:table;width:100%}@keyframes h-skeleton-loading{0%{background-position:100% 50%}100%{background-position:0 50%}}.h-skeleton .h-skeleton-avatar{display:table-cell;vertical-align:top;padding-right:20px}.h-skeleton .h-skeleton-avatar span{margin-top:16px;display:inline-block;background:#f5f5f5}.h-skeleton .h-skeleton-avatar span.h-skeleton-avatar-circle{border-radius:50%}.h-skeleton .h-skeleton-content{display:table-cell;width:100%}.h-skeleton .h-skeleton-content .h-skeleton-title{height:16px;margin-top:16px;background:#f5f5f5}.h-skeleton .h-skeleton-content .h-skeleton-paragraph{margin-top:24px}.h-skeleton .h-skeleton-content .h-skeleton-paragraph>li{width:100%;height:16px;list-style:none;background:#f5f5f5}.h-skeleton .h-skeleton-content .h-skeleton-paragraph>li+li{margin-top:16px}.h-skeleton.h-skeleton-active .h-skeleton-avatar>span,.h-skeleton.h-skeleton-active .h-skeleton-title,.h-skeleton.h-skeleton-active .h-skeleton-paragraph>li{background:linear-gradient(90deg, #f2f2f2 25%, #e6e6e6 37%, #f2f2f2 63%);background-size:400% 100%;animation:h-skeleton-loading 1.4s ease infinite}.h-slider{padding:10px 5px}.h-slider-container{position:relative;min-height:10px}.h-slider-line,.h-slider-track{height:5px;border-radius:5px;top:50%;transform:translate(0%, -50%);position:absolute;left:0}.h-slider-line{background-color:#eeeeee;right:0}.h-slider-track{background-color:#45b984}.h-slider-node{height:16px;width:16px;position:absolute;background:#45b984;border-radius:16px;margin-left:-8px;top:-3px;transform:scale(.8);transition:box-shadow,transform .2s ease-in-out;user-select:none;cursor:pointer;cursor:-webkit-grab}.h-slider-node:hover,.h-slider-node-dragging{transform:scale(1.1)}.h-slider-node-dragging{box-shadow:0 0 0 2px rgba(69,185,132,0.2);cursor:-webkit-grabbing}.h-steps{display:flex}.h-steps-item{flex:1;transition:.2s;position:relative}.h-steps-index{transition:background .2s}.h-steps-icon{transition:color .3s}.h-steps-success{opacity:0}.h-steps-transverse .h-steps-content{display:inline-block;position:relative}.h-steps-transverse .h-steps-content .h-steps-title{background:#FFF;display:inline-block;padding-right:10px}.h-steps-transverse .h-steps-content .h-steps-icon{float:left;vertical-align:top}.h-steps-transverse .h-steps-content .h-steps-words{display:inline-block}.h-steps-actived .h-steps-icon{color:#45b984}.h-steps-actived .h-steps-success{opacity:1}.h-steps-actived .h-steps-index{border-color:#45b984;background-color:#ffffff}.h-steps-actived .h-steps-index-num{opacity:0}.h-steps-actived .h-steps-tail{border-color:#45b984}.h-steps-process .h-steps-icon{color:#45b984}.h-steps-process .h-steps-title{font-weight:bold}.h-steps-process .h-steps-index{background-color:#45b984;border-color:#45b984;color:#FFF;font-weight:bold}.h-steps-icon{font-size:16px;color:#999999;background:#FFF;display:inline-block;padding-right:10px}.h-steps-icon-custom{font-size:25px;line-height:1;background-color:#ffffff}.h-steps-tail{border-top:1px solid #d3d3d3;position:absolute;left:0;right:10px;top:15px}.h-steps-item-first .h-steps-tail{left:0}.h-steps-item-last{flex:inherit}.h-steps-item-last .h-steps-tail{display:none}.h-steps-content{display:inline-block;position:relative;z-index:1}.h-steps-desc{color:#c1c1c1;font-size:13px}.h-steps-index{background-color:#ffffff;border-radius:50%;border:1px solid #d3d3d3;background-color:#eeeeee;display:inline-block;height:30px;width:30px;text-align:center;position:relative}.h-steps-index>i{font-size:16px;overflow:hidden;display:inline-block;position:absolute;width:30px;text-align:center;left:-1px;font-style:normal;line-height:30px;top:-1px}.h-switchlist{display:inline-block;vertical-align:middle}.h-switchlist>span{padding:8px 15px;border-radius:0;color:inherit;font-size:13px;line-height:1;box-sizing:border-box;cursor:pointer;transition:all .2s;background-color:#FFF;border:1px solid #eeeeee;float:left;position:relative}.h-switchlist>span+span{margin-left:-1px}.h-switchlist>span:hover{z-index:2;color:#45b984}.h-switchlist>span:first-of-type{border-radius:4px 0 0 4px}.h-switchlist>span:last-of-type{border-radius:0 4px 4px 0}.h-switchlist>span[checked]{background-color:#45b984;border-color:#42b27f;color:#ffffff;z-index:3}.h-switchlist>span>i{margin-right:5px}.h-switchlist-small>span{padding:5px 8px}.h-switchlist[disabled]>span{pointer-events:painted;cursor:not-allowed;border-color:#d3d3d3;color:inherit;background-color:#f5f5f5}.h-switchlist[disabled]>span[checked]{background-color:#d3d3d3}.h-switch .h-switch-span{display:inline-block;margin:0px;position:relative;font-family:inherit;outline:none;border:1px solid #d3d3d3;border-radius:16px;box-sizing:border-box;background:#d3d3d3;float:left;margin-right:5px;transition:all .3s}.h-switch .h-switch-span:after{background-color:#ffffff;box-shadow:0 1px 3px rgba(0,0,0,0.4);border:2px solid #ffffff;border:none;top:1px;left:1px}.h-switch .h-switch-span:before{background-color:#d3d3d3;top:0;left:0;bottom:0;right:0}.h-switch .h-switch-span:after,.h-switch .h-switch-span:before{content:" ";position:absolute;border-radius:15px;transition:all .3s}.h-switch .h-switch-span .h-switch-inner{font-size:13px;line-height:26px;height:26px;margin-right:8px;margin-left:30px;color:#666666;position:relative;vertical-align:2px}.h-switch .h-switch-span:checked,.h-switch .h-switch-span[checked]{border-color:#45b984;background:#45b984}.h-switch .h-switch-span:checked:before,.h-switch .h-switch-span[checked]:before{transform:scale(0)}.h-switch .h-switch-span:checked .h-switch-inner,.h-switch .h-switch-span[checked] .h-switch-inner{margin-right:30px;margin-left:8px;color:#FFF}.h-switch .h-switch-span:checked[disabled],.h-switch .h-switch-span[checked][disabled]{background-color:#f5f5f5}.h-switch .h-switch-span[disabled]{border-color:#d3d3d3;background-color:#f5f5f5;pointer-events:painted;cursor:not-allowed}.h-switch .h-switch-span[disabled]:after{border-color:#ffffff}.h-switch .h-switch-span[disabled]:before{background-color:#f5f5f5}.h-switch{display:inline-block;position:relative;cursor:pointer;line-height:28px}.h-switch>.h-switch-text{display:inline-block}.h-switch>.h-switch-span{min-width:48.8px;height:28px}.h-switch>.h-switch-span:after{width:24px;height:24px}.h-switch>.h-switch-span:checked:after,.h-switch>.h-switch-span[checked]:after{left:100%;margin-left:-25px}.h-switch.h-switch-small{line-height:20px}.h-switch.h-switch-small>.h-switch-span{min-width:34.4px;height:20px}.h-switch.h-switch-small>.h-switch-span:after{width:16px;height:16px}.h-switch.h-switch-small>.h-switch-span:checked:after,.h-switch.h-switch-small>.h-switch-span[checked]:after{left:100%;margin-left:-17px}.h-switch.h-switch-small .h-switch-inner{display:none}.h-table{border:1px solid #eeeeee;position:relative}.h-table th{white-space:nowrap;overflow:hidden;text-align:left;height:40px;position:relative}.h-table th,.h-table td{overflow:hidden;text-overflow:ellipsis;white-space:normal;word-break:break-all;box-sizing:border-box;min-width:0;padding:3px 0px 3px 8px;vertical-align:middle}.h-table th.text-right,.h-table td.text-right{padding-right:8px;padding-left:0}.h-table th.text-center,.h-table td.text-center{padding-right:0;padding-left:0}th.h-table-th-checkbox,td.h-table-td-checkbox{text-align:center;text-overflow:clip;padding-left:0}th.h-table-th-radio,td.h-table-td-radio{text-align:center;text-overflow:clip;padding-left:0}.h-table td{font-size:13px;height:40px;border-top:1px solid #eeeeee}.h-table table{table-layout:fixed;border-spacing:0}.h-table-header{background-color:#f5f5f5;position:relative;overflow:hidden;border-bottom:1px solid #eeeeee}.h-table-header table{width:100%}.h-table-body{overflow:auto}.h-table-container{position:relative;margin-top:-1px;background:#fff}.h-table-container.h-loading-parent{min-height:40px}.h-table-container table{width:100%}.h-table-fixed-right,.h-table-fixed-left{z-index:1;position:absolute;top:0;background:#FFF;bottom:0;overflow:hidden}.h-table-fixed-right table,.h-table-fixed-left table{background:#ffffff}.h-table-fixed-right table{float:right}.h-table-fixed-header-right,.h-table-fixed-header-left{z-index:1;position:absolute;top:0;overflow:hidden;border-bottom:1px solid #eeeeee}.h-table-fixed-header-right table,.h-table-fixed-header-left table{background:#f5f5f5}.h-table-fixed-cover{position:absolute;right:0;top:0;bottom:1px;background:#f5f5f5;z-index:2}.h-table-fixed-right,.h-table-fixed-header-right{right:0;box-shadow:-2px 0 6px -2px rgba(0,0,0,0.2)}.h-table-fixed-left,.h-table-fixed-header-left{left:0;box-shadow:2px 0 6px -2px rgba(0,0,0,0.2)}.h-table-border{border-left:0}.h-table-border td,.h-table-border th{border-left:1px solid #eeeeee}.h-table-border th{border-bottom:1px solid #eeeeee}.h-table-border .h-table-header table{margin-bottom:-1px}.h-table-border .h-table-content-empty{border-left:1px solid #eeeeee;border-top:1px solid #eeeeee}.h-table-border .h-table-fixed-right td,.h-table-border .h-table-fixed-header-right td,.h-table-border .h-table-fixed-right th,.h-table-border .h-table-fixed-header-right th{border-right:none}.h-table-stripe .h-table-tbody tr:nth-child(2n){background-color:#fcfcfc}.h-table .h-table-tbody tr.h-table-tr-hovered{background-color:#eeeeee}.h-table .h-table-tbody tr.h-table-tr-hovered.h-table-expand-tr{background-color:transparent}.h-table .h-table-tbody tr.h-table-tr-selected{background-color:rgba(69,185,132,0.15)}.h-table-content-empty{color:#999999;text-align:center;box-sizing:border-box;padding:20px}.h-table-sort-handler{display:inline-block;line-height:1;vertical-align:middle;font-size:12px;color:#d3d3d3;transition:color .2s}th:hover .h-table-sort-handler{color:#999999}.h-table-sort-handler>span{cursor:pointer}.h-table-sort-handler>span.h-table-sort-selected{color:#45b984}.h-table-tr-select-disabled{background:#f5f5f5}.h-table-content-empty-width{height:1px}.h-table-tree-icon{font-size:14px;cursor:pointer;transition:color .2s ease}.h-table-tree-icon:hover{color:#45b984 !important}.h-table-tree-icon:active{color:#30815c !important}.h-table-tree-icon.h-pop-trigger .h-table-tree-icon{color:#379469 !important}.h-table-tree-icon[disabled]{color:#999999 !important;cursor:default}.h-table-tree-empty{display:inline-block;width:14px}.h-table-tree-expand-space{display:inline-block;width:8px}.h-table-tree-opened .h-table-tree-icon{transform:rotate(90deg)}.h-taginput{width:250px;display:inline-block;display:flex;flex-wrap:wrap}.h-taginput-input-border{cursor:default;background-color:#ffffff;border-radius:4px;border:1px solid #eeeeee;vertical-align:top;transition:all linear .2s;padding:2px 2px 0px 2px;font-size:13px;position:relative;box-sizing:border-box}.h-taginput-input-border:hover{border-color:#6ac79d}.h-taginput-input-border.h-pop-trigger,.h-taginput-input-border.focusing{border-color:#45b984;box-shadow:0 0 0 2px rgba(69,185,132,0.2)}.h-form-item-valid-error .h-taginput-input-border{border-color:#E11617}.h-form-item-valid-error .h-taginput-input-border:focus,.h-form-item-valid-error .h-taginput-input-border.focusing{box-shadow:0 0 0 2px rgba(225,22,23,0.2)}input[type=text].h-taginput-input{flex:1;min-width:80px;width:auto;max-width:100%;border-color:transparent !important;line-height:13px;font-size:13px;height:24px;margin-bottom:2px;padding-left:5px}input[type=text].h-taginput-input:focus{box-shadow:none !important}.h-taginput>span{margin-bottom:2px;display:inline-block;border-radius:2px;transition:all linear .2s;margin-right:5px;border:1px solid #eeeeee;background-color:#f5f5f5;font-size:13px;padding:0px 8px}.h-taginput>span>span{line-height:22px}.h-taginput>span>.h-icon-close-min{font-size:16px;color:#666666;display:inline-block;width:14px;margin-left:2px;vertical-align:-2px;cursor:pointer;transition:color .2s ease}.h-taginput>span>.h-icon-close-min:hover{color:#45b984 !important}.h-taginput>span>.h-icon-close-min:active{color:#30815c !important}.h-taginput>span>.h-icon-close-min.h-pop-trigger .h-taginput>span>.h-icon-close-min{color:#379469 !important}.h-taginput>span>.h-icon-close-min[disabled]{color:#999999 !important;cursor:default}.h-taginput>span>.h-icon-close{line-height:22px;display:inline-block;font-size:12px;color:#666666;margin-left:5px;cursor:pointer;transition:color .2s ease}.h-taginput>span>.h-icon-close:hover{color:#45b984 !important}.h-taginput>span>.h-icon-close:active{color:#30815c !important}.h-taginput>span>.h-icon-close.h-pop-trigger .h-taginput>span>.h-icon-close{color:#379469 !important}.h-taginput>span>.h-icon-close[disabled]{color:#999999 !important;cursor:default}.h-form-item-valid-error .h-taginput{border-color:#E11617}.h-form-item-valid-error .h-taginput.focusing{box-shadow:0 0 0 2px rgba(225,22,23,0.2)}.h-tag,.h-tag-circle{display:inline-block;padding:4px 10px;border:1px solid #eeeeee;line-height:1;border-radius:4px;background:#ffffff;font-size:13px;vertical-align:middle;cursor:default;margin-right:5px}.h-tag-circle{padding:4px 4px;border-radius:50%}.h-tag-primary{color:#379469;background:#c7eada;border-color:#c7eada}.h-tag-red{color:#b31112;background:#f6b8b8;border-color:#f6b8b8}.h-tag-blue{color:#0874ff;background:#d0e5ff;border-color:#d0e5ff}.h-tag-yellow{color:#cc8b00;background:#ffe7b3;border-color:#ffe7b3}.h-tag-green{color:#0f9f4f;background:#b3efce;border-color:#b3efce}.h-tag-gray{color:#a8a8a8;background:#fafafa;border-color:#fafafa}.h-tag-bg-primary{color:#ffffff;background:#50be8b;border-color:#50be8b}.h-tag-bg-red{color:#ffffff;background:#e91d1e;border-color:#e91d1e}.h-tag-bg-blue{color:#ffffff;background:#4a9aff;border-color:#4a9aff}.h-tag-bg-yellow{color:#ffffff;background:#ffb30f;border-color:#ffb30f}.h-tag-bg-green{color:#ffffff;background:#14dc6d;border-color:#14dc6d}.h-tag-bg-gray{color:#ffffff;background:#c9c9c9;border-color:#c9c9c9}.h-text-ellipsis-limit-text{word-break:break-all}.h-timeline{list-style:none;margin:0;padding:0 10px}.h-timeline-item{list-style:none;position:relative;display:flex}.h-timeline-item.h-timeline-item-green-color .h-timeline-item-circle{border-color:#13CE66;color:#13CE66}.h-timeline-item.h-timeline-item-red-color .h-timeline-item-circle{border-color:#E11617;color:#E11617}.h-timeline-item.h-timeline-item-yellow-color .h-timeline-item-circle{border-color:#FFAE00;color:#FFAE00}.h-timeline-item-pending .h-timeline-item-tail{display:none}.h-timeline-item-time{width:100px;padding:0px 24px 30px 0px;color:#c1c1c1;font-size:13px;display:none;text-align:right}.h-timeline-item-content{padding:0px 0px 30px 24px;position:relative;flex:1}.h-timeline-item-content:after{content:'';position:absolute;top:3px;bottom:0px;left:-0.5px;border-left:1px dashed #d3d3d3;z-index:1}.h-timeline-item-circle{position:absolute;width:14px;height:14px;border:1px solid #3B91FF;color:#3B91FF;border-radius:14px;left:-7px;top:3px;background:#FFF;z-index:2}.h-timeline-item.has-icon .h-timeline-item-circle{font-size:14px;border:none}.h-timeline-item.has-icon .h-timeline-item-circle [class*=" h-icon"],.h-timeline-item.has-icon .h-timeline-item-circle [class^=h-icon]{vertical-align:2px}.h-timeline-item:last-child .h-timeline-item-content,.h-timeline-item:last-child .h-timeline-item-time{padding-bottom:10px}.h-timeline-item:last-child .h-timeline-item-content:after,.h-timeline-item:last-child .h-timeline-item-time:after{border-left:1px solid transparent}.h-timeline-time .h-timeline-item-time{display:block}.h-timeline-pending .h-timeline-item:nth-last-of-type(2) .h-timeline-item-content{padding-bottom:40px}.h-timeline-pending .h-timeline-item:nth-last-of-type(2) .h-timeline-item-content:after{border-left:1px dotted #eeeeee}.h-transfer{display:flex;flex-direction:row;width:480px}.h-transfer .h-transfer-source,.h-transfer .h-transfer-target{flex:1;border:1px solid #EEE}.h-transfer .h-transfer-source .h-transfer-header,.h-transfer .h-transfer-target .h-transfer-header{height:32px;line-height:32px;background:#f9f9f9;text-align:center;border-bottom:1px solid #EEE}.h-transfer .h-transfer-source .h-transfer-filter input,.h-transfer .h-transfer-target .h-transfer-filter input{width:100%;border-radius:0;border-width:0 0 1px 0;height:28px;outline:none;border-color:#EEE;box-shadow:none}.h-transfer .h-transfer-source .h-transfer-list,.h-transfer .h-transfer-target .h-transfer-list{height:240px;overflow-y:auto;padding:4px 0}.h-transfer .h-transfer-source .h-transfer-list .h-transfer-item,.h-transfer .h-transfer-target .h-transfer-list .h-transfer-item{padding-left:8px;height:32px;line-height:32px;cursor:pointer}.h-transfer .h-transfer-source .h-transfer-list .h-transfer-item .h-checkbox,.h-transfer .h-transfer-target .h-transfer-list .h-transfer-item .h-checkbox,.h-transfer .h-transfer-source .h-transfer-list .h-transfer-item .h-checkbox label,.h-transfer .h-transfer-target .h-transfer-list .h-transfer-item .h-checkbox label{display:block}.h-transfer .h-transfer-source .h-transfer-list .h-transfer-item.text-center,.h-transfer .h-transfer-target .h-transfer-list .h-transfer-item.text-center{padding-left:0}.h-transfer .h-transfer-source .h-transfer-list .h-transfer-item:hover,.h-transfer .h-transfer-target .h-transfer-list .h-transfer-item:hover{background:#f9f9f9}.h-transfer-switch{flex-basis:120px;text-align:center}.h-treepicker{display:inline-block;box-sizing:border-box;vertical-align:middle}.h-treepicker-placeholder{color:#787878}.h-treepicker-value-single,.h-treepicker-placeholder{line-height:24px;height:24px;padding-left:5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.h-treepicker-show{padding-right:25px !important;position:relative;cursor:default;line-height:1.5}.h-treepicker-show>.h-icon-down{color:#666666;position:absolute;right:8px;top:50%;height:10px;font-size:12px;line-height:12px;transform:translate(0, -50%) scale(.8);transition:transform .2s}.h-treepicker-show:hover>.h-icon-down{color:#45b984}.h-treepicker-show.h-pop-trigger>.h-icon-down{transform:rotate(-180deg) translate(0, 50%) scale(.8);color:#45b984}.h-treepicker-multiple-tags{display:flex;flex-wrap:wrap}.h-treepicker-multiple-tags>span{margin-bottom:2px;display:inline-block;border-radius:2px;transition:all linear .2s;margin-right:5px;border:1px solid #eeeeee;background-color:#f5f5f5;font-size:13px;padding:0px 8px}.h-treepicker-multiple-tags>span>span{line-height:22px}.h-treepicker-multiple-tags>span>.h-icon-close-min{font-size:16px;color:#666666;display:inline-block;width:14px;margin-left:2px;vertical-align:-2px;cursor:pointer;transition:color .2s ease}.h-treepicker-multiple-tags>span>.h-icon-close-min:hover{color:#45b984 !important}.h-treepicker-multiple-tags>span>.h-icon-close-min:active{color:#30815c !important}.h-treepicker-multiple-tags>span>.h-icon-close-min.h-pop-trigger .h-treepicker-multiple-tags>span>.h-icon-close-min{color:#379469 !important}.h-treepicker-multiple-tags>span>.h-icon-close-min[disabled]{color:#999999 !important;cursor:default}.h-treepicker-multiple-tags>span>.h-icon-close{line-height:22px;display:inline-block;font-size:12px;color:#666666;margin-left:5px;cursor:pointer;transition:color .2s ease}.h-treepicker-multiple-tags>span>.h-icon-close:hover{color:#45b984 !important}.h-treepicker-multiple-tags>span>.h-icon-close:active{color:#30815c !important}.h-treepicker-multiple-tags>span>.h-icon-close.h-pop-trigger .h-treepicker-multiple-tags>span>.h-icon-close{color:#379469 !important}.h-treepicker-multiple-tags>span>.h-icon-close[disabled]{color:#999999 !important;cursor:default}.h-treepicker-input-border .h-treepicker-show{min-height:30px;cursor:default;background-color:#ffffff;border-radius:4px;border:1px solid #eeeeee;vertical-align:top;transition:all linear .2s;padding:2px 2px 0px 2px;font-size:13px;position:relative;box-sizing:border-box}.h-treepicker-input-border .h-treepicker-show:hover{border-color:#6ac79d}.h-treepicker-input-border .h-treepicker-show.h-pop-trigger,.h-treepicker-input-border .h-treepicker-show.focusing{border-color:#45b984;box-shadow:0 0 0 2px rgba(69,185,132,0.2)}.h-form-item-valid-error .h-treepicker-input-border .h-treepicker-show{border-color:#E11617}.h-form-item-valid-error .h-treepicker-input-border .h-treepicker-show:focus,.h-form-item-valid-error .h-treepicker-input-border .h-treepicker-show.focusing{box-shadow:0 0 0 2px rgba(225,22,23,0.2)}.h-form-item-valid-error .h-treepicker-input-border .h-treepicker-show{border-color:#E11617}.h-form-item-valid-error .h-treepicker-input-border .h-treepicker-show.h-pop-trigger{box-shadow:0 0 0 2px rgba(225,22,23,0.2)}.h-treepicker-input-border .h-treepicker-value-single,.h-treepicker-input-border .h-treepicker-placeholder{margin-bottom:2px}.h-treepicker-no-autosize{display:block}.h-treepicker-disabled .h-treepicker-show{background-color:#f5f5f5;color:#999999 !important;border-color:#d3d3d3}.h-treepicker-disabled .h-treepicker-show:hover{border-color:#d3d3d3;color:#999999 !important}.h-treepicker-disabled .h-treepicker-show .h-icon-down{color:#999999 !important}.h-treepicker-item{padding:3px 5px;cursor:pointer}.h-treepicker-item:hover{background:#eeeeee}.h-treepicker-body{padding:15px;overflow:auto;height:300px;width:300px;box-sizing:border-box}.h-treepicker-footer{padding:5px 10px;text-align:right;border-top:1px solid #eeeeee}.h-tree-body{position:relative}.h-tree .h-search{margin-bottom:5px}.h-tree-li-opened>.h-tree-ul{max-height:10000px;transition:max-height 2s ease-in-out}.h-tree-li-opened>.h-tree-show .h-tree-show-expand .h-icon-angle-right{transform:rotate(90deg)}.h-tree-show{padding:3px 0;position:relative}.h-tree-show-space{display:inline-block;width:10px}.h-tree-show-expand{width:20px;text-align:right;color:#c1c1c1;display:inline-block;padding:0 2px 0 0;cursor:pointer;transition:color .2s ease}.h-tree-show-expand:hover{color:#45b984 !important}.h-tree-show-expand:active{color:#30815c !important}.h-tree-show-expand.h-pop-trigger .h-tree-show-expand{color:#379469 !important}.h-tree-show-expand[disabled]{color:#999999 !important;cursor:default}.h-tree-show-expand .h-icon-loading{font-size:12px;line-height:14px;vertical-align:middle}.h-tree-show-expand .h-icon-angle-right{line-height:14px;transition:.2s;vertical-align:-1px}.h-tree-show-desc{display:inline;word-break:break-all;font-size:13px;padding:2px 5px;margin-left:-4px}.h-tree-show-icon{margin-right:5px}.h-tree-show-disabled .h-tree-show-desc{color:#999999}.h-tree-show-disabled .h-tree-show-desc:hover{background-color:transparent}.h-tree-ul{list-style:none;margin:0;padding:0;outline:none;box-sizing:border-box;transition:max-height .2s cubic-bezier(0, 1, 0, 1);max-height:0;overflow:hidden}.h-tree.h-tree-theme-item-selected .h-tree-show-desc{cursor:pointer;border-radius:4px}.h-tree.h-tree-theme-item-selected.h-tree-single .h-tree-show-desc:hover{background-color:#eeeeee}.h-tree.h-tree-theme-item-selected.h-tree-single .h-tree-show-selected .h-tree-show-desc{background-color:#45b984;color:#ffffff}.h-tree.h-tree-theme-item-selected.h-tree-multiple .h-tree-show-desc:hover{color:#45b984}.h-tree-item-slot{display:inline-block}.h-tree-theme-row-selected .h-tree-show{cursor:pointer}.h-tree-theme-row-selected .h-tree-show:hover{background:#eeeeee}.h-tree-theme-row-selected .h-tree-show.h-tree-show-selected{background:rgba(69,185,132,0.15)}.h-uploader-progress{position:absolute;top:50%;transform:translate(0%, -50%);left:0;right:0;z-index:2}.h-uploader-image,.h-uploader-image-empty{position:relative;float:left;height:70px;width:70px;display:inline-block;border-radius:4px;margin-right:10px}.h-uploader-image:last-of-type,.h-uploader-image-empty:last-of-type{margin-right:0px}.h-uploader-image-background{position:absolute;left:0px;right:0px;top:0px;bottom:0px;z-index:1;background-size:cover;border-radius:4px;background-repeat:no-repeat;background-position:center}.h-uploader-image-operate{position:absolute;left:0;bottom:0;right:0;top:0;background:rgba(0,0,0,0.6);z-index:1;opacity:0;text-align:center;border-radius:4px}.h-uploader-image-operate>div{position:absolute;top:50%;transform:translate(0%, -50%);width:100%;left:0;text-align:center;color:#ffffff}.h-uploader-image-operate>div>span{cursor:pointer;color:#f3f3f3}.h-uploader-image-operate>div>span:hover{color:#ffffff}.h-uploader-image-operate-pointer{cursor:pointer}.h-uploader-image:hover .h-uploader-image-operate{transition:.2s;opacity:1}.h-uploader-image-empty{border:1px dashed #c1c1c1;color:#c1c1c1;transition:.2s;cursor:pointer}.h-uploader-image-empty i{font-size:25px;position:absolute;top:50%;left:50%;transform:translate(-50%, -50%)}.h-uploader-image-empty:hover{border-color:#45b984;color:#45b984}.h-uploader-image-container{display:inline-block}.h-uploader-image-container .h-uploader-image-operate{cursor:pointer}.h-uploader-images-container::after{content:"";display:block;clear:both}.h-uploader-files{position:relative;margin-top:5px;font-size:13px;color:#c1c1c1}.h-uploader-file{position:relative;line-height:30px}.h-uploader-file-progress{padding-left:5px}.h-uploader-file-info{padding-right:15px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer;padding-left:5px}.h-uploader-file-info:hover{background-color:#eeeeee}.h-uploader-file-info.disabled{cursor:default}.h-uploader-file-info.disabled:hover{background-color:transparent}.h-uploader-file .h-icon-trash{right:5px}.h-uploader-drop-element{padding:15px;color:#333333;border-radius:4px;border:1px dashed #d3d3d3;cursor:pointer}.h-uploader-drop-element:hover{border-color:#45b984}.h-uploader-drop-element.h-uploader-dragging{border-color:#45b984}.h-colorpicker{display:inline-block;position:relative;width:30px;height:30px}.h-colorpicker-show{width:30px;height:30px;cursor:default;background-color:#ffffff;border-radius:4px;border:1px solid #eeeeee;vertical-align:top;transition:all linear .2s;padding:2px 2px 0px 2px;font-size:13px;position:relative;box-sizing:border-box;padding:2px}.h-colorpicker-show:hover{border-color:#6ac79d}.h-colorpicker-show.h-pop-trigger,.h-colorpicker-show.focusing{border-color:#45b984;box-shadow:0 0 0 2px rgba(69,185,132,0.2)}.h-form-item-valid-error .h-colorpicker-show{border-color:#E11617}.h-form-item-valid-error .h-colorpicker-show:focus,.h-form-item-valid-error .h-colorpicker-show.focusing{box-shadow:0 0 0 2px rgba(225,22,23,0.2)}.h-colorpicker-show>.h-icon-down{color:#666666;position:absolute;left:50%;top:50%;height:10px;font-size:12px;line-height:12px;transform:translate(-50%, -50%) scale(.8);transition:transform .2s}.h-colorpicker-show.h-pop-trigger>.h-icon-down{transform:rotate(-180deg) translate(50%, 50%) scale(.8)}.h-colorpicker-show>.h-icon-down{color:#c1c1c1;z-index:3}.h-colorpicker-disabled .h-colorpicker-show{border-color:#999999 !important}.h-colorpicker-disabled .h-colorpicker-show>.h-icon-down{color:#999999 !important}.h-colorpicker-color-bg{height:24px;position:absolute;top:2px;width:24px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.h-colorpicker-color-show{position:relative;z-index:2;height:24px}.h-colorpicker-group{outline:none;user-select:none;position:relative;width:270px;padding:10px}.h-colorpicker-panel-input{width:150px}.h-colorpicker-panel-footer{display:flex;justify-content:space-between}.h-colorpicker-panel-buttons{padding-top:2px}.h-colorpicker-panel-buttons .h-colorpicker-clear-button{padding:5px}.h-colorpicker-hue-picker .h-slider-line{background:linear-gradient(to right, red 0%, #ff0 17%, lime 33%, cyan 50%, blue 67%, #f0f 83%, red 100%);height:10px}.h-colorpicker-hue-picker .h-slider-track{display:none}.h-colorpicker-hue-picker .h-slider-node{background:#eeeeee;transform:scale(1.1);box-shadow:none}.h-colorpicker-hue-picker .h-slider-node:hover,.h-colorpicker-hue-picker .h-slider-node-dragging{box-shadow:0 0 2px 0 #c1c1c1}.h-colorpicker-alpha-picker .h-slider-line{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);height:10px;background-size:contain}.h-colorpicker-alpha-picker .h-slider-track{height:10px;right:0 !important;background:linear-gradient(to right, rgba(255,255,255,0) 0%, #FFF 100%)}.h-colorpicker-alpha-picker .h-slider-node{background:#eeeeee;box-shadow:none;transform:scale(1.1)}.h-colorpicker-alpha-picker .h-slider-node:hover,.h-colorpicker-alpha-picker .h-slider-node-dragging{box-shadow:0 0 2px 0 #c1c1c1}.h-colorpicker-colors{margin-top:5px;margin-right:-5px}.h-colorpicker-colors .h-colorpicker-color{display:inline-block;border-radius:4px;height:18px;width:18px;margin-right:10px;margin-bottom:10px;cursor:pointer}.h-colorpicker-colors .h-colorpicker-color:hover{opacity:.8}.h-colorpicker-colors .h-colorpicker-color.h-colorpicker-color-choosed{box-shadow:0 0 2px 0 #333333}.h-color-slider-container{height:150px;position:relative;overflow:hidden}.h-color-slider-bg-white{position:absolute;left:0;bottom:0;right:0;top:0;background:linear-gradient(90deg, #fff, hsla(0,0%,100%,0))}.h-color-slider-bg-black{position:absolute;left:0;bottom:0;right:0;top:0;background:linear-gradient(0deg, #000, transparent)}.h-color-slider-node{position:absolute;width:14px;height:14px;border-radius:7px;margin-left:-7px;margin-top:-7px;border:1px solid #FFF;cursor:pointer;box-shadow:0 0 2px 0 #000}
\ No newline at end of file
diff --git a/src/static/favicon.ico b/src/static/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..007fb8b074b77c0bdae38279bfd9b749e86c1097
Binary files /dev/null and b/src/static/favicon.ico differ
diff --git a/src/static/img/cat.png b/src/static/img/cat.png
new file mode 100644
index 0000000000000000000000000000000000000000..bb4d5c6e7a027459ad8b0df5adb4153d7fe64beb
Binary files /dev/null and b/src/static/img/cat.png differ
diff --git a/src/static/index.html b/src/static/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..3df6a84df33a387c7c704071818543991d5f6e94
--- /dev/null
+++ b/src/static/index.html
@@ -0,0 +1,83 @@
+
+
+
+
+ Rule DSL Engine
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/static/js/common.js b/src/static/js/common.js
new file mode 100644
index 0000000000000000000000000000000000000000..24f2664ab11f397516a6244b9cf41784a1034237
--- /dev/null
+++ b/src/static/js/common.js
@@ -0,0 +1,310 @@
+// web socket
+function ws(cfg) {
+ cfg = cfg || {};
+ // web socket
+ let client = window.client;
+ if (client && (client.readyState == 1 || client.readyState == 2)) {
+ console.log('Websocket连接可用不用重创建. state: ' + client.readyState);
+ return;
+ }
+ function doCreate() {
+ console.log('create websocket ...');
+ try {
+ let protocol = "ws://";
+ if (window.location.protocol.startsWith("https")) protocol = "wss://";
+ client = new WebSocket(protocol + window.location.host + (cfg.path || "/test/msg"));
+ window.client = client;
+ } catch (e) {
+ console.log('创建websocket错误', e);
+ setTimeout(function () {
+ ws(cfg);
+ }, cfg.reconnection || (1000 * 60 * 2)); // 每两分钟重试
+ return
+ }
+ client.onclose = cfg.onClose || function (e) {
+ console.log('websocket close', e);
+ // setTimeout(function () {
+ // ws(cfg)
+ // }, cfg.reconnection || (1000 * 60 * 2)); // 每两分钟重试
+ };
+ client.onmessage = cfg.onMsg || function (e) { //接收websocket 消息
+ let jo = toJSON(e.data);
+ if (jo) {
+ // {type: 'xxx': data: null}
+ // if (jo.type == 'xxx') {}
+ if (jo.data.msg) {
+ app.$Notice.info({title: '后台提示', content: jo.data.msg, duration: 5})
+ }
+ } else {
+ app.$Notice.info({title: '后台提示', content: e.data, duration: 7})
+ }
+ };
+ client.onopen = cfg.onOpen || function() {
+ console.log('websocket onopen');
+ client.send('成功连接...');
+ };
+ }
+ doCreate()
+}
+
+function toJSON(str) {
+ if (typeof str == 'string') {
+ try {
+ let obj = JSON.parse(str);
+ return obj
+ } catch(e) {}
+ }
+ return null
+}
+
+
+// 异步加载全局js库: js库名 -> 路径/[路径, 全局变量名]
+let jsMap = new Map([
+ ['md5', 'js/lib/md5.min.js'],
+ ['ace', ['js/lib/ace-1.4.12.js', 'ace']],
+ ['ace-tools', 'js/lib/ext-language_tools.min.js'],
+ ['ace-ext-searchbox', 'js/lib/ext-searchbox.js'],
+ ['ace-lang-rule', 'js/lib/mode-rule.js'],
+ ['ace-snip-rule', 'js/lib/rule-snippets.js'],
+ ['ace-lang-groovy', 'js/lib/mode-groovy.js'],
+ ['ace-lang-json', 'js/lib/mode-json.js'],
+ ['ace-snip-groovy', 'js/lib/groovy-snippets.min.js'],
+ ['moment', 'js/lib/moment.min.js'],
+ ['echarts', ['js/lib/echarts.min.js', 'echarts']],
+]);
+function loadJs() {
+ if (arguments.length < 1) return;
+ let names = [...arguments];
+ let cb = arguments[arguments.length - 1]; // 最后一个参数可为 回调函数
+ if (typeof cb == "string") {
+ cb = null
+ } else {
+ names.pop()
+ }
+ let length = names.length;
+ names.forEach(((item, index) => {
+ length--
+ let value = jsMap.get(item);
+ if (!value) {
+ if (length === 0 && cb) cb();
+ return;
+ }
+ let path = value;
+ if (value instanceof Array) {
+ if (typeof window[(value[1])] !== "undefined") {
+ path = null;
+ if (length === 0 && cb) cb();
+ } else {
+ path = value[0]
+ }
+ }
+ if (path) {
+ $.ajax({
+ url: path,
+ success: (res) => {
+ let script = document.createElement( "script" );
+ script.text = res;
+ document.head.appendChild(script).parentNode.removeChild(script);
+ jsMap.delete(item); //只加载一次
+ if (length === 0 && cb) cb();
+ }
+ })
+ }
+ }));
+}
+
+
+// ================定义公用组件=====================
+// 时间戳格式化组件
+Vue.component('date-item', (resolve, reject) => {
+ loadJs('moment', () => {
+ resolve({
+ props: ['time', 'format'],
+ template: '{{timeStr}} ',
+ computed: {
+ timeStr: function () {
+ if (!this.time) return '';
+ if (this.format) return moment(this.time).format(this.format);
+ else return moment(this.time).format('YYYY-MM-DD HH:mm:ss')
+ }
+ }
+ })
+ })
+});
+
+// json 编辑器
+Vue.component('ace-json', (resolve, reject) => {
+ loadJs('ace', () => {
+ ace.config.set("basePath", "js/lib");
+ loadJs('ace-tools', 'ace-lang-json', () => {
+ resolve({
+ model: {prop: 'content', event: 'update'},
+ props: {content: String, readonly: Boolean, placeholder: String},
+ template: '
',
+ data() {
+ return {
+ editor: null
+ }
+ },
+ mounted: function () {
+ this.$nextTick(this.initEditor)
+ },
+ watch: {
+ content(v) {
+ if (this.editor && v != this.editor.session.getValue()) {
+ this.editor.session.setValue(v);
+ }
+ }
+ },
+ methods: {
+ initEditor() {
+ // if (this.editor) {this.editor.destroy()}
+ this.editor = ace.edit(this.$refs.editor);
+ if (this.content) this.editor.session.setValue(this.content);
+ this.editor.setReadOnly(this.readonly == true ? true : false);
+ this.editor.setOptions({
+ enableBasicAutocompletion: true,
+ enableSnippets: true,
+ enableLiveAutocompletion: true,
+ placeholder: this.placeholder || '',
+ });
+ this.editor.on('change', (e) => {
+ // this.content = this.editor.session.getValue();
+ this.$emit('update', this.editor.session.getValue())
+ });
+ this.editor.session.setMode('ace/mode/json');
+ this.editor.commands.addCommand({
+ name: 'save',
+ bindKey: {win: 'Ctrl-S', mac: 'Command-S'},
+ exec: (editor) => {
+ // this.content = this.editor.session.getValue();
+ this.$emit('update', this.editor.session.getValue());
+ this.$emit('save', this.editor.session.getValue());
+ },
+ // readOnly: false // 如果不需要使用只读模式,这里设置false
+ });
+ },
+ }
+ })
+ });
+ });
+});
+
+// groovy 编辑器
+Vue.component('ace-groovy', (resolve, reject) => {
+ loadJs('ace', () => {
+ ace.config.set("basePath", "js/lib");
+ loadJs('ace-tools', 'ace-lang-groovy', 'ace-snip-groovy', () => {
+ resolve({
+ model: {prop: 'content', event: 'update'},
+ props: {content: String, readonly: Boolean, placeholder: String},
+ template: '
',
+ data() {
+ return {
+ editor: null
+ }
+ },
+ beforeDestroy() {
+ if (this.editor) {this.editor.destroy()}
+ },
+ mounted: function () {
+ this.$nextTick(this.initEditor)
+ },
+ watch: {
+ content(v) {
+ if (this.editor && v != this.editor.session.getValue()) {
+ this.editor.session.setValue(v);
+ }
+ }
+ },
+ methods: {
+ initEditor() {
+ // if (this.editor) {this.editor.destroy()}
+ this.editor = ace.edit(this.$refs.editor);
+ if (this.content) this.editor.session.setValue(this.content);
+ this.editor.setReadOnly(this.readonly == true ? true : false);
+ this.editor.setOptions({
+ enableBasicAutocompletion: true,
+ enableSnippets: true,
+ enableLiveAutocompletion: true,
+ placeholder: this.placeholder || '',
+ });
+ this.editor.on('change', (e) => {
+ // this.content = this.editor.session.getValue();
+ this.$emit('update', this.editor.session.getValue())
+ });
+ this.editor.session.setMode('ace/mode/groovy');
+ //this.editor.execCommand('find');
+ this.editor.commands.addCommand({
+ name: 'save',
+ bindKey: {win: 'Ctrl-S', mac: 'Command-S'},
+ exec: (editor) => {
+ // this.content = this.editor.session.getValue();
+ this.$emit('update', this.editor.session.getValue());
+ this.$emit('save', this.editor.session.getValue());
+ },
+ // readOnly: false // 如果不需要使用只读模式,这里设置false
+ });
+ // let languageTools = ace.require("ace/ext/language_tools");
+ // console.log('languageTools', ace.require("ace/ext/language_tools"));
+ // languageTools.addCompleter({
+ // getCompletions: (editor, session, pos, prefix, callback) => {
+ // callback(null, [
+ // {
+ // name : "第一行", //名称
+ // value : "身份证号码",//值,这就是匹配我们输入的内容,比如输入s或者select,这一行就会出现在提示框里,可根据自己需求修改,就是你想输入什么显示出北京呢,就改成什么
+ // caption: "身",//字幕,下拉提示左侧内容,这也就是我们输入前缀匹配出来的内容,所以这里必须包含我们的前缀
+ // meta: "字段-身份证号码", //类型,下拉提示右侧内容
+ // type: "local",//可写为keyword
+ // score : 1000 // 让它排在最上面,类似权值的概念
+ // },
+ // {
+ // name : "年龄", //名称
+ // value : "年龄",//值,这就是匹配我们输入的内容,比如输入s或者select,这一行就会出现在提示框里,可根据自己需求修改,就是你想输入什么显示出北京呢,就改成什么
+ // caption: "年",//字幕,下拉提示左侧内容,这也就是我们输入前缀匹配出来的内容,所以这里必须包含我们的前缀
+ // meta: "字段-年龄", //类型,下拉提示右侧内容
+ // type: "local",//可写为keyword
+ // score : 1000 // 让它排在最上面,类似权值的概念
+ // }
+ // ]);
+ // }
+ // });
+ },
+ }
+ })
+ });
+ });
+});
+
+
+// 异步加载组件
+new Map([
+ ['Upload', 'components/Upload.vue'],
+ ['Admin', 'views/Admin.vue'],
+ ['Login', 'views/Login.vue'],
+ ['Dashboard', 'views/Dashboard.vue'],
+ ['PolicyCenter', 'views/config/PolicyCenter.vue'],
+ ['DecisionConfig', 'views/config/DecisionConfig.vue'],
+ ['DecisionDetail', 'views/config/DecisionDetail.vue'],
+ ['PolicyDetail', 'views/config/PolicyDetail.vue'],
+ ['RuleDetail', 'views/config/RuleDetail.vue'],
+ ['FieldConfig', 'views/config/FieldConfig.vue'],
+ ['DataCollectorConfig', 'views/config/DataCollectorConfig.vue'],
+ ['OpHistory', 'views/config/OpHistory.vue'],
+ ['UserCenter', 'views/config/UserCenter.vue'],
+ ['UserConfig', 'views/config/UserConfig.vue'],
+ ['Permission', 'views/config/Permission.vue'],
+ ['DecisionData', 'views/data/DecisionData.vue'],
+ ['DecisionResult', 'views/data/DecideResult.vue'],
+ ['CollectResult', 'views/data/CollectResult.vue'],
+ ['DataAnalyse', 'views/data/DataAnalyse.vue'],
+ ['DecisionEChart', 'views/data/DecisionEChart.vue'],
+ ['RuleEChart', 'views/data/RuleEChart.vue'],
+ ['Clock', 'views/Clock.vue'],
+ ['StatusSuspension', 'views/StatusSuspension.vue'],
+ ['MyInfo', 'views/MyInfo.vue'],
+]).forEach((path, key) => {
+ Vue.component(key, (resolve, reject) => {
+ httpVueLoader(path)().then((r) => resolve(r))
+ });
+});
diff --git a/src/static/js/lib/ace-1.4.12.js b/src/static/js/lib/ace-1.4.12.js
new file mode 100644
index 0000000000000000000000000000000000000000..6bd2b01ae5d5bc207c6eaac3640c1ecc528bd09d
--- /dev/null
+++ b/src/static/js/lib/ace-1.4.12.js
@@ -0,0 +1,17 @@
+(function(){function o(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]);if(!i.define||!i.define.packaged)t.original=i.define,i.define=t,i.define.packaged=!0;if(!i.require||!i.require.packaged)r.original=i.require,i.require=r,i.require.packaged=!0}var ACE_NAMESPACE="",e=function(){return this}();!e&&typeof window!="undefined"&&(e=window);if(!ACE_NAMESPACE&&typeof requirejs!="undefined")return;var t=function(e,n,r){if(typeof e!="string"){t.original?t.original.apply(this,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(r=n),t.modules[e]||(t.payloads[e]=r,t.modules[e]=null)};t.modules={},t.payloads={};var n=function(e,t,n){if(typeof t=="string"){var i=s(e,t);if(i!=undefined)return n&&n(),i}else if(Object.prototype.toString.call(t)==="[object Array]"){var o=[];for(var u=0,a=t.length;u=0?parseFloat((s.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]):parseFloat((s.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=s.match(/ Gecko\/\d+/),t.isOpera=typeof opera=="object"&&Object.prototype.toString.call(window.opera)=="[object Opera]",t.isWebKit=parseFloat(s.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(s.split(" Chrome/")[1])||undefined,t.isEdge=parseFloat(s.split(" Edge/")[1])||undefined,t.isAIR=s.indexOf("AdobeAIR")>=0,t.isAndroid=s.indexOf("Android")>=0,t.isChromeOS=s.indexOf(" CrOS ")>=0,t.isIOS=/iPad|iPhone|iPod/.test(s)&&!window.MSStream,t.isIOS&&(t.isMac=!0),t.isMobile=t.isIOS||t.isAndroid}),define("ace/lib/dom",["require","exports","module","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("./useragent"),i="http://www.w3.org/1999/xhtml";t.buildDom=function o(e,t,n){if(typeof e=="string"&&e){var r=document.createTextNode(e);return t&&t.appendChild(r),r}if(!Array.isArray(e))return e&&e.appendChild&&t&&t.appendChild(e),e;if(typeof e[0]!="string"||!e[0]){var i=[];for(var s=0;s=1.5:!0;if(typeof document!="undefined"){var s=document.createElement("div");t.HI_DPI&&s.style.transform!==undefined&&(t.HAS_CSS_TRANSFORMS=!0),!r.isEdge&&typeof s.style.animationName!="undefined"&&(t.HAS_CSS_ANIMATION=!0),s=null}t.HAS_CSS_TRANSFORMS?t.translate=function(e,t,n){e.style.transform="translate("+Math.round(t)+"px, "+Math.round(n)+"px)"}:t.translate=function(e,t,n){e.style.top=Math.round(n)+"px",e.style.left=Math.round(t)+"px"}}),define("ace/lib/oop",["require","exports","module"],function(e,t,n){"use strict";t.inherits=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})},t.mixin=function(e,t){for(var n in t)e[n]=t[n];return e},t.implement=function(e,n){t.mixin(e,n)}}),define("ace/lib/keys",["require","exports","module","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./oop"),i=function(){var e={MODIFIER_KEYS:{16:"Shift",17:"Ctrl",18:"Alt",224:"Meta",91:"MetaLeft",92:"MetaRight",93:"ContextMenu"},KEY_MODS:{ctrl:1,alt:2,option:2,shift:4,"super":8,meta:8,command:8,cmd:8,control:1},FUNCTION_KEYS:{8:"Backspace",9:"Tab",13:"Return",19:"Pause",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"Print",45:"Insert",46:"Delete",96:"Numpad0",97:"Numpad1",98:"Numpad2",99:"Numpad3",100:"Numpad4",101:"Numpad5",102:"Numpad6",103:"Numpad7",104:"Numpad8",105:"Numpad9","-13":"NumpadEnter",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Numlock",145:"Scrolllock"},PRINTABLE_KEYS:{32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",61:"=",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",107:"+",109:"-",110:".",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'",111:"/",106:"*"}},t,n;for(n in e.FUNCTION_KEYS)t=e.FUNCTION_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);for(n in e.PRINTABLE_KEYS)t=e.PRINTABLE_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);return r.mixin(e,e.MODIFIER_KEYS),r.mixin(e,e.PRINTABLE_KEYS),r.mixin(e,e.FUNCTION_KEYS),e.enter=e["return"],e.escape=e.esc,e.del=e["delete"],e[173]="-",function(){var t=["cmd","ctrl","alt","shift"];for(var n=Math.pow(2,t.length);n--;)e.KEY_MODS[n]=t.filter(function(t){return n&e.KEY_MODS[t]}).join("-")+"-"}(),e.KEY_MODS[0]="",e.KEY_MODS[-1]="input-",e}();r.mixin(t,i),t.keyCodeToString=function(e){var t=i[e];return typeof t!="string"&&(t=String.fromCharCode(e)),t.toLowerCase()}}),define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function a(){u=!1;try{document.createComment("").addEventListener("test",function(){},{get passive(){u={passive:!1}}})}catch(e){}}function f(){return u==undefined&&a(),u}function l(e,t,n){this.elem=e,this.type=t,this.callback=n}function d(e,t,n){var u=p(t);if(!i.isMac&&s){t.getModifierState&&(t.getModifierState("OS")||t.getModifierState("Win"))&&(u|=8);if(s.altGr){if((3&u)==3)return;s.altGr=0}if(n===18||n===17){var a="location"in t?t.location:t.keyLocation;if(n===17&&a===1)s[n]==1&&(o=t.timeStamp);else if(n===18&&u===3&&a===2){var f=t.timeStamp-o;f<50&&(s.altGr=!0)}}}n in r.MODIFIER_KEYS&&(n=-1);if(!u&&n===13){var a="location"in t?t.location:t.keyLocation;if(a===3){e(t,u,-n);if(t.defaultPrevented)return}}if(i.isChromeOS&&u&8){e(t,u,n);if(t.defaultPrevented)return;u&=-9}return!!u||n in r.FUNCTION_KEYS||n in r.PRINTABLE_KEYS?e(t,u,n):!1}function v(){s=Object.create(null)}var r=e("./keys"),i=e("./useragent"),s=null,o=0,u;l.prototype.destroy=function(){h(this.elem,this.type,this.callback),this.elem=this.type=this.callback=undefined};var c=t.addListener=function(e,t,n,r){e.addEventListener(t,n,f()),r&&r.$toDestroy.push(new l(e,t,n))},h=t.removeListener=function(e,t,n){e.removeEventListener(t,n,f())};t.stopEvent=function(e){return t.stopPropagation(e),t.preventDefault(e),!1},t.stopPropagation=function(e){e.stopPropagation&&e.stopPropagation()},t.preventDefault=function(e){e.preventDefault&&e.preventDefault()},t.getButton=function(e){return e.type=="dblclick"?0:e.type=="contextmenu"||i.isMac&&e.ctrlKey&&!e.altKey&&!e.shiftKey?2:e.button},t.capture=function(e,t,n){function i(e){t&&t(e),n&&n(e),h(r,"mousemove",t),h(r,"mouseup",i),h(r,"dragstart",i)}var r=e&&e.ownerDocument||document;return c(r,"mousemove",t),c(r,"mouseup",i),c(r,"dragstart",i),i},t.addMouseWheelListener=function(e,t,n){"onmousewheel"in e?c(e,"mousewheel",function(e){var n=8;e.wheelDeltaX!==undefined?(e.wheelX=-e.wheelDeltaX/n,e.wheelY=-e.wheelDeltaY/n):(e.wheelX=0,e.wheelY=-e.wheelDelta/n),t(e)},n):"onwheel"in e?c(e,"wheel",function(e){var n=.35;switch(e.deltaMode){case e.DOM_DELTA_PIXEL:e.wheelX=e.deltaX*n||0,e.wheelY=e.deltaY*n||0;break;case e.DOM_DELTA_LINE:case e.DOM_DELTA_PAGE:e.wheelX=(e.deltaX||0)*5,e.wheelY=(e.deltaY||0)*5}t(e)},n):c(e,"DOMMouseScroll",function(e){e.axis&&e.axis==e.HORIZONTAL_AXIS?(e.wheelX=(e.detail||0)*5,e.wheelY=0):(e.wheelX=0,e.wheelY=(e.detail||0)*5),t(e)},n)},t.addMultiMouseDownListener=function(e,n,r,s,o){function p(e){t.getButton(e)!==0?u=0:e.detail>1?(u++,u>4&&(u=1)):u=1;if(i.isIE){var o=Math.abs(e.clientX-a)>5||Math.abs(e.clientY-f)>5;if(!l||o)u=1;l&&clearTimeout(l),l=setTimeout(function(){l=null},n[u-1]||600),u==1&&(a=e.clientX,f=e.clientY)}e._clicks=u,r[s]("mousedown",e);if(u>4)u=0;else if(u>1)return r[s](h[u],e)}var u=0,a,f,l,h={2:"dblclick",3:"tripleclick",4:"quadclick"};Array.isArray(e)||(e=[e]),e.forEach(function(e){c(e,"mousedown",p,o)})};var p=function(e){return 0|(e.ctrlKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.metaKey?8:0)};t.getModifierString=function(e){return r.KEY_MODS[p(e)]},t.addCommandKeyListener=function(e,n,r){if(i.isOldGecko||i.isOpera&&!("KeyboardEvent"in window)){var o=null;c(e,"keydown",function(e){o=e.keyCode},r),c(e,"keypress",function(e){return d(n,e,o)},r)}else{var u=null;c(e,"keydown",function(e){s[e.keyCode]=(s[e.keyCode]||0)+1;var t=d(n,e,e.keyCode);return u=e.defaultPrevented,t},r),c(e,"keypress",function(e){u&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),u=null)},r),c(e,"keyup",function(e){s[e.keyCode]=null},r),s||(v(),c(window,"focus",v))}};if(typeof window=="object"&&window.postMessage&&!i.isOldIE){var m=1;t.nextTick=function(e,n){n=n||window;var r="zero-timeout-message-"+m++,i=function(s){s.data==r&&(t.stopPropagation(s),h(n,"message",i),e())};c(n,"message",i),n.postMessage(r,"*")}}t.$idleBlocked=!1,t.onIdle=function(e,n){return setTimeout(function r(){t.$idleBlocked?setTimeout(r,100):e()},n)},t.$idleBlockId=null,t.blockIdle=function(e){t.$idleBlockId&&clearTimeout(t.$idleBlockId),t.$idleBlocked=!0,t.$idleBlockId=setTimeout(function(){t.$idleBlocked=!1},e||100)},t.nextFrame=typeof window=="object"&&(window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame),t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),define("ace/range",["require","exports","module"],function(e,t,n){"use strict";var r=function(e,t){return e.row-t.row||e.column-t.column},i=function(e,t,n,r){this.start={row:e,column:t},this.end={row:n,column:r}};(function(){this.isEqual=function(e){return this.start.row===e.start.row&&this.end.row===e.end.row&&this.start.column===e.start.column&&this.end.column===e.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?tthis.end.column?1:0:ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.rowt)var r={row:t+1,column:0};else if(this.start.row0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;nDate.now()-50?!0:r=!1},cancel:function(){r=Date.now()}}}),define("ace/keyboard/textinput",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/lib/dom","ace/lib/lang","ace/clipboard","ace/lib/keys"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("../lib/dom"),o=e("../lib/lang"),u=e("../clipboard"),a=i.isChrome<18,f=i.isIE,l=i.isChrome>63,c=400,h=e("../lib/keys"),p=h.KEY_MODS,d=i.isIOS,v=d?/\s/:/\n/,m=i.isMobile,g=function(e,t){function X(){x=!0,n.blur(),n.focus(),x=!1}function $(e){e.keyCode==27&&n.value.lengthC&&T[s]=="\n")o=h.end;else if(rC&&T.slice(0,s).split("\n").length>2)o=h.down;else if(s>C&&T[s-1]==" ")o=h.right,u=p.option;else if(s>C||s==C&&C!=N&&r==s)o=h.right;r!==s&&(u|=p.shift);if(o){var a=t.onCommandKey({},u,o);if(!a&&t.commands){o=h.keyCodeToString(o);var f=t.commands.findKeyCommand(u,o);f&&t.execCommand(f)}N=r,C=s,O("")}};document.addEventListener("selectionchange",s),t.on("destroy",function(){document.removeEventListener("selectionchange",s)})}var n=s.createElement("textarea");n.className="ace_text-input",n.setAttribute("wrap","off"),n.setAttribute("autocorrect","off"),n.setAttribute("autocapitalize","off"),n.setAttribute("spellcheck",!1),n.style.opacity="0",e.insertBefore(n,e.firstChild);var g=!1,y=!1,b=!1,w=!1,E="";m||(n.style.fontSize="1px");var S=!1,x=!1,T="",N=0,C=0,k=0;try{var L=document.activeElement===n}catch(A){}r.addListener(n,"blur",function(e){if(x)return;t.onBlur(e),L=!1},t),r.addListener(n,"focus",function(e){if(x)return;L=!0;if(i.isEdge)try{if(!document.hasFocus())return}catch(e){}t.onFocus(e),i.isEdge?setTimeout(O):O()},t),this.$focusScroll=!1,this.focus=function(){if(E||l||this.$focusScroll=="browser")return n.focus({preventScroll:!0});var e=n.style.top;n.style.position="fixed",n.style.top="0px";try{var t=n.getBoundingClientRect().top!=0}catch(r){return}var i=[];if(t){var s=n.parentElement;while(s&&s.nodeType==1)i.push(s),s.setAttribute("ace_nocontext",!0),!s.parentElement&&s.getRootNode?s=s.getRootNode().host:s=s.parentElement}n.focus({preventScroll:!0}),t&&i.forEach(function(e){e.removeAttribute("ace_nocontext")}),setTimeout(function(){n.style.position="",n.style.top=="0px"&&(n.style.top=e)},0)},this.blur=function(){n.blur()},this.isFocused=function(){return L},t.on("beforeEndOperation",function(){var e=t.curOp,r=e&&e.command&&e.command.name;if(r=="insertstring")return;var i=r&&(e.docChanged||e.selectionChanged);b&&i&&(T=n.value="",W()),O()});var O=d?function(e){if(!L||g&&!e||w)return;e||(e="");var r="\n ab"+e+"cde fg\n";r!=n.value&&(n.value=T=r);var i=4,s=4+(e.length||(t.selection.isEmpty()?0:1));(N!=i||C!=s)&&n.setSelectionRange(i,s),N=i,C=s}:function(){if(b||w)return;if(!L&&!P)return;b=!0;var e=0,r=0,i="";if(t.session){var s=t.selection,o=s.getRange(),u=s.cursor.row;e=o.start.column,r=o.end.column,i=t.session.getLine(u);if(o.start.row!=u){var a=t.session.getLine(u-1);e=o.start.rowu+1?f.length:r,r+=i.length+1,i=i+"\n"+f}else m&&u>0&&(i="\n"+i,r+=1,e+=1);i.length>c&&(e=T.length&&e.value===T&&T&&e.selectionEnd!==C},_=function(e){if(b)return;g?g=!1:M(n)?(t.selectAll(),O()):m&&n.selectionStart!=N&&O()},D=null;this.setInputHandler=function(e){D=e},this.getInputHandler=function(){return D};var P=!1,H=function(e,r){P&&(P=!1);if(y)return O(),e&&t.onPaste(e),y=!1,"";var s=n.selectionStart,o=n.selectionEnd,u=N,a=T.length-C,f=e,l=e.length-s,c=e.length-o,h=0;while(u>0&&T[h]==e[h])h++,u--;f=f.slice(h),h=1;while(a>0&&T.length-h>N-1&&T[T.length-h]==e[e.length-h])h++,a--;l-=h-1,c-=h-1;var p=f.length-h+1;p<0&&(u=-p,p=0),f=f.slice(0,p);if(!r&&!f&&!l&&!u&&!a&&!c)return"";w=!0;var d=!1;return i.isAndroid&&f==". "&&(f=" ",d=!0),f&&!u&&!a&&!l&&!c||S?t.onTextInput(f):t.onTextInput(f,{extendLeft:u,extendRight:a,restoreStart:l,restoreEnd:c}),w=!1,T=e,N=s,C=o,k=c,d?"\n":f},B=function(e){if(b)return z();if(e&&e.inputType){if(e.inputType=="historyUndo")return t.execCommand("undo");if(e.inputType=="historyRedo")return t.execCommand("redo")}var r=n.value,i=H(r,!0);(r.length>c+100||v.test(i)||m&&N<1&&N==C)&&O()},j=function(e,t,n){var r=e.clipboardData||window.clipboardData;if(!r||a)return;var i=f||n?"Text":"text/plain";try{return t?r.setData(i,t)!==!1:r.getData(i)}catch(e){if(!n)return j(e,t,!0)}},F=function(e,i){var s=t.getCopyText();if(!s)return r.preventDefault(e);j(e,s)?(d&&(O(s),g=s,setTimeout(function(){g=!1},10)),i?t.onCut():t.onCopy(),r.preventDefault(e)):(g=!0,n.value=s,n.select(),setTimeout(function(){g=!1,O(),i?t.onCut():t.onCopy()}))},I=function(e){F(e,!0)},q=function(e){F(e,!1)},R=function(e){var s=j(e);if(u.pasteCancelled())return;typeof s=="string"?(s&&t.onPaste(s,e),i.isIE&&setTimeout(O),r.preventDefault(e)):(n.value="",y=!0)};r.addCommandKeyListener(n,t.onCommandKey.bind(t),t),r.addListener(n,"select",_,t),r.addListener(n,"input",B,t),r.addListener(n,"cut",I,t),r.addListener(n,"copy",q,t),r.addListener(n,"paste",R,t),(!("oncut"in n)||!("oncopy"in n)||!("onpaste"in n))&&r.addListener(e,"keydown",function(e){if(i.isMac&&!e.metaKey||!e.ctrlKey)return;switch(e.keyCode){case 67:q(e);break;case 86:R(e);break;case 88:I(e)}},t);var U=function(e){if(b||!t.onCompositionStart||t.$readOnly)return;b={};if(S)return;e.data&&(b.useTextareaForIME=!1),setTimeout(z,0),t._signal("compositionStart"),t.on("mousedown",X);var r=t.getSelectionRange();r.end.row=r.start.row,r.end.column=r.start.column,b.markerRange=r,b.selectionStart=N,t.onCompositionStart(b),b.useTextareaForIME?(T=n.value="",N=0,C=0):(n.msGetInputContext&&(b.context=n.msGetInputContext()),n.getInputContext&&(b.context=n.getInputContext()))},z=function(){if(!b||!t.onCompositionUpdate||t.$readOnly)return;if(S)return X();if(b.useTextareaForIME)t.onCompositionUpdate(n.value);else{var e=n.value;H(e),b.markerRange&&(b.context&&(b.markerRange.start.column=b.selectionStart=b.context.compositionStartOffset),b.markerRange.end.column=b.markerRange.start.column+C-b.selectionStart+k)}},W=function(e){if(!t.onCompositionEnd||t.$readOnly)return;b=!1,t.onCompositionEnd(),t.off("mousedown",X),e&&B()},V=o.delayedCall(z,50).schedule.bind(null,null);r.addListener(n,"compositionstart",U,t),r.addListener(n,"compositionupdate",z,t),r.addListener(n,"keyup",$,t),r.addListener(n,"keydown",V,t),r.addListener(n,"compositionend",W,t),this.getElement=function(){return n},this.setCommandMode=function(e){S=e,n.readOnly=!1},this.setReadOnly=function(e){S||(n.readOnly=e)},this.setCopyWithEmptySelection=function(e){},this.onContextMenu=function(e){P=!0,O(),t._emit("nativecontextmenu",{target:t,domEvent:e}),this.moveToMouse(e,!0)},this.moveToMouse=function(e,o){E||(E=n.style.cssText),n.style.cssText=(o?"z-index:100000;":"")+(i.isIE?"opacity:0.1;":"")+"text-indent: -"+(N+C)*t.renderer.characterWidth*.5+"px;";var u=t.container.getBoundingClientRect(),a=s.computedStyle(t.container),f=u.top+(parseInt(a.borderTopWidth)||0),l=u.left+(parseInt(u.borderLeftWidth)||0),c=u.bottom-f-n.clientHeight-2,h=function(e){s.translate(n,e.clientX-l-2,Math.min(e.clientY-f-2,c))};h(e);if(e.type!="mousedown")return;t.renderer.$isMousePressed=!0,clearTimeout(J),i.isWin&&r.capture(t.container,h,K)},this.onContextMenuClose=K;var J,Q=function(e){t.textInput.onContextMenu(e),K()};r.addListener(n,"mouseup",Q,t),r.addListener(n,"mousedown",function(e){e.preventDefault(),K()},t),r.addListener(t.renderer.scroller,"contextmenu",Q,t),r.addListener(n,"contextmenu",Q,t),d&&G(e,t,n)};t.TextInput=g,t.$setUserAgentForTests=function(e,t){m=e,d=t}}),define("ace/mouse/default_handlers",["require","exports","module","ace/lib/useragent"],function(e,t,n){"use strict";function o(e){e.$clickSelection=null;var t=e.editor;t.setDefaultHandler("mousedown",this.onMouseDown.bind(e)),t.setDefaultHandler("dblclick",this.onDoubleClick.bind(e)),t.setDefaultHandler("tripleclick",this.onTripleClick.bind(e)),t.setDefaultHandler("quadclick",this.onQuadClick.bind(e)),t.setDefaultHandler("mousewheel",this.onMouseWheel.bind(e));var n=["select","startSelect","selectEnd","selectAllEnd","selectByWordsEnd","selectByLinesEnd","dragWait","dragWaitEnd","focusWait"];n.forEach(function(t){e[t]=this[t]},this),e.selectByLines=this.extendSelectionBy.bind(e,"getLineRange"),e.selectByWords=this.extendSelectionBy.bind(e,"getWordRange")}function u(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}function a(e,t){if(e.start.row==e.end.row)var n=2*t.column-e.start.column-e.end.column;else if(e.start.row==e.end.row-1&&!e.start.column&&!e.end.column)var n=t.column-4;else var n=2*t.row-e.start.row-e.end.row;return n<0?{cursor:e.start,anchor:e.end}:{cursor:e.end,anchor:e.start}}var r=e("../lib/useragent"),i=0,s=550;(function(){this.onMouseDown=function(e){var t=e.inSelection(),n=e.getDocumentPosition();this.mousedownEvent=e;var i=this.editor,s=e.getButton();if(s!==0){var o=i.getSelectionRange(),u=o.isEmpty();(u||s==1)&&i.selection.moveToPosition(n),s==2&&(i.textInput.onContextMenu(e.domEvent),r.isMozilla||e.preventDefault());return}this.mousedownEvent.time=Date.now();if(t&&!i.isFocused()){i.focus();if(this.$focusTimeout&&!this.$clickSelection&&!i.inMultiSelectMode){this.setState("focusWait"),this.captureMouse(e);return}}return this.captureMouse(e),this.startSelect(n,e.domEvent._clicks>1),e.preventDefault()},this.startSelect=function(e,t){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y);var n=this.editor;if(!this.mousedownEvent)return;this.mousedownEvent.getShiftKey()?n.selection.selectToPosition(e):t||n.selection.moveToPosition(e),t||this.select(),n.renderer.scroller.setCapture&&n.renderer.scroller.setCapture(),n.setStyle("ace_selecting"),this.setState("select")},this.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);if(this.$clickSelection){var r=this.$clickSelection.comparePoint(n);if(r==-1)e=this.$clickSelection.end;else if(r==1)e=this.$clickSelection.start;else{var i=a(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.renderer.scrollCursorIntoView()},this.extendSelectionBy=function(e){var t,n=this.editor,r=n.renderer.screenToTextCoordinates(this.x,this.y),i=n.selection[e](r.row,r.column);if(this.$clickSelection){var s=this.$clickSelection.comparePoint(i.start),o=this.$clickSelection.comparePoint(i.end);if(s==-1&&o<=0){t=this.$clickSelection.end;if(i.end.row!=r.row||i.end.column!=r.column)r=i.start}else if(o==1&&s>=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=a(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.renderer.scrollCursorIntoView()},this.selectEnd=this.selectAllEnd=this.selectByWordsEnd=this.selectByLinesEnd=function(){this.$clickSelection=null,this.editor.unsetStyle("ace_selecting"),this.editor.renderer.scroller.releaseCapture&&this.editor.renderer.scroller.releaseCapture()},this.focusWait=function(){var e=u(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>i||t-this.mousedownEvent.time>this.$focusTimeout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);i?(i.isEmpty()&&(i.start.column--,i.end.column++),this.setState("select")):(i=n.selection.getWordRange(t.row,t.column),this.setState("selectByWords")),this.$clickSelection=i,this.select()},this.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState("selectByLines");var r=n.getSelectionRange();r.isMultiLine()&&r.contains(t.row,t.column)?(this.$clickSelection=n.selection.getLineRange(r.start.row),this.$clickSelection.end=n.selection.getLineRange(r.end.row).end):this.$clickSelection=n.selection.getLineRange(t.row),this.select()},this.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},this.onMouseWheel=function(e){if(e.getAccelKey())return;e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0);var t=this.editor;this.$lastScroll||(this.$lastScroll={t:0,vx:0,vy:0,allowed:0});var n=this.$lastScroll,r=e.domEvent.timeStamp,i=r-n.t,o=i?e.wheelX/i:n.vx,u=i?e.wheelY/i:n.vy;i=1&&t.renderer.isScrollableBy(e.wheelX*e.speed,0)&&(f=!0),a<=1&&t.renderer.isScrollableBy(0,e.wheelY*e.speed)&&(f=!0);if(f)n.allowed=r;else if(r-n.allowedt.session.documentToScreenRow(l.row,l.column))return c()}if(f==s)return;f=s.text.join(" "),i.setHtml(f),i.show(),t._signal("showGutterTooltip",i),t.on("mousewheel",c);if(e.$tooltipFollowsMouse)h(u);else{var p=u.domEvent.target,d=p.getBoundingClientRect(),v=i.getElement().style;v.left=d.right+"px",v.top=d.bottom+"px"}}function c(){o&&(o=clearTimeout(o)),f&&(i.hide(),f=null,t._signal("hideGutterTooltip",i),t.off("mousewheel",c))}function h(e){i.setPosition(e.x,e.y)}var t=e.editor,n=t.renderer.$gutterLayer,i=new a(t.container);e.editor.setDefaultHandler("guttermousedown",function(r){if(!t.isFocused()||r.getButton()!=0)return;var i=n.getRegion(r);if(i=="foldWidgets")return;var s=r.getDocumentPosition().row,o=t.session.selection;if(r.getShiftKey())o.selectTo(s,0);else{if(r.domEvent.detail==2)return t.selectAll(),r.preventDefault();e.$clickSelection=t.selection.getLineRange(s)}return e.setState("selectByLines"),e.captureMouse(r),r.preventDefault()});var o,u,f;e.editor.setDefaultHandler("guttermousemove",function(t){var n=t.domEvent.target||t.domEvent.srcElement;if(r.hasCssClass(n,"ace_fold-widget"))return c();f&&e.$tooltipFollowsMouse&&h(t),u=t;if(o)return;o=setTimeout(function(){o=null,u&&!e.isMousePressed?l():c()},50)}),s.addListener(t.renderer.$gutter,"mouseout",function(e){u=null;if(!f||o)return;o=setTimeout(function(){o=null,c()},50)},t),t.on("changeSession",c)}function a(e){o.call(this,e)}var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/event"),o=e("../tooltip").Tooltip;i.inherits(a,o),function(){this.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,r=window.innerHeight||document.documentElement.clientHeight,i=this.getWidth(),s=this.getHeight();e+=15,t+=15,e+i>n&&(e-=e+i-n),t+s>r&&(t-=20+s),o.prototype.setPosition.call(this,e,t)}}.call(a.prototype),t.GutterHandler=u}),define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=t.MouseEvent=function(e,t){this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){r.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){r.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){return this.$pos?this.$pos:(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY),this.$pos)},this.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var e=this.editor,t=e.getSelectionRange();if(t.isEmpty())this.$inSelection=!1;else{var n=this.getDocumentPosition();this.$inSelection=t.contains(n.row,n.column)}return this.$inSelection},this.getButton=function(){return r.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=i.isMac?function(){return this.domEvent.metaKey}:function(){return this.domEvent.ctrlKey}}).call(s.prototype)}),define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function f(e){function T(e,n){var r=Date.now(),i=!n||e.row!=n.row,s=!n||e.column!=n.column;if(!S||i||s)t.moveCursorToPosition(e),S=r,x={x:p,y:d};else{var o=l(x.x,x.y,p,d);o>a?S=null:r-S>=u&&(t.renderer.scrollCursorIntoView(),S=null)}}function N(e,n){var r=Date.now(),i=t.renderer.layerConfig.lineHeight,s=t.renderer.layerConfig.characterWidth,u=t.renderer.scroller.getBoundingClientRect(),a={x:{left:p-u.left,right:u.right-p},y:{top:d-u.top,bottom:u.bottom-d}},f=Math.min(a.x.left,a.x.right),l=Math.min(a.y.top,a.y.bottom),c={row:e.row,column:e.column};f/s<=2&&(c.column+=a.x.left=o&&t.renderer.scrollCursorIntoView(c):E=r:E=null}function C(){var e=g;g=t.renderer.screenToTextCoordinates(p,d),T(g,e),N(g,e)}function k(){m=t.selection.toOrientedRange(),h=t.session.addMarker(m,"ace_selection",t.getSelectionStyle()),t.clearSelection(),t.isFocused()&&t.renderer.$cursorLayer.setBlinking(!1),clearInterval(v),C(),v=setInterval(C,20),y=0,i.addListener(document,"mousemove",O)}function L(){clearInterval(v),t.session.removeMarker(h),h=null,t.selection.fromOrientedRange(m),t.isFocused()&&!w&&t.$resetCursorStyle(),m=null,g=null,y=0,E=null,S=null,i.removeListener(document,"mousemove",O)}function O(){A==null&&(A=setTimeout(function(){A!=null&&h&&L()},20))}function M(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return e=="text/plain"||e=="Text"})}function _(e){var t=["copy","copymove","all","uninitialized"],n=["move","copymove","linkmove","all","uninitialized"],r=s.isMac?e.altKey:e.ctrlKey,i="uninitialized";try{i=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o="none";return r&&t.indexOf(i)>=0?o="copy":n.indexOf(i)>=0?o="move":t.indexOf(i)>=0&&(o="copy"),o}var t=e.editor,n=r.createElement("img");n.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",s.isOpera&&(n.style.cssText="width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;");var f=["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"];f.forEach(function(t){e[t]=this[t]},this),t.on("mousedown",this.onMouseDown.bind(e));var c=t.container,h,p,d,v,m,g,y=0,b,w,E,S,x;this.onDragStart=function(e){if(this.cancelDrag||!c.draggable){var r=this;return setTimeout(function(){r.startSelect(),r.captureMouse(e)},0),e.preventDefault()}m=t.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=t.getReadOnly()?"copy":"copyMove",s.isOpera&&(t.container.appendChild(n),n.scrollTop=0),i.setDragImage&&i.setDragImage(n,0,0),s.isOpera&&t.container.removeChild(n),i.clearData(),i.setData("Text",t.session.getTextRange()),w=!0,this.setState("drag")},this.onDragEnd=function(e){c.draggable=!1,w=!1,this.setState(null);if(!t.getReadOnly()){var n=e.dataTransfer.dropEffect;!b&&n=="move"&&t.session.remove(t.getSelectionRange()),t.$resetCursorStyle()}this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle("")},this.onDragEnter=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||k(),y++,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragOver=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||(k(),y++),A!==null&&(A=null),e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragLeave=function(e){y--;if(y<=0&&h)return L(),b=null,i.preventDefault(e)},this.onDrop=function(e){if(!g)return;var n=e.dataTransfer;if(w)switch(b){case"move":m.contains(g.row,g.column)?m={start:g,end:g}:m=t.moveText(m,g);break;case"copy":m=t.moveText(m,g,!0)}else{var r=n.getData("Text");m={start:g,end:t.session.insert(g,r)},t.focus(),b=null}return L(),i.preventDefault(e)},i.addListener(c,"dragstart",this.onDragStart.bind(e),t),i.addListener(c,"dragend",this.onDragEnd.bind(e),t),i.addListener(c,"dragenter",this.onDragEnter.bind(e),t),i.addListener(c,"dragover",this.onDragOver.bind(e),t),i.addListener(c,"dragleave",this.onDragLeave.bind(e),t),i.addListener(c,"drop",this.onDrop.bind(e),t);var A=null}function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=200,u=200,a=5;(function(){this.dragWait=function(){var e=Date.now()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.$resetCursorStyle(),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=e.container;t.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging");var n=s.isWin?"default":"move";e.renderer.setCursorStyle(n),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;if(s.isIE&&this.state=="dragReady"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>3&&t.dragDrop()}if(this.state==="dragWait"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(!this.$dragEnabled)return;this.mousedownEvent=e;var t=this.editor,n=e.inSelection(),r=e.getButton(),i=e.domEvent.detail||1;if(i===1&&r===0&&n){if(e.editor.inMultiSelectMode&&(e.getAccelKey()||e.getShiftKey()))return;this.mousedownEvent.time=Date.now();var o=e.domEvent.target||e.domEvent.srcElement;"unselectable"in o&&(o.unselectable="on");if(t.getDragDelay()){if(s.isWebKit){this.cancelDrag=!0;var u=t.container;u.draggable=!0}this.setState("dragWait")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}).call(f.prototype),t.DragdropHandler=f}),define("ace/mouse/touch_handler",["require","exports","module","ace/mouse/mouse_event","ace/lib/event","ace/lib/dom"],function(e,t,n){"use strict";var r=e("./mouse_event").MouseEvent,i=e("../lib/event"),s=e("../lib/dom");t.addTouchListeners=function(e,t){function b(){var e=window.navigator&&window.navigator.clipboard,r=!1,i=function(){var n=t.getCopyText(),i=t.session.getUndoManager().hasUndo();y.replaceChild(s.buildDom(r?["span",!n&&["span",{"class":"ace_mobile-button",action:"selectall"},"Select All"],n&&["span",{"class":"ace_mobile-button",action:"copy"},"Copy"],n&&["span",{"class":"ace_mobile-button",action:"cut"},"Cut"],e&&["span",{"class":"ace_mobile-button",action:"paste"},"Paste"],i&&["span",{"class":"ace_mobile-button",action:"undo"},"Undo"],["span",{"class":"ace_mobile-button",action:"find"},"Find"],["span",{"class":"ace_mobile-button",action:"openCommandPallete"},"Pallete"]]:["span"]),y.firstChild)},o=function(n){var s=n.target.getAttribute("action");if(s=="more"||!r)return r=!r,i();if(s=="paste")e.readText().then(function(e){t.execCommand(s,e)});else if(s){if(s=="cut"||s=="copy")e?e.writeText(t.getCopyText()):document.execCommand("copy");t.execCommand(s)}y.firstChild.style.display="none",r=!1,s!="openCommandPallete"&&t.focus()};y=s.buildDom(["div",{"class":"ace_mobile-menu",ontouchstart:function(e){n="menu",e.stopPropagation(),e.preventDefault(),t.textInput.focus()},ontouchend:function(e){e.stopPropagation(),e.preventDefault(),o(e)},onclick:o},["span"],["span",{"class":"ace_mobile-button",action:"more"},"..."]],t.container)}function w(){y||b();var e=t.selection.cursor,n=t.renderer.textToScreenCoordinates(e.row,e.column),r=t.renderer.textToScreenCoordinates(0,0).pageX,i=t.renderer.scrollLeft,s=t.container.getBoundingClientRect();y.style.top=n.pageY-s.top-3+"px",n.pageX-s.left=2?t.selection.getLineRange(p.row):t.session.getBracketRange(p);e&&!e.isEmpty()?t.selection.setRange(e):t.selection.selectWord(),n="wait"}function T(){h+=60,c=setInterval(function(){h--<=0&&(clearInterval(c),c=null),Math.abs(v)<.01&&(v=0),Math.abs(m)<.01&&(m=0),h<20&&(v=.9*v),h<20&&(m=.9*m);var e=t.session.getScrollTop();t.renderer.scrollBy(10*v,10*m),e==t.session.getScrollTop()&&(h=0)},10)}var n="scroll",o,u,a,f,l,c,h=0,p,d=0,v=0,m=0,g,y;i.addListener(e,"contextmenu",function(e){if(!g)return;var n=t.textInput.getElement();n.focus()},t),i.addListener(e,"touchstart",function(e){var i=e.touches;if(l||i.length>1){clearTimeout(l),l=null,a=-1,n="zoom";return}g=t.$mouseHandler.isMousePressed=!0;var s=t.renderer.layerConfig.lineHeight,c=t.renderer.layerConfig.lineHeight,y=e.timeStamp;f=y;var b=i[0],w=b.clientX,E=b.clientY;Math.abs(o-w)+Math.abs(u-E)>s&&(a=-1),o=e.clientX=w,u=e.clientY=E,v=m=0;var T=new r(e,t);p=T.getDocumentPosition();if(y-a<500&&i.length==1&&!h)d++,e.preventDefault(),e.button=0,x();else{d=0;var N=t.selection.cursor,C=t.selection.isEmpty()?N:t.selection.anchor,k=t.renderer.$cursorLayer.getPixelPosition(N,!0),L=t.renderer.$cursorLayer.getPixelPosition(C,!0),A=t.renderer.scroller.getBoundingClientRect(),O=t.renderer.layerConfig.offset,M=t.renderer.scrollLeft,_=function(e,t){return e/=c,t=t/s-.75,e*e+t*t};if(e.clientXP?"cursor":"anchor"),P<3.5?n="anchor":D<3.5?n="cursor":n="scroll",l=setTimeout(S,450)}a=y},t),i.addListener(e,"touchend",function(e){g=t.$mouseHandler.isMousePressed=!1,c&&clearInterval(c),n=="zoom"?(n="",h=0):l?(t.selection.moveToPosition(p),h=0,w()):n=="scroll"?(T(),E()):w(),clearTimeout(l),l=null},t),i.addListener(e,"touchmove",function(e){l&&(clearTimeout(l),l=null);var i=e.touches;if(i.length>1||n=="zoom")return;var s=i[0],a=o-s.clientX,c=u-s.clientY;if(n=="wait"){if(!(a*a+c*c>4))return e.preventDefault();n="cursor"}o=s.clientX,u=s.clientY,e.clientX=s.clientX,e.clientY=s.clientY;var h=e.timeStamp,p=h-f;f=h;if(n=="scroll"){var d=new r(e,t);d.speed=1,d.wheelX=a,d.wheelY=c,10*Math.abs(a)1&&(i=n[n.length-2]);var o=a[t+"Path"];return o==null?o=a.basePath:r=="/"&&(t=r=""),o&&o.slice(-1)!="/"&&(o+="/"),o+t+r+i+this.get("suffix")},t.setModuleUrl=function(e,t){return a.$moduleUrls[e]=t},t.$loading={},t.loadModule=function(n,r){var i,o;Array.isArray(n)&&(o=n[0],n=n[1]);try{i=e(n)}catch(u){}if(i&&!t.$loading[n])return r&&r(i);t.$loading[n]||(t.$loading[n]=[]),t.$loading[n].push(r);if(t.$loading[n].length>1)return;var a=function(){e([n],function(e){t._emit("load.module",{name:n,module:e});var r=t.$loading[n];t.$loading[n]=null,r.forEach(function(t){t&&t(e)})})};if(!t.get("packaged"))return a();s.loadScript(t.moduleUrl(n,o),a),f()};var f=function(){!a.basePath&&!a.workerPath&&!a.modePath&&!a.themePath&&!Object.keys(a.$moduleUrls).length&&(console.error("Unable to infer path to ace from script src,","use ace.config.set('basePath', 'path') to enable dynamic loading of modes and themes","or with webpack use ace/webpack-resolver"),f=function(){})};t.init=l,t.version="1.4.12"}),define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event","ace/mouse/dragdrop_handler","ace/mouse/touch_handler","ace/config"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("./default_handlers").DefaultHandlers,o=e("./default_gutter_handler").GutterHandler,u=e("./mouse_event").MouseEvent,a=e("./dragdrop_handler").DragdropHandler,f=e("./touch_handler").addTouchListeners,l=e("../config"),c=function(e){var t=this;this.editor=e,new s(this),new o(this),new a(this);var n=function(t){var n=!document.hasFocus||!document.hasFocus()||!e.isFocused()&&document.activeElement==(e.textInput&&e.textInput.getElement());n&&window.focus(),e.focus()},u=e.renderer.getMouseEventTarget();r.addListener(u,"click",this.onMouseEvent.bind(this,"click"),e),r.addListener(u,"mousemove",this.onMouseMove.bind(this,"mousemove"),e),r.addMultiMouseDownListener([u,e.renderer.scrollBarV&&e.renderer.scrollBarV.inner,e.renderer.scrollBarH&&e.renderer.scrollBarH.inner,e.textInput&&e.textInput.getElement()].filter(Boolean),[400,300,250],this,"onMouseEvent",e),r.addMouseWheelListener(e.container,this.onMouseWheel.bind(this,"mousewheel"),e),f(e.container,e);var l=e.renderer.$gutter;r.addListener(l,"mousedown",this.onMouseEvent.bind(this,"guttermousedown"),e),r.addListener(l,"click",this.onMouseEvent.bind(this,"gutterclick"),e),r.addListener(l,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick"),e),r.addListener(l,"mousemove",this.onMouseEvent.bind(this,"guttermousemove"),e),r.addListener(u,"mousedown",n,e),r.addListener(l,"mousedown",n,e),i.isIE&&e.renderer.scrollBarV&&(r.addListener(e.renderer.scrollBarV.element,"mousedown",n,e),r.addListener(e.renderer.scrollBarH.element,"mousedown",n,e)),e.on("mousemove",function(n){if(t.state||t.$dragDelay||!t.$dragEnabled)return;var r=e.renderer.screenToTextCoordinates(n.x,n.y),i=e.session.selection.getRange(),s=e.renderer;!i.isEmpty()&&i.insideStart(r.row,r.column)?s.setCursorStyle("default"):s.setCursorStyle("")},e)};(function(){this.onMouseEvent=function(e,t){this.editor._emit(e,new u(t,this.editor))},this.onMouseMove=function(e,t){var n=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!n||!n.length)return;this.editor._emit(e,new u(t,this.editor))},this.onMouseWheel=function(e,t){var n=new u(t,this.editor);n.speed=this.$scrollSpeed*2,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.setState=function(e){this.state=e},this.captureMouse=function(e,t){this.x=e.x,this.y=e.y,this.isMousePressed=!0;var n=this.editor,s=this.editor.renderer;s.$isMousePressed=!0;var o=this,a=function(e){if(!e)return;if(i.isWebKit&&!e.which&&o.releaseMouse)return o.releaseMouse();o.x=e.clientX,o.y=e.clientY,t&&t(e),o.mouseEvent=new u(e,o.editor),o.$mouseMoved=!0},f=function(e){n.off("beforeEndOperation",c),clearInterval(h),l(),o[o.state+"End"]&&o[o.state+"End"](e),o.state="",o.isMousePressed=s.$isMousePressed=!1,s.$keepTextAreaAtCursor&&s.$moveTextAreaToCursor(),o.$onCaptureMouseMove=o.releaseMouse=null,e&&o.onMouseEvent("mouseup",e),n.endOperation()},l=function(){o[o.state]&&o[o.state](),o.$mouseMoved=!1};if(i.isOldIE&&e.domEvent.type=="dblclick")return setTimeout(function(){f(e)});var c=function(e){if(!o.releaseMouse)return;n.curOp.command.name&&n.curOp.selectionChanged&&(o[o.state+"End"]&&o[o.state+"End"](),o.state="",o.releaseMouse())};n.on("beforeEndOperation",c),n.startOperation({command:{name:"mouse"}}),o.$onCaptureMouseMove=a,o.releaseMouse=r.capture(this.editor.container,a,f);var h=setInterval(l,20)},this.releaseMouse=null,this.cancelContextMenu=function(){var e=function(t){if(t&&t.domEvent&&t.domEvent.type!="contextmenu")return;this.editor.off("nativecontextmenu",e),t&&t.domEvent&&r.stopEvent(t.domEvent)}.bind(this);setTimeout(e,10),this.editor.on("nativecontextmenu",e)},this.destroy=function(){this.releaseMouse&&this.releaseMouse()}}).call(c.prototype),l.defineOptions(c.prototype,"mouseHandler",{scrollSpeed:{initialValue:2},dragDelay:{initialValue:i.isMac?150:0},dragEnabled:{initialValue:!0},focusTimeout:{initialValue:0},tooltipFollowsMouse:{initialValue:!0}}),t.MouseHandler=c}),define("ace/mouse/fold_handler",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";function i(e){e.on("click",function(t){var n=t.getDocumentPosition(),i=e.session,s=i.getFoldAt(n.row,n.column,1);s&&(t.getAccelKey()?i.removeFold(s):i.expandFold(s),t.stop());var o=t.domEvent&&t.domEvent.target;o&&r.hasCssClass(o,"ace_inline_button")&&r.hasCssClass(o,"ace_toggle_wrap")&&(i.setOption("wrap",!i.getUseWrapMode()),e.renderer.scrollCursorIntoView())}),e.on("gutterclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session;i.foldWidgets&&i.foldWidgets[r]&&e.session.onFoldWidgetClick(r,t),e.isFocused()||e.focus(),t.stop()}}),e.on("gutterdblclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session,s=i.getParentFoldRangeData(r,!0),o=s.range||s.firstRange;if(o){r=o.start.row;var u=i.getFoldAt(r,i.getLine(r).length,1);u?i.removeFold(u):(i.addFold("...",o),e.renderer.scrollCursorIntoView({row:o.start.row,column:0}))}t.stop()}})}var r=e("../lib/dom");t.FoldHandler=i}),define("ace/keyboard/keybinding",["require","exports","module","ace/lib/keys","ace/lib/event"],function(e,t,n){"use strict";var r=e("../lib/keys"),i=e("../lib/event"),s=function(e){this.$editor=e,this.$data={editor:e},this.$handlers=[],this.setDefaultHandler(e.commands)};(function(){this.setDefaultHandler=function(e){this.removeKeyboardHandler(this.$defaultHandler),this.$defaultHandler=e,this.addKeyboardHandler(e,0)},this.setKeyboardHandler=function(e){var t=this.$handlers;if(t[t.length-1]==e)return;while(t[t.length-1]&&t[t.length-1]!=this.$defaultHandler)this.removeKeyboardHandler(t[t.length-1]);this.addKeyboardHandler(e,1)},this.addKeyboardHandler=function(e,t){if(!e)return;typeof e=="function"&&!e.handleKeyboard&&(e.handleKeyboard=e);var n=this.$handlers.indexOf(e);n!=-1&&this.$handlers.splice(n,1),t==undefined?this.$handlers.push(e):this.$handlers.splice(t,0,e),n==-1&&e.attach&&e.attach(this.$editor)},this.removeKeyboardHandler=function(e){var t=this.$handlers.indexOf(e);return t==-1?!1:(this.$handlers.splice(t,1),e.detach&&e.detach(this.$editor),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.getStatusText=function(){var e=this.$data,t=e.editor;return this.$handlers.map(function(n){return n.getStatusText&&n.getStatusText(t,e)||""}).filter(Boolean).join(" ")},this.$callKeyboardHandlers=function(e,t,n,r){var s,o=!1,u=this.$editor.commands;for(var a=this.$handlers.length;a--;){s=this.$handlers[a].handleKeyboard(this.$data,e,t,n,r);if(!s||!s.command)continue;s.command=="null"?o=!0:o=u.exec(s.command,this.$editor,s.args,r),o&&r&&e!=-1&&s.passEvent!=1&&s.command.passEvent!=1&&i.stopEvent(r);if(o)break}return!o&&e==-1&&(s={command:"insertstring"},o=u.exec("insertstring",this.$editor,t)),o&&this.$editor._signal&&this.$editor._signal("keyboardActivity",s),o},this.onCommandKey=function(e,t,n){var i=r.keyCodeToString(n);return this.$callKeyboardHandlers(t,i,n,e)},this.onTextInput=function(e){return this.$callKeyboardHandlers(-1,e)}}).call(s.prototype),t.KeyBinding=s}),define("ace/lib/bidiutil",["require","exports","module"],function(e,t,n){"use strict";function F(e,t,n,r){var i=s?d:p,c=null,h=null,v=null,m=0,g=null,y=null,b=-1,w=null,E=null,T=[];if(!r)for(w=0,r=[];w0)if(g==16){for(w=b;w-1){for(w=b;w=0;C--){if(r[C]!=N)break;t[C]=s}}}function I(e,t,n){if(o=e){u=i+1;while(u=e)u++;for(a=i,l=u-1;a=t.length||(o=n[r-1])!=b&&o!=w||(c=t[r+1])!=b&&c!=w)return E;return u&&(c=w),c==o?c:E;case k:o=r>0?n[r-1]:S;if(o==b&&r+10&&n[r-1]==b)return b;if(u)return E;p=r+1,h=t.length;while(p=1425&&d<=2303||d==64286;o=t[p];if(v&&(o==y||o==T))return y}if(r<1||(o=t[r-1])==S)return E;return n[r-1];case S:return u=!1,f=!0,s;case x:return l=!0,E;case O:case M:case D:case P:case _:u=!1;case H:return E}}function R(e){var t=e.charCodeAt(0),n=t>>8;return n==0?t>191?g:B[t]:n==5?/[\u0591-\u05f4]/.test(e)?y:g:n==6?/[\u0610-\u061a\u064b-\u065f\u06d6-\u06e4\u06e7-\u06ed]/.test(e)?A:/[\u0660-\u0669\u066b-\u066c]/.test(e)?w:t==1642?L:/[\u06f0-\u06f9]/.test(e)?b:T:n==32&&t<=8287?j[t&255]:n==254?t>=65136?T:E:E}function U(e){return e>="\u064b"&&e<="\u0655"}var r=["\u0621","\u0641"],i=["\u063a","\u064a"],s=0,o=0,u=!1,a=!1,f=!1,l=!1,c=!1,h=!1,p=[[0,3,0,1,0,0,0],[0,3,0,1,2,2,0],[0,3,0,17,2,0,1],[0,3,5,5,4,1,0],[0,3,21,21,4,0,1],[0,3,5,5,4,2,0]],d=[[2,0,1,1,0,1,0],[2,0,1,1,0,2,0],[2,0,2,1,3,2,0],[2,0,2,33,3,1,1]],v=0,m=1,g=0,y=1,b=2,w=3,E=4,S=5,x=6,T=7,N=8,C=9,k=10,L=11,A=12,O=13,M=14,_=15,D=16,P=17,H=18,B=[H,H,H,H,H,H,H,H,H,x,S,x,N,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,S,S,S,x,N,E,E,L,L,L,E,E,E,E,E,k,C,k,C,C,b,b,b,b,b,b,b,b,b,b,C,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,H,H,H,H,H,H,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,C,E,L,L,L,L,E,E,E,E,g,E,E,H,E,E,L,L,b,b,E,g,E,E,E,b,g,E,E,E,E,E],j=[N,N,N,N,N,N,N,N,N,N,N,H,H,H,g,y,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,N,S,O,M,_,D,P,C,L,L,L,L,L,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,C,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,N];t.L=g,t.R=y,t.EN=b,t.ON_R=3,t.AN=4,t.R_H=5,t.B=6,t.RLE=7,t.DOT="\u00b7",t.doBidiReorder=function(e,n,r){if(e.length<2)return{};var i=e.split(""),o=new Array(i.length),u=new Array(i.length),a=[];s=r?m:v,F(i,a,i.length,n);for(var f=0;fT&&n[f]0&&i[f-1]==="\u0644"&&/\u0622|\u0623|\u0625|\u0627/.test(i[f])&&(a[f-1]=a[f]=t.R_H,f++);i[i.length-1]===t.DOT&&(a[i.length-1]=t.B),i[0]==="\u202b"&&(a[0]=t.RLE);for(var f=0;f=0&&(e=this.session.$docRowCache[n])}return e},this.getSplitIndex=function(){var e=0,t=this.session.$screenRowCache;if(t.length){var n,r=this.session.$getRowCacheIndex(t,this.currentRow);while(this.currentRow-e>0){n=this.session.$getRowCacheIndex(t,this.currentRow-e-1);if(n!==r)break;r=n,e++}}else e=this.currentRow;return e},this.updateRowLine=function(e,t){e===undefined&&(e=this.getDocumentRow());var n=e===this.session.getLength()-1,s=n?this.EOF:this.EOL;this.wrapIndent=0,this.line=this.session.getLine(e),this.isRtlDir=this.$isRtl||this.line.charAt(0)===this.RLE;if(this.session.$useWrapMode){var o=this.session.$wrapData[e];o&&(t===undefined&&(t=this.getSplitIndex()),t>0&&o.length?(this.wrapIndent=o.indent,this.wrapOffset=this.wrapIndent*this.charWidths[r.L],this.line=tt?this.session.getOverwrite()?e:e-1:t,i=r.getVisualFromLogicalIdx(n,this.bidiMap),s=this.bidiMap.bidiLevels,o=0;!this.session.getOverwrite()&&e<=t&&s[i]%2!==0&&i++;for(var u=0;ut&&s[i]%2===0&&(o+=this.charWidths[s[i]]),this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset),this.isRtlDir&&(o+=this.rtlLineOffset),o},this.getSelections=function(e,t){var n=this.bidiMap,r=n.bidiLevels,i,s=[],o=0,u=Math.min(e,t)-this.wrapIndent,a=Math.max(e,t)-this.wrapIndent,f=!1,l=!1,c=0;this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset);for(var h,p=0;p=u&&hn+s/2){n+=s;if(r===i.length-1){s=0;break}s=this.charWidths[i[++r]]}return r>0&&i[r-1]%2!==0&&i[r]%2===0?(e0&&i[r-1]%2===0&&i[r]%2!==0?t=1+(e>n?this.bidiMap.logicalFromVisual[r]:this.bidiMap.logicalFromVisual[r-1]):this.isRtlDir&&r===i.length-1&&s===0&&i[r-1]%2===0||!this.isRtlDir&&r===0&&i[r]%2!==0?t=1+this.bidiMap.logicalFromVisual[r]:(r>0&&i[r-1]%2!==0&&s!==0&&r--,t=this.bidiMap.logicalFromVisual[r]),t===0&&this.isRtlDir&&t++,t+this.wrapIndent}}).call(o.prototype),t.BidiHandler=o}),define("ace/selection",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/range"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=function(e){this.session=e,this.doc=e.getDocument(),this.clearSelection(),this.cursor=this.lead=this.doc.createAnchor(0,0),this.anchor=this.doc.createAnchor(0,0),this.$silent=!1;var t=this;this.cursor.on("change",function(e){t.$cursorChanged=!0,t.$silent||t._emit("changeCursor"),!t.$isEmpty&&!t.$silent&&t._emit("changeSelection"),!t.$keepDesiredColumnOnChange&&e.old.column!=e.value.column&&(t.$desiredColumn=null)}),this.anchor.on("change",function(){t.$anchorChanged=!0,!t.$isEmpty&&!t.$silent&&t._emit("changeSelection")})};(function(){r.implement(this,s),this.isEmpty=function(){return this.$isEmpty||this.anchor.row==this.lead.row&&this.anchor.column==this.lead.column},this.isMultiLine=function(){return!this.$isEmpty&&this.anchor.row!=this.cursor.row},this.getCursor=function(){return this.lead.getPosition()},this.setSelectionAnchor=function(e,t){this.$isEmpty=!1,this.anchor.setPosition(e,t)},this.getAnchor=this.getSelectionAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.anchor.getPosition()},this.getSelectionLead=function(){return this.lead.getPosition()},this.isBackwards=function(){var e=this.anchor,t=this.lead;return e.row>t.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.$isEmpty?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){this.$setSelection(0,0,Number.MAX_VALUE,Number.MAX_VALUE)},this.setRange=this.setSelectionRange=function(e,t){var n=t?e.end:e.start,r=t?e.start:e.end;this.$setSelection(n.row,n.column,r.row,r.column)},this.$setSelection=function(e,t,n,r){if(this.$silent)return;var i=this.$isEmpty,s=this.inMultiSelectMode;this.$silent=!0,this.$cursorChanged=this.$anchorChanged=!1,this.anchor.setPosition(e,t),this.cursor.setPosition(n,r),this.$isEmpty=!o.comparePoints(this.anchor,this.cursor),this.$silent=!1,this.$cursorChanged&&this._emit("changeCursor"),(this.$cursorChanged||this.$anchorChanged||i!=this.$isEmpty||s)&&this._emit("changeSelection")},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if(typeof t=="undefined"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var n=typeof e=="number"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t===!0?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.wouldMoveIntoSoftTab=function(e,t,n){var r=e.column,i=e.column+t;return n<0&&(r=e.column-t,i=e.column),this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(r,i).split(" ").length-1==t},this.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column===0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.wouldMoveIntoSoftTab(e,n,-1)&&!this.session.getNavigateWithinSoftTabs()?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t);this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var i=this.session.getFoldAt(e,t,1);if(i){this.moveCursorTo(i.end.row,i.end.column);return}this.session.nonTokenRe.exec(r)&&(t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t));if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e0&&this.moveCursorWordLeft();return}this.session.tokenRe.exec(s)&&(t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0),this.moveCursorTo(e,t)},this.$shortWordEndIndex=function(e){var t=0,n,r=/\s/,i=this.session.tokenRe;i.lastIndex=0;if(this.session.tokenRe.exec(e))t=this.session.tokenRe.lastIndex;else{while((n=e[t])&&r.test(n))t++;if(t<1){i.lastIndex=0;while((n=e[t])&&!i.test(n)){i.lastIndex=0,t++;if(r.test(n)){if(t>2){t--;break}while((n=e[t])&&r.test(n))t++;if(t>2)break}}}}return i.lastIndex=0,t},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e0&&/^\s*$/.test(r));t=r.length,/\s+$/.test(r)||(r="")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column),r;t===0&&(e!==0&&(this.session.$bidiHandler.isBidiRow(n.row,this.lead.row)?(r=this.session.$bidiHandler.getPosLeft(n.column),n.column=Math.round(r/this.session.$bidiHandler.charWidths[0])):r=n.column*this.session.$bidiHandler.charWidths[0]),this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);if(e!=0&&this.session.lineWidgets&&this.session.lineWidgets[this.lead.row]){var i=this.session.lineWidgets[this.lead.row];e<0?e-=i.rowsAbove||0:e>0&&(e+=i.rowCount-(i.rowsAbove||0))}var s=this.session.screenToDocumentPosition(n.row+e,n.column,r);e!==0&&t===0&&s.row===this.lead.row&&s.column===this.lead.column,this.moveCursorTo(s.row,s.column+t,t===0)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0;var i=this.session.getLine(e);/[\uDC00-\uDFFF]/.test(i.charAt(t))&&i.charAt(t-1)&&(this.lead.row==e&&this.lead.column==t+1?t-=1:t+=1),this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},this.detach=function(){this.lead.detach(),this.anchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},this.getRangeOfMovements=function(e){var t=this.getCursor();try{e(this);var n=this.getCursor();return o.fromPoints(t,n)}catch(r){return o.fromPoints(t,t)}finally{this.moveCursorToPosition(t)}},this.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},this.fromJSON=function(e){if(e.start==undefined){if(this.rangeList&&e.length>1){this.toSingleRange(e[0]);for(var t=e.length;t--;){var n=o.fromPoints(e[t].start,e[t].end);e[t].isBackwards&&(n.cursor=n.start),this.addRange(n,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},this.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0}}).call(u.prototype),t.Selection=u}),define("ace/tokenizer",["require","exports","module","ace/config"],function(e,t,n){"use strict";var r=e("./config"),i=2e3,s=function(e){this.states=e,this.regExps={},this.matchMappings={};for(var t in this.states){var n=this.states[t],r=[],i=0,s=this.matchMappings[t]={defaultToken:"text"},o="g",u=[];for(var a=0;a1?f.onMatch=this.$applyToken:f.onMatch=f.token),c>1&&(/\\\d/.test(f.regex)?l=f.regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+i+1)}):(c=1,l=this.removeCapturingGroups(f.regex)),!f.splitRegex&&typeof f.token!="string"&&u.push(f)),s[i]=a,i+=c,r.push(l),f.onMatch||(f.onMatch=null)}r.length||(s[0]=0,r.push("$")),u.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,o)},this),this.regExps[t]=new RegExp("("+r.join(")|(")+")|($)",o)}};(function(){this.$setMaxTokenCount=function(e){i=e|0},this.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),n=this.token.apply(this,t);if(typeof n=="string")return[{type:n,value:e}];var r=[];for(var i=0,s=n.length;il){var g=e.substring(l,m-v.length);h.type==p?h.value+=g:(h.type&&f.push(h),h={type:p,value:g})}for(var y=0;yi){c>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});while(l1&&n[0]!==r&&n.unshift("#tmp",r),{tokens:f,state:n.length?n:r}},this.reportError=r.reportError}).call(s.prototype),t.Tokenizer=s}),define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../lib/lang"),i=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{defaultToken:"text"}]}};(function(){this.addRules=function(e,t){if(!t){for(var n in e)this.$rules[n]=e[n];return}for(var n in e){var r=e[n];for(var i=0;i=this.$rowTokens.length){this.$row+=1,e||(e=this.$session.getLength());if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n},this.getCurrentTokenPosition=function(){return{row:this.$row,column:this.getCurrentTokenColumn()}},this.getCurrentTokenRange=function(){var e=this.$rowTokens[this.$tokenIndex],t=this.getCurrentTokenColumn();return new r(this.$row,t,this.$row,t+e.value.length)}}).call(i.prototype),t.TokenIterator=i}),define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","rparen","paren","punctuation.operator"],a=["text","paren.rparen","rparen","paren","punctuation.operator","comment"],f,l={},c={'"':'"',"'":"'"},h=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},p=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},d=function(e){this.add("braces","insertion",function(t,n,r,i,s){var u=r.getCursorPosition(),a=i.doc.getLine(u.row);if(s=="{"){h(r);var l=r.getSelectionRange(),c=i.doc.getTextRange(l);if(c!==""&&c!=="{"&&r.getWrapBehavioursEnabled())return p(l,c,"{","}");if(d.isSaneInsertion(r,i))return/[\]\}\)]/.test(a[u.column])||r.inMultiSelectMode||e&&e.braces?(d.recordAutoInsert(r,i,"}"),{text:"{}",selection:[1,1]}):(d.recordMaybeInsert(r,i,"{"),{text:"{",selection:[1,1]})}else if(s=="}"){h(r);var v=a.substring(u.column,u.column+1);if(v=="}"){var m=i.$findOpeningBracket("}",{column:u.column+1,row:u.row});if(m!==null&&d.isAutoInsertedClosing(u,a,s))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(s=="\n"||s=="\r\n"){h(r);var g="";d.isMaybeInsertedClosing(u,a)&&(g=o.stringRepeat("}",f.maybeInsertedBrackets),d.clearMaybeInsertedClosing());var v=a.substring(u.column,u.column+1);if(v==="}"){var y=i.findMatchingBracket({row:u.row,column:u.column+1},"}");if(!y)return null;var b=this.$getIndent(i.getLine(y.row))}else{if(!g){d.clearMaybeInsertedClosing();return}var b=this.$getIndent(a)}var w=b+i.getTabString();return{text:"\n"+w+"\n"+b+g,selection:[1,w.length,1,w.length]}}d.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"(",")");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"[","]");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){var s=r.$mode.$quotes||c;if(i.length==1&&s[i]){if(this.lineCommentStart&&this.lineCommentStart.indexOf(i)!=-1)return;h(n);var o=i,u=n.getSelectionRange(),a=r.doc.getTextRange(u);if(a!==""&&(a.length!=1||!s[a])&&n.getWrapBehavioursEnabled())return p(u,a,o,o);if(!a){var f=n.getCursorPosition(),l=r.doc.getLine(f.row),d=l.substring(f.column-1,f.column),v=l.substring(f.column,f.column+1),m=r.getTokenAt(f.row,f.column),g=r.getTokenAt(f.row,f.column+1);if(d=="\\"&&m&&/escape/.test(m.type))return null;var y=m&&/string|escape/.test(m.type),b=!g||/string|escape/.test(g.type),w;if(v==o)w=y!==b,w&&/string\.end/.test(g.type)&&(w=!1);else{if(y&&!b)return null;if(y&&b)return null;var E=r.$mode.tokenRe;E.lastIndex=0;var S=E.test(d);E.lastIndex=0;var x=E.test(d);if(S||x)return null;if(v&&!/[\s;,.})\]\\]/.test(v))return null;var T=l[f.column-2];if(!(d!=o||T!=o&&!E.test(T)))return null;w=!0}return{text:w?o+o:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.$mode.$quotes||c,o=r.doc.getTextRange(i);if(!i.isMultiLine()&&s.hasOwnProperty(o)){h(n);var u=r.doc.getLine(i.start.row),a=u.substring(i.start.column+1,i.start.column+2);if(a==o)return i.end.column++,i}})};d.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){if(/[)}\]]/.test(e.session.getLine(n.row)[n.column]))return!0;var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},d.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},d.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},d.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},d.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},d.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},d.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},d.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(d,i),t.CstyleBehaviour=d}),define("ace/unicode",["require","exports","module"],function(e,t,n){"use strict";var r=[48,9,8,25,5,0,2,25,48,0,11,0,5,0,6,22,2,30,2,457,5,11,15,4,8,0,2,0,18,116,2,1,3,3,9,0,2,2,2,0,2,19,2,82,2,138,2,4,3,155,12,37,3,0,8,38,10,44,2,0,2,1,2,1,2,0,9,26,6,2,30,10,7,61,2,9,5,101,2,7,3,9,2,18,3,0,17,58,3,100,15,53,5,0,6,45,211,57,3,18,2,5,3,11,3,9,2,1,7,6,2,2,2,7,3,1,3,21,2,6,2,0,4,3,3,8,3,1,3,3,9,0,5,1,2,4,3,11,16,2,2,5,5,1,3,21,2,6,2,1,2,1,2,1,3,0,2,4,5,1,3,2,4,0,8,3,2,0,8,15,12,2,2,8,2,2,2,21,2,6,2,1,2,4,3,9,2,2,2,2,3,0,16,3,3,9,18,2,2,7,3,1,3,21,2,6,2,1,2,4,3,8,3,1,3,2,9,1,5,1,2,4,3,9,2,0,17,1,2,5,4,2,2,3,4,1,2,0,2,1,4,1,4,2,4,11,5,4,4,2,2,3,3,0,7,0,15,9,18,2,2,7,2,2,2,22,2,9,2,4,4,7,2,2,2,3,8,1,2,1,7,3,3,9,19,1,2,7,2,2,2,22,2,9,2,4,3,8,2,2,2,3,8,1,8,0,2,3,3,9,19,1,2,7,2,2,2,22,2,15,4,7,2,2,2,3,10,0,9,3,3,9,11,5,3,1,2,17,4,23,2,8,2,0,3,6,4,0,5,5,2,0,2,7,19,1,14,57,6,14,2,9,40,1,2,0,3,1,2,0,3,0,7,3,2,6,2,2,2,0,2,0,3,1,2,12,2,2,3,4,2,0,2,5,3,9,3,1,35,0,24,1,7,9,12,0,2,0,2,0,5,9,2,35,5,19,2,5,5,7,2,35,10,0,58,73,7,77,3,37,11,42,2,0,4,328,2,3,3,6,2,0,2,3,3,40,2,3,3,32,2,3,3,6,2,0,2,3,3,14,2,56,2,3,3,66,5,0,33,15,17,84,13,619,3,16,2,25,6,74,22,12,2,6,12,20,12,19,13,12,2,2,2,1,13,51,3,29,4,0,5,1,3,9,34,2,3,9,7,87,9,42,6,69,11,28,4,11,5,11,11,39,3,4,12,43,5,25,7,10,38,27,5,62,2,28,3,10,7,9,14,0,89,75,5,9,18,8,13,42,4,11,71,55,9,9,4,48,83,2,2,30,14,230,23,280,3,5,3,37,3,5,3,7,2,0,2,0,2,0,2,30,3,52,2,6,2,0,4,2,2,6,4,3,3,5,5,12,6,2,2,6,67,1,20,0,29,0,14,0,17,4,60,12,5,0,4,11,18,0,5,0,3,9,2,0,4,4,7,0,2,0,2,0,2,3,2,10,3,3,6,4,5,0,53,1,2684,46,2,46,2,132,7,6,15,37,11,53,10,0,17,22,10,6,2,6,2,6,2,6,2,6,2,6,2,6,2,6,2,31,48,0,470,1,36,5,2,4,6,1,5,85,3,1,3,2,2,89,2,3,6,40,4,93,18,23,57,15,513,6581,75,20939,53,1164,68,45,3,268,4,27,21,31,3,13,13,1,2,24,9,69,11,1,38,8,3,102,3,1,111,44,25,51,13,68,12,9,7,23,4,0,5,45,3,35,13,28,4,64,15,10,39,54,10,13,3,9,7,22,4,1,5,66,25,2,227,42,2,1,3,9,7,11171,13,22,5,48,8453,301,3,61,3,105,39,6,13,4,6,11,2,12,2,4,2,0,2,1,2,1,2,107,34,362,19,63,3,53,41,11,5,15,17,6,13,1,25,2,33,4,2,134,20,9,8,25,5,0,2,25,12,88,4,5,3,5,3,5,3,2],i=0,s=[];for(var o=0;o2?r%f!=f-1:r%f==0}}var E=Infinity;w(function(e,t){var n=e.search(/\S/);n!==-1?(ne.length&&(E=e.length)}),u==Infinity&&(u=E,s=!1,o=!1),l&&u%f!=0&&(u=Math.floor(u/f)*f),w(o?m:v)},this.toggleBlockComment=function(e,t,n,r){var i=this.blockComment;if(!i)return;!i.start&&i[0]&&(i=i[0]);var s=new f(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,a=t.selection.toOrientedRange(),c,h;if(o&&/comment/.test(o.type)){var p,d;while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.start);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;p=new l(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new f(t,r.row,r.column),o=s.getCurrentToken();while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.end);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;d=new l(m,g,m,g+i.end.length);break}o=s.stepForward()}d&&t.remove(d),p&&(t.remove(p),c=p.start.row,h=-i.start.length)}else h=i.start.length,c=n.start.row,t.insert(n.end,i.end),t.insert(n.start,i.start);a.start.row==c&&(a.start.column+=h),a.end.row==c&&(a.end.column+=h),t.selection.fromOrientedRange(a)},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)if(e[t]){var n=e[t],i=n.prototype.$id,s=r.$modes[i];s||(r.$modes[i]=s=new n),r.$modes[t]||(r.$modes[t]=s),this.$embeds.push(t),this.$modes[t]=s}var o=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"];for(var t=0;t=0&&t.row=0&&t.column<=e[t.row].length}function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.action must be 'insert' or 'remove'"),t.lines instanceof Array||r(t,"delta.lines must be an Array"),(!t.start||!t.end)&&r(t,"delta.start/end must be an present");var n=t.start;i(e,t.start)||r(t,"delta.start must be contained in document");var s=t.end;t.action=="remove"&&!i(e,s)&&r(t,"delta.end must contained in document for 'remove' actions");var o=s.row-n.row,u=s.column-(o==0?n.column:0);(o!=t.lines.length-1||t.lines[o].length!=u)&&r(t,"delta.range must match delta lines")}t.applyDelta=function(e,t,n){var r=t.start.row,i=t.start.column,s=e[r]||"";switch(t.action){case"insert":var o=t.lines;if(o.length===1)e[r]=s.substring(0,i)+t.lines[0]+s.substring(i);else{var u=[r,1].concat(t.lines);e.splice.apply(e,u),e[r]=s.substring(0,i)+e[r],e[r+t.lines.length-1]+=s.substring(i)}break;case"remove":var a=t.end.column,f=t.end.row;r===f?e[r]=s.substring(0,i)+s.substring(a):e.splice(r,f-r+1,s.substring(0,i)+e[f].substring(a))}}}),define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=t.Anchor=function(e,t,n){this.$onChange=this.onChange.bind(this),this.attach(e),typeof n=="undefined"?this.setPosition(t.row,t.column):this.setPosition(t,n)};(function(){function e(e,t,n){var r=n?e.column<=t.column:e.columnthis.row)return;var n=t(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(n.row,n.column,!0)},this.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._signal("change",{old:i,value:r})},this.detach=function(){this.document.off("change",this.$onChange)},this.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},this.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./apply_delta").applyDelta,s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=e("./anchor").Anchor,a=function(e){this.$lines=[""],e.length===0?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)};(function(){r.implement(this,s),this.setValue=function(e){var t=this.getLength()-1;this.remove(new o(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new u(this,e,t)},"aaa".split(/a/).length===0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},this.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))}return t},this.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},this.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},this.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},this.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},this.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),r=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:r,action:"insert",lines:[t]},!0),this.clonePos(r)},this.clippedPos=function(e,t){var n=this.getLength();e===undefined?e=n:e<0?e=0:e>=n&&(e=n-1,t=undefined);var r=this.getLine(e);return t==undefined&&(t=r.length),t=Math.min(Math.max(t,0),r.length),{row:e,column:t}},this.clonePos=function(e){return{row:e.row,column:e.column}},this.pos=function(e,t){return{row:e,column:t}},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},this.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var n=0;e0,r=t=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:"remove",lines:["",""]})},this.replace=function(e,t){e instanceof o||(e=o.fromPoints(e.start,e.end));if(t.length===0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var n;return t?n=this.insert(e.start,t):n=e.start,n},this.applyDeltas=function(e){for(var t=0;t=0;t--)this.revertDelta(e[t])},this.applyDelta=function(e,t){var n=e.action=="insert";if(n?e.lines.length<=1&&!e.lines[0]:!o.comparePoints(e.start,e.end))return;n&&e.lines.length>2e4?this.$splitAndapplyLargeDelta(e,2e4):(i(this.$lines,e,t),this._signal("change",e))},this.$safeApplyDelta=function(e){var t=this.$lines.length;(e.action=="remove"&&e.start.row20){n.running=setTimeout(n.$worker,20);break}}n.currentLine=t,r==-1&&(r=t),s<=r&&n.fireUpdateEvent(s,r)}};(function(){r.implement(this,i),this.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},this.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},this.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._signal("update",{data:n})},this.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},this.$updateOnChange=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.lines[t]=null;else if(e.action=="remove")this.lines.splice(t,n+1,null),this.states.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.lines.splice.apply(this.lines,r),this.states.splice.apply(this.states,r)}this.currentLine=Math.min(t,this.currentLine,this.doc.getLength()),this.stop()},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},this.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},this.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1],r=this.tokenizer.getLineTokens(t,n,e);return this.states[e]+""!=r.state+""?(this.states[e]=r.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=r.tokens}}).call(s.prototype),t.BackgroundTokenizer=s}),define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(e,t,n){this.setRegexp(e),this.clazz=t,this.type=n||"text"};(function(){this.MAX_RANGES=500,this.setRegexp=function(e){if(this.regExp+""==e+"")return;this.regExp=e,this.cache=[]},this.update=function(e,t,n,i){if(!this.regExp)return;var o=i.firstRow,u=i.lastRow;for(var a=o;a<=u;a++){var f=this.cache[a];f==null&&(f=r.getMatchOffsets(n.getLine(a),this.regExp),f.length>this.MAX_RANGES&&(f=f.slice(0,this.MAX_RANGES)),f=f.map(function(e){return new s(a,e.offset,a,e.offset+e.length)}),this.cache[a]=f.length?f:"");for(var l=f.length;l--;)t.drawSingleLineMarker(e,f[l].toScreenRange(n),this.clazz,i)}}}).call(o.prototype),t.SearchHighlight=o}),define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}var r=e("../range").Range;(function(){this.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},this.addFold=function(e){if(e.sameRow){if(e.start.rowthis.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},this.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},this.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f0)continue;var a=i(e,o.start);return u===0?t&&a!==0?-s-2:s:a>0||a===0&&!t?s:-s-1}return-s-1},this.add=function(e){var t=!e.isEmpty(),n=this.pointIndex(e.start,t);n<0&&(n=-n-1);var r=this.pointIndex(e.end,t,n);return r<0?r=-r-1:r++,this.ranges.splice(n,r-n,e)},this.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.apply(t,this.add(e[n]));return t},this.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},this.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return i(e.start,t.start)});var n=t[0],r;for(var s=1;s=0},this.containsPoint=function(e){return this.pointIndex(e)>=0},this.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},this.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.row=r)break}if(e.action=="insert"){var f=i-r,l=-t.column+n.column;for(;or)break;a.start.row==r&&a.start.column>=t.column&&(a.start.column==t.column&&this.$bias<=0||(a.start.column+=l,a.start.row+=f));if(a.end.row==r&&a.end.column>=t.column){if(a.end.column==t.column&&this.$bias<0)continue;a.end.column==t.column&&l>0&&oa.start.column&&a.end.column==s[o+1].start.column&&(a.end.column-=l),a.end.column+=l,a.end.row+=f}}}else{var f=r-i,l=t.column-n.column;for(;oi)break;if(a.end.rowt.column)a.end.column=t.column,a.end.row=t.row}else a.end.column+=l,a.end.row+=f;else a.end.row>i&&(a.end.row+=f);if(a.start.rowt.column)a.start.column=t.column,a.start.row=t.row}else a.start.column+=l,a.start.row+=f;else a.start.row>i&&(a.start.row+=f)}}if(f!=0&&o=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i=t){u=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:(o=new s(t,e),o.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column,c=this.getFoldAt(u,a,1),h=this.getFoldAt(f,l,-1);if(c&&h==c)return c.addSubFold(o);c&&!c.range.isStart(u,a)&&this.removeFold(c),h&&!h.range.isEnd(f,l)&&this.removeFold(h);var p=this.getFoldsInRange(o.range);p.length>0&&(this.removeFolds(p),o.collapseChildren||p.forEach(function(e){o.addSubFold(e)}));for(var d=0;d0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;e==null?(n=new r(0,0,this.getLength(),0),t==null&&(t=!0)):typeof e=="number"?n=new r(e,0,e,this.getLine(e).length):"row"in e?n=r.fromPoints(e,e):n=e,i=this.getFoldsInRangeList(n),t!=0?this.removeFolds(i):this.expandFolds(i);if(i.length)return i},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getRowFoldStart=function(e,t){var n=this.getFoldLine(e,t);return n?n.start.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row),i==null&&(i=0),t==null&&(t=e.end.row),n==null&&(n=this.getLine(t).length);var s=this.doc,o="";return e.walk(function(e,t,n,u){if(tl)break}while(s&&a.test(s.type));s=i.stepBackward()}else s=i.getCurrentToken();return f.end.row=i.getCurrentTokenRow(),f.end.column=i.getCurrentTokenColumn()+s.value.length-2,f}},this.foldAll=function(e,t,n,r){n==undefined&&(n=1e5);var i=this.foldWidgets;if(!i)return;t=t||this.getLength(),e=e||0;for(var s=e;s=e&&(s=o.end.row,o.collapseChildren=n,this.addFold("...",o))}},this.foldToLevel=function(e){this.foldAll();while(e-->0)this.unfold(null,!1)},this.foldAllComments=function(){var e=this;this.foldAll(null,null,null,function(t){var n=e.getTokens(t);for(var r=0;r=0){var s=n[r];s==null&&(s=n[r]=this.getFoldWidget(r));if(s=="start"){var o=this.getFoldWidgetRange(r);i||(i=o);if(o&&o.end.row>=e)break}r--}return{range:r!==-1&&o,firstRange:i}},this.onFoldWidgetClick=function(e,t){t=t.domEvent;var n={children:t.shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey},r=this.$toggleFoldWidget(e,n);if(!r){var i=t.target||t.srcElement;i&&/ace_fold-widget/.test(i.className)&&(i.className+=" ace_invalid")}},this.$toggleFoldWidget=function(e,t){if(!this.getFoldWidget)return;var n=this.getFoldWidget(e),r=this.getLine(e),i=n==="end"?-1:1,s=this.getFoldAt(e,i===-1?0:r.length,i);if(s)return t.children||t.all?this.removeFold(s):this.expandFold(s),s;var o=this.getFoldWidgetRange(e,!0);if(o&&!o.isMultiLine()){s=this.getFoldAt(o.start.row,o.start.column,1);if(s&&o.isEqual(s.range))return this.removeFold(s),s}if(t.siblings){var u=this.getParentFoldRangeData(e);if(u.range)var a=u.range.start.row+1,f=u.range.end.row;this.foldAll(a,f,t.all?1e4:0)}else t.children?(f=o?o.end.row:this.getLength(),this.foldAll(e+1,f,t.all?1e4:0)):o&&(t.all&&(o.collapseChildren=1e4),this.addFold("...",o));return o},this.toggleFoldWidget=function(e){var t=this.selection.getCursor().row;t=this.getRowFoldStart(t);var n=this.$toggleFoldWidget(t,{});if(n)return;var r=this.getParentFoldRangeData(t,!0);n=r.range||r.firstRange;if(n){t=n.start.row;var i=this.getFoldAt(t,this.getLine(t).length,1);i?this.removeFold(i):this.addFold("...",n)}},this.updateFoldWidgets=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.foldWidgets[t]=null;else if(e.action=="remove")this.foldWidgets.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,r)}},this.tokenizerUpdateFoldWidgets=function(e){var t=e.data;t.first!=t.last&&this.foldWidgets.length>t.first&&this.foldWidgets.splice(t.first,this.foldWidgets.length)}}var r=e("../range").Range,i=e("./fold_line").FoldLine,s=e("./fold").Fold,o=e("../token_iterator").TokenIterator;t.Folding=u}),define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,n){"use strict";function s(){this.findMatchingBracket=function(e,t){if(e.column==0)return null;var n=t||this.getLine(e.row).charAt(e.column-1);if(n=="")return null;var r=n.match(/([\(\[\{])|([\)\]\}])/);return r?r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e):null},this.getBracketRange=function(e){var t=this.getLine(e.row),n=!0,r,s=t.charAt(e.column-1),o=s&&s.match(/([\(\[\{])|([\)\]\}])/);o||(s=t.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(/([\(\[\{])|([\)\]\}])/),n=!1);if(!o)return null;if(o[1]){var u=this.$findClosingBracket(o[1],e);if(!u)return null;r=i.fromPoints(e,u),n||(r.end.column++,r.start.column--),r.cursor=r.end}else{var u=this.$findOpeningBracket(o[2],e);if(!u)return null;r=i.fromPoints(u,e),n||(r.start.column++,r.end.column--),r.cursor=r.start}return r},this.getMatchingBracketRanges=function(e){var t=this.getLine(e.row),n=t.charAt(e.column-1),r=n&&n.match(/([\(\[\{])|([\)\]\}])/);r||(n=t.charAt(e.column),e={row:e.row,column:e.column+1},r=n&&n.match(/([\(\[\{])|([\)\]\}])/));if(!r)return null;var s=new i(e.row,e.column-1,e.row,e.column),o=r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e);if(!o)return[s];var u=new i(o.row,o.column,o.row,o.column+1);return[s,u]},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{","<":">",">":"<"},this.$findOpeningBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn()-2,f=u.value;for(;;){while(a>=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:start|begin)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(a=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}r.implement(this,u),this.setDocument=function(e){this.doc&&this.doc.removeListener("change",this.$onChange),this.doc=e,e.on("change",this.$onChange),this.bgTokenizer&&this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},this.getDocument=function(){return this.doc},this.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$docRowCache.length,n=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>n&&(this.$docRowCache.splice(n,t),this.$screenRowCache.splice(n,t))},this.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},this.setUndoManager=function(e){this.$undoManager=e,this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;e.addSession(this),this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.mergeUndoDeltas=!1},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}else this.$syncInformUndoManager=function(){}},this.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},this.$defaultUndoManager={undo:function(){},redo:function(){},hasUndo:function(){},hasRedo:function(){},reset:function(){},add:function(){},addSelection:function(){},startNewGroup:function(){},addSession:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?i.stringRepeat(" ",this.getTabSize()):" "},this.setUseSoftTabs=function(e){this.setOption("useSoftTabs",e)},this.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},this.setTabSize=function(e){this.setOption("tabSize",e)},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize===0},this.setNavigateWithinSoftTabs=function(e){this.setOption("navigateWithinSoftTabs",e)},this.getNavigateWithinSoftTabs=function(){return this.$navigateWithinSoftTabs},this.$overwrite=!1,this.setOverwrite=function(e){this.setOption("overwrite",e)},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._signal("changeBreakpoint",{})},this.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._signal("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\s+$/.test(n.slice(t-1,t+1)))var i=/\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(oe&&(e=t.screenWidth)}),this.lineWidgetWidth=e},this.$computeWidth=function(e){if(this.$modified||e){this.$modified=!1;if(this.$useWrapMode)return this.screenWidth=this.$wrapLimit;var t=this.doc.getAllLines(),n=this.$rowLengthCache,r=0,i=0,s=this.$foldData[i],o=s?s.start.row:Infinity,u=t.length;for(var a=0;ao){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},this.getLine=function(e){return this.doc.getLine(e)},this.getLines=function(e,t){return this.doc.getLines(e,t)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},this.insert=function(e,t){return this.doc.insert(e,t)},this.remove=function(e){return this.doc.remove(e)},this.removeFullLines=function(e,t){return this.doc.removeFullLines(e,t)},this.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=e.length-1;n!=-1;n--){var r=e[n];r.action=="insert"||r.action=="remove"?this.doc.revertDelta(r):r.folds&&this.addFolds(r.folds)}!t&&this.$undoSelect&&(e.selectionBefore?this.selection.fromJSON(e.selectionBefore):this.selection.setRange(this.$getUndoSelection(e,!0))),this.$fromUndo=!1},this.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=0;ne.end.column&&(s.start.column+=u),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=u)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}s.end=this.insert(s.start,r);if(i.length){var a=e.start,f=s.start,o=f.row-a.row,u=f.column-a.column;this.addFolds(i.map(function(e){return e=e.clone(),e.start.row==a.row&&(e.start.column+=u),e.end.row==a.row&&(e.end.column+=u),e.start.row+=o,e.end.row+=o,e}))}return s},this.indentRows=function(e,t,n){n=n.replace(/\t/g,this.getTabString());for(var r=e;r<=t;r++)this.doc.insertInLine({row:r,column:0},n)},this.outdentRows=function(e){var t=e.collapseRows(),n=new l(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o0){var r=this.getRowFoldEnd(t+n);if(r>this.doc.getLength()-1)return 0;var i=r-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var i=t-e+1}var s=new l(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return e=e.clone(),e.start.row+=i,e.end.row+=i,e}),u=n==0?this.doc.getLines(e,t):this.doc.removeFullLines(e,t);return this.doc.insertFullLines(e+i,u),o.length&&this.addFolds(o),i},this.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},this.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},this.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},this.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},this.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},this.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},this.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=Array(t),this.$updateWrapData(0,t-1)}this._signal("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(e,t){if(this.$wrapLimitRange.min!==e||this.$wrapLimitRange.max!==t)this.$wrapLimitRange={min:e,max:t},this.$modified=!0,this.$bidiHandler.markAsDirty(),this.$useWrapMode&&this._signal("changeWrapMode")},this.adjustWrapLimit=function(e,t){var n=this.$wrapLimitRange;n.max<0&&(n={min:t,max:t});var r=this.$constrainWrapLimit(e,n.min,n.max);return r!=this.$wrapLimit&&r>1?(this.$wrapLimit=r,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._signal("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(e,t,n){return t&&(e=Math.max(t,e)),n&&(e=Math.min(n,e)),e},this.getWrapLimit=function(){return this.$wrapLimit},this.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n=e.action,r=e.start,i=e.end,s=r.row,o=i.row,u=o-s,a=null;this.$updating=!0;if(u!=0)if(n==="remove"){this[t?"$wrapData":"$rowLengthCache"].splice(s,u);var f=this.$foldData;a=this.getFoldsInRange(e),this.removeFolds(a);var l=this.getFoldLine(i.row),c=0;if(l){l.addRemoveChars(i.row,i.column,r.column-i.column),l.shiftRow(-u);var h=this.getFoldLine(s);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c=i.row&&l.shiftRow(-u)}o=s}else{var p=Array(u);p.unshift(s,0);var d=t?this.$wrapData:this.$rowLengthCache;d.splice.apply(d,p);var f=this.$foldData,l=this.getFoldLine(s),c=0;if(l){var v=l.range.compareInside(r.row,r.column);v==0?(l=l.split(r.row,r.column),l&&(l.shiftRow(u),l.addRemoveChars(o,0,i.column-r.column))):v==-1&&(l.addRemoveChars(s,0,i.column-r.column),l.shiftRow(u)),c=f.indexOf(l)+1}for(c;c=s&&l.shiftRow(u)}}else{u=Math.abs(e.start.column-e.end.column),n==="remove"&&(a=this.getFoldsInRange(e),this.removeFolds(a),u=-u);var l=this.getFoldLine(s);l&&l.addRemoveChars(s,r.column,u)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),this.$updating=!1,t?this.$updateWrapData(s,o):this.$updateRowLengthCache(s,o),a},this.$updateRowLengthCache=function(e,t,n){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var r=this.doc.getAllLines(),i=this.getTabSize(),o=this.$wrapData,u=this.$wrapLimit,a,f,l=e;t=Math.min(t,r.length-1);while(l<=t)f=this.getFoldLine(l,f),f?(a=[],f.walk(function(e,t,i,o){var u;if(e!=null){u=this.$getDisplayTokens(e,a.length),u[0]=n;for(var f=1;fr-b){var w=f+r-b;if(e[w-1]>=c&&e[w]>=c){y(w);continue}if(e[w]==n||e[w]==s){for(w;w!=f-1;w--)if(e[w]==n)break;if(w>f){y(w);continue}w=f+r;for(w;w>2)),f-1);while(w>E&&e[w]E&&e[w]E&&e[w]==a)w--}else while(w>E&&e[w]E){y(++w);continue}w=f+r,e[w]==t&&w--,y(w-b)}return o},this.$getDisplayTokens=function(n,r){var i=[],s;r=r||0;for(var o=0;o39&&u<48||u>57&&u<64?i.push(a):u>=4352&&m(u)?i.push(e,t):i.push(e)}return i},this.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i=4352&&m(r)?n+=2:n+=1;if(n>t)break}return[n,i]},this.lineWidgets=null,this.getRowLength=function(e){var t=1;return this.lineWidgets&&(t+=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0),!this.$useWrapMode||!this.$wrapData[e]?t:this.$wrapData[e].length+t},this.getRowLineCount=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},this.getRowWrapIndent=function(e){if(this.$useWrapMode){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE),n=this.$wrapData[t.row];return n.length&&n[0]=0)var u=f[l],i=this.$docRowCache[l],h=e>f[c-1];else var h=!c;var p=this.getLength()-1,d=this.getNextFoldLine(i),v=d?d.start.row:Infinity;while(u<=e){a=this.getRowLength(i);if(u+a>e||i>=p)break;u+=a,i++,i>v&&(i=d.end.row+1,d=this.getNextFoldLine(i,d),v=d?d.start.row:Infinity),h&&(this.$docRowCache.push(i),this.$screenRowCache.push(u))}if(d&&d.start.row<=i)r=this.getFoldDisplayLine(d),i=d.start.row;else{if(u+a<=e||i>p)return{row:p,column:this.getLine(p).length};r=this.getLine(i),d=null}var m=0,g=Math.floor(e-u);if(this.$useWrapMode){var y=this.$wrapData[i];y&&(o=y[g],g>0&&y.length&&(m=y.indent,s=y[g-1]||y[y.length-1],r=r.substring(s)))}return n!==undefined&&this.$bidiHandler.isBidiRow(u+g,i,g)&&(t=this.$bidiHandler.offsetToCol(n)),s+=this.$getStringScreenWidth(r,t-m)[1],this.$useWrapMode&&s>=o&&(s=o-1),d?d.idxToPosition(s):{row:i,column:s}},this.documentToScreenPosition=function(e,t){if(typeof t=="undefined")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d="";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);var v=0;if(this.$useWrapMode){var m=this.$wrapData[i];if(m){var g=0;while(d.length>=m[g])r++,g++;d=d.substring(m[g-1]||0,d.length),v=g>0?m.indent:0}}return this.lineWidgets&&this.lineWidgets[u]&&this.lineWidgets[u].rowsAbove&&(r+=this.lineWidgets[u].rowsAbove),{row:r,column:v+this.$getStringScreenWidth(d)[0]}},this.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},this.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},this.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;ro&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}}return this.lineWidgets&&(e+=this.$getWidgetScreenLength()),e},this.$setFontMetrics=function(e){if(!this.$enableVarChar)return;this.$getStringScreenWidth=function(t,n,r){if(n===0)return[0,0];n||(n=Infinity),r=r||0;var i,s;for(s=0;sn)break}return[r,s]}},this.destroy=function(){this.bgTokenizer&&(this.bgTokenizer.setDocument(null),this.bgTokenizer=null),this.$stopWorker(),this.removeAllListeners(),this.selection.detach()},this.isFullWidth=m}.call(d.prototype),e("./edit_session/folding").Folding.call(d.prototype),e("./edit_session/bracket_match").BracketMatch.call(d.prototype),o.defineOptions(d.prototype,"session",{wrap:{set:function(e){!e||e=="off"?e=!1:e=="free"?e=!0:e=="printMargin"?e=-1:typeof e=="string"&&(e=parseInt(e,10)||!1);if(this.$wrap==e)return;this.$wrap=e;if(!e)this.setUseWrapMode(!1);else{var t=typeof e=="number"?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}},get:function(){return this.getUseWrapMode()?this.$wrap==-1?"printMargin":this.getWrapLimitRange().min?this.$wrap:"free":"off"},handlesSet:!0},wrapMethod:{set:function(e){e=e=="auto"?this.$mode.type!="text":e!="text",e!=this.$wrapAsCode&&(this.$wrapAsCode=e,this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0)))},initialValue:"auto"},indentedSoftWrap:{set:function(){this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0))},initialValue:!0},firstLineNumber:{set:function(){this._signal("changeBreakpoint")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){e=parseInt(e),e>0&&this.$tabSize!==e&&(this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal("changeTabSize"))},initialValue:4,handlesSet:!0},navigateWithinSoftTabs:{initialValue:!1},foldStyle:{set:function(e){this.setFoldStyle(e)},handlesSet:!0},overwrite:{set:function(e){this._signal("changeOverwrite")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0},mode:{set:function(e){this.setMode(e)},get:function(){return this.$modeId},handlesSet:!0}}),t.EditSession=d}),define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";function u(e,t){function n(e){return/\w/.test(e)||t.regExp?"\\b":""}return n(e[0])+e+n(e[e.length-1])}var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(){this.$options={}};(function(){this.set=function(e){return i.mixin(this.$options,e),this},this.getOptions=function(){return r.copyObject(this.$options)},this.setOptions=function(e){this.$options=e},this.find=function(e){var t=this.$options,n=this.$matchIterator(e,t);if(!n)return!1;var r=null;return n.forEach(function(e,n,i,o){return r=new s(e,n,i,o),n==o&&t.start&&t.start.start&&t.skipCurrent!=0&&r.isEqual(t.start)?(r=null,!1):!0}),r},this.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var n=t.range,i=n?e.getLines(n.start.row,n.end.row):e.doc.getAllLines(),o=[],u=t.re;if(t.$isMultiLine){var a=u.length,f=i.length-a,l;e:for(var c=u.offset||0;c<=f;c++){for(var h=0;hv)continue;o.push(l=new s(c,v,c+a-1,m)),a>2&&(c=c+a-2)}}else for(var g=0;gE&&o[h].end.row==n.end.row)h--;o=o.slice(g,h+1);for(g=0,h=o.length;g=u;n--)if(c(n,Number.MAX_VALUE,e))return;if(t.wrap==0)return;for(n=a,u=o.row;n>=u;n--)if(c(n,Number.MAX_VALUE,e))return};else var f=function(e){var n=o.row;if(c(n,o.column,e))return;for(n+=1;n<=a;n++)if(c(n,0,e))return;if(t.wrap==0)return;for(n=u,a=o.row;n<=a;n++)if(c(n,0,e))return};if(t.$isMultiLine)var l=n.length,c=function(t,i,s){var o=r?t-l+1:t;if(o<0)return;var u=e.getLine(o),a=u.search(n[0]);if(!r&&ai)return;if(s(o,a,o+l-1,c))return!0};else if(r)var c=function(t,r,i){var s=e.getLine(t),o=[],u,a=0;n.lastIndex=0;while(u=n.exec(s)){var f=u[0].length;a=u.index;if(!f){if(a>=s.length)break;n.lastIndex=a+=1}if(u.index+f>r)break;o.push(u.index,f)}for(var l=o.length-1;l>=0;l-=2){var c=o[l-1],f=o[l];if(i(t,c,t,c+f))return!0}};else var c=function(t,r,i){var s=e.getLine(t),o,u;n.lastIndex=r;while(u=n.exec(s)){var a=u[0].length;o=u.index;if(i(t,o,t,o+a))return!0;if(!a){n.lastIndex=o+=1;if(o>=s.length)return!1}}};return{forEach:f}}}).call(o.prototype),t.Search=o}),define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function o(e,t){this.platform=t||(i.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=!0}function u(e,t){o.call(this,e,t),this.$singleCommand=!1}var r=e("../lib/keys"),i=e("../lib/useragent"),s=r.KEY_MODS;u.prototype=o.prototype,function(){function e(e){return typeof e=="object"&&e.bindKey&&e.bindKey.position||(e.isDefault?-100:0)}this.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e,t){var n=e&&(typeof e=="string"?e:e.name);e=this.commands[n],t||delete this.commands[n];var r=this.commandKeyBinding;for(var i in r){var s=r[i];if(s==e)delete r[i];else if(Array.isArray(s)){var o=s.indexOf(e);o!=-1&&(s.splice(o,1),s.length==1&&(r[i]=s[0]))}}},this.bindKey=function(e,t,n){typeof e=="object"&&e&&(n==undefined&&(n=e.position),e=e[this.platform]);if(!e)return;if(typeof t=="function")return this.addCommand({exec:t,bindKey:e,name:t.name||e});e.split("|").forEach(function(e){var r="";if(e.indexOf(" ")!=-1){var i=e.split(/\s+/);e=i.pop(),i.forEach(function(e){var t=this.parseKeys(e),n=s[t.hashId]+t.key;r+=(r?" ":"")+n,this._addCommandToBinding(r,"chainKeys")},this),r+=" "}var o=this.parseKeys(e),u=s[o.hashId]+o.key;this._addCommandToBinding(r+u,t,n)},this)},this._addCommandToBinding=function(t,n,r){var i=this.commandKeyBinding,s;if(!n)delete i[t];else if(!i[t]||this.$singleCommand)i[t]=n;else{Array.isArray(i[t])?(s=i[t].indexOf(n))!=-1&&i[t].splice(s,1):i[t]=[i[t]],typeof r!="number"&&(r=e(n));var o=i[t];for(s=0;sr)break}o.splice(s,0,n)}},this.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(!n)return;if(typeof n=="string")return this.bindKey(n,t);typeof n=="function"&&(n={exec:n});if(typeof n!="object")return;n.name||(n.name=t),this.addCommand(n)},this)},this.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},this.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},this._buildKeyHash=function(e){this.bindKey(e.bindKey,e)},this.parseKeys=function(e){var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),n=t.pop(),i=r[n];if(r.FUNCTION_KEYS[i])n=r.FUNCTION_KEYS[i].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]=="shift")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=r.KEY_MODS[t[o]];if(u==null)return typeof console!="undefined"&&console.error("invalid modifier "+t[o]+" in "+e),!1;s|=u}return{key:n,hashId:s}},this.findKeyCommand=function(t,n){var r=s[t]+n;return this.commandKeyBinding[r]},this.handleKeyboard=function(e,t,n,r){if(r<0)return;var i=s[t]+n,o=this.commandKeyBinding[i];e.$keyChain&&(e.$keyChain+=" "+i,o=this.commandKeyBinding[e.$keyChain]||o);if(o)if(o=="chainKeys"||o[o.length-1]=="chainKeys")return e.$keyChain=e.$keyChain||i,{command:"null"};if(e.$keyChain)if(!!t&&t!=4||n.length!=1){if(t==-1||r>0)e.$keyChain=""}else e.$keyChain=e.$keyChain.slice(0,-i.length-1);return{command:o}},this.getStatusText=function(e,t){return t.$keyChain||""}}.call(o.prototype),t.HashHandler=o,t.MultiHashHandler=u}),define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../keyboard/hash_handler").MultiHashHandler,s=e("../lib/event_emitter").EventEmitter,o=function(e,t){i.call(this,t,e),this.byName=this.commands,this.setDefaultHandler("exec",function(e){return e.command.exec(e.editor,e.args||{})})};r.inherits(o,i),function(){r.implement(this,s),this.exec=function(e,t,n){if(Array.isArray(e)){for(var r=e.length;r--;)if(this.exec(e[r],t,n))return!0;return!1}typeof e=="string"&&(e=this.commands[e]);if(!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;if(this.$checkCommandState!=0&&e.isAvailable&&!e.isAvailable(t))return!1;var i={editor:t,command:e,args:n};return i.returnValue=this._emit("exec",i),this._signal("afterExec",i),i.returnValue===!1?!1:!0},this.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.off("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},this.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t=="string"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(e){return e.map(function(e){return typeof e[0]!="string"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})}}.call(o.prototype),t.CommandManager=o}),define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"],function(e,t,n){"use strict";function o(e,t){return{win:e,mac:t}}var r=e("../lib/lang"),i=e("../config"),s=e("../range").Range;t.commands=[{name:"showSettingsMenu",bindKey:o("Ctrl-,","Command-,"),exec:function(e){i.loadModule("ace/ext/settings_menu",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:"goToNextError",bindKey:o("Alt-E","F4"),exec:function(e){i.loadModule("./ext/error_marker",function(t){t.showErrorMarker(e,1)})},scrollIntoView:"animate",readOnly:!0},{name:"goToPreviousError",bindKey:o("Alt-Shift-E","Shift-F4"),exec:function(e){i.loadModule("./ext/error_marker",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:"animate",readOnly:!0},{name:"selectall",description:"Select all",bindKey:o("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",description:"Center selection",bindKey:o(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",description:"Go to line...",bindKey:o("Ctrl-L","Command-L"),exec:function(e,t){typeof t=="number"&&!isNaN(t)&&e.gotoLine(t),e.prompt({$type:"gotoLine"})},readOnly:!0},{name:"fold",bindKey:o("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"unfold",bindKey:o("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleFoldWidget",bindKey:o("F2","F2"),exec:function(e){e.session.toggleFoldWidget()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleParentFoldWidget",bindKey:o("Alt-F2","Alt-F2"),exec:function(e){e.session.toggleFoldWidget(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"foldall",description:"Fold all",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAll()},scrollIntoView:"center",readOnly:!0},{name:"foldAllComments",description:"Fold all comments",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAllComments()},scrollIntoView:"center",readOnly:!0},{name:"foldOther",description:"Fold other",bindKey:o("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll(),e.session.unfold(e.selection.getAllRanges())},scrollIntoView:"center",readOnly:!0},{name:"unfoldall",description:"Unfold all",bindKey:o("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},scrollIntoView:"center",readOnly:!0},{name:"findnext",description:"Find next",bindKey:o("Ctrl-K","Command-G"),exec:function(e){e.findNext()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"findprevious",description:"Find previous",bindKey:o("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"selectOrFindNext",description:"Select or find next",bindKey:o("Alt-K","Ctrl-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:"selectOrFindPrevious",description:"Select or find previous",bindKey:o("Alt-Shift-K","Ctrl-Shift-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:"find",description:"Find",bindKey:o("Ctrl-F","Command-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",description:"Overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",description:"Select to start",bindKey:o("Ctrl-Shift-Home","Command-Shift-Home|Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotostart",description:"Go to start",bindKey:o("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectup",description:"Select up",bindKey:o("Shift-Up","Shift-Up|Ctrl-Shift-P"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golineup",description:"Go line up",bindKey:o("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttoend",description:"Select to end",bindKey:o("Ctrl-Shift-End","Command-Shift-End|Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotoend",description:"Go to end",bindKey:o("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectdown",description:"Select down",bindKey:o("Shift-Down","Shift-Down|Ctrl-Shift-N"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golinedown",description:"Go line down",bindKey:o("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordleft",description:"Select word left",bindKey:o("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordleft",description:"Go to word left",bindKey:o("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolinestart",description:"Select to line start",bindKey:o("Alt-Shift-Left","Command-Shift-Left|Ctrl-Shift-A"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolinestart",description:"Go to line start",bindKey:o("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectleft",description:"Select left",bindKey:o("Shift-Left","Shift-Left|Ctrl-Shift-B"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoleft",description:"Go to left",bindKey:o("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordright",description:"Select word right",bindKey:o("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordright",description:"Go to word right",bindKey:o("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolineend",description:"Select to line end",bindKey:o("Alt-Shift-Right","Command-Shift-Right|Shift-End|Ctrl-Shift-E"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolineend",description:"Go to line end",bindKey:o("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectright",description:"Select right",bindKey:o("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoright",description:"Go to right",bindKey:o("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectpagedown",description:"Select page down",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",description:"Page down",bindKey:o(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",description:"Go to page down",bindKey:o("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",description:"Select page up",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",description:"Page up",bindKey:o(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",description:"Go to page up",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",description:"Scroll up",bindKey:o("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",description:"Scroll down",bindKey:o("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",description:"Select line start",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectlineend",description:"Select line end",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"togglerecording",description:"Toggle recording",bindKey:o("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",description:"Replay macro",bindKey:o("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",description:"Jump to matching",bindKey:o("Ctrl-\\|Ctrl-P","Command-\\"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"selecttomatching",description:"Select to matching",bindKey:o("Ctrl-Shift-\\|Ctrl-Shift-P","Command-Shift-\\"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"expandToMatching",description:"Expand to matching",bindKey:o("Ctrl-Shift-M","Ctrl-Shift-M"),exec:function(e){e.jumpToMatching(!0,!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"passKeysToBrowser",description:"Pass keys to browser",bindKey:o(null,null),exec:function(){},passEvent:!0,readOnly:!0},{name:"copy",description:"Copy",exec:function(e){},readOnly:!0},{name:"cut",description:"Cut",exec:function(e){var t=e.$copyWithEmptySelection&&e.selection.isEmpty(),n=t?e.selection.getLineRange():e.selection.getRange();e._emit("cut",n),n.isEmpty()||e.session.remove(n),e.clearSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"paste",description:"Paste",exec:function(e,t){e.$handlePaste(t)},scrollIntoView:"cursor"},{name:"removeline",description:"Remove line",bindKey:o("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},scrollIntoView:"cursor",multiSelectAction:"forEachLine"},{name:"duplicateSelection",description:"Duplicate selection",bindKey:o("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"sortlines",description:"Sort lines",bindKey:o("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},scrollIntoView:"selection",multiSelectAction:"forEachLine"},{name:"togglecomment",description:"Toggle comment",bindKey:o("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"toggleBlockComment",description:"Toggle block comment",bindKey:o("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"modifyNumberUp",description:"Modify number up",bindKey:o("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"modifyNumberDown",description:"Modify number down",bindKey:o("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"replace",description:"Replace",bindKey:o("Ctrl-H","Command-Option-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",description:"Undo",bindKey:o("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",description:"Redo",bindKey:o("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",description:"Copy lines up",bindKey:o("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()},scrollIntoView:"cursor"},{name:"movelinesup",description:"Move lines up",bindKey:o("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()},scrollIntoView:"cursor"},{name:"copylinesdown",description:"Copy lines down",bindKey:o("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()},scrollIntoView:"cursor"},{name:"movelinesdown",description:"Move lines down",bindKey:o("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()},scrollIntoView:"cursor"},{name:"del",description:"Delete",bindKey:o("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"backspace",description:"Backspace",bindKey:o("Shift-Backspace|Backspace","Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"cut_or_delete",description:"Cut or delete",bindKey:o("Shift-Delete",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestart",description:"Remove to line start",bindKey:o("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineend",description:"Remove to line end",bindKey:o("Alt-Delete","Ctrl-K|Command-Delete"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestarthard",description:"Remove to line start hard",bindKey:o("Ctrl-Shift-Backspace",null),exec:function(e){var t=e.selection.getRange();t.start.column=0,e.session.remove(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineendhard",description:"Remove to line end hard",bindKey:o("Ctrl-Shift-Delete",null),exec:function(e){var t=e.selection.getRange();t.end.column=Number.MAX_VALUE,e.session.remove(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordleft",description:"Remove word left",bindKey:o("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordright",description:"Remove word right",bindKey:o("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"outdent",description:"Outdent",bindKey:o("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"indent",description:"Indent",bindKey:o("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"blockoutdent",description:"Block outdent",bindKey:o("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"blockindent",description:"Block indent",bindKey:o("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"insertstring",description:"Insert string",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"inserttext",description:"Insert text",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"splitline",description:"Split line",bindKey:o(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"transposeletters",description:"Transpose letters",bindKey:o("Alt-Shift-X","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:"cursor"},{name:"touppercase",description:"To uppercase",bindKey:o("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"tolowercase",description:"To lowercase",bindKey:o("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"autoindent",description:"Auto Indent",bindKey:o(null,null),exec:function(e){e.autoIndent()},multiSelectAction:"forEachLine",scrollIntoView:"animate"},{name:"expandtoline",description:"Expand to line",bindKey:o("Ctrl-Shift-L","Command-Shift-L"),exec:function(e){var t=e.selection.getRange();t.start.column=t.end.column=0,t.end.row++,e.selection.setRange(t,!1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"joinlines",description:"Join lines",bindKey:o(null,null),exec:function(e){var t=e.selection.isBackwards(),n=t?e.selection.getSelectionLead():e.selection.getSelectionAnchor(),i=t?e.selection.getSelectionAnchor():e.selection.getSelectionLead(),o=e.session.doc.getLine(n.row).length,u=e.session.doc.getTextRange(e.selection.getRange()),a=u.replace(/\n\s*/," ").length,f=e.session.doc.getLine(n.row);for(var l=n.row+1;l<=i.row+1;l++){var c=r.stringTrimLeft(r.stringTrimRight(e.session.doc.getLine(l)));c.length!==0&&(c=" "+c),f+=c}i.row+10?(e.selection.moveCursorTo(n.row,n.column),e.selection.selectTo(n.row,n.column+a)):(o=e.session.doc.getLine(n.row).length>o?o+1:o,e.selection.moveCursorTo(n.row,o))},multiSelectAction:"forEach",readOnly:!0},{name:"invertSelection",description:"Invert selection",bindKey:o(null,null),exec:function(e){var t=e.session.doc.getLength()-1,n=e.session.doc.getLine(t).length,r=e.selection.rangeList.ranges,i=[];r.length<1&&(r=[e.selection.getRange()]);for(var o=0;o=i.lastRow||r.end.row<=i.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead);break;default:}n=="animate"&&this.renderer.animateScrolling(this.curOp.scrollTop)}var s=this.selection.toJSON();this.curOp.selectionAfter=s,this.$lastSel=this.selection.toJSON(),this.session.getUndoManager().addSelection(s),this.prevOp=this.curOp,this.curOp=null}},this.$mergeableCommands=["backspace","del","insertstring"],this.$historyTracker=function(e){if(!this.$mergeUndoDeltas)return;var t=this.prevOp,n=this.$mergeableCommands,r=t.command&&e.command.name==t.command.name;if(e.command.name=="insertstring"){var i=e.args;this.mergeNextCommand===undefined&&(this.mergeNextCommand=!0),r=r&&this.mergeNextCommand&&(!/\s/.test(i)||/\s/.test(t.args)),this.mergeNextCommand=!0}else r=r&&n.indexOf(e.command.name)!==-1;this.$mergeUndoDeltas!="always"&&Date.now()-this.sequenceStartTime>2e3&&(r=!1),r?this.session.mergeUndoDeltas=!0:n.indexOf(e.command.name)!==-1&&(this.sequenceStartTime=Date.now())},this.setKeyboardHandler=function(e,t){if(e&&typeof e=="string"&&e!="ace"){this.$keybindingId=e;var n=this;g.loadModule(["keybinding",e],function(r){n.$keybindingId==e&&n.keyBinding.setKeyboardHandler(r&&r.handler),t&&t()})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e),t&&t()},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(e){if(this.session==e)return;this.curOp&&this.endOperation(),this.curOp={};var t=this.session;if(t){this.session.off("change",this.$onDocumentChange),this.session.off("changeMode",this.$onChangeMode),this.session.off("tokenizerUpdate",this.$onTokenizerUpdate),this.session.off("changeTabSize",this.$onChangeTabSize),this.session.off("changeWrapLimit",this.$onChangeWrapLimit),this.session.off("changeWrapMode",this.$onChangeWrapMode),this.session.off("changeFold",this.$onChangeFold),this.session.off("changeFrontMarker",this.$onChangeFrontMarker),this.session.off("changeBackMarker",this.$onChangeBackMarker),this.session.off("changeBreakpoint",this.$onChangeBreakpoint),this.session.off("changeAnnotation",this.$onChangeAnnotation),this.session.off("changeOverwrite",this.$onCursorChange),this.session.off("changeScrollTop",this.$onScrollTopChange),this.session.off("changeScrollLeft",this.$onScrollLeftChange);var n=this.session.getSelection();n.off("changeCursor",this.$onCursorChange),n.off("changeSelection",this.$onSelectionChange)}this.session=e,e?(this.$onDocumentChange=this.onDocumentChange.bind(this),e.on("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.on("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.on("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.on("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.on("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.on("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.on("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.on("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.on("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.on("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.on("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.on("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.on("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.on("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.on("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.on("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.onCursorChange(),this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull()):(this.selection=null,this.renderer.setSession(e)),this._signal("changeSession",{session:e,oldSession:t}),this.curOp=null,t&&t._signal("changeEditor",{oldEditor:this}),e&&e._signal("changeEditor",{editor:this}),e&&e.bgTokenizer&&e.bgTokenizer.scheduleStart()},this.getSession=function(){return this.session},this.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},this.getValue=function(){return this.session.getValue()},this.getSelection=function(){return this.selection},this.resize=function(e){this.renderer.onResize(e)},this.setTheme=function(e,t){this.renderer.setTheme(e,t)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(e){this.renderer.setStyle(e)},this.unsetStyle=function(e){this.renderer.unsetStyle(e)},this.getFontSize=function(){return this.getOption("fontSize")||i.computedStyle(this.container).fontSize},this.setFontSize=function(e){this.setOption("fontSize",e)},this.$highlightBrackets=function(){if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;t.$bracketHighlight&&(t.$bracketHighlight.markerIds.forEach(function(e){t.removeMarker(e)}),t.$bracketHighlight=null);var n=t.getMatchingBracketRanges(e.getCursorPosition());!n&&t.$mode.getMatching&&(n=t.$mode.getMatching(e.session));if(!n)return;var r="ace_bracket";Array.isArray(n)?n.length==1&&(r="ace_error_bracket"):n=[n],n.length==2&&(p.comparePoints(n[0].end,n[1].start)==0?n=[p.fromPoints(n[0].start,n[1].end)]:p.comparePoints(n[0].start,n[1].end)==0&&(n=[p.fromPoints(n[1].start,n[0].end)])),t.$bracketHighlight={ranges:n,markerIds:n.map(function(e){return t.addMarker(e,r,"text")})}},50)},this.$highlightTags=function(){if(this.$highlightTagPending)return;var e=this;this.$highlightTagPending=!0,setTimeout(function(){e.$highlightTagPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=e.getCursorPosition(),r=new y(e.session,n.row,n.column),i=r.getCurrentToken();if(!i||!/\b(?:tag-open|tag-name)/.test(i.type)){t.removeMarker(t.$tagHighlight),t.$tagHighlight=null;return}if(i.type.indexOf("tag-open")!==-1){i=r.stepForward();if(!i)return}var s=i.value,o=i.value,u=0,a=r.stepBackward();if(a.value==="<"){do a=i,i=r.stepForward(),i&&(i.type.indexOf("tag-name")!==-1?(o=i.value,s===o&&(a.value==="<"?u++:a.value===""&&u--)):s===o&&i.value==="/>"&&u--);while(i&&u>=0)}else{do{i=a,a=r.stepBackward();if(i)if(i.type.indexOf("tag-name")!==-1)s===i.value&&(a.value==="<"?u++:a.value===""&&u--);else if(i.value==="/>"){var f=0,l=a;while(l){if(l.type.indexOf("tag-name")!==-1&&l.value===s){u--;break}if(l.value==="<")break;l=r.stepBackward(),f++}for(var c=0;c1)&&(t=!1)}if(e.$highlightLineMarker&&!t)e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null;else if(!e.$highlightLineMarker&&t){var n=new p(t.row,t.column,t.row,Infinity);n.id=e.addMarker(n,"ace_active-line","screenLine"),e.$highlightLineMarker=n}else t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e.$highlightLineMarker.start.column=t.column,e._signal("changeBackMarker"))},this.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,"ace_selection",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._signal("changeSelection")},this.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column,r=t.end.column,i=e.getLine(t.start.row),s=i.substring(n,r);if(s.length>5e3||!/[\w\d]/.test(s))return;var o=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:s}),u=i.substring(n-1,r+1);if(!o.test(u))return;return o},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(e){this.renderer.updateText(),this._emit("changeMode",e)},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},this.getCopyText=function(){var e=this.getSelectedText(),t=this.session.doc.getNewLineCharacter(),n=!1;if(!e&&this.$copyWithEmptySelection){n=!0;var r=this.selection.getAllRanges();for(var i=0;iu.search(/\S|$/)){var a=u.substr(i.column).search(/\S|$/);n.doc.removeInLine(i.row,i.column,i.column+a)}}this.clearSelection();var f=i.column,l=n.getState(i.row),u=n.getLine(i.row),c=r.checkOutdent(l,u,e);n.insert(i,e),s&&s.selection&&(s.selection.length==2?this.selection.setSelectionRange(new p(i.row,f+s.selection[0],i.row,f+s.selection[1])):this.selection.setSelectionRange(new p(i.row+s.selection[0],s.selection[1],i.row+s.selection[2],s.selection[3])));if(this.$enableAutoIndent){if(n.getDocument().isNewLine(e)){var h=r.getNextLineIndent(l,u.slice(0,i.column),n.getTabString());n.insert({row:i.row+1,column:0},h)}c&&r.autoOutdent(l,n,i.row)}},this.autoIndent=function(){var e=this.session,t=e.getMode(),n,r;if(this.selection.isEmpty())n=0,r=e.doc.getLength()-1;else{var i=this.getSelectionRange();n=i.start.row,r=i.end.row}var s="",o="",u="",a,f,l,c=e.getTabString();for(var h=n;h<=r;h++)h>0&&(s=e.getState(h-1),o=e.getLine(h-1),u=t.getNextLineIndent(s,o,c)),a=e.getLine(h),f=t.$getIndent(a),u!==f&&(f.length>0&&(l=new p(h,0,h,f.length),e.remove(l)),u.length>0&&e.insert({row:h,column:0},u)),t.autoOutdent(s,e,h)},this.onTextInput=function(e,t){if(!t)return this.keyBinding.onTextInput(e);this.startOperation({command:{name:"insertstring"}});var n=this.applyComposition.bind(this,e,t);this.selection.rangeCount?this.forEachSelection(n):n(),this.endOperation()},this.applyComposition=function(e,t){if(t.extendLeft||t.extendRight){var n=this.selection.getRange();n.start.column-=t.extendLeft,n.end.column+=t.extendRight,n.start.column<0&&(n.start.row--,n.start.column+=this.session.getLine(n.start.row).length+1),this.selection.setRange(n),!e&&!n.isEmpty()&&this.remove()}(e||!this.selection.isEmpty())&&this.insert(e,!0);if(t.restoreStart||t.restoreEnd){var n=this.selection.getRange();n.start.column-=t.restoreStart,n.end.column-=t.restoreEnd,this.selection.setRange(n)}},this.onCommandKey=function(e,t,n){return this.keyBinding.onCommandKey(e,t,n)},this.setOverwrite=function(e){this.session.setOverwrite(e)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(e){this.setOption("scrollSpeed",e)},this.getScrollSpeed=function(){return this.getOption("scrollSpeed")},this.setDragDelay=function(e){this.setOption("dragDelay",e)},this.getDragDelay=function(){return this.getOption("dragDelay")},this.setSelectionStyle=function(e){this.setOption("selectionStyle",e)},this.getSelectionStyle=function(){return this.getOption("selectionStyle")},this.setHighlightActiveLine=function(e){this.setOption("highlightActiveLine",e)},this.getHighlightActiveLine=function(){return this.getOption("highlightActiveLine")},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.setHighlightSelectedWord=function(e){this.setOption("highlightSelectedWord",e)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},this.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},this.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.setReadOnly=function(e){this.setOption("readOnly",e)},this.getReadOnly=function(){return this.getOption("readOnly")},this.setBehavioursEnabled=function(e){this.setOption("behavioursEnabled",e)},this.getBehavioursEnabled=function(){return this.getOption("behavioursEnabled")},this.setWrapBehavioursEnabled=function(e){this.setOption("wrapBehavioursEnabled",e)},this.getWrapBehavioursEnabled=function(){return this.getOption("wrapBehavioursEnabled")},this.setShowFoldWidgets=function(e){this.setOption("showFoldWidgets",e)},this.getShowFoldWidgets=function(){return this.getOption("showFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.remove=function(e){this.selection.isEmpty()&&(e=="left"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,"deletion",this,n,t);if(t.end.column===0){var s=n.getTextRange(t);if(s[s.length-1]=="\n"){var o=n.getLine(t.end.row);/^\s+$/.test(o)&&(t.end.column=o.length)}}i&&(t=i)}this.session.remove(t),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.selection.isEmpty()&&this.selection.selectLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;tt.toLowerCase()?1:0});var i=new p(0,0,0,0);for(var r=e.first;r<=e.last;r++){var s=t.getLine(r);i.start.row=r,i.end.row=r,i.end.column=s.length,t.replace(i,n[r-e.first])}},this.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},this.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),n=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,n,e)},this.getNumberAt=function(e,t){var n=/[\-]?[0-9]+(?:\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new p(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(".")>=0?s.start+s.value.indexOf(".")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&n=u&&o<=a&&(n=t,f.selection.clearSelection(),f.moveCursorTo(e,u+r),f.selection.selectTo(e,a+r)),u=a});var l=this.$toggleWordPairs,c;for(var h=0;hp+1)break;p=d.last}l--,u=this.session.$moveLines(h,p,t?0:e),t&&e==-1&&(c=l+1);while(c<=l)o[c].moveBy(u,0),c++;t||(u=0),a+=u}i.fromOrientedRange(i.ranges[0]),i.rangeList.attach(this.session),this.inVirtualSelectionMode=!1}},this.$getSelectedRows=function(e){return e=(e||this.getSelectionRange()).collapseRows(),{first:this.session.getRowFoldStart(e.start.row),last:this.session.getRowFoldEnd(e.end.row)}},this.onCompositionStart=function(e){this.renderer.showComposition(e)},this.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},this.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t===!1&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection());var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},this.selectPageDown=function(){this.$moveByPage(1,!0)},this.selectPageUp=function(){this.$moveByPage(-1,!0)},this.gotoPageDown=function(){this.$moveByPage(1,!1)},this.gotoPageUp=function(){this.$moveByPage(-1,!1)},this.scrollPageDown=function(){this.$moveByPage(1)},this.scrollPageUp=function(){this.$moveByPage(-1)},this.scrollToRow=function(e){this.renderer.scrollToRow(e)},this.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},this.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.selection.selectAll()},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},this.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},this.jumpToMatching=function(e,t){var n=this.getCursorPosition(),r=new y(this.session,n.row,n.column),i=r.getCurrentToken(),s=i||r.stepForward();if(!s)return;var o,u=!1,a={},f=n.column-s.start,l,c={")":"(","(":"(","]":"[","[":"[","{":"{","}":"{"};do{if(s.value.match(/[{}()\[\]]/g))for(;f=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),r},this.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(e,t,n){t||(t={}),typeof e=="string"||e instanceof RegExp?t.needle=e:typeof e=="object"&&r.mixin(t,e);var i=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(i)||this.$search.$options.needle,e||(i=this.session.getWordRange(i.start.row,i.start.column),e=this.session.getTextRange(i)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:i});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?i.start=i.end:i.end=i.start,this.selection.setRange(i)},this.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},this.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},this.revealRange=function(e,t){this.session.unfold(e),this.selection.setSelectionRange(e);var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(n)},this.undo=function(){this.session.getUndoManager().undo(this.session),this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.session.getUndoManager().redo(this.session),this.renderer.scrollCursorIntoView(null,.5)},this.destroy=function(){this.$toDestroy&&(this.$toDestroy.forEach(function(e){e.destroy()}),this.$toDestroy=null),this.$mouseHandler&&this.$mouseHandler.destroy(),this.renderer.destroy(),this._signal("destroy",this),this.session&&this.session.destroy(),this._$emitInputEvent&&this._$emitInputEvent.cancel(),this.removeAllListeners()},this.setAutoScrollEditorIntoView=function(e){if(!e)return;var t,n=this,r=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement("div"));var i=this.$scrollAnchor;i.style.cssText="position:absolute",this.container.insertBefore(i,this.container.firstChild);var s=this.on("changeSelection",function(){r=!0}),o=this.renderer.on("beforeRender",function(){r&&(t=n.renderer.container.getBoundingClientRect())}),u=this.renderer.on("afterRender",function(){if(r&&t&&(n.isFocused()||n.searchBox&&n.searchBox.isFocused())){var e=n.renderer,s=e.$cursorLayer.$pixelPos,o=e.layerConfig,u=s.top-o.offset;s.top>=0&&u+t.top<0?r=!0:s.topwindow.innerHeight?r=!1:r=null,r!=null&&(i.style.top=u+"px",i.style.left=s.left+"px",i.style.height=o.lineHeight+"px",i.scrollIntoView(r)),r=t=null}});this.setAutoScrollEditorIntoView=function(e){if(e)return;delete this.setAutoScrollEditorIntoView,this.off("changeSelection",s),this.renderer.off("afterRender",u),this.renderer.off("beforeRender",o)}},this.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;if(!t)return;t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&e!="wide",i.setCssClass(t.element,"ace_slim-cursors",/slim/.test(e))},this.prompt=function(e,t,n){var r=this;g.loadModule("./ext/prompt",function(i){i.prompt(r,e,t,n)})}}.call(w.prototype),g.defineOptions(w.prototype,"editor",{selectionStyle:{set:function(e){this.onSelectionChange(),this._signal("changeSelectionStyle",{data:e})},initialValue:"line"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.textInput.setReadOnly(e),this.$resetCursorStyle()},initialValue:!1},copyWithEmptySelection:{set:function(e){this.textInput.setCopyWithEmptySelection(e)},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:["ace","slim","smooth","wide"],initialValue:"ace"},mergeUndoDeltas:{values:[!1,!0,"always"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},enableAutoIndent:{initialValue:!0},autoScrollEditorIntoView:{set:function(e){this.setAutoScrollEditorIntoView(e)}},keyboardHandler:{set:function(e){this.setKeyboardHandler(e)},get:function(){return this.$keybindingId},handlesSet:!0},value:{set:function(e){this.session.setValue(e)},get:function(){return this.getValue()},handlesSet:!0,hidden:!0},session:{set:function(e){this.setSession(e)},get:function(){return this.session},handlesSet:!0,hidden:!0},showLineNumbers:{set:function(e){this.renderer.$gutterLayer.setShowLineNumbers(e),this.renderer.$loop.schedule(this.renderer.CHANGE_GUTTER),e&&this.$relativeLineNumbers?E.attach(this):E.detach(this)},initialValue:!0},relativeLineNumbers:{set:function(e){this.$showLineNumbers&&e?E.attach(this):E.detach(this)}},placeholder:{set:function(e){this.$updatePlaceholder||(this.$updatePlaceholder=function(){var e=this.session&&(this.renderer.$composition||this.getValue());if(e&&this.renderer.placeholderNode)this.renderer.off("afterRender",this.$updatePlaceholder),i.removeCssClass(this.container,"ace_hasPlaceholder"),this.renderer.placeholderNode.remove(),this.renderer.placeholderNode=null;else if(!e&&!this.renderer.placeholderNode){this.renderer.on("afterRender",this.$updatePlaceholder),i.addCssClass(this.container,"ace_hasPlaceholder");var t=i.createElement("div");t.className="ace_placeholder",t.textContent=this.$placeholder||"",this.renderer.placeholderNode=t,this.renderer.content.appendChild(this.renderer.placeholderNode)}else!e&&this.renderer.placeholderNode&&(this.renderer.placeholderNode.textContent=this.$placeholder||"")}.bind(this),this.on("input",this.$updatePlaceholder)),this.$updatePlaceholder()}},hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",displayIndentGuides:"renderer",showGutter:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",hasCssTransforms:"renderer",maxPixelHeight:"renderer",useTextareaForIME:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimeout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",navigateWithinSoftTabs:"session",tabSize:"session",wrap:"session",indentedSoftWrap:"session",foldStyle:"session",mode:"session"});var E={getText:function(e,t){return(Math.abs(e.selection.lead.row-t)||t+1+(t<9?"\u00b7":""))+""},getWidth:function(e,t,n){return Math.max(t.toString().length,(n.lastRow+1).toString().length,2)*n.characterWidth},update:function(e,t){t.renderer.$loop.schedule(t.renderer.CHANGE_GUTTER)},attach:function(e){e.renderer.$gutterLayer.$renderer=this,e.on("changeSelection",this.update),this.update(null,e)},detach:function(e){e.renderer.$gutterLayer.$renderer==this&&(e.renderer.$gutterLayer.$renderer=null),e.off("changeSelection",this.update),this.update(null,e)}};t.Editor=w}),define("ace/undomanager",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){for(var n=t;n--;){var r=e[n];if(r&&!r[0].ignore){while(n0){a.row+=i,a.column+=a.row==r.row?s:0;continue}!t&&l<=0&&(a.row=n.row,a.column=n.column,l===0&&(a.bias=1))}}function f(e){return{row:e.row,column:e.column}}function l(e){return{start:f(e.start),end:f(e.end),action:e.action,lines:e.lines.slice()}}function c(e){e=e||this;if(Array.isArray(e))return e.map(c).join("\n");var t="";e.action?(t=e.action=="insert"?"+":"-",t+="["+e.lines+"]"):e.value&&(Array.isArray(e.value)?t=e.value.map(h).join("\n"):t=h(e.value)),e.start&&(t+=h(e));if(e.id||e.rev)t+=" ("+(e.id||e.rev)+")";return t}function h(e){return e.start.row+":"+e.start.column+"=>"+e.end.row+":"+e.end.column}function p(e,t){var n=e.action=="insert",r=t.action=="insert";if(n&&r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}else if(!n&&r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(!n&&!r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}return[t,e]}function d(e,t){for(var n=e.length;n--;)for(var r=0;r=0?m(e,t,-1):o(e.start,t.start)<=0?m(t,e,1):(m(e,s.fromPoints(t.start,e.start),-1),m(t,e,1));else if(!n&&r)o(t.start,e.end)>=0?m(t,e,-1):o(t.start,e.start)<=0?m(e,t,1):(m(t,s.fromPoints(e.start,t.start),-1),m(e,t,1));else if(!n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0)){var i,u;return o(e.start,t.start)<0&&(i=e,e=y(e,t.start)),o(e.end,t.end)>0&&(u=y(e,t.end)),g(t.end,e.start,e.end,-1),u&&!i&&(e.lines=u.lines,e.start=u.start,e.end=u.end,u=e),[t,i,u].filter(Boolean)}m(e,t,-1)}return[t,e]}function m(e,t,n){g(e.start,t.start,t.end,n),g(e.end,t.start,t.end,n)}function g(e,t,n,r){e.row==(r==1?t:n).row&&(e.column+=r*(n.column-t.column)),e.row+=r*(n.row-t.row)}function y(e,t){var n=e.lines,r=e.end;e.end=f(t);var i=e.end.row-e.start.row,s=n.splice(i,n.length),o=i?t.column:t.column-e.start.column;n.push(s[0].substring(0,o)),s[0]=s[0].substr(o);var u={start:f(t),end:r,lines:s,action:e.action};return u}function b(e,t){t=l(t);for(var n=e.length;n--;){var r=e[n];for(var i=0;i0},this.canRedo=function(){return this.$redoStack.length>0},this.bookmark=function(e){e==undefined&&(e=this.$rev),this.mark=e},this.isAtBookmark=function(){return this.$rev===this.mark},this.toJSON=function(){},this.fromJSON=function(){},this.hasUndo=this.canUndo,this.hasRedo=this.canRedo,this.isClean=this.isAtBookmark,this.markClean=this.bookmark,this.$prettyPrint=function(e){return e?c(e):c(this.$undoStack)+"\n---\n"+c(this.$redoStack)}}).call(r.prototype);var s=e("./range").Range,o=s.comparePoints,u=s.comparePoints;t.UndoManager=r}),define("ace/layer/lines",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=function(e,t){this.element=e,this.canvasHeight=t||5e5,this.element.style.height=this.canvasHeight*2+"px",this.cells=[],this.cellCache=[],this.$offsetCoefficient=0};(function(){this.moveContainer=function(e){r.translate(this.element,0,-(e.firstRowScreen*e.lineHeight%this.canvasHeight)-e.offset*this.$offsetCoefficient)},this.pageChanged=function(e,t){return Math.floor(e.firstRowScreen*e.lineHeight/this.canvasHeight)!==Math.floor(t.firstRowScreen*t.lineHeight/this.canvasHeight)},this.computeLineTop=function(e,t,n){var r=t.firstRowScreen*t.lineHeight,i=Math.floor(r/this.canvasHeight),s=n.documentToScreenRow(e,0)*t.lineHeight;return s-i*this.canvasHeight},this.computeLineHeight=function(e,t,n){return t.lineHeight*n.getRowLineCount(e)},this.getLength=function(){return this.cells.length},this.get=function(e){return this.cells[e]},this.shift=function(){this.$cacheCell(this.cells.shift())},this.pop=function(){this.$cacheCell(this.cells.pop())},this.push=function(e){if(Array.isArray(e)){this.cells.push.apply(this.cells,e);var t=r.createFragment(this.element);for(var n=0;ns&&(a=i.end.row+1,i=t.getNextFoldLine(a,i),s=i?i.start.row:Infinity);if(a>r){while(this.$lines.getLength()>u+1)this.$lines.pop();break}o=this.$lines.get(++u),o?o.row=a:(o=this.$lines.createCell(a,e,this.session,f),this.$lines.push(o)),this.$renderCell(o,e,i,a),a++}this._signal("afterRender"),this.$updateGutterWidth(e)},this.$updateGutterWidth=function(e){var t=this.session,n=t.gutterRenderer||this.$renderer,r=t.$firstLineNumber,i=this.$lines.last()?this.$lines.last().text:"";if(this.$fixedWidth||t.$useWrapMode)i=t.getLength()+r-1;var s=n?n.getWidth(t,i,e):i.toString().length*e.characterWidth,o=this.$padding||this.$computePadding();s+=o.left+o.right,s!==this.gutterWidth&&!isNaN(s)&&(this.gutterWidth=s,this.element.parentNode.style.width=this.element.style.width=Math.ceil(this.gutterWidth)+"px",this._signal("changeGutterWidth",s))},this.$updateCursorRow=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.getCursor();if(this.$cursorRow===e.row)return;this.$cursorRow=e.row},this.updateLineHighlight=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.cursor.row;this.$cursorRow=e;if(this.$cursorCell&&this.$cursorCell.row==e)return;this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace("ace_gutter-active-line ",""));var t=this.$lines.cells;this.$cursorCell=null;for(var n=0;n=this.$cursorRow){if(r.row>this.$cursorRow){var i=this.session.getFoldLine(this.$cursorRow);if(!(n>0&&i&&i.start.row==t[n-1].row))break;r=t[n-1]}r.element.className="ace_gutter-active-line "+r.element.className,this.$cursorCell=r;break}}},this.scrollLines=function(e){var t=this.config;this.config=e,this.$updateCursorRow();if(this.$lines.pageChanged(t,e))return this.update(e);this.$lines.moveContainer(e);var n=Math.min(e.lastRow+e.gutterOffset,this.session.getLength()-1),r=this.oldLastRow;this.oldLastRow=n;if(!t||r0;i--)this.$lines.shift();if(r>n)for(var i=this.session.getFoldedRowCount(n+1,r);i>0;i--)this.$lines.pop();e.firstRowr&&this.$lines.push(this.$renderLines(e,r+1,n)),this.updateLineHighlight(),this._signal("afterRender"),this.$updateGutterWidth(e)},this.$renderLines=function(e,t,n){var r=[],i=t,s=this.session.getNextFoldLine(i),o=s?s.start.row:Infinity;for(;;){i>o&&(i=s.end.row+1,s=this.session.getNextFoldLine(i,s),o=s?s.start.row:Infinity);if(i>n)break;var u=this.$lines.createCell(i,e,this.session,f);this.$renderCell(u,e,s,i),r.push(u),i++}return r},this.$renderCell=function(e,t,n,i){var s=e.element,o=this.session,u=s.childNodes[0],a=s.childNodes[1],f=o.$firstLineNumber,l=o.$breakpoints,c=o.$decorations,h=o.gutterRenderer||this.$renderer,p=this.$showFoldWidgets&&o.foldWidgets,d=n?n.start.row:Number.MAX_VALUE,v="ace_gutter-cell ";this.$highlightGutterLine&&(i==this.$cursorRow||n&&i=d&&this.$cursorRow<=n.end.row)&&(v+="ace_gutter-active-line ",this.$cursorCell!=e&&(this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace("ace_gutter-active-line ","")),this.$cursorCell=e)),l[i]&&(v+=l[i]),c[i]&&(v+=c[i]),this.$annotations[i]&&(v+=this.$annotations[i].className),s.className!=v&&(s.className=v);if(p){var m=p[i];m==null&&(m=p[i]=o.getFoldWidget(i))}if(m){var v="ace_fold-widget ace_"+m;m=="start"&&i==d&&in.right-t.right)return"foldWidgets"}}).call(a.prototype),t.Gutter=a}),define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../range").Range,i=e("../lib/dom"),s=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)};(function(){function e(e,t,n,r){return(e?1:0)|(t?2:0)|(n?4:0)|(r?8:0)}this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setMarkers=function(e){this.markers=e},this.elt=function(e,t){var n=this.i!=-1&&this.element.childNodes[this.i];n?this.i++:(n=document.createElement("div"),this.element.appendChild(n),this.i=-1),n.style.cssText=t,n.className=e},this.update=function(e){if(!e)return;this.config=e,this.i=0;var t;for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var i=r.range.clipRows(e.firstRow,e.lastRow);if(i.isEmpty())continue;i=i.toScreenRange(this.session);if(r.renderer){var s=this.$getTop(i.start.row,e),o=this.$padding+i.start.column*e.characterWidth;r.renderer(t,i,o,s,e)}else r.type=="fullLine"?this.drawFullLineMarker(t,i,r.clazz,e):r.type=="screenLine"?this.drawScreenLineMarker(t,i,r.clazz,e):i.isMultiLine()?r.type=="text"?this.drawTextMarker(t,i,r.clazz,e):this.drawMultiLineMarker(t,i,r.clazz,e):this.drawSingleLineMarker(t,i,r.clazz+" ace_start"+" ace_br15",e)}if(this.i!=-1)while(this.ip,l==f),s,l==f?0:1,o)},this.drawMultiLineMarker=function(e,t,n,r,i){var s=this.$padding,o=r.lineHeight,u=this.$getTop(t.start.row,r),a=s+t.start.column*r.characterWidth;i=i||"";if(this.session.$bidiHandler.isBidiRow(t.start.row)){var f=t.clone();f.end.row=f.start.row,f.end.column=this.session.getLine(f.start.row).length,this.drawBidiSingleLineMarker(e,f,n+" ace_br1 ace_start",r,null,i)}else this.elt(n+" ace_br1 ace_start","height:"+o+"px;"+"right:0;"+"top:"+u+"px;left:"+a+"px;"+(i||""));if(this.session.$bidiHandler.isBidiRow(t.end.row)){var f=t.clone();f.start.row=f.end.row,f.start.column=0,this.drawBidiSingleLineMarker(e,f,n+" ace_br12",r,null,i)}else{u=this.$getTop(t.end.row,r);var l=t.end.column*r.characterWidth;this.elt(n+" ace_br12","height:"+o+"px;"+"width:"+l+"px;"+"top:"+u+"px;"+"left:"+s+"px;"+(i||""))}o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<=0)return;u=this.$getTop(t.start.row+1,r);var c=(t.start.column?1:0)|(t.end.column?0:8);this.elt(n+(c?" ace_br"+c:""),"height:"+o+"px;"+"right:0;"+"top:"+u+"px;"+"left:"+s+"px;"+(i||""))},this.drawSingleLineMarker=function(e,t,n,r,i,s){if(this.session.$bidiHandler.isBidiRow(t.start.row))return this.drawBidiSingleLineMarker(e,t,n,r,i,s);var o=r.lineHeight,u=(t.end.column+(i||0)-t.start.column)*r.characterWidth,a=this.$getTop(t.start.row,r),f=this.$padding+t.start.column*r.characterWidth;this.elt(n,"height:"+o+"px;"+"width:"+u+"px;"+"top:"+a+"px;"+"left:"+f+"px;"+(s||""))},this.drawBidiSingleLineMarker=function(e,t,n,r,i,s){var o=r.lineHeight,u=this.$getTop(t.start.row,r),a=this.$padding,f=this.session.$bidiHandler.getSelections(t.start.column,t.end.column);f.forEach(function(e){this.elt(n,"height:"+o+"px;"+"width:"+e.width+(i||0)+"px;"+"top:"+u+"px;"+"left:"+(a+e.left)+"px;"+(s||""))},this)},this.drawFullLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;t.start.row!=t.end.row&&(o+=this.$getTop(t.end.row,r)-s),this.elt(n,"height:"+o+"px;"+"top:"+s+"px;"+"left:0;right:0;"+(i||""))},this.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;this.elt(n,"height:"+o+"px;"+"top:"+s+"px;"+"left:0;right:0;"+(i||""))}}).call(s.prototype),t.Marker=s}),define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/layer/lines","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("./lines").Lines,u=e("../lib/event_emitter").EventEmitter,a=function(e){this.dom=i,this.element=this.dom.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this),this.$lines=new o(this.element)};(function(){r.implement(this,u),this.EOF_CHAR="\u00b6",this.EOL_CHAR_LF="\u00ac",this.EOL_CHAR_CRLF="\u00a4",this.EOL_CHAR=this.EOL_CHAR_LF,this.TAB_CHAR="\u2014",this.SPACE_CHAR="\u00b7",this.$padding=0,this.MAX_LINE_LENGTH=1e4,this.$updateEolChar=function(){var e=this.session.doc,t=e.getNewLineCharacter()=="\n"&&e.getNewLineMode()!="windows",n=t?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;if(this.EOL_CHAR!=n)return this.EOL_CHAR=n,!0},this.setPadding=function(e){this.$padding=e,this.element.style.margin="0 "+e+"px"},this.getLineHeight=function(){return this.$fontMetrics.$characterSize.height||0},this.getCharacterWidth=function(){return this.$fontMetrics.$characterSize.width||0},this.$setFontMetrics=function(e){this.$fontMetrics=e,this.$fontMetrics.on("changeCharacterSize",function(e){this._signal("changeCharacterSize",e)}.bind(this)),this.$pollSizeChanges()},this.checkForSizeChanges=function(){this.$fontMetrics.checkForSizeChanges()},this.$pollSizeChanges=function(){return this.$pollSizeChangesTimer=this.$fontMetrics.$pollSizeChanges()},this.setSession=function(e){this.session=e,e&&this.$computeTabString()},this.showInvisibles=!1,this.showSpaces=!1,this.showTabs=!1,this.showEOL=!1,this.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,typeof e=="string"?(this.showSpaces=/tab/i.test(e),this.showTabs=/space/i.test(e),this.showEOL=/eol/i.test(e)):this.showSpaces=this.showTabs=this.showEOL=e,this.$computeTabString(),!0)},this.displayIndentGuides=!0,this.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},this.$tabStrings=[],this.onChangeTabSize=this.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;nl&&(u=a.end.row+1,a=this.session.getNextFoldLine(u,a),l=a?a.start.row:Infinity);if(u>i)break;var c=s[o++];if(c){this.dom.removeChildren(c),this.$renderLine(c,u,u==l?a:!1),f&&(c.style.top=this.$lines.computeLineTop(u,e,this.session)+"px");var h=e.lineHeight*this.session.getRowLength(u)+"px";c.style.height!=h&&(f=!0,c.style.height=h)}u++}if(f)while(o0;i--)this.$lines.shift();if(t.lastRow>e.lastRow)for(var i=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);i>0;i--)this.$lines.pop();e.firstRowt.lastRow&&this.$lines.push(this.$renderLinesFragment(e,t.lastRow+1,e.lastRow))},this.$renderLinesFragment=function(e,t,n){var r=[],s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=this.$lines.createCell(s,e,this.session),f=a.element;this.dom.removeChildren(f),i.setStyle(f.style,"height",this.$lines.computeLineHeight(s,e,this.session)+"px"),i.setStyle(f.style,"top",this.$lines.computeLineTop(s,e,this.session)+"px"),this.$renderLine(f,s,s==u?o:!1),this.$useLineGroups()?f.className="ace_line_group":f.className="ace_line",r.push(a),s++}return r},this.update=function(e){this.$lines.moveContainer(e),this.config=e;var t=e.firstRow,n=e.lastRow,r=this.$lines;while(r.getLength())r.pop();r.push(this.$renderLinesFragment(e,t,n))},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(e,t,n,r){var i=this,o=/(\t)|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\uFEFF\uFFF9-\uFFFC]+)|(\u3000)|([\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3001-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]|[\uD800-\uDBFF][\uDC00-\uDFFF])/g,u=this.dom.createFragment(this.element),a,f=0;while(a=o.exec(r)){var l=a[1],c=a[2],h=a[3],p=a[4],d=a[5];if(!i.showSpaces&&c)continue;var v=f!=a.index?r.slice(f,a.index):"";f=a.index+a[0].length,v&&u.appendChild(this.dom.createTextNode(v,this.element));if(l){var m=i.session.getScreenTabSize(t+a.index);u.appendChild(i.$tabStrings[m].cloneNode(!0)),t+=m-1}else if(c)if(i.showSpaces){var g=this.dom.createElement("span");g.className="ace_invisible ace_invisible_space",g.textContent=s.stringRepeat(i.SPACE_CHAR,c.length),u.appendChild(g)}else u.appendChild(this.com.createTextNode(c,this.element));else if(h){var g=this.dom.createElement("span");g.className="ace_invisible ace_invisible_space ace_invalid",g.textContent=s.stringRepeat(i.SPACE_CHAR,h.length),u.appendChild(g)}else if(p){t+=1;var g=this.dom.createElement("span");g.style.width=i.config.characterWidth*2+"px",g.className=i.showSpaces?"ace_cjk ace_invisible ace_invisible_space":"ace_cjk",g.textContent=i.showSpaces?i.SPACE_CHAR:p,u.appendChild(g)}else if(d){t+=1;var g=this.dom.createElement("span");g.style.width=i.config.characterWidth*2+"px",g.className="ace_cjk",g.textContent=d,u.appendChild(g)}}u.appendChild(this.dom.createTextNode(f?r.slice(f):r,this.element));if(!this.$textToken[n.type]){var y="ace_"+n.type.replace(/\./g," ace_"),g=this.dom.createElement("span");n.type=="fold"&&(g.style.width=n.value.length*this.config.characterWidth+"px"),g.className=y,g.appendChild(u),e.appendChild(g)}else e.appendChild(u);return t+r.length},this.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);if(r<=0||r>=n)return t;if(t[0]==" "){r-=r%this.tabSize;var i=r/this.tabSize;for(var s=0;s=o)u=this.$renderToken(a,u,l,c.substring(0,o-r)),c=c.substring(o-r),r=o,a=this.$createLineElement(),e.appendChild(a),a.appendChild(this.dom.createTextNode(s.stringRepeat("\u00a0",n.indent),this.element)),i++,u=0,o=n[i]||Number.MAX_VALUE;c.length!=0&&(r+=c.length,u=this.$renderToken(a,u,l,c))}}n[n.length-1]>this.MAX_LINE_LENGTH&&this.$renderOverflowMessage(a,u,null,"",!0)},this.$renderSimpleLine=function(e,t){var n=0,r=t[0],i=r.value;this.displayIndentGuides&&(i=this.renderIndentGuide(e,i)),i&&(n=this.$renderToken(e,n,r,i));for(var s=1;sthis.MAX_LINE_LENGTH)return this.$renderOverflowMessage(e,n,r,i);n=this.$renderToken(e,n,r,i)}},this.$renderOverflowMessage=function(e,t,n,r,i){n&&this.$renderToken(e,t,n,r.slice(0,this.MAX_LINE_LENGTH-t));var s=this.dom.createElement("span");s.className="ace_inline_button ace_keyword ace_toggle_wrap",s.textContent=i?"":"",e.appendChild(s)},this.$renderLine=function(e,t,n){!n&&n!=0&&(n=this.session.getFoldLine(t));if(n)var r=this.$getFoldLineTokens(t,n);else var r=this.session.getTokens(t);var i=e;if(r.length){var s=this.session.getRowSplitData(t);if(s&&s.length){this.$renderWrappedLine(e,r,s);var i=e.lastChild}else{var i=e;this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i)),this.$renderSimpleLine(i,r)}}else this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i));if(this.showEOL&&i){n&&(t=n.end.row);var o=this.dom.createElement("span");o.className="ace_invisible ace_invisible_eol",o.textContent=t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,i.appendChild(o)}},this.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.lengthn-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(sn?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:"fold",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){}}).call(a.prototype),t.Text=a}),define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors"),this.$updateCursors=this.$updateOpacity.bind(this)};(function(){this.$updateOpacity=function(e){var t=this.cursors;for(var n=t.length;n--;)r.setStyle(t[n].style,"opacity",e?"":"0")},this.$startCssAnimation=function(){var e=this.cursors;for(var t=e.length;t--;)e[t].style.animationDuration=this.blinkInterval+"ms";setTimeout(function(){r.addCssClass(this.element,"ace_animate-blinking")}.bind(this))},this.$stopCssAnimation=function(){r.removeCssClass(this.element,"ace_animate-blinking")},this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},this.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},this.setSmoothBlinking=function(e){e!=this.smoothBlinking&&(this.smoothBlinking=e,r.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.restartTimer())},this.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},this.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.restartTimer=function(){var e=this.$updateCursors;clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.$stopCssAnimation(),this.smoothBlinking&&r.removeCssClass(this.element,"ace_smooth-blinking"),e(!0);if(!this.isBlinking||!this.blinkInterval||!this.isVisible){this.$stopCssAnimation();return}this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this));if(r.HAS_CSS_ANIMATION)this.$startCssAnimation();else{var t=function(){this.timeoutId=setTimeout(function(){e(!1)},.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){e(!0),t()},this.blinkInterval),t()}},this.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+(this.session.$bidiHandler.isBidiRow(n.row,e.row)?this.session.$bidiHandler.getPosLeft(n.column):n.column*this.config.characterWidth),i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},this.isCursorInView=function(e,t){return e.top>=0&&e.tope.height+e.offset||o.top<0)&&n>1)continue;var u=this.cursors[i++]||this.addCursor(),a=u.style;this.drawCursor?this.drawCursor(u,o,e,t[n],this.session):this.isCursorInView(o,e)?(r.setStyle(a,"display","block"),r.translate(u,o.left,o.top),r.setStyle(a,"width",Math.round(e.characterWidth)+"px"),r.setStyle(a,"height",e.lineHeight+"px")):r.setStyle(a,"display","none")}while(this.cursors.length>i)this.removeCursor();var f=this.session.getOverwrite();this.$setOverwrite(f),this.$pixelPos=o,this.restartTimer()},this.drawCursor=null,this.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(i.prototype),t.Cursor=i}),define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/event"),o=e("./lib/event_emitter").EventEmitter,u=32768,a=function(e){this.element=i.createElement("div"),this.element.className="ace_scrollbar ace_scrollbar"+this.classSuffix,this.inner=i.createElement("div"),this.inner.className="ace_scrollbar-inner",this.inner.textContent="\u00a0",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,s.addListener(this.element,"scroll",this.onScroll.bind(this)),s.addListener(this.element,"mousedown",s.preventDefault)};(function(){r.implement(this,o),this.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e,this.coeff=1}}).call(a.prototype);var f=function(e,t){a.call(this,e),this.scrollTop=0,this.scrollHeight=0,t.$scrollbarWidth=this.width=i.scrollbarWidth(e.ownerDocument),this.inner.style.width=this.element.style.width=(this.width||15)+5+"px",this.$minWidth=0};r.inherits(f,a),function(){this.classSuffix="-v",this.onScroll=function(){if(!this.skipEvent){this.scrollTop=this.element.scrollTop;if(this.coeff!=1){var e=this.element.clientHeight/this.scrollHeight;this.scrollTop=this.scrollTop*(1-e)/(this.coeff-e)}this._emit("scroll",{data:this.scrollTop})}this.skipEvent=!1},this.getWidth=function(){return Math.max(this.isVisible?this.width:0,this.$minWidth||0)},this.setHeight=function(e){this.element.style.height=e+"px"},this.setInnerHeight=this.setScrollHeight=function(e){this.scrollHeight=e,e>u?(this.coeff=u/e,e=u):this.coeff!=1&&(this.coeff=1),this.inner.style.height=e+"px"},this.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=e,this.element.scrollTop=e*this.coeff)}}.call(f.prototype);var l=function(e,t){a.call(this,e),this.scrollLeft=0,this.height=t.$scrollbarWidth,this.inner.style.height=this.element.style.height=(this.height||15)+5+"px"};r.inherits(l,a),function(){this.classSuffix="-h",this.onScroll=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit("scroll",{data:this.scrollLeft})),this.skipEvent=!1},this.getHeight=function(){return this.isVisible?this.height:0},this.setWidth=function(e){this.element.style.width=e+"px"},this.setInnerWidth=function(e){this.inner.style.width=e+"px"},this.setScrollWidth=function(e){this.inner.style.width=e+"px"},this.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)}}.call(l.prototype),t.ScrollBar=f,t.ScrollBarV=f,t.ScrollBarH=l,t.VScrollBar=f,t.HScrollBar=l}),define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,n){"use strict";var r=e("./lib/event"),i=function(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.$recursionLimit=2,this.window=t||window;var n=this;this._flush=function(e){n.pending=!1;var t=n.changes;t&&(r.blockIdle(100),n.changes=0,n.onRender(t));if(n.changes){if(n.$recursionLimit--<0)return;n.schedule()}else n.$recursionLimit=2}};(function(){this.schedule=function(e){this.changes=this.changes|e,this.changes&&!this.pending&&(r.nextFrame(this._flush),this.pending=!0)},this.clear=function(e){var t=this.changes;return this.changes=0,t}}).call(i.prototype),t.RenderLoop=i}),define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/event"),u=e("../lib/useragent"),a=e("../lib/event_emitter").EventEmitter,f=256,l=typeof ResizeObserver=="function",c=200,h=t.FontMetrics=function(e){this.el=i.createElement("div"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=i.createElement("div"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=i.createElement("div"),this.$setMeasureNodeStyles(this.$measureNode.style),this.el.appendChild(this.$main),this.el.appendChild(this.$measureNode),e.appendChild(this.el),this.$measureNode.textContent=s.stringRepeat("X",f),this.$characterSize={width:0,height:0},l?this.$addObserver():this.checkForSizeChanges()};(function(){r.implement(this,a),this.$characterSize={width:0,height:0},this.$setMeasureNodeStyles=function(e,t){e.width=e.height="auto",e.left=e.top="0px",e.visibility="hidden",e.position="absolute",e.whiteSpace="pre",u.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},this.checkForSizeChanges=function(e){e===undefined&&(e=this.$measureSizes());if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.charSizes=Object.create(null),this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},this.$addObserver=function(){var e=this;this.$observer=new window.ResizeObserver(function(t){e.checkForSizeChanges()}),this.$observer.observe(this.$measureNode)},this.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer||this.$observer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=o.onIdle(function t(){e.checkForSizeChanges(),o.onIdle(t,500)},500)},this.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&(clearInterval(this.$pollSizeChangesTimer),this.$pollSizeChangesTimer=0)},this.$measureSizes=function(e){var t={height:(e||this.$measureNode).clientHeight,width:(e||this.$measureNode).clientWidth/f};return t.width===0||t.height===0?null:t},this.$measureCharWidth=function(e){this.$main.textContent=s.stringRepeat(e,f);var t=this.$main.getBoundingClientRect();return t.width/f},this.getCharacterWidth=function(e){var t=this.charSizes[e];return t===undefined&&(t=this.charSizes[e]=this.$measureCharWidth(e)/this.$characterSize.width),t},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$observer&&this.$observer.disconnect(),this.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)},this.$getZoom=function e(t){return!t||!t.parentElement?1:(window.getComputedStyle(t).zoom||1)*e(t.parentElement)},this.$initTransformMeasureNodes=function(){var e=function(e,t){return["div",{style:"position: absolute;top:"+e+"px;left:"+t+"px;"}]};this.els=i.buildDom([e(0,0),e(c,0),e(0,c),e(c,c)],this.el)},this.transformCoordinates=function(e,t){function r(e,t,n){var r=e[1]*t[0]-e[0]*t[1];return[(-t[1]*n[0]+t[0]*n[1])/r,(+e[1]*n[0]-e[0]*n[1])/r]}function i(e,t){return[e[0]-t[0],e[1]-t[1]]}function s(e,t){return[e[0]+t[0],e[1]+t[1]]}function o(e,t){return[e*t[0],e*t[1]]}function u(e){var t=e.getBoundingClientRect();return[t.left,t.top]}if(e){var n=this.$getZoom(this.el);e=o(1/n,e)}this.els||this.$initTransformMeasureNodes();var a=u(this.els[0]),f=u(this.els[1]),l=u(this.els[2]),h=u(this.els[3]),p=r(i(h,f),i(h,l),i(s(f,l),s(h,a))),d=o(1+p[0],i(f,a)),v=o(1+p[1],i(l,a));if(t){var m=t,g=p[0]*m[0]/c+p[1]*m[1]/c+1,y=s(o(m[0],d),o(m[1],v));return s(o(1/g/c,y),a)}var b=i(e,a),w=r(i(d,o(p[0],b)),i(v,o(p[1],b)),b);return o(c,w)}}).call(h.prototype)}),define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/config","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./config"),o=e("./layer/gutter").Gutter,u=e("./layer/marker").Marker,a=e("./layer/text").Text,f=e("./layer/cursor").Cursor,l=e("./scrollbar").HScrollBar,c=e("./scrollbar").VScrollBar,h=e("./renderloop").RenderLoop,p=e("./layer/font_metrics").FontMetrics,d=e("./lib/event_emitter").EventEmitter,v='.ace_br1 {border-top-left-radius : 3px;}.ace_br2 {border-top-right-radius : 3px;}.ace_br3 {border-top-left-radius : 3px; border-top-right-radius: 3px;}.ace_br4 {border-bottom-right-radius: 3px;}.ace_br5 {border-top-left-radius : 3px; border-bottom-right-radius: 3px;}.ace_br6 {border-top-right-radius : 3px; border-bottom-right-radius: 3px;}.ace_br7 {border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px;}.ace_br8 {border-bottom-left-radius : 3px;}.ace_br9 {border-top-left-radius : 3px; border-bottom-left-radius: 3px;}.ace_br10{border-top-right-radius : 3px; border-bottom-left-radius: 3px;}.ace_br11{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br13{border-top-left-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br14{border-top-right-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br15{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_editor {position: relative;overflow: hidden;padding: 0;font: 12px/normal \'Monaco\', \'Menlo\', \'Ubuntu Mono\', \'Consolas\', \'source-code-pro\', monospace;direction: ltr;text-align: left;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}.ace_scroller {position: absolute;overflow: hidden;top: 0;bottom: 0;background-color: inherit;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;cursor: text;}.ace_content {position: absolute;box-sizing: border-box;min-width: 100%;contain: style size layout;font-variant-ligatures: no-common-ligatures;}.ace_dragging .ace_scroller:before{position: absolute;top: 0;left: 0;right: 0;bottom: 0;content: \'\';background: rgba(250, 250, 250, 0.01);z-index: 1000;}.ace_dragging.ace_dark .ace_scroller:before{background: rgba(0, 0, 0, 0.01);}.ace_selecting, .ace_selecting * {cursor: text !important;}.ace_gutter {position: absolute;overflow : hidden;width: auto;top: 0;bottom: 0;left: 0;cursor: default;z-index: 4;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;contain: style size layout;}.ace_gutter-active-line {position: absolute;left: 0;right: 0;}.ace_scroller.ace_scroll-left {box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;}.ace_gutter-cell {position: absolute;top: 0;left: 0;right: 0;padding-left: 19px;padding-right: 6px;background-repeat: no-repeat;}.ace_gutter-cell.ace_error {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: 2px center;}.ace_gutter-cell.ace_warning {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");background-position: 2px center;}.ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");background-position: 2px center;}.ace_dark .ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");}.ace_scrollbar {contain: strict;position: absolute;right: 0;bottom: 0;z-index: 6;}.ace_scrollbar-inner {position: absolute;cursor: text;left: 0;top: 0;}.ace_scrollbar-v{overflow-x: hidden;overflow-y: scroll;top: 0;}.ace_scrollbar-h {overflow-x: scroll;overflow-y: hidden;left: 0;}.ace_print-margin {position: absolute;height: 100%;}.ace_text-input {position: absolute;z-index: 0;width: 0.5em;height: 1em;opacity: 0;background: transparent;-moz-appearance: none;appearance: none;border: none;resize: none;outline: none;overflow: hidden;font: inherit;padding: 0 1px;margin: 0 -1px;contain: strict;-ms-user-select: text;-moz-user-select: text;-webkit-user-select: text;user-select: text;white-space: pre!important;}.ace_text-input.ace_composition {background: transparent;color: inherit;z-index: 1000;opacity: 1;}.ace_composition_placeholder { color: transparent }.ace_composition_marker { border-bottom: 1px solid;position: absolute;border-radius: 0;margin-top: 1px;}[ace_nocontext=true] {transform: none!important;filter: none!important;clip-path: none!important;mask : none!important;contain: none!important;perspective: none!important;mix-blend-mode: initial!important;z-index: auto;}.ace_layer {z-index: 1;position: absolute;overflow: hidden;word-wrap: normal;white-space: pre;height: 100%;width: 100%;box-sizing: border-box;pointer-events: none;}.ace_gutter-layer {position: relative;width: auto;text-align: right;pointer-events: auto;height: 1000000px;contain: style size layout;}.ace_text-layer {font: inherit !important;position: absolute;height: 1000000px;width: 1000000px;contain: style size layout;}.ace_text-layer > .ace_line, .ace_text-layer > .ace_line_group {contain: style size layout;position: absolute;top: 0;left: 0;right: 0;}.ace_hidpi .ace_text-layer,.ace_hidpi .ace_gutter-layer,.ace_hidpi .ace_content,.ace_hidpi .ace_gutter {contain: strict;will-change: transform;}.ace_hidpi .ace_text-layer > .ace_line, .ace_hidpi .ace_text-layer > .ace_line_group {contain: strict;}.ace_cjk {display: inline-block;text-align: center;}.ace_cursor-layer {z-index: 4;}.ace_cursor {z-index: 4;position: absolute;box-sizing: border-box;border-left: 2px solid;transform: translatez(0);}.ace_multiselect .ace_cursor {border-left-width: 1px;}.ace_slim-cursors .ace_cursor {border-left-width: 1px;}.ace_overwrite-cursors .ace_cursor {border-left-width: 0;border-bottom: 1px solid;}.ace_hidden-cursors .ace_cursor {opacity: 0.2;}.ace_hasPlaceholder .ace_hidden-cursors .ace_cursor {opacity: 0;}.ace_smooth-blinking .ace_cursor {transition: opacity 0.18s;}.ace_animate-blinking .ace_cursor {animation-duration: 1000ms;animation-timing-function: step-end;animation-name: blink-ace-animate;animation-iteration-count: infinite;}.ace_animate-blinking.ace_smooth-blinking .ace_cursor {animation-duration: 1000ms;animation-timing-function: ease-in-out;animation-name: blink-ace-animate-smooth;}@keyframes blink-ace-animate {from, to { opacity: 1; }60% { opacity: 0; }}@keyframes blink-ace-animate-smooth {from, to { opacity: 1; }45% { opacity: 1; }60% { opacity: 0; }85% { opacity: 0; }}.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {position: absolute;z-index: 3;}.ace_marker-layer .ace_selection {position: absolute;z-index: 5;}.ace_marker-layer .ace_bracket {position: absolute;z-index: 6;}.ace_marker-layer .ace_error_bracket {position: absolute;border-bottom: 1px solid #DE5555;border-radius: 0;}.ace_marker-layer .ace_active-line {position: absolute;z-index: 2;}.ace_marker-layer .ace_selected-word {position: absolute;z-index: 4;box-sizing: border-box;}.ace_line .ace_fold {box-sizing: border-box;display: inline-block;height: 11px;margin-top: -2px;vertical-align: middle;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");background-repeat: no-repeat, repeat-x;background-position: center center, top left;color: transparent;border: 1px solid black;border-radius: 2px;cursor: pointer;pointer-events: auto;}.ace_dark .ace_fold {}.ace_fold:hover{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");}.ace_tooltip {background-color: #FFF;background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));border: 1px solid gray;border-radius: 1px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);color: black;max-width: 100%;padding: 3px 4px;position: fixed;z-index: 999999;box-sizing: border-box;cursor: default;white-space: pre;word-wrap: break-word;line-height: normal;font-style: normal;font-weight: normal;letter-spacing: normal;pointer-events: none;}.ace_folding-enabled > .ace_gutter-cell {padding-right: 13px;}.ace_fold-widget {box-sizing: border-box;margin: 0 -12px 0 1px;display: none;width: 11px;vertical-align: top;background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: center;border-radius: 3px;border: 1px solid transparent;cursor: pointer;}.ace_folding-enabled .ace_fold-widget {display: inline-block; }.ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");}.ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");}.ace_fold-widget:hover {border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(255, 255, 255, 0.2);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);}.ace_fold-widget:active {border: 1px solid rgba(0, 0, 0, 0.4);background-color: rgba(0, 0, 0, 0.05);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);}.ace_dark .ace_fold-widget {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC");}.ace_dark .ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget:hover {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);background-color: rgba(255, 255, 255, 0.1);}.ace_dark .ace_fold-widget:active {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);}.ace_inline_button {border: 1px solid lightgray;display: inline-block;margin: -1px 8px;padding: 0 5px;pointer-events: auto;cursor: pointer;}.ace_inline_button:hover {border-color: gray;background: rgba(200,200,200,0.2);display: inline-block;pointer-events: auto;}.ace_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {transition: opacity 0.05s ease 0.05s;opacity:1;}.ace_underline {text-decoration: underline;}.ace_bold {font-weight: bold;}.ace_nobold .ace_bold {font-weight: normal;}.ace_italic {font-style: italic;}.ace_error-marker {background-color: rgba(255, 0, 0,0.2);position: absolute;z-index: 9;}.ace_highlight-marker {background-color: rgba(255, 255, 0,0.2);position: absolute;z-index: 8;}.ace_mobile-menu {position: absolute;line-height: 1.5;border-radius: 4px;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;background: white;box-shadow: 1px 3px 2px grey;border: 1px solid #dcdcdc;color: black;}.ace_dark > .ace_mobile-menu {background: #333;color: #ccc;box-shadow: 1px 3px 2px grey;border: 1px solid #444;}.ace_mobile-button {padding: 2px;cursor: pointer;overflow: hidden;}.ace_mobile-button:hover {background-color: #eee;opacity:1;}.ace_mobile-button:active {background-color: #ddd;}.ace_placeholder {font-family: arial;transform: scale(0.9);transform-origin: left;white-space: pre;opacity: 0.7;margin: 0 10px;}',m=e("./lib/useragent"),g=m.isIE;i.importCssString(v,"ace_editor.css");var y=function(e,t){var n=this;this.container=e||i.createElement("div"),i.addCssClass(this.container,"ace_editor"),i.HI_DPI&&i.addCssClass(this.container,"ace_hidpi"),this.setTheme(t),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.$gutter.setAttribute("aria-hidden",!0),this.scroller=i.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=i.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new o(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new u(this.content);var r=this.$textLayer=new a(this.content);this.canvas=r.element,this.$markerFront=new u(this.content),this.$cursorLayer=new f(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new c(this.container,this),this.scrollBarH=new l(this.container,this),this.scrollBarV.on("scroll",function(e){n.$scrollAnimation||n.session.setScrollTop(e.data-n.scrollMargin.top)}),this.scrollBarH.on("scroll",function(e){n.$scrollAnimation||n.session.setScrollLeft(e.data-n.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new p(this.container),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.on("changeCharacterSize",function(e){n.updateCharacterSize(),n.onResize(!0,n.gutterWidth,n.$size.width,n.$size.height),n._signal("changeCharacterSize",e)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0,$dirty:!0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1,gutterOffset:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.margin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$keepTextAreaAtCursor=!m.isIOS,this.$loop=new h(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),s.resetOptions(this),s._signal("renderer",this)};(function(){this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,r.implement(this,d),this.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin(),i.setStyle(this.scroller.style,"line-height",this.lineHeight+"px")},this.setSession=function(e){this.session&&this.session.doc.off("changeNewLineMode",this.onChangeNewLineMode),this.session=e,e&&this.scrollMargin.top&&e.getScrollTop()<=0&&e.setScrollTop(-this.scrollMargin.top),this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e);if(!e)return;this.$loop.schedule(this.CHANGE_FULL),this.session.$setFontMetrics(this.$fontMetrics),this.scrollBarH.scrollLeft=this.scrollBarV.scrollTop=null,this.onChangeNewLineMode=this.onChangeNewLineMode.bind(this),this.onChangeNewLineMode(),this.session.doc.on("changeNewLineMode",this.onChangeNewLineMode)},this.updateLines=function(e,t,n){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRowthis.layerConfig.lastRow)return;this.$loop.schedule(this.CHANGE_LINES)},this.onChangeNewLineMode=function(){this.$loop.schedule(this.CHANGE_TEXT),this.$textLayer.$updateEolChar(),this.session.$bidiHandler.setEolChar(this.$textLayer.EOL_CHAR)},this.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.$changes=0,this.$updateSizeAsync=function(){this.$loop.pending?this.$size.$dirty=!0:this.onResize()},this.onResize=function(e,t,n,r){if(this.resizing>2)return;this.resizing>0?this.resizing++:this.resizing=e?1:0;var i=this.container;r||(r=i.clientHeight||i.scrollHeight),n||(n=i.clientWidth||i.scrollWidth);var s=this.$updateCachedSize(e,t,n,r);if(!this.$size.scrollerHeight||!n&&!r)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(s|this.$changes,!0):this.$loop.schedule(s|this.$changes),this.resizing&&(this.resizing=0),this.scrollBarV.scrollLeft=this.scrollBarV.scrollTop=null},this.$updateCachedSize=function(e,t,n,r){r-=this.$extraHeight||0;var s=0,o=this.$size,u={width:o.width,height:o.height,scrollerHeight:o.scrollerHeight,scrollerWidth:o.scrollerWidth};r&&(e||o.height!=r)&&(o.height=r,s|=this.CHANGE_SIZE,o.scrollerHeight=o.height,this.$horizScroll&&(o.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",s|=this.CHANGE_SCROLL);if(n&&(e||o.width!=n)){s|=this.CHANGE_SIZE,o.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,i.setStyle(this.scrollBarH.element.style,"left",t+"px"),i.setStyle(this.scroller.style,"left",t+this.margin.left+"px"),o.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()-this.margin.h),i.setStyle(this.$gutter.style,"left",this.margin.left+"px");var a=this.scrollBarV.getWidth()+"px";i.setStyle(this.scrollBarH.element.style,"right",a),i.setStyle(this.scroller.style,"right",a),i.setStyle(this.scroller.style,"bottom",this.scrollBarH.getHeight());if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)s|=this.CHANGE_FULL}return o.$dirty=!n||!r,s&&this._signal("resize",u),s},this.onGutterResize=function(e){var t=this.$showGutter?e:0;t!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,t,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):this.$size.$dirty?this.$loop.schedule(this.CHANGE_FULL):this.$computeLayerConfig()},this.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},this.setAnimatedScroll=function(e){this.setOption("animatedScroll",e)},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(e){this.setOption("showInvisibles",e),this.session.$bidiHandler.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.getOption("showInvisibles")},this.getDisplayIndentGuides=function(){return this.getOption("displayIndentGuides")},this.setDisplayIndentGuides=function(e){this.setOption("displayIndentGuides",e)},this.setShowPrintMargin=function(e){this.setOption("showPrintMargin",e)},this.getShowPrintMargin=function(){return this.getOption("showPrintMargin")},this.setPrintMarginColumn=function(e){this.setOption("printMarginColumn",e)},this.getPrintMarginColumn=function(){return this.getOption("printMarginColumn")},this.getShowGutter=function(){return this.getOption("showGutter")},this.setShowGutter=function(e){return this.setOption("showGutter",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=i.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=Math.round(this.characterWidth*this.$printMarginColumn+this.$padding)+"px",t.visibility=this.$showPrintMargin?"visible":"hidden",this.session&&this.session.$wrap==-1&&this.adjustWrapLimit()},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.scroller},this.getTextAreaContainer=function(){return this.container},this.$moveTextAreaToCursor=function(){if(this.$isMousePressed)return;var e=this.textarea.style,t=this.$composition;if(!this.$keepTextAreaAtCursor&&!t){i.translate(this.textarea,-100,0);return}var n=this.$cursorLayer.$pixelPos;if(!n)return;t&&t.markerRange&&(n=this.$cursorLayer.getPixelPosition(t.markerRange.start,!0));var r=this.layerConfig,s=n.top,o=n.left;s-=r.offset;var u=t&&t.useTextareaForIME?this.lineHeight:g?0:1;if(s<0||s>r.height-u){i.translate(this.textarea,0,0);return}var a=1,f=this.$size.height-u;if(!t)s+=this.lineHeight;else if(t.useTextareaForIME){var l=this.textarea.value;a=this.characterWidth*this.session.$getStringScreenWidth(l)[0]}else s+=this.lineHeight+2;o-=this.scrollLeft,o>this.$size.scrollerWidth-a&&(o=this.$size.scrollerWidth-a),o+=this.gutterWidth+this.margin.left,i.setStyle(e,"height",u+"px"),i.setStyle(e,"width",a+"px"),i.translate(this.textarea,Math.min(o,this.$size.scrollerWidth-a),Math.min(s,f))},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var e=this.layerConfig,t=e.lastRow,n=this.session.documentToScreenRow(t,0)*e.lineHeight;return n-this.session.getScrollTop()>e.height-e.lineHeight?t-1:t},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.setScrollMargin=function(e,t,n,r){var i=this.scrollMargin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,i.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-i.top),this.updateFull()},this.setMargin=function(e,t,n,r){var i=this.margin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,this.$updateCachedSize(!0,this.gutterWidth,this.$size.width,this.$size.height),this.updateFull()},this.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setHScrollBarAlwaysVisible=function(e){this.setOption("hScrollBarAlwaysVisible",e)},this.getVScrollBarAlwaysVisible=function(){return this.$vScrollBarAlwaysVisible},this.setVScrollBarAlwaysVisible=function(e){this.setOption("vScrollBarAlwaysVisible",e)},this.$updateScrollBarV=function(){var e=this.layerConfig.maxHeight,t=this.$size.scrollerHeight;!this.$maxLines&&this.$scrollPastEnd&&(e-=(t-this.lineHeight)*this.$scrollPastEnd,this.scrollTop>e-t&&(e=this.scrollTop+t,this.scrollBarV.scrollTop=null)),this.scrollBarV.setScrollHeight(e+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},this.$updateScrollBarH=function(){this.scrollBarH.setScrollWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},this.$frozen=!1,this.freeze=function(){this.$frozen=!0},this.unfreeze=function(){this.$frozen=!1},this.$renderChanges=function(e,t){this.$changes&&(e|=this.$changes,this.$changes=0);if(!this.session||!this.container.offsetWidth||this.$frozen||!e&&!t){this.$changes|=e;return}if(this.$size.$dirty)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal("beforeRender",e),this.session&&this.session.$bidiHandler&&this.session.$bidiHandler.updateCharacterWidths(this.$fontMetrics);var n=this.layerConfig;if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL){e|=this.$computeLayerConfig()|this.$loop.clear();if(n.firstRow!=this.layerConfig.firstRow&&n.firstRowScreen==this.layerConfig.firstRowScreen){var r=this.scrollTop+(n.firstRow-this.layerConfig.firstRow)*this.lineHeight;r>0&&(this.scrollTop=r,e|=this.CHANGE_SCROLL,e|=this.$computeLayerConfig()|this.$loop.clear())}n=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),i.translate(this.content,-this.scrollLeft,-n.offset);var s=n.width+2*this.$padding+"px",o=n.minHeight+"px";i.setStyle(this.content.style,"width",s),i.setStyle(this.content.style,"height",o)}e&this.CHANGE_H_SCROLL&&(i.translate(this.content,-this.scrollLeft,-n.offset),this.scroller.className=this.scrollLeft<=0?"ace_scroller":"ace_scroller ace_scroll-left");if(e&this.CHANGE_FULL){this.$changedLines=null,this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal("afterRender",e);return}if(e&this.CHANGE_SCROLL){this.$changedLines=null,e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(n):this.$textLayer.scrollLines(n),this.$showGutter&&(e&this.CHANGE_GUTTER||e&this.CHANGE_LINES?this.$gutterLayer.update(n):this.$gutterLayer.scrollLines(n)),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal("afterRender",e);return}e&this.CHANGE_TEXT?(this.$changedLines=null,this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n)):e&this.CHANGE_LINES?(this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(n):e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER?this.$showGutter&&this.$gutterLayer.update(n):e&this.CHANGE_CURSOR&&this.$highlightGutterLine&&this.$gutterLayer.updateLineHighlight(n),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(n),this.$moveTextAreaToCursor()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(n),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(n),this._signal("afterRender",e)},this.$autosize=function(){var e=this.session.getScreenLength()*this.lineHeight,t=this.$maxLines*this.lineHeight,n=Math.min(t,Math.max((this.$minLines||1)*this.lineHeight,e))+this.scrollMargin.v+(this.$extraHeight||0);this.$horizScroll&&(n+=this.scrollBarH.getHeight()),this.$maxPixelHeight&&n>this.$maxPixelHeight&&(n=this.$maxPixelHeight);var r=n<=2*this.lineHeight,i=!r&&e>t;if(n!=this.desiredHeight||this.$size.height!=this.desiredHeight||i!=this.$vScroll){i!=this.$vScroll&&(this.$vScroll=i,this.scrollBarV.setVisible(i));var s=this.container.clientWidth;this.container.style.height=n+"px",this.$updateCachedSize(!0,this.$gutterWidth,s,n),this.desiredHeight=n,this._signal("autosize")}},this.$computeLayerConfig=function(){var e=this.session,t=this.$size,n=t.height<=2*this.lineHeight,r=this.session.getScreenLength(),i=r*this.lineHeight,s=this.$getLongestLine(),o=!n&&(this.$hScrollBarAlwaysVisible||t.scrollerWidth-s-2*this.$padding<0),u=this.$horizScroll!==o;u&&(this.$horizScroll=o,this.scrollBarH.setVisible(o));var a=this.$vScroll;this.$maxLines&&this.lineHeight>1&&this.$autosize();var f=t.scrollerHeight+this.lineHeight,l=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;i+=l;var c=this.scrollMargin;this.session.setScrollTop(Math.max(-c.top,Math.min(this.scrollTop,i-t.scrollerHeight+c.bottom))),this.session.setScrollLeft(Math.max(-c.left,Math.min(this.scrollLeft,s+2*this.$padding-t.scrollerWidth+c.right)));var h=!n&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-i+l<0||this.scrollTop>c.top),p=a!==h;p&&(this.$vScroll=h,this.scrollBarV.setVisible(h));var d=this.scrollTop%this.lineHeight,v=Math.ceil(f/this.lineHeight)-1,m=Math.max(0,Math.round((this.scrollTop-d)/this.lineHeight)),g=m+v,y,b,w=this.lineHeight;m=e.screenToDocumentRow(m,0);var E=e.getFoldLine(m);E&&(m=E.start.row),y=e.documentToScreenRow(m,0),b=e.getRowLength(m)*w,g=Math.min(e.screenToDocumentRow(g,0),e.getLength()-1),f=t.scrollerHeight+e.getRowLength(g)*w+b,d=this.scrollTop-y*w;var S=0;if(this.layerConfig.width!=s||u)S=this.CHANGE_H_SCROLL;if(u||p)S|=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),p&&(s=this.$getLongestLine());return this.layerConfig={width:s,padding:this.$padding,firstRow:m,firstRowScreen:y,lastRow:g,lineHeight:w,characterWidth:this.characterWidth,minHeight:f,maxHeight:i,offset:d,gutterOffset:w?Math.max(0,Math.ceil((d+t.height-t.scrollerHeight)/w)):0,height:this.$size.scrollerHeight},this.session.$bidiHandler&&this.session.$bidiHandler.setContentWidth(s-this.$padding),S},this.$updateLines=function(){if(!this.$changedLines)return;var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(tthis.$textLayer.MAX_LINE_LENGTH&&(e=this.$textLayer.MAX_LINE_LENGTH+30),Math.max(this.$size.scrollerWidth-2*this.$padding,Math.round(e*this.characterWidth))},this.updateFrontMarkers=function(){this.$markerFront.setMarkers(this.session.getMarkers(!0)),this.$loop.schedule(this.CHANGE_MARKER_FRONT)},this.updateBackMarkers=function(){this.$markerBack.setMarkers(this.session.getMarkers()),this.$loop.schedule(this.CHANGE_MARKER_BACK)},this.addGutterDecoration=function(e,t){this.$gutterLayer.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){this.$gutterLayer.removeGutterDecoration(e,t)},this.updateBreakpoints=function(e){this.$loop.schedule(this.CHANGE_GUTTER)},this.setAnnotations=function(e){this.$gutterLayer.setAnnotations(e),this.$loop.schedule(this.CHANGE_GUTTER)},this.updateCursor=function(){this.$loop.schedule(this.CHANGE_CURSOR)},this.hideCursor=function(){this.$cursorLayer.hideCursor()},this.showCursor=function(){this.$cursorLayer.showCursor()},this.scrollSelectionIntoView=function(e,t,n){this.scrollCursorIntoView(e,n),this.scrollCursorIntoView(t,n)},this.scrollCursorIntoView=function(e,t,n){if(this.$size.scrollerHeight===0)return;var r=this.$cursorLayer.getPixelPosition(e),i=r.left,s=r.top,o=n&&n.top||0,u=n&&n.bottom||0,a=this.$scrollAnimation?this.session.getScrollTop():this.scrollTop;a+o>s?(t&&a+o>s+this.lineHeight&&(s-=t*this.$size.scrollerHeight),s===0&&(s=-this.scrollMargin.top),this.session.setScrollTop(s)):a+this.$size.scrollerHeight-ui?(i=1-this.scrollMargin.top)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight<-1+this.scrollMargin.bottom)return!0;if(e<0&&this.session.getScrollLeft()>=1-this.scrollMargin.left)return!0;if(e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right)return!0},this.pixelToScreenCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),u=this.$blockCursor?Math.floor(s):Math.round(s);return{row:o,column:u,side:s-u>0?1:-1,offsetX:i}},this.screenToTextCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=this.$blockCursor?Math.floor(s):Math.round(s),u=Math.floor((t+this.scrollTop-n.top)/this.lineHeight);return this.session.screenToDocumentPosition(u,Math.max(o,0),i)},this.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+(this.session.$bidiHandler.isBidiRow(r.row,e)?this.session.$bidiHandler.getPosLeft(r.column):Math.round(r.column*this.characterWidth)),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},this.visualizeFocus=function(){i.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){i.removeCssClass(this.container,"ace_focus")},this.showComposition=function(e){this.$composition=e,e.cssText||(e.cssText=this.textarea.style.cssText),e.useTextareaForIME==undefined&&(e.useTextareaForIME=this.$useTextareaForIME),this.$useTextareaForIME?(i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor(),this.$cursorLayer.element.style.display="none"):e.markerId=this.session.addMarker(e.markerRange,"ace_composition_marker","text")},this.setCompositionText=function(e){var t=this.session.selection.cursor;this.addToken(e,"composition_placeholder",t.row,t.column),this.$moveTextAreaToCursor()},this.hideComposition=function(){if(!this.$composition)return;this.$composition.markerId&&this.session.removeMarker(this.$composition.markerId),i.removeCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText=this.$composition.cssText;var e=this.session.selection.cursor;this.removeExtraToken(e.row,e.column),this.$composition=null,this.$cursorLayer.element.style.display=""},this.addToken=function(e,t,n,r){var i=this.session;i.bgTokenizer.lines[n]=null;var s={type:t,value:e},o=i.getTokens(n);if(r==null)o.push(s);else{var u=0;for(var a=0;a50&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e})}}).call(f.prototype);var l=function(e,t,n){var r=null,i=!1,u=Object.create(s),a=[],l=new f({messageBuffer:a,terminate:function(){},postMessage:function(e){a.push(e);if(!r)return;i?setTimeout(c):c()}});l.setEmitSync=function(e){i=e};var c=function(){var e=a.shift();e.command?r[e.command].apply(r,e.args):e.event&&u._signal(e.event,e.data)};return u.postMessage=function(e){l.onMessage({data:e})},u.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},u.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},o.loadModule(["worker",t],function(e){r=new e[n](u);while(a.length)c()}),l};t.UIWorkerClient=l,t.WorkerClient=f,t.createWorker=a}),define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./range").Range,i=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop"),o=function(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)};(function(){s.implement(this,i),this.setup=function(){var e=this,t=this.doc,n=this.session;this.selectionBefore=n.selection.toJSON(),n.selection.inMultiSelectMode&&n.selection.toSingleRange(),this.pos=t.createAnchor(this.$pos.row,this.$pos.column);var i=this.pos;i.$insertRight=!0,i.detach(),i.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);r.$insertRight=!0,r.detach(),e.others.push(r)}),n.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1)})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e=this.pos.column&&t.start.column<=this.pos.column+this.length+1,s=t.start.column-this.pos.column;this.updateAnchors(e),i&&(this.length+=n);if(i&&!this.session.$fromUndo)if(e.action==="insert")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.insertMergedLines(a,e.lines)}else if(e.action==="remove")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.remove(new r(a.row,a.column,a.row,a.column-n))}this.$updating=!1,this.updateMarkers()},this.updateAnchors=function(e){this.pos.onChange(e);for(var t=this.others.length;t--;)this.others[t].onChange(e);this.updateMarkers()},this.updateMarkers=function(){if(this.$updating)return;var e=this,t=this.session,n=function(n,i){t.removeMarker(n.markerId),n.markerId=t.addMarker(new r(n.row,n.column,n.row,n.column+e.length),i,null,!1)};n(this.pos,this.mainClass);for(var i=this.others.length;i--;)n(this.others[i],this.othersClass)},this.onCursorChange=function(e){if(this.$updating||!this.session)return;var t=this.session.selection.getCursor();t.row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))},this.detach=function(){this.session.removeMarker(this.pos&&this.pos.markerId),this.hideOtherMarkers(),this.doc.off("change",this.$onUpdate),this.session.selection.off("changeCursor",this.$onCursorChange),this.session.setUndoSelect(!0),this.session=null},this.cancel=function(){if(this.$undoStackDepth===-1)return;var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth;for(var n=0;n1?e.multiSelect.joinSelections():e.multiSelect.splitIntoLines()},bindKey:{win:"Ctrl-Alt-L",mac:"Ctrl-Alt-L"},readOnly:!0},{name:"splitSelectionIntoLines",description:"Split into lines",exec:function(e){e.multiSelect.splitIntoLines()},readOnly:!0},{name:"alignCursors",description:"Align cursors",exec:function(e){e.alignCursors()},bindKey:{win:"Ctrl-Alt-A",mac:"Ctrl-Alt-A"},scrollIntoView:"cursor"},{name:"findAll",description:"Find all",exec:function(e){e.findAll()},bindKey:{win:"Ctrl-Alt-K",mac:"Ctrl-Alt-G"},scrollIntoView:"cursor",readOnly:!0}],t.multiSelectCommands=[{name:"singleSelection",description:"Single selection",bindKey:"esc",exec:function(e){e.exitMultiSelectMode()},scrollIntoView:"cursor",readOnly:!0,isAvailable:function(e){return e&&e.inMultiSelectMode}}];var r=e("../keyboard/hash_handler").HashHandler;t.keyboardHandler=new r(t.multiSelectCommands)}),define("ace/multi_select",["require","exports","module","ace/range_list","ace/range","ace/selection","ace/mouse/multi_select_handler","ace/lib/event","ace/lib/lang","ace/commands/multi_select_commands","ace/search","ace/edit_session","ace/editor","ace/config"],function(e,t,n){function h(e,t,n){return c.$options.wrap=!0,c.$options.needle=t,c.$options.backwards=n==-1,c.find(e)}function v(e,t){return e.row==t.row&&e.column==t.column}function m(e){if(e.$multiselectOnSessionChange)return;e.$onAddRange=e.$onAddRange.bind(e),e.$onRemoveRange=e.$onRemoveRange.bind(e),e.$onMultiSelect=e.$onMultiSelect.bind(e),e.$onSingleSelect=e.$onSingleSelect.bind(e),e.$multiselectOnSessionChange=t.onSessionChange.bind(e),e.$checkMultiselectChange=e.$checkMultiselectChange.bind(e),e.$multiselectOnSessionChange(e),e.on("changeSession",e.$multiselectOnSessionChange),e.on("mousedown",o),e.commands.addCommands(f.defaultCommands),g(e)}function g(e){function r(t){n&&(e.renderer.setMouseCursor(""),n=!1)}if(!e.textInput)return;var t=e.textInput.getElement(),n=!1;u.addListener(t,"keydown",function(t){var i=t.keyCode==18&&!(t.ctrlKey||t.shiftKey||t.metaKey);e.$blockSelectEnabled&&i?n||(e.renderer.setMouseCursor("crosshair"),n=!0):n&&r()},e),u.addListener(t,"keyup",r,e),u.addListener(t,"blur",r,e)}var r=e("./range_list").RangeList,i=e("./range").Range,s=e("./selection").Selection,o=e("./mouse/multi_select_handler").onMouseDown,u=e("./lib/event"),a=e("./lib/lang"),f=e("./commands/multi_select_commands");t.commands=f.defaultCommands.concat(f.multiSelectCommands);var l=e("./search").Search,c=new l,p=e("./edit_session").EditSession;(function(){this.getSelectionMarkers=function(){return this.$selectionMarkers}}).call(p.prototype),function(){this.ranges=null,this.rangeList=null,this.addRange=function(e,t){if(!e)return;if(!this.inMultiSelectMode&&this.rangeCount===0){var n=this.toOrientedRange();this.rangeList.add(n),this.rangeList.add(e);if(this.rangeList.ranges.length!=2)return this.rangeList.removeAll(),t||this.fromOrientedRange(e);this.rangeList.removeAll(),this.rangeList.add(n),this.$onAddRange(n)}e.cursor||(e.cursor=e.end);var r=this.rangeList.add(e);return this.$onAddRange(e),r.length&&this.$onRemoveRange(r),this.rangeCount>1&&!this.inMultiSelectMode&&(this._signal("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length&&this.$onRemoveRange(e)},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal("addRange",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._signal("removeRange",{ranges:e}),this.rangeCount===0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){var e=this.ranges.length?this.ranges:[this.getRange()],t=[];for(var n=0;n1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.cursor),s=this.session.documentToScreenPosition(this.anchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column0)g--;if(g>0){var y=0;while(r[y].isEmpty())y++}for(var b=g;b>=y;b--)r[b].isEmpty()&&r.splice(b,1)}return r}}.call(s.prototype);var d=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit("changeSelection")},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;if(!t.multiSelectAction){var r=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}else t.multiSelectAction=="forEach"?r=n.forEachSelection(t,e.args):t.multiSelectAction=="forEachLine"?r=n.forEachSelection(t,e.args,!0):t.multiSelectAction=="single"?(n.exitMultiSelectMode(),r=t.exec(n,e.args||{})):r=t.multiSelectAction(n,e.args||{});return r},this.forEachSelection=function(e,t,n){if(this.inVirtualSelectionMode)return;var r=n&&n.keepOrder,i=n==1||n&&n.$byLines,o=this.session,u=this.selection,a=u.rangeList,f=(r?u:a).ranges,l;if(!f.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var c=u._eventRegistry;u._eventRegistry={};var h=new s(o);this.inVirtualSelectionMode=!0;for(var p=f.length;p--;){if(i)while(p>0&&f[p].start.row==f[p-1].end.row)p--;h.fromOrientedRange(f[p]),h.index=p,this.selection=o.selection=h;var d=e.exec?e.exec(this,t||{}):e(this,t||{});!l&&d!==undefined&&(l=d),h.toOrientedRange(f[p])}h.detach(),this.selection=o.selection=u,this.inVirtualSelectionMode=!1,u._eventRegistry=c,u.mergeOverlappingRanges(),u.ranges[0]&&u.fromOrientedRange(u.ranges[0]);var v=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),v&&v.from==v.to&&this.renderer.animateScrolling(v.from),l},this.exitMultiSelectMode=function(){if(!this.inMultiSelectMode||this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var t=this.multiSelect.rangeList.ranges,n=[];for(var r=0;r0);u<0&&(u=0),f>=c&&(f=c-1)}var p=this.session.removeFullLines(u,f);p=this.$reAlignText(p,l),this.session.insert({row:u,column:0},p.join("\n")+"\n"),l||(o.start.column=0,o.end.column=p[p.length-1].length),this.selection.setRange(o)}else{s.forEach(function(e){t.substractPoint(e.cursor)});var d=0,v=Infinity,m=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\S/g);return i==-1&&(i=0),n.column>d&&(d=n.column),io?e.insert(r,a.stringRepeat(" ",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=d,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e,t){function u(e){return a.stringRepeat(" ",e)}function f(e){return e[2]?u(i)+e[2]+u(s-e[2].length+o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function l(e){return e[2]?u(i+s-e[2].length)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function c(e){return e[2]?u(i)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var n=!0,r=!0,i,s,o;return e.map(function(e){var t=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return t?i==null?(i=t[1].length,s=t[2].length,o=t[3].length,t):(i+s+o!=t[1].length+t[2].length+t[3].length&&(r=!1),i!=t[1].length&&(n=!1),i>t[1].length&&(i=t[1].length),st[3].length&&(o=t[3].length),t):[e]}).map(t?f:n?r?l:f:c)}}).call(d.prototype),t.onSessionChange=function(e){var t=e.session;t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect;var n=e.oldSession;n&&(n.multiSelect.off("addRange",this.$onAddRange),n.multiSelect.off("removeRange",this.$onRemoveRange),n.multiSelect.off("multiSelect",this.$onMultiSelect),n.multiSelect.off("singleSelect",this.$onSingleSelect),n.multiSelect.lead.off("change",this.$checkMultiselectChange),n.multiSelect.anchor.off("change",this.$checkMultiselectChange)),t&&(t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),t.multiSelect.lead.on("change",this.$checkMultiselectChange),t.multiSelect.anchor.on("change",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=m,e("./config").defineOptions(d.prototype,"editor",{enableMultiselect:{set:function(e){m(this),e?(this.on("changeSession",this.$multiselectOnSessionChange),this.on("mousedown",o)):(this.off("changeSession",this.$multiselectOnSessionChange),this.off("mousedown",o))},value:!0},enableBlockSelect:{set:function(e){this.$blockSelectEnabled=e},value:!0}})}),define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../../range").Range,i=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?"start":t=="markbeginend"&&this.foldingStopMarker&&this.foldingStopMarker.test(r)?"end":""},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var i=/\S/,s=e.getLine(t),o=s.search(i);if(o==-1)return;var u=n||s.length,a=e.getLength(),f=t,l=t;while(++tf){var p=e.getLine(l).length;return new r(f,u,l,p)}},this.openingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i+1},u=e.$findClosingBracket(t,o,s);if(!u)return;var a=e.foldWidgets[u.row];return a==null&&(a=e.getFoldWidget(u.row)),a=="start"&&u.row>o.row&&(u.row--,u.column=e.getLine(u.row).length),r.fromPoints(o,u)},this.closingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i},u=e.$findOpeningBracket(t,o);if(!u)return;return u.column++,o.column--,r.fromPoints(u,o)}}).call(i.prototype)}),define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;color: black;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}',t.$id="ace/theme/textmate";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}),define("ace/line_widgets",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";function i(e){this.session=e,this.session.widgetManager=this,this.session.getRowLength=this.getRowLength,this.session.$getWidgetScreenLength=this.$getWidgetScreenLength,this.updateOnChange=this.updateOnChange.bind(this),this.renderWidgets=this.renderWidgets.bind(this),this.measureWidgets=this.measureWidgets.bind(this),this.session._changedWidgets=[],this.$onChangeEditor=this.$onChangeEditor.bind(this),this.session.on("change",this.updateOnChange),this.session.on("changeFold",this.updateOnFold),this.session.on("changeEditor",this.$onChangeEditor)}var r=e("./lib/dom");(function(){this.getRowLength=function(e){var t;return this.lineWidgets?t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0:t=0,!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.$getWidgetScreenLength=function(){var e=0;return this.lineWidgets.forEach(function(t){t&&t.rowCount&&!t.hidden&&(e+=t.rowCount)}),e},this.$onChangeEditor=function(e){this.attach(e.editor)},this.attach=function(e){e&&e.widgetManager&&e.widgetManager!=this&&e.widgetManager.detach();if(this.editor==e)return;this.detach(),this.editor=e,e&&(e.widgetManager=this,e.renderer.on("beforeRender",this.measureWidgets),e.renderer.on("afterRender",this.renderWidgets))},this.detach=function(e){var t=this.editor;if(!t)return;this.editor=null,t.widgetManager=null,t.renderer.off("beforeRender",this.measureWidgets),t.renderer.off("afterRender",this.renderWidgets);var n=this.session.lineWidgets;n&&n.forEach(function(e){e&&e.el&&e.el.parentNode&&(e._inDocument=!1,e.el.parentNode.removeChild(e.el))})},this.updateOnFold=function(e,t){var n=t.lineWidgets;if(!n||!e.action)return;var r=e.data,i=r.start.row,s=r.end.row,o=e.action=="add";for(var u=i+1;ut[n].column&&n++,s.unshift(n,0),t.splice.apply(t,s),this.$updateRows()}},this.$updateRows=function(){var e=this.session.lineWidgets;if(!e)return;var t=!0;e.forEach(function(e,n){if(e){t=!1,e.row=n;while(e.$oldWidget)e.$oldWidget.row=n,e=e.$oldWidget}}),t&&(this.session.lineWidgets=null)},this.$registerLineWidget=function(e){this.session.lineWidgets||(this.session.lineWidgets=new Array(this.session.getLength()));var t=this.session.lineWidgets[e.row];return t&&(e.$oldWidget=t,t.el&&t.el.parentNode&&(t.el.parentNode.removeChild(t.el),t._inDocument=!1)),this.session.lineWidgets[e.row]=e,e},this.addLineWidget=function(e){this.$registerLineWidget(e),e.session=this.session;if(!this.editor)return e;var t=this.editor.renderer;e.html&&!e.el&&(e.el=r.createElement("div"),e.el.innerHTML=e.html),e.el&&(r.addCssClass(e.el,"ace_lineWidgetContainer"),e.el.style.position="absolute",e.el.style.zIndex=5,t.container.appendChild(e.el),e._inDocument=!0,e.coverGutter||(e.el.style.zIndex=3),e.pixelHeight==null&&(e.pixelHeight=e.el.offsetHeight)),e.rowCount==null&&(e.rowCount=e.pixelHeight/t.layerConfig.lineHeight);var n=this.session.getFoldAt(e.row,0);e.$fold=n;if(n){var i=this.session.lineWidgets;e.row==n.end.row&&!i[n.start.row]?i[n.start.row]=e:e.hidden=!0}return this.session._emit("changeFold",{data:{start:{row:e.row}}}),this.$updateRows(),this.renderWidgets(null,t),this.onWidgetChanged(e),e},this.removeLineWidget=function(e){e._inDocument=!1,e.session=null,e.el&&e.el.parentNode&&e.el.parentNode.removeChild(e.el);if(e.editor&&e.editor.destroy)try{e.editor.destroy()}catch(t){}if(this.session.lineWidgets){var n=this.session.lineWidgets[e.row];if(n==e)this.session.lineWidgets[e.row]=e.$oldWidget,e.$oldWidget&&this.onWidgetChanged(e.$oldWidget);else while(n){if(n.$oldWidget==e){n.$oldWidget=e.$oldWidget;break}n=n.$oldWidget}}this.session._emit("changeFold",{data:{start:{row:e.row}}}),this.$updateRows()},this.getWidgetsAtRow=function(e){var t=this.session.lineWidgets,n=t&&t[e],r=[];while(n)r.push(n),n=n.$oldWidget;return r},this.onWidgetChanged=function(e){this.session._changedWidgets.push(e),this.editor&&this.editor.renderer.updateFull()},this.measureWidgets=function(e,t){var n=this.session._changedWidgets,r=t.layerConfig;if(!n||!n.length)return;var i=Infinity;for(var s=0;s0&&!r[i])i--;this.firstRow=n.firstRow,this.lastRow=n.lastRow,t.$cursorLayer.config=n;for(var o=i;o<=s;o++){var u=r[o];if(!u||!u.el)continue;if(u.hidden){u.el.style.top=-100-(u.pixelHeight||0)+"px";continue}u._inDocument||(u._inDocument=!0,t.container.appendChild(u.el));var a=t.$cursorLayer.getPixelPosition({row:o,column:0},!0).top;u.coverLine||(a+=n.lineHeight*this.session.getRowLineCount(u.row)),u.el.style.top=a-n.offset+"px";var f=u.coverGutter?0:t.gutterWidth;u.fixedWidth||(f-=t.scrollLeft),u.el.style.left=f+"px",u.fullWidth&&u.screenWidth&&(u.el.style.minWidth=n.width+2*n.padding+"px"),u.fixedWidth?u.el.style.right=t.scrollBar.getWidth()+"px":u.el.style.right=""}}}).call(i.prototype),t.LineWidgets=i}),define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e,t,n){var r=0,i=e.length-1;while(r<=i){var s=r+i>>1,o=n(t,e[s]);if(o>0)r=s+1;else{if(!(o<0))return s;i=s-1}}return-(r+1)}function u(e,t,n){var r=e.getAnnotations().sort(s.comparePoints);if(!r.length)return;var i=o(r,{row:t,column:-1},s.comparePoints);i<0&&(i=-i-1),i>=r.length?i=n>0?0:r.length-1:i===0&&n<0&&(i=r.length-1);var u=r[i];if(!u||!n)return;if(u.row===t){do u=r[i+=n];while(u&&u.row===t);if(!u)return r.slice()}var a=[];t=u.row;do a[n<0?"unshift":"push"](u),u=r[i+=n];while(u&&u.row==t);return a.length&&a}var r=e("../line_widgets").LineWidgets,i=e("../lib/dom"),s=e("../range").Range;t.showErrorMarker=function(e,t){var n=e.session;n.widgetManager||(n.widgetManager=new r(n),n.widgetManager.attach(e));var s=e.getCursorPosition(),o=s.row,a=n.widgetManager.getWidgetsAtRow(o).filter(function(e){return e.type=="errorMarker"})[0];a?a.destroy():o-=t;var f=u(n,o,t),l;if(f){var c=f[0];s.column=(c.pos&&typeof c.column!="number"?c.pos.sc:c.column)||0,s.row=c.row,l=e.renderer.$gutterLayer.$annotations[s.row]}else{if(a)return;l={text:["Looks good!"],className:"ace_ok"}}e.session.unfold(s.row),e.selection.moveToPosition(s);var h={row:s.row,fixedWidth:!0,coverGutter:!0,el:i.createElement("div"),type:"errorMarker"},p=h.el.appendChild(i.createElement("div")),d=h.el.appendChild(i.createElement("div"));d.className="error_widget_arrow "+l.className;var v=e.renderer.$cursorLayer.getPixelPosition(s).left;d.style.left=v+e.renderer.gutterWidth-5+"px",h.el.className="error_widget_wrapper",p.className="error_widget "+l.className,p.innerHTML=l.text.join(" "),p.appendChild(i.createElement("div"));var m=function(e,t,n){if(t===0&&(n==="esc"||n==="return"))return h.destroy(),{command:"null"}};h.destroy=function(){if(e.$mouseHandler.isMousePressed)return;e.keyBinding.removeKeyboardHandler(m),n.widgetManager.removeLineWidget(h),e.off("changeSelection",h.destroy),e.off("changeSession",h.destroy),e.off("mouseup",h.destroy),e.off("change",h.destroy)},e.keyBinding.addKeyboardHandler(m),e.on("changeSelection",h.destroy),e.on("changeSession",h.destroy),e.on("mouseup",h.destroy),e.on("change",h.destroy),e.session.widgetManager.addLineWidget(h),h.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:h.el.offsetHeight})},i.importCssString(" .error_widget_wrapper { background: inherit; color: inherit; border:none } .error_widget { border-top: solid 2px; border-bottom: solid 2px; margin: 5px 0; padding: 10px 40px; white-space: pre-wrap; } .error_widget.ace_error, .error_widget_arrow.ace_error{ border-color: #ff5a5a } .error_widget.ace_warning, .error_widget_arrow.ace_warning{ border-color: #F1D817 } .error_widget.ace_info, .error_widget_arrow.ace_info{ border-color: #5a5a5a } .error_widget.ace_ok, .error_widget_arrow.ace_ok{ border-color: #5aaa5a } .error_widget_arrow { position: absolute; border: solid 5px; border-top-color: transparent!important; border-right-color: transparent!important; border-left-color: transparent!important; top: -5px; }","")}),define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/range","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config"],function(e,t,n){"use strict";e("./lib/fixoldbrowsers");var r=e("./lib/dom"),i=e("./lib/event"),s=e("./range").Range,o=e("./editor").Editor,u=e("./edit_session").EditSession,a=e("./undomanager").UndoManager,f=e("./virtual_renderer").VirtualRenderer;e("./worker/worker_client"),e("./keyboard/hash_handler"),e("./placeholder"),e("./multi_select"),e("./mode/folding/fold_mode"),e("./theme/textmate"),e("./ext/error_marker"),t.config=e("./config"),t.require=e,typeof define=="function"&&(t.define=define),t.edit=function(e,n){if(typeof e=="string"){var s=e;e=document.getElementById(s);if(!e)throw new Error("ace.edit can't find div #"+s)}if(e&&e.env&&e.env.editor instanceof o)return e.env.editor;var u="";if(e&&/input|textarea/i.test(e.tagName)){var a=e;u=a.value,e=r.createElement("pre"),a.parentNode.replaceChild(e,a)}else e&&(u=e.textContent,e.innerHTML="");var l=t.createEditSession(u),c=new o(new f(e),l,n),h={document:l,editor:c,onResize:c.resize.bind(c,null)};return a&&(h.textarea=a),i.addListener(window,"resize",h.onResize),c.on("destroy",function(){i.removeListener(window,"resize",h.onResize),h.editor.container.env=null}),c.container.env=c.env=h,c},t.createEditSession=function(e,t){var n=new u(e,t);return n.setUndoManager(new a),n},t.Range=s,t.Editor=o,t.EditSession=u,t.UndoManager=a,t.VirtualRenderer=f,t.version=t.config.version}); (function() {
+ window.require(["ace/ace"], function(a) {
+ if (a) {
+ a.config.init(true);
+ a.define = window.define;
+ }
+ if (!window.ace)
+ window.ace = a;
+ for (var key in a) if (a.hasOwnProperty(key))
+ window.ace[key] = a[key];
+ window.ace["default"] = window.ace;
+ if (typeof module == "object" && typeof exports == "object" && module) {
+ module.exports = window.ace;
+ }
+ });
+ })();
+
\ No newline at end of file
diff --git a/src/static/js/lib/echarts.min.js b/src/static/js/lib/echarts.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..97081b3f1b908e054b1776d79056fa56058d6320
--- /dev/null
+++ b/src/static/js/lib/echarts.min.js
@@ -0,0 +1,45 @@
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you 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.
+*/
+
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).echarts={})}(this,(function(t){"use strict";
+ /*! *****************************************************************************
+ Copyright (c) Microsoft Corporation.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************** */var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])})(t,n)};function n(t,n){function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}var i=function(){return(i=Object.assign||function(t){for(var e,n=1,i=arguments.length;n18);a&&(n.weChat=!0);e.canvasSupported=!!document.createElement("canvas").getContext,e.svgSupported="undefined"!=typeof SVGRect,e.touchEventsSupported="ontouchstart"in window&&!n.ie&&!n.edge,e.pointerEventsSupported="onpointerdown"in window&&(n.edge||n.ie&&+n.version>=11),e.domSupported="undefined"!=typeof document}(navigator.userAgent,a);var s={"[object Function]":!0,"[object RegExp]":!0,"[object Date]":!0,"[object Error]":!0,"[object CanvasGradient]":!0,"[object CanvasPattern]":!0,"[object Image]":!0,"[object Canvas]":!0},l={"[object Int8Array]":!0,"[object Uint8Array]":!0,"[object Uint8ClampedArray]":!0,"[object Int16Array]":!0,"[object Uint16Array]":!0,"[object Int32Array]":!0,"[object Uint32Array]":!0,"[object Float32Array]":!0,"[object Float64Array]":!0},u=Object.prototype.toString,h=Array.prototype,c=h.forEach,p=h.filter,d=h.slice,f=h.map,g=function(){}.constructor,y=g?g.prototype:null,v={};function m(t,e){v[t]=e}var _=2311;function x(){return _++}function b(){for(var t=[],e=0;e>1)%2;a.style.cssText=["position: absolute","visibility: hidden","padding: 0","margin: 0","border-width: 0","user-select: none","width:0","height:0",i[s]+":0",r[l]+":0",i[1-s]+":auto",r[1-l]+":auto",""].join("!important;"),t.appendChild(a),n.push(a)}return n}(e,o),o,r);if(s)return s(t,n,i),!0}return!1}function Ut(t){return"CANVAS"===t.nodeName.toUpperCase()}var Zt="undefined"!=typeof window&&!!window.addEventListener,jt=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,qt=[];function Kt(t,e,n,i){return n=n||{},i||!a.canvasSupported?$t(t,e,n):a.browser.firefox&&null!=e.layerX&&e.layerX!==e.offsetX?(n.zrX=e.layerX,n.zrY=e.layerY):null!=e.offsetX?(n.zrX=e.offsetX,n.zrY=e.offsetY):$t(t,e,n),n}function $t(t,e,n){if(a.domSupported&&t.getBoundingClientRect){var i=e.clientX,r=e.clientY;if(Ut(t)){var o=t.getBoundingClientRect();return n.zrX=i-o.left,void(n.zrY=r-o.top)}if(Xt(qt,t,i,r))return n.zrX=qt[0],void(n.zrY=qt[1])}n.zrX=n.zrY=0}function Jt(t){return t||window.event}function Qt(t,e,n){if(null!=(e=Jt(e)).zrX)return e;var i=e.type;if(i&&i.indexOf("touch")>=0){var r="touchend"!==i?e.targetTouches[0]:e.changedTouches[0];r&&Kt(t,r,e,n)}else{Kt(t,e,e,n);var o=function(t){var e=t.wheelDelta;if(e)return e;var n=t.deltaX,i=t.deltaY;if(null==n||null==i)return e;return 3*(0!==i?Math.abs(i):Math.abs(n))*(i>0?-1:i<0?1:n>0?-1:1)}(e);e.zrDelta=o?o/120:-(e.detail||0)/3}var a=e.button;return null==e.which&&void 0!==a&&jt.test(e.type)&&(e.which=1&a?1:2&a?3:4&a?2:0),e}function te(t,e,n,i){Zt?t.addEventListener(e,n,i):t.attachEvent("on"+e,n)}var ee=Zt?function(t){t.preventDefault(),t.stopPropagation(),t.cancelBubble=!0}:function(t){t.returnValue=!1,t.cancelBubble=!0};function ne(t){return 2===t.which||3===t.which}var ie=function(){function t(){this._track=[]}return t.prototype.recognize=function(t,e,n){return this._doTrack(t,e,n),this._recognize(t)},t.prototype.clear=function(){return this._track.length=0,this},t.prototype._doTrack=function(t,e,n){var i=t.touches;if(i){for(var r={points:[],touches:[],target:e,event:t},o=0,a=i.length;o1&&r&&r.length>1){var a=re(r)/re(o);!isFinite(a)&&(a=1),e.pinchScale=a;var s=[((i=r)[0][0]+i[1][0])/2,(i[0][1]+i[1][1])/2];return e.pinchX=s[0],e.pinchY=s[1],{type:"pinch",target:t[0].target,event:e}}}}},ae="silent";function se(){ee(this.event)}var le=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.handler=null,e}return n(e,t),e.prototype.dispose=function(){},e.prototype.setCursor=function(){},e}(Ft),ue=function(t,e){this.x=t,this.y=e},he=["click","dblclick","mousewheel","mouseout","mouseup","mousedown","mousemove","contextmenu"],ce=function(t){function e(e,n,i,r){var o=t.call(this)||this;return o._hovered=new ue(0,0),o.storage=e,o.painter=n,o.painterRoot=r,i=i||new le,o.proxy=null,o.setHandlerProxy(i),o._draggingMgr=new Vt(o),o}return n(e,t),e.prototype.setHandlerProxy=function(t){this.proxy&&this.proxy.dispose(),t&&(P(he,(function(e){t.on&&t.on(e,this[e],this)}),this),t.handler=this),this.proxy=t},e.prototype.mousemove=function(t){var e=t.zrX,n=t.zrY,i=de(this,e,n),r=this._hovered,o=r.target;o&&!o.__zr&&(o=(r=this.findHover(r.x,r.y)).target);var a=this._hovered=i?new ue(e,n):this.findHover(e,n),s=a.target,l=this.proxy;l.setCursor&&l.setCursor(s?s.cursor:"default"),o&&s!==o&&this.dispatchToElement(r,"mouseout",t),this.dispatchToElement(a,"mousemove",t),s&&s!==o&&this.dispatchToElement(a,"mouseover",t)},e.prototype.mouseout=function(t){var e=t.zrEventControl;"only_globalout"!==e&&this.dispatchToElement(this._hovered,"mouseout",t),"no_globalout"!==e&&this.trigger("globalout",{type:"globalout",event:t})},e.prototype.resize=function(){this._hovered=new ue(0,0)},e.prototype.dispatch=function(t,e){var n=this[t];n&&n.call(this,e)},e.prototype.dispose=function(){this.proxy.dispose(),this.storage=null,this.proxy=null,this.painter=null},e.prototype.setCursorStyle=function(t){var e=this.proxy;e.setCursor&&e.setCursor(t)},e.prototype.dispatchToElement=function(t,e,n){var i=(t=t||{}).target;if(!i||!i.silent){for(var r="on"+e,o=function(t,e,n){return{type:t,event:n,target:e.target,topTarget:e.topTarget,cancelBubble:!1,offsetX:n.zrX,offsetY:n.zrY,gestureEvent:n.gestureEvent,pinchX:n.pinchX,pinchY:n.pinchY,pinchScale:n.pinchScale,wheelDelta:n.zrDelta,zrByTouch:n.zrByTouch,which:n.which,stop:se}}(e,t,n);i&&(i[r]&&(o.cancelBubble=!!i[r].call(i,o)),i.trigger(e,o),i=i.__hostTarget?i.__hostTarget:i.parent,!o.cancelBubble););o.cancelBubble||(this.trigger(e,o),this.painter&&this.painter.eachOtherLayer&&this.painter.eachOtherLayer((function(t){"function"==typeof t[r]&&t[r].call(t,o),t.trigger&&t.trigger(e,o)})))}},e.prototype.findHover=function(t,e,n){for(var i=this.storage.getDisplayList(),r=new ue(t,e),o=i.length-1;o>=0;o--){var a=void 0;if(i[o]!==n&&!i[o].ignore&&(a=pe(i[o],t,e))&&(!r.topTarget&&(r.topTarget=i[o]),a!==ae)){r.target=i[o];break}}return r},e.prototype.processGesture=function(t,e){this._gestureMgr||(this._gestureMgr=new ie);var n=this._gestureMgr;"start"===e&&n.clear();var i=n.recognize(t,this.findHover(t.zrX,t.zrY,null).target,this.proxy.dom);if("end"===e&&n.clear(),i){var r=i.type;t.gestureEvent=r;var o=new ue;o.target=i.target,this.dispatchToElement(o,r,i.event)}},e}(Ft);function pe(t,e,n){if(t[t.rectHover?"rectContain":"contain"](e,n)){for(var i=t,r=void 0,o=!1;i;){if(i.ignoreClip&&(o=!0),!o){var a=i.getClipPath();if(a&&!a.contain(e,n))return!1;i.silent&&(r=!0)}var s=i.__hostTarget;i=s||i.parent}return!r||ae}return!1}function de(t,e,n){var i=t.painter;return e<0||e>i.getWidth()||n<0||n>i.getHeight()}function fe(){return[1,0,0,1,0,0]}function ge(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t}function ye(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t}function ve(t,e,n){var i=e[0]*n[0]+e[2]*n[1],r=e[1]*n[0]+e[3]*n[1],o=e[0]*n[2]+e[2]*n[3],a=e[1]*n[2]+e[3]*n[3],s=e[0]*n[4]+e[2]*n[5]+e[4],l=e[1]*n[4]+e[3]*n[5]+e[5];return t[0]=i,t[1]=r,t[2]=o,t[3]=a,t[4]=s,t[5]=l,t}function me(t,e,n){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4]+n[0],t[5]=e[5]+n[1],t}function _e(t,e,n){var i=e[0],r=e[2],o=e[4],a=e[1],s=e[3],l=e[5],u=Math.sin(n),h=Math.cos(n);return t[0]=i*h+a*u,t[1]=-i*u+a*h,t[2]=r*h+s*u,t[3]=-r*u+h*s,t[4]=h*o+u*l,t[5]=h*l-u*o,t}function xe(t,e,n){var i=n[0],r=n[1];return t[0]=e[0]*i,t[1]=e[1]*r,t[2]=e[2]*i,t[3]=e[3]*r,t[4]=e[4]*i,t[5]=e[5]*r,t}function be(t,e){var n=e[0],i=e[2],r=e[4],o=e[1],a=e[3],s=e[5],l=n*a-o*i;return l?(l=1/l,t[0]=a*l,t[1]=-o*l,t[2]=-i*l,t[3]=n*l,t[4]=(i*s-a*r)*l,t[5]=(o*r-n*s)*l,t):null}function we(t){var e=[1,0,0,1,0,0];return ye(e,t),e}P(["click","mousedown","mouseup","mousewheel","dblclick","contextmenu"],(function(t){ce.prototype[t]=function(e){var n,i,r=e.zrX,o=e.zrY,a=de(this,r,o);if("mouseup"===t&&a||(i=(n=this.findHover(r,o)).target),"mousedown"===t)this._downEl=i,this._downPoint=[e.zrX,e.zrY],this._upEl=i;else if("mouseup"===t)this._upEl=i;else if("click"===t){if(this._downEl!==this._upEl||!this._downPoint||Lt(this._downPoint,[e.zrX,e.zrY])>4)return;this._downPoint=null}this.dispatchToElement(n,t,e)}}));var Se=Object.freeze({__proto__:null,create:fe,identity:ge,copy:ye,mul:ve,translate:me,rotate:_e,scale:xe,invert:be,clone:we}),Me=ge,Ie=5e-5;function Te(t){return t>Ie||t<-5e-5}var Ce=[],Ae=[],De=[1,0,0,1,0,0],Le=Math.abs,ke=function(){function t(){}return t.prototype.setPosition=function(t){this.x=t[0],this.y=t[1]},t.prototype.setScale=function(t){this.scaleX=t[0],this.scaleY=t[1]},t.prototype.setOrigin=function(t){this.originX=t[0],this.originY=t[1]},t.prototype.needLocalTransform=function(){return Te(this.rotation)||Te(this.x)||Te(this.y)||Te(this.scaleX-1)||Te(this.scaleY-1)},t.prototype.updateTransform=function(){var t=this.parent,e=t&&t.transform,n=this.needLocalTransform(),i=this.transform;n||e?(i=i||[1,0,0,1,0,0],n?this.getLocalTransform(i):Me(i),e&&(n?ve(i,t.transform,i):ye(i,t.transform)),this.transform=i,this._resolveGlobalScaleRatio(i)):i&&Me(i)},t.prototype._resolveGlobalScaleRatio=function(t){var e=this.globalScaleRatio;if(null!=e&&1!==e){this.getGlobalScale(Ce);var n=Ce[0]<0?-1:1,i=Ce[1]<0?-1:1,r=((Ce[0]-n)*e+n)/Ce[0]||0,o=((Ce[1]-i)*e+i)/Ce[1]||0;t[0]*=r,t[1]*=r,t[2]*=o,t[3]*=o}this.invTransform=this.invTransform||[1,0,0,1,0,0],be(this.invTransform,t)},t.prototype.getLocalTransform=function(e){return t.getLocalTransform(this,e)},t.prototype.getComputedTransform=function(){for(var t=this,e=[];t;)e.push(t),t=t.parent;for(;t=e.pop();)t.updateTransform();return this.transform},t.prototype.setLocalTransform=function(t){if(t){var e=t[0]*t[0]+t[1]*t[1],n=t[2]*t[2]+t[3]*t[3];Te(e-1)&&(e=Math.sqrt(e)),Te(n-1)&&(n=Math.sqrt(n)),t[0]<0&&(e=-e),t[3]<0&&(n=-n),this.rotation=Math.atan2(-t[1]/n,t[0]/e),e<0&&n<0&&(this.rotation+=Math.PI,e=-e,n=-n),this.x=t[4],this.y=t[5],this.scaleX=e,this.scaleY=n}},t.prototype.decomposeTransform=function(){if(this.transform){var t=this.parent,e=this.transform;t&&t.transform&&(ve(Ae,t.invTransform,e),e=Ae);var n=this.originX,i=this.originY;(n||i)&&(De[4]=n,De[5]=i,ve(Ae,e,De),Ae[4]-=n,Ae[5]-=i,e=Ae),this.setLocalTransform(e)}},t.prototype.getGlobalScale=function(t){var e=this.transform;return t=t||[],e?(t[0]=Math.sqrt(e[0]*e[0]+e[1]*e[1]),t[1]=Math.sqrt(e[2]*e[2]+e[3]*e[3]),e[0]<0&&(t[0]=-t[0]),e[3]<0&&(t[1]=-t[1]),t):(t[0]=1,t[1]=1,t)},t.prototype.transformCoordToLocal=function(t,e){var n=[t,e],i=this.invTransform;return i&&Rt(n,n,i),n},t.prototype.transformCoordToGlobal=function(t,e){var n=[t,e],i=this.transform;return i&&Rt(n,n,i),n},t.prototype.getLineScale=function(){var t=this.transform;return t&&Le(t[0]-1)>1e-10&&Le(t[3]-1)>1e-10?Math.sqrt(Le(t[0]*t[3]-t[2]*t[1])):1},t.getLocalTransform=function(t,e){Me(e=e||[]);var n=t.originX||0,i=t.originY||0,r=t.scaleX,o=t.scaleY,a=t.rotation||0,s=t.x,l=t.y;return e[4]-=n,e[5]-=i,e[0]*=r,e[1]*=o,e[2]*=r,e[3]*=o,e[4]*=r,e[5]*=o,a&&_e(e,e,a),e[4]+=n,e[5]+=i,e[4]+=s,e[5]+=l,e},t.initDefaultProps=function(){var e=t.prototype;e.x=0,e.y=0,e.scaleX=1,e.scaleY=1,e.originX=0,e.originY=0,e.rotation=0,e.globalScaleRatio=1}(),t}(),Pe={linear:function(t){return t},quadraticIn:function(t){return t*t},quadraticOut:function(t){return t*(2-t)},quadraticInOut:function(t){return(t*=2)<1?.5*t*t:-.5*(--t*(t-2)-1)},cubicIn:function(t){return t*t*t},cubicOut:function(t){return--t*t*t+1},cubicInOut:function(t){return(t*=2)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},quarticIn:function(t){return t*t*t*t},quarticOut:function(t){return 1- --t*t*t*t},quarticInOut:function(t){return(t*=2)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},quinticIn:function(t){return t*t*t*t*t},quinticOut:function(t){return--t*t*t*t*t+1},quinticInOut:function(t){return(t*=2)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},sinusoidalIn:function(t){return 1-Math.cos(t*Math.PI/2)},sinusoidalOut:function(t){return Math.sin(t*Math.PI/2)},sinusoidalInOut:function(t){return.5*(1-Math.cos(Math.PI*t))},exponentialIn:function(t){return 0===t?0:Math.pow(1024,t-1)},exponentialOut:function(t){return 1===t?1:1-Math.pow(2,-10*t)},exponentialInOut:function(t){return 0===t?0:1===t?1:(t*=2)<1?.5*Math.pow(1024,t-1):.5*(2-Math.pow(2,-10*(t-1)))},circularIn:function(t){return 1-Math.sqrt(1-t*t)},circularOut:function(t){return Math.sqrt(1- --t*t)},circularInOut:function(t){return(t*=2)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},elasticIn:function(t){var e,n=.1;return 0===t?0:1===t?1:(!n||n<1?(n=1,e=.1):e=.4*Math.asin(1/n)/(2*Math.PI),-n*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/.4))},elasticOut:function(t){var e,n=.1;return 0===t?0:1===t?1:(!n||n<1?(n=1,e=.1):e=.4*Math.asin(1/n)/(2*Math.PI),n*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/.4)+1)},elasticInOut:function(t){var e,n=.1,i=.4;return 0===t?0:1===t?1:(!n||n<1?(n=1,e=.1):e=i*Math.asin(1/n)/(2*Math.PI),(t*=2)<1?n*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/i)*-.5:n*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/i)*.5+1)},backIn:function(t){var e=1.70158;return t*t*((e+1)*t-e)},backOut:function(t){var e=1.70158;return--t*t*((e+1)*t+e)+1},backInOut:function(t){var e=2.5949095;return(t*=2)<1?t*t*((e+1)*t-e)*.5:.5*((t-=2)*t*((e+1)*t+e)+2)},bounceIn:function(t){return 1-Pe.bounceOut(1-t)},bounceOut:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},bounceInOut:function(t){return t<.5?.5*Pe.bounceIn(2*t):.5*Pe.bounceOut(2*t-1)+.5}},Oe=function(){function t(t){this._initialized=!1,this._startTime=0,this._pausedTime=0,this._paused=!1,this._life=t.life||1e3,this._delay=t.delay||0,this.loop=null!=t.loop&&t.loop,this.gap=t.gap||0,this.easing=t.easing||"linear",this.onframe=t.onframe,this.ondestroy=t.ondestroy,this.onrestart=t.onrestart}return t.prototype.step=function(t,e){if(this._initialized||(this._startTime=t+this._delay,this._initialized=!0),!this._paused){var n=(t-this._startTime-this._pausedTime)/this._life;n<0&&(n=0),n=Math.min(n,1);var i=this.easing,r="string"==typeof i?Pe[i]:i,o="function"==typeof r?r(n):n;if(this.onframe&&this.onframe(o),1===n){if(!this.loop)return!0;this._restart(t),this.onrestart&&this.onrestart()}return!1}this._pausedTime+=e},t.prototype._restart=function(t){var e=(t-this._startTime-this._pausedTime)%this._life;this._startTime=t-e+this.gap,this._pausedTime=0},t.prototype.pause=function(){this._paused=!0},t.prototype.resume=function(){this._paused=!1},t}(),Re=function(t){this.value=t},Ne=function(){function t(){this._len=0}return t.prototype.insert=function(t){var e=new Re(t);return this.insertEntry(e),e},t.prototype.insertEntry=function(t){this.head?(this.tail.next=t,t.prev=this.tail,t.next=null,this.tail=t):this.head=this.tail=t,this._len++},t.prototype.remove=function(t){var e=t.prev,n=t.next;e?e.next=n:this.head=n,n?n.prev=e:this.tail=e,t.next=t.prev=null,this._len--},t.prototype.len=function(){return this._len},t.prototype.clear=function(){this.head=this.tail=null,this._len=0},t}(),Ee=function(){function t(t){this._list=new Ne,this._maxSize=10,this._map={},this._maxSize=t}return t.prototype.put=function(t,e){var n=this._list,i=this._map,r=null;if(null==i[t]){var o=n.len(),a=this._lastRemovedEntry;if(o>=this._maxSize&&o>0){var s=n.head;n.remove(s),delete i[s.key],r=s.value,this._lastRemovedEntry=s}a?a.value=e:a=new Re(e),a.key=t,n.insertEntry(a),i[t]=a}return r},t.prototype.get=function(t){var e=this._map[t],n=this._list;if(null!=e)return e!==n.tail&&(n.remove(e),n.insertEntry(e)),e.value},t.prototype.clear=function(){this._list.clear(),this._map={}},t.prototype.len=function(){return this._list.len()},t}(),ze={transparent:[0,0,0,0],aliceblue:[240,248,255,1],antiquewhite:[250,235,215,1],aqua:[0,255,255,1],aquamarine:[127,255,212,1],azure:[240,255,255,1],beige:[245,245,220,1],bisque:[255,228,196,1],black:[0,0,0,1],blanchedalmond:[255,235,205,1],blue:[0,0,255,1],blueviolet:[138,43,226,1],brown:[165,42,42,1],burlywood:[222,184,135,1],cadetblue:[95,158,160,1],chartreuse:[127,255,0,1],chocolate:[210,105,30,1],coral:[255,127,80,1],cornflowerblue:[100,149,237,1],cornsilk:[255,248,220,1],crimson:[220,20,60,1],cyan:[0,255,255,1],darkblue:[0,0,139,1],darkcyan:[0,139,139,1],darkgoldenrod:[184,134,11,1],darkgray:[169,169,169,1],darkgreen:[0,100,0,1],darkgrey:[169,169,169,1],darkkhaki:[189,183,107,1],darkmagenta:[139,0,139,1],darkolivegreen:[85,107,47,1],darkorange:[255,140,0,1],darkorchid:[153,50,204,1],darkred:[139,0,0,1],darksalmon:[233,150,122,1],darkseagreen:[143,188,143,1],darkslateblue:[72,61,139,1],darkslategray:[47,79,79,1],darkslategrey:[47,79,79,1],darkturquoise:[0,206,209,1],darkviolet:[148,0,211,1],deeppink:[255,20,147,1],deepskyblue:[0,191,255,1],dimgray:[105,105,105,1],dimgrey:[105,105,105,1],dodgerblue:[30,144,255,1],firebrick:[178,34,34,1],floralwhite:[255,250,240,1],forestgreen:[34,139,34,1],fuchsia:[255,0,255,1],gainsboro:[220,220,220,1],ghostwhite:[248,248,255,1],gold:[255,215,0,1],goldenrod:[218,165,32,1],gray:[128,128,128,1],green:[0,128,0,1],greenyellow:[173,255,47,1],grey:[128,128,128,1],honeydew:[240,255,240,1],hotpink:[255,105,180,1],indianred:[205,92,92,1],indigo:[75,0,130,1],ivory:[255,255,240,1],khaki:[240,230,140,1],lavender:[230,230,250,1],lavenderblush:[255,240,245,1],lawngreen:[124,252,0,1],lemonchiffon:[255,250,205,1],lightblue:[173,216,230,1],lightcoral:[240,128,128,1],lightcyan:[224,255,255,1],lightgoldenrodyellow:[250,250,210,1],lightgray:[211,211,211,1],lightgreen:[144,238,144,1],lightgrey:[211,211,211,1],lightpink:[255,182,193,1],lightsalmon:[255,160,122,1],lightseagreen:[32,178,170,1],lightskyblue:[135,206,250,1],lightslategray:[119,136,153,1],lightslategrey:[119,136,153,1],lightsteelblue:[176,196,222,1],lightyellow:[255,255,224,1],lime:[0,255,0,1],limegreen:[50,205,50,1],linen:[250,240,230,1],magenta:[255,0,255,1],maroon:[128,0,0,1],mediumaquamarine:[102,205,170,1],mediumblue:[0,0,205,1],mediumorchid:[186,85,211,1],mediumpurple:[147,112,219,1],mediumseagreen:[60,179,113,1],mediumslateblue:[123,104,238,1],mediumspringgreen:[0,250,154,1],mediumturquoise:[72,209,204,1],mediumvioletred:[199,21,133,1],midnightblue:[25,25,112,1],mintcream:[245,255,250,1],mistyrose:[255,228,225,1],moccasin:[255,228,181,1],navajowhite:[255,222,173,1],navy:[0,0,128,1],oldlace:[253,245,230,1],olive:[128,128,0,1],olivedrab:[107,142,35,1],orange:[255,165,0,1],orangered:[255,69,0,1],orchid:[218,112,214,1],palegoldenrod:[238,232,170,1],palegreen:[152,251,152,1],paleturquoise:[175,238,238,1],palevioletred:[219,112,147,1],papayawhip:[255,239,213,1],peachpuff:[255,218,185,1],peru:[205,133,63,1],pink:[255,192,203,1],plum:[221,160,221,1],powderblue:[176,224,230,1],purple:[128,0,128,1],red:[255,0,0,1],rosybrown:[188,143,143,1],royalblue:[65,105,225,1],saddlebrown:[139,69,19,1],salmon:[250,128,114,1],sandybrown:[244,164,96,1],seagreen:[46,139,87,1],seashell:[255,245,238,1],sienna:[160,82,45,1],silver:[192,192,192,1],skyblue:[135,206,235,1],slateblue:[106,90,205,1],slategray:[112,128,144,1],slategrey:[112,128,144,1],snow:[255,250,250,1],springgreen:[0,255,127,1],steelblue:[70,130,180,1],tan:[210,180,140,1],teal:[0,128,128,1],thistle:[216,191,216,1],tomato:[255,99,71,1],turquoise:[64,224,208,1],violet:[238,130,238,1],wheat:[245,222,179,1],white:[255,255,255,1],whitesmoke:[245,245,245,1],yellow:[255,255,0,1],yellowgreen:[154,205,50,1]};function Be(t){return(t=Math.round(t))<0?0:t>255?255:t}function Ve(t){return t<0?0:t>1?1:t}function Fe(t){var e=t;return e.length&&"%"===e.charAt(e.length-1)?Be(parseFloat(e)/100*255):Be(parseInt(e,10))}function Ge(t){var e=t;return e.length&&"%"===e.charAt(e.length-1)?Ve(parseFloat(e)/100):Ve(parseFloat(e))}function He(t,e,n){return n<0?n+=1:n>1&&(n-=1),6*n<1?t+(e-t)*n*6:2*n<1?e:3*n<2?t+(e-t)*(2/3-n)*6:t}function We(t,e,n){return t+(e-t)*n}function Ye(t,e,n,i,r){return t[0]=e,t[1]=n,t[2]=i,t[3]=r,t}function Xe(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t}var Ue=new Ee(20),Ze=null;function je(t,e){Ze&&Xe(Ze,e),Ze=Ue.put(t,Ze||e.slice())}function qe(t,e){if(t){e=e||[];var n=Ue.get(t);if(n)return Xe(e,n);var i=(t+="").replace(/ /g,"").toLowerCase();if(i in ze)return Xe(e,ze[i]),je(t,e),e;var r,o=i.length;if("#"===i.charAt(0))return 4===o||5===o?(r=parseInt(i.slice(1,4),16))>=0&&r<=4095?(Ye(e,(3840&r)>>4|(3840&r)>>8,240&r|(240&r)>>4,15&r|(15&r)<<4,5===o?parseInt(i.slice(4),16)/15:1),je(t,e),e):void Ye(e,0,0,0,1):7===o||9===o?(r=parseInt(i.slice(1,7),16))>=0&&r<=16777215?(Ye(e,(16711680&r)>>16,(65280&r)>>8,255&r,9===o?parseInt(i.slice(7),16)/255:1),je(t,e),e):void Ye(e,0,0,0,1):void 0;var a=i.indexOf("("),s=i.indexOf(")");if(-1!==a&&s+1===o){var l=i.substr(0,a),u=i.substr(a+1,s-(a+1)).split(","),h=1;switch(l){case"rgba":if(4!==u.length)return 3===u.length?Ye(e,+u[0],+u[1],+u[2],1):Ye(e,0,0,0,1);h=Ge(u.pop());case"rgb":return 3!==u.length?void Ye(e,0,0,0,1):(Ye(e,Fe(u[0]),Fe(u[1]),Fe(u[2]),h),je(t,e),e);case"hsla":return 4!==u.length?void Ye(e,0,0,0,1):(u[3]=Ge(u[3]),Ke(u,e),je(t,e),e);case"hsl":return 3!==u.length?void Ye(e,0,0,0,1):(Ke(u,e),je(t,e),e);default:return}}Ye(e,0,0,0,1)}}function Ke(t,e){var n=(parseFloat(t[0])%360+360)%360/360,i=Ge(t[1]),r=Ge(t[2]),o=r<=.5?r*(i+1):r+i-r*i,a=2*r-o;return Ye(e=e||[],Be(255*He(a,o,n+1/3)),Be(255*He(a,o,n)),Be(255*He(a,o,n-1/3)),1),4===t.length&&(e[3]=t[3]),e}function $e(t,e){var n=qe(t);if(n){for(var i=0;i<3;i++)n[i]=e<0?n[i]*(1-e)|0:(255-n[i])*e+n[i]|0,n[i]>255?n[i]=255:n[i]<0&&(n[i]=0);return an(n,4===n.length?"rgba":"rgb")}}function Je(t){var e=qe(t);if(e)return((1<<24)+(e[0]<<16)+(e[1]<<8)+ +e[2]).toString(16).slice(1)}function Qe(t,e,n){if(e&&e.length&&t>=0&&t<=1){n=n||[];var i=t*(e.length-1),r=Math.floor(i),o=Math.ceil(i),a=e[r],s=e[o],l=i-r;return n[0]=Be(We(a[0],s[0],l)),n[1]=Be(We(a[1],s[1],l)),n[2]=Be(We(a[2],s[2],l)),n[3]=Ve(We(a[3],s[3],l)),n}}var tn=Qe;function en(t,e,n){if(e&&e.length&&t>=0&&t<=1){var i=t*(e.length-1),r=Math.floor(i),o=Math.ceil(i),a=qe(e[r]),s=qe(e[o]),l=i-r,u=an([Be(We(a[0],s[0],l)),Be(We(a[1],s[1],l)),Be(We(a[2],s[2],l)),Ve(We(a[3],s[3],l))],"rgba");return n?{color:u,leftIndex:r,rightIndex:o,value:i}:u}}var nn=en;function rn(t,e,n,i){var r=qe(t);if(t)return r=function(t){if(t){var e,n,i=t[0]/255,r=t[1]/255,o=t[2]/255,a=Math.min(i,r,o),s=Math.max(i,r,o),l=s-a,u=(s+a)/2;if(0===l)e=0,n=0;else{n=u<.5?l/(s+a):l/(2-s-a);var h=((s-i)/6+l/2)/l,c=((s-r)/6+l/2)/l,p=((s-o)/6+l/2)/l;i===s?e=p-c:r===s?e=1/3+h-p:o===s&&(e=2/3+c-h),e<0&&(e+=1),e>1&&(e-=1)}var d=[360*e,n,u];return null!=t[3]&&d.push(t[3]),d}}(r),null!=e&&(r[0]=function(t){return(t=Math.round(t))<0?0:t>360?360:t}(e)),null!=n&&(r[1]=Ge(n)),null!=i&&(r[2]=Ge(i)),an(Ke(r),"rgba")}function on(t,e){var n=qe(t);if(n&&null!=e)return n[3]=Ve(e),an(n,"rgba")}function an(t,e){if(t&&t.length){var n=t[0]+","+t[1]+","+t[2];return"rgba"!==e&&"hsva"!==e&&"hsla"!==e||(n+=","+t[3]),e+"("+n+")"}}function sn(t,e){var n=qe(t);return n?(.299*n[0]+.587*n[1]+.114*n[2])*n[3]/255+(1-n[3])*e:0}var ln=Object.freeze({__proto__:null,parse:qe,lift:$e,toHex:Je,fastLerp:Qe,fastMapToColor:tn,lerp:en,mapToColor:nn,modifyHSL:rn,modifyAlpha:on,stringify:an,lum:sn,random:function(){return"rgb("+Math.round(255*Math.random())+","+Math.round(255*Math.random())+","+Math.round(255*Math.random())+")"}}),un=Array.prototype.slice;function hn(t,e,n){return(e-t)*n+t}function cn(t,e,n,i){for(var r=e.length,o=0;oa)i.length=a;else for(var s=o;s=2&&this.interpolable},t.prototype.getAdditiveTrack=function(){return this._additiveTrack},t.prototype.addKeyframe=function(t,e){t>=this.maxTime?this.maxTime=t:this._needsSort=!0;var n=this.keyframes,i=n.length;if(this.interpolable)if(k(e)){var r=function(t){return k(t&&t[0])?2:1}(e);if(i>0&&this.arrDim!==r)return void(this.interpolable=!1);if(1===r&&"number"!=typeof e[0]||2===r&&"number"!=typeof e[0][0])return void(this.interpolable=!1);if(i>0){var o=n[i-1];this._isAllValueEqual&&(1===r&&gn(e,o.value)||(this._isAllValueEqual=!1))}this.arrDim=r}else{if(this.arrDim>0)return void(this.interpolable=!1);if("string"==typeof e){var a=qe(e);a?(e=a,this.isValueColor=!0):this.interpolable=!1}else if("number"!=typeof e)return void(this.interpolable=!1);if(this._isAllValueEqual&&i>0){o=n[i-1];(this.isValueColor&&!gn(o.value,e)||o.value!==e)&&(this._isAllValueEqual=!1)}}var s={time:t,value:e,percent:0};return this.keyframes.push(s),s},t.prototype.prepare=function(t){var e=this.keyframes;this._needsSort&&e.sort((function(t,e){return t.time-e.time}));for(var n=this.arrDim,i=e.length,r=e[i-1],o=0;o