diff --git a/.github/workflows/python-app-mysql55.yml b/.github/workflows/python-app-mysql55.yml index ec8a8e6a1..a1866f13a 100644 --- a/.github/workflows/python-app-mysql55.yml +++ b/.github/workflows/python-app-mysql55.yml @@ -34,5 +34,5 @@ jobs: cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.5 - name: Start DEBUG run: | - gunicorn -c setting.py app:app - python3 task.py & + cd web && gunicorn -c setting.py app:app + python3 panel_task.py & diff --git a/.github/workflows/python-app-mysql56.yml b/.github/workflows/python-app-mysql56.yml index 75b168717..d4b4a2618 100644 --- a/.github/workflows/python-app-mysql56.yml +++ b/.github/workflows/python-app-mysql56.yml @@ -34,5 +34,5 @@ jobs: cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.6 - name: Start DEBUG run: | - gunicorn -c setting.py app:app - python3 task.py & + cd web && gunicorn -c setting.py app:app + python3 panel_task.py & diff --git a/.github/workflows/python-app-mysql57.yml b/.github/workflows/python-app-mysql57.yml index 18f1a4f90..fa5871402 100644 --- a/.github/workflows/python-app-mysql57.yml +++ b/.github/workflows/python-app-mysql57.yml @@ -34,5 +34,5 @@ jobs: cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.7 - name: Start DEBUG run: | - gunicorn -c setting.py app:app - python3 task.py & + cd web && gunicorn -c setting.py app:app + python3 panel_task.py & diff --git a/.github/workflows/python-app-mysql80.yml b/.github/workflows/python-app-mysql80.yml index 2f2799bdd..c51d2ff7a 100644 --- a/.github/workflows/python-app-mysql80.yml +++ b/.github/workflows/python-app-mysql80.yml @@ -34,5 +34,5 @@ jobs: cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 8.0 - name: Start DEBUG run: | - gunicorn -c setting.py app:app - python3 task.py & + cd web && gunicorn -c setting.py app:app + python3 panel_task.py & diff --git a/.github/workflows/python-app-op.yml b/.github/workflows/python-app-op.yml index 53ab5f7fc..bd26948b7 100644 --- a/.github/workflows/python-app-op.yml +++ b/.github/workflows/python-app-op.yml @@ -31,8 +31,8 @@ jobs: sudo bash scripts/install_dev.sh - name: Install OpenResty run: | - cd /www/server/mdserver-web/plugins/openresty && sudo bash install.sh install 1.21.4.1 + cd /www/server/mdserver-web/plugins/openresty && sudo bash install.sh install 1.27.1 - name: Start DEBUG run: | - gunicorn -c setting.py app:app - python3 task.py & + source /www/server/mdserver-web/bin/activate && cd web && gunicorn -c setting.py app:app + cd /www/server/mdserver-web && python3 panel_task.py & diff --git a/.github/workflows/python-app-php.yml b/.github/workflows/python-app-php.yml index 790544208..71796d15b 100644 --- a/.github/workflows/python-app-php.yml +++ b/.github/workflows/python-app-php.yml @@ -64,5 +64,5 @@ jobs: cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 81 - name: Start DEBUG run: | - gunicorn -c setting.py app:app - python3 task.py & + source /www/server/mdserver-web/bin/activate && cd web && gunicorn -c setting.py app:app + cd /www/server/mdserver-web && python3 panel_task.py & diff --git a/.gitignore b/.gitignore index 63aba0e20..0061db5f6 100644 --- a/.gitignore +++ b/.gitignore @@ -119,7 +119,13 @@ debug.out mdioks.session-journal *.session -logs/* +temp + +logs/panel.pid +logs/panel.log +logs/panel_task.log +logs/panel_exec.log +logs/panel_task.lock ssl/local ssl/nginx @@ -161,6 +167,7 @@ data/api.json data/bind_domain.pl data/unauthorized_status.pl data/auth_secret.pl +data/panelSpeed.pl plugins/vip_* plugins/own_* @@ -170,7 +177,6 @@ plugins/l2tp plugins/openlitespeed plugins/tamper_proof plugins/cryptocurrency_trade -plugins/mtproxy plugins/zimg plugins/bk_demo plugins/mail @@ -185,4 +191,5 @@ plugins/goedge-node plugins/goedge-happy plugins/dztasks -data/default_new.pl +/logs +/data diff --git a/README.md b/README.md index 1107bbb41..b169f8225 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,10 @@ Talk is cheap, show me the code. -- linus - [兼容性测试报告](/compatibility.md) - [常用命令说明](/cmd.md) [ mw default ] [ mw update_dev ] +### 特别赞助 + +- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan1.cc) + ### 主要插件介绍 * OpenResty - 轻量级,占有内存少,并发能力强。 @@ -88,10 +92,6 @@ PHP[72-84]支持phpMyAdmin[5.2.1] - https://www.youtube.com/watch?v=2taa5K-Jmmw -# 特别赞助 - -- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan1.cc) - # AD - VPS推荐 - 🙏 - [ZZZ评测](https://www.zzzvps.com/) @@ -110,11 +110,14 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443: ``` -### 版本更新 0.17.3 +### 版本更新 0.18.0 -- 新增pgadmin插件。 -- 新增acme_pandominassl_apply插件。 -- 新增快捷指令:mw pgdb。 +- 重新架构面板。 +- PHP84正式版。 +- 加入alist插件。 +- 数据管理-常用命令。 +- 插件读取优化(加入缓存+定时检查状态)。 +- 恢复面板书签。 - 常规更新。 ### JSDelivr安装地址 @@ -182,11 +185,12 @@ curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/install_dev.sh | bash curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh | bash +``` - - -curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_new.sh | bash -curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_new.sh | bash +### 旧版安装 +``` +curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/install.sh | bash +curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/update.sh | bash ``` ### 捐赠地址 USDT(TRC20) diff --git a/app.py b/app.py deleted file mode 100644 index 685346c77..000000000 --- a/app.py +++ /dev/null @@ -1,52 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 入口文件 -# --------------------------------------------------------------------------------- - - -# pip install profiler_online -# 性能测试 -# from profiler_online import run_profiler -# run_profiler() -import sys -import io -import os - -sys.dont_write_bytecode = True -from route import app, socketio - - -from gevent.pywsgi import WSGIServer -from geventwebsocket.handler import WebSocketHandler - - -from gevent import monkey -monkey.patch_all() - -try: - if __name__ == "__main__": - - PORT = 7200 - if os.path.exists('data/port.pl'): - f = open('data/port.pl') - PORT = int(f.read()) - f.close() - - # HOST = '0.0.0.0' - # app.run(host=HOST, port=PORT) - - http_server = WSGIServer( - (HOST, PORT), app, handler_class=WebSocketHandler) - http_server.serve_forever() - socketio.run(app, host=HOST, port=PORT) -except Exception as ex: - print(ex) diff --git a/class/core/cert_api.py b/class/core/cert_api.py deleted file mode 100644 index 8fbc15967..000000000 --- a/class/core/cert_api.py +++ /dev/null @@ -1,1621 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 证书申请 -# --------------------------------------------------------------------------------- - -import re -import fcntl -import datetime -import binascii -import hashlib -import base64 -import json -import time -import os -import sys -import argparse - -if os.path.exists('/www/server/mdserver-web'): - os.chdir('/www/server/mdserver-web') - -import mw - -try: - import OpenSSL -except: - mw.execShell("pip install pyopenssl") - import OpenSSL - - -def echoErr(msg): - writeLog("\033[31m=" * 65) - writeLog("|-错误:{}\033[0m".format(msg)) - exit() - - -def writeLog(log_str, mode="ab+"): - # 写日志 - if __name__ == "__main__": - print(log_str) - return - _log_file = 'logs/letsencrypt.log' - f = open(_log_file, mode) - log_str += "\n" - f.write(log_str.encode('utf-8')) - f.close() - return True - - -class cert_api: - __debug = False - __user_agent = "MW-Panel" - __apis = None - __url = None - __replay_nonce = None - __acme_timeout = 30 - __max_check_num = 5 - __wait_time = 5 - __bits = 2048 - __digest = "sha256" - __verify = False - __config = {} - __dns_class = None - __auto_wildcard = False - __mod_index = {True: "Staging", False: "Production"} - __save_path = 'data/letsencrypt' - __cfg_file = 'data/letsencrypt.json' - - def __init__(self): - self.__user_agent = 'MW-Panel:' + mw.getRandomString(8) - self.__save_path = mw.getServerDir() + '/web_conf/letsencrypt' - if not os.path.exists(self.__save_path): - os.makedirs(self.__save_path) - if self.__debug: - self.__url = 'https://acme-staging-v02.api.letsencrypt.org/directory' - else: - self.__url = 'https://acme-v02.api.letsencrypt.org/directory' - self.__config = self.readConfig() - - def D(self, name, val): - if self.__debug: - print('---------{} start--------'.format(name)) - print(val) - print('---------{} end--------'.format(name)) - - # 读配置文件 - def readConfig(self): - if not os.path.exists(self.__cfg_file): - self.__config['orders'] = {} - self.__config['account'] = {} - self.__config['apis'] = {} - self.__config['email'] = mw.M('users').where( - 'id=?', (1,)).getField('email') - if self.__config['email'] in ['midoks@163.com']: - self.__config['email'] = None - self.saveConfig() - return self.__config - tmp_config = mw.readFile(self.__cfg_file) - if not tmp_config: - return self.__config - try: - self.__config = json.loads(tmp_config) - except: - self.saveConfig() - return self.__config - return self.__config - - def saveConfig(self): - # 写配置文件 - fp = open(self.__cfg_file, 'w+') - fcntl.flock(fp, fcntl.LOCK_EX) # 加锁 - fp.write(json.dumps(self.__config)) - fcntl.flock(fp, fcntl.LOCK_UN) # 解锁 - fp.close() - return True - - def createCertCron(self): - - if mw.isAppleSystem(): - print('在Macos上不支持创建证书自动续签任务!') - return '' - - # 创建证书自动续签任务 - try: - import crontab_api - api = crontab_api.crontab_api() - - echo = mw.md5(mw.md5('panel_renew_lets_cron')) - cron_id = mw.M('crontab').where('echo=?', (echo,)).getField('id') - - cron_path = mw.getServerDir() + '/cron' - if not os.path.exists(cron_path): - mw.execShell('mkdir -p ' + cron_path) - - shell = 'python3 -u {}/class/core/cert_api.py --renew=1'.format( - mw.getRunDir()) - - logs_file = cron_path + '/' + echo + '.log' - - cmd = '''#!/bin/bash -PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin -export PATH - -dst_dir=%s -logs_file=%s -cd $dst_dir - -if [ -f bin/activate ];then - source bin/activate -fi - -''' % (mw.getRunDir(), logs_file) - cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n" - cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n" - cmd += 'cd $dst_dir && ' + shell + ' >> $logs_file 2>&1' + "\n" - cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n" - cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n" - - file = cron_path + '/' + echo - - if type(cron_id) != int: - - mw.writeFile(file, cmd) - mw.execShell('chmod 750 ' + file) - - info = {} - info['type'] = 'day' - info['minute'] = '10' - info['hour'] = '0' - shell_cron, rinfo, name = api.getCrondCycle(info) - shell_cron += ' ' + cron_path + '/' + echo + \ - ' >> ' + logs_file + ' 2>&1' - - api.writeShell(shell_cron) - - insert_id = mw.M('crontab').add('name,type,where1,where_hour,where_minute,echo,addtime,status,save,backup_to,stype,sname,sbody,urladdress', ( - "[勿删]续签Let's Encrypt证书", 'day', '', '0', '10', echo, time.strftime('%Y-%m-%d %X', time.localtime()), '1', '', 'localhost', 'toShell', '', cmd, '')) - - if insert_id > 0: - print('创建证书自动续签任务成功!') - else: - mw.writeFile(file, cmd) - mw.execShell('chmod 750 ' + file) - mw.M('crontab').where('id=?', (cron_id)).save('sbody', (cmd,)) - except Exception as e: - print(mw.getTracebackInfo()) - - def getApis(self): - if not self.__apis: - # 尝试从配置文件中获取 - api_index = self.__mod_index[self.__debug] - if not 'apis' in self.__config: - self.__config['apis'] = {} - - if api_index in self.__config['apis']: - if 'expires' in self.__config['apis'][api_index] and 'directory' in self.__config['apis'][api_index]: - if time.time() < self.__config['apis'][api_index]['expires']: - self.__apis = self.__config[ - 'apis'][api_index]['directory'] - return self.__apis - - # 尝试从官方获取获取 - try: - res = mw.httpGet(self.__url) - result = json.loads(res) - self.__apis = {} - self.__apis['newAccount'] = result['newAccount'] - self.__apis['newNonce'] = result['newNonce'] - self.__apis['newOrder'] = result['newOrder'] - self.__apis['revokeCert'] = result['revokeCert'] - self.__apis['keyChange'] = result['keyChange'] - - # 保存到配置文件 - self.__config['apis'][api_index] = {} - self.__config['apis'][api_index]['directory'] = self.__apis - self.__config['apis'][api_index]['expires'] = time.time() + \ - 86400 # 24小时后过期 - self.saveConfig() - except Exception as e: - raise Exception('服务因维护而关闭或发生内部错误,查看 https://letsencrypt.status.io/ 了解更多详细信息。') - return self.__apis - - # 系列化payload - def stringfyItems(self, payload): - if isinstance(payload, str): - return payload - - for k, v in payload.items(): - if isinstance(k, bytes): - k = k.decode("utf-8") - if isinstance(v, bytes): - v = v.decode("utf-8") - payload[k] = v - return payload - - # 构造域名列表 - def formatDomains(self, domains): - if type(domains) != list: - return [] - # 是否自动构造通配符 - if self.__auto_wildcard: - domains = self.autoWildcard(domains) - wildcard = [] - tmp_domains = [] - for domain in domains: - domain = domain.strip() - if domain in tmp_domains: - continue - # 将通配符域名转为验证正则表达式 - f_index = domain.find("*.") - if f_index not in [-1, 0]: - continue - if f_index == 0: - wildcard.append(domain.replace( - "*", r"^[\w-]+").replace(".", r"\.")) - # 添加到申请列表 - tmp_domains.append(domain) - - # 处理通配符包含 - apply_domains = tmp_domains[:] - for domain in tmp_domains: - for w in wildcard: - if re.match(w, domain): - apply_domains.pop(domain) - - return apply_domains - - # 转为无填充的Base64 - def calculateSafeBase64(self, un_encoded_data): - if sys.version_info[0] == 3: - if isinstance(un_encoded_data, str): - un_encoded_data = un_encoded_data.encode("utf8") - r = base64.urlsafe_b64encode(un_encoded_data).rstrip(b"=") - return r.decode("utf8") - - # 创建Key - def createKey(self, key_type=OpenSSL.crypto.TYPE_RSA): - key = OpenSSL.crypto.PKey() - key.generate_key(key_type, self.__bits) - private_key = OpenSSL.crypto.dump_privatekey( - OpenSSL.crypto.FILETYPE_PEM, key) - return private_key - - # 获用户取密钥对 - def getAccountKey(self): - if not 'account' in self.__config: - self.__config['account'] = {} - k = self.__mod_index[self.__debug] - if not k in self.__config['account']: - self.__config['account'][k] = {} - - if not 'key' in self.__config['account'][k]: - self.__config['account'][k]['key'] = self.createKey() - if type(self.__config['account'][k]['key']) == bytes: - self.__config['account'][k]['key'] = self.__config[ - 'account'][k]['key'].decode() - self.saveConfig() - return self.__config['account'][k]['key'] - - # 注册acme帐户 - def register(self, existing=False): - if not 'email' in self.__config: - self.__config['email'] = 'mdioks@163.com' - if existing: - payload = {"onlyReturnExisting": True} - elif self.__config['email']: - payload = { - "termsOfServiceAgreed": True, - "contact": ["mailto:{0}".format(self.__config['email'])], - } - else: - payload = {"termsOfServiceAgreed": True} - - res = self.acmeRequest(url=self.__apis['newAccount'], payload=payload) - - if res.status not in [201, 200, 409]: - raise Exception("注册ACME帐户失败: {}".format(res.json())) - kid = res.headers["Location"] - return kid - - # 获取kid - def getKid(self, force=False): - # 如果配置文件中不存在kid或force = True时则重新注册新的acme帐户 - if not 'account' in self.__config: - self.__config['account'] = {} - k = self.__mod_index[self.__debug] - if not k in self.__config['account']: - self.__config['account'][k] = {} - - if not 'kid' in self.__config['account'][k]: - self.__config['account'][k]['kid'] = self.register() - self.saveConfig() - time.sleep(3) - self.__config = self.readConfig() - return self.__config['account'][k]['kid'] - - def requestsGet(self, url, timeout): - try: - import urllib.request - import ssl - try: - ssl._create_default_https_context = ssl._create_unverified_context - except: - pass - - headers = {"User-Agent": self.__user_agent} - req = urllib.request.Request(url=url, headers=headers) - req = urllib.request.urlopen(url, timeout=timeout) - return req - except Exception as ex: - raise Exception("requestsGet: {}".format(str(ex))) - - def requestsPost(self, url, data, timeout): - try: - import urllib.request - import ssl - try: - ssl._create_default_https_context = ssl._create_unverified_context - except: - pass - - headers = {"User-Agent": self.__user_agent} - headers.update({"Content-Type": "application/jose+json"}) - data = bytes(data, encoding="utf8") - req = urllib.request.Request( - url, data, headers=headers, method='POST') - response = urllib.request.urlopen(req, timeout=timeout) - return response - except Exception as ex: - # self.getError() - raise Exception("异常: {}".format(self.getError(str(ex)))) - - def getRequestJson(self, response): - try: - data = response.read().decode('utf-8') - return json.loads(data) - except Exception as ex: - raise Exception("getRequestJson: {}".format(str(ex))) - - def getNonce(self, force=False): - # 获取随机数 - # 如果没有保存上一次的随机数或force=True时则重新获取新的随机数 - if not self.__replay_nonce or force: - try: - response = self.requestsGet( - self.__apis['newNonce'], timeout=self.__acme_timeout) - self.__replay_nonce = response.headers["replay-nonce"] - except Exception as e: - raise Exception("获取随机数失败: {}".format(str(e))) - - return self.__replay_nonce - - # 获请ACME请求头 - def getAcmeHeader(self, url): - nonce = self.getNonce() - - header = {"alg": "RS256", "nonce": nonce, "url": url} - if url in [self.__apis['newAccount'], 'GET_THUMBPRINT']: - from cryptography.hazmat.backends import default_backend - from cryptography.hazmat.primitives import serialization - private_key = serialization.load_pem_private_key( - self.getAccountKey().encode(), - password=None, - backend=default_backend(), - ) - public_key_public_numbers = private_key.public_key().public_numbers() - - exponent = "{0:x}".format(public_key_public_numbers.e) - exponent = "0{0}".format(exponent) if len( - exponent) % 2 else exponent - modulus = "{0:x}".format(public_key_public_numbers.n) - jwk = { - "kty": "RSA", - "e": self.calculateSafeBase64(binascii.unhexlify(exponent)), - "n": self.calculateSafeBase64(binascii.unhexlify(modulus)), - } - header["jwk"] = jwk - else: - header["kid"] = self.getKid() - return header - - # 计算signature - def signMessage(self, message): - pk = OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, self.getAccountKey().encode()) - return OpenSSL.crypto.sign(pk, message.encode("utf8"), self.__digest) - - def getSiteRunPathByid(self, site_id): - if mw.M('sites').where('id=?', (site_id,)).count() >= 1: - site_path = mw.M('sites').where('id=?', site_id).getField('path') - if not site_path: - return None - if not os.path.exists(site_path): - return None - return site_path - else: - return False - - def getSiteRunPath(self, domains): - site_id = 0 - for domain in domains: - site_id = mw.M('domain').where("name=?", domain).getField('pid') - if site_id: - break - - if not site_id: - return None - return self.getSiteRunPathByid(site_id) - - # 清理验证文件 - def clearAuthFile(self, index): - if not self.__config['orders'][index]['auth_type'] in ['http', 'tls']: - return True - acme_path = '{}/.well-known/acme-challenge'.format( - self.__config['orders'][index]['auth_to']) - writeLog("|-验证目录:{}".format(acme_path)) - if os.path.exists(acme_path): - mw.execShell("rm -f {}/*".format(acme_path)) - - acme_path = mw.getServerDir() + '/stop/.well-known/acme-challenge' - if os.path.exists(acme_path): - mw.execShell("rm -f {}/*".format(acme_path)) - - # 获取域名验证方式 - def getAuthType(self, index): - if not index in self.__config['orders']: - raise Exception('指定订单不存在!') - s_type = 'http-01' - if 'auth_type' in self.__config['orders'][index]: - if self.__config['orders'][index]['auth_type'] == 'dns': - s_type = 'dns-01' - elif self.__config['orders'][index]['auth_type'] == 'tls': - s_type = 'tls-alpn-01' - else: - s_type = 'http-01' - return s_type - - # 构造验证信息 - def getIdentifierAuth(self, index, url, auth_info): - s_type = self.getAuthType(index) - writeLog("|-验证类型:{}".format(s_type)) - domain = auth_info['identifier']['value'] - wildcard = False - # 处理通配符 - if 'wildcard' in auth_info: - wildcard = auth_info['wildcard'] - if wildcard: - domain = "*." + domain - - for auth in auth_info['challenges']: - if auth['type'] != s_type: - continue - identifier_auth = { - "domain": domain, - "url": url, - "wildcard": wildcard, - "token": auth['token'], - "dns_challenge_url": auth['url'], - } - return identifier_auth - return None - - # 构造域名验证头和验证值 - def getKeyauthorization(self, token): - acme_header_jwk_json = json.dumps( - self.getAcmeHeader("GET_THUMBPRINT")["jwk"], sort_keys=True, separators=(",", ":") - ) - acme_thumbprint = self.calculateSafeBase64( - hashlib.sha256(acme_header_jwk_json.encode("utf8")).digest() - ) - acme_keyauthorization = "{0}.{1}".format(token, acme_thumbprint) - base64_of_acme_keyauthorization = self.calculateSafeBase64( - hashlib.sha256(acme_keyauthorization.encode("utf8")).digest() - ) - - return acme_keyauthorization, base64_of_acme_keyauthorization - - # 写验证文件 - def writeAuthFile(self, auth_to, token, acme_keyauthorization): - try: - # self.D('writeAuthFile', auth_to) - acme_path = '{}/.well-known/acme-challenge'.format(auth_to) - if not os.path.exists(acme_path): - os.makedirs(acme_path) - mw.setOwn(acme_path, 'www') - wellknown_path = '{}/{}'.format(acme_path, token) - mw.writeFile(wellknown_path, acme_keyauthorization) - mw.setOwn(wellknown_path, 'www') - - acme_path = mw.getServerDir() + '/stop/.well-known/acme-challenge' - if not os.path.exists(acme_path): - os.makedirs(acme_path) - mw.setOwn(acme_path, 'www') - wellknown_path = '{}/{}'.format(acme_path, token) - mw.writeFile(wellknown_path, acme_keyauthorization) - mw.setOwn(wellknown_path, 'www') - return True - except: - err = mw.getTracebackInfo() - raise Exception("写入验证文件失败: {}".format(err)) - - # 设置验证信息 - def setAuthInfo(self, identifier_auth): - - # 是否手动验证DNS - if identifier_auth['auth_to'] == 'dns': - return None - - # 是否文件验证 - if identifier_auth['type'] in ['http', 'tls']: - self.writeAuthFile(identifier_auth['auth_to'], identifier_auth[ - 'token'], identifier_auth['acme_keyauthorization']) - - # 获取验证信息 - def getAuths(self, index): - if not index in self.__config['orders']: - raise Exception('指定订单不存在!') - - # 检查是否已经获取过授权信息 - if 'auths' in self.__config['orders'][index]: - # 检查授权信息是否过期 - if time.time() < self.__config['orders'][index]['auths'][0]['expires']: - return self.__config['orders'][index]['auths'] - if self.__config['orders'][index]['auth_type'] != 'dns': - site_run_path = self.getSiteRunPath( - self.__config['orders'][index]['domains']) - if site_run_path: - self.__config['orders'][index]['auth_to'] = site_run_path - - # 清理旧验证 - self.clearAuthFile(index) - - auths = [] - for auth_url in self.__config['orders'][index]['authorizations']: - res = self.acmeRequest(auth_url, "") - if res.status not in [200, 201]: - raise Exception("获取授权失败: {}".format(res.json())) - - s_body = self.getRequestJson(res) - if 'status' in s_body: - if s_body['status'] in ['invalid']: - raise Exception("无效订单,此订单当前为验证失败状态!") - if s_body['status'] in ['valid']: # 跳过无需验证的域名 - continue - - s_body['expires'] = self.utcToTime(s_body['expires']) - identifier_auth = self.getIdentifierAuth(index, auth_url, s_body) - if not identifier_auth: - raise Exception("验证信息构造失败!{}") - - acme_keyauthorization, auth_value = self.getKeyauthorization(identifier_auth[ - 'token']) - identifier_auth['acme_keyauthorization'] = acme_keyauthorization - identifier_auth['auth_value'] = auth_value - identifier_auth['expires'] = s_body['expires'] - identifier_auth['auth_to'] = self.__config[ - 'orders'][index]['auth_to'] - identifier_auth['type'] = self.__config[ - 'orders'][index]['auth_type'] - - # print(identifier_auth) - # 设置验证信息 - self.setAuthInfo(identifier_auth) - auths.append(identifier_auth) - self.__config['orders'][index]['auths'] = auths - self.saveConfig() - return auths - - # 更新随机数 - def updateReplayNonce(self, res): - replay_nonce = res.headers.get('replay-nonce') - if replay_nonce: - self.__replay_nonce = replay_nonce - - # 请求到ACME接口 - def acmeRequest(self, url, payload): - headers = {"User-Agent": self.__user_agent} - payload = self.stringfyItems(payload) - - if payload == "": - payload64 = payload - else: - payload64 = self.calculateSafeBase64(json.dumps(payload)) - protected = self.getAcmeHeader(url) - protected64 = self.calculateSafeBase64(json.dumps(protected)) - signature = self.signMessage( - message="{0}.{1}".format(protected64, payload64)) # bytes - signature64 = self.calculateSafeBase64(signature) # str - data = json.dumps({ - "protected": protected64, - "payload": payload64, - "signature": signature64 - }) - - headers.update({"Content-Type": "application/jose+json"}) - response = self.requestsPost( - url, data=data, timeout=self.__acme_timeout) - # 更新随机数 - self.updateReplayNonce(response) - return response - - # UTC时间转时间戳 - def utcToTime(self, utc_string): - try: - utc_string = utc_string.split('.')[0] - utc_date = datetime.datetime.strptime( - utc_string, "%Y-%m-%dT%H:%M:%S") - # 按北京时间返回 - return int(time.mktime(utc_date.timetuple())) + (3600 * 8) - except: - return int(time.time() + 86400 * 7) - - # 保存订单 - def saveOrder(self, order_object, index): - if not 'orders' in self.__config: - self.__config['orders'] = {} - renew = False - if not index: - index = mw.md5(json.dumps(order_object['identifiers'])) - else: - renew = True - order_object['certificate_url'] = self.__config[ - 'orders'][index]['certificate_url'] - order_object['save_path'] = self.__config[ - 'orders'][index]['save_path'] - - order_object['expires'] = self.utcToTime(order_object['expires']) - self.__config['orders'][index] = order_object - self.__config['orders'][index]['index'] = index - if not renew: - self.__config['orders'][index]['create_time'] = int(time.time()) - self.__config['orders'][index]['renew_time'] = 0 - self.saveConfig() - return index - - def getError(self, error): - # 格式化错误输出 - if error.find("Max checks allowed") >= 0: - return "CA无法验证您的域名,请检查域名解析是否正确,或等待5-10分钟后重试." - elif error.find("Max retries exceeded with") >= 0 or error.find('status_code=0 ') != -1: - return "CA服务器连接超时,请稍候重试." - elif error.find("The domain name belongs") >= 0: - return "域名不属于此DNS服务商,请确保域名填写正确." - elif error.find('login token ID is invalid') >= 0: - return 'DNS服务器连接失败,请检查密钥是否正确.' - elif error.find('Error getting validation data') != -1: - return '数据验证失败,CA无法从验证连接中获到正确的验证码.' - elif "too many certificates already issued for exact set of domains" in error: - return '签发失败,该域名%s超出了每周的重复签发次数限制!' % re.findall("exact set of domains: (.+):", error) - elif "Error creating new account :: too many registrations for this IP" in error: - return '签发失败,当前服务器IP已达到每3小时最多创建10个帐户的限制.' - elif "DNS problem: NXDOMAIN looking up A for" in error: - return '验证失败,没有解析域名,或解析未生效!' - elif "Invalid response from" in error: - return '验证失败,域名解析错误或验证URL无法被访问!' - elif error.find('TLS Web Server Authentication') != -1: - return "连接CA服务器失败,请稍候重试." - elif error.find('Name does not end in a public suffix') != -1: - return "不支持的域名%s,请检查域名是否正确!" % re.findall("Cannot issue for \"(.+)\":", error) - elif error.find('No valid IP addresses found for') != -1: - return "域名%s没有找到解析记录,请检查域名是否解析生效!" % re.findall("No valid IP addresses found for (.+)", error) - elif error.find('No TXT record found at') != -1: - return "没有在域名%s中找到有效的TXT解析记录,请检查是否正确解析TXT记录,如果是DNSAPI方式申请的,请10分钟后重试!" % re.findall("No TXT record found at (.+)", error) - elif error.find('Incorrect TXT record') != -1: - return "在%s上发现错误的TXT记录:%s,请检查TXT解析是否正确,如果是DNSAPI方式申请的,请10分钟后重试!" % (re.findall("found at (.+)", error), re.findall("Incorrect TXT record \"(.+)\"", error)) - elif error.find('Domain not under you or your user') != -1: - return "这个dnspod账户下面不存在这个域名,添加解析失败!" - elif error.find('SERVFAIL looking up TXT for') != -1: - return "没有在域名%s中找到有效的TXT解析记录,请检查是否正确解析TXT记录,如果是DNSAPI方式申请的,请10分钟后重试!" % re.findall("looking up TXT for (.+)", error) - elif error.find('Timeout during connect') != -1: - return "连接超时,CA服务器无法访问您的网站!" - elif error.find("DNS problem: SERVFAIL looking up CAA for") != -1: - return "域名%s当前被要求验证CAA记录,请手动解析CAA记录,或1小时后重新尝试申请!" % re.findall("looking up CAA for (.+)", error) - elif error.find("Read timed out.") != -1: - return "验证超时,请检查域名是否正确解析,若已正确解析,可能服务器与Let'sEncrypt连接异常,请稍候再重试!" - elif error.find('Cannot issue for') != -1: - return "无法为{}颁发证书,不能直接用域名后缀申请通配符证书!".format(re.findall(r'for\s+"(.+)"', error)) - elif error.find('too many failed authorizations recently'): - return '该帐户1小时内失败的订单次数超过5次,请等待1小时再重试!' - elif error.find("Error creating new order") != -1: - return "订单创建失败,请稍候重试!" - elif error.find("Too Many Requests") != -1: - return "1小时内超过5次验证失败,暂时禁止申请,请稍候重试!" - elif error.find('HTTP Error 400: Bad Request') != -1: - return "CA服务器拒绝访问,请稍候重试!" - elif error.find('Temporary failure in name resolution') != -1: - return '服务器DNS故障,无法解析域名,请使用Linux工具箱检查dns配置' - elif error.find('Too Many Requests') != -1: - return '该域名请求申请次数过多,请3小时后重试' - else: - return error - - # 创建订单 - def createOrder(self, domains, auth_type, auth_to, index=None): - domains = self.formatDomains(domains) - if not domains: - raise Exception("至少需要有一个域名") - # 构造标识 - identifiers = [] - for domain_name in domains: - identifiers.append({"type": 'dns', "value": domain_name}) - payload = {"identifiers": identifiers} - - # 请求创建订单 - res = self.acmeRequest(self.__apis['newOrder'], payload) - if not res.status in [201]: # 如果创建失败 - e_body = self.getRequestJson(res) - if 'type' in e_body: - # 如果随机数失效 - if e_body['type'].find('error:badNonce') != -1: - self.getNonce(force=True) - res = self.acmeRequest(self.__apis['newOrder'], payload) - - # 如果帐户失效 - if e_body['detail'].find('KeyID header contained an invalid account URL') != -1: - k = self._mod_index[self.__debug] - del(self.__config['account'][k]) - self.getKid() - self.getNonce(force=True) - res = self.acmeRequest(self.__apis['newOrder'], payload) - if not res.status in [201]: - a_auth = e_body - - ret_title = self.getError(str(a_auth)) - raise StopIteration("{0} >>>> {1}".format( - ret_title, json.dumps(a_auth))) - - # 返回验证地址和验证 - s_json = self.getRequestJson(res) - s_json['auth_type'] = auth_type - s_json['domains'] = domains - s_json['auth_to'] = auth_to - index = self.saveOrder(s_json, index) - return index - - # 检查验证状态 - def checkAuthStatus(self, url, desired_status=None): - - desired_status = desired_status or ["pending", "valid", "invalid"] - number_of_checks = 0 - rdata = None - while True: - if desired_status == ['valid', 'invalid']: - writeLog("|-第{}次查询验证结果..".format(number_of_checks + 1)) - time.sleep(self.__wait_time) - check_authorization_status_response = self.acmeRequest(url, "") - a_auth = rdata = self.getRequestJson( - check_authorization_status_response) - authorization_status = a_auth["status"] - number_of_checks += 1 - if authorization_status in desired_status: - if authorization_status == "invalid": - writeLog("|-验证失败!") - try: - if 'error' in a_auth['challenges'][0]: - ret_title = a_auth['challenges'][ - 0]['error']['detail'] - elif 'error' in a_auth['challenges'][1]: - ret_title = a_auth['challenges'][ - 1]['error']['detail'] - elif 'error' in a_auth['challenges'][2]: - ret_title = a_auth['challenges'][ - 2]['error']['detail'] - else: - ret_title = str(a_auth) - ret_title = self.getError(ret_title) - except: - ret_title = str(a_auth) - raise StopIteration("{0} >>>> {1}".format( - ret_title, json.dumps(a_auth))) - break - - if number_of_checks == self.__max_check_num: - raise StopIteration( - "错误:已尝试验证{0}次. 最大验证次数为{1}. 验证时间间隔为{2}秒.".format( - number_of_checks, - self.__max_check_num, - self.__wait_time - ) - ) - if desired_status == ['valid', 'invalid']: - writeLog("|-验证成功!") - return rdata - - # 发送验证请求 - def respondToChallenge(self, auth): - payload = {"keyAuthorization": "{0}".format( - auth['acme_keyauthorization'])} - respond_to_challenge_response = self.acmeRequest( - auth['dns_challenge_url'], payload) - return respond_to_challenge_response - - # 检查DNS记录 - def checkDns(self, domain, value, s_type='TXT'): - writeLog( - "|-尝试本地验证DNS记录,域名: {} , 类型: {} 记录值: {}".format(domain, s_type, value)) - time.sleep(10) - n = 0 - while n < 20: - n += 1 - try: - import dns.resolver - ns = dns.resolver.query(domain, s_type) - for j in ns.response.answer: - for i in j.items: - txt_value = i.to_text().replace('"', '').strip() - writeLog("|-第 {} 次验证值: {}".format(n, txt_value)) - if txt_value == value: - write_log("|-本地验证成功!") - return True - except: - try: - import dns.resolver - except: - return False - time.sleep(3) - writeLog("|-本地验证失败!") - return True - - def authDomain(self, index): - if not index in self.__config['orders']: - raise Exception('指定订单不存在!') - - # 开始验证 - for auth in self.__config['orders'][index]['auths']: - res = self.checkAuthStatus(auth['url']) # 检查是否需要验证 - if res['status'] == 'pending': - if auth['type'] == 'dns': # 尝试提前验证dns解析 - self.checkDns( - "_acme-challenge.{}".format( - auth['domain'].replace('*.', '')), - auth['auth_value'], - "TXT" - ) - self.respondToChallenge(auth) - - # 检查验证结果 - for i in range(len(self.__config['orders'][index]['auths'])): - self.checkAuthStatus(self.__config['orders'][index]['auths'][i]['url'], [ - 'valid', 'invalid']) - self.__config['orders'][index]['status'] = 'valid' - - # 构造可选名称 - def getAltNames(self, index): - domain_name = self.__config['orders'][index]['domains'][0] - domain_alt_names = [] - if len(self.__config['orders'][index]['domains']) > 1: - domain_alt_names = self.__config['orders'][index]['domains'][1:] - return domain_name, domain_alt_names - - # 创建CSR - def createCsr(self, index): - if 'csr' in self.__config['orders'][index]: - return self.__config['orders']['csr'] - domain_name, domain_alt_names = self.getAltNames(index) - X509Req = OpenSSL.crypto.X509Req() - X509Req.get_subject().CN = domain_name - if domain_alt_names: - SAN = "DNS:{0}, ".format(domain_name).encode("utf8") + ", ".join( - "DNS:" + i for i in domain_alt_names - ).encode("utf8") - else: - SAN = "DNS:{0}".format(domain_name).encode("utf8") - - X509Req.add_extensions( - [ - OpenSSL.crypto.X509Extension( - "subjectAltName".encode("utf8"), critical=False, value=SAN - ) - ] - ) - pk = OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, self.createCertificateKey( - index).encode() - ) - X509Req.set_pubkey(pk) - # X509Req.set_version(2) - X509Req.set_version(0) - X509Req.sign(pk, self.__digest) - return OpenSSL.crypto.dump_certificate_request(OpenSSL.crypto.FILETYPE_ASN1, X509Req) - - # 获取证书密钥对 - def createCertificateKey(self, index): - # 判断是否已经创建private_key - if 'private_key' in self.__config['orders'][index]: - return self.__config['orders'][index]['private_key'] - # 创建新的私钥 - private_key = self.createKey() - if type(private_key) == bytes: - private_key = private_key.decode() - # 保存私钥到订单配置文件 - self.__config['orders'][index]['private_key'] = private_key - self.saveConfig() - return private_key - - # 发送CSR - def sendCsr(self, index): - csr = self.createCsr(index) - payload = {"csr": self.calculateSafeBase64(csr)} - send_csr_response = self.acmeRequest( - url=self.__config['orders'][index]['finalize'], payload=payload) - send_csr_response_json = self.getRequestJson(send_csr_response) - if send_csr_response.status not in [200, 201]: - raise ValueError( - "错误: 发送CSR: 响应状态{status_code} 响应值:{response}".format( - status_code=send_csr_response.status, - response=send_csr_response_json, - ) - ) - certificate_url = send_csr_response_json["certificate"] - self.__config['orders'][index]['certificate_url'] = certificate_url - self.saveConfig() - return certificate_url - - # 转换时间 - def strfDate(self, sdate): - return time.strftime('%Y-%m-%d', time.strptime(sdate, '%Y%m%d%H%M%S')) - - # 证书转为DER - def dumpDer(self, cert_path): - cert = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, mw.readFile(cert_path + '/cert.csr')) - return OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, cert) - - # 证书转为pkcs12 - def dumpPkcs12(self, key_pem=None, cert_pem=None, ca_pem=None, friendly_name=None): - p12 = OpenSSL.crypto.PKCS12() - if cert_pem: - p12.set_certificate(OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, cert_pem.encode())) - if key_pem: - p12.set_privatekey(OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, key_pem.encode())) - if ca_pem: - p12.set_ca_certificates((OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, ca_pem.encode()),)) - if friendly_name: - p12.set_friendlyname(friendly_name.encode()) - return p12.export() - - # 拆分根证书 - def splitCaData(self, cert): - sp_key = '-----END CERTIFICATE-----\n' - datas = cert.split(sp_key) - return {"cert": datas[0] + sp_key, "root": sp_key.join(datas[1:])} - - # 获取指定证书基本信息 - def getCertInit(self, pem_file): - if not os.path.exists(pem_file): - return None - try: - result = {} - x509 = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, mw.readFile(pem_file)) - # 取产品名称 - issuer = x509.get_issuer() - result['issuer'] = '' - if hasattr(issuer, 'CN'): - result['issuer'] = issuer.CN - if not result['issuer']: - is_key = [b'0', '0'] - issue_comp = issuer.get_components() - if len(issue_comp) == 1: - is_key = [b'CN', 'CN'] - for iss in issue_comp: - if iss[0] in is_key: - result['issuer'] = iss[1].decode() - break - # 取到期时间 - result['notAfter'] = self.strfDate( - bytes.decode(x509.get_notAfter())[:-1]) - # 取申请时间 - result['notBefore'] = self.strfDate( - bytes.decode(x509.get_notBefore())[:-1]) - # 取可选名称 - result['dns'] = [] - for i in range(x509.get_extension_count()): - s_name = x509.get_extension(i) - if s_name.get_short_name() in [b'subjectAltName', 'subjectAltName']: - s_dns = str(s_name).split(',') - for d in s_dns: - result['dns'].append(d.split(':')[1]) - subject = x509.get_subject().get_components() - # 取主要认证名称 - if len(subject) == 1: - result['subject'] = subject[0][1].decode() - else: - result['subject'] = result['dns'][0] - return result - except: - return None - - # 替换服务器上的同域名同品牌证书 - def subAllCert(self, key_file, pem_file): - cert_init = self.getCertInit(pem_file) # 获取新证书的基本信息 - paths = ['/www/server/mdserver-web/data/letsencrypt'] - is_panel = False - for path in paths: - if not os.path.exists(path): - continue - for p_name in os.listdir(path): - to_path = path + '/' + p_name - to_pem_file = to_path + '/fullchain.pem' - to_key_file = to_path + '/privkey.pem' - to_info = to_path + '/info.json' - # 判断目标证书是否存在 - if not os.path.exists(to_pem_file): - if not p_name in ['ssl']: - continue - to_pem_file = to_path + '/certificate.pem' - to_key_file = to_path + '/privateKey.pem' - if not os.path.exists(to_pem_file): - continue - if path == paths[-1]: - is_panel = True - # 获取目标证书的基本信息 - to_cert_init = self.getCertInit(to_pem_file) - # 判断证书品牌是否一致 - try: - if to_cert_init['issuer'] != cert_init['issuer'] and to_cert_init['issuer'].find("Let's Encrypt") == -1 and to_cert_init['issuer'] != 'R3': - continue - except: - continue - # 判断目标证书的到期时间是否较早 - if to_cert_init['notAfter'] > cert_init['notAfter']: - continue - # 判断认识名称是否一致 - if len(to_cert_init['dns']) != len(cert_init['dns']): - continue - is_copy = True - for domain in to_cert_init['dns']: - if not domain in cert_init['dns']: - is_copy = False - if not is_copy: - continue - - # 替换新的证书文件和基本信息 - mw.writeFile(to_pem_file, mw.readFile(pem_file, 'rb'), 'wb') - mw.writeFile(to_key_file, mw.readFile(key_file, 'rb'), 'wb') - mw.writeFile(to_info, json.dumps(cert_init)) - writeLog("|-检测到{}下的证书与本次申请的证书重叠,且到期时间较早,已替换为新证书!".format(to_path)) - # 重载web服务 - mw.restartWeb() - - # 保存证书到文件 - def saveCert(self, cert, index): - try: - domain_name = self.__config['orders'][index]['domains'][0] - path = self.__config['orders'][index]['save_path'] - if not os.path.exists(path): - os.makedirs(path, 384) - - # 存储证书 - key_file = path + "/privkey.pem" - pem_file = path + "/fullchain.pem" - mw.writeFile(key_file, cert['private_key']) - mw.writeFile(pem_file, cert['cert'] + cert['root']) - mw.writeFile(path + "/cert.csr", cert['cert']) - mw.writeFile(path + "/root_cert.csr", cert['root']) - - # 转为IIS证书 - pfx_buffer = self.dumpPkcs12( - cert['private_key'], cert['cert'] + cert['root'], cert['root'], domain_name) - mw.writeFile(path + "/fullchain.pfx", pfx_buffer, 'wb+') - - ps = '''文件说明: -privkey.pem 证书私钥 -fullchain.pem 包含证书链的PEM格式证书(nginx/apache) -root_cert.csr 根证书 -cert.csr 域名证书 -fullchain.pfx 用于IIS的证书格式 - -如何在MW面板使用: -privkey.pem 粘贴到密钥输入框 -fullchain.pem 粘贴到证书输入框 -''' - mw.writeFile(path + '/readme.txt', ps) - self.subAllCert(key_file, pem_file) - except: - writeLog(mw.getTracebackInfo()) - - # 获取证书到期时间 - def getCertTimeout(self, cret_data): - try: - x509 = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, cret_data) - cert_timeout = bytes.decode(x509.get_notAfter())[:-1] - return int(time.mktime(time.strptime(cert_timeout, '%Y%m%d%H%M%S'))) - except: - return int(time.time() + (86400 * 90)) - - # 下载证书 - def downloadCert(self, index): - res = self.acmeRequest( - self.__config['orders'][index]['certificate_url'], "") - - pem_certificate = res.read().decode('utf-8') - if res.status not in [200, 201]: - raise Exception("下载证书失败: {}".format(json.loads(pem_certificate))) - - cert = self.splitCaData(pem_certificate) - cert['cert_timeout'] = self.getCertTimeout(cert['cert']) - cert['private_key'] = self.__config['orders'][index]['private_key'] - cert['domains'] = self.__config['orders'][index]['domains'] - del(self.__config['orders'][index]['private_key']) - del(self.__config['orders'][index]['auths']) - del(self.__config['orders'][index]['expires']) - del(self.__config['orders'][index]['authorizations']) - del(self.__config['orders'][index]['finalize']) - del(self.__config['orders'][index]['identifiers']) - if 'cert' in self.__config['orders'][index]: - del(self.__config['orders'][index]['cert']) - self.__config['orders'][index]['status'] = 'valid' - self.__config['orders'][index]['cert_timeout'] = cert['cert_timeout'] - domain_name = self.__config['orders'][index]['domains'][0] - self.__config['orders'][index]['save_path'] = '{}/{}'.format( - self.__save_path, domain_name) - cert['save_path'] = self.__config['orders'][index]['save_path'] - self.saveConfig() - self.saveCert(cert, index) - return cert - - def applyCert(self, domains, auth_type='http', auth_to='Dns_com|None|None', args={}): - writeLog("", "wb+") - try: - self.getApis() - index = None - if 'index' in args and args.index: - index = args.index - if not index: - # 判断是否只想验证域名 - writeLog("|-正在创建订单..") - index = self.createOrder(domains, auth_type, auth_to) - writeLog("|-正在获取验证信息..") - self.getAuths(index) - if auth_to == 'dns' and len(self.__config['orders'][index]['auths']) > 0: - return self.__config['orders'][index] - writeLog("|-正在验证域名..") - self.authDomain(index) - - writeLog("|-正在发送CSR..") - self.sendCsr(index) - - writeLog("|-正在下载证书..") - cert = self.downloadCert(index) - - # 保存证书配置 - self.saveConfig() - cert['status'] = True - cert['msg'] = '申请成功!' - writeLog("|-申请成功,正在部署到站点..") - - self.clearAuthFile(index) - - if os.path.exists(auth_to): - mw.execShell("rm -rf {}/.well-known".format(auth_to)) - return cert - except Exception as ex: - ex = str(ex) - if ex.find(">>>>") != -1: - msg = ex.split(">>>>") - msg[1] = json.loads(msg[1]) - else: - msg = ex - writeLog(mw.getTracebackInfo()) - - cert = {} - cert['status'] = False - cert['msg'] = msg - return cert - - # 取根域名和记录值 - def extractZone(self, domain_name): - top_domain_list = ['.ac.cn', '.ah.cn', '.bj.cn', '.com.cn', '.cq.cn', '.fj.cn', '.gd.cn', '.gov.cn', '.gs.cn', - '.gx.cn', '.gz.cn', '.ha.cn', '.hb.cn', '.he.cn', '.hi.cn', '.hk.cn', '.hl.cn', '.hn.cn', - '.jl.cn', '.js.cn', '.jx.cn', '.ln.cn', '.mo.cn', '.net.cn', '.nm.cn', '.nx.cn', '.org.cn', - '.my.id', '.com.ac', '.com.ad', '.com.ae', '.com.af', '.com.ag', '.com.ai', '.com.al', '.com.am', - '.com.an', '.com.ao', '.com.aq', '.com.ar', '.com.as', '.com.as', '.com.at', '.com.au', '.com.aw', - '.com.az', '.com.ba', '.com.bb', '.com.bd', '.com.be', '.com.bf', '.com.bg', '.com.bh', '.com.bi', - '.com.bj', '.com.bm', '.com.bn', '.com.bo', '.com.br', '.com.bs', '.com.bt', '.com.bv', '.com.bw', - '.com.by', '.com.bz', '.com.ca', '.com.ca', '.com.cc', '.com.cd', '.com.cf', '.com.cg', '.com.ch', - '.com.ci', '.com.ck', '.com.cl', '.com.cm', '.com.cn', '.com.co', '.com.cq', '.com.cr', '.com.cu', - '.com.cv', '.com.cx', '.com.cy', '.com.cz', '.com.de', '.com.dj', '.com.dk', '.com.dm', '.com.do', - '.com.dz', '.com.ec', '.com.ee', '.com.eg', '.com.eh', '.com.es', '.com.et', '.com.eu', '.com.ev', - '.com.fi', '.com.fj', '.com.fk', '.com.fm', '.com.fo', '.com.fr', '.com.ga', '.com.gb', '.com.gd', - '.com.ge', '.com.gf', '.com.gh', '.com.gi', '.com.gl', '.com.gm', '.com.gn', '.com.gp', '.com.gr', - '.com.gt', '.com.gu', '.com.gw', '.com.gy', '.com.hm', '.com.hn', '.com.hr', '.com.ht', '.com.hu', - '.com.id', '.com.id', '.com.ie', '.com.il', '.com.il', '.com.in', '.com.io', '.com.iq', '.com.ir', - '.com.is', '.com.it', '.com.jm', '.com.jo', '.com.jp', '.com.ke', '.com.kg', '.com.kh', '.com.ki', - '.com.km', '.com.kn', '.com.kp', '.com.kr', '.com.kw', '.com.ky', '.com.kz', '.com.la', '.com.lb', - '.com.lc', '.com.li', '.com.lk', '.com.lr', '.com.ls', '.com.lt', '.com.lu', '.com.lv', '.com.ly', - '.com.ma', '.com.mc', '.com.md', '.com.me', '.com.mg', '.com.mh', '.com.ml', '.com.mm', '.com.mn', - '.com.mo', '.com.mp', '.com.mq', '.com.mr', '.com.ms', '.com.mt', '.com.mv', '.com.mw', '.com.mx', - '.com.my', '.com.mz', '.com.na', '.com.nc', '.com.ne', '.com.nf', '.com.ng', '.com.ni', '.com.nl', - '.com.no', '.com.np', '.com.nr', '.com.nr', '.com.nt', '.com.nu', '.com.nz', '.com.om', '.com.pa', - '.com.pe', '.com.pf', '.com.pg', '.com.ph', '.com.pk', '.com.pl', '.com.pm', '.com.pn', '.com.pr', - '.com.pt', '.com.pw', '.com.py', '.com.qa', '.com.re', '.com.ro', '.com.rs', '.com.ru', '.com.rw', - '.com.sa', '.com.sb', '.com.sc', '.com.sd', '.com.se', '.com.sg', '.com.sh', '.com.si', '.com.sj', - '.com.sk', '.com.sl', '.com.sm', '.com.sn', '.com.so', '.com.sr', '.com.st', '.com.su', '.com.sy', - '.com.sz', '.com.tc', '.com.td', '.com.tf', '.com.tg', '.com.th', '.com.tj', '.com.tk', '.com.tl', - '.com.tm', '.com.tn', '.com.to', '.com.tp', '.com.tr', '.com.tt', '.com.tv', '.com.tw', '.com.tz', - '.com.ua', '.com.ug', '.com.uk', '.com.uk', '.com.us', '.com.uy', '.com.uz', '.com.va', '.com.vc', - '.com.ve', '.com.vg', '.com.vn', '.com.vu', '.com.wf', '.com.ws', '.com.ye', '.com.za', '.com.zm', - '.com.zw', '.mil.cn', '.qh.cn', '.sc.cn', '.sd.cn', '.sh.cn', '.sx.cn', '.tj.cn', '.tw.cn', '.tw.cn', - '.xj.cn', '.xz.cn', '.yn.cn', '.zj.cn', '.bj.cn', '.edu.kg' - ] - old_domain_name = domain_name - top_domain = "." + ".".join(domain_name.rsplit('.')[-2:]) - new_top_domain = "." + top_domain.replace(".", "") - is_tow_top = False - if top_domain in top_domain_list: - is_tow_top = True - domain_name = domain_name[:-len(top_domain)] + new_top_domain - - if domain_name.count(".") > 1: - zone, middle, last = domain_name.rsplit(".", 2) - if is_tow_top: - last = top_domain[1:] - root = ".".join([middle, last]) - else: - zone = "" - root = old_domain_name - return root, zone - - # 外部API - START ---------------------------------------------------------- - def getHostConf(self, siteName): - return mw.getServerDir() + '/web_conf/nginx/vhost/' + siteName + '.conf' - - def getSitePath(self, siteName): - file = self.getHostConf(siteName) - if os.path.exists(file): - conf = mw.readFile(file) - rep = r'\s*root\s*(.+);' - path = re.search(rep, conf).groups()[0] - return path - return '' - - def applyCertApi(self, args): - ''' - 申请证书 - api - ''' - return self.applyCert(args['domains'], args['auth_type'], args['auth_to']) - # 外部API - END ---------------------------------------------------------- - - def getSiteNameByDomains(self, domains): - # 通过域名获取网站名称 - sql = mw.M('domain') - site_sql = mw.M('sites') - siteName = None - for domain in domains: - pid = sql.where('name=?', (domain,)).getField('pid') - if pid: - siteName = site_sql.where('id=?', (pid,)).getField('name') - break - return siteName - - def renewCertTo(self, domains, auth_type, auth_to, index=None): - site_name = None - cert = {} - - import site_api - api = site_api.site_api() - if os.path.exists(auth_to): - if mw.M('sites').where('path=?', auth_to).count() == 1: - site_id = m.M('sites').where('path=?', auth_to).getField('id') - site_name = m.M('sites').where( - 'path=?', auth_to).getField('name') - - rdata = api.getSiteRunPath(site_id) - runPath = rdata['runPath'] - if runPath and not runPath in ['/']: - path = auth_to + '/' + runPath - if os.path.exists(path): - auth_to = path.replace('//', '/') - - else: - site_name = self.getSiteNameByDomains(domains) - is_rep = api.httpToHttps(site_name) - api.operateProxyConf(site_name,'stop') - mw.restartWeb() - try: - index = self.createOrder( - domains, - auth_type, - auth_to.replace('//', '/'), - index - ) - - writeLog("|-正在获取验证信息..") - self.getAuths(index) - writeLog("|-正在验证域名..") - self.authDomain(index) - writeLog("|-正在发送CSR..") - self.sendCsr(index) - writeLog("|-正在下载证书..") - cert = self.downloadCert(index) - self.__config['orders'][index]['renew_time'] = int(time.time()) - - # 清理失败重试记录 - self.__config['orders'][index]['retry_count'] = 0 - self.__config['orders'][index]['next_retry_time'] = 0 - - # 保存证书配置 - self.saveConfig() - cert['status'] = True - cert['msg'] = '续签成功!' - writeLog("|-续签成功!") - except Exception as e: - if str(e).find('429') > -1: - msg = '至少超过7天,才能续期!' - writeLog("|-" + msg) - return mw.returnJson(False, msg) - - if str(e).find('请稍候重试') == -1: # 受其它证书影响和连接CA失败的的不记录重试次数 - if index: - # 设置下次重试时间 - self.__config['orders'][index][ - 'next_retry_time'] = int(time.time() + (86400 * 2)) - # 记录重试次数 - if not 'retry_count' in self.__config['orders'][index].keys(): - self.__config['orders'][index]['retry_count'] = 1 - self.__config['orders'][index]['retry_count'] += 1 - # 保存证书配置 - self.saveConfig() - msg = str(e).split('>>>>')[0] - writeLog("|-" + msg) - return mw.returnJson(False, msg) - finally: - is_rep_decode = json.loads(is_rep) - if is_rep_decode['status']: - api.closeToHttps(site_name) - - api.operateProxyConf(site_name,'start') - mw.restartWeb() - writeLog("-" * 70) - return cert - - def checkDomainIsExsits(self, domain): - if mw.M('domain').where("name=?", (domain,)).count() > 0: - return True - - if mw.M('sites').where("name=?", (domain,)).count() > 0: - return True - - if mw.M('binding').where("name=?", (domain,)).count() > 0: - return True - return False - - def renewCert(self, index): - writeLog("", "wb+") - # self.D('renew_cert', index) - try: - order_index = [] - if index: - if type(index) != str: - index = index.index - if not index in self.__config['orders']: - raise Exception("指定订单号不存在,无法续签!") - order_index.append(index) - else: - # 测试一天过期 - now_time = time.time() - # print(self.__config) - if not 'orders' in self.__config: - self.__config['orders'] = {} - - for i in self.__config['orders'].keys(): - # print(self.__config['orders'][i]) - if not 'save_path' in self.__config['orders'][i]: - continue - - if 'cert' in self.__config['orders'][i]: - self.__config['orders'][i]['cert_timeout'] = self.__config['orders'][i]['cert']['cert_timeout'] - - if not 'cert_timeout' in self.__config['orders'][i]: - self.__config['orders'][i]['cert_timeout'] = int(time.time()) - - # print(self.__config['orders'][i]['domains'][0], (self.__config['orders'][i]['cert_timeout'] - now_time)/86400, now_time) - if self.__config['orders'][i]['cert_timeout'] - now_time > 83*86400: - msg = "|-本次跳过域名: {},未过期!".format(self.__config['orders'][i]['domains'][0]) - writeLog(msg) - continue - - # 已删除的网站直接跳过续签 - if self.__config['orders'][i]['auth_to'].find('|') == -1 and self.__config['orders'][i]['auth_to'].find('/') != -1: - if not os.path.exists(self.__config['orders'][i]['auth_to']): - auth_to = self.__config['orders'][i]['auth_to'] - if not auth_to: - continue - - # 域名不存在? - for domain in self.__config['orders'][i]['domains']: - if domain.find('*') != -1: - break - - - if not mw.M('domain').where("name=?", (domain,)).count() and not mw.M('binding').where("domain=?", domain).count(): - auth_to = None - msg = "|-跳过被删除的域名: {}".format( - self.__config['orders'][i]['domains']) - writeLog(msg) - if not auth_to: - continue - - self.__config['orders'][i]['auth_to'] = auth_to - - auth_to = True - for domain in self.__config['orders'][i]['domains']: - if not self.checkDomainIsExsits(domain): - auth_to = False - msg = "|-跳过被删除的域名: {}!".format(self.__config['orders'][i]['domains']) - writeLog(msg) - if not auth_to: - continue - # 是否到了允许重试的时间 - if 'next_retry_time' in self.__config['orders'][i]: - timeout = self.__config['orders'][i][ - 'next_retry_time'] - int(time.time()) - if timeout > 0: - msg = '|-本次跳过域名:{},因第上次续签失败,还需要等待{}小时后再重试'.format( - self.__config['orders'][i]['domains'], int(timeout / 60 / 60)) - writeLog(msg) - continue - - # 是否到了最大重试次数 - if 'retry_count' in self.__config['orders'][i]: - if self.__config['orders'][i]['retry_count'] >= 100: - msg = '|-本次跳过域名:{},因连续100次续签失败,不再续签此证书(可尝试手动续签此证书,成功后错误次数将被重置)'.format( - self.__config['orders'][i]['domains']) - writeLog(msg) - continue - - # 加入到续签订单 - order_index.append(i) - if not order_index: - writeLog("|-所有任务已处理完成!") - return - writeLog("|-共需要续签 {} 张证书".format(len(order_index))) - - n = 0 - self.getApis() - cert = None - for index in order_index: - n += 1 - writeLog("|-正在续签第 {} 张,域名: {}..".format(n, - self.__config['orders'][index]['domains'])) - writeLog("|-正在创建订单..") - cert = self.renewCertTo(self.__config['orders'][index]['domains'], self.__config[ - 'orders'][index]['auth_type'], self.__config['orders'][index]['auth_to'], index) - self.clearAuthFile(index) - return cert - except Exception as ex: - ex = str(ex) - if ex.find(">>>>") != -1: - msg = ex.split(">>>>") - msg[1] = json.loads(msg[1]) - else: - msg = ex - writeLog(mw.getTracebackInfo()) - return mw.returnJson(False, msg) - - def revokeOrder(self, index): - if not index in self.__config['orders']: - raise Exception("指定订单不存在!") - cert_path = self.__config['orders'][index]['save_path'] - if not os.path.exists(cert_path): - raise Exception("指定订单没有找到可用的证书!") - cert = self.dumpDer(cert_path) - if not cert: - raise Exception("证书读取失败!") - payload = { - "certificate": self.calculateSafeBase64(cert), - "reason": 4 - } - - self.getApis() - res = self.acmeRequest(self.__apis['revokeCert'], payload) - if res.status in [200, 201]: - if os.path.exists(cert_path): - mw.execShell("rm -rf {}".format(cert_path)) - del(self.__config['orders'][index]) - self.saveConfig() - return mw.returnJson(True, "证书吊销成功!") - return res.json() - - def do(self, args): - cert = None - try: - if not args.index: - if not args.domains: - echoErr("请在--domain参数中指定要申请证书的域名,多个以逗号(,)隔开") - if not args.auth_type in ['http', 'tls']: - echoErr("请在--type参数中指定正确的验证类型,http") - - auth_to = '' - if args.auth_type in ['http', 'tls']: - if not args.path: - echoErr("请在--path参数中指定网站根目录!") - if not os.path.exists(args.path): - echoErr("指定网站根目录不存在,请检查:{}".format(args.path)) - auth_to = args.path - else: - echoErr("仅支持文件验证!") - exit() - - domains = args.domains.strip().split(',') - cert = self.applyCert( - domains, auth_type=args.auth_type, auth_to=auth_to, args=args) - else: - # 重新验证 - cert = self.applyCert([], auth_type='dns', - auth_to='dns', index=args.index) - except Exception as e: - writeLog("|-{}".format(mw.getTracebackInfo())) - exit() - - if not cert: - exit() - - # print(cert) - if not cert['status']: - writeLog('|-' + cert['msg'][0]) - exit() - writeLog("=" * 65) - writeLog("|-证书获取成功!") - writeLog("=" * 65) - writeLog("证书到期时间: {}".format( - mw.formatDate(times=cert['cert_timeout']))) - writeLog("证书已保存在: {}/".format(cert['save_path'])) - - -# exp: -''' -// create -python3 class/core/cert_api.py --domain=dev38.cachecha.com --type=http --path=/www/wwwroot/dev38.cachecha.com -// renew -cd /www/server/mdserver-web && python3 class/core/cert_api.py --renew=1 --index=46c558aa1fae96facf36ac7df8eb4750 -// revoke -cd /www/server/mdserver-web && python3 class/core/cert_api.py --revoke=1 --index=370423ed29481b2caf22e36d90a6894a - - - -python3 class/core/cert_request.py --domain=dev38.cachecha.com --type=http --path=/Users/midoks/Desktop/mwdev/wwwroot/test - -python3 class/core/cert_api.py --domain=dev38.cachecha.com --type=http --path=/Users/midoks/Desktop/mwdev/wwwroot/test -python3 class/core/cert_api.py --renew=1 -python3 class/core/cert_api.py --revoke=1 --index=370423ed29481b2caf22e36d90a6894a -''' -if __name__ == "__main__": - p = argparse.ArgumentParser(usage="必要的参数:--domain 域名列表,多个以逗号隔开!") - p.add_argument('--domain', default=None, - help="请指定要申请证书的域名", dest="domains") - p.add_argument('--type', default=None, help="请指定验证类型", dest="auth_type") - p.add_argument('--path', default=None, help="请指定网站根目录", dest="path") - p.add_argument('--index', default=None, help="指定订单索引", dest="index") - p.add_argument('--renew', default=None, help="续签证书", dest="renew") - p.add_argument('--revoke', default=None, help="吊销证书", dest="revoke") - - args = p.parse_args() - cr = cert_api() - - if args.revoke: - if not args.index: - echoErr("请在--index参数中传入要被吊销的订单索引") - result = cr.revokeOrder(args.index) - writeLog(result) - exit() - - if args.renew: - cr.renewCert(args.index) - exit() - - cr.do(args) diff --git a/class/core/cert_request.py b/class/core/cert_request.py deleted file mode 100644 index 2a2ccc8d3..000000000 --- a/class/core/cert_request.py +++ /dev/null @@ -1,1472 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 证书申请 -# --------------------------------------------------------------------------------- - -import re -import fcntl -import datetime -import binascii -import hashlib -import base64 -import json -import time -import os -import sys -import argparse - -if os.path.exists('/www/server/mdserver-web'): - os.chdir('/www/server/mdserver-web') - -import mw - -try: - import OpenSSL -except: - mw.execShell("pip install pyopenssl") - import OpenSSL - -# import http_requests as requests -# requests.DEFAULT_TYPE = 'curl' -import requests -requests.packages.urllib3.disable_warnings() - - -def echoErr(msg): - writeLog("\033[31m=" * 65) - writeLog("|-错误:{}\033[0m".format(msg)) - exit() - - -def writeLog(log_str, mode="ab+"): - # 写日志 - if __name__ == "__main__": - print(log_str) - return - _log_file = 'logs/letsencrypt.log' - f = open(_log_file, mode) - log_str += "\n" - f.write(log_str.encode('utf-8')) - f.close() - return True - - -class cert_request: - __debug = False - __user_agent = "MW-Panel" - __apis = None - __url = None - __replay_nonce = None - __acme_timeout = 30 - __max_check_num = 5 - __wait_time = 5 - __bits = 2048 - __digest = "sha256" - __verify = False - __config = {} - __dns_class = None - __auto_wildcard = False - __mod_index = {True: "Staging", False: "Production"} - __save_path = 'data/letsencrypt' - __cfg_file = 'data/letsencrypt.json' - - def __init__(self): - self.__save_path = mw.getServerDir() + '/web_conf/letsencrypt' - if not os.path.exists(self.__save_path): - os.makedirs(self.__save_path) - if self.__debug: - self.__url = 'https://acme-staging-v02.api.letsencrypt.org/directory' - else: - self.__url = 'https://acme-v02.api.letsencrypt.org/directory' - self.__config = self.readConfig() - - def D(self, name, val): - if self.__debug: - print('---------{} start--------'.format(name)) - print(val) - print('---------{} end--------'.format(name)) - - # 读配置文件 - def readConfig(self): - if not os.path.exists(self.__cfg_file): - self.__config['orders'] = {} - self.__config['account'] = {} - self.__config['apis'] = {} - self.__config['email'] = mw.M('users').where( - 'id=?', (1,)).getField('email') - if self.__config['email'] in ['midoks@163.com']: - self.__config['email'] = None - self.saveConfig() - return self.__config - tmp_config = mw.readFile(self.__cfg_file) - if not tmp_config: - return self.__config - try: - self.__config = json.loads(tmp_config) - except: - self.saveConfig() - return self.__config - return self.__config - - # 写配置文件 - def saveConfig(self): - fp = open(self.__cfg_file, 'w+') - fcntl.flock(fp, fcntl.LOCK_EX) # 加锁 - fp.write(json.dumps(self.__config)) - fcntl.flock(fp, fcntl.LOCK_UN) # 解锁 - fp.close() - return True - - def getApis(self): - if not self.__apis: - # 尝试从配置文件中获取 - api_index = self.__mod_index[self.__debug] - if not 'apis' in self.__config: - self.__config['apis'] = {} - - if api_index in self.__config['apis']: - if 'expires' in self.__config['apis'][api_index] and 'directory' in self.__config['apis'][api_index]: - if time.time() < self.__config['apis'][api_index]['expires']: - self.__apis = self.__config[ - 'apis'][api_index]['directory'] - return self.__apis - - # 尝试从官方获取获取 - try: - res = mw.httpGet(self.__url) - result = json.loads(res) - self.__apis = {} - self.__apis['newAccount'] = result['newAccount'] - self.__apis['newNonce'] = result['newNonce'] - self.__apis['newOrder'] = result['newOrder'] - self.__apis['revokeCert'] = result['revokeCert'] - self.__apis['keyChange'] = result['keyChange'] - - # 保存到配置文件 - self.__config['apis'][api_index] = {} - self.__config['apis'][api_index]['directory'] = self.__apis - self.__config['apis'][api_index]['expires'] = time.time() + \ - 86400 # 24小时后过期 - self.saveConfig() - except Exception as e: - raise Exception( - '服务因维护而关闭或发生内部错误,查看 https://letsencrypt.status.io/ 了解更多详细信息。') - return self.__apis - - # 系列化payload - def stringfyItems(self, payload): - if isinstance(payload, str): - return payload - - for k, v in payload.items(): - if isinstance(k, bytes): - k = k.decode("utf-8") - if isinstance(v, bytes): - v = v.decode("utf-8") - payload[k] = v - return payload - - # 构造域名列表 - def formatDomains(self, domains): - if type(domains) != list: - return [] - # 是否自动构造通配符 - if self.__auto_wildcard: - domains = self.autoWildcard(domains) - wildcard = [] - tmp_domains = [] - for domain in domains: - domain = domain.strip() - if domain in tmp_domains: - continue - # 将通配符域名转为验证正则表达式 - f_index = domain.find("*.") - if f_index not in [-1, 0]: - continue - if f_index == 0: - wildcard.append(domain.replace( - "*", r"^[\w-]+").replace(".", r"\.")) - # 添加到申请列表 - tmp_domains.append(domain) - - # 处理通配符包含 - apply_domains = tmp_domains[:] - for domain in tmp_domains: - for w in wildcard: - if re.match(w, domain): - apply_domains.pop(domain) - - return apply_domains - - # 转为无填充的Base64 - def calculateSafeBase64(self, un_encoded_data): - if sys.version_info[0] == 3: - if isinstance(un_encoded_data, str): - un_encoded_data = un_encoded_data.encode("utf8") - r = base64.urlsafe_b64encode(un_encoded_data).rstrip(b"=") - return r.decode("utf8") - - # 创建Key - def createKey(self, key_type=OpenSSL.crypto.TYPE_RSA): - key = OpenSSL.crypto.PKey() - key.generate_key(key_type, self.__bits) - private_key = OpenSSL.crypto.dump_privatekey( - OpenSSL.crypto.FILETYPE_PEM, key) - return private_key - - # 获用户取密钥对 - def getAccountKey(self): - if not 'account' in self.__config: - self.__config['account'] = {} - k = self.__mod_index[self.__debug] - if not k in self.__config['account']: - self.__config['account'][k] = {} - - if not 'key' in self.__config['account'][k]: - self.__config['account'][k]['key'] = self.createKey() - if type(self.__config['account'][k]['key']) == bytes: - self.__config['account'][k]['key'] = self.__config[ - 'account'][k]['key'].decode() - self.saveConfig() - return self.__config['account'][k]['key'] - - # 注册acme帐户 - def register(self, existing=False): - if not 'email' in self.__config: - self.__config['email'] = 'xxxx.xxxx@gmail.com' - if existing: - payload = {"onlyReturnExisting": True} - elif self.__config['email']: - payload = { - "termsOfServiceAgreed": True, - "contact": ["mailto:{0}".format(self.__config['email'])], - } - else: - payload = {"termsOfServiceAgreed": True} - - res = self.acmeRequest(url=self.__apis['newAccount'], payload=payload) - - if res.status_code not in [201, 200, 409]: - raise Exception("注册ACME帐户失败: {}".format(res.json())) - kid = res.headers["Location"] - return kid - - # 获取kid - def getKid(self, force=False): - # 如果配置文件中不存在kid或force = True时则重新注册新的acme帐户 - if not 'account' in self.__config: - self.__config['account'] = {} - k = self.__mod_index[self.__debug] - if not k in self.__config['account']: - self.__config['account'][k] = {} - - if not 'kid' in self.__config['account'][k]: - self.__config['account'][k]['kid'] = self.register() - self.saveConfig() - time.sleep(3) - self.__config = self.readConfig() - return self.__config['account'][k]['kid'] - - # 获取随机数 - def getNonce(self, force=False): - return '1AADXO5Sdc3EzuOS8ViYj-MVdhgx3DRE2kSDSwrObJ1jG_w' - # 如果没有保存上一次的随机数或force=True时则重新获取新的随机数 - if not self.__replay_nonce or force: - try: - - import urllib.request - try: - response = urllib.request.urlopen(self.__apis['newNonce'], - timeout=self.__acme_timeout, - headers=headers, - verify=self.__verify) - self.__replay_nonce = response.headers["replay-nonce"] - return self.__replay_nonce - except Exception as e: - pass - - headers = {"User-Agent": self.__user_agent} - response = requests.get(self.__apis['newNonce'], - timeout=self.__acme_timeout, - headers=headers, - verify=self.__verify) - - self.__replay_nonce = response.headers["replay-nonce"] - except Exception as e: - raise Exception("获取随机数失败: {}".format(str(e))) - - return self.__replay_nonce - - # 获请ACME请求头 - def getAcmeHeader(self, url): - nonce = self.getNonce() - - header = {"alg": "RS256", "nonce": nonce, "url": url} - if url in [self.__apis['newAccount'], 'GET_THUMBPRINT']: - from cryptography.hazmat.backends import default_backend - from cryptography.hazmat.primitives import serialization - private_key = serialization.load_pem_private_key( - self.getAccountKey().encode(), - password=None, - backend=default_backend(), - ) - public_key_public_numbers = private_key.public_key().public_numbers() - - exponent = "{0:x}".format(public_key_public_numbers.e) - exponent = "0{0}".format(exponent) if len( - exponent) % 2 else exponent - modulus = "{0:x}".format(public_key_public_numbers.n) - jwk = { - "kty": "RSA", - "e": self.calculateSafeBase64(binascii.unhexlify(exponent)), - "n": self.calculateSafeBase64(binascii.unhexlify(modulus)), - } - header["jwk"] = jwk - else: - header["kid"] = self.getKid() - return header - - # 计算signature - def signMessage(self, message): - pk = OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, self.getAccountKey().encode()) - return OpenSSL.crypto.sign(pk, message.encode("utf8"), self.__digest) - - def getSiteRunPathByid(self, site_id): - if mw.M('sites').where('id=?', (site_id,)).count() >= 1: - site_path = mw.M('sites').where('id=?', site_id).getField('path') - if not site_path: - return None - if not os.path.exists(site_path): - return None - args = mw.dict_obj() - args.id = site_id - import panelSite - run_path = panelSite.panelSite().GetRunPath(args) - if run_path in ['/']: - run_path = '' - if run_path: - if run_path[0] == '/': - run_path = run_path[1:] - site_run_path = os.path.join(site_path, run_path) - if not os.path.exists(site_run_path): - return site_path - return site_run_path - else: - return False - - def getSiteRunPath(self, domains): - site_id = 0 - for domain in domains: - site_id = mw.M('domain').where("name=?", domain).getField('pid') - if site_id: - break - - if not site_id: - return None - return self.getSiteRunPathByid(site_id) - - # 清理验证文件 - def claerAuthFile(self, index): - if not self.__config['orders'][index]['auth_type'] in ['http', 'tls']: - return True - acme_path = '{}/.well-known/acme-challenge'.format( - self.__config['orders'][index]['auth_to']) - writeLog("|-验证目录:{}".format(acme_path)) - if os.path.exists(acme_path): - mw.execShell("rm -f {}/*".format(acme_path)) - - acme_path = mw.getServerDir() + '/stop/.well-known/acme-challenge' - if os.path.exists(acme_path): - mw.execShell("rm -f {}/*".format(acme_path)) - - # 获取域名验证方式 - def getAuthType(self, index): - if not index in self.__config['orders']: - raise Exception('指定订单不存在!') - s_type = 'http-01' - if 'auth_type' in self.__config['orders'][index]: - if self.__config['orders'][index]['auth_type'] == 'dns': - s_type = 'dns-01' - elif self.__config['orders'][index]['auth_type'] == 'tls': - s_type = 'tls-alpn-01' - else: - s_type = 'http-01' - return s_type - - # 构造验证信息 - def getIdentifierAuth(self, index, url, auth_info): - s_type = self.getAuthType(index) - writeLog("|-验证类型:{}".format(s_type)) - domain = auth_info['identifier']['value'] - wildcard = False - # 处理通配符 - if 'wildcard' in auth_info: - wildcard = auth_info['wildcard'] - if wildcard: - domain = "*." + domain - - for auth in auth_info['challenges']: - if auth['type'] != s_type: - continue - identifier_auth = { - "domain": domain, - "url": url, - "wildcard": wildcard, - "token": auth['token'], - "dns_challenge_url": auth['url'], - } - return identifier_auth - return None - - # 构造域名验证头和验证值 - def getKeyauthorization(self, token): - acme_header_jwk_json = json.dumps( - self.getAcmeHeader("GET_THUMBPRINT")["jwk"], sort_keys=True, separators=(",", ":") - ) - acme_thumbprint = self.calculateSafeBase64( - hashlib.sha256(acme_header_jwk_json.encode("utf8")).digest() - ) - acme_keyauthorization = "{0}.{1}".format(token, acme_thumbprint) - base64_of_acme_keyauthorization = self.calculateSafeBase64( - hashlib.sha256(acme_keyauthorization.encode("utf8")).digest() - ) - - return acme_keyauthorization, base64_of_acme_keyauthorization - - # 写验证文件 - def writeAuthFile(self, auth_to, token, acme_keyauthorization): - try: - # self.D('writeAuthFile', auth_to) - acme_path = '{}/.well-known/acme-challenge'.format(auth_to) - if not os.path.exists(acme_path): - os.makedirs(acme_path) - mw.setOwn(acme_path, 'www') - wellknown_path = '{}/{}'.format(acme_path, token) - mw.writeFile(wellknown_path, acme_keyauthorization) - mw.setOwn(wellknown_path, 'www') - - acme_path = mw.getServerDir() + '/stop/.well-known/acme-challenge' - if not os.path.exists(acme_path): - os.makedirs(acme_path) - mw.setOwn(acme_path, 'www') - wellknown_path = '{}/{}'.format(acme_path, token) - mw.writeFile(wellknown_path, acme_keyauthorization) - mw.setOwn(wellknown_path, 'www') - return True - except: - err = mw.getTracebackInfo() - raise Exception("写入验证文件失败: {}".format(err)) - - # 设置验证信息 - def setAuthInfo(self, identifier_auth): - - # 是否手动验证DNS - if identifier_auth['auth_to'] == 'dns': - return None - - # 是否文件验证 - if identifier_auth['type'] in ['http', 'tls']: - self.writeAuthFile(identifier_auth['auth_to'], identifier_auth[ - 'token'], identifier_auth['acme_keyauthorization']) - - # 获取验证信息 - def getAuths(self, index): - if not index in self.__config['orders']: - raise Exception('指定订单不存在!') - - # 检查是否已经获取过授权信息 - if 'auths' in self.__config['orders'][index]: - # 检查授权信息是否过期 - if time.time() < self.__config['orders'][index]['auths'][0]['expires']: - return self.__config['orders'][index]['auths'] - if self.__config['orders'][index]['auth_type'] != 'dns': - site_run_path = self.getSiteRunPath( - self.__config['orders'][index]['domains']) - if site_run_path: - self.__config['orders'][index]['auth_to'] = site_run_path - - # 清理旧验证 - self.claerAuthFile(index) - - auths = [] - for auth_url in self.__config['orders'][index]['authorizations']: - res = self.acmeRequest(auth_url, "") - if res.status_code not in [200, 201]: - raise Exception("获取授权失败: {}".format(res.json())) - - s_body = res.json() - if 'status' in s_body: - if s_body['status'] in ['invalid']: - raise Exception("无效订单,此订单当前为验证失败状态!") - if s_body['status'] in ['valid']: # 跳过无需验证的域名 - continue - - s_body['expires'] = self.utcToTime(s_body['expires']) - identifier_auth = self.getIdentifierAuth(index, auth_url, s_body) - if not identifier_auth: - raise Exception("验证信息构造失败!{}") - - acme_keyauthorization, auth_value = self.getKeyauthorization(identifier_auth[ - 'token']) - identifier_auth['acme_keyauthorization'] = acme_keyauthorization - identifier_auth['auth_value'] = auth_value - identifier_auth['expires'] = s_body['expires'] - identifier_auth['auth_to'] = self.__config[ - 'orders'][index]['auth_to'] - identifier_auth['type'] = self.__config[ - 'orders'][index]['auth_type'] - - # print(identifier_auth) - # 设置验证信息 - self.setAuthInfo(identifier_auth) - auths.append(identifier_auth) - self.__config['orders'][index]['auths'] = auths - self.saveConfig() - return auths - - # 更新随机数 - def updateReplayNonce(self, res): - replay_nonce = res.headers.get('replay-nonce') - if replay_nonce: - self.__replay_nonce = replay_nonce - - # 请求到ACME接口 - def acmeRequest(self, url, payload): - headers = {"User-Agent": self.__user_agent} - payload = self.stringfyItems(payload) - - if payload == "": - payload64 = payload - else: - payload64 = self.calculateSafeBase64(json.dumps(payload)) - protected = self.getAcmeHeader(url) - protected64 = self.calculateSafeBase64(json.dumps(protected)) - signature = self.signMessage( - message="{0}.{1}".format(protected64, payload64)) # bytes - signature64 = self.calculateSafeBase64(signature) # str - data = json.dumps( - {"protected": protected64, "payload": payload64, "signature": signature64}) - headers.update({"Content-Type": "application/jose+json"}) - response = requests.post( - url, - data=data.encode("utf8"), - timeout=self.__acme_timeout, - headers=headers, - verify=self.__verify - ) - # 更新随机数 - self.updateReplayNonce(response) - return response - - # UTC时间转时间戳 - def utcToTime(self, utc_string): - try: - utc_string = utc_string.split('.')[0] - utc_date = datetime.datetime.strptime( - utc_string, "%Y-%m-%dT%H:%M:%S") - # 按北京时间返回 - return int(time.mktime(utc_date.timetuple())) + (3600 * 8) - except: - return int(time.time() + 86400 * 7) - - # 保存订单 - def saveOrder(self, order_object, index): - if not 'orders' in self.__config: - self.__config['orders'] = {} - renew = False - if not index: - index = mw.md5(json.dumps(order_object['identifiers'])) - else: - renew = True - order_object['certificate_url'] = self.__config[ - 'orders'][index]['certificate_url'] - order_object['save_path'] = self.__config[ - 'orders'][index]['save_path'] - - order_object['expires'] = self.utcToTime(order_object['expires']) - self.__config['orders'][index] = order_object - self.__config['orders'][index]['index'] = index - if not renew: - self.__config['orders'][index]['create_time'] = int(time.time()) - self.__config['orders'][index]['renew_time'] = 0 - self.saveConfig() - return index - - def getError(self, error): - # 格式化错误输出 - if error.find("Max checks allowed") >= 0: - return "CA无法验证您的域名,请检查域名解析是否正确,或等待5-10分钟后重试." - elif error.find("Max retries exceeded with") >= 0 or error.find('status_code=0 ') != -1: - return "CA服务器连接超时,请稍候重试." - elif error.find("The domain name belongs") >= 0: - return "域名不属于此DNS服务商,请确保域名填写正确." - elif error.find('login token ID is invalid') >= 0: - return 'DNS服务器连接失败,请检查密钥是否正确.' - elif error.find('Error getting validation data') != -1: - return '数据验证失败,CA无法从验证连接中获到正确的验证码.' - elif "too many certificates already issued for exact set of domains" in error: - return '签发失败,该域名%s超出了每周的重复签发次数限制!' % re.findall("exact set of domains: (.+):", error) - elif "Error creating new account :: too many registrations for this IP" in error: - return '签发失败,当前服务器IP已达到每3小时最多创建10个帐户的限制.' - elif "DNS problem: NXDOMAIN looking up A for" in error: - return '验证失败,没有解析域名,或解析未生效!' - elif "Invalid response from" in error: - return '验证失败,域名解析错误或验证URL无法被访问!' - elif error.find('TLS Web Server Authentication') != -1: - return "连接CA服务器失败,请稍候重试." - elif error.find('Name does not end in a public suffix') != -1: - return "不支持的域名%s,请检查域名是否正确!" % re.findall("Cannot issue for \"(.+)\":", error) - elif error.find('No valid IP addresses found for') != -1: - return "域名%s没有找到解析记录,请检查域名是否解析生效!" % re.findall("No valid IP addresses found for (.+)", error) - elif error.find('No TXT record found at') != -1: - return "没有在域名%s中找到有效的TXT解析记录,请检查是否正确解析TXT记录,如果是DNSAPI方式申请的,请10分钟后重试!" % re.findall("No TXT record found at (.+)", error) - elif error.find('Incorrect TXT record') != -1: - return "在%s上发现错误的TXT记录:%s,请检查TXT解析是否正确,如果是DNSAPI方式申请的,请10分钟后重试!" % (re.findall("found at (.+)", error), re.findall("Incorrect TXT record \"(.+)\"", error)) - elif error.find('Domain not under you or your user') != -1: - return "这个dnspod账户下面不存在这个域名,添加解析失败!" - elif error.find('SERVFAIL looking up TXT for') != -1: - return "没有在域名%s中找到有效的TXT解析记录,请检查是否正确解析TXT记录,如果是DNSAPI方式申请的,请10分钟后重试!" % re.findall("looking up TXT for (.+)", error) - elif error.find('Timeout during connect') != -1: - return "连接超时,CA服务器无法访问您的网站!" - elif error.find("DNS problem: SERVFAIL looking up CAA for") != -1: - return "域名%s当前被要求验证CAA记录,请手动解析CAA记录,或1小时后重新尝试申请!" % re.findall("looking up CAA for (.+)", error) - elif error.find("Read timed out.") != -1: - return "验证超时,请检查域名是否正确解析,若已正确解析,可能服务器与Let'sEncrypt连接异常,请稍候再重试!" - elif error.find('Cannot issue for') != -1: - return "无法为{}颁发证书,不能直接用域名后缀申请通配符证书!".format(re.findall(r'for\s+"(.+)"', error)) - elif error.find('too many failed authorizations recently'): - return '该帐户1小时内失败的订单次数超过5次,请等待1小时再重试!' - elif error.find("Error creating new order") != -1: - return "订单创建失败,请稍候重试!" - elif error.find("Too Many Requests") != -1: - return "1小时内超过5次验证失败,暂时禁止申请,请稍候重试!" - elif error.find('HTTP Error 400: Bad Request') != -1: - return "CA服务器拒绝访问,请稍候重试!" - elif error.find('Temporary failure in name resolution') != -1: - return '服务器DNS故障,无法解析域名,请使用Linux工具箱检查dns配置' - elif error.find('Too Many Requests') != -1: - return '该域名请求申请次数过多,请3小时后重试' - else: - return error - - # 创建订单 - def createOrder(self, domains, auth_type, auth_to, index=None): - domains = self.formatDomains(domains) - if not domains: - raise Exception("至少需要有一个域名") - # 构造标识 - identifiers = [] - for domain_name in domains: - identifiers.append({"type": 'dns', "value": domain_name}) - payload = {"identifiers": identifiers} - - # 请求创建订单 - res = self.acmeRequest(self.__apis['newOrder'], payload) - if not res.status_code in [201]: # 如果创建失败 - e_body = res.json() - if 'type' in e_body: - # 如果随机数失效 - if e_body['type'].find('error:badNonce') != -1: - self.getNonce(force=True) - res = self.acmeRequest(self.__apis['newOrder'], payload) - - # 如果帐户失效 - if e_body['detail'].find('KeyID header contained an invalid account URL') != -1: - k = self._mod_index[self.__debug] - del(self.__config['account'][k]) - self.getKid() - self.getNonce(force=True) - res = self.acmeRequest(self.__apis['newOrder'], payload) - if not res.status_code in [201]: - a_auth = res.json() - - ret_title = self.getError(str(a_auth)) - raise StopIteration("{0} >>>> {1}".format( - ret_title, json.dumps(a_auth))) - - # 返回验证地址和验证 - s_json = res.json() - s_json['auth_type'] = auth_type - s_json['domains'] = domains - s_json['auth_to'] = auth_to - index = self.saveOrder(s_json, index) - return index - - # 检查验证状态 - def checkAuthStatus(self, url, desired_status=None): - desired_status = desired_status or ["pending", "valid", "invalid"] - number_of_checks = 0 - while True: - if desired_status == ['valid', 'invalid']: - writeLog("|-第{}次查询验证结果..".format(number_of_checks + 1)) - time.sleep(self.__wait_time) - check_authorization_status_response = self.acmeRequest(url, "") - a_auth = check_authorization_status_response.json() - authorization_status = a_auth["status"] - number_of_checks += 1 - if authorization_status in desired_status: - if authorization_status == "invalid": - writeLog("|-验证失败!") - try: - if 'error' in a_auth['challenges'][0]: - ret_title = a_auth['challenges'][ - 0]['error']['detail'] - elif 'error' in a_auth['challenges'][1]: - ret_title = a_auth['challenges'][ - 1]['error']['detail'] - elif 'error' in a_auth['challenges'][2]: - ret_title = a_auth['challenges'][ - 2]['error']['detail'] - else: - ret_title = str(a_auth) - ret_title = self.getError(ret_title) - except: - ret_title = str(a_auth) - raise StopIteration("{0} >>>> {1}".format( - ret_title, json.dumps(a_auth))) - break - - if number_of_checks == self.__max_check_num: - raise StopIteration( - "错误:已尝试验证{0}次. 最大验证次数为{1}. 验证时间间隔为{2}秒.".format( - number_of_checks, - self.__max_check_num, - self.__wait_time - ) - ) - if desired_status == ['valid', 'invalid']: - writeLog("|-验证成功!") - return check_authorization_status_response - - # 发送验证请求 - def respondToChallenge(self, auth): - payload = {"keyAuthorization": "{0}".format( - auth['acme_keyauthorization'])} - respond_to_challenge_response = self.acmeRequest( - auth['dns_challenge_url'], payload) - return respond_to_challenge_response - - # 检查DNS记录 - def checkDns(self, domain, value, s_type='TXT'): - writeLog( - "|-尝试本地验证DNS记录,域名: {} , 类型: {} 记录值: {}".format(domain, s_type, value)) - time.sleep(10) - n = 0 - while n < 20: - n += 1 - try: - import dns.resolver - ns = dns.resolver.query(domain, s_type) - for j in ns.response.answer: - for i in j.items: - txt_value = i.to_text().replace('"', '').strip() - writeLog("|-第 {} 次验证值: {}".format(n, txt_value)) - if txt_value == value: - write_log("|-本地验证成功!") - return True - except: - try: - import dns.resolver - except: - return False - time.sleep(3) - writeLog("|-本地验证失败!") - return True - - def authDomain(self, index): - if not index in self.__config['orders']: - raise Exception('指定订单不存在!') - - # 开始验证 - for auth in self.__config['orders'][index]['auths']: - res = self.checkAuthStatus(auth['url']) # 检查是否需要验证 - if res.json()['status'] == 'pending': - if auth['type'] == 'dns': # 尝试提前验证dns解析 - self.checkDns( - "_acme-challenge.{}".format( - auth['domain'].replace('*.', '')), - auth['auth_value'], - "TXT" - ) - self.respondToChallenge(auth) - - # 检查验证结果 - for i in range(len(self.__config['orders'][index]['auths'])): - self.checkAuthStatus(self.__config['orders'][index]['auths'][i]['url'], [ - 'valid', 'invalid']) - self.__config['orders'][index]['status'] = 'valid' - - # 构造可选名称 - def getAltNames(self, index): - domain_name = self.__config['orders'][index]['domains'][0] - domain_alt_names = [] - if len(self.__config['orders'][index]['domains']) > 1: - domain_alt_names = self.__config['orders'][index]['domains'][1:] - return domain_name, domain_alt_names - - # 创建CSR - def createCsr(self, index): - if 'csr' in self.__config['orders'][index]: - return self.__config['orders']['csr'] - domain_name, domain_alt_names = self.getAltNames(index) - X509Req = OpenSSL.crypto.X509Req() - X509Req.get_subject().CN = domain_name - if domain_alt_names: - SAN = "DNS:{0}, ".format(domain_name).encode("utf8") + ", ".join( - "DNS:" + i for i in domain_alt_names - ).encode("utf8") - else: - SAN = "DNS:{0}".format(domain_name).encode("utf8") - - X509Req.add_extensions( - [ - OpenSSL.crypto.X509Extension( - "subjectAltName".encode("utf8"), critical=False, value=SAN - ) - ] - ) - pk = OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, self.createCertificateKey( - index).encode() - ) - X509Req.set_pubkey(pk) - # X509Req.set_version(2) - X509Req.set_version(0) - X509Req.sign(pk, self.__digest) - return OpenSSL.crypto.dump_certificate_request(OpenSSL.crypto.FILETYPE_ASN1, X509Req) - - # 获取证书密钥对 - def createCertificateKey(self, index): - # 判断是否已经创建private_key - if 'private_key' in self.__config['orders'][index]: - return self.__config['orders'][index]['private_key'] - # 创建新的私钥 - private_key = self.createKey() - if type(private_key) == bytes: - private_key = private_key.decode() - # 保存私钥到订单配置文件 - self.__config['orders'][index]['private_key'] = private_key - self.saveConfig() - return private_key - - # 发送CSR - def sendCsr(self, index): - csr = self.createCsr(index) - payload = {"csr": self.calculateSafeBase64(csr)} - send_csr_response = self.acmeRequest( - url=self.__config['orders'][index]['finalize'], payload=payload) - if send_csr_response.status_code not in [200, 201]: - raise ValueError( - "错误: 发送CSR: 响应状态{status_code} 响应值:{response}".format( - status_code=send_csr_response.status_code, - response=send_csr_response.json(), - ) - ) - send_csr_response_json = send_csr_response.json() - certificate_url = send_csr_response_json["certificate"] - self.__config['orders'][index]['certificate_url'] = certificate_url - self.saveConfig() - return certificate_url - - # 转换时间 - def strfDate(self, sdate): - return time.strftime('%Y-%m-%d', time.strptime(sdate, '%Y%m%d%H%M%S')) - - # 证书转为DER - def dumpDer(self, cert_path): - cert = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, mw.readFile(cert_path + '/cert.csr')) - return OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, cert) - - # 证书转为pkcs12 - def dumpPkcs12(self, key_pem=None, cert_pem=None, ca_pem=None, friendly_name=None): - p12 = OpenSSL.crypto.PKCS12() - if cert_pem: - p12.set_certificate(OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, cert_pem.encode())) - if key_pem: - p12.set_privatekey(OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, key_pem.encode())) - if ca_pem: - p12.set_ca_certificates((OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, ca_pem.encode()),)) - if friendly_name: - p12.set_friendlyname(friendly_name.encode()) - return p12.export() - - # 拆分根证书 - def splitCaData(self, cert): - sp_key = '-----END CERTIFICATE-----\n' - datas = cert.split(sp_key) - return {"cert": datas[0] + sp_key, "root": sp_key.join(datas[1:])} - - # 获取指定证书基本信息 - def getCertInit(self, pem_file): - if not os.path.exists(pem_file): - return None - try: - result = {} - x509 = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, mw.readFile(pem_file)) - # 取产品名称 - issuer = x509.get_issuer() - result['issuer'] = '' - if hasattr(issuer, 'CN'): - result['issuer'] = issuer.CN - if not result['issuer']: - is_key = [b'0', '0'] - issue_comp = issuer.get_components() - if len(issue_comp) == 1: - is_key = [b'CN', 'CN'] - for iss in issue_comp: - if iss[0] in is_key: - result['issuer'] = iss[1].decode() - break - # 取到期时间 - result['notAfter'] = self.strfDate( - bytes.decode(x509.get_notAfter())[:-1]) - # 取申请时间 - result['notBefore'] = self.strfDate( - bytes.decode(x509.get_notBefore())[:-1]) - # 取可选名称 - result['dns'] = [] - for i in range(x509.get_extension_count()): - s_name = x509.get_extension(i) - if s_name.get_short_name() in [b'subjectAltName', 'subjectAltName']: - s_dns = str(s_name).split(',') - for d in s_dns: - result['dns'].append(d.split(':')[1]) - subject = x509.get_subject().get_components() - # 取主要认证名称 - if len(subject) == 1: - result['subject'] = subject[0][1].decode() - else: - result['subject'] = result['dns'][0] - return result - except: - return None - - # 替换服务器上的同域名同品牌证书 - def subAllCert(self, key_file, pem_file): - cert_init = self.getCertInit(pem_file) # 获取新证书的基本信息 - paths = ['/www/server/mdserver-web/data/letsencrypt'] - is_panel = False - for path in paths: - if not os.path.exists(path): - continue - for p_name in os.listdir(path): - to_path = path + '/' + p_name - to_pem_file = to_path + '/fullchain.pem' - to_key_file = to_path + '/privkey.pem' - to_info = to_path + '/info.json' - # 判断目标证书是否存在 - if not os.path.exists(to_pem_file): - if not p_name in ['ssl']: - continue - to_pem_file = to_path + '/certificate.pem' - to_key_file = to_path + '/privateKey.pem' - if not os.path.exists(to_pem_file): - continue - if path == paths[-1]: - is_panel = True - # 获取目标证书的基本信息 - to_cert_init = self.getCertInit(to_pem_file) - # 判断证书品牌是否一致 - try: - if to_cert_init['issuer'] != cert_init['issuer'] and to_cert_init['issuer'].find("Let's Encrypt") == -1 and to_cert_init['issuer'] != 'R3': - continue - except: - continue - # 判断目标证书的到期时间是否较早 - if to_cert_init['notAfter'] > cert_init['notAfter']: - continue - # 判断认识名称是否一致 - if len(to_cert_init['dns']) != len(cert_init['dns']): - continue - is_copy = True - for domain in to_cert_init['dns']: - if not domain in cert_init['dns']: - is_copy = False - if not is_copy: - continue - - # 替换新的证书文件和基本信息 - mw.writeFile(to_pem_file, mw.readFile(pem_file, 'rb'), 'wb') - mw.writeFile(to_key_file, mw.readFile(key_file, 'rb'), 'wb') - mw.writeFile(to_info, json.dumps(cert_init)) - writeLog("|-检测到{}下的证书与本次申请的证书重叠,且到期时间较早,已替换为新证书!".format(to_path)) - # 重载web服务 - mw.restartWeb() - - # 保存证书到文件 - def saveCert(self, cert, index): - try: - domain_name = self.__config['orders'][index]['domains'][0] - path = self.__config['orders'][index]['save_path'] - if not os.path.exists(path): - os.makedirs(path, 384) - - # 存储证书 - key_file = path + "/privkey.pem" - pem_file = path + "/fullchain.pem" - mw.writeFile(key_file, cert['private_key']) - mw.writeFile(pem_file, cert['cert'] + cert['root']) - mw.writeFile(path + "/cert.csr", cert['cert']) - mw.writeFile(path + "/root_cert.csr", cert['root']) - - # 转为IIS证书 - pfx_buffer = self.dumpPkcs12( - cert['private_key'], cert['cert'] + cert['root'], cert['root'], domain_name) - mw.writeFile(path + "/fullchain.pfx", pfx_buffer, 'wb+') - - ps = '''文件说明: -privkey.pem 证书私钥 -fullchain.pem 包含证书链的PEM格式证书(nginx/apache) -root_cert.csr 根证书 -cert.csr 域名证书 -fullchain.pfx 用于IIS的证书格式 - -如何在MW面板使用: -privkey.pem 粘贴到密钥输入框 -fullchain.pem 粘贴到证书输入框 -''' - mw.writeFile(path + '/readme.txt', ps) - self.subAllCert(key_file, pem_file) - except: - writeLog(mw.getTracebackInfo()) - - # 获取证书到期时间 - def getCertTimeout(self, cret_data): - try: - x509 = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, cret_data) - cert_timeout = bytes.decode(x509.get_notAfter())[:-1] - return int(time.mktime(time.strptime(cert_timeout, '%Y%m%d%H%M%S'))) - except: - return int(time.time() + (86400 * 90)) - - # 下载证书 - def downloadCert(self, index): - res = self.acmeRequest( - self.__config['orders'][index]['certificate_url'], "") - if res.status_code not in [200, 201]: - raise Exception("下载证书失败: {}".format(res.json())) - - pem_certificate = res.content - if type(pem_certificate) == bytes: - pem_certificate = pem_certificate.decode('utf-8') - cert = self.splitCaData(pem_certificate) - cert['cert_timeout'] = self.getCertTimeout(cert['cert']) - cert['private_key'] = self.__config['orders'][index]['private_key'] - cert['domains'] = self.__config['orders'][index]['domains'] - del(self.__config['orders'][index]['private_key']) - del(self.__config['orders'][index]['auths']) - del(self.__config['orders'][index]['expires']) - del(self.__config['orders'][index]['authorizations']) - del(self.__config['orders'][index]['finalize']) - del(self.__config['orders'][index]['identifiers']) - if 'cert' in self.__config['orders'][index]: - del(self.__config['orders'][index]['cert']) - self.__config['orders'][index]['status'] = 'valid' - self.__config['orders'][index]['cert_timeout'] = cert['cert_timeout'] - domain_name = self.__config['orders'][index]['domains'][0] - self.__config['orders'][index]['save_path'] = '{}/{}'.format( - self.__save_path, domain_name) - cert['save_path'] = self.__config['orders'][index]['save_path'] - self.saveConfig() - self.saveCert(cert, index) - return cert - - def applyCert(self, domains, auth_type='http', auth_to='Dns_com|None|None', args={}): - writeLog("", "wb+") - try: - self.getApis() - index = None - if 'index' in args and args.index: - index = args.index - if not index: - # 判断是否只想验证域名 - writeLog("|-正在创建订单..") - index = self.createOrder(domains, auth_type, auth_to) - writeLog("|-正在获取验证信息..") - self.getAuths(index) - if auth_to == 'dns' and len(self.__config['orders'][index]['auths']) > 0: - return self.__config['orders'][index] - writeLog("|-正在验证域名..") - self.authDomain(index) - - writeLog("|-正在发送CSR..") - self.sendCsr(index) - - writeLog("|-正在下载证书..") - cert = self.downloadCert(index) - - # 保存证书配置 - self.saveConfig() - cert['status'] = True - cert['msg'] = '申请成功!' - writeLog("|-申请成功,正在部署到站点..") - return cert - except Exception as ex: - ex = str(ex) - if ex.find(">>>>") != -1: - msg = ex.split(">>>>") - msg[1] = json.loads(msg[1]) - else: - msg = ex - writeLog(mw.getTracebackInfo()) - - cert = {} - cert['status'] = False - cert['msg'] = msg - return cert - - # 取根域名和记录值 - def extractZone(self, domain_name): - top_domain_list = ['.ac.cn', '.ah.cn', '.bj.cn', '.com.cn', '.cq.cn', '.fj.cn', '.gd.cn', '.gov.cn', '.gs.cn', - '.gx.cn', '.gz.cn', '.ha.cn', '.hb.cn', '.he.cn', '.hi.cn', '.hk.cn', '.hl.cn', '.hn.cn', - '.jl.cn', '.js.cn', '.jx.cn', '.ln.cn', '.mo.cn', '.net.cn', '.nm.cn', '.nx.cn', '.org.cn', - '.my.id', '.com.ac', '.com.ad', '.com.ae', '.com.af', '.com.ag', '.com.ai', '.com.al', '.com.am', - '.com.an', '.com.ao', '.com.aq', '.com.ar', '.com.as', '.com.as', '.com.at', '.com.au', '.com.aw', - '.com.az', '.com.ba', '.com.bb', '.com.bd', '.com.be', '.com.bf', '.com.bg', '.com.bh', '.com.bi', - '.com.bj', '.com.bm', '.com.bn', '.com.bo', '.com.br', '.com.bs', '.com.bt', '.com.bv', '.com.bw', - '.com.by', '.com.bz', '.com.ca', '.com.ca', '.com.cc', '.com.cd', '.com.cf', '.com.cg', '.com.ch', - '.com.ci', '.com.ck', '.com.cl', '.com.cm', '.com.cn', '.com.co', '.com.cq', '.com.cr', '.com.cu', - '.com.cv', '.com.cx', '.com.cy', '.com.cz', '.com.de', '.com.dj', '.com.dk', '.com.dm', '.com.do', - '.com.dz', '.com.ec', '.com.ee', '.com.eg', '.com.eh', '.com.es', '.com.et', '.com.eu', '.com.ev', - '.com.fi', '.com.fj', '.com.fk', '.com.fm', '.com.fo', '.com.fr', '.com.ga', '.com.gb', '.com.gd', - '.com.ge', '.com.gf', '.com.gh', '.com.gi', '.com.gl', '.com.gm', '.com.gn', '.com.gp', '.com.gr', - '.com.gt', '.com.gu', '.com.gw', '.com.gy', '.com.hm', '.com.hn', '.com.hr', '.com.ht', '.com.hu', - '.com.id', '.com.id', '.com.ie', '.com.il', '.com.il', '.com.in', '.com.io', '.com.iq', '.com.ir', - '.com.is', '.com.it', '.com.jm', '.com.jo', '.com.jp', '.com.ke', '.com.kg', '.com.kh', '.com.ki', - '.com.km', '.com.kn', '.com.kp', '.com.kr', '.com.kw', '.com.ky', '.com.kz', '.com.la', '.com.lb', - '.com.lc', '.com.li', '.com.lk', '.com.lr', '.com.ls', '.com.lt', '.com.lu', '.com.lv', '.com.ly', - '.com.ma', '.com.mc', '.com.md', '.com.me', '.com.mg', '.com.mh', '.com.ml', '.com.mm', '.com.mn', - '.com.mo', '.com.mp', '.com.mq', '.com.mr', '.com.ms', '.com.mt', '.com.mv', '.com.mw', '.com.mx', - '.com.my', '.com.mz', '.com.na', '.com.nc', '.com.ne', '.com.nf', '.com.ng', '.com.ni', '.com.nl', - '.com.no', '.com.np', '.com.nr', '.com.nr', '.com.nt', '.com.nu', '.com.nz', '.com.om', '.com.pa', - '.com.pe', '.com.pf', '.com.pg', '.com.ph', '.com.pk', '.com.pl', '.com.pm', '.com.pn', '.com.pr', - '.com.pt', '.com.pw', '.com.py', '.com.qa', '.com.re', '.com.ro', '.com.rs', '.com.ru', '.com.rw', - '.com.sa', '.com.sb', '.com.sc', '.com.sd', '.com.se', '.com.sg', '.com.sh', '.com.si', '.com.sj', - '.com.sk', '.com.sl', '.com.sm', '.com.sn', '.com.so', '.com.sr', '.com.st', '.com.su', '.com.sy', - '.com.sz', '.com.tc', '.com.td', '.com.tf', '.com.tg', '.com.th', '.com.tj', '.com.tk', '.com.tl', - '.com.tm', '.com.tn', '.com.to', '.com.tp', '.com.tr', '.com.tt', '.com.tv', '.com.tw', '.com.tz', - '.com.ua', '.com.ug', '.com.uk', '.com.uk', '.com.us', '.com.uy', '.com.uz', '.com.va', '.com.vc', - '.com.ve', '.com.vg', '.com.vn', '.com.vu', '.com.wf', '.com.ws', '.com.ye', '.com.za', '.com.zm', - '.com.zw', '.mil.cn', '.qh.cn', '.sc.cn', '.sd.cn', '.sh.cn', '.sx.cn', '.tj.cn', '.tw.cn', '.tw.cn', - '.xj.cn', '.xz.cn', '.yn.cn', '.zj.cn', '.bj.cn', '.edu.kg' - ] - old_domain_name = domain_name - top_domain = "." + ".".join(domain_name.rsplit('.')[-2:]) - new_top_domain = "." + top_domain.replace(".", "") - is_tow_top = False - if top_domain in top_domain_list: - is_tow_top = True - domain_name = domain_name[:-len(top_domain)] + new_top_domain - - if domain_name.count(".") > 1: - zone, middle, last = domain_name.rsplit(".", 2) - if is_tow_top: - last = top_domain[1:] - root = ".".join([middle, last]) - else: - zone = "" - root = old_domain_name - return root, zone - - # 获取当前正在使用此证书的网站目录 - def getSslUsedSite(self, save_path): - pkey_file = '{}/privkey.pem'.format(save_path) - pkey = mw.readFile(pkey_file) - if not pkey: - return False - cert_paths = 'vhost/cert' - import panelSite - args = mw.dict_obj() - args.siteName = '' - for c_name in os.listdir(cert_paths): - skey_file = '{}/{}/privkey.pem'.format(cert_paths, c_name) - skey = mw.readFile(skey_file) - if not skey: - continue - if skey == pkey: - args.siteName = c_name - run_path = panelSite.panelSite().GetRunPath(args) - if not run_path: - continue - sitePath = mw.M('sites').where( - 'name=?', c_name).getField('path') - if not sitePath: - continue - to_path = "{}/{}".format(sitePath, run_path) - return to_path - return False - - def renewCertOther(self): - cert_path = "{}/vhost/cert".format(mw.getRunDir()) - if not os.path.exists(cert_path): - return - new_time = time.time() + (86400 * 30) - n = 0 - if not 'orders' in self.__config: - self.__config['orders'] = {} - import panelSite - siteObj = panelSite.panelSite() - args = mw.dict_obj() - for siteName in os.listdir(cert_path): - try: - cert_file = '{}/{}/fullchain.pem'.format(cert_path, siteName) - if not os.path.exists(cert_file): - continue # 无证书文件 - siteInfo = mw.M('sites').where('name=?', siteName).find() - if not siteInfo: - continue # 无网站信息 - cert_init = self.getCertInit(cert_file) - if not cert_init: - continue # 无法获取证书 - end_time = time.mktime(time.strptime( - cert_init['notAfter'], '%Y-%m-%d')) - if end_time > new_time: - continue # 未到期 - try: - if not cert_init['issuer'] in ['R3', "Let's Encrypt"] and cert_init['issuer'].find("Let's Encrypt") == -1: - continue # 非同品牌证书 - except: - continue - - if isinstance(cert_init['dns'], str): - cert_init['dns'] = [cert_init['dns']] - index = self.getIndex(cert_init['dns']) - if index in self.__config['orders'].keys(): - continue # 已在订单列表 - - n += 1 - writeLog( - "|-正在续签第 {} 张其它证书,域名: {}..".format(n, cert_init['subject'])) - writeLog("|-正在创建订单..") - args.id = siteInfo['id'] - runPath = siteObj.GetRunPath(args) - if runPath and not runPath in ['/']: - path = siteInfo['path'] + '/' + runPath - else: - path = siteInfo['path'] - except: - write_log("|-[{}]续签失败".format(siteName)) - - # 外部API - START ---------------------------------------------------------- - def applyCertApi(self, args): - ''' - 申请证书 - api - ''' - return self.applyCert(args['domains'], args['auth_type'], args['auth_to']) - # 外部API - END ---------------------------------------------------------- - - def renewCert(self, index): - writeLog("", "wb+") - # self.D('renew_cert', index) - try: - order_index = [] - if index: - if type(index) != str: - index = index.index - if not index in self.__config['orders']: - raise Exception("指定订单号不存在,无法续签!") - order_index.append(index) - else: - start_time = time.time() + (30 * 86400) - # print(self.__config) - if not 'orders' in self.__config: - self.__config['orders'] = {} - - for i in self.__config['orders'].keys(): - # print(self.__config['orders'][i]) - if not 'save_path' in self.__config['orders'][i]: - continue - - if 'cert' in self.__config['orders'][i]: - self.__config['orders'][i]['cert_timeout'] = self.__config[ - 'orders'][i]['cert']['cert_timeout'] - - if not 'cert_timeout' in self.__config['orders'][i]: - self.__config['orders'][i][ - 'cert_timeout'] = int(time.time()) - - if self.__config['orders'][i]['cert_timeout'] > start_time: - writeLog( - "|-本次跳过域名: {},未过期!".format(self.__config['orders'][i]['domains'][0])) - continue - - # 已删除的网站直接跳过续签 - if self.__config['orders'][i]['auth_to'].find('|') == -1 and self.__config['orders'][i]['auth_to'].find('/') != -1: - if not os.path.exists(self.__config['orders'][i]['auth_to']): - auth_to = self.getSslUsedSite( - self.__config['orders'][i]['save_path']) - if not auth_to: - continue - - # 域名不存在? - for domain in self.__config['orders'][i]['domains']: - if domain.find('*') != -1: - break - if not mw.M('domain').where("name=?", (domain,)).count() and not mw.M('binding').where("domain=?", domain).count(): - auth_to = None - writeLog( - "|-跳过被删除的域名: {}".format(self.__config['orders'][i]['domains'])) - if not auth_to: - continue - - self.__config['orders'][i]['auth_to'] = auth_to - - # 是否到了允许重试的时间 - if 'next_retry_time' in self._config['orders'][i]: - timeout = self.__config['orders'][i][ - 'next_retry_time'] - int(time.time()) - if timeout > 0: - writeLog('|-本次跳过域名:{},因第上次续签失败,还需要等待{}小时后再重试'.format( - self.__config['orders'][i]['domains'], int(timeout / 60 / 60))) - continue - - # 是否到了最大重试次数 - if 'retry_count' in self.__config['orders'][i]: - if self.__config['orders'][i]['retry_count'] >= 5: - writeLog('|-本次跳过域名:{},因连续5次续签失败,不再续签此证书(可尝试手动续签此证书,成功后错误次数将被重置)'.format( - self.__config['orders'][i]['domains'])) - continue - - # 加入到续签订单 - order_index.append(i) - if not order_index: - writeLog("|-没有找到30天内到期的SSL证书,正在尝试去寻找其它可续签证书!") - self.getApis() - # self.renewCertOther() - writeLog("|-所有任务已处理完成!") - return - writeLog("|-共需要续签 {} 张证书".format(len(order_index))) - except Exception as ex: - ex = str(ex) - if ex.find(">>>>") != -1: - msg = ex.split(">>>>") - msg[1] = json.loads(msg[1]) - else: - msg = ex - writeLog(mw.getTracebackInfo()) - return mw.returnJson(False, msg) - - def revokeOrder(self, index): - if not index in self.__config['orders']: - raise Exception("指定订单不存在!") - cert_path = self.__config['orders'][index]['save_path'] - if not os.path.exists(cert_path): - raise Exception("指定订单没有找到可用的证书!") - cert = self.dumpDer(cert_path) - if not cert: - raise Exception("证书读取失败!") - payload = { - "certificate": self.calculateSafeBase64(cert), - "reason": 4 - } - - self.getApis() - res = self.acmeRequest(self.__apis['revokeCert'], payload) - if res.status_code in [200, 201]: - if os.path.exists(cert_path): - mw.execShell("rm -rf {}".format(cert_path)) - del(self.__config['orders'][index]) - self.saveConfig() - return mw.returnJson(True, "证书吊销成功!") - return res.json() - - def do(self, args): - cert = None - try: - if not args.index: - if not args.domains: - echoErr("请在--domain参数中指定要申请证书的域名,多个以逗号(,)隔开") - if not args.auth_type in ['http', 'tls']: - echoErr("请在--type参数中指定正确的验证类型,http") - - auth_to = '' - if args.auth_type in ['http', 'tls']: - if not args.path: - echoErr("请在--path参数中指定网站根目录!") - if not os.path.exists(args.path): - echoErr("指定网站根目录不存在,请检查:{}".format(args.path)) - auth_to = args.path - else: - echoErr("仅支持文件验证!") - exit() - - domains = args.domains.strip().split(',') - cert = self.applyCert( - domains, auth_type=args.auth_type, auth_to=auth_to, args=args) - else: - # 重新验证 - cert = self.applyCert([], auth_type='dns', - auth_to='dns', index=args.index) - except Exception as e: - writeLog("|-{}".format(mw.getTracebackInfo())) - exit() - - if not cert: - exit() - - # print(cert) - if not cert['status']: - writeLog('|-' + cert['msg'][0]) - exit() - writeLog("=" * 65) - writeLog("|-证书获取成功!") - writeLog("=" * 65) - writeLog("证书到期时间: {}".format( - mw.formatDate(times=cert['cert_timeout']))) - writeLog("证书已保存在: {}/".format(cert['save_path'])) - - -# exp: -''' -// create -python3 class/core/cert_request.py --domain=dev38.cachecha.com --type=http --path=/www/wwwroot/dev38.cachecha.com -// renew -cd /www/server/mdserver-web && python3 class/core/cert_request.py --renew=1 -// revoke -cd /www/server/mdserver-web && python3 class/core/cert_request.py --revoke=1 --index=370423ed29481b2caf22e36d90a6894a - -python3 class/core/cert_request.py --domain=dev38.cachecha.com --type=http --path=/Users/midoks/Desktop/mwdev/wwwroot/test -python3 class/core/cert_request.py --renew=1 -python3 class/core/cert_request.py --revoke=1 --index=370423ed29481b2caf22e36d90a6894a -''' -if __name__ == "__main__": - p = argparse.ArgumentParser(usage="必要的参数:--domain 域名列表,多个以逗号隔开!") - p.add_argument('--domain', default=None, - help="请指定要申请证书的域名", dest="domains") - p.add_argument('--type', default=None, help="请指定验证类型", dest="auth_type") - p.add_argument('--path', default=None, help="请指定网站根目录", dest="path") - p.add_argument('--index', default=None, help="指定订单索引", dest="index") - p.add_argument('--renew', default=None, help="续签证书", dest="renew") - p.add_argument('--revoke', default=None, help="吊销证书", dest="revoke") - - args = p.parse_args() - cr = cert_request() - - if args.revoke: - if not args.index: - echoErr("请在--index参数中传入要被吊销的订单索引") - result = cr.revokeOrder(args.index) - writeLog(result) - exit() - - if args.renew: - cr.renewCert(args.index) - exit() - - cr.do(args) diff --git a/class/core/common.py b/class/core/common.py deleted file mode 100755 index 9e91a4811..000000000 --- a/class/core/common.py +++ /dev/null @@ -1,146 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 公共操作 -# --------------------------------------------------------------------------------- - -import os -import sys -import time -import string -import json -import hashlib -import shlex -import datetime -import subprocess -import re -import hashlib -from random import Random - -import mw -import db - -from flask import redirect - - -def init(): - initDB() - initUserInfo() - initInitD() - initInitTask() - return True - - -def local(): - result = checkClose() - if result: - return result - - -# 检查面板是否关闭 -def checkClose(): - if os.path.exists('data/close.pl'): - return redirect('/close') - - -def initDB(): - try: - sql = db.Sql().dbfile('default') - csql = mw.readFile('data/sql/default.sql') - csql_list = csql.split(';') - for index in range(len(csql_list)): - sql.execute(csql_list[index], ()) - - except Exception as ex: - print(str(ex)) - - -def doContentReplace(src, dst): - content = mw.readFile(src) - content = content.replace("{$SERVER_PATH}", mw.getRunDir()) - mw.writeFile(dst, content) - - -def initInitD(): - - # systemctl - # sysCfgDir = mw.systemdCfgDir() - # if os.path.exists(sysCfgDir) and mw.getOsName() == 'centos' and mw.getOsID() == '9': - # systemd_mw = sysCfgDir + '/mw.service' - # systemd_mw_task = sysCfgDir + '/mw-task.service' - - # systemd_mw_tpl = mw.getRunDir() + '/scripts/init.d/mw.service.tpl' - # systemd_mw_task_tpl = mw.getRunDir() + '/scripts/init.d/mw-task.service.tpl' - - # if os.path.exists(systemd_mw): - # os.remove(systemd_mw) - # if os.path.exists(systemd_mw_task): - # os.remove(systemd_mw_task) - - # doContentReplace(systemd_mw_tpl, systemd_mw) - # doContentReplace(systemd_mw_task_tpl, systemd_mw_task) - - # mw.execShell('systemctl enable mw') - # mw.execShell('systemctl enable mw-task') - # mw.execShell('systemctl daemon-reload') - - script = mw.getRunDir() + '/scripts/init.d/mw.tpl' - script_bin = mw.getRunDir() + '/scripts/init.d/mw' - doContentReplace(script, script_bin) - mw.execShell('chmod +x ' + script_bin) - - # 在linux系统中,确保/etc/init.d存在 - if not mw.isAppleSystem() and not os.path.exists("/etc/rc.d/init.d"): - mw.execShell('mkdir -p /etc/rc.d/init.d') - - if not mw.isAppleSystem() and not os.path.exists("/etc/init.d"): - mw.execShell('mkdir -p /etc/init.d') - - # initd - if os.path.exists('/etc/rc.d/init.d'): - initd_bin = '/etc/rc.d/init.d/mw' - if not os.path.exists(initd_bin): - import shutil - shutil.copyfile(script_bin, initd_bin) - mw.execShell('chmod +x ' + initd_bin) - # 加入自启动 - mw.execShell('which chkconfig && chkconfig --add mw') - - if os.path.exists('/etc/init.d'): - initd_bin = '/etc/init.d/mw' - if not os.path.exists(initd_bin): - import shutil - shutil.copyfile(script_bin, initd_bin) - mw.execShell('chmod +x ' + initd_bin) - # 加入自启动 - mw.execShell('which update-rc.d && update-rc.d -f mw defaults') - - # 获取系统IPV4 - mw.setHostAddr(mw.getLocalIp()) - - -def initInitTask(): - # 创建证书同步命令 - import cert_api - api = cert_api.cert_api() - api.createCertCron() - - -def initUserInfo(): - - data = mw.M('users').where('id=?', (1,)).getField('password') - if data == '21232f297a57a5a743894a0e4a801fc3': - - pwd = mw.getRandomString(8).lower() - file_pw = mw.getRunDir() + '/data/default.pl' - mw.writeFile(file_pw, pwd) - mw.M('users').where('id=?', (1,)).setField( - 'password', mw.md5(pwd)) diff --git a/class/core/config_api.py b/class/core/config_api.py deleted file mode 100755 index 9a89bd4d0..000000000 --- a/class/core/config_api.py +++ /dev/null @@ -1,1191 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 配置操作 -# --------------------------------------------------------------------------------- - -import psutil -import time -import os -import sys -import mw -import re -import json -import pwd -import pytz - -from flask import session -from flask import request - - -class config_api: - - __version = '0.17.4' - __api_addr = 'data/api.json' - - # 统一默认配置文件 - __file = { - 'api' : 'data/api.json', # API文件 - 'debug' : 'data/debug.pl', # DEBUG文件 - 'close' : 'data/close.pl', # 识别关闭面板文件 - 'basic_auth' : 'data/basic_auth.json', # 面板Basic验证 - 'admin_path' : 'data/admin_path.pl', # 面板后缀路径设置 - 'ipv6' : 'data/ipv6.pl', # ipv6识别文件 - 'bind_domain' : 'data/bind_domain.pl', # 面板域名绑定 - 'unauth_status' : 'data/unauthorized_status.pl', # URL路径未成功显示状态 - 'auth_secret': 'data/auth_secret.pl', # 二次验证密钥 - 'ssl':'ssl/choose.pl', # ssl设置 - 'hook_database' : 'data/hook_database.json', # 数据库钩子 - 'hook_menu' : 'data/hook_menu.json', # 菜单钩子 - 'hook_global_static' : 'data/hook_global_static.json', # 静态文件钩子 - } - - def __init__(self): - pass - - def getVersion(self): - return self.__version - - ##### ----- start ----- ### - - # 取面板列表 - def getPanelListApi(self): - data = mw.M('panel').field( - 'id,title,url,username,password,click,addtime').order('click desc').select() - return mw.getJson(data) - - def addPanelInfoApi(self): - title = request.form.get('title', '') - url = request.form.get('url', '') - username = request.form.get('username', '') - password = request.form.get('password', '') - # 校验是还是重复 - isAdd = mw.M('panel').where( - 'title=? OR url=?', (title, url)).count() - if isAdd: - return mw.returnJson(False, '备注或面板地址重复!') - isRe = mw.M('panel').add('title,url,username,password,click,addtime', - (title, url, username, password, 0, int(time.time()))) - if isRe: - return mw.returnJson(True, '添加成功!') - return mw.returnJson(False, '添加失败!') - - # 删除面板资料 - def delPanelInfoApi(self): - mid = request.form.get('id', '') - isExists = mw.M('panel').where('id=?', (mid,)).count() - if not isExists: - return mw.returnJson(False, '指定面板资料不存在!') - mw.M('panel').where('id=?', (mid,)).delete() - return mw.returnJson(True, '删除成功!') - - # 修改面板资料 - def setPanelInfoApi(self): - title = request.form.get('title', '') - url = request.form.get('url', '') - username = request.form.get('username', '') - password = request.form.get('password', '') - mid = request.form.get('id', '') - # 校验是还是重复 - isSave = mw.M('panel').where( - '(title=? OR url=?) AND id!=?', (title, url, mid)).count() - if isSave: - return mw.returnJson(False, '备注或面板地址重复!') - - # 更新到数据库 - isRe = mw.M('panel').where('id=?', (mid,)).save( - 'title,url,username,password', (title, url, username, password)) - if isRe: - return mw.returnJson(True, '修改成功!') - return mw.returnJson(False, '修改失败!') - - def syncDateApi(self): - if mw.isAppleSystem(): - return mw.returnJson(True, '开发系统不必同步时间!') - - data = mw.execShell('ntpdate -s time.nist.gov') - if data[0] == '': - return mw.returnJson(True, '同步成功!') - return mw.returnJson(False, '同步失败:' + data[0]) - - def setPasswordApi(self): - password1 = request.form.get('password1', '') - password2 = request.form.get('password2', '') - if password1 != password2: - return mw.returnJson(False, '两次输入的密码不一致,请重新输入!') - if len(password1) < 5: - return mw.returnJson(False, '用户密码不能小于5位!') - mw.M('users').where("username=?", (session['username'],)).setField( - 'password', mw.md5(password1.strip())) - return mw.returnJson(True, '密码修改成功!') - - def setNameApi(self): - name1 = request.form.get('name1', '') - name2 = request.form.get('name2', '') - if name1 != name2: - return mw.returnJson(False, '两次输入的用户名不一致,请重新输入!') - if len(name1) < 3: - return mw.returnJson(False, '用户名长度不能少于3位') - - mw.M('users').where("username=?", (session['username'],)).setField( - 'username', name1.strip()) - - session['username'] = name1 - return mw.returnJson(True, '用户修改成功!') - - def setWebnameApi(self): - webname = request.form.get('webname', '') - if webname != mw.getConfig('title'): - mw.setConfig('title', webname) - return mw.returnJson(True, '面板别名保存成功!') - - def setPortApi(self): - port = request.form.get('port', '') - if port != mw.getHostPort(): - import system_api - import firewall_api - - sysCfgDir = mw.systemdCfgDir() - if os.path.exists(sysCfgDir + "/firewalld.service"): - if not firewall_api.firewall_api().getFwStatus(): - return mw.returnJson(False, 'firewalld必须先启动!') - - mw.setHostPort(port) - - msg = mw.getInfo('放行端口[{1}]成功', (port,)) - mw.writeLog("防火墙管理", msg) - addtime = time.strftime('%Y-%m-%d %X', time.localtime()) - mw.M('firewall').add('port,ps,addtime', (port, "配置修改", addtime)) - - firewall_api.firewall_api().addAcceptPort(port) - firewall_api.firewall_api().firewallReload() - - system_api.system_api().restartMw() - - return mw.returnJson(True, '端口保存成功!') - - def setIpApi(self): - host_ip = request.form.get('host_ip', '') - if host_ip != mw.getHostAddr(): - mw.setHostAddr(host_ip) - return mw.returnJson(True, 'IP保存成功!') - - def setWwwDirApi(self): - sites_path = request.form.get('sites_path', '') - if sites_path != mw.getWwwDir(): - mw.setWwwDir(sites_path) - return mw.returnJson(True, '修改默认建站目录成功!') - - def setBackupDirApi(self): - backup_path = request.form.get('backup_path', '') - if backup_path != mw.getBackupDir(): - mw.setBackupDir(backup_path) - return mw.returnJson(True, '修改默认备份目录成功!') - - def setBasicAuthApi(self): - basic_user = request.form.get('basic_user', '').strip() - basic_pwd = request.form.get('basic_pwd', '').strip() - basic_open = request.form.get('is_open', '').strip() - - salt = '_md_salt' - path = self.__file['basic_auth'] - is_open = True - - if basic_open == 'false': - if os.path.exists(path): - os.remove(path) - return mw.returnJson(True, '删除BasicAuth成功!') - - if basic_user == '' or basic_pwd == '': - return mw.returnJson(True, '用户和密码不能为空!') - - ba_conf = None - if os.path.exists(path): - try: - ba_conf = json.loads(public.readFile(path)) - except: - os.remove(path) - - if not ba_conf: - ba_conf = { - "basic_user": mw.md5(basic_user + salt), - "basic_pwd": mw.md5(basic_pwd + salt), - "open": is_open - } - else: - ba_conf['basic_user'] = mw.md5(basic_user + salt) - ba_conf['basic_pwd'] = mw.md5(basic_pwd + salt) - ba_conf['open'] = is_open - - mw.writeFile(path, json.dumps(ba_conf)) - os.chmod(path, 384) - mw.writeLog('面板设置', '设置BasicAuth状态为: %s' % is_open) - - mw.restartMw() - return mw.returnJson(True, '设置成功!') - - def setApi(self): - webname = request.form.get('webname', '') - port = request.form.get('port', '') - host_ip = request.form.get('host_ip', '') - domain = request.form.get('domain', '') - sites_path = request.form.get('sites_path', '') - backup_path = request.form.get('backup_path', '') - - if domain != '': - reg = "^([\\w\\-\\*]{1,100}\\.){1,4}(\\w{1,10}|\\w{1,10}\\.\\w{1,10})$" - if not re.match(reg, domain): - return mw.returnJson(False, '主域名格式不正确') - - if int(port) >= 65535 or int(port) < 100: - return mw.returnJson(False, '端口范围不正确!') - - if webname != mw.getConfig('title'): - mw.setConfig('title', webname) - - if sites_path != mw.getWwwDir(): - mw.setWwwDir(sites_path) - - if backup_path != mw.getWwwDir(): - mw.setBackupDir(backup_path) - - if port != mw.getHostPort(): - import system_api - import firewall_api - - sysCfgDir = mw.systemdCfgDir() - if os.path.exists(sysCfgDir + "/firewalld.service"): - if not firewall_api.firewall_api().getFwStatus(): - return mw.returnJson(False, 'firewalld必须先启动!') - - mw.setHostPort(port) - - msg = mw.getInfo('放行端口[{1}]成功', (port,)) - mw.writeLog("防火墙管理", msg) - addtime = time.strftime('%Y-%m-%d %X', time.localtime()) - mw.M('firewall').add('port,ps,addtime', (port, "配置修改", addtime)) - - firewall_api.firewall_api().addAcceptPort(port) - firewall_api.firewall_api().firewallReload() - - system_api.system_api().restartMw() - - if host_ip != mw.getHostAddr(): - mw.setHostAddr(host_ip) - - mhost = mw.getHostAddr() - info = { - 'uri': '/config', - 'host': mhost + ':' + port - } - return mw.returnJson(True, '保存成功!', info) - - def setAdminPathApi(self): - admin_path = request.form.get('admin_path', '').strip() - admin_path_checks = ['/', '/close', '/login', - '/do_login', '/site', '/sites', - '/download_file', '/control', '/crontab', - '/firewall', '/files', 'config', - '/soft', '/system', '/code', - '/ssl', '/plugins', '/hook'] - if admin_path == '': - admin_path = '/' - if admin_path != '/': - if len(admin_path) < 6: - return mw.returnJson(False, '安全入口地址长度不能小于6位!') - if admin_path in admin_path_checks: - return mw.returnJson(False, '该入口已被面板占用,请使用其它入口!') - if not re.match("^/[\\w\\./-_]+$", admin_path): - return mw.returnJson(False, '入口地址格式不正确,示例: /mw_rand') - # else: - # domain = mw.readFile('data/bind_domain.pl') - # if not domain: - # domain = '' - # limitip = mw.readFile('data/bind_limitip.pl') - # if not limitip: - # limitip = '' - # if not domain.strip() and not limitip.strip(): - # return mw.returnJson(False, - # '警告,关闭安全入口等于直接暴露你的后台地址在外网,十分危险,至少开启以下一种安全方式才能关闭:
1、绑定访问域名
2、绑定授权IP
') - - admin_path_file = self.__file['admin_path'] - admin_path_old = '/' - if os.path.exists(admin_path_file): - admin_path_old = mw.readFile(admin_path_file).strip() - - if admin_path_old != admin_path: - mw.writeFile(admin_path_file, admin_path) - mw.restartMw() - return mw.returnJson(True, '修改成功!') - - def closePanelApi(self): - filename = self.__file['close'] - if os.path.exists(filename): - os.remove(filename) - return mw.returnJson(True, '开启成功') - mw.writeFile(filename, 'True') - mw.execShell("chmod 600 " + filename) - mw.execShell("chown root.root " + filename) - return mw.returnJson(True, '面板已关闭!') - - def openDebugApi(self): - filename = 'data/debug.pl' - if os.path.exists(filename): - os.remove(filename) - return mw.returnJson(True, '开发模式关闭!') - mw.writeFile(filename, 'True') - return mw.returnJson(True, '开发模式开启!') - - def setIpv6StatusApi(self): - ipv6_file = self.__file['ipv6'] - if os.path.exists(ipv6_file): - os.remove(ipv6_file) - mw.writeLog('面板设置', '关闭面板IPv6兼容!') - else: - mw.writeFile(ipv6_file, 'True') - mw.writeLog('面板设置', '开启面板IPv6兼容!') - mw.restartMw() - return mw.returnJson(True, '设置成功!') - - # 获取面板证书 - def getPanelSslApi(self): - cert = self.getPanelSslData() - return mw.getJson(cert) - - def getPanelSslData(self): - rdata = {} - choose_file = self.__file['ssl'] - rdata['choose'] = mw.readFile(choose_file) - - - keyPath = 'ssl/local/private.pem' - certPath = 'ssl/local/cert.pem' - - if not os.path.exists(certPath): - mw.createLocalSSL() - - cert = {} - cert['privateKey'] = mw.readFile(keyPath) - cert['is_https'] = '' - cert['certPem'] = mw.readFile(certPath) - cert['info'] = mw.getCertName(certPath) - rdata['local'] = cert - - panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf" - if not os.path.exists(panel_ssl): - cert['is_https'] = '' - else: - ssl_data = mw.readFile(panel_ssl) - if ssl_data.find('$server_port !~ 443') != -1: - cert['is_https'] = 'checked' - - keyPath = 'ssl/nginx/private.pem' - certPath = 'ssl/nginx/cert.pem' - - cert = {} - cert['privateKey'] = mw.readFile(keyPath) - cert['certPem'] = mw.readFile(certPath) - cert['info'] = mw.getCertName(certPath) - - rdata['nginx'] = cert - - return rdata - - # 面板本地SSL设置 - def setPanelLocalSslApi(self): - cert = {} - keyPath = 'ssl/local/private.pem' - certPath = 'ssl/local/cert.pem' - - if not os.path.exists(certPath): - mw.createLocalSSL() - - choose_file = self.__file['ssl'] - mw.writeFile(choose_file, 'local') - - mw.restartMw() - return mw.returnJson(True, '设置成功') - - # 关闭SSL - def closePanelSslApi(self): - choose_file = self.__file['ssl'] - if os.path.exists(choose_file): - os.remove(choose_file) - - local_ssl = 'ssl/local' - if os.path.exists(local_ssl): - mw.execShell('rm -rf '+ local_ssl) - - nginx_ssl = 'ssl/nginx' - if os.path.exists(nginx_ssl): - mw.execShell('rm -rf '+ nginx_ssl) - - mw.restartMw() - return mw.returnJson(True, '关闭SSL成功') - - # 保存面板证书 - def savePanelSslApi(self): - - choose = request.form.get('choose', '').strip() - certPem = request.form.get('certPem', '').strip() - privateKey = request.form.get('privateKey', '').strip() - - if not mw.inArray(['local','nginx'], choose): - return mw.returnJson(True, '保存错误面板SSL类型!') - - - keyPath = 'ssl/'+choose+'/private.pem' - certPath = 'ssl/'+choose+'/cert.pem' - checkCert = '/tmp/cert.pl' - - if not os.path.exists(keyPath): - return mw.returnJson(False, '【'+choose+'】SSL类型不存在,先申请!') - - if(privateKey.find('KEY') == -1): - return mw.returnJson(False, '秘钥错误,请检查!') - if(certPem.find('CERTIFICATE') == -1): - return mw.returnJson(False, '证书错误,请检查!') - - mw.writeFile(checkCert, certPem) - if privateKey: - mw.writeFile(keyPath, privateKey) - if certPem: - mw.writeFile(certPath, certPem) - if not mw.checkCert(checkCert): - return mw.returnJson(False, '证书错误,请检查!') - return mw.returnJson(True, '证书已保存!') - - # 设置面板SSL证书设置 - # def setPanelHttpToHttpsApi(self): - - # bind_domain = self.__file['bind_domain'] - # if not os.path.exists(bind_domain): - # return mw.returnJson(False, '先要绑定域名!') - - # choose_file = self.__file['ssl'] - # choose = mw.readFile(choose_file) - # if choose == 'local': - # return mw.returnJson(False, '本地SSL无法使用!') - - # keyPath = 'ssl/nginx/private.pem' - # if not os.path.exists(keyPath): - # return mw.returnJson(False, '未申请SSL证书!') - - # is_https = request.form.get('https', '').strip() - - # panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf" - # if not os.path.exists(panel_ssl): - # return mw.returnJson(False, '未开启面板SSL!') - - # if is_https == 'false': - # conf = mw.readFile(panel_ssl) - # if conf: - # if conf.find('ssl_certificate') == -1: - # return mw.returnJson(False, '当前未开启SSL') - # to = "#error_page 404/404.html;\n\ - # #HTTP_TO_HTTPS_START\n\ - # if ($server_port !~ 443){\n\ - # rewrite ^(/.*)$ https://$host$1 permanent;\n\ - # }\n\ - # #HTTP_TO_HTTPS_END" - # conf = conf.replace('#error_page 404/404.html;', to) - # mw.writeFile(panel_ssl, conf) - # else: - # conf = mw.readFile(panel_ssl) - # if conf: - # rep = "\n\\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END" - # conf = re.sub(rep, '', conf) - # rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}" - # conf = re.sub(rep, '', conf) - # mw.writeFile(panel_ssl, conf) - - # mw.restartNginx() - - # action = '开启' - # if is_https == 'true': - # action = '关闭' - # return mw.returnJson(True, action + 'HTTPS跳转成功!') - - # 删除面板证书 - def delPanelSslApi(self): - ip = mw.getLocalIp() - if mw.isAppleSystem(): - ip = '127.0.0.1' - - port = mw.readFile('data/port.pl').strip() - - choose = request.form.get('choose', '').strip() - - if not mw.inArray(['local','nginx'], choose): - return mw.returnJson(True, '删除错误面板SSL类型!') - - to_panel_url = 'http://'+ip+":"+port+'/config' - - if choose == 'local': - dst_path = mw.getRunDir() + '/ssl/local' - ssl_file = self.__file['ssl'] - if os.path.exists(dst_path): - mw.execShell('rm -rf ' + dst_path) - mw.execShell('rm -rf ' + ssl_file) - mw.restartMw(); - return mw.returnJson(True, '删除本地面板SSL成功!',to_panel_url) - else: - return mw.returnJson(True, '已经删除本地面板SSL!',to_panel_url) - - if choose == 'nginx': - - bind_domain = self.__file['bind_domain'] - if not os.path.exists(bind_domain): - return mw.returnJson(False, '未绑定域名!') - - siteName = mw.readFile(bind_domain).strip() - - src_path = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName - - dst_path = mw.getRunDir() + '/ssl/nginx' - dst_csrpath = dst_path + '/cert.pem' - dst_keypath = dst_path + '/private.pem' - - if os.path.exists(src_path) or os.path.exists(dst_path): - if os.path.exists(src_letpath): - mw.execShell('rm -rf ' + src_letpath) - if os.path.exists(dst_csrpath): - mw.execShell('rm -rf ' + dst_csrpath) - if os.path.exists(dst_keypath): - mw.execShell('rm -rf ' + dst_keypath) - mw.restartNginx() - return mw.returnJson(True, '删除面板SSL成功!') - - mw.restartNginx() - mw.restartMw() - return mw.returnJson(False, '已经删除面板SSL!') - return mw.returnJson(False, '未知类型!') - - # 申请面板let证书 - # def applyPanelAcmeSslApi(self): - - # bind_domain = self.__file['bind_domain'] - # if not os.path.exists(bind_domain): - # return mw.returnJson(False, '先要绑定域名!') - - # # 生成nginx配置 - # domain = mw.readFile(bind_domain) - # panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf" - # dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf" - # if not os.path.exists(dst_panel_path): - # reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$" - # if not re.match(reg, domain): - # return mw.returnJson(False, '主域名格式不正确') - - # op_dir = mw.getServerDir() + "/openresty" - # if not os.path.exists(op_dir): - # return mw.returnJson(False, '依赖OpenResty,先安装启动它!') - - # content = mw.readFile(panel_tpl) - # content = content.replace("{$PORT}", "80") - # content = content.replace("{$SERVER_NAME}", domain) - # content = content.replace("{$PANAL_PORT}", mw.readFile('data/port.pl')) - # content = content.replace("{$LOGPATH}", mw.getRunDir() + '/logs') - # content = content.replace("{$PANAL_ADDR}", mw.getRunDir()) - # mw.writeFile(dst_panel_path, content) - # mw.restartNginx() - - # siteName = mw.readFile(bind_domain).strip() - # auth_to = mw.getRunDir() + "/tmp" - # to_args = { - # 'domains': [siteName], - # 'auth_type': 'http', - # 'auth_to': auth_to, - # } - - # src_path = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName - # src_csrpath = src_path + "/fullchain.pem" # 生成证书路径 - # src_keypath = src_path + "/privkey.pem" # 密钥文件路径 - - # dst_path = mw.getRunDir() + '/ssl/nginx' - # dst_csrpath = dst_path + '/cert.pem' - # dst_keypath = dst_path + '/private.pem' - - # is_already_apply = False - - # if not os.path.exists(src_path): - # import cert_api - # data = cert_api.cert_api().applyCertApi(to_args) - # if not data['status']: - # msg = data['msg'] - # if type(data['msg']) != str: - # msg = data['msg'][0] - # emsg = data['msg'][1]['challenges'][0]['error'] - # msg = msg + '

响应状态:' + str(emsg['status']) + '

错误类型:' + emsg[ - # 'type'] + '

错误代码:' + emsg['detail'] + '

' - # return mw.returnJson(data['status'], msg, data['msg']) - # else: - # is_already_apply = True - - # mw.buildSoftLink(src_csrpath, dst_csrpath, True) - # mw.buildSoftLink(src_keypath, dst_keypath, True) - # mw.execShell('echo "acme" > "' + dst_path + '/README"') - - # tmp_well_know = auth_to + '/.well-known' - # if os.path.exists(tmp_well_know): - # mw.execShell('rm -rf ' + tmp_well_know) - - # if os.path.exists(dst_path): - # choose_file = self.__file['ssl'] - # mw.writeFile(choose_file, 'nginx') - - # data = self.getPanelSslData() - - # if is_already_apply: - # return mw.returnJson(True, '重复申请!', data) - # return mw.returnJson(True, '申请成功!', data) - - def setPanelDomainApi(self): - domain = request.form.get('domain', '') - - cfg_domain = self.__file['bind_domain'] - - port = mw.readFile('data/port.pl').strip() - ip = mw.getLocalIp() - - if domain == '': - if os.path.exists(cfg_domain): - os.remove(cfg_domain) - - to_panel_url = 'http://'+ip+":"+port+'/config' - - mw.restartMw() - return mw.returnJson(True, '清空域名成功!', to_panel_url) - - mw.writeFile(cfg_domain, domain) - to_panel_url = 'http://'+domain+":"+port+'/config' - mw.restartMw() - return mw.returnJson(True, '设置域名成功!',to_panel_url) - - # 设置面板SSL - # def setPanelSslApi(self): - # sslConf = mw.getRunDir() + '/' + self.__file['ssl'] - - # panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf" - # dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf" - # if os.path.exists(sslConf): - # os.system('rm -f ' + sslConf) - - # conf = mw.readFile(dst_panel_path) - # if conf: - # rep = "\\s+ssl_certificate\\s+.+;\\s+ssl_certificate_key\\s+.+;" - # conf = re.sub(rep, '', conf) - # rep = "\\s+ssl_protocols\\s+.+;\n" - # conf = re.sub(rep, '', conf) - # rep = "\\s+ssl_ciphers\\s+.+;\n" - # conf = re.sub(rep, '', conf) - # rep = "\\s+ssl_prefer_server_ciphers\\s+.+;\n" - # conf = re.sub(rep, '', conf) - # rep = "\\s+ssl_session_cache\\s+.+;\n" - # conf = re.sub(rep, '', conf) - # rep = "\\s+ssl_session_timeout\\s+.+;\n" - # conf = re.sub(rep, '', conf) - # rep = "\\s+ssl_ecdh_curve\\s+.+;\n" - # conf = re.sub(rep, '', conf) - # rep = "\\s+ssl_session_tickets\\s+.+;\n" - # conf = re.sub(rep, '', conf) - # rep = "\\s+ssl_stapling\\s+.+;\n" - # conf = re.sub(rep, '', conf) - # rep = "\\s+ssl_stapling_verify\\s+.+;\n" - # conf = re.sub(rep, '', conf) - # rep = "\\s+ssl\\s+on;" - # conf = re.sub(rep, '', conf) - # rep = "\\s+error_page\\s497.+;" - # conf = re.sub(rep, '', conf) - # rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}" - # conf = re.sub(rep, '', conf) - # rep = "\\s+listen\\s+443.*;" - # conf = re.sub(rep, '', conf) - # rep = "\\s+listen\\s+\\[\\:\\:\\]\\:443.*;" - # conf = re.sub(rep, '', conf) - # mw.writeFile(dst_panel_path, conf) - - # mw.writeLog('面板配置', '面板SSL关闭成功!') - # mw.restartWeb() - # return mw.returnJson(True, 'SSL已关闭,请使用http协议访问面板!') - # else: - # try: - # mw.writeFile(sslConf, 'True') - - # keyPath = mw.getRunDir() + '/ssl/private.pem' - # certPath = mw.getRunDir() + '/ssl/cert.pem' - - # conf = mw.readFile(dst_panel_path) - # if conf: - # if conf.find('ssl_certificate') == -1: - # sslStr = """#error_page 404/404.html; - # ssl_certificate %s; - # ssl_certificate_key %s; - # ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - # ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; - # ssl_prefer_server_ciphers on; - # ssl_session_cache shared:SSL:10m; - # ssl_session_timeout 10m; - # error_page 497 https://$host$request_uri;""" % (certPath, keyPath) - # if(conf.find('ssl_certificate') != -1): - # return mw.returnJson(True, 'SSL开启成功!') - - # conf = conf.replace('#error_page 404/404.html;', sslStr) - - # rep = "listen\\s+([0-9]+)\\s*[default_server]*;" - # tmp = re.findall(rep, conf) - # if not mw.inArray(tmp, '443'): - # listen = re.search(rep, conf).group() - # http_ssl = "\n\tlisten 443 ssl http2;" - # http_ssl = http_ssl + "\n\tlisten [::]:443 ssl http2;" - # conf = conf.replace(listen, listen + http_ssl) - - # mw.backFile(dst_panel_path) - # mw.writeFile(dst_panel_path, conf) - # isError = mw.checkWebConfig() - # if(isError != True): - # mw.restoreFile(dst_panel_path) - # return mw.returnJson(False, '证书错误:
' + isError.replace("\n", '
') + '
') - # except Exception as ex: - # return mw.returnJson(False, '开启失败:' + str(ex)) - # mw.restartWeb() - # return mw.returnJson(True, '开启成功,请使用https协议访问面板!') - - def getApi(self): - data = {} - return mw.getJson(data) - ##### ----- end ----- ### - - # 获取临时登录列表 - def getTempLoginApi(self): - if 'tmp_login_expire' in session: - return mw.returnJson(False, '没有权限') - limit = request.form.get('limit', '10').strip() - p = request.form.get('p', '1').strip() - tojs = request.form.get('tojs', '').strip() - - tempLoginM = mw.M('temp_login') - tempLoginM.where('state=? and expire?', (0, s_time)).delete() - token = mw.getRandomString(48) - salt = mw.getRandomString(12) - - pdata = { - 'token': mw.md5(token + salt), - 'salt': salt, - 'state': 0, - 'login_time': 0, - 'login_addr': '', - 'expire': s_time + 3600, - 'addtime': s_time - } - - if not mw.M('temp_login').count(): - pdata['id'] = 101 - - if mw.M('temp_login').insert(pdata): - mw.writeLog('面板设置', '生成临时连接,过期时间:{}'.format( - mw.formatDate(times=pdata['expire']))) - return mw.getJson({'status': True, 'msg': "临时连接已生成", 'token': token, 'expire': pdata['expire']}) - return mw.returnJson(False, '连接生成失败') - - def getTempLoginLogsApi(self): - if 'tmp_login_expire' in session: - return mw.returnJson(False, '没有权限') - - logs_id = request.form.get('id', '').strip() - logs_id = int(logs_id) - data = mw.M('logs').where( - 'uid=?', (logs_id,)).order('id desc').field( - 'id,type,uid,log,addtime').select() - return mw.returnJson(False, 'ok', data) - - def checkPanelToken(self): - api_file = self.__api_addr - if not os.path.exists(api_file): - return False, '' - - tmp = mw.readFile(api_file) - data = json.loads(tmp) - if data['open']: - return True, data - else: - return False, '' - - def setStatusCodeApi(self): - status_code = request.form.get('status_code', '').strip() - if re.match("^\\d+$", status_code): - status_code = int(status_code) - if status_code != 0: - if status_code < 100 or status_code > 999: - return mw.returnJson(False, '状态码范围错误!') - else: - return mw.returnJson(False, '状态码范围错误!') - unauthorized_status = self.__file['unauth_status'] - mw.writeFile(unauthorized_status, str(status_code)) - mw.writeLog('面板设置', '将未授权响应状态码设置为:{}'.format(status_code)) - return mw.returnJson(True, '设置成功!') - - def getNotifyApi(self): - # 获取 - data = mw.getNotifyData(True) - return mw.returnData(True, 'ok', data) - - def setNotifyApi(self): - tag = request.form.get('tag', '').strip() - data = request.form.get('data', '').strip() - - cfg = mw.getNotifyData(False) - - crypt_data = mw.enDoubleCrypt(tag, data) - if tag in cfg: - cfg[tag]['cfg'] = crypt_data - else: - t = {'cfg': crypt_data} - cfg[tag] = t - - mw.writeNotify(cfg) - return mw.returnData(True, '设置成功') - - def setNotifyTestApi(self): - # 异步通知验证 - tag = request.form.get('tag', '').strip() - tag_data = request.form.get('data', '').strip() - - if tag == 'tgbot': - t = json.loads(tag_data) - test_bool = mw.tgbotNotifyTest(t['app_token'], t['chat_id']) - if test_bool: - return mw.returnData(True, '验证成功') - return mw.returnData(False, '验证失败') - - if tag == 'email': - t = json.loads(tag_data) - test_bool = mw.emailNotifyTest(t) - if test_bool: - return mw.returnData(True, '验证成功') - return mw.returnData(False, '验证失败') - - return mw.returnData(False, '暂时未支持该验证') - - def setNotifyEnableApi(self): - # 异步通知验证 - tag = request.form.get('tag', '').strip() - tag_enable = request.form.get('enable', '').strip() - - data = mw.getNotifyData(False) - op_enable = True - op_action = '开启' - if tag_enable != 'true': - op_enable = False - op_action = '关闭' - - if tag in data: - data[tag]['enable'] = op_enable - - mw.writeNotify(data) - - return mw.returnData(True, op_action + '成功') - - def getPanelTokenApi(self): - api_file = self.__api_addr - tmp = mw.readFile(api_file) - if not os.path.exists(api_file): - ready_data = {"open": False, "token": "", "limit_addr": []} - mw.writeFile(api_file, json.dumps(ready_data)) - mw.execShell("chmod 600 " + api_file) - tmp = mw.readFile(api_file) - data = json.loads(tmp) - - if not 'key' in data: - data['key'] = mw.getRandomString(16) - mw.writeFile(api_file, json.dumps(data)) - - if 'token_crypt' in data: - data['token'] = mw.deCrypt(data['token'], data['token_crypt']) - else: - token = mw.getRandomString(32) - data['token'] = mw.md5(token) - data['token_crypt'] = mw.enCrypt( - data['token'], token) - mw.writeFile(api_file, json.dumps(data)) - data['token'] = "***********************************" - - data['limit_addr'] = '\n'.join(data['limit_addr']) - - del(data['key']) - return mw.returnJson(True, 'ok', data) - - def setPanelTokenApi(self): - op_type = request.form.get('op_type', '').strip() - - if op_type == '1': - token = mw.getRandomString(32) - data['token'] = mw.md5(token) - data['token_crypt'] = mw.enCrypt( - data['token'], token).decode('utf-8') - mw.writeLog('API配置', '重新生成API-Token') - mw.writeFile(api_file, json.dumps(data)) - return mw.returnJson(True, 'ok', token) - - api_file = self.__api_addr - if not os.path.exists(api_file): - return mw.returnJson(False, "先在API接口配置") - else: - tmp = mw.readFile(api_file) - data = json.loads(tmp) - - if op_type == '2': - data['open'] = not data['open'] - stats = {True: '开启', False: '关闭'} - if not 'token_crypt' in data: - token = mw.getRandomString(32) - data['token'] = mw.md5(token) - data['token_crypt'] = mw.enCrypt(data['token'], token).decode('utf-8') - - token = stats[data['open']] + '成功!' - mw.writeLog('API配置', '%sAPI接口' % stats[data['open']]) - mw.writeFile(api_file, json.dumps(data)) - return mw.returnJson(not not data['open'], token) - - elif op_type == '3': - - limit_addr = request.form.get('limit_addr', '').strip() - data['limit_addr'] = limit_addr.split('\n') - mw.writeLog('API配置', '变更IP限制为[%s]' % limit_addr) - mw.writeFile(api_file, json.dumps(data)) - return mw.returnJson(True, '保存成功!') - - def renderUnauthorizedStatus(self, data): - cfg_unauth_status = self.__file['unauth_status'] - if os.path.exists(cfg_unauth_status): - status_code = mw.readFile(cfg_unauth_status) - data['status_code'] = status_code - data['status_code_msg'] = status_code - if status_code == '0': - data['status_code_msg'] = "默认-安全入口错误提示" - elif status_code == '400': - data['status_code_msg'] = "400-客户端请求错误" - elif status_code == '401': - data['status_code_msg'] = "401-未授权访问" - elif status_code == '403': - data['status_code_msg'] = "403-拒绝访问" - elif status_code == '404': - data['status_code_msg'] = "404-页面不存在" - elif status_code == '408': - data['status_code_msg'] = "408-客户端超时" - elif status_code == '416': - data['status_code_msg'] = "416-无效的请求" - else: - data['status_code'] = '0' - data['status_code_msg'] = "默认-安全入口错误提示" - return data - - - def getTimezoneListApi(self): - # 获取时区列表 - # pytz.all_timezones | 所有 - # pytz.common_timezones - return pytz.all_timezones - - def setTimezoneApi(self): - # 设置时区列表 - timezone = request.form.get('timezone', '').strip() - cmd = 'timedatectl set-timezone "'+timezone+'"' - mw.execShell(cmd) - return mw.returnJson(True, '设置成功!') - - - def getAuthSecretApi(self): - reset = request.form.get('reset', '') - - import pyotp - auth = self.__file['auth_secret'] - tag = 'mdserver-web' - if os.path.exists(auth) and reset != '1': - content = mw.readFile(auth) - sec = mw.deDoubleCrypt(tag,content) - else: - sec = pyotp.random_base32() - crypt_data = mw.enDoubleCrypt(tag, sec) - mw.writeFile(auth, crypt_data) - - ip = mw.getHostAddr() - url = pyotp.totp.TOTP(sec).provisioning_uri(name=ip, issuer_name=tag) - - rdata = {} - rdata['secret'] = sec - rdata['url'] = url - return mw.returnJson(True, '设置成功!', rdata) - - def setAuthSecretApi(self): - auth = self.__file['auth_secret'] - if os.path.exists(auth): - os.remove(auth) - return mw.returnJson(True, '关闭成功!', 0) - else: - return mw.returnJson(True, '开启成功!', 1) - - - def get(self): - - data = {} - data['title'] = mw.getConfig('title') - data['site_path'] = mw.getWwwDir() - data['backup_path'] = mw.getBackupDir() - sformat = 'date +"%Y-%m-%d %H:%M:%S %Z %z"' - data['systemdate'] = mw.execShell(sformat)[0].strip() - - data['port'] = mw.getHostPort() - data['ip'] = mw.getHostAddr() - - admin_path_file = self.__file['admin_path'] - if not os.path.exists(admin_path_file): - data['admin_path'] = '/' - else: - data['admin_path'] = mw.readFile(admin_path_file) - - ipv6_file = self.__file['ipv6'] - if os.path.exists(ipv6_file): - data['ipv6'] = 'checked' - else: - data['ipv6'] = '' - - debug_file = self.__file['debug'] - if os.path.exists(debug_file): - data['debug'] = 'checked' - else: - data['debug'] = '' - - ssl_file = self.__file['ssl'] - if os.path.exists(ssl_file): - data['ssl'] = 'checked' - else: - data['ssl'] = '' - - - auth_secret = self.__file['auth_secret'] - if os.path.exists(auth_secret): - data['auth_secret'] = 'checked' - else: - data['auth_secret'] = '' - - basic_auth = self.__file['basic_auth'] - if os.path.exists(basic_auth): - bac = mw.readFile(basic_auth) - bac = json.loads(bac) - if bac['open']: - data['basic_auth'] = 'checked' - else: - data['basic_auth'] = '' - - cfg_domain = self.__file['bind_domain'] - if os.path.exists(cfg_domain): - domain = mw.readFile(cfg_domain) - data['bind_domain'] = domain.strip() - else: - data['bind_domain'] = '' - - data = self.renderUnauthorizedStatus(data) - - #api - api_token = self.__api_addr - if os.path.exists(api_token): - bac = mw.readFile(api_token) - bac = json.loads(bac) - if bac['open']: - data['api_token'] = 'checked' - else: - data['api_token'] = '' - - #auth - - - data['site_count'] = mw.M('sites').count() - - data['username'] = mw.M('users').where( - "id=?", (1,)).getField('username') - - data['hook_tag'] = request.args.get('tag', '') - - # databases hook - database_hook_file = self.__file['hook_database'] - if os.path.exists(database_hook_file): - df = mw.readFile(database_hook_file) - df = json.loads(df) - data['hook_database'] = df - else: - data['hook_database'] = [] - - # menu hook - menu_hook_file = self.__file['hook_menu'] - if os.path.exists(menu_hook_file): - df = mw.readFile(menu_hook_file) - df = json.loads(df) - data['hook_menu'] = df - else: - data['hook_menu'] = [] - - # global_static hook - global_static_hook_file = self.__file['hook_global_static'] - if os.path.exists(global_static_hook_file): - df = mw.readFile(global_static_hook_file) - df = json.loads(df) - data['hook_global_static'] = df - else: - data['hook_global_static'] = [] - - # notiy config - notify_data = mw.getNotifyData(True) - notify_tag_list = ['tgbot', 'email'] - for tag in notify_tag_list: - new_tag = 'notify_' + tag + '_enable' - data[new_tag] = '' - if tag in notify_data and 'enable' in notify_data[tag]: - if notify_data[tag]['enable']: - data[new_tag] = 'checked' - - return data diff --git a/class/core/crontab_api.py b/class/core/crontab_api.py deleted file mode 100755 index c9f7d682d..000000000 --- a/class/core/crontab_api.py +++ /dev/null @@ -1,694 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 计划任务操作 -# --------------------------------------------------------------------------------- - - -import psutil -import time -import os -import sys -import mw -import re -import json -import pwd - -from flask import request - - -class crontab_api: - - field = 'id,name,type,where1,where_hour,where_minute,echo,addtime,status,save,backup_to,stype,sname,sbody,urladdress' - - def __init__(self): - pass - - ##### ----- start ----- ### - def listApi(self): - p = request.args.get('p', 1) - psize = 10 - - startPage = (int(p) - 1) * psize - pageInfo = str(startPage) + ',' + str(psize) - - _list = mw.M('crontab').where('', ()).field( - self.field).limit(pageInfo).order('id desc').select() - - data = [] - for i in range(len(_list)): - tmp = _list[i] - if _list[i]['type'] == "day": - tmp['type'] = '每天' - tmp['cycle'] = mw.getInfo('每天, {1}点{2}分 执行', (str( - _list[i]['where_hour']), str(_list[i]['where_minute']))) - elif _list[i]['type'] == "day-n": - tmp['type'] = mw.getInfo( - '每{1}天', (str(_list[i]['where1']),)) - tmp['cycle'] = mw.getInfo('每隔{1}天, {2}点{3}分 执行', (str( - _list[i]['where1']), str(_list[i]['where_hour']), str(_list[i]['where_minute']))) - elif _list[i]['type'] == "hour": - tmp['type'] = '每小时' - tmp['cycle'] = mw.getInfo( - '每小时, 第{1}分钟 执行', (str(_list[i]['where_minute']),)) - elif _list[i]['type'] == "hour-n": - tmp['type'] = mw.getInfo( - '每{1}小时', (str(_list[i]['where1']),)) - tmp['cycle'] = mw.getInfo('每{1}小时, 第{2}分钟 执行', (str( - _list[i]['where1']), str(_list[i]['where_minute']))) - elif _list[i]['type'] == "minute-n": - tmp['type'] = mw.getInfo( - '每{1}分钟', (str(_list[i]['where1']),)) - tmp['cycle'] = mw.getInfo( - '每隔{1}分钟执行', (str(_list[i]['where1']),)) - elif _list[i]['type'] == "week": - tmp['type'] = '每周' - if not _list[i]['where1']: - _list[i]['where1'] = '0' - tmp['cycle'] = mw.getInfo('每周{1}, {2}点{3}分执行', (self.toWeek(int( - _list[i]['where1'])), str(_list[i]['where_hour']), str(_list[i]['where_minute']))) - elif _list[i]['type'] == "month": - tmp['type'] = '每月' - tmp['cycle'] = mw.getInfo('每月, {1}日 {2}点{3}分执行', (str(_list[i]['where1']), str( - _list[i]['where_hour']), str(_list[i]['where_minute']))) - data.append(tmp) - - rdata = {} - rdata['data'] = data - - count = mw.M('crontab').where('', ()).count() - _page = {} - _page['count'] = count - _page['p'] = p - _page['row'] = psize - _page['tojs'] = "getCronData" - - rdata['list'] = mw.getPage(_page) - rdata['p'] = p - - # backup hook - bh_file = mw.getPanelDataDir() + "/hook_backup.json" - if os.path.exists(bh_file): - hb_data = mw.readFile(bh_file) - hb_data = json.loads(hb_data) - rdata['backup_hook'] = hb_data - - return mw.getJson(rdata) - - # 设置计划任务状态 - def setCronStatusApi(self): - mid = request.form.get('id', '') - cronInfo = mw.M('crontab').where( - 'id=?', (mid,)).field(self.field).find() - status = 1 - if cronInfo['status'] == status: - status = 0 - self.removeForCrond(cronInfo['echo']) - else: - cronInfo['status'] = 1 - self.syncToCrond(cronInfo) - - mw.M('crontab').where('id=?', (mid,)).setField('status', status) - mw.writeLog( - '计划任务', '修改计划任务[' + cronInfo['name'] + ']状态为[' + str(status) + ']') - return mw.returnJson(True, '设置成功') - - # 获取指定任务数据 - def getCrondFindApi(self): - sid = request.form.get('id', '') - data = mw.M('crontab').where( - 'id=?', (sid,)).field(self.field).find() - return mw.getJson(data) - - # 参数校验 - def cronCheck(self, params): - - if params['stype'] == 'site' or params['stype'] == 'database' or params['stype'].find('database_') > -1 or params['stype'] == 'logs' or params['stype'] == 'path': - if params['save'] == '': - return False, '保留份数不能为空!' - - if params['type'] == 'day': - if params['hour'] == '': - return False, '小时不能为空!' - if params['minute'] == '': - return False, '分钟不能为空!' - - if params['type'] == 'day-n': - if params['where1'] == '': - return False, '天不能为空!' - if params['hour'] == '': - return False, '小时不能为空!' - if params['minute'] == '': - return False, '分钟不能为空!' - if params['type'] == 'hour': - if params['minute'] == '': - return False, '分钟不能为空!' - - if params['type'] == 'hour-n': - if params['where1'] == '': - return False, '小时不能为空!' - if params['minute'] == '': - return False, '分钟不能为空!' - - if params['type'] == 'minute-n': - if params['where1'] == '': - return False, '分钟不能为空!' - - if params['type'] == 'week': - if params['hour'] == '': - return False, '小时不能为空!' - if params['minute'] == '': - return False, '分钟不能为空!' - - if params['type'] == 'month': - if params['where1'] == '': - return False, '日不能为空!' - if params['hour'] == '': - return False, '小时不能为空!' - if params['minute'] == '': - return False, '分钟不能为空!' - - return True, 'OK' - - def modifyCrondApi(self): - sid = request.form.get('id', '') - iname = request.form.get('name', '') - field_type = request.form.get('type', '') - week = request.form.get('week', '') - where1 = request.form.get('where1', '') - hour = request.form.get('hour', '') - minute = request.form.get('minute', '') - save = request.form.get('save', '') - backup_to = request.form.get('backup_to', '') - stype = request.form.get('stype', '') - sname = request.form.get('sname', '') - sbody = request.form.get('sbody', '') - urladdress = request.form.get('urladdress', '') - - if len(iname) < 1: - return mw.returnJson(False, '任务名称不能为空!') - - params = { - 'name': iname, - 'type': field_type, - 'week': week, - 'where1': where1, - 'hour': hour, - 'minute': minute, - 'save': save, - 'backup_to': backup_to, - 'stype': stype, - 'sname': sname, - 'sbody': sbody, - 'urladdress': urladdress, - } - - is_check_pass, msg = self.cronCheck(params) - if not is_check_pass: - return mw.returnJson(is_check_pass, msg) - - cuonConfig, get, name = self.getCrondCycle(params) - cronInfo = mw.M('crontab').where( - 'id=?', (sid,)).field(self.field).find() - del(cronInfo['id']) - del(cronInfo['addtime']) - cronInfo['name'] = get['name'] - cronInfo['type'] = get['type'] - cronInfo['where1'] = get['where1'] - cronInfo['where_hour'] = get['hour'] - cronInfo['where_minute'] = get['minute'] - cronInfo['save'] = get['save'] - cronInfo['backup_to'] = get['backup_to'] - cronInfo['sbody'] = get['sbody'] - cronInfo['urladdress'] = get['urladdress'] - - addData = mw.M('crontab').where('id=?', (sid,)).save('name,type,where1,where_hour,where_minute,save,backup_to, sname, sbody,urladdress', - (iname, field_type, get['where1'], get['hour'], get['minute'], get['save'], get['backup_to'], sname, get['sbody'], get['urladdress'])) - self.removeForCrond(cronInfo['echo']) - self.syncToCrond(cronInfo) - mw.writeLog('计划任务', '修改计划任务[' + cronInfo['name'] + ']成功') - return mw.returnJson(True, '修改成功') - - def logsApi(self): - sid = request.form.get('id', '') - echo = mw.M('crontab').where("id=?", (sid,)).field('echo').find() - logFile = mw.getServerDir() + '/cron/' + echo['echo'] + '.log' - if not os.path.exists(logFile): - return mw.returnJson(False, '当前日志为空!') - log = mw.getLastLine(logFile, 500) - return mw.returnJson(True, log) - - def addApi(self): - iname = request.form.get('name', '') - field_type = request.form.get('type', '') - week = request.form.get('week', '') - where1 = request.form.get('where1', '') - hour = request.form.get('hour', '') - minute = request.form.get('minute', '') - save = request.form.get('save', '') - backup_to = request.form.get('backupTo', '') - stype = request.form.get('sType', '') - sname = request.form.get('sName', '') - sbody = request.form.get('sBody', '') - urladdress = request.form.get('urladdress', '') - - if len(iname) < 1: - return mw.returnJson(False, '任务名称不能为空!') - - params = { - 'name': iname, - 'type': field_type, - 'week': week, - 'where1': where1, - 'hour': hour, - 'minute': minute, - 'save': save, - 'backup_to': backup_to, - 'stype': stype, - 'sname': sname, - 'sbody': sbody, - 'urladdress': urladdress, - } - - is_check_pass, msg = self.cronCheck(params) - if not is_check_pass: - return mw.returnJson(is_check_pass, msg) - - addData = self.add(params) - if type(addData) == str: - return addData - - if addData > 0: - return mw.returnJson(True, '添加成功') - return mw.returnJson(False, '添加失败') - - def add(self, params): - - iname = params["name"] - field_type = params["type"] - week = params["week"] - where1 = params["where1"] - hour = params["hour"] - minute = params["minute"] - save = params["save"] - backup_to = params["backup_to"] - stype = params["stype"] - sname = params["sname"] - sbody = params["sbody"] - urladdress = params["urladdress"] - - # print params - cronConfig, get, name = self.getCrondCycle(params) - cronPath = mw.getServerDir() + '/cron' - cronName = self.getShell(params) - - if type(cronName) == dict: - return cronName - - cronConfig += ' ' + cronPath + '/' + cronName + \ - ' >> ' + cronPath + '/' + cronName + '.log 2>&1' - - # print(cronConfig) - if not mw.isAppleSystem(): - wRes = self.writeShell(cronConfig) - if type(wRes) != bool: - return wRes - self.crondReload() - - add_time = time.strftime('%Y-%m-%d %X', time.localtime()) - task_id = mw.M('crontab').add('name,type,where1,where_hour,where_minute,echo,addtime,status,save,backup_to,stype,sname,sbody,urladdress', - (iname, field_type, where1, hour, minute, cronName, add_time, 1, save, backup_to, stype, sname, sbody, urladdress,)) - return task_id - - def startTaskApi(self): - sid = request.form.get('id', '') - echo = mw.M('crontab').where('id=?', (sid,)).getField('echo') - execstr = mw.getServerDir() + '/cron/' + echo - os.system('chmod +x ' + execstr) - os.system('nohup ' + execstr + ' >> ' + execstr + '.log 2>&1 &') - return mw.returnJson(True, '任务已执行!') - - def delApi(self): - task_id = request.form.get('id', '') - try: - data = self.delete(task_id) - if not data[0]: - return mw.returnJson(False, data[1]) - return mw.returnJson(True, '删除成功') - except Exception as e: - return mw.returnJson(False, '删除失败:' + str(e)) - - def delete(self, tid): - - find = mw.M('crontab').where("id=?", (tid,)).field('name,echo').find() - if not self.removeForCrond(find['echo']): - return (False, '无法写入文件,请检查是否开启了系统加固功能!') - - cronPath = mw.getServerDir() + '/cron' - sfile = cronPath + '/' + find['echo'] - - if os.path.exists(sfile): - os.remove(sfile) - sfile = cronPath + '/' + find['echo'] + '.log' - if os.path.exists(sfile): - os.remove(sfile) - - mw.M('crontab').where("id=?", (tid,)).delete() - mw.writeLog('计划任务', mw.getInfo('删除计划任务[{1}]成功!', (find['name'],))) - return (True, "OK") - - def delLogsApi(self): - sid = request.form.get('id', '') - try: - echo = mw.M('crontab').where("id=?", (sid,)).getField('echo') - logFile = mw.getServerDir() + '/cron/' + echo + '.log' - os.remove(logFile) - return mw.returnJson(True, '任务日志已清空!') - except: - return mw.returnJson(False, '任务日志清空失败!') - - # 取数据列表 - def getDataListApi(self): - stype = request.form.get('type', '') - - bak_data = [] - if stype == 'site' or stype == 'sites' or stype == 'database' or stype.find('database_') > -1 or stype == 'path': - hookPath = mw.getPanelDataDir() + "/hook_backup.json" - if os.path.exists(hookPath): - t = mw.readFile(hookPath) - bak_data = json.loads(t) - - if stype == 'database' or stype.find('database_') > -1: - sqlite3_name = 'mysql' - path = mw.getServerDir() + '/mysql' - if stype != 'database': - soft_name = stype.replace('database_', '') - path = mw.getServerDir() + '/' + soft_name - - if soft_name == 'postgresql': - sqlite3_name = 'pgsql' - - if soft_name == 'mongodb': - sqlite3_name = 'mongodb' - - db_list = {} - db_list['orderOpt'] = bak_data - - if not os.path.exists(path + '/' + sqlite3_name + '.db'): - db_list['data'] = [] - else: - db_list['data'] = mw.M('databases').dbPos(path, sqlite3_name).field('name,ps').select() - return mw.getJson(db_list) - - if stype == 'path': - db_list = {} - db_list['data'] = [{"name": mw.getWwwDir(), "ps": "www"}] - db_list['orderOpt'] = bak_data - return mw.getJson(db_list) - - data = {} - data['orderOpt'] = bak_data - - default_db = 'sites' - # if stype == 'site' or stype == 'logs': - # stype == 'sites' - - data['data'] = mw.M(default_db).field('name,ps').select() - return mw.getJson(data) - ##### ----- start ----- ### - - # 转换大写星期 - def toWeek(self, num): - wheres = { - 0: '日', - 1: '一', - 2: '二', - 3: '三', - 4: '四', - 5: '五', - 6: '六' - } - try: - return wheres[num] - except: - return '' - - def getCrondCycle(self, params): - cuonConfig = '' - name = '' - if params['type'] == "day": - cuonConfig = self.getDay(params) - name = '每天' - elif params['type'] == "day-n": - cuonConfig = self.getDay_N(params) - name = mw.getInfo('每{1}天', (params['where1'],)) - elif params['type'] == "hour": - cuonConfig = self.getHour(params) - name = '每小时' - elif params['type'] == "hour-n": - cuonConfig = self.getHour_N(params) - name = '每小时' - elif params['type'] == "minute-n": - cuonConfig = self.minute_N(params) - elif params['type'] == "week": - params['where1'] = params['week'] - cuonConfig = self.week(params) - elif params['type'] == "month": - cuonConfig = self.month(params) - return cuonConfig, params, name - - # 取任务构造Day - def getDay(self, param): - cuonConfig = "{0} {1} * * * ".format(param['minute'], param['hour']) - return cuonConfig - # 取任务构造Day_n - - def getDay_N(self, param): - cuonConfig = "{0} {1} */{2} * * ".format( - param['minute'], param['hour'], param['where1']) - return cuonConfig - - # 取任务构造Hour - def getHour(self, param): - cuonConfig = "{0} * * * * ".format(param['minute']) - return cuonConfig - - # 取任务构造Hour-N - def getHour_N(self, param): - cuonConfig = "{0} */{1} * * * ".format( - param['minute'], param['where1']) - return cuonConfig - - # 取任务构造Minute-N - def minute_N(self, param): - cuonConfig = "*/{0} * * * * ".format(param['where1']) - return cuonConfig - - # 取任务构造week - def week(self, param): - cuonConfig = "{0} {1} * * {2}".format( - param['minute'], param['hour'], param['week']) - return cuonConfig - - # 取任务构造Month - def month(self, param): - cuonConfig = "{0} {1} {2} * * ".format( - param['minute'], param['hour'], param['where1']) - return cuonConfig - - # 取执行脚本 - def getShell(self, param): - # try: - stype = param['stype'] - if stype == 'toFile': - shell = param.sFile - else: - head = "#!/bin/bash\nPATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin\nexport PATH\n" - - start_head = ''' -SCRIPT_RUN_TIME="0s" -MW_ToSeconds() -{ - SEC=$1 - if [ $SEC -lt 60 ]; then - SCRIPT_RUN_TIME="${SEC}s" - elif [ $SEC -ge 60 ] && [ $SEC -lt 3600 ];then - SCRIPT_RUN_TIME="$(( SEC / 60 ))m$(( SEC % 60 ))s" - elif [ $SEC -ge 3600 ]; then - SCRIPT_RUN_TIME="$(( SEC / 3600 ))h$(( (SEC % 3600) / 60 ))m$(( (SEC % 3600) % 60 ))s" - fi -} -START_MW_SHELL_TIME=`date +%s` -''' - - source_bin_activate = ''' -export LANG=en_US.UTF-8 -MW_PATH=%s/bin/activate -if [ -f $MW_PATH ];then - source $MW_PATH -fi''' % (mw.getRunDir(),) - - head = head + start_head + source_bin_activate + "\n" - log = '.log' - - #所有 - if param['sname'] == 'ALL': - log = '' - - script_dir = mw.getRunDir() + "/scripts" - source_stype = 'database' - if stype.find('database_') > -1: - plugin_name = stype.replace('database_', '') - script_dir = mw.getRunDir() + "/plugins/" + plugin_name + "/scripts" - - source_stype = stype - stype = 'database' - - wheres = { - 'path': head + "python3 " + script_dir + "/backup.py path " + param['sname'] + " " + str(param['save']), - 'site': head + "python3 " + script_dir + "/backup.py site " + param['sname'] + " " + str(param['save']), - 'database': head + "python3 " + script_dir + "/backup.py database " + param['sname'] + " " + str(param['save']), - 'logs': head + "python3 " + script_dir + "/logs_backup.py " + param['sname'] + log + " " + str(param['save']), - 'rememory': head + "/bin/bash " + script_dir + '/rememory.sh' - } - if param['backup_to'] != 'localhost': - cfile = mw.getPluginDir() + "/" + \ - param['backup_to'] + "/index.py" - - wheres['path'] = head + "python3 " + cfile + \ - " path " + param['sname'] + " " + str(param['save']) - wheres['site'] = head + "python3 " + cfile + \ - " site " + param['sname'] + " " + str(param['save']) - wheres['database'] = head + "python3 " + cfile + " " + \ - source_stype + " " + \ - param['sname'] + " " + str(param['save']) - try: - shell = wheres[stype] - except: - if stype == 'toUrl': - shell = head + "curl -sS --connect-timeout 10 -m 60 '" + \ - param['urladdress'] + "'" - else: - shell = head + param['sbody'].replace("\r\n", "\n") - - shell += ''' -echo "----------------------------------------------------------------------------" -endDate=`date +"%Y-%m-%d %H:%M:%S"` -END_MW_SHELL_TIME=`date +"%s"` -((SHELL_COS_TIME=($END_MW_SHELL_TIME-$START_MW_SHELL_TIME))) -MW_ToSeconds $SHELL_COS_TIME -echo "★[$endDate] Successful | Script Run [$SCRIPT_RUN_TIME] " -echo "----------------------------------------------------------------------------" -''' - cronPath = mw.getServerDir() + '/cron' - if not os.path.exists(cronPath): - mw.execShell('mkdir -p ' + cronPath) - - if not 'echo' in param: - cronName = mw.md5(mw.md5(str(time.time()) + '_mw')) - else: - cronName = param['echo'] - file = cronPath + '/' + cronName - mw.writeFile(file, self.checkScript(shell)) - mw.execShell('chmod 750 ' + file) - return cronName - - # 检查脚本 - def checkScript(self, shell): - keys = ['shutdown', 'init 0', 'mkfs', 'passwd', - 'chpasswd', '--stdin', 'mkfs.ext', 'mke2fs'] - for key in keys: - shell = shell.replace(key, '[***]') - return shell - - # 将Shell脚本写到文件 - def writeShell(self, config): - file = '/var/spool/cron/crontabs/root' - current_os = mw.getOs() - if current_os == 'darwin': - file = '/etc/crontab' - elif current_os.startswith("freebsd"): - file = '/var/cron/tabs/root' - - if not os.path.exists(file): - file = '/var/spool/cron/root' - - if not os.path.exists(file): - mw.writeFile(file, '') - conf = mw.readFile(file) - conf += str(config) + "\n" - if mw.writeFile(file, conf): - if not os.path.exists(file): - mw.execShell("chmod 600 '" + file + - "' && chown root.root " + file) - else: - mw.execShell("chmod 600 '" + file + - "' && chown root.crontab " + file) - return True - return mw.returnJson(False, '文件写入失败,请检查是否开启系统加固功能!') - - # 重载配置 - def crondReload(self): - if mw.isAppleSystem(): - # mw.execShell('/usr/sbin/cron restart') - if os.path.exists('/etc/crontab'): - pass - else: - if os.path.exists('/etc/init.d/crond'): - mw.execShell('/etc/init.d/crond reload') - elif os.path.exists('/etc/init.d/cron'): - mw.execShell('service cron restart') - else: - mw.execShell("systemctl reload crond") - - # 从crond删除 - def removeForCrond(self, echo): - u_file = '/var/spool/cron/crontabs/root' - if not os.path.exists(u_file): - file = '/var/spool/cron/root' - if mw.isAppleSystem(): - file = '/etc/crontab' - - if not os.path.exists(file): - return False - else: - file = u_file - - if mw.isAppleSystem(): - return True - - conf = mw.readFile(file) - rep = ".+" + str(echo) + ".+\n" - conf = re.sub(rep, "", conf) - if not mw.writeFile(file, conf): - return False - self.crondReload() - return True - - def syncToCrond(self, cronInfo): - if 'status' in cronInfo: - if cronInfo['status'] == 0: - return False - if 'where_hour' in cronInfo: - cronInfo['hour'] = cronInfo['where_hour'] - cronInfo['minute'] = cronInfo['where_minute'] - cronInfo['week'] = cronInfo['where1'] - cuonConfig, cronInfo, name = self.getCrondCycle(cronInfo) - cronPath = mw.getServerDir() + '/cron' - cronName = self.getShell(cronInfo) - if type(cronName) == dict: - return cronName - cuonConfig += ' ' + cronPath + '/' + cronName + \ - ' >> ' + cronPath + '/' + cronName + '.log 2>&1' - wRes = self.writeShell(cuonConfig) - if type(wRes) != bool: - return False - self.crondReload() diff --git a/class/core/db.py b/class/core/db.py deleted file mode 100755 index 3ae39f8d4..000000000 --- a/class/core/db.py +++ /dev/null @@ -1,401 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# sqlite3操作 -# --------------------------------------------------------------------------------- - - -import sqlite3 -import os -import sys - - -class Sql(): - #------------------------------ - # 数据库操作类 For sqlite3 - #------------------------------ - __DB_FILE = None # 数据库文件 - __DB_CONN = None # 数据库连接对象 - __DB_TABLE = "" # 被操作的表名称 - __OPT_WHERE = "" # where条件 - __OPT_LIMIT = "" # limit条件 - __OPT_GROUP = "" # group条件 - __OPT_ORDER = "" # order条件 - __OPT_FIELD = "*" # field条件 - __OPT_PARAM = () # where值 - - def __init__(self): - self.__DB_FILE = 'data/default.db' - - def __getConn(self): - # 取数据库对象 - try: - if self.__DB_CONN == None: - self.__DB_CONN = sqlite3.connect(self.__DB_FILE) - self.__DB_CONN.text_factory = str - except Exception as ex: - # print(mw.getTracebackInfo()) - return "error: " + str(ex) - - def changeTextFactoryToBytes(self): - self.__DB_CONN.text_factory = bytes - return True - - def autoTextFactory(self): - if sys.version_info[0] == 3: - self.__DB_CONN.text_factory = lambda x: str( - x, encoding="utf-8", errors='ignore') - else: - self.__DB_CONN.text_factory = lambda x: unicode( - x, "utf-8", "ignore") - - def dbfile(self, name): - self.__DB_FILE = 'data/' + name + '.db' - return self - - def dbPos(self, path, name, suffix_name = 'db'): - self.__DB_FILE = path + '/' + name + '.' + suffix_name - return self - - def table(self, table): - # 设置表名 - self.__DB_TABLE = table - return self - - def where(self, where, param=()): - # WHERE条件 - if where: - self.__OPT_WHERE = " WHERE " + where - self.__OPT_PARAM = param - return self - - def andWhere(self, where, param): - # WHERE条件 - if where: - self.__OPT_WHERE = self.__OPT_WHERE + " and " + where - # print(param) - # print(self.__OPT_PARAM) - self.__OPT_PARAM = self.__OPT_PARAM + param - return self - - def order(self, order): - # ORDER条件 - if len(order): - self.__OPT_ORDER = " ORDER BY " + order - else: - self.__OPT_ORDER = "" - return self - - def group(self, group): - if len(group): - self.__OPT_GROUP = " GROUP BY " + group - else: - self.__OPT_GROUP = "" - return self - - def limit(self, limit): - # LIMIT条件 - if len(limit): - self.__OPT_LIMIT = " LIMIT " + limit - else: - self.__OPT_LIMIT = "" - return self - - def field(self, field): - # FIELD条件 - if len(field): - self.__OPT_FIELD = field - return self - - def select(self): - # 查询数据集 - self.__getConn() - try: - sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \ - self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT - # print(sql) - # print(self.__OPT_PARAM) - result = self.__DB_CONN.execute(sql, self.__OPT_PARAM) - data = result.fetchall() - # 构造字曲系列 - if self.__OPT_FIELD != "*": - field = self.__OPT_FIELD.split(',') - tmp = [] - for row in data: - i = 0 - tmp1 = {} - for key in field: - tmp1[key] = row[i] - i += 1 - tmp.append(tmp1) - del(tmp1) - data = tmp - del(tmp) - else: - # 将元组转换成列表 - tmp = map(list, data) - data = tmp - del(tmp) - self.__close() - return data - except Exception as ex: - return "error: " + str(ex) - - def inquiry(self, input_field=''): - # 查询数据集 - # 不清空查询参数 - self.__getConn() - try: - sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \ - self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT - - if os.path.exists('data/debug.pl'): - print(sql, self.__OPT_PARAM) - result = self.__DB_CONN.execute(sql, self.__OPT_PARAM) - data = result.fetchall() - # 构造字曲系列 - if self.__OPT_FIELD != "*": - - if input_field != "": - field = input_field.split(',') - else: - field = self.__OPT_FIELD.split(',') - - tmp = [] - for row in data: - i = 0 - tmp1 = {} - for key in field: - tmp1[key] = row[i] - i += 1 - tmp.append(tmp1) - del(tmp1) - data = tmp - del(tmp) - else: - # 将元组转换成列表 - tmp = map(list, data) - data = tmp - del(tmp) - return data - except Exception as ex: - return "error: " + str(ex) - - def getField(self, keyName): - # 取回指定字段 - result = self.field(keyName).select() - # print(result) - if len(result) == 1: - return result[0][keyName] - return result - - def setField(self, keyName, keyValue): - # 更新指定字段 - return self.save(keyName, (keyValue,)) - - def find(self): - # 取一行数据 - result = self.limit("1").select() - if len(result) == 1: - return result[0] - return result - - def count(self): - # 取行数 - key = "COUNT(*)" - data = self.field(key).select() - try: - return int(data[0][key]) - except: - return 0 - - def add(self, keys, param): - # 插入数据 - self.__getConn() - try: - values = "" - for key in keys.split(','): - values += "?," - values = self.checkInput(values[0:len(values) - 1]) - sql = "INSERT INTO " + self.__DB_TABLE + \ - "(" + keys + ") " + "VALUES(" + values + ")" - result = self.__DB_CONN.execute(sql, param) - last_id = result.lastrowid - self.__close() - self.__DB_CONN.commit() - return last_id - except Exception as ex: - return "error: " + str(ex) - - # 插入数据 - def insert(self, pdata): - if not pdata: - return False - keys, param = self.__format_pdata(pdata) - return self.add(keys, param) - - # 更新数据 - def update(self, pdata): - if not pdata: - return False - keys, param = self.__format_pdata(pdata) - return self.save(keys, param) - - # 构造数据 - def __format_pdata(self, pdata): - keys = pdata.keys() - keys_str = ','.join(keys) - param = [] - for k in keys: - param.append(pdata[k]) - return keys_str, tuple(param) - - def checkInput(self, data): - if not data: - return data - if type(data) != str: - return data - checkList = [ - {'d': '<', 'r': '<'}, - {'d': '>', 'r': '>'}, - {'d': '\'', 'r': '‘'}, - {'d': '"', 'r': '“'}, - {'d': '&', 'r': '&'}, - {'d': '#', 'r': '#'}, - {'d': '<', 'r': '<'} - ] - for v in checkList: - data = data.replace(v['d'], v['r']) - return data - - def addAll(self, keys, param): - # 插入数据 - self.__getConn() - try: - values = "" - for key in keys.split(','): - values += "?," - values = values[0:len(values) - 1] - sql = "INSERT INTO " + self.__DB_TABLE + \ - "(" + keys + ") " + "VALUES(" + values + ")" - result = self.__DB_CONN.execute(sql, param) - return True - except Exception as ex: - return "error: " + str(ex) - - def commit(self): - self.__close() - self.__DB_CONN.commit() - - def save(self, keys, param): - # 更新数据 - self.__getConn() - try: - opt = "" - for key in keys.split(','): - opt += key + "=?," - opt = opt[0:len(opt) - 1] - sql = "UPDATE " + self.__DB_TABLE + " SET " + opt + self.__OPT_WHERE - - # import mw - # mw.writeFile('/tmp/test.pl', sql) - - # 处理拼接WHERE与UPDATE参数 - tmp = list(param) - for arg in self.__OPT_PARAM: - tmp.append(arg) - self.__OPT_PARAM = tuple(tmp) - result = self.__DB_CONN.execute(sql, self.__OPT_PARAM) - self.__close() - self.__DB_CONN.commit() - return result.rowcount - except Exception as ex: - return "error: " + str(ex) - - def delete(self, id=None): - # 删除数据 - self.__getConn() - try: - if id: - self.__OPT_WHERE = " WHERE id=?" - self.__OPT_PARAM = (id,) - sql = "DELETE FROM " + self.__DB_TABLE + self.__OPT_WHERE - result = self.__DB_CONN.execute(sql, self.__OPT_PARAM) - self.__close() - self.__DB_CONN.commit() - return result.rowcount - except Exception as ex: - return "error: " + str(ex) - - def originExecute(self, sql, param=()): - self.__getConn() - try: - result = self.__DB_CONN.execute(sql, param) - self.__DB_CONN.commit() - return result - except Exception as ex: - return "error: " + str(ex) - - def execute(self, sql, param=()): - # 执行SQL语句返回受影响行 - self.__getConn() - # print sql, param - try: - result = self.__DB_CONN.execute(sql, param) - self.__DB_CONN.commit() - return result.rowcount - except Exception as ex: - return "error: " + str(ex) - - def query(self, sql, param=()): - # 执行SQL语句返回数据集 - self.__getConn() - try: - result = self.__DB_CONN.execute(sql, param) - # 将元组转换成列表 - # data = map(list, result) - return result - except Exception as ex: - return "error: " + str(ex) - - def create(self, name): - # 创建数据表 - self.__getConn() - import mw - script = mw.readFile('data/' + name + '.sql') - result = self.__DB_CONN.executescript(script) - self.__DB_CONN.commit() - return result.rowcount - - def fofile(self, filename): - # 执行脚本 - self.__getConn() - import mw - script = mw.readFile(filename) - result = self.__DB_CONN.executescript(script) - self.__DB_CONN.commit() - return result.rowcount - - def __close(self): - # 清理条件属性 - self.__OPT_WHERE = "" - self.__OPT_FIELD = "*" - self.__OPT_ORDER = "" - self.__OPT_LIMIT = "" - self.__OPT_PARAM = () - - def close(self): - # 释放资源 - try: - self.__DB_CONN.close() - self.__DB_CONN = None - except: - pass diff --git a/class/core/files_api.py b/class/core/files_api.py deleted file mode 100755 index 02aadf1be..000000000 --- a/class/core/files_api.py +++ /dev/null @@ -1,1160 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 文件类操作 -# --------------------------------------------------------------------------------- - - -import psutil -import time -import os -import sys -import mw -import re -import json -import pwd -import shutil - -from flask import request -from flask import send_file, send_from_directory -from flask import make_response -from flask import session - - -class files_api: - - rPath = None - - def __init__(self): - self.rPath = mw.getRootDir() + '/recycle_bin/' - - ##### ----- start ----- ### - def getBodyApi(self): - path = request.form.get('path', '') - return self.getBody(path) - - def getLastBodyApi(self): - path = request.form.get('path', '') - line = request.form.get('line', '100') - - if not os.path.exists(path): - return mw.returnJson(False, '文件不存在', (path,)) - - try: - data = mw.getLastLine(path, int(line)) - return mw.returnJson(True, 'OK', data) - except Exception as ex: - return mw.returnJson(False, '无法正确读取文件!' + str(ex)) - - def saveBodyApi(self): - path = request.form.get('path', '') - data = request.form.get('data', '') - encoding = request.form.get('encoding', '') - return self.saveBody(path, data, encoding) - - def downloadApi(self): - filename = request.args.get('filename', '') - if not os.path.exists(filename): - return '' - - is_attachment = True - if filename.endswith(".svg"): - is_attachment = False - - response = make_response(send_from_directory( - os.path.dirname(filename), os.path.basename(filename), as_attachment=is_attachment)) - return response - - def zipApi(self): - sfile = request.form.get('sfile', '') - dfile = request.form.get('dfile', '') - stype = request.form.get('type', '') - path = request.form.get('path', '') - return self.zip(sfile, dfile, stype, path) - - def unzipApi(self): - sfile = request.form.get('sfile', '') - dfile = request.form.get('dfile', '') - stype = request.form.get('type', '') - path = request.form.get('path', '') - return self.unzip(sfile, dfile, stype, path) - - def uncompressApi(self): - sfile = request.form.get('sfile', '') - dfile = request.form.get('dfile', '') - path = request.form.get('path', '') - return self.uncompress(sfile, dfile, path) - - # 移动文件或目录 - def mvFileApi(self): - sfile = request.form.get('sfile', '') - dfile = request.form.get('dfile', '') - if not self.checkFileName(dfile): - return mw.returnJson(False, '文件名中不能包含特殊字符!') - if not os.path.exists(sfile): - return mw.returnJson(False, '指定文件不存在!') - - if not self.checkDir(sfile): - return mw.returnJson(False, 'FILE_DANGER') - - import shutil - try: - shutil.move(sfile, dfile) - msg = mw.getInfo('移动或重名命文件[{1}]到[{2}]成功!', (sfile, dfile,)) - mw.writeLog('文件管理', msg) - return mw.returnJson(True, '移动或重名命文件成功!') - except: - return mw.returnJson(False, '移动或重名命文件失败!') - - def deleteApi(self): - path = request.form.get('path', '') - return self.delete(path) - - def fileAccessApi(self): - filename = request.form.get('filename', '') - data = self.getAccess(filename) - data['sys_users'] = self.getSysUserList() - return mw.getJson(data) - - def setFileAccessApi(self): - - if mw.isAppleSystem(): - return mw.returnJson(True, '开发机不设置!') - - filename = request.form.get('filename', '') - user = request.form.get('user', '') - access = request.form.get('access', '755') - sall = '-R' - try: - if not self.checkDir(filename): - return mw.returnJson(False, '请不要花样作死') - - if not os.path.exists(filename): - return mw.returnJson(False, '指定文件不存在!') - - os.system('chmod ' + sall + ' ' + access + " '" + filename + "'") - os.system('chown ' + sall + ' ' + user + - ':' + user + " '" + filename + "'") - msg = mw.getInfo( - '设置[{1}]权限为[{2}]所有者为[{3}]', (filename, access, user,)) - mw.writeLog('文件管理', msg) - return mw.returnJson(True, '设置成功!') - except: - return mw.returnJson(False, '设置失败!') - - def getDirSizeApi(self): - path = request.form.get('path', '') - tmp = self.getDirSize(path) - return mw.returnJson(True, tmp[0].split()[0]) - - def getDirApi(self): - path = request.form.get('path', '') - if not os.path.exists(path): - path = mw.getRootDir() + "/wwwroot" - search = request.args.get('search', '').strip().lower() - search_all = request.args.get('all', '').strip().lower() - page = request.args.get('p', '1').strip().lower() - row = request.args.get('row', '10') - order = request.form.get('order', '') - - if search_all == 'yes' and search != '': - return self.getAllDir(path, int(page), int(row), order, search) - return self.getDir(path, int(page), int(row), order, search) - - def createFileApi(self): - file = request.form.get('path', '') - try: - if not self.checkFileName(file): - return mw.returnJson(False, '文件名中不能包含特殊字符!') - if os.path.exists(file): - return mw.returnJson(False, '指定文件已存在!') - _path = os.path.dirname(file) - if not os.path.exists(_path): - os.makedirs(_path) - open(file, 'w+').close() - self.setFileAccept(file) - msg = mw.getInfo('创建文件[{1}]成功!', (file,)) - mw.writeLog('文件管理', msg) - return mw.returnJson(True, '文件创建成功!') - except Exception as e: - return mw.returnJson(True, '文件创建失败!') - - def createDirApi(self): - path = request.form.get('path', '') - try: - if not self.checkFileName(path): - return mw.returnJson(False, '目录名中不能包含特殊字符!') - if os.path.exists(path): - return mw.returnJson(False, '指定目录已存在!') - os.makedirs(path) - self.setFileAccept(path) - msg = mw.getInfo('创建目录[{1}]成功!', (path,)) - mw.writeLog('文件管理', msg) - return mw.returnJson(True, '目录创建成功!') - except Exception as e: - return mw.returnJson(False, '目录创建失败!') - - def downloadFileApi(self): - import db - import time - url = request.form.get('url', '') - path = request.form.get('path', '') - filename = request.form.get('filename', '') - execstr = url + '|mw|' + path + '/' + filename - execstr = execstr.strip() - mw.M('tasks').add('name,type,status,addtime,execstr', - ('下载文件[' + filename + ']', 'download', '-1', time.strftime('%Y-%m-%d %H:%M:%S'), execstr)) - - # self.setFileAccept(path + '/' + filename) - mw.triggerTask() - return mw.returnJson(True, '已将下载任务添加到队列!') - - # 删除进程下的所有进程 - def removeTaskRecursion(self, pid): - cmd = "ps -ef|grep " + pid + \ - " | grep -v grep |sed -n '2,1p' | awk '{print $2}'" - sub_pid = mw.execShell(cmd) - - if sub_pid[0].strip() == '': - return 'ok' - - self.removeTaskRecursion(sub_pid[0].strip()) - mw.execShell('kill -9 ' + sub_pid[0].strip()) - return sub_pid[0].strip() - - def removeTaskApi(self): - import system_api - mid = request.form.get('id', '') - try: - name = mw.M('tasks').where('id=?', (mid,)).getField('name') - status = mw.M('tasks').where('id=?', (mid,)).getField('status') - mw.M('tasks').delete(mid) - if status == '-1': - task_pid = mw.execShell( - "ps aux | grep 'task.py' | grep -v grep |awk '{print $2}'") - - task_list = task_pid[0].strip().split("\n") - for i in range(len(task_list)): - self.removeTaskRecursion(task_list[i]) - - mw.triggerTask() - system_api.system_api().restartTask() - except: - system_api.system_api().restartTask() - - # 删除日志 - task_log = mw.getRunDir() + "/tmp/panelTask.pl" - if os.path.exists(task_log): - os.remove(task_log) - return mw.returnJson(True, '任务已删除!') - - def uploadFileApi(self): - # 上传文件 - from werkzeug.utils import secure_filename - from flask import request - - path = request.args.get('path', '') - - if not os.path.exists(path): - os.makedirs(path) - f = request.files['zunfile'] - filename = os.path.join(path, f.filename) - - s_path = path - if os.path.exists(filename): - s_path = filename - p_stat = os.stat(s_path) - - # print(filename) - f.save(filename) - os.chown(filename, p_stat.st_uid, p_stat.st_gid) - os.chmod(filename, p_stat.st_mode) - - msg = mw.getInfo('上传文件[{1}] 到 [{2}]成功!', (filename, path)) - mw.writeLog('文件管理', msg) - return mw.returnMsg(True, '上传成功!') - - # 设置文件和目录权限 - def setMode(self, path): - s_path = os.path.dirname(path) - p_stat = os.stat(s_path) - os.chown(path, p_stat.st_uid, p_stat.st_gid) - os.chmod(path, p_stat.st_mode) - - def uploadSegmentApi(self): - # 分段上传 - path = request.form.get('path', '') - name = request.form.get('name', '') - size = request.form.get('size') - start = request.form.get('start') - dir_mode = request.form.get('dir_mode', '') - file_mode = request.form.get('file_mode', '') - - if not mw.fileNameCheck(name): - return mw.returnJson(False, '文件名中不能包含特殊字符!') - - if path == '/': - return mw.returnJson(False, '不能直接上传文件到系统根目录!') - - if name.find('./') != -1 or path.find('./') != -1: - return mw.returnJson(False, '错误的参数') - - if not os.path.exists(path): - os.makedirs(path, 493) - if not dir_mode != '' or not file_mode != '': - mw.setMode(path) - - save_path = os.path.join( - path, name + '.' + str(int(size)) + '.upload.tmp') - d_size = 0 - if os.path.exists(save_path): - d_size = os.path.getsize(save_path) - - if d_size != int(start): - return str(d_size) - - f = open(save_path, 'ab') - b64_data = request.form.get('b64_data', '0') - if b64_data == '1': - import base64 - b64_data = base64.b64decode(args.b64_data) - f.write(b64_data) - else: - upload_files = request.files.getlist("blob") - for tmp_f in upload_files: - f.write(tmp_f.read()) - - f.close() - f_size = os.path.getsize(save_path) - if f_size != int(size): - return str(f_size) - - new_name = os.path.join(path, name) - if os.path.exists(new_name): - if new_name.find('.user.ini') != -1: - mw.execShell("chattr -i " + new_name) - try: - os.remove(new_name) - except: - mw.execShell("rm -f %s" % new_name) - - os.renames(save_path, new_name) - - if dir_mode != '' and dir_mode != '': - mode_tmp1 = dir_mode.split(',') - mw.setMode(path, mode_tmp1[0]) - mw.setOwn(path, mode_tmp1[1]) - mode_tmp2 = file_mode.split(',') - mw.setMode(new_name, mode_tmp2[0]) - mw.setOwn(new_name, mode_tmp2[1]) - else: - self.setMode(new_name) - - msg = mw.getInfo('上传文件[{1}] 到 [{2}]成功!', (new_name, path)) - mw.writeLog('文件管理', msg) - return mw.returnMsg(True, '上传成功!') - - def getRecycleBinApi(self): - rPath = self.rPath - if not os.path.exists(rPath): - os.system('mkdir -p ' + rPath) - data = {} - data['dirs'] = [] - data['files'] = [] - data['status'] = os.path.exists('data/recycle_bin.pl') - data['status_db'] = os.path.exists('data/recycle_bin_db.pl') - for file in os.listdir(rPath): - try: - tmp = {} - fname = rPath + file - tmp1 = file.split('_mw_') - tmp2 = tmp1[len(tmp1) - 1].split('_t_') - tmp['rname'] = file - tmp['dname'] = file.replace('_mw_', '/').split('_t_')[0] - tmp['name'] = tmp2[0] - tmp['time'] = int(float(tmp2[1])) - if os.path.islink(fname): - filePath = os.readlink(fname) - link = ' -> ' + filePath - if os.path.exists(filePath): - tmp['size'] = os.path.getsize(filePath) - else: - tmp['size'] = 0 - else: - tmp['size'] = os.path.getsize(fname) - if os.path.isdir(fname): - data['dirs'].append(tmp) - else: - data['files'].append(tmp) - except Exception as e: - continue - return mw.returnJson(True, 'OK', data) - - # 回收站开关 - def recycleBinApi(self): - c = 'data/recycle_bin.pl' - db = request.form.get('db', '') - if db != '': - c = 'data/recycle_bin_db.pl' - if os.path.exists(c): - os.remove(c) - mw.writeLog('文件管理', '已关闭回收站功能!') - return mw.returnJson(True, '已关闭回收站功能!') - else: - mw.writeFile(c, 'True') - mw.writeLog('文件管理', '已开启回收站功能!') - return mw.returnJson(True, '已开启回收站功能!') - - def reRecycleBinApi(self): - rPath = self.rPath - path = request.form.get('path', '') - dFile = path.replace('_mw_', '/').split('_t_')[0] - try: - import shutil - shutil.move(rPath + path, dFile) - msg = mw.getInfo('移动文件[{1}]到回收站成功!', (dFile,)) - mw.writeLog('文件管理', msg) - return mw.returnJson(True, '恢复成功!') - except Exception as e: - msg = mw.getInfo('从回收站恢复[{1}]失败!', (dFile,)) - mw.writeLog('文件管理', msg) - return mw.returnJson(False, '恢复失败!') - - def delRecycleBinApi(self): - rPath = self.rPath - path = request.form.get('path', '') - empty = request.form.get('empty', '') - dFile = path.split('_t_')[0] - - if not self.checkDir(path): - return mw.returnJson(False, '敏感目录,请不要花样作死!') - - mw.execShell('which chattr && chattr -R -i ' + rPath + path) - if os.path.isdir(rPath + path): - import shutil - shutil.rmtree(rPath + path) - else: - os.remove(rPath + path) - - tfile = path.replace('_mw_', '/').split('_t_')[0] - msg = mw.getInfo('已彻底从回收站删除{1}!', (tfile,)) - mw.writeLog('文件管理', msg) - return mw.returnJson(True, msg) - - # 获取进度 - def getSpeedApi(self): - data = mw.getSpeed() - return mw.returnJson(True, '已清空回收站!', data) - - def closeRecycleBinApi(self): - rPath = self.rPath - mw.execShell('which chattr && chattr -R -i ' + rPath) - rlist = os.listdir(rPath) - i = 0 - l = len(rlist) - for name in rlist: - i += 1 - path = rPath + name - mw.writeSpeed(name, i, l) - if os.path.isdir(path): - shutil.rmtree(path) - else: - os.remove(path) - mw.writeSpeed(None, 0, 0) - mw.writeLog('文件管理', '已清空回收站!') - return mw.returnJson(True, '已清空回收站!') - - def deleteDirApi(self): - path = request.form.get('path', '') - if not os.path.exists(path): - return mw.returnJson(False, '指定文件不存在!') - - # 检查是否为.user.ini - if path.find('.user.ini'): - os.system("which chattr && chattr -i '" + path + "'") - try: - if os.path.exists('data/recycle_bin.pl'): - if self.mvRecycleBin(path): - return mw.returnJson(True, '已将文件移动到回收站!') - mw.execShell('rm -rf ' + path) - mw.writeLog('文件管理', '删除文件成功!', (path,)) - return mw.returnJson(True, '删除文件成功!') - except: - return mw.returnJson(False, '删除文件失败!') - - def closeLogsApi(self): - logPath = mw.getLogsDir() - os.system('rm -f ' + logPath + '/*') - os.system('kill -USR1 `cat ' + mw.getServerDir() + - 'openresty/nginx/logs/nginx.pid`') - mw.writeLog('文件管理', '网站日志已被清空!') - tmp = self.getDirSize(logPath) - return mw.returnJson(True, tmp[0].split()[0]) - - def setBatchDataApi(self): - path = request.form.get('path', '') - stype = request.form.get('type', '') - access = request.form.get('access', '') - user = request.form.get('user', '') - data = request.form.get('data') - if stype == '1' or stype == '2': - session['selected'] = { - 'path': path, - 'type': stype, - 'access': access, - 'user': user, - 'data': data - } - return mw.returnJson(True, '标记成功,请在目标目录点击粘贴所有按钮!') - elif stype == '3': - for key in json.loads(data): - try: - filename = path + '/' + key - if not self.checkDir(filename): - return mw.returnJson(False, 'FILE_DANGER') - os.system('chmod -R ' + access + " '" + filename + "'") - os.system('chown -R ' + user + ':' + - user + " '" + filename + "'") - except: - continue - mw.writeLog('文件管理', '批量设置权限成功!') - return mw.returnJson(True, '批量设置权限成功!') - else: - import shutil - isRecyle = os.path.exists('data/recycle_bin.pl') - data = json.loads(data) - l = len(data) - i = 0 - for key in data: - try: - filename = path + '/' + key - topath = filename - if not os.path.exists(filename): - continue - - i += 1 - mw.writeSpeed(key, i, l) - if os.path.isdir(filename): - if not self.checkDir(filename): - return mw.returnJson(False, '请不要花样作死!') - if isRecyle: - self.mvRecycleBin(topath) - else: - shutil.rmtree(filename) - else: - if key == '.user.ini': - os.system('which chattr && chattr -i ' + filename) - if isRecyle: - self.mvRecycleBin(topath) - else: - os.remove(filename) - except: - continue - mw.writeSpeed(None, 0, 0) - mw.writeLog('文件管理', '批量删除成功!') - return mw.returnJson(True, '批量删除成功!') - - def checkExistsFilesApi(self): - dfile = request.form.get('dfile', '') - filename = request.form.get('filename', '') - data = [] - filesx = [] - if filename == '': - filesx = json.loads(session['selected']['data']) - else: - filesx.append(filename) - - for fn in filesx: - if fn == '.': - continue - filename = dfile + '/' + fn - if os.path.exists(filename): - tmp = {} - stat = os.stat(filename) - tmp['filename'] = fn - tmp['size'] = os.path.getsize(filename) - tmp['mtime'] = str(int(stat.st_mtime)) - data.append(tmp) - return mw.returnJson(True, 'ok', data) - - def batchPasteApi(self): - path = request.form.get('path', '') - stype = request.form.get('type', '') - # filename = request.form.get('filename', '') - import shutil - if not self.checkDir(path): - return mw.returnJson(False, '请不要花样作死!') - i = 0 - myfiles = json.loads(session['selected']['data']) - l = len(myfiles) - if stype == '1': - for key in myfiles: - i += 1 - mw.writeSpeed(key, i, l) - try: - - sfile = session['selected'][ - 'path'] + '/' + key - dfile = path + '/' + key - - if os.path.isdir(sfile): - shutil.copytree(sfile, dfile) - else: - shutil.copyfile(sfile, dfile) - stat = os.stat(sfile) - os.chown(dfile, stat.st_uid, stat.st_gid) - except: - continue - msg = mw.getInfo('从[{1}]批量复制到[{2}]成功', - (session['selected']['path'], path,)) - mw.writeLog('文件管理', msg) - else: - for key in myfiles: - try: - i += 1 - mw.writeSpeed(key, i, l) - - sfile = session['selected'][ - 'path'] + '/' + key - dfile = path + '/' + key - - shutil.move(sfile, dfile) - except: - continue - msg = mw.getInfo('从[{1}]批量移动到[{2}]成功', - (session['selected']['path'], path,)) - mw.writeLog('文件管理', msg) - mw.writeSpeed(None, 0, 0) - errorCount = len(myfiles) - i - del(session['selected']) - msg = mw.getInfo('批量操作成功[{1}],失败[{2}]', (str(i), str(errorCount))) - return mw.returnJson(True, msg) - - def copyFileApi(self): - sfile = request.form.get('sfile', '') - dfile = request.form.get('dfile', '') - - if sfile == dfile: - return mw.returnJson(False, '源与目的一致!') - - if not os.path.exists(sfile): - return mw.returnJson(False, '指定文件不存在!') - - if os.path.isdir(sfile): - return self.copyDir(sfile, dfile) - - try: - import shutil - shutil.copyfile(sfile, dfile) - msg = mw.getInfo('复制文件[{1}]到[{2}]成功!', (sfile, dfile,)) - mw.writeLog('文件管理', msg) - stat = os.stat(sfile) - os.chown(dfile, stat.st_uid, stat.st_gid) - return mw.returnJson(True, '文件复制成功!') - except: - return mw.returnJson(False, '文件复制失败!') - - ##### ----- end ----- ### - - def copyDir(self, sfile, dfile): - - if not os.path.exists(sfile): - return mw.returnJson(False, '指定目录不存在!') - - if os.path.exists(dfile): - return mw.returnJson(False, '指定目录已存在!') - import shutil - try: - shutil.copytree(sfile, dfile) - stat = os.stat(sfile) - os.chown(dfile, stat.st_uid, stat.st_gid) - msg = mw.getInfo('复制目录[{1}]到[{2}]成功!', (sfile, dfile)) - mw.writeLog('文件管理', msg) - return mw.returnJson(True, '目录复制成功!') - except: - return mw.returnJson(False, '目录复制失败!') - - # 检查敏感目录 - def checkDir(self, path): - # path = str(path, encoding='utf-8') - path = path.replace('//', '/') - if path[-1:] == '/': - path = path[:-1] - - nDirs = ('', - '/', - '/*', - '/www', - '/root', - '/boot', - '/bin', - '/etc', - '/home', - '/dev', - '/sbin', - '/var', - '/usr', - '/tmp', - '/sys', - '/proc', - '/media', - '/mnt', - '/opt', - '/lib', - '/srv', - '/selinux', - '/www/server', - mw.getRootDir()) - - return not path in nDirs - - def getDirSize(self, path): - tmp = mw.execShell('du -sh ' + path) - return tmp - - def checkFileName(self, filename): - # 检测文件名 - nots = ['\\', '&', '*', '|', ';'] - if filename.find('/') != -1: - filename = filename.split('/')[-1] - for n in nots: - if n in filename: - return False - return True - - def setFileAccept(self, filename): - auth = 'www:www' - if mw.getOs() == 'darwin': - user = mw.execShell( - "who | sed -n '2, 1p' |awk '{print $1}'")[0].strip() - auth = user + ':staff' - - os.system('chown -R ' + auth + ' ' + filename) - os.system('chmod -R 755 ' + filename) - - # 移动到回收站 - def mvRecycleBin(self, path): - rPath = self.rPath - if not os.path.exists(rPath): - os.system('mkdir -p ' + rPath) - - rFile = rPath + path.replace('/', '_mw_') + '_t_' + str(time.time()) - try: - import shutil - shutil.move(path, rFile) - mw.writeLog('文件管理', mw.getInfo( - '移动文件[{1}]到回收站成功!', (path))) - return True - except: - mw.writeLog('文件管理', mw.getInfo( - '移动文件[{1}]到回收站失败!', (path))) - return False - - def getBody(self, path): - if not os.path.exists(path): - return mw.returnJson(False, '文件不存在', (path,)) - - if os.path.getsize(path) > 2097152: - return mw.returnJson(False, '不能在线编辑大于2MB的文件!') - - if os.path.isdir(path): - return mw.returnJson(False, '这不是一个文件!') - - fp = open(path, 'rb') - data = {} - data['status'] = True - if fp: - srcBody = fp.read() - fp.close() - - encoding_list = ['utf-8', 'GBK', 'BIG5'] - for el in encoding_list: - try: - data['encoding'] = el - data['data'] = srcBody.decode(data['encoding']) - break - except Exception as ex: - if el == 'BIG5': - return mw.returnJson(False, '文件编码不被兼容,无法正确读取文件!' + str(ex)) - else: - return mw.returnJson(False, '文件未正常打开!') - - return mw.returnJson(True, 'OK', data) - - def saveBody(self, path, data, encoding='utf-8'): - if not os.path.exists(path): - return mw.returnJson(False, '文件不存在') - try: - if encoding == 'ascii': - encoding = 'utf-8' - - data = data.encode( - encoding, errors='ignore').decode(encoding) - fp = open(path, 'w+', encoding=encoding) - fp.write(data) - fp.close() - - if path.find("web_conf") > 0: - mw.restartWeb() - - mw.writeLog('文件管理', '文件[{1}]保存成功', (path,)) - return mw.returnJson(True, '文件保存成功') - except Exception as ex: - return mw.returnJson(False, '文件保存错误:' + str(ex)) - - def zip(self, sfile, dfile, stype, path): - if sfile.find(',') == -1: - if not os.path.exists(path + '/' + sfile): - return mw.returnMsg(False, '指定文件不存在!') - - try: - tmps = mw.getRunDir() + '/tmp/panelExec.log' - if stype == 'zip': - mw.execShell("cd '" + path + "' && zip '" + dfile + - "' -r '" + sfile + "' > " + tmps + " 2>&1") - else: - sfiles = '' - for sfile in sfile.split(','): - if not sfile: - continue - sfiles += " '" + sfile + "'" - mw.execShell("cd '" + path + "' && tar -zcvf '" + - dfile + "' " + sfiles + " > " + tmps + " 2>&1") - self.setFileAccept(dfile) - mw.writeLog("文件管理", '文件[{1}]压缩[{2}]成功!', (sfile, dfile)) - return mw.returnJson(True, '文件压缩成功!') - except: - return mw.returnJson(False, '文件压缩失败!') - - def uncompress(self, sfile, dfile, path): - if not os.path.exists(sfile): - return mw.returnJson(False, '指定文件不存在!') - - filename = os.path.basename(sfile) - extension = os.path.splitext(filename)[-1] - extension = extension.strip('.') - - tar_gz = 'tar.gz' - tar_gz_len = len(tar_gz) - suffix_gz = sfile[-tar_gz_len:] - if suffix_gz == tar_gz: - extension = suffix_gz - - if not extension in ['tar.gz', 'gz', 'zip', 'rar']: - return mw.returnJson(False, '现在仅支持gz,zip,rar格式解压!') - - if mw.isAppleSystem() and extension == 'rar': - return mw.returnJson(False, 'macosx暂时不支持rar格式解压') - - try: - tmps = mw.getRunDir() + '/tmp/panelExec.log' - if extension == 'zip': - cmd = "cd " + path + " && unzip -o -d '" + dfile + \ - "' '" + sfile + "' > " + tmps + " 2>&1 &" - mw.execShell(cmd) - if extension == 'tar.gz': - cmd = "cd " + path + " && tar -zxvf " + sfile + \ - " -C " + dfile + " > " + tmps + " 2>&1 &" - mw.execShell(cmd) - if extension == 'gz': - cmd = "cd " + path + " && gunzip -k " + sfile + " > " + tmps + " 2>&1 &" - mw.execShell(cmd) - if extension == 'rar': - cmd = "cd " + path + " && unrar x " + sfile + \ - " " + dfile + " > " + tmps + " 2>&1 &" - mw.execShell(cmd) - - if os.path.exists(dfile): - self.setFileAccept(dfile) - mw.writeLog("文件管理", '文件[{1}]解压[{2}]成功!', (sfile, dfile)) - return mw.returnJson(True, '文件解压成功!') - except Exception as e: - return mw.returnJson(False, '文件解压失败!:' + str(e)) - - def unzip(self, sfile, dfile, stype, path): - if not os.path.exists(sfile): - return mw.returnJson(False, '指定文件不存在!') - - try: - tmps = mw.getRunDir() + '/tmp/panelExec.log' - if stype == 'zip': - mw.execShell("cd " + path + " && unzip -o -d '" + dfile + - "' '" + sfile + "' > " + tmps + " 2>&1 &") - else: - sfiles = '' - for sfile in sfile.split(','): - if not sfile: - continue - sfiles += " '" + sfile + "'" - mw.execShell("cd " + path + " && tar -zxvf " + sfiles + - " -C " + dfile + " > " + tmps + " 2>&1 &") - - self.setFileAccept(dfile) - mw.writeLog("文件管理", '文件[{1}]解压[{2}]成功!', (sfile, dfile)) - return mw.returnJson(True, '文件解压成功!') - except: - return mw.returnJson(False, '文件解压失败!') - - def delete(self, path): - - if not os.path.exists(path): - return mw.returnJson(False, '指定文件不存在!') - - # 检查是否为.user.ini - if path.find('.user.ini') >= 0: - mw.execShell("which chattr && chattr -i '" + path + "'") - - try: - if os.path.exists('data/recycle_bin.pl'): - if self.mvRecycleBin(path): - return mw.returnJson(True, '已将文件移动到回收站!') - os.remove(path) - mw.writeLog('文件管理', mw.getInfo( - '删除文件[{1}]成功!', (path))) - return mw.returnJson(True, '删除文件成功!') - except: - return mw.returnJson(False, '删除文件失败!') - - def getAccess(self, filename): - data = {} - try: - stat = os.stat(filename) - data['chmod'] = str(oct(stat.st_mode)[-3:]) - data['chown'] = pwd.getpwuid(stat.st_uid).pw_name - except: - data['chmod'] = 755 - data['chown'] = 'www' - return data - - def getSysUserList(self): - pwd_file = '/etc/passwd' - if os.path.exists(pwd_file): - content = mw.readFile(pwd_file) - clist = content.split('\n') - sys_users = [] - for line in clist: - if line.find(":")<0: - continue - lines = line.split(":",1) - sys_users.append(lines[0]) - return sys_users - return ['root','mysql','www'] - - # 计算文件数量 - def getCount(self, path, search): - i = 0 - for name in os.listdir(path): - if search: - if name.lower().find(search) == -1: - continue - if name == '.': - continue - i += 1 - return i - - def getAllDir(self, path, page=1, page_size=10, order='', search=None): - # print("search:", search) - data = {} - dirnames = [] - filenames = [] - - info = {} - - i = 0 - n = 0 - count = 0 - max_limit = 3000 - order_arr = order.split(' ') - if len(order_arr) < 2: - plist = mw.sortAllFileList(path, order_arr[0],'',search, max_limit) - else: - plist = mw.sortAllFileList(path, order_arr[0], order_arr[1], search,max_limit) - - info['count'] = len(plist) - info['row'] = page_size - info['p'] = page - info['tojs'] = 'getFiles' - pageObj = mw.getPageObject(info, '1,2,3,4,5,6,7,8') - data['PAGE'] = pageObj[0] - - for dst_file in plist: - - if not os.path.exists(dst_file): - continue - - i += 1 - if n >= pageObj[1].ROW: - break - if i < pageObj[1].SHIFT: - continue - - if os.path.isdir(dst_file): - dirnames.append(self.__get_stats(dst_file, path)) - else: - filenames.append(self.__get_stats(dst_file, path)) - n += 1 - - data['DIR'] = dirnames - data['FILES'] = filenames - data['PATH'] = path.replace('//', '/') - - return mw.getJson(data) - - #备份 - def getAllDirBk(self, path, page=1, page_size=10, order='', search=None): - data = {} - dirnames = [] - filenames = [] - - count = 0 - max_limit = 3000 - for d_list in os.walk(path): - - if count >= max_limit: - break - - for d in d_list[1]: - if count >= max_limit: - break - if d.lower().find(search) != -1: - filename = d_list[0] + '/' + d - if not os.path.exists(filename): - continue - dirnames.append(self.__get_stats(filename, path)) - count += 1 - - for f in d_list[2]: - if count >= max_limit: - break - - if f.lower().find(search) != -1: - filename = d_list[0] + '/' + f - if not os.path.exists(filename): - continue - filenames.append(self.__get_stats(filename, path)) - count += 1 - - data['DIR'] = sorted(dirnames) - data['FILES'] = sorted(filenames) - data['PATH'] = path.replace('//', '/') - - info = {} - info['count'] = len(dirnames) + len(filenames) - info['row'] = page_size - info['p'] = page - info['tojs'] = 'getFiles' - pageObj = mw.getPageObject(info, '1,2,3,4,5,6,7,8') - data['PAGE'] = pageObj[0] - - return mw.getJson(data) - - def getDir(self, path, page=1, page_size=10, order = '', search=None): - data = {} - dirnames = [] - filenames = [] - - info = {} - info['count'] = self.getCount(path, search) - info['row'] = page_size - info['p'] = page - info['tojs'] = 'getFiles' - pageObj = mw.getPageObject(info, '1,2,3,4,5,6,7,8') - data['PAGE'] = pageObj[0] - - i = 0 - n = 0 - - order_arr = order.split(' ') - if len(order_arr) < 2: - plist = mw.sortFileList(path, order_arr[0], '') - else: - plist = mw.sortFileList(path, order_arr[0], order_arr[1]) - - for filename in plist: - if search: - if filename.lower().find(search) == -1: - continue - i += 1 - if n >= pageObj[1].ROW: - break - if i < pageObj[1].SHIFT: - continue - try: - filePath = path + '/' + filename - if not os.path.exists(filePath): - continue - - file_stats = self.__get_stats(filePath, path) - if os.path.isdir(filePath): - dirnames.append(file_stats) - else: - filenames.append(file_stats) - n += 1 - except Exception as e: - continue - data['DIR'] = dirnames - data['FILES'] = filenames - data['PATH'] = path.replace('//', '/') - - return mw.getJson(data) - - def execShellApi(self): - # 执行SHELL命令 - shell = request.form.get('shell', '').strip() - path = request.form.get('path', '').strip() - disabled = ['vi', 'vim', 'top', 'passwd', 'su'] - tmp = shell.split(' ') - if tmp[0] in disabled: - return mw.returnJson(False, '禁止执行[{}]'.format(tmp[0])) - shellStr = '''#!/bin/bash -PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin -export PATH -cd %s -%s -''' % (path, shell) - mw.writeFile('/tmp/panelShell.sh', shellStr) - mw.execShell( - 'nohup bash /tmp/panelShell.sh > /tmp/panelShell.pl 2>&1 &') - return mw.returnJson(True, 'ok') - - def getExecShellMsgApi(self): - # 取SHELL执行结果 - fileName = '/tmp/panelShell.pl' - if not os.path.exists(fileName): - return '' - status = not mw.processExists('bash', None, '/tmp/panelShell.sh') - return mw.returnJson(status, mw.getNumLines(fileName, 200)) - - def __get_stats(self, filename, path=None): - # print(filename,path) - filename = filename.replace('//', '/') - try: - stat = os.stat(filename) - accept = str(oct(stat.st_mode)[-3:]) - mtime = str(int(stat.st_mtime)) - user = '' - try: - user = str(pwd.getpwuid(stat.st_uid).pw_name) - except: - user = str(stat.st_uid) - size = str(stat.st_size) - link = '' - if os.path.islink(filename): - link = ' -> ' + os.readlink(filename) - - if path: - tmp_path = (path + '/').replace('//', '/') - filename = filename.replace(tmp_path, '', 1) - - return filename + ';' + size + ';' + mtime + ';' + accept + ';' + user + ';' + link - except Exception as e: - # print(e) - return ';;;;;' diff --git a/class/core/firewall_api.py b/class/core/firewall_api.py deleted file mode 100755 index 04d48f06b..000000000 --- a/class/core/firewall_api.py +++ /dev/null @@ -1,513 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 防火墙操作 -# --------------------------------------------------------------------------------- - - -import psutil -import time -import os -import sys -import mw -import re -import json -import pwd - -from flask import request - - -class firewall_api: - - __isFirewalld = False - __isIptables = False - __isUfw = False - __isMac = False - - def __init__(self): - iptables_file = mw.systemdCfgDir() + '/iptables.service' - if os.path.exists('/usr/sbin/firewalld'): - self.__isFirewalld = True - elif os.path.exists('/usr/sbin/ufw'): - self.__isUfw = True - elif os.path.exists(iptables_file): - self.__isIptables = True - elif mw.isAppleSystem(): - self.__isMac = True - - ##### ----- start ----- ### - # 添加屏蔽IP - def addDropAddressApi(self): - import re - port = request.form.get('port', '').strip() - ps = request.form.get('ps', '').strip() - - rep = r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$" - if not re.search(rep, port): - return mw.returnJson(False, '您输入的IP地址不合法!') - address = port - if mw.M('firewall').where("port=?", (address,)).count() > 0: - return mw.returnJson(False, '您要放屏蔽的IP已存在屏蔽列表,无需重复处理!') - if self.__isUfw: - mw.execShell('ufw deny from ' + address + ' to any') - else: - if self.__isIptables: - cmd = 'iptables -I INPUT -s ' + address + ' -j DROP' - mw.execShell(cmd) - elif self.__isFirewalld: - cmd = 'firewall-cmd --permanent --add-rich-rule=\'rule family=ipv4 source address="' + \ - address + '" drop\'' - mw.execShell(cmd) - else: - pass - - msg = mw.getInfo('屏蔽IP[{1}]成功!', (address,)) - mw.writeLog("防火墙管理", msg) - addtime = time.strftime('%Y-%m-%d %X', time.localtime()) - mw.M('firewall').add('port,ps,addtime', (address, ps, addtime)) - self.firewallReload() - return mw.returnJson(True, '添加成功!') - - # 添加放行端口 - def addAcceptPortApi(self): - if not self.getFwStatus(): - return mw.returnJson(False, '防火墙启动时,才能添加规则!') - - port = request.form.get('port', '').strip() - ps = request.form.get('ps', '').strip() - stype = request.form.get('type', '').strip() - protocol = request.form.get('protocol', '').strip() - - data = self.addAcceptPortArgs(port, ps, stype, protocol) - return mw.getJson(data) - - # 添加放行端口 - def addAcceptPortArgs(self, port, ps, stype, protocol='tcp'): - import re - import time - - if not self.getFwStatus(): - self.setFw(0) - - rep = r"^\d{1,5}(:\d{1,5})?$" - if not re.search(rep, port): - return mw.returnData(False, '端口范围不正确!') - - if mw.M('firewall').where("port=?", (port,)).count() > 0: - return mw.returnData(False, '您要放行的端口已存在,无需重复放行!') - - msg = mw.getInfo('放行端口[{1}][{2}]成功', (port, protocol,)) - mw.writeLog("防火墙管理", msg) - addtime = time.strftime('%Y-%m-%d %X', time.localtime()) - mw.M('firewall').add('port,protocol,ps,addtime', - (port, protocol, ps, addtime)) - - self.addAcceptPort(port, protocol) - self.firewallReload() - return mw.returnData(True, '添加放行(' + port + ')端口成功!') - - # 删除IP屏蔽 - def delDropAddressApi(self): - if not self.getFwStatus(): - return mw.returnJson(False, '防火墙启动时,才能删除规则!') - - port = request.form.get('port', '').strip() - ps = request.form.get('ps', '').strip() - sid = request.form.get('id', '').strip() - address = port - if self.__isUfw: - mw.execShell('ufw delete deny from ' + address + ' to any') - elif self.__isFirewalld: - mw.execShell( - 'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="' + address + '" drop\'') - elif self.__isIptables: - cmd = 'iptables -D INPUT -s ' + address + ' -j DROP' - mw.execShell(cmd) - else: - pass - - msg = mw.getInfo('解除IP[{1}]的屏蔽!', (address,)) - mw.writeLog("防火墙管理", msg) - mw.M('firewall').where("id=?", (sid,)).delete() - - self.firewallReload() - return mw.returnJson(True, '删除成功!') - - def delAcceptPortArgs(self, port, protocol='tcp'): - if self.__isUfw: - if protocol == 'tcp': - mw.execShell('ufw delete allow ' + port + '/tcp') - if protocol == 'udp': - mw.execShell('ufw delete allow ' + port + '/udp') - if protocol == 'tcp/udp': - mw.execShell('ufw delete allow ' + port + '/tcp') - mw.execShell('ufw delete allow ' + port + '/udp') - elif self.__isFirewalld: - port = port.replace(':', '-') - if protocol == 'tcp': - mw.execShell( - 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp') - if protocol == 'udp': - mw.execShell( - 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp') - if protocol == 'tcp/udp': - mw.execShell( - 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp') - mw.execShell( - 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp') - elif self.__isIptables: - if protocol == 'tcp': - mw.execShell( - 'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT') - if protocol == 'udp': - mw.execShell( - 'iptables -D INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT') - if protocol == 'tcp/udp': - mw.execShell( - 'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT') - mw.execShell( - 'iptables -D INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT') - else: - pass - - mw.M('firewall').where("port=?", (port,)).delete() - - msg = mw.getInfo('删除防火墙放行端口[{1}][{2}]成功!', (port, protocol,)) - mw.writeLog("防火墙管理", msg) - - self.firewallReload() - return True - - # 删除放行端口 - def delAcceptPortApi(self): - port = request.form.get('port', '').strip() - protocol = request.form.get('protocol', 'tcp').strip() - sid = request.form.get('id', '').strip() - mw_port = mw.readFile('data/port.pl') - - if(port == mw_port): - return mw.returnJson(False, '失败,不能删除当前面板端口!') - try: - self.delAcceptPortArgs(port, protocol) - mw.M('firewall').where("id=?", (sid,)).delete() - - return mw.returnJson(True, '删除成功!') - except Exception as e: - return mw.returnJson(False, '删除失败!:' + str(e)) - - def getWwwPathApi(self): - path = mw.getLogsDir() - return mw.getJson({'path': path}) - - def getListApi(self): - p = request.form.get('p', '1').strip() - limit = request.form.get('limit', '10').strip() - return self.getList(int(p), int(limit)) - - def getSshInfoApi(self): - data = {} - - file = '/etc/ssh/sshd_config' - conf = mw.readFile(file) - rep = r"#*Port\s+([0-9]+)\s*\n" - port = re.search(rep, conf).groups(0)[0] - - isPing = True - try: - if mw.isAppleSystem(): - isPing = True - else: - file = '/etc/sysctl.conf' - sys_conf = mw.readFile(file) - rep = r"#*net\.ipv4\.icmp_echo_ignore_all\s*=\s*([0-9]+)" - tmp = re.search(rep, sys_conf).groups(0)[0] - if tmp == '1': - isPing = False - except: - isPing = True - - # sshd 检测 - status = True - cmd = "service sshd status | grep -P '(dead|stop)'|grep -v grep" - ssh_status = mw.execShell(cmd) - if ssh_status[0] != '': - status = False - - cmd = "systemctl status sshd.service | grep 'dead'|grep -v grep" - ssh_status = mw.execShell(cmd) - if ssh_status[0] != '': - status = False - - data['pass_prohibit_status'] = False - # 密码登陆配置检查 - pass_rep = r"PasswordAuthentication\s+(\w*)\s*\n" - pass_status = re.search(pass_rep, conf) - if pass_status: - if pass_status and pass_status.groups(0)[0].strip() == 'no': - data['pass_prohibit_status'] = True - else: - data['pass_prohibit_status'] = True - - data['port'] = port - data['status'] = status - data['ping'] = isPing - if mw.isAppleSystem(): - data['firewall_status'] = False - else: - data['firewall_status'] = self.getFwStatus() - return mw.getJson(data) - - def setSshPortApi(self): - port = request.form.get('port', '1').strip() - if int(port) < 22 or int(port) > 65535: - return mw.returnJson(False, '端口范围必需在22-65535之间!') - - ports = ['21', '25', '80', '443', '888'] - if port in ports: - return mw.returnJson(False, '(' + port + ')' + '特殊端口不可设置!') - - file = '/etc/ssh/sshd_config' - conf = mw.readFile(file) - - rep = r"#*Port\s+([0-9]+)\s*\n" - conf = re.sub(rep, "Port " + port + "\n", conf) - mw.writeFile(file, conf) - - self.addAcceptPortArgs(port, 'SSH端口修改', 'port') - if self.__isUfw: - mw.execShell("service ssh restart") - elif self.__isIptables: - mw.execShell("/etc/init.d/sshd restart") - elif self.__isFirewalld: - mw.execShell("systemctl restart sshd.service") - else: - return mw.returnJson(False, '修改失败!') - return mw.returnJson(True, '修改成功!') - - def setSshStatusApi(self): - if mw.isAppleSystem(): - return mw.returnJson(True, '开发机不能操作!') - - status = request.form.get('status', '1').strip() - msg = 'SSH服务已启用' - act = 'start' - if status == "1": - msg = 'SSH服务已停用' - act = 'stop' - - ssh_service = mw.systemdCfgDir() + '/sshd.service' - if os.path.exists(ssh_service): - mw.execShell("systemctl " + act + " sshd.service") - else: - mw.execShell('service sshd ' + act) - - if os.path.exists('/etc/init.d/sshd'): - mw.execShell('/etc/init.d/sshd ' + act) - - mw.writeLog("防火墙管理", msg) - return mw.returnJson(True, '操作成功!') - - def setSshPassStatusApi(self): - if mw.isAppleSystem(): - return mw.returnJson(True, '开发机不能操作!') - - status = request.form.get('status', '1').strip() - msg = '禁止密码登陆成功' - if status == "1": - msg = '开启密码登陆成功' - - file = '/etc/ssh/sshd_config' - if not os.path.exists(file): - return mw.returnJson(False, '无法设置!') - - conf = mw.readFile(file) - - pass_rep = r"PasswordAuthentication\s+(\w*)\s*\n" - pass_status = re.search(pass_rep, conf) - if not pass_status: - rep = r"(#)?PasswordAuthentication\s+(\w*)\s*\n" - conf = re.sub(rep, "PasswordAuthentication yes\n", conf) - - if status == '1': - rep = r"PasswordAuthentication\s+(\w*)\s*\n" - conf = re.sub(rep, "PasswordAuthentication yes\n", conf) - else: - rep = r"PasswordAuthentication\s+(\w*)\s*\n" - conf = re.sub(rep, "PasswordAuthentication no\n", conf) - mw.writeFile(file, conf) - mw.execShell("systemctl restart sshd.service") - mw.writeLog("SSH管理", msg) - return mw.returnJson(True, msg) - - def setPingApi(self): - if mw.isAppleSystem(): - return mw.returnJson(True, '开发机不能操作!') - - status = request.form.get('status') - filename = '/etc/sysctl.conf' - conf = mw.readFile(filename) - if conf.find('net.ipv4.icmp_echo') != -1: - rep = r"net\.ipv4\.icmp_echo.*" - conf = re.sub(rep, 'net.ipv4.icmp_echo_ignore_all=' + status, conf) - else: - conf += "\nnet.ipv4.icmp_echo_ignore_all=" + status - - mw.writeFile(filename, conf) - mw.execShell('sysctl -p') - return mw.returnJson(True, '设置成功!') - - def setFwApi(self): - if mw.isAppleSystem(): - return mw.returnJson(True, '开发机不能设置!') - - status = request.form.get('status', '1') - return mw.getJson(self.setFw(status)) - - def setFwIptables(self, status): - # iptables特殊处理 - if status == '1': - mw.execShell('service iptables save') - mw.execShell('service iptables stop') - else: - # 重新导入数据 - _list = mw.M('firewall').field('id,port,ps,addtime').limit( - '0,1000').order('id desc').select() - - mw.execShell('iptables -P INPUT DROP') - mw.execShell('iptables -P OUTPUT ACCEPT') - for x in _list: - port = x['port'] - if mw.isIpAddr(port): - cmd = 'iptables -I INPUT -s ' + port + ' -j DROP' - mw.execShell(cmd) - else: - self.addAcceptPort(port) - - mw.execShell('service iptables save') - mw.execShell('service iptables start') - - def setFw(self, status): - - if self.__isIptables: - self.setFwIptables(status) - return mw.returnData(True, '设置成功!') - - if status == '1': - if self.__isUfw: - mw.execShell('/usr/sbin/ufw disable') - - elif self.__isFirewalld: - mw.execShell('systemctl stop firewalld.service') - mw.execShell('systemctl disable firewalld.service') - else: - pass - else: - if self.__isUfw: - mw.execShell("echo 'y'| ufw enable") - elif self.__isFirewalld: - mw.execShell('systemctl start firewalld.service') - mw.execShell('systemctl enable firewalld.service') - else: - pass - - return mw.returnData(True, '设置成功!') - - ##### ----- start ----- ### - - def getList(self, page, limit): - - start = (page - 1) * limit - - _list = mw.M('firewall').field('id,port,protocol,ps,addtime').limit( - str(start) + ',' + str(limit)).order('id desc').select() - data = {} - data['data'] = _list - - count = mw.M('firewall').count() - _page = {} - _page['count'] = count - _page['tojs'] = 'showAccept' - _page['p'] = page - - data['page'] = mw.getPage(_page) - return mw.getJson(data) - - def addAcceptPort(self, port, protocol='tcp'): - if self.__isUfw: - if protocol == 'tcp': - mw.execShell('ufw allow ' + port + '/tcp') - if protocol == 'udp': - mw.execShell('ufw allow ' + port + '/udp') - if protocol == 'tcp/udp': - mw.execShell('ufw allow ' + port + '/tcp') - mw.execShell('ufw allow ' + port + '/udp') - elif self.__isFirewalld: - port = port.replace(':', '-') - if protocol == 'tcp': - cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp' - mw.execShell(cmd) - if protocol == 'udp': - cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/udp' - mw.execShell(cmd) - if protocol == 'tcp/udp': - cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp' - mw.execShell(cmd) - cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/udp' - mw.execShell(cmd) - elif self.__isIptables: - if protocol == 'tcp': - cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT' - mw.execShell(cmd) - if protocol == 'udp': - cmd = 'iptables -I INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT' - mw.execShell(cmd) - if protocol == 'tcp/udp': - cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT' - mw.execShell(cmd) - cmd = 'iptables -I INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT' - mw.execShell(cmd) - else: - pass - return True - - def firewallReload(self): - if self.__isUfw: - mw.execShell('/usr/sbin/ufw reload') - return - elif self.__isIptables: - mw.execShell('service iptables save') - mw.execShell('service iptables restart') - elif self.__isFirewalld: - mw.execShell('firewall-cmd --reload') - else: - pass - - def getFwStatus(self): - if self.__isUfw: - cmd = "/usr/sbin/ufw status| grep Status | awk -F ':' '{print $2}'" - data = mw.execShell(cmd) - if data[0].strip() == 'inactive': - return False - return True - elif self.__isIptables: - cmd = "systemctl status iptables | grep 'inactive'" - data = mw.execShell(cmd) - if data[0] != '': - return False - return True - elif self.__isFirewalld: - cmd = "ps -ef|grep firewalld |grep -v grep | awk '{print $2}'" - data = mw.execShell(cmd) - if data[0] == '': - return False - return True - else: - return False diff --git a/class/core/logs_api.py b/class/core/logs_api.py deleted file mode 100644 index 128d9a5dd..000000000 --- a/class/core/logs_api.py +++ /dev/null @@ -1,355 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 日志类操作 -# --------------------------------------------------------------------------------- - -import psutil -import time -import os -import sys -import mw -import re -import json -import pwd - -from datetime import datetime -from flask import request - - -class logs_api: - - __months = {'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06', - 'Jul': '07', 'Aug': '08', 'Sep': '09', 'Sept': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'} - - def __init__(self): - pass - - def delPanelLogsApi(self): - mw.M('logs').where('id>?', (0,)).delete() - mw.writeLog('面板设置', '面板操作日志已清空!') - return mw.returnJson(True, '面板操作日志已清空!') - - def getLogListApi(self): - p = request.form.get('p', '1').strip() - limit = request.form.get('limit', '10').strip() - search = request.form.get('search', '').strip() - return self.getLogList(int(p), int(limit), search) - - def getLogList(self, page, limit, search=''): - find_search = '' - if search != '': - find_search = "type like '%" + search + "%' or log like '%" + \ - search + "%' or addtime like '%" + search + "%'" - - start = (page - 1) * limit - - _list = mw.M('logs').where(find_search, ()).field( - 'id,type,log,addtime').limit(str(start) + ',' + str(limit)).order('id desc').select() - data = {} - data['data'] = _list - - count = mw.M('logs').where(find_search, ()).count() - _page = {} - _page['count'] = count - _page['tojs'] = 'getLogs' - _page['p'] = page - - data['page'] = mw.getPage(_page) - return mw.getJson(data) - - def getLogsTitle(self, log_name): - log_name = log_name.replace('.1', '') - if log_name in ['mw-update.log']: - return '面板更新日志' - if log_name in ['mw-install.log']: - return '面板安装日志' - if log_name in ['auth.log', 'secure'] or log_name.find('auth.') == 0: - return '授权日志' - if log_name in ['dmesg'] or log_name.find('dmesg') == 0: - return '内核缓冲区日志' - if log_name in ['syslog'] or log_name.find('syslog') == 0: - return '系统日志' - if log_name in ['rsyncd.log']: - return '远程同步日志' - if log_name in ['btmp']: - return '失败的登录记录' - if log_name in ['utmp', 'wtmp']: - return '登录和重启记录' - if log_name in ['lastlog']: - return '用户最后登录' - if log_name in ['yum.log']: - return 'yum包管理器日志' - if log_name in ['anaconda.log']: - return 'Anaconda日志' - if log_name in ['dpkg.log']: - return 'dpkg日志' - if log_name in ['daemon.log']: - return '系统后台守护进程日志' - if log_name in ['boot.log']: - return '启动日志' - if log_name in ['kern.log']: - return '内核日志' - if log_name in ['maillog', 'mail.log']: - return '邮件日志' - if log_name.find('Xorg') == 0: - return 'Xorg日志' - if log_name in ['cron.log']: - return '定时任务日志' - if log_name in ['alternatives.log']: - return '更新替代信息' - if log_name in ['debug']: - return '调试信息' - if log_name.find('apt') == 0: - return 'apt-get相关日志' - if log_name.find('installer') == 0: - return '系统安装相关日志' - if log_name in ['messages']: - return '综合日志' - return '{}日志'.format(log_name.split('.')[0]) - - def getAuditLogsFilesApi(self): - log_dir = '/var/log' - log_files = [] - for log_file in os.listdir(log_dir): - log_suffix = log_file.split('.')[-1:] - if log_suffix[0] in ['gz', 'xz', 'bz2', 'asl']: - continue - - if log_file in ['.', '..']: - continue - - filename = os.path.join(log_dir, log_file) - if not os.path.exists(filename): - continue - - if os.path.isfile(filename): - file_size = os.path.getsize(filename) - if not file_size: - continue - - tmp = { - 'name': log_file, - 'size': file_size, - 'log_file': filename, - 'title': self.getLogsTitle(log_file), - 'uptime': os.path.getmtime(filename) - } - - log_files.append(tmp) - else: - for next_name in os.listdir(filename): - if next_name[-3:] in ['.gz', '.xz']: - continue - next_file = os.path.join(filename, next_name) - if not os.path.isfile(next_file): - continue - file_size = os.path.getsize(next_file) - if not file_size: - continue - log_name = '{}/{}'.format(log_file, next_name) - tmp = { - 'name': log_name, - 'size': file_size, - 'log_file': next_file, - 'title': self.getLogsTitle(log_name), - 'uptime': os.path.getmtime(next_file) - } - log_files.append(tmp) - log_files = sorted(log_files, key=lambda x: x['name'], reverse=True) - return mw.getJson(log_files) - - def getAuditFileApi(self): - log_name = request.form.get('log_name', '').strip() - return self.getAuditLogsName(log_name) - - def __to_date2(self, date_str): - tmp = date_str.split() - s_date = str(tmp[-1]) + '-' + self.__months.get(tmp[1], tmp[1]) + '-' + tmp[2] + ' ' + tmp[3] - return s_date - - def __to_date3(self, date_str): - tmp = date_str.split() - s_date = str(datetime.now().year) + '-' + self.__months.get(tmp[1], tmp[1]) + '-' + tmp[2] + ' ' + tmp[3] - return s_date - - def __to_date4(self, date_str): - tmp = date_str.split() - s_date = str(datetime.now().year) + '-' + \ - self.__months.get(tmp[0], tmp[0]) + '-' + tmp[1] + ' ' + tmp[2] - return s_date - - def getAuditLast(self, log_name): - # 获取日志 - cmd = '''LANG=en_US.UTF-8 last -n 200 -x -f {} |grep -v 127.0.0.1|grep -v " begins"'''.format( - '/var/log/' + log_name) - result = mw.execShell(cmd) - lastlog_list = [] - for _line in result[0].split("\n"): - if not _line: - continue - tmp = {} - sp_arr = _line.split() - tmp['用户'] = sp_arr[0] - if sp_arr[0] == 'runlevel': - tmp['来源'] = sp_arr[4] - tmp['端口'] = ' '.join(sp_arr[1:4]) - tmp['时间'] = self.__to_date3( - ' '.join(sp_arr[5:])) + ' ' + ' '.join(sp_arr[-2:]) - elif sp_arr[0] in ['reboot', 'shutdown']: - tmp['来源'] = sp_arr[3] - tmp['端口'] = ' '.join(sp_arr[1:3]) - if sp_arr[-3] == '-': - tmp['时间'] = self.__to_date3( - ' '.join(sp_arr[4:])) + ' ' + ' '.join(sp_arr[-3:]) - else: - tmp['时间'] = self.__to_date3( - ' '.join(sp_arr[4:])) + ' ' + ' '.join(sp_arr[-2:]) - elif sp_arr[1] in ['tty1', 'tty', 'tty2', 'tty3', 'hvc0', 'hvc1', 'hvc2'] or len(sp_arr) == 9: - tmp['来源'] = '' - tmp['端口'] = sp_arr[1] - tmp['时间'] = self.__to_date3( - ' '.join(sp_arr[2:])) + ' ' + ' '.join(sp_arr[-3:]) - else: - tmp['来源'] = sp_arr[2] - tmp['端口'] = sp_arr[1] - tmp['时间'] = self.__to_date3( - ' '.join(sp_arr[3:])) + ' ' + ' '.join(sp_arr[-3:]) - - # tmp['_line'] = _line - lastlog_list.append(tmp) - # lastlog_list = sorted(lastlog_list,key=lambda x:x['时间'],reverse=True) - return mw.returnData(True, 'ok!', lastlog_list) - - def getAuditLastLog(self): - cmd = '''LANG=en_US.UTF-8 lastlog|grep -v Username''' - result = mw.execShell(cmd) - lastlog_list = [] - for _line in result[0].split("\n"): - if not _line: - continue - tmp = {} - sp_arr = _line.split() - tmp['用户'] = sp_arr[0] - # tmp['_line'] = _line - if _line.find('Never logged in') != -1: - tmp['最后登录时间'] = '0' - tmp['最后登录来源'] = '-' - tmp['最后登录端口'] = '-' - lastlog_list.append(tmp) - continue - tmp['最后登录来源'] = sp_arr[2] - tmp['最后登录端口'] = sp_arr[1] - tmp['最后登录时间'] = self.__to_date2(' '.join(sp_arr[3:])) - - lastlog_list.append(tmp) - lastlog_list = sorted(lastlog_list, key=lambda x: x[ - '最后登录时间'], reverse=True) - for i in range(len(lastlog_list)): - if lastlog_list[i]['最后登录时间'] == '0': - lastlog_list[i]['最后登录时间'] = '从未登录过' - return mw.returnData(True, 'ok!', lastlog_list) - - def parseAuditFile(self, log_name, result): - log_list = [] - is_string = True - for _line in result.split("\n"): - if not _line.strip(): - continue - if log_name.find('sa/sa') == -1: - if _line[:3] in self.__months: - _msg = _line[16:] - _tmp = _msg.split(": ") - _act = '' - if len(_tmp) > 1: - _act = _tmp[0] - _msg = _tmp[1] - else: - _msg = _tmp[0] - _line = { - "时间": self.__to_date4(_line[:16].strip()), - "角色": _act, - "事件": _msg - } - is_string = False - elif _line[:2] in ['19', '20', '21', '22', '23', '24']: - _msg = _line[19:] - _tmp = _msg.split(" ") - _act = _tmp[1] - _msg = ' '.join(_tmp[2:]) - _line = { - "时间": _line[:19].strip(), - "角色": _act, - "事件": _msg - } - is_string = False - elif log_name.find('alternatives') == 0: - _tmp = _line.split(": ") - _last = _tmp[0].split(" ") - _act = _last[0] - _msg = ' '.join(_tmp[1:]) - _line = { - "时间": ' '.join(_last[1:]).strip(), - "角色": _act, - "事件": _msg - } - is_string = False - else: - if not is_string: - if type(_line) != dict: - continue - - log_list.append(_line) - - return log_list - - def getAuditLogsName(self, log_name): - # print(log_name) - if log_name in ['wtmp', 'btmp', 'utmp'] or log_name.find('wtmp') == 0 or log_name.find('btmp') == 0 or log_name.find('utmp') == 0: - return self.getAuditLast(log_name) - - if log_name.find('lastlog') == 0: - return self.getAuditLastLog() - - if log_name.find('sa/sa') == 0: - if log_name.find('sa/sar') == -1: - return mw.execShell("sar -f /var/log/{}".format(log_name))[0] - log_dir = '/var/log' - log_file = log_dir + '/' + log_name - if not os.path.exists(log_file): - return mw.returnData(False, '日志文件不存在!') - result = mw.getLastLine(log_file, 100) - try: - log_list = self.parseAuditFile(log_name, result) - _string = [] - _dict = [] - _list = [] - for _line in log_list: - if isinstance(_line, str): - _string.append(_line.strip()) - elif isinstance(_line, dict): - _dict.append(_line) - elif isinstance(_line, list): - _list.append(_line) - else: - continue - _str_len = len(_string) - _dict_len = len(_dict) - _list_len = len(_list) - if _str_len > _dict_len + _list_len: - return "\n".join(_string) - elif _dict_len > _str_len + _list_len: - return mw.returnData(True, 'ok!', _dict) - else: - return mw.returnData(True, 'ok!', _list) - - except: - return mw.returnData(True, 'ok!', result) diff --git a/class/core/mw.py b/class/core/mw.py deleted file mode 100755 index a4441260e..000000000 --- a/class/core/mw.py +++ /dev/null @@ -1,2287 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 核心方法库 -# --------------------------------------------------------------------------------- - - -import os -import sys -import time -import string -import json -import hashlib -import shlex -import datetime -import subprocess -import glob -import base64 -import re - -from random import Random - -import db - - -def execShell(cmdstring, cwd=None, timeout=None, shell=True): - - if shell: - cmdstring_list = cmdstring - else: - cmdstring_list = shlex.split(cmdstring) - if timeout: - end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout) - - sub = subprocess.Popen(cmdstring_list, cwd=cwd, stdin=subprocess.PIPE, - shell=shell, bufsize=4096, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - while sub.poll() is None: - time.sleep(0.1) - if timeout: - if end_time <= datetime.datetime.now(): - raise Exception("Timeout:%s" % cmdstring) - - if sys.version_info[0] == 2: - return sub.communicate() - - data = sub.communicate() - # python3 fix 返回byte数据 - if isinstance(data[0], bytes): - t1 = str(data[0], encoding='utf-8') - - if isinstance(data[1], bytes): - t2 = str(data[1], encoding='utf-8') - return (t1, t2) - - -def getTracebackInfo(): - import traceback - errorMsg = traceback.format_exc() - return errorMsg - - -def getRunDir(): - return os.getcwd() - - -def getRootDir(): - return os.path.dirname(os.path.dirname(getRunDir())) - - -def getPluginDir(): - return getRunDir() + '/plugins' - -def getPanelDataDir(): - return getRunDir() + '/data' - -def getMWLogs(): - return getRunDir() + '/logs' - -def getPanelTmp(): - return getRunDir() + '/tmp' - - -def getServerDir(): - return getRootDir() + '/server' - - -def getLogsDir(): - return getRootDir() + '/wwwlogs' - - -def getWwwDir(): - file = getRunDir() + '/data/site.pl' - if os.path.exists(file): - return readFile(file).strip() - return getRootDir() + '/wwwroot' - - -def setWwwDir(wdir): - file = getRunDir() + '/data/site.pl' - return writeFile(file, wdir) - - -def getBackupDir(): - file = getRunDir() + '/data/backup.pl' - if os.path.exists(file): - return readFile(file).strip() - return getRootDir() + '/backup' - - -def setBackupDir(bdir): - file = getRunDir() + '/data/backup.pl' - return writeFile(file, bdir) - - -def getAcmeDir(): - acme = '/root/.acme.sh' - if isAppleSystem(): - cmd = "who | sed -n '2, 1p' |awk '{print $1}'" - user = execShell(cmd)[0].strip() - acme = '/Users/' + user + '/.acme.sh' - if not os.path.exists(acme): - acme = '/.acme.sh' - return acme - - -def getAcmeDomainDir(domain): - acme_dir = getAcmeDir() - acme_domain = acme_dir + '/' + domain - acme_domain_ecc = acme_domain + '_ecc' - if os.path.exists(acme_domain_ecc): - acme_domain = acme_domain_ecc - return acme_domain - - -def fileNameCheck(filename): - f_strs = [';', '&', '<', '>'] - for fs in f_strs: - if filename.find(fs) != -1: - return False - return True - - -def triggerTask(): - isTask = getRunDir() + '/tmp/panelTask.pl' - writeFile(isTask, 'True') - - -def systemdCfgDir(): - # ubuntu - cfg_dir = '/lib/systemd/system' - if os.path.exists(cfg_dir): - return cfg_dir - - # debian,centos - cfg_dir = '/usr/lib/systemd/system' - if os.path.exists(cfg_dir): - return cfg_dir - - # local test - return "/tmp" - - -def getSslCrt(): - if os.path.exists('/etc/ssl/certs/ca-certificates.crt'): - return '/etc/ssl/certs/ca-certificates.crt' - if os.path.exists('/etc/pki/tls/certs/ca-bundle.crt'): - return '/etc/pki/tls/certs/ca-bundle.crt' - return '' - - -def getOs(): - return sys.platform - - -def getOsName(): - cmd = "cat /etc/*-release | grep PRETTY_NAME |awk -F = '{print $2}' | awk -F '\"' '{print $2}'| awk '{print $1}'" - data = execShell(cmd) - return data[0].strip().lower() - - -def getOsID(): - cmd = "cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F '\"' '{print $2}'" - data = execShell(cmd) - return data[0].strip() - - -def getFileSuffix(file): - tmp = file.split('.') - ext = tmp[len(tmp) - 1] - return ext - - - -def isAppleSystem(): - if getOs() == 'darwin': - return True - return False - -def isDocker(): - return os.path.exists('/.dockerenv') - - -def isSupportSystemctl(): - if isAppleSystem(): - return False - if isDocker(): - return False - - current_os = getOs() - if current_os.startswith("freebsd"): - return False - return True - - -def isDebugMode(): - if isAppleSystem(): - return True - - debugPath = getRunDir() + "/data/debug.pl" - if os.path.exists(debugPath): - return True - - return False - - -def isNumber(s): - try: - float(s) - return True - except ValueError: - pass - - try: - import unicodedata - unicodedata.numeric(s) - return True - except (TypeError, ValueError): - pass - - return False - - -def deleteFile(file): - if os.path.exists(file): - os.remove(file) - - -def isInstalledWeb(): - path = getServerDir() + '/openresty/nginx/sbin/nginx' - if os.path.exists(path): - return True - return False - - -def isIpAddr(ip): - check_ip = re.compile( - '^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$') - if check_ip.match(ip): - return True - else: - return False - - -def getWebStatus(): - pid = getServerDir() + '/openresty/nginx/logs/nginx.pid' - if os.path.exists(pid): - return True - return False - - -# ------------------------------ openresty start ----------------------------- -def restartWeb(): - return opWeb("reload") - - -def opWeb(method): - if not isInstalledWeb(): - return False - - # systemd - systemd = '/lib/systemd/system/openresty.service' - if os.path.exists(systemd): - execShell('systemctl ' + method + ' openresty') - return True - - # initd - initd = getServerDir() + '/openresty/init.d/openresty' - - if os.path.exists(initd): - execShell(initd + ' ' + method) - return True - - return False - - -def opLuaMake(cmd_name): - path = getServerDir() + '/web_conf/nginx/lua/lua.conf' - root_dir = getServerDir() + '/web_conf/nginx/lua/' + cmd_name - dst_path = getServerDir() + '/web_conf/nginx/lua/' + cmd_name + '.lua' - def_path = getServerDir() + '/web_conf/nginx/lua/empty.lua' - - if not os.path.exists(root_dir): - execShell('mkdir -p ' + root_dir) - - files = [] - for fl in os.listdir(root_dir): - suffix = getFileSuffix(fl) - if suffix != 'lua': - continue - flpath = os.path.join(root_dir, fl) - files.append(flpath) - - if len(files) > 0: - def_path = dst_path - content = '' - for f in files: - t = readFile(f) - f_base = os.path.basename(f) - content += '-- ' + '*' * 20 + ' ' + f_base + ' start ' + '*' * 20 + "\n" - content += t - content += "\n" + '-- ' + '*' * 20 + ' ' + f_base + ' end ' + '*' * 20 + "\n" - writeFile(dst_path, content) - else: - if os.path.exists(dst_path): - os.remove(dst_path) - - conf = readFile(path) - conf = re.sub(cmd_name + ' (.*);', - cmd_name + " " + def_path + ";", conf) - writeFile(path, conf) - - -def opLuaInitFile(): - opLuaMake('init_by_lua_file') - - -def opLuaInitWorkerFile(): - opLuaMake('init_worker_by_lua_file') - - -def opLuaInitAccessFile(): - opLuaMake('access_by_lua_file') - - -def opLuaMakeAll(): - opLuaInitFile() - opLuaInitWorkerFile() - opLuaInitAccessFile() - -# ------------------------------ openresty end ----------------------------- - - -def restartMw(): - import system_api - system_api.system_api().restartMw() - -def restartNginx(self): - writeFile('data/restart_nginx.pl', 'True') - return True - -def checkWebConfig(): - op_dir = getServerDir() + '/openresty/nginx' - # "ulimit -n 10240 && " + - cmd = op_dir + \ - "/sbin/nginx -t -c " + op_dir + "/conf/nginx.conf" - result = execShell(cmd) - searchStr = 'test is successful' - if result[1].find(searchStr) == -1: - msg = getInfo('配置文件错误: {1}', (result[1],)) - writeLog("软件管理", msg) - return result[1] - return True - - -def M(table): - sql = db.Sql() - return sql.table(table) - - -def getPage(args, result='1,2,3,4,5,8'): - data = getPageObject(args, result) - return data[0] - - -def getPageObject(args, result='1,2,3,4,5,8'): - # 取分页 - import page - # 实例化分页类 - page = page.Page() - info = {} - - info['count'] = 0 - if 'count' in args: - info['count'] = int(args['count']) - - info['row'] = 10 - if 'row' in args: - info['row'] = int(args['row']) - - info['p'] = 1 - if 'p' in args: - info['p'] = int(args['p']) - info['uri'] = {} - info['return_js'] = '' - if 'tojs' in args: - info['return_js'] = args['tojs'] - - if 'args_tpl' in args: - info['args_tpl'] = args['args_tpl'] - - return (page.GetPage(info, result), page) - - -def md5(content): - # 生成MD5 - try: - m = hashlib.md5() - m.update(content.encode("utf-8")) - return m.hexdigest() - except Exception as ex: - return False - - -def getFileMd5(filename): - # 文件的MD5值 - if not os.path.isfile(filename): - return False - - myhash = hashlib.md5() - f = file(filename, 'rb') - while True: - b = f.read(8096) - if not b: - break - myhash.update(b) - f.close() - return myhash.hexdigest() - - -def getRandomString(length): - # 取随机字符串 - rnd_str = '' - chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789' - chrlen = len(chars) - 1 - random = Random() - for i in range(length): - rnd_str += chars[random.randint(0, chrlen)] - return rnd_str - - -def getUniqueId(): - """ - 根据时间生成唯一ID - :return: - """ - current_time = datetime.datetime.now() - str_time = current_time.strftime('%Y%m%d%H%M%S%f')[:-3] - unique_id = "{0}".format(str_time) - return unique_id - - -def getJson(data): - import json - return json.dumps(data) - -def getObjectByJson(data): - import json - return json.loads(data) - -def returnData(status, msg, data=None): - return {'status': status, 'msg': msg, 'data': data} - - -def returnJson(status, msg, data=None): - # if data == None: - # return {'status': status, 'msg': msg} - # return {'status': status, 'msg': msg, 'data': data} - if data == None: - return getJson({'status': status, 'msg': msg}) - return getJson({'status': status, 'msg': msg, 'data': data}) - - -def getLanguage(): - path = 'data/language.pl' - if not os.path.exists(path): - return 'Simplified_Chinese' - return readFile(path).strip() - - -def getStaticJson(name="public"): - file = 'static/language/' + getLanguage() + '/' + name + '.json' - if not os.path.exists(file): - file = 'route/static/language/' + getLanguage() + '/' + name + '.json' - return file - - -def returnMsg(status, msg, args=()): - # 取通用字曲返回 - pjson = getStaticJson('public') - logMessage = json.loads(readFile(pjson)) - keys = logMessage.keys() - - if msg in keys: - msg = logMessage[msg] - for i in range(len(args)): - rep = '{' + str(i + 1) + '}' - msg = msg.replace(rep, args[i]) - return {'status': status, 'msg': msg, 'data': args} - - -def getInfo(msg, args=()): - # 取提示消息 - for i in range(len(args)): - rep = '{' + str(i + 1) + '}' - msg = msg.replace(rep, args[i]) - return msg - - -def getMsg(key, args=()): - # 取提示消息 - try: - pjson = getStaticJson('public') - logMessage = json.loads(pjson) - keys = logMessage.keys() - msg = None - if key in keys: - msg = logMessage[key] - for i in range(len(args)): - rep = '{' + str(i + 1) + '}' - msg = msg.replace(rep, args[i]) - return msg - except: - return key - - -def getLan(key): - # 取提示消息 - pjson = getStaticJson('public') - logMessage = json.loads(pjson) - keys = logMessage.keys() - msg = None - if key in keys: - msg = logMessage[key] - return msg - - -def readFile(filename): - # 读文件内容 - try: - fp = open(filename, 'r') - fBody = fp.read() - fp.close() - return fBody - except Exception as e: - # print(e) - return False - - -def getDate(): - # 取格式时间 - import time - return time.strftime('%Y-%m-%d %X', time.localtime()) - - -def getDateFromNow(tf_format="%Y-%m-%d %H:%M:%S", time_zone="Asia/Shanghai"): - # 取格式时间 - import time - os.environ['TZ'] = time_zone - time.tzset() - return time.strftime(tf_format, time.localtime()) - - -def getDataFromInt(val): - time_format = '%Y-%m-%d %H:%M:%S' - time_str = time.localtime(val) - return time.strftime(time_format, time_str) - - -def writeLog(stype, msg, args=()): - # 写日志 - uid = 1 - try: - from flask import session - if 'uid' in session: - uid = session['uid'] - except Exception as e: - print('writeL:',e) - # pass - # writeFileLog(getTracebackInfo()) - - print(stype, msg) - return writeDbLog(stype, msg, args, uid) - - -def writeFileLog(msg, path=None, limit_size=50 * 1024 * 1024, save_limit=3): - log_file = getServerDir() + '/mdserver-web/logs/debug.log' - if path != None: - log_file = path - - if os.path.exists(log_file): - size = os.path.getsize(log_file) - if size > limit_size: - log_file_rename = log_file + "_" + \ - time.strftime("%Y-%m-%d_%H%M%S") + '.log' - os.rename(log_file, log_file_rename) - logs = sorted(glob.glob(log_file + "_*")) - count = len(logs) - save_limit = count - save_limit - for i in range(count): - if i > save_limit: - break - os.remove(logs[i]) - # print('|---多余日志[' + logs[i] + ']已删除!') - - f = open(log_file, 'ab+') - msg += "\n" - if __name__ == '__main__': - print(msg) - f.write(msg.encode('utf-8')) - f.close() - return True - - -def writeDbLog(stype, msg, args=(), uid=1): - try: - import time - import db - import json - sql = db.Sql() - mdate = time.strftime('%Y-%m-%d %X', time.localtime()) - wmsg = getInfo(msg, args) - data = (stype, wmsg, uid, mdate) - result = sql.table('logs').add('type,log,uid,addtime', data) - return True - except Exception as e: - return False - - -def writeFile(filename, content, mode='w+'): - # 写文件内容 - try: - fp = open(filename, mode) - fp.write(content) - fp.close() - return True - except Exception as e: - return False - - -def backFile(file, act=None): - """ - @name 备份配置文件 - @param file 需要备份的文件 - @param act 如果存在,则备份一份作为默认配置 - """ - file_type = "_bak" - if act: - file_type = "_def" - - # print("cp -p {0} {1}".format(file, file + file_type)) - execShell("cp -p {0} {1}".format(file, file + file_type)) - - -def removeBackFile(file, act=None): - """ - @name 删除备份配置文件 - @param file 需要删除备份文件 - @param act 如果存在,则还原默认配置 - """ - file_type = "_bak" - if act: - file_type = "_def" - execShell("rm -rf {0}".format(file + file_type)) - - -def restoreFile(file, act=None): - """ - @name 还原配置文件 - @param file 需要还原的文件 - @param act 如果存在,则还原默认配置 - """ - file_type = "_bak" - if act: - file_type = "_def" - execShell("cp -p {1} {0}".format(file, file + file_type)) - -def base64StrEncode(content): - content = bytes(content,'utf-8') - content = base64.b64encode(content) - return content.decode('utf8') - -def base64StrDecode(content): - content = bytes(content,'utf-8') - content = base64.urlsafe_b64decode(content) - return content.decode('utf8') - -def enPunycode(domain): - if sys.version_info[0] == 2: - domain = domain.encode('utf8') - tmp = domain.split('.') - newdomain = '' - for dkey in tmp: - if dkey == '*': - continue - # 匹配非ascii字符 - match = re.search(u"[\x80-\xff]+", dkey) - if not match: - match = re.search(u"[\u4e00-\u9fa5]+", dkey) - if not match: - newdomain += dkey + '.' - else: - if sys.version_info[0] == 2: - newdomain += 'xn--' + dkey.decode('utf-8').encode('punycode') + '.' - else: - newdomain += 'xn--' + dkey.encode('punycode').decode('utf-8') + '.' - if tmp[0] == '*': - newdomain = "*." + newdomain - return newdomain[0:-1] - - -def dePunycode(domain): - # punycode 转中文 - tmp = domain.split('.') - newdomain = '' - for dkey in tmp: - if dkey.find('xn--') >= 0: - newdomain += dkey.replace('xn--','').encode('utf-8').decode('punycode') + '.' - else: - newdomain += dkey + '.' - return newdomain[0:-1] - - -def enCrypt(key, strings): - # 加密字符串 - try: - import base64 - _key = key.encode('utf-8') - _key = base64.urlsafe_b64encode(_key) - - if type(strings) != bytes: - strings = strings.encode('utf-8') - import cryptography - from cryptography.fernet import Fernet - f = Fernet(_key) - result = f.encrypt(strings) - return result.decode('utf-8') - except: - writeFileLog(getTracebackInfo()) - return strings - - -def deCrypt(key, strings): - - # 解密字符串 - try: - import base64 - _key = key.encode('utf-8') - _key = base64.urlsafe_b64encode(_key) - - if type(strings) != bytes: - strings = strings.encode('utf-8') - from cryptography.fernet import Fernet - f = Fernet(_key) - result = f.decrypt(strings).decode('utf-8') - return result - except: - writeFileLog(getTracebackInfo()) - return strings - - -def enDoubleCrypt(key, strings): - # 加密字符串 - try: - import base64 - _key = md5(key).encode('utf-8') - _key = base64.urlsafe_b64encode(_key) - - if type(strings) != bytes: - strings = strings.encode('utf-8') - import cryptography - from cryptography.fernet import Fernet - f = Fernet(_key) - result = f.encrypt(strings) - return result.decode('utf-8') - except: - writeFileLog(getTracebackInfo()) - return strings - - -def deDoubleCrypt(key, strings): - # 解密字符串 - try: - import base64 - _key = md5(key).encode('utf-8') - _key = base64.urlsafe_b64encode(_key) - - if type(strings) != bytes: - strings = strings.encode('utf-8') - from cryptography.fernet import Fernet - f = Fernet(_key) - result = f.decrypt(strings).decode('utf-8') - return result - except: - writeFileLog(getTracebackInfo()) - return strings - - -def aesEncrypt(data, key='ABCDEFGHIJKLMNOP', vi='0102030405060708'): - # aes加密 - # @param data 被加密的数据 - # @param key 加解密密匙 16位 - # @param vi 16位 - - from cryptography.hazmat.primitives import padding - from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - from cryptography.hazmat.backends import default_backend - - if not isinstance(data, bytes): - data = data.encode() - - # AES_CBC_KEY = os.urandom(32) - # AES_CBC_IV = os.urandom(16) - - AES_CBC_KEY = key.encode() - AES_CBC_IV = vi.encode() - - # print("AES_CBC_KEY:", AES_CBC_KEY) - # print("AES_CBC_IV:", AES_CBC_IV) - - padder = padding.PKCS7(algorithms.AES.block_size).padder() - padded_data = padder.update(data) + padder.finalize() - - cipher = Cipher(algorithms.AES(AES_CBC_KEY), - modes.CBC(AES_CBC_IV), - backend=default_backend()) - encryptor = cipher.encryptor() - - edata = encryptor.update(padded_data) - - # print(edata) - # print(str(edata)) - # print(edata.decode()) - return edata - - -def aesDecrypt(data, key='ABCDEFGHIJKLMNOP', vi='0102030405060708'): - # aes加密 - # @param data 被解密的数据 - # @param key 加解密密匙 16位 - # @param vi 16位 - - from cryptography.hazmat.primitives import padding - from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - from cryptography.hazmat.backends import default_backend - - from cryptography.hazmat.primitives import padding - from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - from cryptography.hazmat.backends import default_backend - - if not isinstance(data, bytes): - data = data.encode() - - AES_CBC_KEY = key.encode() - AES_CBC_IV = vi.encode() - - cipher = Cipher(algorithms.AES(AES_CBC_KEY), - modes.CBC(AES_CBC_IV), - backend=default_backend()) - decryptor = cipher.decryptor() - - ddata = decryptor.update(data) - - unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() - data = unpadder.update(ddata) - - try: - uppadded_data = data + unpadder.finalize() - except ValueError: - raise Exception('无效的加密信息!') - - return uppadded_data - - -def aesEncrypt_Crypto(data, key, vi): - # 该方法保留,暂时不使用 - # aes加密 - # @param data 被加密的数据 - # @param key 加解密密匙 16位 - # @param vi 16位 - - from Crypto.Cipher import AES - cryptor = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8')) - # 判断是否含有中文 - zhmodel = re.compile(u'[\u4e00-\u9fff]') - match = zhmodel.search(data) - if match == None: - # 无中文时 - add = 16 - len(data) % 16 - pad = lambda s: s + add * chr(add) - data = pad(data) - enctext = cryptor.encrypt(data.encode('utf8')) - else: - # 含有中文时 - data = data.encode() - add = 16 - len(data) % 16 - data = data + add * (chr(add)).encode() - enctext = cryptor.encrypt(data) - encodestrs = base64.b64encode(enctext).decode('utf8') - return encodestrs - - -def aesDecrypt_Crypto(data, key, vi): - # 该方法保留,暂时不使用 - # aes加密 - # @param data 被加密的数据 - # @param key 加解密密匙 16位 - # @param vi 16位 - - from crypto.Cipher import AES - data = data.encode('utf8') - encodebytes = base64.urlsafe_b64decode(data) - cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8')) - text_decrypted = cipher.decrypt(encodebytes) - # 判断是否含有中文 - zhmodel = re.compile(u'[\u4e00-\u9fff]') - match = zhmodel.search(text_decrypted) - if match == False: - # 无中文时补位 - unpad = lambda s: s[0:-s[-1]] - text_decrypted = unpad(text_decrypted) - text_decrypted = text_decrypted.decode('utf8').rstrip() # 去掉补位的右侧空格 - return text_decrypted - -def getDefault(data,val,def_val=''): - if val in data: - return data[val] - return def_val - -def encodeImage(imgsrc, newsrc): - # 图片加密 - import struct - old_fp = open(imgsrc, 'rb') - imgFile = old_fp.read() - old_fp.close() - - new_fp = open(newsrc,"wb") - for x in imgFile: - value = x ^ 86 - value = hex(value) - s = struct.pack('B',int(value,16)) - new_fp.write(s) - new_fp.close() - return True - -def buildSoftLink(src, dst, force=False): - ''' - 建立软连接 - ''' - if not os.path.exists(src): - return False - - if os.path.exists(dst) and force: - os.remove(dst) - - if not os.path.exists(dst): - execShell('ln -sf "' + src + '" "' + dst + '"') - return True - return False - - -def HttpGet(url, timeout=10): - """ - 发送GET请求 - @url 被请求的URL地址(必需) - @timeout 超时时间默认60秒 - return string - """ - if sys.version_info[0] == 2: - try: - import urllib2 - import ssl - if sys.version_info[0] == 2: - reload(urllib2) - reload(ssl) - try: - ssl._create_default_https_context = ssl._create_unverified_context - except: - pass - response = urllib2.urlopen(url, timeout=timeout) - return response.read() - except Exception as ex: - return str(ex) - else: - try: - import urllib.request - import ssl - try: - ssl._create_default_https_context = ssl._create_unverified_context - except: - pass - response = urllib.request.urlopen(url, timeout=timeout) - result = response.read() - if type(result) == bytes: - result = result.decode('utf-8') - return result - except Exception as ex: - return str(ex) - - -def HttpGet2(url, timeout): - import urllib.request - - try: - import ssl - try: - ssl._create_default_https_context = ssl._create_unverified_context - except: - pass - req = urllib.request.urlopen(url, timeout=timeout) - result = req.read().decode('utf-8') - return result - - except Exception as e: - return str(e) - - -def httpGet(url, timeout=10): - return HttpGet2(url, timeout) - - -def HttpPost(url, data, timeout=10): - """ - 发送POST请求 - @url 被请求的URL地址(必需) - @data POST参数,可以是字符串或字典(必需) - @timeout 超时时间默认60秒 - return string - """ - if sys.version_info[0] == 2: - try: - import urllib - import urllib2 - import ssl - ssl._create_default_https_context = ssl._create_unverified_context - data = urllib.urlencode(data) - req = urllib2.Request(url, data) - response = urllib2.urlopen(req, timeout=timeout) - return response.read() - except Exception as ex: - return str(ex) - else: - try: - import urllib.request - import ssl - try: - ssl._create_default_https_context = ssl._create_unverified_context - except: - pass - data = urllib.parse.urlencode(data).encode('utf-8') - req = urllib.request.Request(url, data) - response = urllib.request.urlopen(req, timeout=timeout) - result = response.read() - if type(result) == bytes: - result = result.decode('utf-8') - return result - except Exception as ex: - return str(ex) - - -def httpPost(url, data, timeout=10): - return HttpPost(url, data, timeout) - - -def writeSpeed(title, used, total, speed=0): - # 写进度 - if not title: - data = {'title': None, 'progress': 0, - 'total': 0, 'used': 0, 'speed': 0} - else: - progress = int((100.0 * used / total)) - data = {'title': title, 'progress': progress, - 'total': total, 'used': used, 'speed': speed} - writeFile('/tmp/panelSpeed.pl', json.dumps(data)) - return True - - -def getSpeed(): - # 取进度 - path = getRootDir() - data = readFile(path + '/tmp/panelSpeed.pl') - if not data: - data = json.dumps({'title': None, 'progress': 0, - 'total': 0, 'used': 0, 'speed': 0}) - writeFile(path + '/tmp/panelSpeed.pl', data) - return json.loads(data) - - -def getLastLineBk(inputfile, lineNum): - # 读文件指定倒数行数 - try: - fp = open(inputfile, 'rb') - lastLine = "" - lines = fp.readlines() - count = len(lines) - if count > lineNum: - num = lineNum - else: - num = count - i = 1 - lastre = [] - for i in range(1, (num + 1)): - n = -i - try: - lastLine = lines[n].decode("utf-8", "ignore").strip() - except Exception as e: - lastLine = "" - lastre.append(lastLine) - - fp.close() - result = '' - num -= 1 - while num >= 0: - result += lastre[num] + "\n" - num -= 1 - return result - except Exception as e: - return str(e) - # return getMsg('TASK_SLEEP') - - -def getLastLine(path, num, p=1): - pyVersion = sys.version_info[0] - try: - import html - if not os.path.exists(path): - return "" - start_line = (p - 1) * num - count = start_line + num - fp = open(path, 'rb') - buf = "" - - fp.seek(0, 2) - if fp.read(1) == "\n": - fp.seek(0, 2) - data = [] - b = True - n = 0 - - for i in range(count): - while True: - newline_pos = str.rfind(str(buf), "\n") - pos = fp.tell() - if newline_pos != -1: - if n >= start_line: - line = buf[newline_pos + 1:] - try: - data.insert(0, html.escape(line)) - except Exception as e: - pass - buf = buf[:newline_pos] - n += 1 - break - else: - if pos == 0: - b = False - break - to_read = min(4096, pos) - fp.seek(-to_read, 1) - t_buf = fp.read(to_read) - if pyVersion == 3: - if type(t_buf) == bytes: - t_buf = t_buf.decode("utf-8", "ignore").strip() - buf = t_buf + buf - fp.seek(-to_read, 1) - if pos - to_read == 0: - buf = "\n" + buf - if not b: - break - fp.close() - except Exception as e: - return str(e) - - return "\n".join(data) - - -def downloadFile(url, filename): - import urllib - urllib.urlretrieve(url, filename=filename, reporthook=downloadHook) - - -def downloadHook(count, blockSize, totalSize): - speed = {'total': totalSize, 'block': blockSize, 'count': count} - print('%02d%%' % (100.0 * count * blockSize / totalSize)) - - -def getLocalIpBack(): - # 取本地外网IP - try: - import re - filename = 'data/iplist.txt' - ipaddress = readFile(filename) - if not ipaddress or ipaddress == '127.0.0.1': - import urllib - url = 'http://pv.sohu.com/cityjson?ie=utf-8' - req = urllib.request.urlopen(url, timeout=10) - content = req.read().decode('utf-8') - ipaddress = re.search('\\d+.\\d+.\\d+.\\d+', content).group(0) - writeFile(filename, ipaddress) - - ipaddress = re.search('\\d+.\\d+.\\d+.\\d+', ipaddress).group(0) - return ipaddress - except Exception as ex: - # print(ex) - return '127.0.0.1' - - -def getClientIp(): - from flask import request - return request.remote_addr.replace('::ffff:', '') - - -def getLocalIp(): - filename = 'data/iplist.txt' - try: - ipaddress = readFile(filename) - if not ipaddress or ipaddress == '127.0.0.1': - cmd = "curl --insecure -4 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text" - ip = execShell(cmd) - result = ip[0].strip() - if result == '': - raise Exception("ipv4 is empty!") - writeFile(filename, result) - return result - return ipaddress - except Exception as e: - cmd = "curl --insecure -6 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text" - ip = execShell(cmd) - result = ip[0].strip() - if result == '': - return '127.0.0.1' - writeFile(filename, result) - return result - finally: - pass - return '127.0.0.1' - - -def inArray(arrays, searchStr): - # 搜索数据中是否存在 - for key in arrays: - if key == searchStr: - return True - - return False - - -def formatDate(format="%Y-%m-%d %H:%M:%S", times=None): - # 格式化指定时间戳 - if not times: - times = int(time.time()) - time_local = time.localtime(times) - return time.strftime(format, time_local) - - -def strfToTime(sdate): - # 转换时间 - import time - return time.strftime('%Y-%m-%d', time.strptime(sdate, '%b %d %H:%M:%S %Y %Z')) - - -def checkIp(ip): - # 检查是否为IPv4地址 - import re - p = re.compile( - '^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$') - if p.match(ip): - return True - else: - return False - - -def getHost(port=False): - from flask import request - host_tmp = request.headers.get('host') - if not host_tmp: - if request.url_root: - tmp = re.findall(r"(https|http)://([\w:\.-]+)", request.url_root) - if tmp: - host_tmp = tmp[0][1] - if not host_tmp: - host_tmp = getLocalIp() + ':' + readFile('data/port.pl').strip() - try: - if host_tmp.find(':') == -1: - host_tmp += ':80' - except: - host_tmp = "127.0.0.1:8888" - h = host_tmp.split(':') - if port: - return h[-1] - return ':'.join(h[0:-1]) - - -def getClientIp(): - from flask import request - return request.remote_addr.replace('::ffff:', '') - - -def checkDomainPanel(): - tmp = getHost() - domain = readFile('data/bind_domain.pl') - - port = 7200 - if os.path.exists('data/port.pl'): - port = readFile('data/port.pl').strip() - - scheme = 'http' - - choose_file = getRunDir()+'/ssl/choose.pl' - if os.path.exists(choose_file): - choose = readFile(choose_file).strip() - if not inArray(['local','nginx'], choose): - return False - else: - return False - - local_ssl = getRunDir()+'/ssl/local' - if choose == 'local': - scheme = 'https' - - if choose == 'nginx': - # print(port) - npid = getServerDir() + "/openresty/nginx/logs/nginx.pid" - if not os.path.exists(npid): - return False - - nconf = getServerDir() + "/web_conf/nginx/vhost/panel.conf" - if os.path.exists(nconf): - port = "80" - if not domain: - return False - - client_ip = getClientIp() - if client_ip in ['127.0.0.1', 'localhost', '::1']: - return False - - if tmp.strip().lower() != domain.strip().lower(): - from flask import Flask, redirect, request, url_for - to = scheme + "://" + domain + ":" + str(port) - # print(to) - return redirect(to, code=302) - - return False - - - -def createLinuxUser(user, group): - execShell("groupadd {}".format(group)) - execShell('useradd -s /sbin/nologin -g {} {}'.format(user, group)) - return True - - -def setOwn(filename, user, group=None): - if isAppleSystem(): - return True - - # 设置用户组 - if not os.path.exists(filename): - return False - from pwd import getpwnam - try: - user_info = getpwnam(user) - user = user_info.pw_uid - if group: - user_info = getpwnam(group) - group = user_info.pw_gid - except: - if user == 'www': - createLinuxUser(user) - # 如果指定用户或组不存在,则使用www - try: - user_info = getpwnam('www') - except: - createLinuxUser(user) - user_info = getpwnam('www') - user = user_info.pw_uid - group = user_info.pw_gid - os.chown(filename, user, group) - return True - - -def setMode(filename, mode): - # 设置文件权限 - if not os.path.exists(filename): - return False - mode = int(str(mode), 8) - os.chmod(filename, mode) - return True - - -def checkPort(port): - # 检查端口是否合法 - ports = ['21', '443', '888'] - if port in ports: - return False - intport = int(port) - if intport < 1 or intport > 65535: - return False - return True - - -def getStrBetween(startStr, endStr, srcStr): - # 字符串取中间 - start = srcStr.find(startStr) - if start == -1: - return None - end = srcStr.find(endStr) - if end == -1: - return None - return srcStr[start + 1:end] - - -def getCpuType(): - cpuType = '' - if isAppleSystem(): - cmd = "system_profiler SPHardwareDataType | grep 'Processor Name' | awk -F ':' '{print $2}'" - cpuinfo = execShell(cmd) - return cpuinfo[0].strip() - - current_os = getOs() - if current_os.startswith('freebsd'): - cmd = "sysctl -a | egrep -i 'hw.model' | awk -F ':' '{print $2}'" - cpuinfo = execShell(cmd) - return cpuinfo[0].strip() - - # 取CPU类型 - cpuinfo = open('/proc/cpuinfo', 'r').read() - rep = "model\\s+name\\s+:\\s+(.+)" - tmp = re.search(rep, cpuinfo, re.I) - if tmp: - cpuType = tmp.groups()[0] - else: - cpuinfo = execShell('LANG="en_US.UTF-8" && lscpu')[0] - rep = "Model\\s+name:\\s+(.+)" - tmp = re.search(rep, cpuinfo, re.I) - if tmp: - cpuType = tmp.groups()[0] - return cpuType - - -def isRestart(): - # 检查是否允许重启 - num = M('tasks').where('status!=?', ('1',)).count() - if num > 0: - return False - return True - - -def isUpdateLocalSoft(): - num = M('tasks').where('status!=?', ('1',)).count() - if os.path.exists('mdserver-web.zip'): - return True - - if num > 0: - data = M('tasks').where('status!=?', ('1',)).field( - 'id,type,execstr').limit('1').select() - argv = data[0]['execstr'].split('|dl|') - if data[0]['type'] == 'download' and argv[1] == 'mdserver-web.zip': - return True - - return False - - -def hasPwd(password): - # 加密密码字符 - import crypt - return crypt.crypt(password, password) - - -def getTimeout(url): - start = time.time() - result = httpGet(url) - if result != 'True': - return False - return int((time.time() - start) * 1000) - - -def makeConf(): - file = getRunDir() + '/data/json/config.json' - if not os.path.exists(file): - c = {} - c['title'] = '后羿面板' - c['home'] = 'http://github/midoks/mdserver-web' - c['recycle_bin'] = True - c['template'] = 'default' - writeFile(file, json.dumps(c)) - return c - c = readFile(file) - return json.loads(c) - - -def getConfig(k): - c = makeConf() - return c[k] - - -def setConfig(k, v): - c = makeConf() - c[k] = v - file = getRunDir() + '/data/json/config.json' - return writeFile(file, json.dumps(c)) - - -def getHostAddr(): - if os.path.exists('data/iplist.txt'): - return readFile('data/iplist.txt').strip() - return '127.0.0.1' - - -def setHostAddr(addr): - file = getRunDir() + '/data/iplist.txt' - return writeFile(file, addr) - - -def getHostPort(): - if os.path.exists('data/port.pl'): - return readFile('data/port.pl').strip() - return '7200' - - -def setHostPort(port): - file = getRunDir() + '/data/port.pl' - return writeFile(file, port) - - -def auth_decode(data): - # 解密数据 - token = GetToken() - # 是否有生成Token - if not token: - return returnMsg(False, 'REQUEST_ERR') - - # 校验access_key是否正确 - if token['access_key'] != data['btauth_key']: - return returnMsg(False, 'REQUEST_ERR') - - # 解码数据 - import binascii - import hashlib - import urllib - import hmac - import json - tdata = binascii.unhexlify(data['data']) - - # 校验signature是否正确 - signature = binascii.hexlify( - hmac.new(token['secret_key'], tdata, digestmod=hashlib.sha256).digest()) - if signature != data['signature']: - return returnMsg(False, 'REQUEST_ERR') - - # 返回 - return json.loads(urllib.unquote(tdata)) - - -# 数据加密 -def auth_encode(data): - token = GetToken() - pdata = {} - - # 是否有生成Token - if not token: - return returnMsg(False, 'REQUEST_ERR') - - # 生成signature - import binascii - import hashlib - import urllib - import hmac - import json - tdata = urllib.quote(json.dumps(data)) - # 公式 hex(hmac_sha256(data)) - pdata['signature'] = binascii.hexlify( - hmac.new(token['secret_key'], tdata, digestmod=hashlib.sha256).digest()) - - # 加密数据 - pdata['btauth_key'] = token['access_key'] - pdata['data'] = binascii.hexlify(tdata) - pdata['timestamp'] = time.time() - - # 返回 - return pdata - - -def checkToken(get): - # 检查Token - tempFile = 'data/tempToken.json' - if not os.path.exists(tempFile): - return False - import json - import time - tempToken = json.loads(readFile(tempFile)) - if time.time() > tempToken['timeout']: - return False - if get.token != tempToken['token']: - return False - return True - - -def checkInput(data): - # 过滤输入 - if not data: - return data - if type(data) != str: - return data - checkList = [ - {'d': '<', 'r': '<'}, - {'d': '>', 'r': '>'}, - {'d': '\'', 'r': '‘'}, - {'d': '"', 'r': '“'}, - {'d': '&', 'r': '&'}, - {'d': '#', 'r': '#'}, - {'d': '<', 'r': '<'} - ] - for v in checkList: - data = data.replace(v['d'], v['r']) - return data - - -def checkCert(certPath='ssl/certificate.pem'): - # 验证证书 - openssl = '/usr/bin/openssl' - if not os.path.exists(openssl): - openssl = '/usr/local/openssl/bin/openssl' - if not os.path.exists(openssl): - openssl = 'openssl' - certPem = readFile(certPath) - s = "\n-----BEGIN CERTIFICATE-----" - tmp = certPem.strip().split(s) - for tmp1 in tmp: - if tmp1.find('-----BEGIN CERTIFICATE-----') == -1: - tmp1 = s + tmp1 - writeFile(certPath, tmp1) - result = execShell(openssl + " x509 -in " + - certPath + " -noout -subject") - if result[1].find('-bash:') != -1: - return True - if len(result[1]) > 2: - return False - if result[0].find('error:') != -1: - return False - return True - - -def sortFileList(path, ftype = 'mtime', sort = 'desc'): - flist = os.listdir(path) - if ftype == 'mtime': - if sort == 'desc': - flist = sorted(flist, key=lambda f: os.path.getmtime(os.path.join(path,f)), reverse=True) - if sort == 'asc': - flist = sorted(flist, key=lambda f: os.path.getmtime(os.path.join(path,f)), reverse=False) - - if ftype == 'size': - if sort == 'desc': - flist = sorted(flist, key=lambda f: os.path.getsize(os.path.join(path,f)), reverse=True) - if sort == 'asc': - flist = sorted(flist, key=lambda f: os.path.getsize(os.path.join(path,f)), reverse=False) - - if ftype == 'fname': - if sort == 'desc': - flist = sorted(flist, key=lambda f: os.path.join(path,f), reverse=True) - if sort == 'asc': - flist = sorted(flist, key=lambda f: os.path.join(path,f), reverse=False) - return flist - - -def sortAllFileList(path, ftype = 'mtime', sort = 'desc', search = '',limit = 3000): - count = 0 - flist = [] - for d_list in os.walk(path): - if count >= limit: - break - - for d in d_list[1]: - if count >= limit: - break - if d.lower().find(search) != -1: - filename = d_list[0] + '/' + d - if not os.path.exists(filename): - continue - count += 1 - flist.append(filename) - - for f in d_list[2]: - if count >= limit: - break - - if f.lower().find(search) != -1: - filename = d_list[0] + '/' + f - if not os.path.exists(filename): - continue - count += 1 - flist.append(filename) - - if ftype == 'mtime': - if sort == 'desc': - flist = sorted(flist, key=lambda f: os.path.getmtime(f), reverse=True) - if sort == 'asc': - flist = sorted(flist, key=lambda f: os.path.getmtime(f), reverse=False) - - if ftype == 'size': - if sort == 'desc': - flist = sorted(flist, key=lambda f: os.path.getsize(f), reverse=True) - if sort == 'asc': - flist = sorted(flist, key=lambda f: os.path.getsize(f), reverse=False) - return flist - -def getPathSize(path): - # 取文件或目录大小 - if not os.path.exists(path): - return 0 - if not os.path.isdir(path): - return os.path.getsize(path) - size_total = 0 - for nf in os.walk(path): - for f in nf[2]: - filename = nf[0] + '/' + f - size_total += os.path.getsize(filename) - return size_total - - -def toSize(size): - # 字节单位转换 - d = ('b', 'KB', 'MB', 'GB', 'TB') - s = d[0] - for b in d: - if size < 1024: - return str(round(size, 2)) + ' ' + b - size = float(size) / 1024.0 - s = b - return str(round(size, 2)) + ' ' + b - - -def getPathSuffix(path): - return os.path.splitext(path)[-1] - - -def getMacAddress(): - # 获取mac - import uuid - mac = uuid.UUID(int=uuid.getnode()).hex[-12:] - return ":".join([mac[e:e + 2] for e in range(0, 11, 2)]) - - -def get_string(t): - if t != -1: - max = 126 - m_types = [{'m': 122, 'n': 97}, {'m': 90, 'n': 65}, {'m': 57, 'n': 48}, { - 'm': 47, 'n': 32}, {'m': 64, 'n': 58}, {'m': 96, 'n': 91}, {'m': 125, 'n': 123}] - else: - max = 256 - t = 0 - m_types = [{'m': 255, 'n': 0}] - arr = [] - for i in range(max): - if i < m_types[t]['n'] or i > m_types[t]['m']: - continue - arr.append(chr(i)) - return arr - - -def get_string_find(t): - if type(t) != list: - t = [t] - return_str = '' - for s1 in t: - return_str += get_string(int(s1[0]))[int(s1[1:])] - return return_str - - -def get_string_arr(t): - s_arr = {} - t_arr = [] - for s1 in t: - for i in range(6): - if not i in s_arr: - s_arr[i] = get_string(i) - for j in range(len(s_arr[i])): - if s1 == s_arr[i][j]: - t_arr.append(str(i) + str(j)) - return t_arr - - # 转换时间 - - -def strfDate(sdate): - return time.strftime('%Y-%m-%d', time.strptime(sdate, '%Y%m%d%H%M%S')) - - -# 获取证书名称 -def getCertName(certPath): - if not os.path.exists(certPath): - return None - try: - import OpenSSL - result = {} - x509 = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, readFile(certPath)) - # 取产品名称 - issuer = x509.get_issuer() - result['issuer'] = '' - if hasattr(issuer, 'CN'): - result['issuer'] = issuer.CN - if not result['issuer']: - is_key = [b'0', '0'] - issue_comp = issuer.get_components() - if len(issue_comp) == 1: - is_key = [b'CN', 'CN'] - for iss in issue_comp: - if iss[0] in is_key: - result['issuer'] = iss[1].decode() - break - if not result['issuer']: - if hasattr(issuer, 'O'): - result['issuer'] = issuer.O - # 取到期时间 - result['notAfter'] = strfDate( - bytes.decode(x509.get_notAfter())[:-1]) - # 取申请时间 - result['notBefore'] = strfDate( - bytes.decode(x509.get_notBefore())[:-1]) - # 取可选名称 - result['dns'] = [] - for i in range(x509.get_extension_count()): - s_name = x509.get_extension(i) - if s_name.get_short_name() in [b'subjectAltName', 'subjectAltName']: - s_dns = str(s_name).split(',') - for d in s_dns: - result['dns'].append(d.split(':')[1]) - subject = x509.get_subject().get_components() - # 取主要认证名称 - if len(subject) == 1: - result['subject'] = subject[0][1].decode() - else: - if not result['dns']: - for sub in subject: - if sub[0] == b'CN': - result['subject'] = sub[1].decode() - break - if 'subject' in result: - result['dns'].append(result['subject']) - else: - result['subject'] = result['dns'][0] - result['endtime'] = int(int(time.mktime(time.strptime( - result['notAfter'], "%Y-%m-%d")) - time.time()) / 86400) - return result - except Exception as e: - writeFileLog(getTracebackInfo()) - return None - - -def createLocalSSL(): - if not os.path.exists('ssl/local'): - execShell('mkdir -p ssl/local') - - - # 自签证书 - # if os.path.exists('ssl/local/input.pl'): - # return True - - client_ip = getClientIp() - - import OpenSSL - key = OpenSSL.crypto.PKey() - key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) - cert = OpenSSL.crypto.X509() - cert.set_serial_number(0) - - if client_ip == '127.0.0.1': - cert.get_subject().CN = '127.0.0.1' - else: - cert.get_subject().CN = getLocalIp() - - cert.set_issuer(cert.get_subject()) - cert.gmtime_adj_notBefore(0) - cert.gmtime_adj_notAfter(86400 * 3650) - cert.set_pubkey(key) - cert.sign(key, 'md5') - cert_ca = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) - private_key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) - if len(cert_ca) > 100 and len(private_key) > 100: - writeFile('ssl/local/cert.pem', cert_ca, 'wb+') - writeFile('ssl/local/private.pem', private_key, 'wb+') - return True - return False - - -def getSSHPort(): - try: - file = '/etc/ssh/sshd_config' - conf = readFile(file) - rep = "(#*)?Port\\s+([0-9]+)\\s*\n" - port = re.search(rep, conf).groups(0)[1] - return int(port) - except: - return 22 - - -def getSSHStatus(): - if os.path.exists('/usr/bin/apt-get'): - status = execShell("service ssh status | grep -P '(dead|stop)'") - else: - import system_api - version = system_api.system_api().getSystemVersion() - if version.find(' Mac ') != -1: - return True - if version.find(' 7.') != -1: - status = execShell("systemctl status sshd.service | grep 'dead'") - else: - status = execShell( - "/etc/init.d/sshd status | grep -e 'stopped' -e '已停'") - if len(status[0]) > 3: - status = False - else: - status = True - return status - - -def requestFcgiPHP(sock, uri, document_root='/tmp', method='GET', pdata=b''): - # 直接请求到PHP-FPM - # version php版本 - # uri 请求uri - # filename 要执行的php文件 - # args 请求参数 - # method 请求方式 - sys.path.append(os.getcwd() + "/class/plugin") - - import fpm - p = fpm.fpm(sock, document_root) - - if type(pdata) == dict: - pdata = url_encode(pdata) - result = p.load_url_public(uri, pdata, method) - return result - - -def getMyORM(): - ''' - 获取MySQL资源的ORM - ''' - sys.path.append(os.getcwd() + "/class/plugin") - import orm - o = orm.ORM() - return o - - -def getMyORMDb(): - ''' - 获取MySQL资源的ORM pip install mysqlclient==2.0.3 | pip install mysql-python - ''' - sys.path.append(os.getcwd() + "/class/plugin") - import ormDb - o = ormDb.ORM() - return o - -##################### notify start ######################################### - - -def initNotifyConfig(): - p = getNotifyPath() - if not os.path.exists(p): - writeFile(p, '{}') - return True - - -def getNotifyPath(): - path = 'data/notify.json' - return path - - -def getNotifyData(is_parse=False): - initNotifyConfig() - notify_file = getNotifyPath() - notify_data = readFile(notify_file) - - data = json.loads(notify_data) - - if is_parse: - tag_list = ['tgbot', 'email'] - for t in tag_list: - if t in data and 'cfg' in data[t]: - data[t]['data'] = json.loads(deDoubleCrypt(t, data[t]['cfg'])) - return data - - -def writeNotify(data): - p = getNotifyPath() - return writeFile(p, json.dumps(data)) - - -def tgbotNotifyChatID(): - data = getNotifyData(True) - if 'tgbot' in data and 'enable' in data['tgbot']: - if data['tgbot']['enable']: - t = data['tgbot']['data'] - return t['chat_id'] - return '' - - -def tgbotNotifyObject(): - data = getNotifyData(True) - if 'tgbot' in data and 'enable' in data['tgbot']: - if data['tgbot']['enable']: - t = data['tgbot']['data'] - import telebot - bot = telebot.TeleBot(app_token) - return True, bot - return False, None - - -def tgbotNotifyMessage(app_token, chat_id, msg): - import telebot - bot = telebot.TeleBot(app_token) - try: - data = bot.send_message(chat_id, msg) - return True - except Exception as e: - writeFileLog(str(e)) - return False - - -def tgbotNotifyHttpPost(app_token, chat_id, msg): - try: - url = 'https://api.telegram.org/bot' + app_token + '/sendMessage' - post_data = { - 'chat_id': chat_id, - 'text': msg, - } - rdata = httpPost(url, post_data) - return True - except Exception as e: - writeFileLog(str(e)) - return False - - -def tgbotNotifyTest(app_token, chat_id): - msg = 'MW-通知验证测试OK' - return tgbotNotifyHttpPost(app_token, chat_id, msg) - - -def emailNotifyMessage(data): - ''' - 邮件通知 - ''' - sys.path.append(os.getcwd() + "/class/plugin") - import memail - try: - if data['smtp_ssl'] == 'ssl': - memail.sendSSL(data['smtp_host'], data['smtp_port'], - data['username'], data['password'], - data['to_mail_addr'], data['subject'], data['content']) - else: - memail.send(data['smtp_host'], data['smtp_port'], - data['username'], data['password'], - data['to_mail_addr'], data['subject'], data['content']) - return True - except Exception as e: - print(getTracebackInfo()) - return False - - -def emailNotifyTest(data): - # print(data) - data['subject'] = 'MW通知测试' - data['content'] = data['mail_test'] - return emailNotifyMessage(data) - - -def notifyMessageTry(msg, stype='common', trigger_time=300, is_write_log=True): - - lock_file = getPanelTmp() + '/notify_lock.json' - if not os.path.exists(lock_file): - writeFile(lock_file, '{}') - - lock_data = json.loads(readFile(lock_file)) - if stype in lock_data: - diff_time = time.time() - lock_data[stype]['do_time'] - if diff_time >= trigger_time: - lock_data[stype]['do_time'] = time.time() - else: - return False - else: - lock_data[stype] = {'do_time': time.time()} - - writeFile(lock_file, json.dumps(lock_data)) - - if is_write_log: - writeLog("通知管理[" + stype + "]", msg) - - data = getNotifyData(True) - # tag_list = ['tgbot', 'email'] - # tagbot - do_notify = False - if 'tgbot' in data and 'enable' in data['tgbot']: - if data['tgbot']['enable']: - t = data['tgbot']['data'] - i = sys.version_info - - # telebot 在python小于3.7无法使用 - if i[0] < 3 or i[1] < 7: - do_notify = tgbotNotifyHttpPost( - t['app_token'], t['chat_id'], msg) - else: - do_notify = tgbotNotifyMessage( - t['app_token'], t['chat_id'], msg) - - if 'email' in data and 'enable' in data['email']: - if data['email']['enable']: - t = data['email']['data'] - t['subject'] = 'MW通知' - t['content'] = msg - do_notify = emailNotifyMessage(t) - return do_notify - - -def notifyMessage(msg, stype='common', trigger_time=300, is_write_log=True): - try: - return notifyMessageTry(msg, stype, trigger_time, is_write_log) - except Exception as e: - writeFileLog(getTracebackInfo()) - return False - - -##################### notify end ######################################### - - -def getGlibcVersion(): - try: - cmd_result = execShell("ldd --version")[0] - if not cmd_result: return '' - glibc_version = cmd_result.split("\n")[0].split()[-1] - except: - return '' - return glibc_version - -##################### ssh start ######################################### -def getSshDir(): - if isAppleSystem(): - user = execShell("who | sed -n '2, 1p' |awk '{print $1}'")[0].strip() - return '/Users/' + user + '/.ssh' - return '/root/.ssh' - - -def processExists(pname, exe=None, cmdline=None): - # 进程是否存在 - try: - import psutil - pids = psutil.pids() - for pid in pids: - try: - p = psutil.Process(pid) - if p.name() == pname: - if not exe and not cmdline: - return True - else: - if exe: - if p.exe() == exe: - return True - if cmdline: - if cmdline in p.cmdline(): - return True - except: - pass - return False - except: - return True - - -def createRsa(): - # ssh-keygen -t rsa -P "" -C "midoks@163.com" - ssh_dir = getSshDir() - # mw.execShell("rm -f /root/.ssh/*") - if not os.path.exists(ssh_dir + '/authorized_keys'): - execShell('touch ' + ssh_dir + '/authorized_keys') - - if not os.path.exists(ssh_dir + '/id_rsa.pub') and os.path.exists(ssh_dir + '/id_rsa'): - execShell('echo y | ssh-keygen -q -t rsa -P "" -f ' + - ssh_dir + '/id_rsa') - else: - execShell('ssh-keygen -q -t rsa -P "" -f ' + ssh_dir + '/id_rsa') - - execShell('cat ' + ssh_dir + '/id_rsa.pub >> ' + - ssh_dir + '/authorized_keys') - execShell('chmod 600 ' + ssh_dir + '/authorized_keys') - - -def createSshInfo(): - ssh_dir = getSshDir() - if not os.path.exists(ssh_dir + '/id_rsa') or not os.path.exists(ssh_dir + '/id_rsa.pub'): - createRsa() - - # 检查是否写入authorized_keys - data = execShell("cat " + ssh_dir + "/id_rsa.pub | awk '{print $3}'") - if data[0] != "": - cmd = "cat " + ssh_dir + "/authorized_keys | grep " + data[0] - ak_data = execShell(cmd) - if ak_data[0] == "": - cmd = 'cat ' + ssh_dir + '/id_rsa.pub >> ' + ssh_dir + '/authorized_keys' - execShell(cmd) - execShell('chmod 600 ' + ssh_dir + '/authorized_keys') - - -def connectSsh(): - import paramiko - ssh = paramiko.SSHClient() - createSshInfo() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - port = getSSHPort() - try: - ssh.connect('127.0.0.1', port, timeout=5) - except Exception as e: - ssh.connect('localhost', port, timeout=5) - except Exception as e: - ssh.connect(getHostAddr(), port, timeout=30) - except Exception as e: - return False - - shell = ssh.invoke_shell(term='xterm', width=83, height=21) - shell.setblocking(0) - return shell - - -def clearSsh(): - # 服务器IP - ip = getHostAddr() - sh = ''' -#!/bin/bash -PLIST=`who | grep localhost | awk '{print $2}'` -for i in $PLIST -do - ps -t /dev/$i |grep -v TTY | awk '{print $1}' | xargs kill -9 -done - -# getHostAddr -PLIST=`who | grep "${ip}" | awk '{print $2}'` -for i in $PLIST -do - ps -t /dev/$i |grep -v TTY | awk '{print $1}' | xargs kill -9 -done -''' - if not isAppleSystem(): - info = execShell(sh) - print(info[0], info[1]) -##################### ssh end ######################################### - -# --------------------------------------------------------------------------------- -# 打印相关 START -# --------------------------------------------------------------------------------- - - -def echoStart(tag): - print("=" * 89) - print("★开始{}[{}]".format(tag, formatDate())) - print("=" * 89) - - -def echoEnd(tag): - print("=" * 89) - print("☆{}完成[{}]".format(tag, formatDate())) - print("=" * 89) - print("\n") - - -def echoInfo(msg): - print("|-{}".format(msg)) - -# --------------------------------------------------------------------------------- -# 打印相关 END -# --------------------------------------------------------------------------------- diff --git a/class/core/page.py b/class/core/page.py deleted file mode 100755 index e2ccdb8a1..000000000 --- a/class/core/page.py +++ /dev/null @@ -1,245 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 分页库操作 -# --------------------------------------------------------------------------------- - - -import math -import string - - -class Page(): - #-------------------------- - # 分页类 - JS回调版 - #-------------------------- - __PREV = '上一页' - __NEXT = '下一页' - __START = '首页' - __END = '尾页' - __COUNT_START = '共' - __COUNT_END = '条数据' - __FO = '从' - __LINE = '条' - __LIST_NUM = 4 - SHIFT = None # 偏移量 - ROW = None # 每页行数 - __C_PAGE = None # 当前页 - __COUNT_PAGE = None # 总页数 - __COUNT_ROW = None # 总行数 - __URI = None # URI - __RTURN_JS = False # 是否返回JS回调 - __START_NUM = None # 起始行 - __END_NUM = None # 结束行 - __ARGS_TPL = '' - - def __init__(self): - if False: - self.__PREV = tmp['PREV'] - self.__NEXT = tmp['NEXT'] - self.__START = tmp['START'] - self.__END = tmp['END'] - self.__COUNT_START = tmp['COUNT_START'] - self.__COUNT_END = tmp['COUNT_END'] - self.__FO = tmp['FO'] - self.__LINE = tmp['LINE'] - - def getPageNum(self, num): - return str(num) + self.__ARGS_TPL - - def GetPage(self, pageInfo, limit='1,2,3,4,5,6,7,8'): - # 取分页信息 - # @param pageInfo 传入分页参数字典 - # @param limit 返回系列 - self.__RTURN_JS = pageInfo['return_js'] - self.__COUNT_ROW = pageInfo['count'] - - if 'args_tpl' in pageInfo: - self.__ARGS_TPL = pageInfo['args_tpl'] - - self.ROW = pageInfo['row'] - self.__C_PAGE = self.__GetCpage(pageInfo['p']) - self.__START_NUM = self.__StartRow() - self.__END_NUM = self.__EndRow() - self.__COUNT_PAGE = self.__GetCountPage() - self.__URI = self.__SetUri(pageInfo['uri']) - self.SHIFT = self.__START_NUM - 1 - - keys = limit.split(',') - pages = {} - # 起始页 - pages['1'] = self.__GetStart() - # 上一页 - pages['2'] = self.__GetPrev() - # 分页 - pages['3'] = self.__GetPages() - # 下一页 - pages['4'] = self.__GetNext() - # 尾页 - pages['5'] = self.__GetEnd() - - # 当前显示页与总页数 - pages['6'] = "" + \ - str(self.__C_PAGE) + "/" + \ - str(self.__COUNT_PAGE) + "" - - # 本页显示开始与结束行 - pages['7'] = "" + str(self.__FO) + \ - str(self.__START_NUM) + "-" + \ - str(self.__END_NUM) + str(self.__LINE) + "" - # 行数 - pages['8'] = "" + str(self.__COUNT_START) + \ - str(self.__COUNT_ROW) + str(self.__COUNT_END) + "" - - # 构造返回数据 - retuls = '
' - for value in keys: - retuls += pages[value] - retuls += '
' - - # 返回分页数据 - return retuls - - def __GetEnd(self): - # 构造尾页 - endStr = "" - if self.__C_PAGE >= self.__COUNT_PAGE: - endStr = '' - else: - if self.__RTURN_JS == "": - endStr = "" + str(self.__END) + "" - else: - endStr = "" + \ - str(self.__END) + "" - return endStr - - def __GetNext(self): - # 构造下一页 - nextStr = "" - if self.__C_PAGE >= self.__COUNT_PAGE: - nextStr = '' - else: - if self.__RTURN_JS == "": - nextStr = "" + str(self.__NEXT) + "" - else: - nextStr = "" + \ - str(self.__NEXT) + "" - - return nextStr - - def __GetPages(self): - # 构造分页 - pages = '' - num = 0 - # 当前页之前 - if (self.__COUNT_PAGE - self.__C_PAGE) < self.__LIST_NUM: - num = self.__LIST_NUM + \ - (self.__LIST_NUM - (self.__COUNT_PAGE - self.__C_PAGE)) - else: - num = self.__LIST_NUM - n = 0 - for i in range(num): - n = num - i - page = self.__C_PAGE - n - if page > 0: - if self.__RTURN_JS == "": - pages += "" + str(page) + "" - else: - pages += "" + str(page) + "" - - # 当前页 - if self.__C_PAGE > 0: - pages += "" + \ - str(self.__C_PAGE) + "" - - # 当前页之后 - if self.__C_PAGE <= self.__LIST_NUM: - num = self.__LIST_NUM + (self.__LIST_NUM - self.__C_PAGE) + 1 - else: - num = self.__LIST_NUM - for i in range(num): - if i == 0: - continue - page = self.__C_PAGE + i - if page > self.__COUNT_PAGE: - break - if self.__RTURN_JS == "": - pages += "" + str(page) + "" - else: - pages += "" + str(page) + "" - - return pages - - def __GetPrev(self): - # 构造上一页 - startStr = '' - if self.__C_PAGE == 1: - startStr = '' - else: - if self.__RTURN_JS == "": - startStr = "" + str(self.__PREV) + "" - else: - startStr = "" + \ - str(self.__PREV) + "" - return startStr - - def __GetStart(self): - # 构造起始分页 - startStr = '' - if self.__C_PAGE == 1: - startStr = '' - else: - if self.__RTURN_JS == "": - startStr = "" + str(self.__START) + "" - else: - startStr = "" + str(self.__START) + "" - return startStr - - def __GetCpage(self, p): - # 取当前页 - if p: - return p - return 1 - - def __StartRow(self): - # 从多少行开始 - return (self.__C_PAGE - 1) * self.ROW + 1 - - def __EndRow(self): - # 从多少行结束 - if self.ROW > self.__COUNT_ROW: - return self.__COUNT_ROW - return self.__C_PAGE * self.ROW - - def __GetCountPage(self): - # 取总页数 - return int(math.ceil(self.__COUNT_ROW / float(self.ROW))) - - def __SetUri(self, sinput): - # 构造URI - uri = '?' - for key in sinput: - if key == 'p': - continue - uri += key + '=' + sinput[key] + '&' - return str(uri) diff --git a/class/core/plugins_api.py b/class/core/plugins_api.py deleted file mode 100755 index a08779624..000000000 --- a/class/core/plugins_api.py +++ /dev/null @@ -1,1105 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 插件操作 -# --------------------------------------------------------------------------------- - - -import psutil -import time -import os -import mw -import re -import json - -import sys -# reload(sys) -# sys.setdefaultencoding('utf8') - -import threading -import multiprocessing - -from flask import render_template -from flask import request - - -class pa_thread(threading.Thread): - - def __init__(self, func, args, name=''): - threading.Thread.__init__(self) - self.name = name - self.func = func - self.args = args - self.result = self.func(*self.args) - - def getResult(self): - try: - return self.result - except Exception: - return None - - -class plugins_api: - __tasks = None - __plugin_dir = 'plugins' - __type = 'data/json/type.json' - __index = 'data/json/index.json' - __index_data = None - setupPath = None - - def __init__(self): - self.setupPath = 'server' - self.__plugin_dir = mw.getRunDir() + '/plugins' - self.__type = mw.getRunDir() + '/data/json/type.json' - self.__index = mw.getRunDir() + '/data/json/index.json' - - self.initIndexData() - - def initIndexData(self): - if not os.path.exists(self.__index): - mw.writeFile(self.__index, '[]') - - self.__index_data = json.loads(mw.readFile(self.__index)) - - ##### ----- start ----- ### - def listApi(self): - sType = request.args.get('type', '0') - sPage = request.args.get('p', '1') - - if not mw.isNumber(sPage): - sPage = 1 - - if not mw.isNumber(sType): - sType = 0 - - # print(sPage, sType) - search = request.args.get('search', '').lower() - data = self.getPluginList(sType, search, int(sPage)) - return mw.getJson(data) - - def menuGetAbsPath(self, tag, path): - if path[0:1] == '/': - return path - else: - return mw.getPluginDir() + '/' + tag + '/' + path - - def menuApi(self): - import config_api - data = config_api.config_api().get() - tag = request.args.get('tag', '') - menu_file = 'data/hook_menu.json' - content = '' - if os.path.exists(menu_file): - t = mw.readFile(menu_file) - tlist = json.loads(t) - for menu_data in tlist: - if tag == menu_data['name'] and 'path' in menu_data: - tpath = self.menuGetAbsPath(tag, menu_data['path']) - content = mw.readFile(tpath) - data['plugin_content'] = content - return render_template('plugin_menu.html', data=data) - - def fileApi(self): - name = request.args.get('name', '') - if name.strip() == '': - return '' - - f = request.args.get('f', '') - - if f.strip() == '': - return '' - - file = mw.getPluginDir() + '/' + name + '/' + f - if not os.path.exists(file): - return '' - - suffix = mw.getPathSuffix(file) - if suffix == '.css': - content = mw.readFile(file) - from flask import Response - from flask import make_response - v = Response(content, headers={ - 'Content-Type': 'text/css; charset="utf-8"'}) - return make_response(v) - content = open(file, 'rb').read() - return content - - def indexListApi(self): - data = self.getIndexList() - return mw.getJson(data) - - def indexSortApi(self): - sort = request.form.get('ssort', '') - if sort.strip() == '': - return mw.returnJson(False, '排序数据不能为空!') - data = self.setIndexSort(sort) - if data: - return mw.returnJson(True, '成功!') - return mw.returnJson(False, '失败!') - - def initApi(self): - - plugin_names = { - 'openresty': '1.25.3', - 'php': '56', - 'swap': '1.1', - 'mysql': '5.7', - 'phpmyadmin': '4.4.15', - } - - pn_dir = mw.getPluginDir() - pn_server_dir = mw.getServerDir() - pn_list = [] - for pn in plugin_names: - info = {} - pn_json = pn_dir + '/' + pn + '/info.json' - pn_server = pn_server_dir + '/' + pn - if not os.path.exists(pn_server): - - tmp = mw.readFile(pn_json) - tmp = json.loads(tmp) - - info['title'] = tmp['title'] - info['name'] = tmp['name'] - info['versions'] = tmp['versions'] - info['default_ver'] = plugin_names[pn] - pn_list.append(info) - else: - return mw.returnJson(False, 'ok') - - return mw.returnJson(True, 'ok', pn_list) - - def initInstallApi(self): - pn_list = request.form.get('list', '') - try: - pn_list = json.loads(pn_list) - - for pn in pn_list: - name = pn['name'] - version = pn['version'] - infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json' - pluginInfo = json.loads(mw.readFile(infoJsonPos)) - self.hookInstall(pluginInfo) - execstr = 'cd ' + mw.getPluginDir() + '/' + name + ' && bash ' + \ - pluginInfo['shell'] + ' install ' + version - - taskAdd = ('安装[' + name + '-' + version + ']', - 'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr) - - mw.M('tasks').add('name,type,status,addtime, execstr', taskAdd) - os.mkdir(mw.getServerDir() + '/php') - # 任务执行相关 - mw.triggerTask() - return mw.returnJson(True, '添加成功') - except Exception as e: - return mw.returnJson(False, mw.getTracebackInfo()) - - def installApi(self): - rundir = mw.getRunDir() - name = request.form.get('name', '') - version = request.form.get('version', '') - - mmsg = '安装' - if hasattr(request.form, 'upgrade'): - mtype = 'update' - mmsg = 'upgrade' - - if name.strip() == '': - return mw.returnJson(False, '缺少插件名称!', ()) - - if version.strip() == '': - return mw.returnJson(False, '缺少版本信息!', ()) - - infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json' - # print infoJsonPos - - if not os.path.exists(infoJsonPos): - return mw.returnJson(False, '配置文件不存在!', ()) - - pluginInfo = json.loads(mw.readFile(infoJsonPos)) - self.hookInstall(pluginInfo) - - execstr = 'cd ' + mw.getPluginDir() + '/' + name + ' && bash ' + \ - pluginInfo['shell'] + ' install ' + version - - if mw.isAppleSystem(): - print(execstr) - - taskAdd = (mmsg + '[' + name + '-' + version + ']', - 'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr) - - mw.M('tasks').add('name,type,status,addtime, execstr', taskAdd) - - # 任务执行相关 - mw.triggerTask() - return mw.returnJson(True, '已将安装任务添加到队列!') - - def hookInstallFile(self, hook_name, info): - hookPath = mw.getPanelDataDir() + "/hook_" + hook_name + ".json" - data = [] - if os.path.exists(hookPath): - t = mw.readFile(hookPath) - data = json.loads(t) - - isNeedAdd = True - for x in range(len(data)): - if data[x]['title'] == info['title'] and data[x]['name'] == info['name']: - isNeedAdd = False - - if isNeedAdd: - data.append(info) - mw.writeFile(hookPath, json.dumps(data)) - - def hookUninstallFile(self, hook_name, info): - hookPath = mw.getPanelDataDir() + "/hook_" + hook_name + ".json" - data = [] - if os.path.exists(hookPath): - t = mw.readFile(hookPath) - data = json.loads(t) - - for idx in range(len(data)): - if data[idx]['name'] == info['name']: - data.remove(data[idx]) - break - mw.writeFile(hookPath, json.dumps(data)) - - def hookInstall(self, info): - valid_hook = ['backup', 'database'] - valid_list_hook = ['menu', 'global_static', 'site_cb'] - if 'hook' in info: - hooks = info['hook'] - for h in hooks: - hooks_type = type(h) - if hooks_type == dict: - tag = h['tag'] - if tag in valid_list_hook: - self.hookInstallFile(tag, h[tag]) - elif hooks_type == str: - for x in hooks: - if x in valid_hook: - self.hookInstallFile(x, info) - return True - return False - - def hookUninstall(self, info): - valid_hook = ['backup', 'database'] - valid_list_hook = ['menu', 'global_static', 'site_cb'] - if 'hook' in info: - hooks = info['hook'] - for h in hooks: - hooks_type = type(h) - if hooks_type == dict: - tag = h['tag'] - if tag in valid_list_hook: - self.hookUninstallFile(tag, h[tag]) - elif hooks_type == str: - for x in hooks: - if x in valid_hook: - self.hookUninstallFile(x, info) - return True - return False - - def uninstallOldApi(self): - rundir = mw.getRunDir() - name = request.form.get('name', '') - version = request.form.get('version', '') - if name.strip() == '': - return mw.returnJson(False, "缺少插件名称!", ()) - - if version.strip() == '': - return mw.returnJson(False, "缺少版本信息!", ()) - - infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json' - - if not os.path.exists(infoJsonPos): - return mw.returnJson(False, "配置文件不存在!", ()) - - pluginInfo = json.loads(mw.readFile(infoJsonPos)) - execstr = "cd " + os.getcwd() + "/plugins/" + \ - name + " && /bin/bash " + pluginInfo["shell"] \ - + " uninstall " + version - - taskAdd = (None, '卸载[' + name + '-' + version + ']', - 'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr) - - mw.M('tasks').add('id,name,type,status,addtime, execstr', taskAdd) - return mw.returnJson(True, '已将卸载任务添加到队列!') - - # 卸载时间短,不加入任务中... - def uninstallApi(self): - rundir = mw.getRunDir() - name = request.form.get('name', '') - version = request.form.get('version', '') - if name.strip() == '': - return mw.returnJson(False, "缺少插件名称!", ()) - - if version.strip() == '': - return mw.returnJson(False, "缺少版本信息!", ()) - - infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json' - - if not os.path.exists(infoJsonPos): - return mw.returnJson(False, "配置文件不存在!", ()) - - pluginInfo = json.loads(mw.readFile(infoJsonPos)) - self.hookUninstall(pluginInfo) - execstr = "cd " + os.getcwd() + "/plugins/" + \ - name + " && /bin/bash " + pluginInfo["shell"] \ - + " uninstall " + version - - data = mw.execShell(execstr) - if mw.isDebugMode(): - print(execstr) - print(data[0], data[1]) - self.removeIndex(name, version) - return mw.returnJson(True, '卸载执行成功!') - # if data[1] == '': - # return mw.returnJson(True, '已将卸载成功!') - # else: - # return mw.returnJson(False, '卸载出现错误信息!' + data[1]) - - def checkApi(self): - name = request.form.get('name', '') - if name.strip() == '': - return mw.returnJson(False, "缺少插件名称!", ()) - - infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json' - if not os.path.exists(infoJsonPos): - return mw.returnJson(False, "配置文件不存在!", ()) - return mw.returnJson(True, "插件存在!", ()) - - def setIndexApi(self): - name = request.form.get('name', '') - status = request.form.get('status', '0') - version = request.form.get('version', '') - if status == '1': - return self.addIndex(name, version) - return self.removeIndex(name, version) - - def settingApi(self): - name = request.args.get('name', '') - html = self.__plugin_dir + '/' + name + '/index.html' - return mw.readFile(html) - - def runApi(self): - name = request.form.get('name', '') - func = request.form.get('func', '') - version = request.form.get('version', '') - args = request.form.get('args', '') - script = request.form.get('script', 'index') - - data = self.run(name, func, version, args, script) - if data[1] == '': - r = mw.returnJson(True, "OK", data[0].strip()) - else: - r = mw.returnJson(False, data[1].strip()) - return r - - def callbackApi(self): - name = request.form.get('name', '') - func = request.form.get('func', '') - args = request.form.get('args', '') - script = request.form.get('script', 'index') - - data = self.callback(name, func, args, script) - if data[0]: - return mw.returnJson(True, "OK", data[1]) - return mw.returnJson(False, data[1]) - - def updateZipApi(self): - tmp_path = mw.getRootDir() + '/temp' - if not os.path.exists(tmp_path): - os.makedirs(tmp_path) - mw.execShell("rm -rf " + tmp_path + '/*') - - tmp_file = tmp_path + '/plugin_tmp.zip' - from werkzeug.utils import secure_filename - from flask import request - f = request.files['plugin_zip'] - if f.filename[-4:] != '.zip': - return mw.returnJson(False, '仅支持zip文件!') - f.save(tmp_file) - mw.execShell('cd ' + tmp_path + ' && unzip ' + tmp_file) - os.remove(tmp_file) - - p_info = tmp_path + '/info.json' - if not os.path.exists(p_info): - d_path = None - for df in os.walk(tmp_path): - if len(df[2]) < 3: - continue - if not 'info.json' in df[2]: - continue - if not 'install.sh' in df[2]: - continue - if not os.path.exists(df[0] + '/info.json'): - continue - d_path = df[0] - if d_path: - tmp_path = d_path - p_info = tmp_path + '/info.json' - try: - data = json.loads(mw.readFile(p_info)) - data['size'] = mw.getPathSize(tmp_path) - if not 'author' in data: - data['author'] = '未知' - if not 'home' in data: - data['home'] = 'https://github.com/midoks/mdserver-web' - plugin_path = mw.getPluginDir() + data['name'] + '/info.json' - data['old_version'] = '0' - data['tmp_path'] = tmp_path - if os.path.exists(plugin_path): - try: - old_info = json.loads(mw.ReadFile(plugin_path)) - data['old_version'] = old_info['versions'] - except: - pass - except: - mw.execShell("rm -rf " + tmp_path) - return mw.returnJson(False, '在压缩包中没有找到插件信息,请检查插件包!') - protectPlist = ('openresty', 'mysql', 'php', 'redis', 'memcached' - 'mongodb', 'swap', 'gogs', 'pureftp') - if data['name'] in protectPlist: - return mw.returnJson(False, '[' + data['name'] + '],重要插件不可修改!') - return mw.getJson(data) - - def inputZipApi(self): - plugin_name = request.form.get('plugin_name', '') - tmp_path = request.form.get('tmp_path', '') - - if not os.path.exists(tmp_path): - return mw.returnJson(False, '临时文件不存在,请重新上传!') - plugin_path = mw.getPluginDir() + '/' + plugin_name - if not os.path.exists(plugin_path): - print(mw.execShell('mkdir -p ' + plugin_path)) - mw.execShell("cp -rf " + tmp_path + '/* ' + plugin_path + '/') - mw.execShell('chmod -R 755 ' + plugin_path) - p_info = mw.readFile(plugin_path + '/info.json') - if p_info: - mw.writeLog('软件管理', '安装第三方插件[%s]' % - json.loads(p_info)['title']) - return mw.returnJson(True, '安装成功!') - mw.execShell("rm -rf " + plugin_path) - return mw.returnJson(False, '安装失败!') - ##### ----- end ----- ### - - # 进程是否存在 - def processExists(self, pname, exe=None): - try: - if not self.pids: - self.pids = psutil.pids() - for pid in self.pids: - try: - p = psutil.Process(pid) - if p.name() == pname: - if not exe: - return True - else: - if p.exe() == exe: - return True - except: - pass - return False - except: - return True - - # 检查是否正在安装 - def checkSetupTask(self, sName, sVer, sCoexist): - if not self.__tasks: - self.__tasks = mw.M('tasks').where( - "status!=?", ('1',)).field('status,name').select() - isTask = '1' - for task in self.__tasks: - tmpt = mw.getStrBetween('[', ']', task['name']) - if not tmpt: - continue - task_sign = tmpt.split('-') - task_len = len(task_sign) - - task_name = task_sign[0].lower() - task_ver = task_sign[1] - if task_len > 2: - nameArr = task_sign[0:task_len - 1] - task_name = '-'.join(nameArr).lower() - task_ver = task_sign[task_len - 1] - if sCoexist: - if task_name == sName and task_ver == sVer: - isTask = task['status'] - else: - if task_name == sName: - isTask = task['status'] - return isTask - - def checkStatus(self, info): - if not info['setup']: - return False - - data = self.run(info['name'], 'status', info['setup_version']) - if data[0] == 'start': - return True - return False - - def checkStatusProcess(self, info, i, return_dict): - if not info['setup']: - return_dict[i] = False - return - - data = self.run(info['name'], 'status', info['setup_version']) - if data[0] == 'start': - return_dict[i] = True - else: - return_dict[i] = False - - def checkStatusThreads(self, info, i): - if not info['setup']: - return False - data = self.run(info['name'], 'status', info['setup_version']) - if data[0] == 'start': - return True - else: - return False - - # 还无法正常运行,先保留 - def checkStatusThreadsCallback(self, info, i): - if not info['setup']: - return False - - try: - data = self.callback(info['name'], 'status', info['setup_version']) - except Exception as e: - data = self.callback(info['name'], 'status') - - # data = self.run(info['name'], 'status', info['setup_version']) - if data[0] == 'start': - return True - else: - return False - - def checkStatusMThreads(self, plugins_info): - try: - threads = [] - ntmp_list = range(len(plugins_info)) - for i in ntmp_list: - t = pa_thread(self.checkStatusThreads, - (plugins_info[i], i)) - threads.append(t) - - for i in ntmp_list: - threads[i].start() - for i in ntmp_list: - threads[i].join() - - for i in ntmp_list: - t = threads[i].getResult() - plugins_info[i]['status'] = t - except Exception as e: - print('checkStatusMThreads:', str(e)) - - return plugins_info - - def checkStatusMProcess(self, plugins_info): - manager = multiprocessing.Manager() - return_dict = manager.dict() - jobs = [] - for i in range(len(plugins_info)): - p = multiprocessing.Process( - target=self.checkStatusProcess, args=(plugins_info[i], i, return_dict)) - jobs.append(p) - p.start() - - for proc in jobs: - proc.join() - - returnData = return_dict.values() - for i in ntmp_list: - plugins_info[i]['status'] = returnData[i] - - return plugins_info - - def checkIndexList(self, name, version): - indexList = self.__index_data - for i in indexList: - nv = i.split('-') - if nv[0] == name: - return True - return False - - def checkDisplayIndex(self, name, version, coexist): - # if not os.path.exists(self.__index): - # mw.writeFile(self.__index, '[]') - # indexList = json.loads(mw.readFile(self.__index)) - - indexList = self.__index_data - if coexist: - if type(version) == list: - for index in range(len(version)): - vname = name + '-' + version[index] - if vname in indexList: - return True - else: - vname = name + '-' + version - if vname in indexList: - return True - - else: - if type(version) == list: - for index in range(len(version)): - return self.checkIndexList(name, version) - else: - return self.checkIndexList(name, version) - return False - - def getVersion(self, path): - version_f = path + '/version.pl' - if os.path.exists(version_f): - return mw.readFile(version_f).strip() - return '' - - # 构造本地插件信息 - def getPluginInfo(self, info): - checks = '' - path = '' - coexist = False - - if info["checks"][0:1] == '/': - checks = info["checks"] - else: - checks = mw.getRootDir() + '/' + info['checks'] - - if 'path' in info: - path = info['path'] - - if path[0:1] != '/': - path = mw.getRootDir() + '/' + path - - if 'coexist' in info and info['coexist']: - coexist = True - - pInfo = { - "id": 10000, - "pid": info['pid'], - "type": 1000, - "name": info['name'], - "title": info['title'], - "ps": info['ps'], - "dependnet": "", - "mutex": "", - "icon": "", - "path": path, - "install_checks": checks, - "uninsatll_checks": checks, - "coexist": coexist, - "versions": info['versions'], - # "updates": info['updates'], - "display": False, - "setup": False, - "setup_version": "", - "status": False, - "install_pre_inspection": False, - "uninstall_pre_inspection": False, - } - - if 'icon' in info: - pInfo['icon'] = info['icon'] - - if checks.find('VERSION') > -1: - pInfo['install_checks'] = checks.replace( - 'VERSION', info['versions']) - - if path.find('VERSION') > -1: - pInfo['path'] = path.replace( - 'VERSION', info['versions']) - - pInfo['task'] = self.checkSetupTask( - pInfo['name'], info['versions'], coexist) - pInfo['display'] = self.checkDisplayIndex( - info['name'], pInfo['versions'], coexist) - - pInfo['setup'] = os.path.exists(pInfo['install_checks']) - - if coexist and pInfo['setup']: - pInfo['setup_version'] = info['versions'] - else: - pInfo['setup_version'] = self.getVersion(pInfo['install_checks']) - # pluginInfo['status'] = self.checkStatus(pluginInfo) - - if 'install_pre_inspection' in info: - pInfo['install_pre_inspection'] = info['install_pre_inspection'] - if 'uninstall_pre_inspection' in info: - pInfo['uninstall_pre_inspection'] = info[ - 'uninstall_pre_inspection'] - - pInfo['status'] = False - return pInfo - - def makeCoexist(self, data): - plugins_info = [] - for index in range(len(data['versions'])): - tmp = data.copy() - tmp['title'] = tmp['title'] + \ - '-' + data['versions'][index] - tmp['versions'] = data['versions'][index] - pg = self.getPluginInfo(tmp) - plugins_info.append(pg) - - return plugins_info - - def makeList(self, data, sType='0'): - plugins_info = [] - - # 相应类型 - if (data['pid'] == sType): - if type(data['versions']) == list and 'coexist' in data and data['coexist']: - tmp_data = self.makeCoexist(data) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - else: - pg = self.getPluginInfo(data) - plugins_info.append(pg) - return plugins_info - - # 全部 - if sType == '0': - if type(data['versions']) == list and 'coexist' in data and data['coexist']: - tmp_data = self.makeCoexist(data) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - else: - pg = self.getPluginInfo(data) - plugins_info.append(pg) - - # 已经安装 - if sType == '-1': - if type(data['versions']) == list and 'coexist' in data and data['coexist']: - tmp_data = self.makeCoexist(data) - for index in range(len(tmp_data)): - if tmp_data[index]['setup']: - plugins_info.append(tmp_data[index]) - else: - pg = self.getPluginInfo(data) - if pg['setup']: - plugins_info.append(pg) - - # print plugins_info, data - return plugins_info - - def getAllList(self, sType='0'): - plugins_info = [] - for dirinfo in os.listdir(self.__plugin_dir): - if dirinfo[0:1] == '.': - continue - path = self.__plugin_dir + '/' + dirinfo - json_file = path + '/info.json' - if os.path.exists(json_file): - try: - data = json.loads(mw.readFile(json_file)) - tmp_data = self.makeList(data, sType) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - except Exception as e: - print(e) - return plugins_info - - def searchKey(self, info, kw): - try: - if info['title'].lower().find(kw) > -1: - return True - if info['ps'].lower().find(kw) > -1: - return True - if info['name'].lower().find(kw) > -1: - return True - except Exception as e: - return False - - def getAllListPage(self, sType='0', kw='', page=1, pageSize=10): - plugins_info = [] - for dirinfo in os.listdir(self.__plugin_dir): - if dirinfo[0:1] == '.': - continue - path = self.__plugin_dir + '/' + dirinfo - if os.path.isdir(path): - info_file = path + '/info.json' - if os.path.exists(info_file): - try: - data = json.loads(mw.readFile(info_file)) - # 判断是否搜索 - if kw != '' and not self.searchKey(data, kw): - continue - tmp_data = self.makeList(data, sType) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - except Exception as e: - print(mw.getTracebackInfo()) - - start = (page - 1) * pageSize - end = start + pageSize - _plugins_info = plugins_info[start:end] - - _plugins_info = self.checkStatusMThreads(_plugins_info) - return (_plugins_info, len(plugins_info)) - - def makeListThread(self, data, sType='0'): - plugins_info = [] - - if (data['pid'] == sType): - if type(data['versions']) == list and data.has_key('coexist') and data['coexist']: - tmp_data = self.makeCoexist(data) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - else: - pg = self.getPluginInfo(data) - plugins_info.append(pg) - return plugins_info - - if sType == '0': - if type(data['versions']) == list and data.has_key('coexist') and data['coexist']: - tmp_data = self.makeCoexist(data) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - else: - pg = self.getPluginInfo(data) - plugins_info.append(pg) - - # print plugins_info, data - return plugins_info - - def getAllListThread(self, sType='0'): - plugins_info = [] - tmp_list = [] - threads = [] - for dirinfo in os.listdir(self.__plugin_dir): - if dirinfo[0:1] == '.': - continue - path = self.__plugin_dir + '/' + dirinfo - if os.path.isdir(path): - json_file = path + '/info.json' - if os.path.exists(json_file): - data = json.loads(mw.readFile(json_file)) - if sType == '0': - tmp_list.append(data) - - if (data['pid'] == sType): - tmp_list.append(data) - - ntmp_list = range(len(tmp_list)) - for i in ntmp_list: - t = pa_thread(self.makeListThread, (tmp_list[i], sType)) - threads.append(t) - for i in ntmp_list: - threads[i].start() - for i in ntmp_list: - threads[i].join() - - for i in ntmp_list: - t = threads[i].getResult() - for index in range(len(t)): - plugins_info.append(t[index]) - - return plugins_info - - def makeListProcess(self, data, sType, i, return_dict): - plugins_info = [] - - if (data['pid'] == sType): - if type(data['versions']) == list and data.has_key('coexist') and data['coexist']: - tmp_data = self.makeCoexist(data) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - else: - pg = self.getPluginInfo(data) - plugins_info.append(pg) - # return plugins_info - - if sType == '0': - if type(data['versions']) == list and data.has_key('coexist') and data['coexist']: - tmp_data = self.makeCoexist(data) - for index in range(len(tmp_data)): - plugins_info.append(tmp_data[index]) - else: - pg = self.getPluginInfo(data) - plugins_info.append(pg) - - return_dict[i] = plugins_info - # return plugins_info - - def getAllListProcess(self, sType='0'): - plugins_info = [] - tmp_list = [] - manager = multiprocessing.Manager() - return_dict = manager.dict() - jobs = [] - for dirinfo in os.listdir(self.__plugin_dir): - if dirinfo[0:1] == '.': - continue - path = self.__plugin_dir + '/' + dirinfo - if os.path.isdir(path): - json_file = path + '/info.json' - if os.path.exists(json_file): - data = json.loads(mw.readFile(json_file)) - if sType == '0': - tmp_list.append(data) - - if (data['pid'] == sType): - tmp_list.append(data) - - ntmp_list = range(len(tmp_list)) - for i in ntmp_list: - p = multiprocessing.Process( - target=self.makeListProcess, args=(tmp_list[i], sType, i, return_dict)) - jobs.append(p) - p.start() - - for proc in jobs: - proc.join() - - returnData = return_dict.values() - for i in ntmp_list: - for index in range(len(returnData[i])): - plugins_info.append(returnData[i][index]) - - return plugins_info - - def getPluginList(self, sType, kw='', sPage=1, sPageSize=10): - # print(sType, kw, sPage, sPageSize) - - ret = {} - ret['type'] = json.loads(mw.readFile(self.__type)) - # plugins_info = self.getAllListThread(sType) - # plugins_info = self.getAllListProcess(sType) - data = self.getAllListPage(sType, kw, sPage, sPageSize) - ret['data'] = data[0] - - args = {} - args['count'] = data[1] - args['p'] = sPage - args['tojs'] = 'getSList' - args['row'] = sPageSize - - ret['list'] = mw.getPage(args) - return ret - - def getIndexList(self): - if not os.path.exists(self.__index): - mw.writeFile(self.__index, '[]') - - indexList = json.loads(mw.readFile(self.__index)) - plist = [] - for i in indexList: - tmp = i.split('-') - tmp_len = len(tmp) - plugin_name = tmp[0] - plugin_ver = tmp[1] - if tmp_len > 2: - tmpArr = tmp[0:tmp_len - 1] - plugin_name = '-'.join(tmpArr) - plugin_ver = tmp[tmp_len - 1] - - read_json_file = self.__plugin_dir + '/' + plugin_name + '/info.json' - if os.path.exists(read_json_file): - content = mw.readFile(read_json_file) - try: - data = json.loads(content) - data = self.makeList(data) - for index in range(len(data)): - if data[index]['coexist']: - if data[index]['versions'] == plugin_ver or plugin_ver in data[index]['versions']: - data[index]['display'] = True - plist.append(data[index]) - continue - else: - data[index]['display'] = True - plist.append(data[index]) - - except Exception as e: - print('getIndexList:', mw.getTracebackInfo()) - - # 使用gevent模式时,无法使用多进程 - # plist = self.checkStatusMProcess(plist) - plist = self.checkStatusMThreads(plist) - return plist - - def setIndexSort(self, sort): - data = sort.split('|') - mw.writeFile(self.__index, json.dumps(data)) - return True - - def addIndex(self, name, version): - if not os.path.exists(self.__index): - mw.writeFile(self.__index, '[]') - - indexList = json.loads(mw.readFile(self.__index)) - vname = name + '-' + version - - if vname in indexList: - return mw.returnJson(False, '请不要重复添加!') - if len(indexList) > 12: - return mw.returnJson(False, '首页最多只能显示12个软件!') - - indexList.append(vname) - mw.writeFile(self.__index, json.dumps(indexList)) - return mw.returnJson(True, '添加成功!') - - def removeIndex(self, name, version): - if not os.path.exists(self.__index): - mw.writeFile(self.__index, '[]') - - indexList = json.loads(mw.readFile(self.__index)) - vname = name + '-' + version - if not vname in indexList: - return mw.returnJson(True, '删除成功!') - indexList.remove(vname) - mw.writeFile(self.__index, json.dumps(indexList)) - return mw.returnJson(True, '删除成功!') - - # shell 调用 - def run(self, name, func, version='', args='', script='index'): - - path = self.__plugin_dir + '/' + name + '/' + script + '.py' - if not os.path.exists(path): - path = self.__plugin_dir + '/' + name + '/' + name + '.py' - - py = 'python3 ' + path - - if args == '': - py_cmd = py + ' ' + func + ' ' + version - else: - py_cmd = py + ' ' + func + ' ' + version + ' ' + args - - if not os.path.exists(path): - return ('', '') - data = mw.execShell(py_cmd) - - if mw.isDebugMode(): - print('run', py_cmd) - print(data) - # print os.path.exists(py_cmd) - - return (data[0].strip(), data[1].strip()) - - # 映射包调用 - def callback(self, name, func, args='', script='index'): - package = self.__plugin_dir + '/' + name - if not os.path.exists(package): - return (False, "插件不存在!") - - if not package in sys.path: - sys.path.append(package) - eval_str = "__import__('" + script + "')." + func + '(' + args + ')' - newRet = None - try: - newRet = eval(eval_str) - except Exception as e: - print(mw.getTracebackInfo()) - - if mw.isDebugMode(): - print('callback', eval_str) - - return (True, newRet) diff --git a/class/core/site_api.py b/class/core/site_api.py deleted file mode 100755 index 3375146db..000000000 --- a/class/core/site_api.py +++ /dev/null @@ -1,2909 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 站点操作 -# --------------------------------------------------------------------------------- - -import time -import os -import sys -from urllib.parse import urlparse -import mw -import re -import json -import shutil - - -from flask import request - - -class site_api: - siteName = None # 网站名称 - sitePath = None # 根目录 - sitePort = None # 端口 - phpVersion = None # PHP版本 - - setupPath = None # 安装路径 - vhostPath = None - logsPath = None - passPath = None - rewritePath = None - redirectPath = None - sslDir = None # ssl目录 - sslLetsDir = None # lets ssl目录 - - def __init__(self): - # nginx conf - self.setupPath = mw.getServerDir() + '/web_conf' - - self.vhostPath = vhost = self.setupPath + '/nginx/vhost' - if not os.path.exists(vhost): - mw.execShell("mkdir -p " + vhost + " && chmod -R 755 " + vhost) - self.rewritePath = rewrite = self.setupPath + '/nginx/rewrite' - if not os.path.exists(rewrite): - mw.execShell("mkdir -p " + rewrite + " && chmod -R 755 " + rewrite) - - self.passPath = passwd = self.setupPath + '/nginx/pass' - if not os.path.exists(passwd): - mw.execShell("mkdir -p " + passwd + " && chmod -R 755 " + passwd) - - self.redirectPath = redirect = self.setupPath + '/nginx/redirect' - if not os.path.exists(redirect): - mw.execShell("mkdir -p " + redirect + - " && chmod -R 755 " + redirect) - - self.proxyPath = proxy = self.setupPath + '/nginx/proxy' - if not os.path.exists(proxy): - mw.execShell("mkdir -p " + proxy + " && chmod -R 755 " + proxy) - - self.logsPath = mw.getRootDir() + '/wwwlogs' - # ssl conf - self.sslDir = self.setupPath + '/ssl' - self.sslLetsDir = self.setupPath + '/letsencrypt' - if not os.path.exists(self.sslLetsDir): - mw.execShell("mkdir -p " + self.sslLetsDir + - " && chmod -R 755 " + self.sslLetsDir) - - def runHook(self, hook_name, func_name): - # 站点操作Hook - hook_file = 'data/hook_site_cb.json' - hook_cfg = [] - if os.path.exists(hook_file): - t = mw.readFile(hook_file) - hook_cfg = json.loads(t) - - hook_num = len(hook_cfg) - if hook_num == 0: - return - - import plugins_api - pa = plugins_api.plugins_api() - - for x in range(hook_num): - hook_data = hook_cfg[x] - if func_name in hook_data: - app_name = hook_data["name"] - run_func = hook_data[func_name]['func'] - # print(app_name, run_func) - pa.run(app_name, run_func) - return True - - ##### ----- start ----- ### - def listApi(self): - limit = request.form.get('limit', '10') - p = request.form.get('p', '1') - type_id = request.form.get('type_id', '0').strip() - search = request.form.get('search', '').strip() - - start = (int(p) - 1) * (int(limit)) - - siteM = siteC = mw.M('sites').field('id,name,path,status,ps,addtime,edate,type_id') - - sql_where = '' - if search != '': - sql_where = " name like '%" + search + "%' or ps like '%" + search + "%' " - - if type_id != '' and int(type_id) >= 0 and search != '': - sql_where = sql_where + " and type_id=" + type_id + "" - if type_id != '' and int(type_id) >= 0: - sql_where = " type_id=" + type_id - - if sql_where != '': - siteM.where(sql_where) - - _list = siteM.limit((str(start)) + ',' +limit).order('id desc').select() - if _list != None: - for i in range(len(_list)): - _list[i]['backup_count'] = mw.M('backup').where("pid=? AND type=?", (_list[i]['id'], 0)).count() - - _ret = {} - _ret['data'] = _list - - if sql_where != '': - count = siteC.where(sql_where).count() - else: - count = siteC.count() - - _page = {} - _page['count'] = count - _page['tojs'] = 'getWeb' - _page['p'] = p - _page['row'] = limit - - page_args_tpl = '' - page_args_tpl += ','+type_id+'' - if search != '': - page_args_tpl += ',"'+search+'"' - _page['args_tpl'] = page_args_tpl - - _ret['page'] = mw.getPage(_page) - return mw.getJson(_ret) - - def setDefaultSiteApi(self): - name = request.form.get('name', '') - import time - # 清理旧的 - default_site = mw.readFile('data/default_site.pl') - if default_site: - path = self.getHostConf(default_site) - if os.path.exists(path): - conf = mw.readFile(path) - rep = "listen\s+80.+;" - conf = re.sub(rep, 'listen 80;', conf, 1) - rep = "listen\s+443.+;" - conf = re.sub(rep, 'listen 443 ssl;', conf, 1) - mw.writeFile(path, conf) - - path = self.getHostConf(name) - if os.path.exists(path): - conf = mw.readFile(path) - rep = "listen\s+80\s*;" - conf = re.sub(rep, 'listen 80 default_server;', conf, 1) - rep = "listen\s+443\s*ssl\s*\w*\s*;" - conf = re.sub(rep, 'listen 443 ssl default_server;', conf, 1) - mw.writeFile(path, conf) - - mw.writeFile('data/default_site.pl', name) - mw.restartWeb() - return mw.returnJson(True, '设置成功!') - - def getDefaultSiteApi(self): - data = {} - data['sites'] = mw.M('sites').field( - 'name').order('id desc').select() - data['default_site'] = mw.readFile('data/default_site.pl') - return mw.getJson(data) - - def getCliPhpVersionApi(self): - php_dir = mw.getServerDir() + '/php' - if not os.path.exists(php_dir): - return mw.returnJson(False, '未安装PHP,无法设置') - - php_bin = '/usr/bin/php' - php_versions = self.getPhpVersion() - php_versions = php_versions[1:] - - if len(php_versions) < 1: - return mw.returnJson(False, '未安装PHP,无法设置') - - if os.path.exists(php_bin) and os.path.islink(php_bin): - link_re = os.readlink(php_bin) - for v in php_versions: - if link_re.find(v['version']) != -1: - return mw.getJson({"select": v, "versions": php_versions}) - - return mw.getJson({ - "select": php_versions[0], - "versions": php_versions}) - - def setCliPhpVersionApi(self): - if mw.isAppleSystem(): - return mw.returnJson(False, "开发机不可设置!") - - version = request.form.get('version', '') - - php_bin = '/usr/bin/php' - php_bin_src = "/www/server/php/%s/bin/php" % version - php_ize = '/usr/bin/phpize' - php_ize_src = "/www/server/php/%s/bin/phpize" % version - php_fpm = '/usr/bin/php-fpm' - php_fpm_src = "/www/server/php/%s/sbin/php-fpm" % version - php_pecl = '/usr/bin/pecl' - php_pecl_src = "/www/server/php/%s/bin/pecl" % version - php_pear = '/usr/bin/pear' - php_pear_src = "/www/server/php/%s/bin/pear" % version - if not os.path.exists(php_bin_src): - return mw.returnJson(False, '指定PHP版本未安装!') - - is_chattr = mw.execShell('lsattr /usr|grep /usr/bin')[0].find('-i-') - if is_chattr != -1: - mw.execShell('chattr -i /usr/bin') - mw.execShell("rm -f " + php_bin + ' ' + php_ize + ' ' + - php_fpm + ' ' + php_pecl + ' ' + php_pear) - mw.execShell("ln -sf %s %s" % (php_bin_src, php_bin)) - mw.execShell("ln -sf %s %s" % (php_ize_src, php_ize)) - mw.execShell("ln -sf %s %s" % (php_fpm_src, php_fpm)) - mw.execShell("ln -sf %s %s" % (php_pecl_src, php_pecl)) - mw.execShell("ln -sf %s %s" % (php_pear_src, php_pear)) - if is_chattr != -1: - mw.execShell('chattr +i /usr/bin') - mw.writeLog('面板设置', '设置PHP-CLI版本为: %s' % version) - return mw.returnJson(True, '设置成功!') - - def setPsApi(self): - mid = request.form.get('id', '') - ps = request.form.get('ps', '') - if mw.M('sites').where("id=?", (mid,)).setField('ps', ps): - return mw.returnJson(True, '修改成功!') - return mw.returnJson(False, '修改失败!') - - def stopApi(self): - mid = request.form.get('id', '') - name = request.form.get('name', '') - - return self.stop(mid, name) - - def stop(self, mid, name): - path = self.setupPath + '/stop' - if not os.path.exists(path): - os.makedirs(path) - default_text = 'The website has been closed!!!' - mw.writeFile(path + '/index.html', default_text) - - binding = mw.M('binding').where('pid=?', (mid,)).field( - 'id,pid,domain,path,port,addtime').select() - for b in binding: - bpath = path + '/' + b['path'] - if not os.path.exists(bpath): - mw.execShell('mkdir -p ' + bpath) - mw.execShell('ln -sf ' + path + - '/index.html ' + bpath + '/index.html') - - sitePath = mw.M('sites').where("id=?", (mid,)).getField('path') - - # nginx - file = self.getHostConf(name) - conf = mw.readFile(file) - if conf: - conf = conf.replace(sitePath, path) - mw.writeFile(file, conf) - - mw.M('sites').where("id=?", (mid,)).setField('status', '0') - mw.restartWeb() - msg = mw.getInfo('网站[{1}]已被停用!', (name,)) - mw.writeLog('网站管理', msg) - return mw.returnJson(True, '站点已停用!') - - def startApi(self): - mid = request.form.get('id', '') - name = request.form.get('name', '') - path = self.setupPath + '/stop' - sitePath = mw.M('sites').where("id=?", (mid,)).getField('path') - - # nginx - file = self.getHostConf(name) - conf = mw.readFile(file) - if conf: - conf = conf.replace(path, sitePath) - mw.writeFile(file, conf) - - mw.M('sites').where("id=?", (mid,)).setField('status', '1') - mw.restartWeb() - msg = mw.getInfo('网站[{1}]已被启用!', (name,)) - mw.writeLog('网站管理', msg) - return mw.returnJson(True, '站点已启用!') - - def getBackupApi(self): - limit = request.form.get('limit', '') - p = request.form.get('p', '') - mid = request.form.get('search', '') - - find = mw.M('sites').where("id=?", (mid,)).field( - "id,name,path,status,ps,addtime,edate").find() - - start = (int(p) - 1) * (int(limit)) - _list = mw.M('backup').where('pid=?', (mid,)).field('id,type,name,pid,filename,size,addtime').limit( - (str(start)) + ',' + limit).order('id desc').select() - _ret = {} - _ret['data'] = _list - - count = mw.M('backup').where("id=?", (mid,)).count() - info = {} - info['count'] = count - info['tojs'] = 'getBackup' - info['p'] = p - info['row'] = limit - _ret['page'] = mw.getPage(info) - _ret['site'] = find - return mw.getJson(_ret) - - def toBackupApi(self): - mid = request.form.get('id', '') - find = mw.M('sites').where( - "id=?", (mid,)).field('name,path,id').find() - fileName = find['name'] + '_' + \ - time.strftime('%Y%m%d_%H%M%S', time.localtime()) + '.zip' - backupPath = mw.getBackupDir() + '/site' - zipName = backupPath + '/' + fileName - if not (os.path.exists(backupPath)): - os.makedirs(backupPath) - tmps = mw.getRunDir() + '/tmp/panelExec.log' - execStr = "cd '" + find['path'] + "' && zip '" + \ - zipName + "' -r ./* > " + tmps + " 2>&1" - # print execStr - mw.execShell(execStr) - - if os.path.exists(zipName): - fsize = os.path.getsize(zipName) - else: - fsize = 0 - sql = mw.M('backup').add('type,name,pid,filename,size,addtime', - (0, fileName, find['id'], zipName, fsize, mw.getDate())) - - msg = mw.getInfo('备份网站[{1}]成功!', (find['name'],)) - mw.writeLog('网站管理', msg) - return mw.returnJson(True, '备份成功!') - - def delBackupApi(self): - mid = request.form.get('id', '') - filename = mw.M('backup').where( - "id=?", (mid,)).getField('filename') - if os.path.exists(filename): - os.remove(filename) - name = mw.M('backup').where("id=?", (mid,)).getField('name') - msg = mw.getInfo('删除网站[{1}]的备份[{2}]成功!', (name, filename)) - mw.writeLog('网站管理', msg) - mw.M('backup').where("id=?", (mid,)).delete() - return mw.returnJson(True, '站点删除成功!') - - def getPhpVersionApi(self): - data = self.getPhpVersion() - return mw.getJson(data) - - def setPhpVersionApi(self): - siteName = request.form.get('siteName', '') - version = request.form.get('version', '') - - # nginx - file = self.getHostConf(siteName) - conf = mw.readFile(file) - if conf: - rep = "enable-php-(.*)\.conf" - tmp = re.search(rep, conf).group() - conf = conf.replace(tmp, 'enable-php-' + version + '.conf') - mw.writeFile(file, conf) - - msg = mw.getInfo('成功切换网站[{1}]的PHP版本为PHP-{2}', (siteName, version)) - mw.writeLog("网站管理", msg) - mw.restartWeb() - return mw.returnJson(True, msg) - - def getDomainApi(self): - pid = request.form.get('pid', '') - return self.getDomain(pid) - - # 获取站点所有域名 - def getSiteDomainsApi(self): - pid = request.form.get('id', '') - - data = {} - domains = mw.M('domain').where( - 'pid=?', (pid,)).field('name,id').select() - binding = mw.M('binding').where( - 'pid=?', (pid,)).field('domain,id').select() - if type(binding) == str: - return binding - for b in binding: - tmp = {} - tmp['name'] = b['domain'] - tmp['id'] = b['id'] - domains.append(tmp) - data['domains'] = domains - data['email'] = mw.M('users').getField('email') - if data['email'] == 'midoks@163.com': - data['email'] = '' - return mw.returnJson(True, 'OK', data) - - def getDirBindingApi(self): - mid = request.form.get('id', '') - - path = mw.M('sites').where('id=?', (mid,)).getField('path') - if not os.path.exists(path): - checks = ['/', '/usr', '/etc'] - if path in checks: - data = {} - data['dirs'] = [] - data['binding'] = [] - return mw.returnJson(True, 'OK', data) - os.system('mkdir -p ' + path) - os.system('chmod 755 ' + path) - os.system('chown www:www ' + path) - siteName = mw.M('sites').where( - 'id=?', (get.id,)).getField('name') - mw.writeLog( - '网站管理', '站点[' + siteName + '],根目录[' + path + ']不存在,已重新创建!') - - dirnames = [] - for filename in os.listdir(path): - try: - filePath = path + '/' + filename - if os.path.islink(filePath): - continue - if os.path.isdir(filePath): - dirnames.append(filename) - except: - pass - - data = {} - data['dirs'] = dirnames - data['binding'] = mw.M('binding').where('pid=?', (mid,)).field( - 'id,pid,domain,path,port,addtime').select() - return mw.returnJson(True, 'OK', data) - - def getDirUserIniApi(self): - mid = request.form.get('id', '') - - path = mw.M('sites').where('id=?', (mid,)).getField('path') - name = mw.M('sites').where("id=?", (mid,)).getField('name') - data = {} - data['logs'] = self.getLogsStatus(name) - data['runPath'] = self.getSiteRunPath(mid) - - data['userini'] = False - if os.path.exists(path + '/.user.ini'): - data['userini'] = True - - if data['runPath']['runPath'] != '/': - if os.path.exists(path + data['runPath']['runPath'] + '/.user.ini'): - data['userini'] = True - - data['pass'] = self.getHasPwd(name) - data['path'] = path - data['name'] = name - return mw.returnJson(True, 'OK', data) - - def setDirUserIniApi(self): - path = request.form.get('path', '') - runPath = request.form.get('runPath', '') - filename = path + '/.user.ini' - - if os.path.exists(filename): - self.delUserInI(path) - mw.execShell("which chattr && chattr -i " + filename) - os.remove(filename) - return mw.returnJson(True, '已清除防跨站设置!') - - self.setDirUserINI(path, runPath) - mw.execShell("which chattr && chattr +i " + filename) - - return mw.returnJson(True, '已打开防跨站设置!') - - def setRewriteApi(self): - data = request.form.get('data', '') - path = request.form.get('path', '') - encoding = request.form.get('encoding', '') - if not os.path.exists(path): - mw.writeFile(path, '') - - mw.backFile(path) - mw.writeFile(path, data) - isError = mw.checkWebConfig() - if(type(isError) == str): - mw.restoreFile(path) - return mw.returnJson(False, 'ERROR:
' + isError.replace("\n", '
') + '
') - mw.restartWeb() - return mw.returnJson(True, '设置成功!') - - def setRewriteTplApi(self): - data = request.form.get('data', '') - name = request.form.get('name', '') - path = mw.getRunDir() + "/rewrite/nginx/" + name + ".conf" - if os.path.exists(path): - return mw.returnJson(False, '模版已经存在!') - - if data == "": - return mw.returnJson(False, '模版内容不能为空!') - ok = mw.writeFile(path, data) - if not ok: - return mw.returnJson(False, '模版保持失败!') - - return mw.returnJson(True, '设置模板成功!') - - def logsOpenApi(self): - mid = request.form.get('id', '') - name = mw.M('sites').where("id=?", (mid,)).getField('name') - - # NGINX - filename = self.getHostConf(name) - if os.path.exists(filename): - conf = mw.readFile(filename) - rep = self.logsPath + "/" + name + ".log" - if conf.find(rep) != -1: - conf = conf.replace(rep + " main", "off") - else: - conf = conf.replace('access_log off', - 'access_log ' + rep + " main") - mw.writeFile(filename, conf) - - mw.restartWeb() - return mw.returnJson(True, '操作成功!') - - def getCertListApi(self): - try: - vpath = self.sslDir - if not os.path.exists(vpath): - os.system('mkdir -p ' + vpath) - data = [] - for d in os.listdir(vpath): - - # keyPath = self.sslDir + siteName + '/privkey.pem' - # certPath = self.sslDir + siteName + '/fullchain.pem' - - keyPath = vpath + '/' + d + '/privkey.pem' - certPath = vpath + '/' + d + '/fullchain.pem' - if os.path.exists(keyPath) and os.path.exists(certPath): - self.saveCert(keyPath, certPath) - - mpath = vpath + '/' + d + '/info.json' - if not os.path.exists(mpath): - continue - - tmp = mw.readFile(mpath) - if not tmp: - continue - tmp1 = json.loads(tmp) - data.append(tmp1) - return mw.returnJson(True, 'OK', data) - except: - return mw.returnJson(True, 'OK', []) - - def deleteSslApi(self): - site_name = request.form.get('site_name', '') - ssl_type = request.form.get('ssl_type', '') - - path = self.sslDir + '/' + site_name - csr_path = path + '/fullchain.pem' # 生成证书路径 - - file = self.getHostConf(site_name) - content = mw.readFile(file) - key_text = 'ssl_certificate' - status = True - if content.find(key_text) == -1: - status = False - - if ssl_type == 'now': - if status: - return mw.returnJson(False, '使用中,先关闭再删除') - if os.path.exists(path): - mw.execShell('rm -rf ' + path) - else: - return mw.returnJson(False, '还未申请!') - elif ssl_type == 'lets': - ssl_lets_dir = self.sslLetsDir + '/' + site_name - csr_lets_path = ssl_lets_dir + '/fullchain.pem' # 生成证书路径 - if mw.md5(mw.readFile(csr_lets_path)) == mw.md5(mw.readFile(csr_path)): - return mw.returnJson(False, '使用中,先关闭再删除') - mw.execShell('rm -rf ' + ssl_lets_dir) - elif ssl_type == 'acme': - ssl_acme_dir = mw.getAcmeDomainDir(site_name) - csr_acme_path = ssl_acme_dir + '/fullchain.cer' # 生成证书路径 - if mw.md5(mw.readFile(csr_acme_path)) == mw.md5(mw.readFile(csr_path)): - return mw.returnJson(False, '使用中,先关闭再删除') - mw.execShell('rm -rf ' + ssl_acme_dir) - - # mw.restartWeb() - return mw.returnJson(True, '删除成功') - - def getSslApi(self): - site_name = request.form.get('site_name', '') - ssl_type = request.form.get('ssl_type', '') - - path = self.sslDir + '/' + site_name - - file = self.getHostConf(site_name) - content = mw.readFile(file) - - key_text = 'ssl_certificate' - status = True - stype = 0 - if content.find(key_text) == -1: - status = False - stype = -1 - - to_https = self.isToHttps(site_name) - sid = mw.M('sites').where("name=?", (site_name,)).getField('id') - domains = mw.M('domain').where("pid=?", (sid,)).field('name').select() - - csr_path = path + '/fullchain.pem' # 生成证书路径 - key_path = path + '/privkey.pem' # 密钥文件路径 - - cert_data = None - if ssl_type == 'lets': - csr_path = self.sslLetsDir + '/' + site_name + '/fullchain.pem' # 生成证书路径 - key_path = self.sslLetsDir + '/' + site_name + '/privkey.pem' # 密钥文件路径 - elif ssl_type == 'acme': - acme_dir = mw.getAcmeDomainDir(site_name) - csr_path = acme_dir + '/fullchain.cer' # 生成证书路径 - key_path = acme_dir + '/' + site_name + '.key' # 密钥文件路径 - - key = mw.readFile(key_path) - csr = mw.readFile(csr_path) - cert_data = mw.getCertName(csr_path) - data = { - 'status': status, - 'domain': domains, - 'key': key, - 'csr': csr, - 'type': stype, - 'httpTohttps': to_https, - 'cert_data': cert_data, - } - return mw.returnJson(True, 'OK', data) - - def setSslApi(self): - siteName = request.form.get('siteName', '') - - key = request.form.get('key', '') - csr = request.form.get('csr', '') - - path = self.sslDir + '/' + siteName - if not os.path.exists(path): - mw.execShell('mkdir -p ' + path) - - csrpath = path + "/fullchain.pem" # 生成证书路径 - keypath = path + "/privkey.pem" # 密钥文件路径 - - if(key.find('KEY') == -1): - return mw.returnJson(False, '秘钥错误,请检查!') - if(csr.find('CERTIFICATE') == -1): - return mw.returnJson(False, '证书错误,请检查!') - - mw.writeFile('/tmp/cert.pl', csr) - if not mw.checkCert('/tmp/cert.pl'): - return mw.returnJson(False, '证书错误,请粘贴正确的PEM格式证书!') - - mw.backFile(keypath) - mw.backFile(csrpath) - - mw.writeFile(keypath, key) - mw.writeFile(csrpath, csr) - - # 写入配置文件 - result = self.setSslConf(siteName) - if not result['status']: - return mw.getJson(result) - - isError = mw.checkWebConfig() - if(type(isError) == str): - mw.restoreFile(keypath) - mw.restoreFile(csrpath) - return mw.returnJson(False, 'ERROR:
' + isError.replace("\n", '
') + '
') - - mw.writeLog('网站管理', '证书已保存!') - mw.restartWeb() - return mw.returnJson(True, '证书已保存!') - - def setCertToSiteApi(self): - certName = request.form.get('certName', '') - siteName = request.form.get('siteName', '') - try: - path = self.sslDir + '/' + siteName.strip() - if not os.path.exists(path): - return mw.returnJson(False, '证书不存在!') - - result = self.setSslConf(siteName) - if not result['status']: - return mw.getJson(result) - - mw.restartWeb() - mw.writeLog('网站管理', '证书已部署!') - return mw.returnJson(True, '证书已部署!') - except Exception as ex: - return mw.returnJson(False, '设置错误:' + str(ex)) - - def removeCertApi(self): - certName = request.form.get('certName', '') - try: - path = self.sslDir + '/' + certName - if not os.path.exists(path): - return mw.returnJson(False, '证书已不存在!') - os.system("rm -rf " + path) - return mw.returnJson(True, '证书已删除!') - except: - return mw.returnJson(False, '删除失败!') - - def closeSslConfApi(self): - siteName = request.form.get('siteName', '') - - file = self.getHostConf(siteName) - conf = mw.readFile(file) - - if conf: - rep = "\n\\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END" - conf = re.sub(rep, '', conf) - rep = "\\s+ssl_certificate\\s+.+;\\s+ssl_certificate_key\\s+.+;" - conf = re.sub(rep, '', conf) - rep = "\\s+ssl_protocols\\s+.+;\n" - conf = re.sub(rep, '', conf) - rep = "\\s+ssl_ciphers\\s+.+;\n" - conf = re.sub(rep, '', conf) - rep = "\\s+ssl_prefer_server_ciphers\\s+.+;\n" - conf = re.sub(rep, '', conf) - rep = "\\s+ssl_session_cache\\s+.+;\n" - conf = re.sub(rep, '', conf) - rep = r"\s+ssl_session_timeout\s+.+;\n" - conf = re.sub(rep, '', conf) - rep = r"\s+ssl_ecdh_curve\s+.+;\n" - conf = re.sub(rep, '', conf) - rep = r"\s+ssl_session_tickets\s+.+;\n" - conf = re.sub(rep, '', conf) - rep = r"\s+ssl_stapling\s+.+;\n" - conf = re.sub(rep, '', conf) - rep = r"\s+ssl_stapling_verify\s+.+;\n" - conf = re.sub(rep, '', conf) - rep = r"\s+add_header\s+.+;\n" - conf = re.sub(rep, '', conf) - rep = r"\s+add_header\s+.+;\n" - conf = re.sub(rep, '', conf) - rep = r"\s+ssl\s+on;" - conf = re.sub(rep, '', conf) - rep = r"\s+error_page\s497.+;" - conf = re.sub(rep, '', conf) - rep = r"\s+if.+server_port.+\n.+\n\s+\s*}" - conf = re.sub(rep, '', conf) - rep = r"\s+listen\s+443.*;" - conf = re.sub(rep, '', conf) - rep = r"\s+listen\s+\[\:\:\]\:443.*;" - conf = re.sub(rep, '', conf) - rep = r"\s+http2\s+on;" - conf = re.sub(rep, '', conf) - mw.writeFile(file, conf) - - msg = mw.getInfo('网站[{1}]关闭SSL成功!', (siteName,)) - mw.writeLog('网站管理', msg) - mw.restartWeb() - return mw.returnJson(True, 'SSL已关闭!') - - def deploySslApi(self): - site_name = request.form.get('site_name', '') - ssl_type = request.form.get('ssl_type', '') - - path = self.sslDir + '/' + site_name - csr_path = path + '/fullchain.pem' # 生成证书路径 - key_path = path + '/privkey.pem' # 生成证书路径 - - if not os.path.exists(path): - os.makedirs(path) - - if ssl_type == 'lets': - ssl_lets_dir = self.sslLetsDir + '/' + site_name - lets_csrpath = ssl_lets_dir + '/fullchain.pem' - lets_keypath = ssl_lets_dir + '/privkey.pem' - if mw.md5(mw.readFile(lets_csrpath)) == mw.md5(mw.readFile(csr_path)): - return mw.returnJson(False, '已部署Lets') - else: - - mw.buildSoftLink(lets_csrpath, csr_path, True) - mw.buildSoftLink(lets_keypath, key_path, True) - mw.execShell('echo "lets" > "' + path + '/README"') - elif ssl_type == 'acme': - ssl_acme_dir = mw.getAcmeDir() + '/' + site_name - if not os.path.exists(ssl_acme_dir): - ssl_acme_dir = mw.getAcmeDir() + '/' + site_name + '_ecc' - - acme_csrpath = ssl_acme_dir + '/fullchain.cer' - acme_keypath = ssl_acme_dir + '/' + site_name + '.key' - if mw.md5(mw.readFile(acme_csrpath)) == mw.md5(mw.readFile(csr_path)): - return mw.returnJson(False, '已部署ACME') - else: - mw.buildSoftLink(acme_csrpath, csr_path, True) - mw.buildSoftLink(acme_keypath, key_path, True) - mw.execShell('echo "acme" > "' + path + '/README"') - - result = self.setSslConf(site_name) - if not result['status']: - return mw.getJson(result) - return mw.returnJson(True, '部署成功') - - def getLetsIndex(self, site_name): - cfg = mw.getRunDir() + '/data/letsencrypt.json' - if not os.path.exists(cfg): - return False - - data = mw.readFile(cfg) - lets_data = json.loads(data) - order_list = lets_data['orders'] - - for x in order_list: - if order_list[x]['status'] == 'valid': - for d in order_list[x]['domains']: - if d == site_name: - return x - return False - - def renewSslApi(self): - site_name = request.form.get('site_name', '') - ssl_type = request.form.get('ssl_type', '') - if ssl_type == 'lets': - index = self.getLetsIndex(site_name) - if index: - import cert_api - data = cert_api.cert_api().renewCert(index) - return data - else: - return mw.returnJson(False, '无效操作') - - return mw.returnJson(True, '续期成功') - - def getLetLogsApi(self): - log_file = mw.getRunDir() + '/logs/letsencrypt.log' - if not os.path.exists(log_file): - mw.execShell('touch ' + log_file) - return mw.returnJson(True, 'OK', log_file) - - def createLetApi(self): - siteName = request.form.get('siteName', '') - domains = request.form.get('domains', '') - force = request.form.get('force', '') - renew = request.form.get('renew', '') - email_args = request.form.get('email', '') - - domains = json.loads(domains) - email = mw.M('users').getField('email') - if email_args.strip() != '': - mw.M('users').setField('email', email_args) - email = email_args - - if not len(domains): - return mw.returnJson(False, '请选择域名') - - host_conf_file = self.getHostConf(siteName) - if os.path.exists(host_conf_file): - siteConf = mw.readFile(host_conf_file) - if siteConf.find('301-END') != -1: - return mw.returnJson(False, '检测到您的站点做了301重定向设置,请先关闭重定向!') - - # 检测存在反向代理 - data_path = self.getProxyDataPath(siteName) - data_content = mw.readFile(data_path) - if data_content != False: - try: - data = json.loads(data_content) - except: - pass - for proxy in data: - proxy_dir = "{}/{}".format(self.proxyPath, siteName) - proxy_dir_file = proxy_dir + '/' + proxy['id'] + '.conf' - if os.path.exists(proxy_dir_file): - return mw.returnJson(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!') - - # fix binddir domain ssl apply question - mw.backFile(host_conf_file) - auth_to = self.getSitePath(siteName) - rep = r"\s*root\s*(.+);" - replace_root = "\n\troot " + auth_to + ";" - siteConf = re.sub(rep, replace_root, siteConf) - mw.writeFile(host_conf_file, siteConf) - mw.restartWeb() - - to_args = { - 'domains': domains, - 'auth_type': 'http', - 'auth_to': auth_to, - } - - src_letpath = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName - src_csrpath = src_letpath + "/fullchain.pem" # 生成证书路径 - src_keypath = src_letpath + "/privkey.pem" # 密钥文件路径 - - dst_letpath = self.sslDir + '/' + siteName - dst_csrpath = dst_letpath + '/fullchain.pem' - dst_keypath = dst_letpath + '/privkey.pem' - - if not os.path.exists(src_letpath): - import cert_api - data = cert_api.cert_api().applyCertApi(to_args) - mw.restoreFile(host_conf_file) - if not data['status']: - msg = data['msg'] - if type(data['msg']) != str: - msg = data['msg'][0] - emsg = data['msg'][1]['challenges'][0]['error'] - msg = msg + '

响应状态:' + str(emsg['status']) + '

错误类型:' + emsg[ - 'type'] + '

错误代码:' + emsg['detail'] + '

' - return mw.returnJson(data['status'], msg, data['msg']) - - mw.execShell('mkdir -p ' + dst_letpath) - mw.buildSoftLink(src_csrpath, dst_csrpath, True) - mw.buildSoftLink(src_keypath, dst_keypath, True) - mw.execShell('echo "lets" > "' + dst_letpath + '/README"') - - # 写入配置文件 - result = self.setSslConf(siteName) - if not result['status']: - return mw.getJson(result) - - result['csr'] = mw.readFile(src_csrpath) - result['key'] = mw.readFile(src_keypath) - - mw.restartWeb() - return mw.returnJson(data['status'], data['msg'], result) - - def getAcmeLogsApi(self): - log_file = mw.getRunDir() + '/logs/acme.log' - if not os.path.exists(log_file): - mw.execShell('touch ' + log_file) - return mw.returnJson(True, 'OK', log_file) - - def createAcmeApi(self): - siteName = request.form.get('siteName', '') - domains = request.form.get('domains', '') - force = request.form.get('force', '') - renew = request.form.get('renew', '') - email_args = request.form.get('email', '') - - domains = json.loads(domains) - email = mw.M('users').getField('email') - if email_args.strip() != '': - mw.M('users').setField('email', email_args) - email = email_args - - if not len(domains): - return mw.returnJson(False, '请选择域名') - - file = self.getHostConf(siteName) - if os.path.exists(file): - siteConf = mw.readFile(file) - if siteConf.find('301-END') != -1: - return mw.returnJson(False, '检测到您的站点做了301重定向设置,请先关闭重定向!') - - # 检测存在反向代理 - data_path = self.getProxyDataPath(siteName) - data_content = mw.readFile(data_path) - if data_content != False: - try: - data = json.loads(data_content) - except: - pass - for proxy in data: - proxy_dir = "{}/{}".format(self.proxyPath, siteName) - proxy_dir_file = proxy_dir + '/' + proxy['id'] + '.conf' - if os.path.exists(proxy_dir_file): - return mw.returnJson(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!') - - siteInfo = mw.M('sites').where( - 'name=?', (siteName,)).field('id,name,path').find() - path = self.getSitePath(siteName) - if path == '': - return mw.returnJson(False, '【'+siteName+'】配置文件,异常!') - - srcPath = siteInfo['path'] - - # 检测acme是否安装 - acme_dir = mw.getAcmeDir() - if not os.path.exists(acme_dir): - try: - mw.execShell("curl -sS curl https://get.acme.sh | sh") - except: - pass - if not os.path.exists(acme_dir): - return mw.returnJson(False, '尝试自动安装ACME失败,请通过以下命令尝试手动安装

安装命令: curl https://get.acme.sh | sh

') - - # 避免频繁执行 - checkAcmeRun = mw.execShell('ps -ef|grep acme.sh |grep -v grep') - if checkAcmeRun[0] != '': - return mw.returnJson(False, '正在申请或更新SSL中...') - - if force == 'true': - force_bool = True - - if renew == 'true': - execStr = acme_dir + "/acme.sh --renew --yes-I-know-dns-manual-mode-enough-go-ahead-please" - else: - execStr = acme_dir + "/acme.sh --issue --force" - - # 确定主域名顺序 - domainsTmp = [] - if siteName in domains: - domainsTmp.append(siteName) - for domainTmp in domains: - if domainTmp == siteName: - continue - domainsTmp.append(domainTmp) - domains = domainsTmp - - domainCount = 0 - for domain in domains: - if mw.checkIp(domain): - continue - if domain.find('*.') != -1: - return mw.returnJson(False, '泛域名不能使用【文件验证】的方式申请证书!') - execStr += ' -w ' + path - execStr += ' -d ' + domain - domainCount += 1 - if domainCount == 0: - return mw.returnJson(False, '请选择域名(不包括IP地址与泛域名)!') - - log_file = mw.getRunDir() + '/logs/acme.log' - mw.writeFile(log_file, "开始ACME申请...\n", "wb+") - cmd = 'export ACCOUNT_EMAIL=' + email + ' && ' + \ - execStr + ' >> ' + log_file - # print(domains) - # print(cmd) - result = mw.execShell(cmd) - - src_path = mw.getAcmeDomainDir(domains[0]) - src_cert = src_path + '/fullchain.cer' - src_key = src_path + '/' + domains[0] + '.key' - src_cert.replace("\\*", "*") - - msg = '签发失败,您尝试申请证书的失败次数已达上限!

1、检查域名是否绑定到对应站点

\ -

2、检查域名是否正确解析到本服务器,或解析还未完全生效

\ -

3、如果您的站点设置了反向代理,或使用了CDN,请先将其关闭

\ -

4、如果您的站点设置了301重定向,请先将其关闭

\ -

5、如果以上检查都确认没有问题,请尝试更换DNS服务商

' - if not os.path.exists(src_cert): - data = {} - data['err'] = result - data['out'] = result[0] - data['msg'] = msg - data['result'] = {} - if result[1].find('new-authz error:') != -1: - data['result'] = json.loads( - re.search("{.+}", result[1]).group()) - if data['result']['status'] == 429: - data['msg'] = msg - data['status'] = False - return mw.getJson(data) - - dst_path = self.sslDir + '/' + siteName - dst_cert = dst_path + "/fullchain.pem" # 生成证书路径 - dst_key = dst_path + "/privkey.pem" # 密钥文件路径 - - if not os.path.exists(dst_path): - mw.execShell("mkdir -p " + dst_path) - - mw.buildSoftLink(src_cert, dst_cert, True) - mw.buildSoftLink(src_key, dst_key, True) - mw.execShell('echo "acme" > "' + dst_path + '/README"') - - # 写入配置文件 - result = self.setSslConf(siteName) - if not result['status']: - return mw.getJson(result) - result['csr'] = mw.readFile(src_cert) - result['key'] = mw.readFile(src_key) - - mw.restartWeb() - return mw.returnJson(True, '证书已更新!', result) - - def httpToHttpsApi(self): - siteName = request.form.get('siteName', '') - return self.httpToHttps(siteName) - - def httpToHttps(self, site_name): - file = self.getHostConf(site_name) - conf = mw.readFile(file) - if conf: - if conf.find('ssl_certificate') == -1: - return mw.returnJson(False, '当前未开启SSL') - to = "#error_page 404/404.html;\n\ - #HTTP_TO_HTTPS_START\n\ - if ($server_port !~ 443){\n\ - rewrite ^(/.*)$ https://$host$1 permanent;\n\ - }\n\ - #HTTP_TO_HTTPS_END" - conf = conf.replace('#error_page 404/404.html;', to) - mw.writeFile(file, conf) - - mw.restartWeb() - return mw.returnJson(True, '设置成功!') - - def closeToHttpsApi(self): - siteName = request.form.get('siteName', '') - return self.closeToHttps(siteName) - - def closeToHttps(self, site_name): - file = self.getHostConf(site_name) - conf = mw.readFile(file) - if conf: - rep = r"\n\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END" - conf = re.sub(rep, '', conf) - rep = r"\s+if.+server_port.+\n.+\n\s+\s*}" - conf = re.sub(rep, '', conf) - mw.writeFile(file, conf) - - mw.restartWeb() - return mw.returnJson(True, '关闭HTTPS跳转成功!') - - def getIndexApi(self): - sid = request.form.get('id', '') - data = {} - index = self.getIndex(sid) - data['index'] = index - return mw.getJson(data) - - def setIndexApi(self): - sid = request.form.get('id', '') - index = request.form.get('index', '') - return self.setIndex(sid, index) - - def getLimitNetApi(self): - sid = request.form.get('id', '') - return self.getLimitNet(sid) - - def saveLimitNetApi(self): - sid = request.form.get('id', '') - perserver = request.form.get('perserver', '') - perip = request.form.get('perip', '') - limit_rate = request.form.get('limit_rate', '') - return self.saveLimitNet(sid, perserver, perip, limit_rate) - - def closeLimitNetApi(self): - sid = request.form.get('id', '') - return self.closeLimitNet(sid) - - def getSecurityApi(self): - sid = request.form.get('id', '') - name = request.form.get('name', '') - return self.getSecurity(sid, name) - - def setSecurityApi(self): - fix = request.form.get('fix', '') - domains = request.form.get('domains', '') - status = request.form.get('status', '') - name = request.form.get('name', '') - none = request.form.get('none', '') - sid = request.form.get('id', '') - return self.setSecurity(sid, name, fix, domains, status, none) - - def getLogsApi(self): - siteName = request.form.get('siteName', '') - return self.getLogs(siteName) - - def getErrorLogsApi(self): - siteName = request.form.get('siteName', '') - return self.getErrorLogs(siteName) - - def getSitePhpVersionApi(self): - siteName = request.form.get('siteName', '') - return self.getSitePhpVersion(siteName) - - def getHostConfApi(self): - siteName = request.form.get('siteName', '') - host = self.getHostConf(siteName) - return mw.getJson({'host': host}) - - def saveHostConfApi(self): - path = request.form.get('path', '') - data = request.form.get('data', '') - encoding = request.form.get('encoding', '') - - import files_api - - mw.backFile(path) - save_ret_data = files_api.files_api().saveBody(path, data, encoding) - rdata = json.loads(save_ret_data) - - if rdata['status']: - isError = mw.checkWebConfig() - if isError != True: - mw.restoreFile(path) - return mw.returnJson(False, 'ERROR: 检测到配置文件有错误,请先排除后再操作

' + isError.replace("\n", '
') + '
') - mw.restartWeb() - mw.removeBackFile(path) - return save_ret_data - - def getRewriteConfApi(self): - siteName = request.form.get('siteName', '') - rewrite = self.getRewriteConf(siteName) - return mw.getJson({'rewrite': rewrite}) - - def getRewriteTplApi(self): - tplname = request.form.get('tplname', '') - file = mw.getRunDir() + '/rewrite/nginx/' + tplname + '.conf' - if not os.path.exists(file): - return mw.returnJson(False, '模版不存在!') - return mw.returnJson(True, 'OK', file) - - def getRewriteListApi(self): - rlist = self.getRewriteList() - return mw.getJson(rlist) - - def getRootDirApi(self): - data = {} - data['dir'] = mw.getWwwDir() - return mw.getJson(data) - - def setEndDateApi(self): - sid = request.form.get('id', '') - edate = request.form.get('edate', '') - return self.setEndDate(sid, edate) - - def addApi(self): - webname = request.form.get('webinfo', '') - ps = request.form.get('ps', '') - path = request.form.get('path', '') - version = request.form.get('version', '') - port = request.form.get('port', '') - return self.add(webname, port, ps, path, version) - - def checkWebStatusApi(self): - ''' - 创建站点检查web服务 - ''' - if not mw.isInstalledWeb(): - return mw.returnJson(False, '请安装并启动OpenResty服务!') - - # 这个快点 - pid = mw.getServerDir() + '/openresty/nginx/logs/nginx.pid' - if not os.path.exists(pid): - return mw.returnJson(False, '请启动OpenResty服务!') - - # path = mw.getServerDir() + '/openresty/init.d/openresty' - # data = mw.execShell(path + " status") - # if data[0].strip().find('stopped') != -1: - # return mw.returnJson(False, '请启动OpenResty服务!') - - # import plugins_api - # data = plugins_api.plugins_api().run('openresty', 'status') - # if data[0].strip() == 'stop': - # return mw.returnJson(False, '请启动OpenResty服务!') - - return mw.returnJson(True, 'OK') - - def addDomainApi(self): - isError = mw.checkWebConfig() - if isError != True: - return mw.returnJson(False, 'ERROR: 检测到配置文件有错误,请先排除后再操作

' + isError.replace("\n", '
') + '
') - - domain = request.form.get('domain', '') - webname = request.form.get('webname', '') - pid = request.form.get('id', '') - return self.addDomain(domain, webname, pid) - - def addDomain(self, domain, webname, pid): - if len(domain) < 3: - return mw.returnJson(False, '域名不能为空!') - domains = domain.split(',') - for domain in domains: - if domain == "": - continue - domain = domain.split(':') - # print domain - domain_name = self.toPunycode(domain[0]) - domain_port = '80' - - reg = r"^([\w\-\*]{1,100}\.){1,4}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$" - if not re.match(reg, domain_name): - return mw.returnJson(False, '域名格式不正确!') - - if len(domain) == 2: - domain_port = domain[1] - if domain_port == "": - domain_port = "80" - - if not mw.checkPort(domain_port): - return mw.returnJson(False, '端口范围不合法!') - - opid = mw.M('domain').where( - "name=? AND (port=? OR pid=?)", (domain_name, domain_port, pid,)).getField('pid') - # print(opid) - is_bind = False - if type(opid) == list and len(opid) > 0: - is_bind = True - - if type(opid) == int and opid > 0: - is_bind = True - - if type(opid) == str and int(opid) > 0: - is_bind = True - - if is_bind: - return mw.returnJson(False, '您添加的域名[{}],已绑定!'.format(domain_name)) - # if mw.M('sites').where('id=?', (opid,)).count(): - # return mw.returnJson(False, '指定域名已绑定过!') - # mw.M('domain').where('pid=?', (opid,)).delete() - - if mw.M('binding').where('domain=?', (domain,)).count(): - return mw.returnJson(False, '您添加的域名,子目录已绑定!') - - self.nginxAddDomain(webname, domain_name, domain_port) - - mw.restartWeb() - msg = mw.getInfo('网站[{1}]添加域名[{2}]成功!', (webname, domain_name)) - mw.writeLog('网站管理', msg) - mw.M('domain').add('pid,name,port,addtime', - (pid, domain_name, domain_port, mw.getDate())) - - self.runHook('site_cb', 'add') - return mw.returnJson(True, '域名添加成功!') - - def addDirBindApi(self): - pid = request.form.get('id', '') - domain = request.form.get('domain', '') - dirName = request.form.get('dirName', '') - tmp = domain.split(':') - domain = tmp[0] - port = '80' - if len(tmp) > 1: - port = tmp[1] - if dirName == '': - mw.returnJson(False, '目录不能为空!') - - reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$" - if not re.match(reg, domain): - return mw.returnJson(False, '主域名格式不正确!') - - siteInfo = mw.M('sites').where( - "id=?", (pid,)).field('id,path,name').find() - webdir = siteInfo['path'] + '/' + dirName - - if mw.M('binding').where("domain=?", (domain,)).count() > 0: - return mw.returnJson(False, '您添加的域名已存在!') - if mw.M('domain').where("name=?", (domain,)).count() > 0: - return mw.returnJson(False, '您添加的域名已存在!') - - filename = self.getHostConf(siteInfo['name']) - conf = mw.readFile(filename) - if conf: - rep = r"enable-php-([0-9]{2,3})\.conf" - tmp = re.search(rep, conf).groups() - version = tmp[0] - - source_dirbind_tpl = mw.getRunDir() + '/data/tpl/nginx_dirbind.conf' - content = mw.readFile(source_dirbind_tpl) - content = content.replace('{$PORT}', port) - content = content.replace('{$PHPVER}', version) - content = content.replace('{$DIRBIND}', domain) - content = content.replace('{$ROOT_DIR}', webdir) - content = content.replace('{$SERVER_MAIN}', siteInfo['name']) - content = content.replace('{$OR_REWRITE}', self.rewritePath) - content = content.replace('{$PHP_DIR}', self.setupPath + '/php') - content = content.replace('{$LOGPATH}', mw.getLogsDir()) - - conf += "\r\n" + content - mw.backFile(filename) - mw.writeFile(filename, conf) - conf = mw.readFile(filename) - - # 检查配置是否有误 - isError = mw.checkWebConfig() - if isError != True: - mw.restoreFile(filename) - return mw.returnJson(False, 'ERROR:
' + isError.replace("\n", '
') + '
') - - mw.M('binding').add('pid,domain,port,path,addtime', - (pid, domain, port, dirName, mw.getDate())) - - msg = mw.getInfo('网站[{1}]子目录[{2}]绑定到[{3}]', - (siteInfo['name'], dirName, domain)) - mw.writeLog('网站管理', msg) - mw.restartWeb() - mw.removeBackFile(filename) - return mw.returnJson(True, '添加成功!') - - def delDirBindApi(self): - mid = request.form.get('id', '') - binding = mw.M('binding').where( - "id=?", (mid,)).field('id,pid,domain,path').find() - siteName = mw.M('sites').where( - "id=?", (binding['pid'],)).getField('name') - - filename = self.getHostConf(siteName) - conf = mw.readFile(filename) - if conf: - rep = "\s*.+BINDING-" + \ - binding['domain'] + \ - "-START(.|\n)+BINDING-" + binding['domain'] + "-END" - conf = re.sub(rep, '', conf) - mw.writeFile(filename, conf) - - mw.M('binding').where("id=?", (mid,)).delete() - - filename = self.getDirBindRewrite(siteName, binding['path']) - if os.path.exists(filename): - os.remove(filename) - mw.restartWeb() - msg = mw.getInfo('删除网站[{1}]子目录[{2}]绑定', - (siteName, binding['path'])) - mw.writeLog('网站管理', msg) - return mw.returnJson(True, '删除成功!') - - # 取子目录Rewrite - def getDirBindRewriteApi(self): - mid = request.form.get('id', '') - add = request.form.get('add', '0') - find = mw.M('binding').where( - "id=?", (mid,)).field('id,pid,domain,path').find() - site = mw.M('sites').where( - "id=?", (find['pid'],)).field('id,name,path').find() - - filename = self.getDirBindRewrite(site['name'], find['path']) - if add == '1': - mw.writeFile(filename, '') - file = self.getHostConf(site['name']) - conf = mw.readFile(file) - domain = find['domain'] - rep = "\n#BINDING-" + domain + \ - "-START(.|\n)+BINDING-" + domain + "-END" - tmp = re.search(rep, conf).group() - dirConf = tmp.replace('rewrite/' + site['name'] + '.conf;', 'rewrite/' + site[ - 'name'] + '_' + find['path'] + '.conf;') - conf = conf.replace(tmp, dirConf) - mw.writeFile(file, conf) - data = {} - data['rewrite_dir'] = self.rewritePath - data['status'] = False - if os.path.exists(filename): - data['status'] = True - data['data'] = mw.readFile(filename) - data['rlist'] = [] - for ds in os.listdir(self.rewritePath): - if ds[0:1] == '.': - continue - if ds == 'list.txt': - continue - data['rlist'].append(ds[0:len(ds) - 5]) - data['filename'] = filename - return mw.getJson(data) - - # 修改物理路径 - def setPathApi(self): - mid = request.form.get('id', '') - path = request.form.get('path', '') - - path = self.getPath(path) - if path == "" or mid == '0': - return mw.returnJson(False, "目录不能为空!") - - import files_api - if not files_api.files_api().checkDir(path): - return mw.returnJson(False, "不能以系统关键目录作为站点目录") - - siteFind = mw.M("sites").where( - "id=?", (mid,)).field('path,name').find() - if siteFind["path"] == path: - return mw.returnJson(False, "与原路径一致,无需修改!") - file = self.getHostConf(siteFind['name']) - conf = mw.readFile(file) - if conf: - conf = conf.replace(siteFind['path'], path) - mw.writeFile(file, conf) - - # 创建basedir - # userIni = path + '/.user.ini' - # if os.path.exists(userIni): - # mw.execShell("chattr -i " + userIni) - # mw.writeFile(userIni, 'open_basedir=' + path + '/:/tmp/:/proc/') - # mw.execShell('chmod 644 ' + userIni) - # mw.execShell('chown root:root ' + userIni) - # mw.execShell('chattr +i ' + userIni) - - mw.restartWeb() - mw.M("sites").where("id=?", (mid,)).setField('path', path) - msg = mw.getInfo('修改网站[{1}]物理路径成功!', (siteFind['name'],)) - mw.writeLog('网站管理', msg) - return mw.returnJson(True, "设置成功!") - - # 设置当前站点运行目录 - def setSiteRunPathApi(self): - mid = request.form.get('id', '') - runPath = request.form.get('runPath', '') - siteName = mw.M('sites').where('id=?', (mid,)).getField('name') - sitePath = mw.M('sites').where('id=?', (mid,)).getField('path') - - newPath = sitePath + runPath - - # 处理Nginx - filename = self.getHostConf(siteName) - if os.path.exists(filename): - conf = mw.readFile(filename) - rep = r'\s*root\s*(.+);' - path = re.search(rep, conf).groups()[0] - conf = conf.replace(path, newPath) - mw.writeFile(filename, conf) - - self.setDirUserINI(sitePath, runPath) - - mw.restartWeb() - return mw.returnJson(True, '设置成功!') - - # 设置目录加密 - def setHasPwdApi(self): - username = request.form.get('username', '') - password = request.form.get('password', '') - siteName = request.form.get('siteName', '') - mid = request.form.get('id', '') - - if len(username.strip()) == 0 or len(password.strip()) == 0: - return mw.returnJson(False, '用户名或密码不能为空!') - - if siteName == '': - siteName = mw.M('sites').where('id=?', (mid,)).getField('name') - - # self.closeHasPwd(get) - filename = self.passPath + '/' + siteName + '.pass' - # print(filename) - passconf = username + ':' + mw.hasPwd(password) - - if siteName == 'phpmyadmin': - configFile = self.getHostConf('phpmyadmin') - else: - configFile = self.getHostConf(siteName) - - # 处理Nginx配置 - conf = mw.readFile(configFile) - if conf: - rep = '#error_page 404 /404.html;' - if conf.find(rep) == -1: - rep = '#error_page 404/404.html;' - data = ''' - #AUTH_START - auth_basic "Authorization"; - auth_basic_user_file %s; - #AUTH_END''' % (filename,) - conf = conf.replace(rep, rep + data) - mw.writeFile(configFile, conf) - # 写密码配置 - passDir = self.passPath - if not os.path.exists(passDir): - mw.execShell('mkdir -p ' + passDir) - mw.writeFile(filename, passconf) - - mw.restartWeb() - msg = mw.getInfo('设置网站[{1}]为需要密码认证!', (siteName,)) - mw.writeLog("网站管理", msg) - return mw.returnJson(True, '设置成功!') - - # 取消目录加密 - def closeHasPwdApi(self): - siteName = request.form.get('siteName', '') - mid = request.form.get('id', '') - if siteName == '': - siteName = mw.M('sites').where('id=?', (mid,)).getField('name') - - if siteName == 'phpmyadmin': - configFile = self.getHostConf('phpmyadmin') - else: - configFile = self.getHostConf(siteName) - - if os.path.exists(configFile): - conf = mw.readFile(configFile) - rep = r"\n\s*#AUTH_START(.|\n){1,200}#AUTH_END" - conf = re.sub(rep, '', conf) - mw.writeFile(configFile, conf) - - mw.restartWeb() - msg = mw.getInfo('清除网站[{1}]的密码认证!', (siteName,)) - mw.writeLog("网站管理", msg) - return mw.returnJson(True, '设置成功!') - - def delDomainApi(self): - domain = request.form.get('domain', '') - webname = request.form.get('webname', '') - port = request.form.get('port', '') - pid = request.form.get('id', '') - - find = mw.M('domain').where("pid=? AND name=?", - (pid, domain)).field('id,name').find() - - domain_count = mw.M('domain').where("pid=?", (pid,)).count() - if domain_count == 1: - return mw.returnJson(False, '最后一个域名不能删除!') - - file = self.getHostConf(webname) - conf = mw.readFile(file) - if conf: - # 删除域名 - rep = r"server_name\s+(.+);" - tmp = re.search(rep, conf).group() - newServerName = tmp.replace(' ' + domain + ';', ';') - newServerName = newServerName.replace(' ' + domain + ' ', ' ') - conf = conf.replace(tmp, newServerName) - - # 删除端口 - rep = r"listen\s+([0-9]+);" - tmp = re.findall(rep, conf) - port_count = mw.M('domain').where( - 'pid=? AND port=?', (pid, port)).count() - if mw.inArray(tmp, port) == True and port_count < 2: - rep = r"\n*\s+listen\s+" + port + ";" - conf = re.sub(rep, '', conf) - # 保存配置 - mw.writeFile(file, conf) - - mw.M('domain').where("id=?", (find['id'],)).delete() - msg = mw.getInfo('网站[{1}]删除域名[{2}]成功!', (webname, domain)) - mw.writeLog('网站管理', msg) - mw.restartWeb() - return mw.returnJson(True, '站点删除成功!') - - def deleteApi(self): - sid = request.form.get('id', '') - webname = request.form.get('webname', '') - path = request.form.get('path', '0') - return self.delete(sid, webname, path) - - # 操作 重定向配置 - def operateRedirectConf(self, siteName, method='start'): - vhost_file = self.vhostPath + '/' + siteName + '.conf' - content = mw.readFile(vhost_file) - - cnf_301 = '''#301-START - include %s/*.conf; - #301-END''' % (self.getRedirectPath( siteName)) - - cnf_301_source = '#301-START' - # print('operateRedirectConf', content.find('#301-END')) - if content.find('#301-END') != -1: - if method == 'stop': - rep = '#301-START(\n|.){1,500}#301-END' - content = re.sub(rep, '#301-START', content) - else: - if method == 'start': - content = re.sub(cnf_301_source, cnf_301, content) - - mw.writeFile(vhost_file, content) - - # get_redirect_status - def getRedirectApi(self): - _siteName = request.form.get("siteName", '') - - # read data base - data_path = self.getRedirectDataPath(_siteName) - data_content = mw.readFile(data_path) - if data_content == False: - mw.execShell("mkdir {}/{}".format(self.redirectPath, _siteName)) - return mw.returnJson(True, "", {"result": [], "count": 0}) - # get - # conf_path = "{}/{}/*.conf".format(self.redirectPath, siteName) - # conf_list = glob.glob(conf_path) - # if conf_list == []: - # return mw.returnJson(True, "", {"result": [], "count": 0}) - try: - data = json.loads(data_content) - except: - mw.execShell("rm -rf {}/{}".format(self.redirectPath, _siteName)) - return mw.returnJson(True, "", {"result": [], "count": 0}) - - # 处理301信息 - return mw.returnJson(True, "ok", {"result": data, "count": len(data)}) - - def getRedirectConfApi(self): - _siteName = request.form.get("siteName", '') - _id = request.form.get("id", '') - if _id == '' or _siteName == '': - return mw.returnJson(False, "必填项不能为空!") - - data = mw.readFile( - "{}/{}/{}.conf".format(self.redirectPath, _siteName, _id)) - if data == False: - return mw.returnJson(False, "获取失败!") - return mw.returnJson(True, "ok", {"result": data}) - - def saveRedirectConfApi(self): - _siteName = request.form.get("siteName", '') - _id = request.form.get("id", '') - _config = request.form.get("config", "") - if _id == '' or _siteName == '': - return mw.returnJson(False, "必填项不能为空!") - - _old_config = mw.readFile( - "{}/{}/{}.conf".format(self.redirectPath, _siteName, _id)) - if _old_config == False: - return mw.returnJson(False, "非法操作") - - mw.writeFile("{}/{}/{}.conf".format(self.redirectPath, - _siteName, _id), _config) - rule_test = mw.checkWebConfig() - if rule_test != True: - mw.writeFile("{}/{}/{}.conf".format(self.redirectPath, - _siteName, _id), _old_config) - return mw.returnJson(False, "OpenResty 配置测试不通过, 请重试: {}".format(rule_test)) - - self.operateRedirectConf(_siteName, 'start') - mw.restartWeb() - return mw.returnJson(True, "ok") - - # get redirect status - def setRedirectApi(self): - _siteName = request.form.get("siteName", '') - # from (example.com / /test/) - _from = request.form.get("from", '') - _to = request.form.get("to", '') # redirect to - _type = request.form.get("type", '') # path / domain - _rType = request.form.get("r_type", '') # redirect type - _keepPath = request.form.get("keep_path", '') # keep path - - if _siteName == '' or _from == '' or _to == '' or _type == '' or _rType == '': - return mw.returnJson(False, "必填项不能为空!") - - data_path = self.getRedirectDataPath(_siteName) - data_content = mw.readFile( - data_path) if os.path.exists(data_path) else "" - data = json.loads(data_content) if data_content != "" else [] - - _rTypeCode = 0 if _rType == "301" else 1 - _typeCode = 0 if _type == "path" else 1 - _keepPath = 1 if _keepPath == "1" else 0 - - # check if domain exists in site - if _typeCode == 1: - pid = mw.M('domain').where("name=?", (_siteName,)).field( - 'id,pid,name,port,addtime').select() - site_domain_lists = mw.M('domain').where("pid=?", (pid[0]['pid'],)).field( - 'name').select() - found = False - for item in site_domain_lists: - if item['name'] == _from: - found = True - break - if found == False: - return mw.returnJson(False, "域名不存在!") - - file_content = "" - # path - if _typeCode == 0: - redirect_type = "permanent" if _rTypeCode == 0 else "redirect" - if not _from.startswith("/"): - _from = "/{}".format(_from) - if _keepPath == 1: - _to = "{}$1".format(_to) - _from = "{}(.*)".format(_from) - file_content = "rewrite ^{} {} {};".format( - _from, _to, redirect_type) - # domain - else: - if _keepPath == 1: - _to = "{}$request_uri".format(_to) - - redirect_type = "301" if _rTypeCode == 0 else "302" - _if = "if ($host ~ '^{}')".format(_from) - _return = "return {} {}; ".format(redirect_type, _to) - file_content = _if + "{\r\n " + _return + "\r\n}" - - _id = mw.md5("{}+{}".format(file_content, _siteName)) - - # 防止规则重复 - for item in data: - if item["r_from"] == _from: - return mw.returnJson(False, "重复的规则!") - - rep = r"http(s)?\:\/\/([a-zA-Z0-9][-a-zA-Z0-9]{0,62}\.)+([a-zA-Z0-9][a-zA-Z0-9]{0,62})+.?" - if not re.match(rep, _to): - return mw.returnJson(False, "错误的目标地址") - - # write data json file - data.append({"r_from": _from, "type": _typeCode, "r_type": _rTypeCode, - "r_to": _to, 'keep_path': _keepPath, 'id': _id}) - mw.writeFile(data_path, json.dumps(data)) - mw.writeFile("{}/{}.conf".format(self.getRedirectPath(_siteName), _id), file_content) - - self.operateRedirectConf(_siteName, 'start') - mw.restartWeb() - return mw.returnJson(True, "ok") - - # 删除指定重定向 - def delRedirectApi(self): - _siteName = request.form.get("siteName", '') - _id = request.form.get("id", '') - if _id == '' or _siteName == '': - return mw.returnJson(False, "必填项不能为空!") - - try: - data_path = self.getRedirectDataPath(_siteName) - data_content = mw.readFile( - data_path) if os.path.exists(data_path) else "" - data = json.loads(data_content) if data_content != "" else [] - for item in data: - if item["id"] == _id: - data.remove(item) - break - # write database - mw.writeFile(data_path, json.dumps(data)) - # data is empty ,should stop - if len(data) == 0: - self.operateRedirectConf(_siteName, 'stop') - # remove conf file - mw.execShell( - "rm -rf {}/{}.conf".format(self.getRedirectPath(_siteName), _id)) - except: - return mw.returnJson(False, "删除失败!") - return mw.returnJson(True, "删除成功!") - - # 操作 反向代理配置 - def operateProxyConf(self, siteName, method='start'): - vhost_file = self.vhostPath + '/' + siteName + '.conf' - content = mw.readFile(vhost_file) - - proxy_cnf = '''#PROXY-START - include %s/*.conf; - #PROXY-END''' % (self.getProxyPath(siteName)) - - proxy_cnf_source = '#PROXY-START' - - if content.find('#PROXY-END') != -1: - if method == 'stop': - rep = '#PROXY-START(\n|.){1,500}#PROXY-END' - content = re.sub(rep, '#PROXY-START', content) - else: - if method == 'start': - content = re.sub(proxy_cnf_source, proxy_cnf, content) - - mw.writeFile(vhost_file, content) - - def getProxyConfApi(self): - _siteName = request.form.get("siteName", '') - _id = request.form.get("id", '') - if _id == '' or _siteName == '': - return mw.returnJson(False, "必填项不能为空!") - - conf_file = "{}/{}/{}.conf".format(self.proxyPath, _siteName, _id) - if not os.path.exists(conf_file): - conf_file = "{}/{}/{}.conf.txt".format( - self.proxyPath, _siteName, _id) - - data = mw.readFile(conf_file) - if data == False: - return mw.returnJson(False, "获取失败!") - return mw.returnJson(True, "ok", {"result": data}) - - def setProxyStatusApi(self): - _siteName = request.form.get("siteName", '') - _status = request.form.get("status", '') - _id = request.form.get("id", '') - if _status == '' or _siteName == '' or _id == '': - return mw.returnJson(False, "必填项不能为空!") - - conf_file = "{}/{}/{}.conf".format(self.proxyPath, _siteName, _id) - conf_txt = "{}/{}/{}.conf.txt".format(self.proxyPath, _siteName, _id) - - if _status == '1': - mw.execShell('mv ' + conf_txt + ' ' + conf_file) - else: - mw.execShell('mv ' + conf_file + ' ' + conf_txt) - - mw.restartWeb() - return mw.returnJson(True, "OK") - - def saveProxyConfApi(self): - _siteName = request.form.get("siteName", '') - _id = request.form.get("id", '') - _config = request.form.get("config", "") - if _id == '' or _siteName == '': - return mw.returnJson(False, "必填项不能为空!") - - proxy_file = "{}/{}/{}.conf".format(self.proxyPath, _siteName, _id) - mw.backFile(proxy_file) - mw.writeFile(proxy_file, _config) - rule_test = mw.checkWebConfig() - if rule_test != True: - mw.restoreFile(proxy_file) - mw.removeBackFile(proxy_file) - return mw.returnJson(False, "OpenResty 配置测试不通过, 请重试: {}".format(rule_test)) - - mw.removeBackFile(proxy_file) - self.operateRedirectConf(_siteName, 'start') - mw.restartWeb() - return mw.returnJson(True, "ok") - - # 读取 网站 反向代理列表 - def getProxyListApi(self): - _siteName = request.form.get('siteName', '') - - data_path = self.getProxyDataPath(_siteName) - data_content = mw.readFile(data_path) - - # not exists - if data_content == False: - mw.execShell("mkdir {}/{}".format(self.proxyPath, _siteName)) - return mw.returnJson(True, "", {"result": [], "count": 0}) - - try: - data = json.loads(data_content) - except: - mw.execShell("rm -rf {}/{}".format(self.proxyPath, _siteName)) - return mw.returnJson(True, "", {"result": [], "count": 0}) - - tmp = [] - for proxy in data: - proxy_dir = "{}/{}".format(self.proxyPath, _siteName) - proxy_dir_file = proxy_dir + '/' + proxy['id'] + '.conf' - if os.path.exists(proxy_dir_file): - proxy['status'] = True - else: - proxy['status'] = False - tmp.append(proxy) - - return mw.returnJson(True, "ok", {"result": data, "count": len(data)}) - - # 设置 网站 反向代理列表 - def setProxyApi(self): - _siteName = request.form.get('siteName', '') - _from = request.form.get('from', '') - _to = request.form.get('to', '') - _host = request.form.get('host', '') - _name = request.form.get('name', '') - _open_proxy = request.form.get('open_proxy', '') - _open_cache = request.form.get('open_cache', '') - _cache_time = request.form.get('cache_time', '') - _id = request.form.get('id', '') - - # print(_name, _siteName, _from, _to, _host) - if _name == "" or _siteName == "" or _from == "" or _to == "" or _host == "": - return mw.returnJson(False, "必填项不能为空") - - rep = r"http(s)?\:\/\/([a-zA-Z0-9][-a-zA-Z0-9]{0,62}\.)+([a-zA-Z0-9][a-zA-Z0-9]{0,62})+.?" - if not re.match(rep, _to): - return mw.returnJson(False, "错误的目标地址!") - - # get host from url - try: - if _host == "$host": - host_tmp = urlparse(_to) - _host = host_tmp.netloc - except: - return mw.returnJson(False, "错误的目标地址") - - proxy_site_path = self.getProxyDataPath(_siteName) - data_content = mw.readFile( - proxy_site_path) if os.path.exists(proxy_site_path) else "" - data = json.loads(data_content) if data_content != "" else [] - - proxy_action = 'add' - if _id == "": - _id = mw.md5("{}".format(_name)) - else: - proxy_action = 'edit' - - if proxy_action == "add": - for item in data: - if item["name"] == _name: - return mw.returnJson(False, "名称重复!!") - if item["from"] == _from: - return mw.returnJson(False, "代理目录已存在!!") - - tpl = "#PROXY-START\n\ -location ^~ {from} {\n\ - proxy_pass {to};\n\ - proxy_set_header Host {host};\n\ - proxy_ssl_server_name on;\n\ - proxy_set_header X-Real-IP $remote_addr;\n\ - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n\ - proxy_set_header REMOTE-HOST $remote_addr;\n\ - proxy_set_header Upgrade $http_upgrade;\n\ - proxy_set_header Connection $connection_upgrade;\n\ - proxy_http_version 1.1;\n\ - \n\ - add_header X-Cache $upstream_cache_status;\n\ - \n\ - {proxy_cache}\n\ -}\n\ -# PROXY-END" - - tpl_proxy_cache = r"\n\ - if ( $uri ~* \"\.(gif|png|jpg|css|js|woff|woff2)$\" )\n\ - {\n\ - expires {cache_time}m;\n\ - }\n\ - proxy_ignore_headers Set-Cookie Cache-Control expires;\n\ - proxy_cache mw_cache;\n\ - proxy_cache_key \"$host$uri$is_args$args\";\n\ - proxy_cache_valid 200 304 301 302 {cache_time}m;\n\ -" - - tpl_proxy_nocache = r"\n\ - set $static_files_app 0; \n\ - if ( $uri ~* \"\.(gif|png|jpg|css|js|woff|woff2)$\" )\n\ - {\n\ - set $static_files_app 1;\n\ - expires 12h;\n\ - }\n\ - if ( $static_files_app = 0 )\n\ - {\n\ - add_header Cache-Control no-cache;\n\ - }\n\ -" - - # replace - if _from[0] != '/': - _from = '/' + _from - tpl = tpl.replace("{from}", _from, 999) - tpl = tpl.replace("{to}", _to) - tpl = tpl.replace("{host}", _host, 999) - tpl = tpl.replace("{cache_time}", _cache_time, 999) - - if _open_cache == 'on': - tpl_proxy_cache = tpl_proxy_cache.replace( - "{cache_time}", _cache_time, 999) - tpl = tpl.replace("{proxy_cache}", tpl_proxy_cache, 999) - else: - tpl = tpl.replace("{proxy_cache}", tpl_proxy_nocache, 999) - - - conf_proxy = "{}/{}.conf".format(self.getProxyPath(_siteName), _id) - conf_bk = "{}/{}.conf.txt".format(self.getProxyPath(_siteName), _id) - mw.writeFile(conf_proxy, tpl) - - rule_test = mw.checkWebConfig() - if rule_test != True: - os.remove(conf_proxy) - return mw.returnJson(False, "OpenResty配置测试不通过, 请重试: {}".format(rule_test)) - - if proxy_action == "add": - # 添加代理 - _id = mw.md5("{}".format(_name)) - for item in data: - if item["name"] == _name: - return mw.returnJson(False, "名称重复!") - if item["from"] == _from: - return mw.returnJson(False, "代理目录已存在!") - data.append({ - "name": _name, - "from": _from, - "to": _to, - "host": _host, - "open_cache": _open_cache, - "open_proxy": _open_proxy, - "cache_time": _cache_time, - "id": _id, - }) - else: - # 修改代理 - dindex = -1 - for x in range(len(data)): - if data[x]["id"] == _id: - dindex = x - break - if dindex < 0: - return mw.returnJson(False, "异常请求") - data[dindex]['from'] = _from - data[dindex]['to'] = _to - data[dindex]['host'] = _host - data[dindex]['open_cache'] = _open_cache - data[dindex]['open_proxy'] = _open_proxy - data[dindex]['cache_time'] = _cache_time - - if _open_proxy != 'on': - os.rename(conf_proxy, conf_bk) - - mw.writeFile(proxy_site_path, json.dumps(data)) - self.operateProxyConf(_siteName, 'start') - mw.restartWeb() - return mw.returnJson(True, "ok", {"hash": _id}) - - def delProxyApi(self): - _siteName = request.form.get("siteName", '') - _id = request.form.get("id", '') - if _id == '' or _siteName == '': - return mw.returnJson(False, "必填项不能为空!") - - try: - data_path = self.getProxyDataPath(_siteName) - data_content = mw.readFile( - data_path) if os.path.exists(data_path) else "" - data = json.loads(data_content) if data_content != "" else [] - for item in data: - if item["id"] == _id: - data.remove(item) - break - # write database - mw.writeFile(data_path, json.dumps(data)) - - # data is empty,should stop - if len(data) == 0: - self.operateProxyConf(_siteName, 'stop') - # remove conf file - cmd = "rm -rf {}/{}.conf*".format( - self.getProxyPath(_siteName), _id) - mw.execShell(cmd) - except: - return mw.returnJson(False, "删除反代失败!") - - mw.restartWeb() - return mw.returnJson(True, "删除反代成功!") - - def getSiteTypesApi(self): - # 取网站分类 - data = mw.M("site_types").field("id,name").order("id asc").select() - data.insert(0, {"id": 0, "name": "默认分类"}) - return mw.getJson(data) - - def getSiteDocApi(self): - stype = request.form.get('type', '0').strip() - vlist = [] - vlist.append('') - vlist.append(mw.getServerDir() + - '/openresty/nginx/html/index.html') - vlist.append(mw.getServerDir() + '/openresty/nginx/html/404.html') - vlist.append(mw.getServerDir() + - '/openresty/nginx/html/index.html') - vlist.append(mw.getServerDir() + '/web_conf/stop/index.html') - data = {} - data['path'] = vlist[int(stype)] - return mw.returnJson(True, 'ok', data) - - def addSiteTypeApi(self): - name = request.form.get('name', '').strip() - if not name: - return mw.returnJson(False, "分类名称不能为空") - if len(name) > 18: - return mw.returnJson(False, "分类名称长度不能超过6个汉字或18位字母") - if mw.M('site_types').count() >= 10: - return mw.returnJson(False, '最多添加10个分类!') - if mw.M('site_types').where('name=?', (name,)).count() > 0: - return mw.returnJson(False, "指定分类名称已存在!") - mw.M('site_types').add("name", (name,)) - return mw.returnJson(True, '添加成功!') - - def removeSiteTypeApi(self): - mid = request.form.get('id', '') - if mw.M('site_types').where('id=?', (mid,)).count() == 0: - return mw.returnJson(False, "指定分类不存在!") - mw.M('site_types').where('id=?', (mid,)).delete() - mw.M("sites").where("type_id=?", (mid,)).save("type_id", (0,)) - return mw.returnJson(True, "分类已删除!") - - def modifySiteTypeNameApi(self): - # 修改网站分类名称 - name = request.form.get('name', '').strip() - mid = request.form.get('id', '') - if not name: - return mw.returnJson(False, "分类名称不能为空") - if len(name) > 18: - return mw.returnJson(False, "分类名称长度不能超过6个汉字或18位字母") - if mw.M('site_types').where('id=?', (mid,)).count() == 0: - return mw.returnJson(False, "指定分类不存在!") - mw.M('site_types').where('id=?', (mid,)).setField('name', name) - return mw.returnJson(True, "修改成功!") - - def setSiteTypeApi(self): - # 设置指定站点的分类 - site_ids = request.form.get('site_ids', '') - mid = request.form.get('id', '') - site_ids = json.loads(site_ids) - for sid in site_ids: - print(mw.M('sites').where('id=?', (sid,)).setField('type_id', mid)) - return mw.returnJson(True, "设置成功!") - - ##### ----- end ----- ### - - # 域名编码转换 - def toPunycode(self, domain): - import re - if sys.version_info[0] == 2: - domain = domain.encode('utf8') - tmp = domain.split('.') - newdomain = '' - for dkey in tmp: - # 匹配非ascii字符 - match = re.search(u"[\x80-\xff]+", dkey) - if not match: - newdomain += dkey + '.' - else: - newdomain += 'xn--' + \ - dkey.decode('utf-8').encode('punycode') + '.' - return newdomain[0:-1] - - # 中文路径处理 - def toPunycodePath(self, path): - if sys.version_info[0] == 2: - path = path.encode('utf-8') - if os.path.exists(path): - return path - import re - match = re.search(u"[\x80-\xff]+", path) - if not match: - return path - npath = '' - for ph in path.split('/'): - npath += '/' + self.toPunycode(ph) - return npath.replace('//', '/') - - # 路径处理 - def getPath(self, path): - if path[-1] == '/': - return path[0:-1] - return path - - def getSitePath(self, siteName): - file = self.getHostConf(siteName) - if os.path.exists(file): - conf = mw.readFile(file) - rep = r'\s*root\s*(.+);' - find_cnf = re.search(rep, conf) - if not find_cnf: - return '' - path = find_cnf.groups()[0] - return path - return '' - - # 取当站点前运行目录 - def getSiteRunPath(self, mid): - siteName = mw.M('sites').where('id=?', (mid,)).getField('name') - sitePath = mw.M('sites').where('id=?', (mid,)).getField('path') - path = sitePath - - filename = self.getHostConf(siteName) - if os.path.exists(filename): - conf = mw.readFile(filename) - rep = r'\s*root\s*(.+);' - path = re.search(rep, conf).groups()[0] - - data = {} - if sitePath == path: - data['runPath'] = '/' - else: - data['runPath'] = path.replace(sitePath, '') - - dirnames = [] - dirnames.append('/') - for filename in os.listdir(sitePath): - try: - filePath = sitePath + '/' + filename - if os.path.islink(filePath): - continue - if os.path.isdir(filePath): - dirnames.append('/' + filename) - except: - pass - - data['dirs'] = dirnames - return data - - def getHostConf(self, siteName): - return self.vhostPath + '/' + siteName + '.conf' - - def getRewriteConf(self, siteName): - return self.rewritePath + '/' + siteName + '.conf' - - def getRedirectDataPath(self, siteName): - return "{}/{}/data.json".format(self.redirectPath, siteName) - - def getRedirectPath(self, siteName): - return "{}/{}".format(self.redirectPath, siteName) - - def getProxyDataPath(self, siteName): - return "{}/{}/data.json".format(self.proxyPath, siteName) - - def getProxyPath(self, siteName): - return "{}/{}".format(self.proxyPath, siteName) - - def getDirBindRewrite(self, siteName, dirname): - return self.rewritePath + '/' + siteName + '_' + dirname + '.conf' - - def getIndexConf(self): - return mw.getServerDir() + '/openresty/nginx/conf/nginx.conf' - - def getDomain(self, pid): - _list = mw.M('domain').where("pid=?", (pid,)).field( - 'id,pid,name,port,addtime').select() - return mw.getJson(_list) - - def getLogs(self, siteName): - logPath = mw.getLogsDir() + '/' + siteName + '.log' - if not os.path.exists(logPath): - return mw.returnJson(False, '日志为空') - return mw.returnJson(True, mw.getLastLine(logPath, 100)) - - def getErrorLogs(self, siteName): - logPath = mw.getLogsDir() + '/' + siteName + '.error.log' - if not os.path.exists(logPath): - return mw.returnJson(False, '日志为空') - return mw.returnJson(True, mw.getLastLine(logPath, 100)) - - # 取日志状态 - def getLogsStatus(self, siteName): - filename = self.getHostConf(siteName) - conf = mw.readFile(filename) - if conf.find('#ErrorLog') != -1: - return False - if conf.find("access_log off") != -1: - return False - return True - - # 取目录加密状态 - def getHasPwd(self, siteName): - filename = self.getHostConf(siteName) - conf = mw.readFile(filename) - if conf.find('#AUTH_START') != -1: - return True - return False - - def getSitePhpVersion(self, siteName): - conf = mw.readFile(self.getHostConf(siteName)) - rep = r"enable-php-(.*)\.conf" - find_php_cnf = re.search(rep, conf) - - def_pver = '00' - if find_php_cnf: - tmp = find_php_cnf.groups() - def_pver = tmp[0] - - data = {} - data['phpversion'] = def_pver - return mw.getJson(data) - - def getIndex(self, sid): - siteName = mw.M('sites').where("id=?", (sid,)).getField('name') - file = self.getHostConf(siteName) - conf = mw.readFile(file) - rep = r"\s+index\s+(.+);" - tmp = re.search(rep, conf).groups() - return tmp[0].replace(' ', ',') - - def setIndex(self, sid, index): - if index.find('.') == -1: - return mw.returnJson(False, '默认文档格式不正确,例:index.html') - - index = index.replace(' ', '') - index = index.replace(',,', ',') - - if len(index) < 3: - return mw.returnJson(False, '默认文档不能为空!') - - siteName = mw.M('sites').where("id=?", (sid,)).getField('name') - index_l = index.replace(",", " ") - file = self.getHostConf(siteName) - conf = mw.readFile(file) - if conf: - rep = r"\s+index\s+.+;" - conf = re.sub(rep, "\n\tindex " + index_l + ";", conf) - mw.writeFile(file, conf) - - mw.writeLog('TYPE_SITE', 'SITE_INDEX_SUCCESS', (siteName, index_l)) - return mw.returnJson(True, '设置成功!') - - def getLimitNet(self, sid): - siteName = mw.M('sites').where("id=?", (sid,)).getField('name') - filename = self.getHostConf(siteName) - # 站点总并发 - data = {} - conf = mw.readFile(filename) - try: - rep = r"\s+limit_conn\s+perserver\s+([0-9]+);" - tmp = re.search(rep, conf).groups() - data['perserver'] = int(tmp[0]) - - # IP并发限制 - rep = r"\s+limit_conn\s+perip\s+([0-9]+);" - tmp = re.search(rep, conf).groups() - data['perip'] = int(tmp[0]) - - # 请求并发限制 - rep = r"\s+limit_rate\s+([0-9]+)\w+;" - tmp = re.search(rep, conf).groups() - data['limit_rate'] = int(tmp[0]) - except: - data['perserver'] = 0 - data['perip'] = 0 - data['limit_rate'] = 0 - - return mw.getJson(data) - - def checkIndexConf(self): - limit = self.getIndexConf() - nginxConf = mw.readFile(limit) - limitConf = "limit_conn_zone $binary_remote_addr zone=perip:10m;\n\t\tlimit_conn_zone $server_name zone=perserver:10m;" - nginxConf = nginxConf.replace( - "#limit_conn_zone $binary_remote_addr zone=perip:10m;", limitConf) - mw.writeFile(limit, nginxConf) - - def saveLimitNet(self, sid, perserver, perip, limit_rate): - - str_perserver = 'limit_conn perserver ' + perserver + ';' - str_perip = 'limit_conn perip ' + perip + ';' - str_limit_rate = 'limit_rate ' + limit_rate + 'k;' - - siteName = mw.M('sites').where("id=?", (sid,)).getField('name') - filename = self.getHostConf(siteName) - - conf = mw.readFile(filename) - if(conf.find('limit_conn perserver') != -1): - # 替换总并发 - rep = r"limit_conn\s+perserver\s+([0-9]+);" - conf = re.sub(rep, str_perserver, conf) - - # 替换IP并发限制 - rep = r"limit_conn\s+perip\s+([0-9]+);" - conf = re.sub(rep, str_perip, conf) - - # 替换请求流量限制 - rep = r"limit_rate\s+([0-9]+)\w+;" - conf = re.sub(rep, str_limit_rate, conf) - else: - conf = conf.replace('#error_page 404/404.html;', "#error_page 404/404.html;\n " + - str_perserver + "\n " + str_perip + "\n " + str_limit_rate) - - mw.writeFile(filename, conf) - mw.restartWeb() - mw.writeLog('TYPE_SITE', 'SITE_NETLIMIT_OPEN_SUCCESS', (siteName,)) - return mw.returnJson(True, '设置成功!') - - def closeLimitNet(self, sid): - siteName = mw.M('sites').where("id=?", (sid,)).getField('name') - filename = self.getHostConf(siteName) - conf = mw.readFile(filename) - # 清理总并发 - rep = r"\s+limit_conn\s+perserver\s+([0-9]+);" - conf = re.sub(rep, '', conf) - - # 清理IP并发限制 - rep = r"\s+limit_conn\s+perip\s+([0-9]+);" - conf = re.sub(rep, '', conf) - - # 清理请求流量限制 - rep = r"\s+limit_rate\s+([0-9]+)\w+;" - conf = re.sub(rep, '', conf) - mw.writeFile(filename, conf) - mw.restartWeb() - mw.writeLog( - 'TYPE_SITE', 'SITE_NETLIMIT_CLOSE_SUCCESS', (siteName,)) - return mw.returnJson(True, '已关闭流量限制!') - - def getSecurity(self, sid, name): - filename = self.getHostConf(name) - conf = mw.readFile(filename) - data = {} - if conf.find('SECURITY-START') != -1: - rep = "#SECURITY-START(\n|.){1,500}#SECURITY-END" - tmp = re.search(rep, conf).group() - data['fix'] = re.search( - r"\(.+\)\$", tmp).group().replace('(', '').replace(')$', '').replace('|', ',') - - data['status'] = False - data['none'] = False - - valid_referers = re.search( - r"valid_referers\s+(.+);\n", tmp) - valid_referers_none = re.search( - r"valid_referers\s+none\s+blocked\s+(.+);\n", tmp) - - if valid_referers or valid_referers_none: - data['status'] = True - - if valid_referers_none: - domain_t = valid_referers_none.groups()[0].split() - data['domains'] = ','.join(domain_t) - data['none'] = True - elif valid_referers: - domain_t = valid_referers.groups()[0].split() - data['domains'] = ','.join(domain_t) - data['none'] = False - - # print(data) - else: - data['fix'] = 'jpg,jpeg,gif,png,js,css' - domains = mw.M('domain').where( - 'pid=?', (sid,)).field('name').select() - tmp = [] - for domain in domains: - tmp.append(domain['name']) - data['domains'] = ','.join(tmp) - data['status'] = False - data['none'] = False - return mw.getJson(data) - - def setSecurity(self, sid, name, fix, domains, status, none=''): - if len(fix) < 2: - return mw.returnJson(False, 'URL后缀不能为空!') - file = self.getHostConf(name) - if os.path.exists(file): - conf = mw.readFile(file) - if status == 'false': - rep = r"\s{0,4}#SECURITY-START(\n|.){1,500}#SECURITY-END\n?" - conf = re.sub(rep, '', conf) - mw.writeLog('网站管理', '站点[' + name + ']已关闭防盗链设置!') - else: - rep = r"\s{0,4}#SECURITY-START(\n|.){1,500}#SECURITY-END\n?" - conf = re.sub(rep, '', conf) - - valid_referers = domains.strip().replace(',', ' ') - if none == 'true': - valid_referers = 'none blocked ' + valid_referers - - pre_path = self.setupPath + "/php/conf" - re_path = r"include\s+" + pre_path + "/enable-php-" - rconf = r'''#SECURITY-START 防盗链配置 - location ~ .*\.(%s)$ - { - expires 30d; - access_log /dev/null; - valid_referers %s; - if ($invalid_referer){ - return 404; - } - } - #SECURITY-END - include %s/enable-php-''' % (fix.strip().replace(',', '|'), valid_referers, pre_path) - conf = re.sub(re_path, rconf, conf) - mw.writeLog('网站管理', '站点[' + name + ']已开启防盗链!') - mw.writeFile(file, conf) - mw.restartWeb() - return mw.returnJson(True, '设置成功!') - - def getPhpVersion(self): - phpVersions = ('00', '52', '53', '54', '55', - '56', '70', '71', '72', '73', - '74', '80', '81', '82', '83', - '84') - data = [] - for val in phpVersions: - tmp = {} - if val == '00': - tmp['version'] = '00' - tmp['name'] = '纯静态' - data.append(tmp) - - # 标准判断 - checkPath = mw.getServerDir() + '/php/' + val + '/bin/php' - if os.path.exists(checkPath): - tmp['version'] = val - tmp['name'] = 'PHP-' + val - data.append(tmp) - - # 其他PHP安装类型 - conf_dir = mw.getServerDir() + "/web_conf/php/conf" - conf_list = os.listdir(conf_dir) - l = len(conf_list) - rep = r"enable-php-(.*?)\.conf" - for name in conf_list: - tmp = {} - try: - matchVer = re.search(rep, name).groups()[0] - except Exception as e: - continue - - if matchVer in phpVersions: - continue - - tmp['version'] = matchVer - tmp['name'] = 'PHP-' + matchVer - data.append(tmp) - - return data - - # 是否跳转到https - def isToHttps(self, siteName): - file = self.getHostConf(siteName) - conf = mw.readFile(file) - if conf: - # if conf.find('HTTP_TO_HTTPS_START') != -1: - # return True - if conf.find('$server_port !~ 443') != -1: - return True - return False - - def getRewriteList(self): - rewriteList = {} - rewriteList['rewrite'] = [] - rewriteList['rewrite'].append('0.当前') - for ds in os.listdir('rewrite/nginx'): - rewriteList['rewrite'].append(ds[0:len(ds) - 5]) - rewriteList['rewrite'] = sorted(rewriteList['rewrite']) - return rewriteList - - def createRootDir(self, path): - autoInit = False - if not os.path.exists(path): - autoInit = True - os.makedirs(path) - if not mw.isAppleSystem(): - mw.execShell('chown -R www:www ' + path) - - if autoInit: - mw.writeFile(path + '/index.html', 'Work has started!!!') - mw.execShell('chmod -R 755 ' + path) - - def nginxAddDomain(self, webname, domain, port): - file = self.getHostConf(webname) - conf = mw.readFile(file) - if not conf: - return - - # 添加域名 - rep = r"server_name\s*(.*);" - tmp = re.search(rep, conf).group() - domains = tmp.split(' ') - if not mw.inArray(domains, domain): - newServerName = tmp.replace(';', ' ' + domain + ';') - conf = conf.replace(tmp, newServerName) - - # 添加端口 - rep = r"listen\s+([0-9]+)\s*[default_server]*\s*;" - tmp = re.findall(rep, conf) - if not mw.inArray(tmp, port): - listen = re.search(rep, conf).group() - conf = conf.replace( - listen, listen + "\n\tlisten " + port + ';') - # 保存配置文件 - mw.writeFile(file, conf) - return True - - def nginxAddConf(self): - source_tpl = mw.getRunDir() + '/data/tpl/nginx.conf' - vhost_file = self.vhostPath + '/' + self.siteName + '.conf' - content = mw.readFile(source_tpl) - - content = content.replace('{$PORT}', self.sitePort) - content = content.replace('{$SERVER_NAME}', self.siteName) - content = content.replace('{$ROOT_DIR}', self.sitePath) - content = content.replace('{$PHP_DIR}', self.setupPath + '/php') - content = content.replace('{$PHPVER}', self.phpVersion) - content = content.replace('{$OR_REWRITE}', self.rewritePath) - # content = content.replace('{$OR_REDIRECT}', self.redirectPath) - # content = content.replace('{$OR_PROXY}', self.proxyPath) - - logsPath = mw.getLogsDir() - content = content.replace('{$LOGPATH}', logsPath) - mw.writeFile(vhost_file, content) - -# 和反代配置冲突 && 默认伪静态为空 -# rewrite_content = ''' -# location /{ -# if ($PHP_ENV != "1"){ -# break; -# } - -# if (!-e $request_filename) { -# rewrite ^(.*)$ /index.php/$1 last; -# break; -# } -# } -# ''' - rewrite_file = self.rewritePath + '/' + self.siteName + '.conf' - mw.writeFile(rewrite_file, '') - - def add(self, webname, port, ps, path, version): - site_root_dir = mw.getWwwDir() - if site_root_dir == path.rstrip('/'): - return mw.returnJson(False, '不要以网站根目录创建站点!') - - siteMenu = json.loads(webname) - self.siteName = self.toPunycode(siteMenu['domain'].strip().split(':')[0]).strip() - self.sitePath = self.toPunycodePath(self.getPath(path.replace(' ', ''))) - self.sitePort = port.strip().replace(' ', '') - self.phpVersion = version - - if mw.M('sites').where("name=?", (self.siteName,)).count(): - return mw.returnJson(False, '您添加的站点已存在!') - - # 写入数据库 - pid = mw.M('sites').add('name,path,status,ps,edate,addtime,type_id', - (self.siteName, self.sitePath, '1', ps, '0000-00-00', mw.getDate(), 0,)) - opid = mw.M('domain').where("name=?", (self.siteName,)).getField('pid') - if opid: - if mw.M('sites').where('id=?', (opid,)).count(): - return mw.returnJson(False, '您添加的域名已存在!') - mw.M('domain').where('pid=?', (opid,)).delete() - - self.createRootDir(self.sitePath) - self.nginxAddConf() - - mw.M('domain').add('pid,name,port,addtime', - (pid, self.siteName, self.sitePort, mw.getDate())) - - # 添加更多域名 - for domain in siteMenu['domainlist']: - self.addDomain(domain, self.siteName, pid) - - data = {} - data['siteStatus'] = False - mw.restartWeb() - - self.runHook('site_cb', 'add') - return mw.returnJson(True, '添加成功') - - def deleteWSLogs(self, webname): - assLogPath = mw.getLogsDir() + '/' + webname + '.log' - errLogPath = mw.getLogsDir() + '/' + webname + '.error.log' - confFile = self.setupPath + '/nginx/vhost/' + webname + '.conf' - rewriteFile = self.setupPath + '/nginx/rewrite/' + webname + '.conf' - passFile = self.setupPath + '/nginx/pass/' + webname + '.conf' - keyPath = self.sslDir + webname + '/privkey.pem' - certPath = self.sslDir + webname + '/fullchain.pem' - logs = [assLogPath, - errLogPath, - confFile, - rewriteFile, - passFile, - keyPath, - certPath] - for i in logs: - mw.deleteFile(i) - - # 重定向目录 - redirectDir = self.setupPath + '/nginx/redirect/' + webname - if os.path.exists(redirectDir): - mw.execShell('rm -rf ' + redirectDir) - # 代理目录 - proxyDir = self.setupPath + '/nginx/proxy/' + webname - if os.path.exists(proxyDir): - mw.execShell('rm -rf ' + proxyDir) - - def delete(self, sid, webname, path): - self.deleteWSLogs(webname) - if path == '1': - rootPath = mw.getWwwDir() + '/' + webname - mw.execShell('rm -rf ' + rootPath) - - # ssl - ssl_dir = self.sslDir + '/' + webname - if os.path.exists(ssl_dir): - mw.execShell('rm -rf ' + ssl_dir) - - ssl_lets_dir = self.sslLetsDir + '/' + webname - if os.path.exists(ssl_lets_dir): - mw.execShell('rm -rf ' + ssl_lets_dir) - - ssl_acme_dir = mw.getAcmeDir() + '/' + webname - if os.path.exists(ssl_acme_dir): - mw.execShell('rm -rf ' + ssl_acme_dir) - - mw.M('sites').where("id=?", (sid,)).delete() - mw.M('domain').where("pid=?", (sid,)).delete() - mw.M('domain').where("name=?", (webname,)).delete() - - # binding domain delete - binding_list = mw.M('binding').field( - 'id,domain').where("pid=?", (sid,)).select() - - for x in binding_list: - wlog = mw.getLogsDir() + "/" + webname + "_" + x['domain'] + ".log" - wlog_error = mw.getLogsDir() + "/" + webname + "_" + \ - x['domain'] + ".error.log" - - if os.path.exists(wlog): - mw.execShell('rm -rf ' + wlog) - if os.path.exists(wlog_error): - mw.execShell('rm -rf ' + wlog_error) - - mw.M('binding').where("pid=?", (sid,)).delete() - mw.restartWeb() - - self.runHook('site_cb', 'delete') - return mw.returnJson(True, '站点删除成功!') - - def setEndDate(self, sid, edate): - result = mw.M('sites').where( - 'id=?', (sid,)).setField('edate', edate) - siteName = mw.M('sites').where('id=?', (sid,)).getField('name') - mw.writeLog('TYPE_SITE', '设置成功,站点到期后将自动停止!', (siteName, edate)) - return mw.returnJson(True, '设置成功,站点到期后将自动停止!') - - # ssl相关方法 start - def setSslConf(self, siteName): - file = self.getHostConf(siteName) - conf = mw.readFile(file) - - version = '' - version_file_pl = mw.getServerDir() + '/openresty/version.pl' - if os.path.exists(version_file_pl): - version = mw.readFile(version_file_pl) - version = version.strip() - - - keyPath = self.sslDir + '/' + siteName + '/privkey.pem' - certPath = self.sslDir + '/' + siteName + '/fullchain.pem' - if conf: - if conf.find('ssl_certificate') == -1: - # ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; - # add_header Alt-Svc 'h3=":443";ma=86400,h3-29=":443";ma=86400'; - http3Header = """ - add_header Strict-Transport-Security "max-age=63072000"; - add_header Alt-Svc 'h3=":443";ma=86400'; -""" - if not version.startswith('1.25'): - http3Header = ''; - - sslStr = """#error_page 404/404.html; - ssl_certificate %s; - ssl_certificate_key %s; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; - ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305; - ssl_prefer_server_ciphers on; - ssl_session_cache shared:SSL:10m; - ssl_session_timeout 10m; - %s - error_page 497 https://$host$request_uri;""" % (certPath, keyPath, http3Header) - if(conf.find('ssl_certificate') != -1): - return mw.returnData(True, 'SSL开启成功!') - - conf = conf.replace('#error_page 404/404.html;', sslStr) - - rep = r"listen\s+([0-9]+)\s*[default_server|reuseport]*;" - tmp = re.findall(rep, conf) - if not mw.inArray(tmp, '443'): - listen = re.search(rep, conf).group() - - if version.startswith('1.25'): - http_ssl = "\n\tlisten 443 ssl;" - http_ssl = http_ssl + "\n\tlisten [::]:443 ssl;" - http_ssl = http_ssl + "\n\tlisten 443 quic;" - http_ssl = http_ssl + "\n\tlisten [::]:443 quic;" - http_ssl = http_ssl + "\n\thttp2 on;" - else: - http_ssl = "\n\tlisten 443 ssl http2;" - http_ssl = http_ssl + "\n\tlisten [::]:443 ssl http2;" - - - conf = conf.replace(listen, listen + http_ssl) - - mw.backFile(file) - mw.writeFile(file, conf) - isError = mw.checkWebConfig() - if(isError != True): - mw.restoreFile(file) - return mw.returnData(False, '证书错误:
' + isError.replace("\n", '
') + '
') - - self.saveCert(keyPath, certPath) - - msg = mw.getInfo('网站[{1}]开启SSL成功!', (siteName,)) - mw.writeLog('网站管理', msg) - - mw.restartWeb() - return mw.returnData(True, 'SSL开启成功!') - - def saveCert(self, keyPath, certPath): - try: - certInfo = mw.getCertName(certPath) - if not certInfo: - return mw.returnData(False, '证书解析失败!') - vpath = self.sslDir + '/' + certInfo['subject'].strip() - if not os.path.exists(vpath): - os.system('mkdir -p ' + vpath) - mw.writeFile(vpath + '/privkey.pem', mw.readFile(keyPath)) - mw.writeFile(vpath + '/fullchain.pem', mw.readFile(certPath)) - mw.writeFile(vpath + '/info.json', json.dumps(certInfo)) - return mw.returnData(True, '证书保存成功!') - except Exception as e: - return mw.returnData(False, '证书保存失败!') - - # 清除多余user.ini - def delUserInI(self, path, up=0): - for p1 in os.listdir(path): - try: - npath = path + '/' + p1 - if os.path.isdir(npath): - if up < 100: - self.delUserInI(npath, up + 1) - else: - continue - useriniPath = npath + '/.user.ini' - if not os.path.exists(useriniPath): - continue - mw.execShell('which chattr && chattr -i ' + useriniPath) - mw.execShell('rm -f ' + useriniPath) - except: - continue - return True - - # 设置目录防御 - def setDirUserINI(self, sitePath, runPath): - newPath = sitePath + runPath - - filename = newPath + '/.user.ini' - if os.path.exists(filename): - mw.execShell("chattr -i " + filename) - os.remove(filename) - return mw.returnJson(True, '已清除防跨站设置!') - - self.delUserInI(newPath) - openPath = 'open_basedir={}/:{}/'.format(newPath, sitePath) - if runPath == '/': - openPath = 'open_basedir={}/'.format(sitePath) - - mw.writeFile(filename, openPath + ':/www/server/php:/tmp/:/proc/') - mw.execShell("chattr +i " + filename) - - return mw.returnJson(True, '已打开防跨站设置!') -# ssl相关方法 end diff --git a/class/core/system_api.py b/class/core/system_api.py deleted file mode 100755 index 12208a35f..000000000 --- a/class/core/system_api.py +++ /dev/null @@ -1,978 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 系统信息操作 -# --------------------------------------------------------------------------------- - - -import psutil -import time -import os -import re -import math -import sys -import json - -from flask import Flask, session -from flask import request - -import db -import mw - -import config_api -import requests - -from threading import Thread -from time import sleep - - -def mw_async(f): - def wrapper(*args, **kwargs): - thr = Thread(target=f, args=args, kwargs=kwargs) - thr.start() - return wrapper - - -class system_api: - setupPath = None - pids = None - cache = {} - def __init__(self): - self.setupPath = mw.getServerDir() - - - ##### ----- start ----- ### - def networkApi(self): - data = self.getNetWork() - return mw.getJson(data) - - def updateServerApi(self): - stype = request.args.get('type', 'check') - version = request.args.get('version', '') - return self.updateServer(stype, version) - - def systemTotalApi(self): - data = self.getSystemTotal() - return mw.getJson(data) - - def diskInfoApi(self): - diskInfo = self.getDiskInfo() - return mw.getJson(diskInfo) - - def setControlApi(self): - stype = request.form.get('type', '') - day = request.form.get('day', '') - data = self.setControl(stype, day) - return data - - def getLoadAverageApi(self): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = self.getLoadAverageData(start, end) - return mw.getJson(data) - - def getCpuIoApi(self): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = self.getCpuIoData(start, end) - return mw.getJson(data) - - def getDiskIoApi(self): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = self.getDiskIoData(start, end) - return mw.getJson(data) - - def getNetworkIoApi(self): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = self.getNetWorkIoData(start, end) - return mw.getJson(data) - - def rememoryApi(self): - os.system('sync') - scriptFile = mw.getRunDir() + '/script/rememory.sh' - mw.execShell("/bin/bash " + scriptFile) - data = self.getMemInfo() - return mw.getJson(data) - - # 重启面板 - def restartApi(self): - self.restartMw() - return mw.returnJson(True, '面板已重启!') - - def restartServerApi(self): - if mw.isAppleSystem(): - return mw.returnJson(False, "开发环境不可重起") - self.restartServer() - return mw.returnJson(True, '正在重启服务器!') - ##### ----- end ----- ### - - def restartTask(self): - initd = mw.getRunDir() + '/scripts/init.d/mw' - if os.path.exists(initd): - os.system(initd + ' ' + 'restart_task') - return True - - def restartMw(self): - mw.writeFile('data/restart.pl', 'True') - return True - - @mw_async - def restartServer(self): - if not mw.isRestart(): - return mw.returnJson(False, '请等待所有安装任务完成再执行!') - mw.execShell("sync && init 6 &") - return mw.returnJson(True, '命令发送成功!') - - # 名取PID - def getPid(self, pname): - try: - if not self.pids: - self.pids = psutil.pids() - for pid in self.pids: - if psutil.Process(pid).name() == pname: - return True - return False - except: - return False - - # 检查端口是否占用 - def isOpen(self, port): - import socket - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - s.connect(('127.0.0.1', int(port))) - s.shutdown(2) - return True - except: - return False - - # 检测指定进程是否存活 - def checkProcess(self, pid): - try: - if not self.pids: - self.pids = psutil.pids() - if int(pid) in self.pids: - return True - return False - except: - return False - - def getEnvInfoApi(self, get=None): - serverInfo = {} - serverInfo['status'] = True - sdir = mw.getServerDir() - - serverInfo['webserver'] = '未安装' - if os.path.exists(sdir + '/openresty/nginx/sbin/nginx'): - serverInfo['webserver'] = 'OpenResty' - serverInfo['php'] = [] - phpversions = ['52', '53', '54', '55', '56', '70', '71', - '72', '73', '74', '80', '81', '82', '83', '84'] - phpPath = sdir + '/php/' - for pv in phpversions: - if not os.path.exists(phpPath + pv + '/bin/php'): - continue - serverInfo['php'].append(pv) - serverInfo['mysql'] = False - if os.path.exists(sdir + '/mysql/bin/mysql'): - serverInfo['mysql'] = True - import psutil - try: - diskInfo = psutil.disk_usage('/www') - except: - diskInfo = psutil.disk_usage('/') - serverInfo['disk'] = diskInfo[2] - return mw.returnJson(True, 'ok', serverInfo) - - def getPanelInfo(self, get=None): - # 取面板配置 - address = mw.GetLocalIp() - try: - try: - port = web.ctx.host.split(':')[1] - except: - port = mw.readFile('data/port.pl') - except: - port = '7200' - domain = '' - if os.path.exists('data/domain.conf'): - domain = mw.readFile('data/domain.conf') - - autoUpdate = '' - if os.path.exists('data/autoUpdate.pl'): - autoUpdate = 'checked' - limitip = '' - if os.path.exists('data/limitip.conf'): - limitip = mw.readFile('data/limitip.conf') - - templates = [] - for template in os.listdir('templates/'): - if os.path.isdir('templates/' + template): - templates.append(template) - template = mw.readFile('data/templates.pl') - - check502 = '' - if os.path.exists('data/502Task.pl'): - check502 = 'checked' - return {'port': port, 'address': address, 'domain': domain, 'auto': autoUpdate, '502': check502, 'limitip': limitip, 'templates': templates, 'template': template} - - def getSystemTotal(self, interval=1): - # 取系统统计信息 - data = self.getMemInfo() - cpu = self.getCpuInfo(interval) - data['cpuNum'] = cpu[1] - data['cpuRealUsed'] = cpu[0] - data['time'] = self.getBootTime() - data['system'] = self.getSystemVersion() - data['isuser'] = mw.M('users').where( - 'username=?', ('admin',)).count() - data['version'] = '0.0.1' - return data - - def getLoadAverage(self): - c = os.getloadavg() - data = {} - data['one'] = round(float(c[0]), 2) - data['five'] = round(float(c[1]), 2) - data['fifteen'] = round(float(c[2]), 2) - data['max'] = psutil.cpu_count() * 2 - data['limit'] = data['max'] - data['safe'] = data['max'] * 0.75 - return data - - def getAllInfo(self, get): - data = {} - data['load_average'] = self.GetLoadAverage(get) - data['title'] = self.GetTitle() - data['network'] = self.GetNetWorkApi(get) - data['panel_status'] = not os.path.exists('/www/server/mdserver-web/data/close.pl') - import firewalls - ssh_info = firewalls.firewalls().GetSshInfo(None) - data['enable_ssh_status'] = ssh_info['status'] - data['disable_ping_status'] = not ssh_info['ping'] - data['time'] = self.GetBootTime() - # data['system'] = self.GetSystemVersion(); - # data['mem'] = self.GetMemInfo(); - data['version'] = web.ctx.session.version - return data - - def getTitle(self): - titlePl = 'data/title.pl' - title = 'Linux面板' - if os.path.exists(titlePl): - title = mw.readFile(titlePl).strip() - return title - - def getSystemDeviceTemperature(self): - if not hasattr(psutil, "sensors_temperatures"): - return False, "platform not supported" - temps = psutil.sensors_temperatures() - if not temps: - return False, "can't read any temperature" - for name, entries in temps.items(): - for entry in entries: - return True, entry.label - # print("%-20s %s °C (high = %s °C, critical = %s °C)" % ( - # entry.label or name, entry.current, entry.high, - # entry.critical)) - return False, "" - - def getSystemVersion(self): - # - # 取操作系统版本 - current_os = mw.getOs() - # sys_temper = self.getSystemDeviceTemperature() - # print(sys_temper) - # mac - if current_os == 'darwin': - data = mw.execShell('sw_vers')[0] - data_list = data.strip().split("\n") - mac_version = '' - for x in data_list: - xlist = x.split("\t") - mac_version += xlist[len(xlist)-1] + ' ' - - arch_ver = mw.execShell("arch") - return mac_version + " (" + arch_ver[0].strip() + ")" - - # freebsd - if current_os.startswith('freebsd'): - version = mw.execShell( - "cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") - arch_ver = mw.execShell( - "sysctl -a | egrep -i 'hw.machine_arch' | awk -F ':' '{print $2}'") - return version[0].strip() + " (" + arch_ver[0].strip() + ")" - - redhat_series = '/etc/redhat-release' - if os.path.exists(redhat_series): - version = mw.readFile('/etc/redhat-release') - version = version.replace('release ', '').strip() - - arch_ver = mw.execShell("arch") - return version + " (" + arch_ver[0].strip() + ")" - - os_series = '/etc/os-release' - if os.path.exists(os_series): - version = mw.execShell( - "cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") - - arch_ver = mw.execShell("arch") - return version[0].strip() + " (" + arch_ver[0].strip() + ")" - - return '未识别系统信息' - - def getBootTime(self): - # 取系统启动时间 - uptime = mw.readFile('/proc/uptime') - if uptime == False: - start_time = psutil.boot_time() - run_time = time.time() - start_time - else: - run_time = uptime.split()[0] - tStr = float(run_time) - min = tStr / 60 - hours = min / 60 - days = math.floor(hours / 24) - hours = math.floor(hours - (days * 24)) - min = math.floor(min - (days * 60 * 24) - (hours * 60)) - return mw.getInfo('已不间断运行: {1}天{2}小时{3}分钟', (str(int(days)), str(int(hours)), str(int(min)))) - - def getCpuInfo(self, interval=1): - # 取CPU信息 - cpuCount = psutil.cpu_count() - cpuLogicalNum = psutil.cpu_count(logical=False) - used = psutil.cpu_percent(interval=interval) - cpuLogicalNum = 0 - if os.path.exists('/proc/cpuinfo'): - c_tmp = mw.readFile('/proc/cpuinfo') - d_tmp = re.findall("physical id.+", c_tmp) - cpuLogicalNum = len(set(d_tmp)) - - used_all = psutil.cpu_percent(percpu=True) - cpu_name = mw.getCpuType() + " * {}".format(cpuLogicalNum) - return used, cpuCount, used_all, cpu_name, cpuCount, cpuLogicalNum - - def getMemInfo(self): - # 取内存信息 - mem = psutil.virtual_memory() - if mw.getOs() == 'darwin': - memInfo = { - 'memTotal': mem.total, - } - memInfo['memRealUsed'] = memInfo['memTotal'] * (mem.percent / 100) - else: - memInfo = { - 'memTotal': mem.total, - 'memFree': mem.free, - 'memBuffers': mem.buffers, - 'memCached': mem.cached - } - - memInfo['memRealUsed'] = memInfo['memTotal'] - \ - memInfo['memFree'] - memInfo['memBuffers'] - \ - memInfo['memCached'] - return memInfo - - def getMemUsed(self): - # 取内存使用率 - try: - import psutil - mem = psutil.virtual_memory() - - if mw.getOs() == 'darwin': - return mem.percent - - memInfo = {'memTotal': mem.total / 1024 / 1024, 'memFree': mem.free / 1024 / 1024, - 'memBuffers': mem.buffers / 1024 / 1024, 'memCached': mem.cached / 1024 / 1024} - tmp = memInfo['memTotal'] - memInfo['memFree'] - \ - memInfo['memBuffers'] - memInfo['memCached'] - tmp1 = memInfo['memTotal'] / 100 - return (tmp / tmp1) - except Exception as ex: - return 1 - - def getDiskInfo(self): - info = self.getDiskInfo2() - if len(info) != 0: - return info - - # 取磁盘分区信息 - diskIo = psutil.disk_partitions() - diskInfo = [] - - for disk in diskIo: - if disk[1] == '/mnt/cdrom': - continue - if disk[1] == '/boot': - continue - tmp = {} - tmp['path'] = disk[1] - size_tmp = psutil.disk_usage(disk[1]) - tmp['size'] = [mw.toSize(size_tmp[0]), mw.toSize( - size_tmp[1]), mw.toSize(size_tmp[2]), str(size_tmp[3]) + '%'] - diskInfo.append(tmp) - return diskInfo - - def getDiskInfo2(self): - # 取磁盘分区信息 - temp = mw.execShell( - "df -h -P|grep '/'|grep -v tmpfs | grep -v devfs")[0] - tempInodes = mw.execShell( - "df -i -P|grep '/'|grep -v tmpfs | grep -v devfs")[0] - temp1 = temp.split('\n') - tempInodes1 = tempInodes.split('\n') - diskInfo = [] - n = 0 - cuts = ['/mnt/cdrom', '/boot', '/boot/efi', '/dev', - '/dev/shm', '/zroot', '/run/lock', '/run', '/run/shm', '/run/user'] - for tmp in temp1: - n += 1 - inodes = tempInodes1[n - 1].split() - disk = tmp.split() - if len(disk) < 5: - continue - if disk[1].find('M') != -1: - continue - if disk[1].find('K') != -1: - continue - if len(disk[5].split('/')) > 4: - continue - if disk[5] in cuts: - continue - arr = {} - arr['path'] = disk[5] - tmp1 = [disk[1], disk[2], disk[3], disk[4]] - arr['size'] = tmp1 - arr['inodes'] = [inodes[1], inodes[2], inodes[3], inodes[4]] - diskInfo.append(arr) - return diskInfo - - # 清理系统垃圾 - def clearSystem(self, get): - count = total = 0 - tmp_total, tmp_count = self.ClearMail() - count += tmp_count - total += tmp_total - tmp_total, tmp_count = self.ClearOther() - count += tmp_count - total += tmp_total - return count, total - - # 清理邮件日志 - def clearMail(self): - rpath = '/var/spool' - total = count = 0 - import shutil - con = ['cron', 'anacron', 'mail'] - for d in os.listdir(rpath): - if d in con: - continue - dpath = rpath + '/' + d - time.sleep(0.2) - num = size = 0 - for n in os.listdir(dpath): - filename = dpath + '/' + n - fsize = os.path.getsize(filename) - size += fsize - if os.path.isdir(filename): - shutil.rmtree(filename) - else: - os.remove(filename) - # print('mail clear ok') - num += 1 - total += size - count += num - return total, count - - # 清理其它 - def clearOther(self): - clearPath = [ - {'path': '/www/server/mdserver-web', 'find': 'testDisk_'}, - {'path': '/www/wwwlogs', 'find': 'log'}, - {'path': '/tmp', 'find': 'panelBoot.pl'}, - {'path': '/www/server/mdserver-web/install', 'find': '.rpm'} - ] - - total = count = 0 - for c in clearPath: - for d in os.listdir(c['path']): - if d.find(c['find']) == -1: - continue - filename = c['path'] + '/' + d - fsize = os.path.getsize(filename) - total += fsize - if os.path.isdir(filename): - shutil.rmtree(filename) - else: - os.remove(filename) - count += 1 - mw.restartWeb() - os.system('echo > /tmp/panelBoot.pl') - return total, count - - def psutilNetIoCounters(self): - ''' - 统计网卡流量 - ''' - stat_pl = 'data/only_netio_counters.pl' - if os.path.exists(stat_pl): - local_lo = (0, 0, 0, 0) - ioName = psutil.net_io_counters(pernic=True).keys() - for x in ioName: - - if x.find("lo") > -1: - local_lo = psutil.net_io_counters( - pernic=True).get(x)[:4] - - all_io = psutil.net_io_counters()[:4] - result_io = tuple([all_io[i] - local_lo[i] - for i in range(0, len(all_io))]) - - # print(local_lo) - # print(all_io) - # print(result_io) - return result_io - return psutil.net_io_counters()[:4] - - def getNetWork(self): - # 取网络流量信息 - try: - networkInfo = {} - # 取网络流量信息 - # networkIo = self.psutilNetIoCounters() - # if not "otime" in session: - # session['up'] = networkIo[0] - # session['down'] = networkIo[1] - # session['otime'] = time.time() - - # ntime = time.time() - # print("source",ntime - session['otime']) - - # networkInfo['upTotal'] = networkIo[0] - # networkInfo['downTotal'] = networkIo[1] - # networkInfo['up'] = round(float( - # networkIo[0] - session['up']) / (ntime - session['otime']), 2) - # networkInfo['down'] = round( - # float(networkIo[1] - session['down']) / (ntime - session['otime']), 2) - # networkInfo['downPackets'] = networkIo[3] - # networkInfo['upPackets'] = networkIo[2] - - # # print networkIo[1], session['down'], ntime, session['otime'] - # session['up'] = networkIo[0] - # session['down'] = networkIo[1] - # session['otime'] = time.time() - - networkInfo['cpu'] = self.getCpuInfo() - networkInfo['load'] = self.getLoadAverage() - networkInfo['mem'] = self.getMemInfo() - networkInfo['iostat'] = self.getDiskIostat() - networkInfo['network'] = self.getNetWorkIostat() - - return networkInfo - except Exception as e: - print(mw.getTracebackInfo()) - return None - - def getNetWorkIostat(self): - netInfo = {} - - netInfo['ALL'] = {} - netInfo['ALL']['up'] = 0 - netInfo['ALL']['down'] = 0 - netInfo['ALL']['upTotal'] = 0 - netInfo['ALL']['downTotal'] = 0 - netInfo['ALL']['upPackets'] = 0 - netInfo['ALL']['downPackets'] = 0 - - mtime = time.time() - iokey = 'netstat' - netio = None - if iokey in self.cache: - netio = self.cache[iokey] - - if not netio: - netio = {} - netio['info'] = None - netio['all_io'] = None - netio['time'] = mtime - - stime = mtime - netio['time'] - if not stime: stime = 1 - - # print("new:",stime) - netio_group = psutil.net_io_counters(pernic=True).keys() - - netio_cache = netio['info'] - allio_cache = netio['all_io'] - if not netio_cache: - netio_cache = {} - - netio_group_t = {} - for name in netio_group: - netInfo[name] = {} - - io_data = psutil.net_io_counters(pernic=True).get(name) - if not name in netio_cache: - netio_cache[name] = io_data - - netio_group_t[name] = io_data - - netInfo[name]['up'] = round(float((io_data[0] - netio_cache[name][0]) / stime), 2) - netInfo[name]['down'] = round(float((io_data[1] - netio_cache[name][1])/ stime), 2) - - netInfo[name]['upTotal'] = io_data[0] - netInfo[name]['downTotal'] = io_data[1] - netInfo[name]['upPackets'] = io_data[2] - netInfo[name]['downPackets'] = io_data[3] - - all_io = psutil.net_io_counters()[:4] - if not allio_cache: - allio_cache = all_io - - netInfo['ALL']['up'] = round(float((all_io[0] - allio_cache[0]) /stime), 2) - netInfo['ALL']['down'] = round(float((all_io[1] - allio_cache[1]) /stime), 2) - netInfo['ALL']['upTotal'] = all_io[0] - netInfo['ALL']['downTotal'] = all_io[1] - netInfo['ALL']['upPackets'] = all_io[2] - netInfo['ALL']['downPackets'] = all_io[3] - - self.cache[iokey] = {'info':netio_group_t,'all_io':all_io,'time':mtime} - return netInfo - - - def getDiskIostat(self): - iokey = 'iostat' - - diskInfo = {} - diskInfo['ALL'] = {} - diskInfo['ALL']['read_count'] = 0 - diskInfo['ALL']['write_count'] = 0 - diskInfo['ALL']['read_bytes'] = 0 - diskInfo['ALL']['write_bytes'] = 0 - diskInfo['ALL']['read_time'] = 0 - diskInfo['ALL']['write_time'] = 0 - diskInfo['ALL']['read_merged_count'] = 0 - diskInfo['ALL']['write_merged_count'] = 0 - - try: - diskio = None - if iokey in self.cache: - diskio = self.cache[iokey] - - mtime = int(time.time()) - if not diskio: - diskio = {} - diskio['info'] = None - diskio['time'] = mtime - - diskio_cache = diskio['info'] - stime = mtime - diskio['time'] - if not stime: stime = 1 - - diskio_group = psutil.disk_io_counters(perdisk=True) - if not diskio_cache: - diskio_cache = diskio_group - - for disk_name in diskio_group.keys(): - diskInfo[disk_name] = {} - # print('disk_name',disk_name) - # print(diskio_group[disk_name].write_time , diskio_cache[disk_name].write_time) - # print(diskio_group[disk_name].write_count , diskio_cache[disk_name].write_count) - - diskInfo[disk_name]['read_count'] = int((diskio_group[disk_name].read_count - diskio_cache[disk_name].read_count) / stime) - diskInfo[disk_name]['write_count'] = int((diskio_group[disk_name].write_count - diskio_cache[disk_name].write_count) / stime) - diskInfo[disk_name]['read_bytes'] = int((diskio_group[disk_name].read_bytes - diskio_cache[disk_name].read_bytes) / stime) - diskInfo[disk_name]['write_bytes'] = int((diskio_group[disk_name].write_bytes - diskio_cache[disk_name].write_bytes) / stime) - diskInfo[disk_name]['read_time'] = int((diskio_group[disk_name].read_time - diskio_cache[disk_name].read_time) / stime) - diskInfo[disk_name]['write_time'] = int((diskio_group[disk_name].write_time - diskio_cache[disk_name].write_time) / stime) - - if 'read_merged_count' in diskio_group[disk_name] and 'read_merged_count' in diskio_cache[disk_name]: - diskInfo[disk_name]['read_merged_count'] = int((diskio_group[disk_name].read_merged_count - diskio_cache[disk_name].read_merged_count) / stime) - if 'write_merged_count' in diskio_group[disk_name] and 'write_merged_count' in diskio_cache[disk_name]: - diskInfo[disk_name]['write_merged_count'] = int((diskio_group[disk_name].write_merged_count - diskio_cache[disk_name].write_merged_count) / stime) - - diskInfo['ALL']['read_count'] += diskInfo[disk_name]['read_count'] - diskInfo['ALL']['write_count'] += diskInfo[disk_name]['write_count'] - diskInfo['ALL']['read_bytes'] += diskInfo[disk_name]['read_bytes'] - diskInfo['ALL']['write_bytes'] += diskInfo[disk_name]['write_bytes'] - if diskInfo['ALL']['read_time'] < diskInfo[disk_name]['read_time']: - diskInfo['ALL']['read_time'] = diskInfo[disk_name]['read_time'] - if diskInfo['ALL']['write_time'] < diskInfo[disk_name]['write_time']: - diskInfo['ALL']['write_time'] = diskInfo[disk_name]['write_time'] - - if 'read_merged_count' in diskInfo[disk_name] and 'read_merged_count' in diskInfo[disk_name]: - diskInfo['ALL']['read_merged_count'] += diskInfo[disk_name]['read_merged_count'] - if 'write_merged_count' in diskInfo[disk_name] and 'write_merged_count' in diskInfo[disk_name]: - diskInfo['ALL']['write_merged_count'] += diskInfo[disk_name]['write_merged_count'] - - self.cache[iokey] = {'info':diskio_group,'time':mtime} - except Exception as e: - pass - - return diskInfo - - def getNetWorkIoData(self, start, end): - # 取指定时间段的网络Io - data = mw.M('network').dbfile('system').where("addtime>=? AND addtime<=?", (start, end)).field( - 'id,up,down,total_up,total_down,down_packets,up_packets,addtime').order('id asc').select() - return self.toAddtime(data) - - def getDiskIoData(self, start, end): - # 取指定时间段的磁盘Io - data = mw.M('diskio').dbfile('system').where("addtime>=? AND addtime<=?", (start, end)).field( - 'id,read_count,write_count,read_bytes,write_bytes,read_time,write_time,addtime').order('id asc').select() - return self.toAddtime(data) - - def getCpuIoData(self, start, end): - # 取指定时间段的CpuIo - data = mw.M('cpuio').dbfile('system').where("addtime>=? AND addtime<=?", - (start, end)).field('id,pro,mem,addtime').order('id asc').select() - return self.toAddtime(data, True) - - def getLoadAverageData(self, start, end): - data = mw.M('load_average').dbfile('system').where("addtime>=? AND addtime<=?", ( - start, end)).field('id,pro,one,five,fifteen,addtime').order('id asc').select() - return self.toAddtime(data) - - # 格式化addtime列 - def toAddtime(self, data, tomem=False): - import time - if tomem: - import psutil - mPre = (psutil.virtual_memory().total / 1024 / 1024) / 100 - length = len(data) - he = 1 - if length > 100: - he = 1 - if length > 1000: - he = 3 - if length > 10000: - he = 15 - if he == 1: - for i in range(length): - data[i]['addtime'] = time.strftime( - '%m/%d %H:%M', time.localtime(float(data[i]['addtime']))) - if tomem and data[i]['mem'] > 100: - data[i]['mem'] = data[i]['mem'] / mPre - - return data - else: - count = 0 - tmp = [] - for value in data: - if count < he: - count += 1 - continue - value['addtime'] = time.strftime( - '%m/%d %H:%M', time.localtime(float(value['addtime']))) - if tomem and value['mem'] > 100: - value['mem'] = value['mem'] / mPre - tmp.append(value) - count = 0 - return tmp - - def setControl(self, stype, day): - - filename = 'data/control.conf' - stat_pl = 'data/only_netio_counters.pl' - - if stype == '0': - mw.execShell("rm -rf " + filename) - elif stype == '1': - _day = int(day) - if _day < 1: - return mw.returnJson(False, "设置失败!") - mw.writeFile(filename, day) - elif stype == '2': - mw.execShell("rm -rf " + stat_pl) - elif stype == '3': - mw.execShell("echo 'True' > " + stat_pl) - elif stype == 'del': - if not mw.isRestart(): - return mw.returnJson(False, '请等待所有安装任务完成再执行') - os.remove("data/system.db") - - sql = db.Sql().dbfile('system') - csql = mw.readFile('data/sql/system.sql') - csql_list = csql.split(';') - for index in range(len(csql_list)): - sql.execute(csql_list[index], ()) - return mw.returnJson(True, "监控服务已关闭") - else: - data = {} - if os.path.exists(filename): - try: - data['day'] = int(mw.readFile(filename)) - except: - data['day'] = 30 - data['status'] = True - else: - data['day'] = 30 - data['status'] = False - - if os.path.exists(stat_pl): - data['stat_all_status'] = True - else: - data['stat_all_status'] = False - - return mw.getJson(data) - - return mw.returnJson(True, "设置成功!") - - def versionDiff(self, old, new): - ''' - test 测试 - new 有新版本 - none 没有新版本 - ''' - new_list = new.split('.') - if len(new_list) > 3: - return 'test' - - old_list = old.split('.') - ret = 'none' - - isHasNew = True - if int(new_list[0]) == int(old_list[0]) and int(new_list[1]) == int(old_list[1]) and int(new_list[2]) == int(old_list[2]): - isHasNew = False - - if isHasNew: - return 'new' - return ret - - def getServerInfo(self): - import urllib.request - import ssl - upAddr = 'https://api.github.com/repos/midoks/mdserver-web/releases/latest' - try: - context = ssl._create_unverified_context() - req = urllib.request.urlopen(upAddr, context=context, timeout=3) - result = req.read().decode('utf-8') - version = json.loads(result) - return version - except Exception as e: - print('getServerInfo', e) - return {} - - def updateServer(self, stype, version=''): - return mw.returnJson(False, '与后续版本差异太大,不再提供更新') - # 更新服务 - try: - if not mw.isRestart(): - return mw.returnJson(False, '请等待所有安装任务完成再执行!') - - version_new_info = self.getServerInfo() - version_now = config_api.config_api().getVersion() - - new_ver = version_new_info['name'] - - if stype == 'check': - if not 'name' in version_new_info: - return mw.returnJson(False, '服务器数据或网络有问题!') - - diff = self.versionDiff(version_now, new_ver) - if diff == 'new': - return mw.returnJson(True, '有新版本!', new_ver) - elif diff == 'test': - return mw.returnJson(True, '有测试版本!', new_ver) - else: - return mw.returnJson(False, '已经是最新,无需更新!') - - if stype == 'info': - if not 'name' in version_new_info: - return mw.returnJson(False, '服务器数据有问题!') - diff = self.versionDiff(version_now, new_ver) - data = {} - data['version'] = new_ver - data['content'] = version_new_info[ - 'body'].replace("\n", "
") - return mw.returnJson(True, '更新信息!', data) - - if stype == 'update': - if version == '': - return mw.returnJson(False, '缺少版本信息!') - - if new_ver != version: - return mw.returnJson(False, '更新失败,请重试!') - - toPath = mw.getRootDir() + '/temp' - if not os.path.exists(toPath): - mw.execShell('mkdir -p ' + toPath) - - newUrl = "https://github.com/midoks/mdserver-web/archive/refs/tags/" + version + ".zip" - - dist_mw = toPath + '/mw.zip' - if not os.path.exists(dist_mw): - mw.execShell( - 'wget --no-check-certificate -O ' + dist_mw + ' ' + newUrl) - - dist_to = toPath + "/mdserver-web-" + version - if not os.path.exists(dist_to): - os.system('unzip -o ' + toPath + - '/mw.zip' + ' -d ' + toPath) - - cmd_cp = 'cp -rf ' + toPath + '/mdserver-web-' + \ - version + '/* ' + mw.getServerDir() + '/mdserver-web' - mw.execShell(cmd_cp) - - mw.execShell('rm -rf ' + toPath + '/mdserver-web-' + version) - mw.execShell('rm -rf ' + toPath + '/mw.zip') - - update_env = ''' -#!/bin/bash -PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin - -P_VER=`python3 -V | awk '{print $2}'` - -if [ ! -f /www/server/mdserver-web/bin/activate ];then - cd /www/server/mdserver-web && python3 -m venv . - cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate -else - cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate -fi - -cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"") -PIPSRC="https://pypi.python.org/simple" -if [ ! -z "$cn" ];then - PIPSRC="https://pypi.tuna.tsinghua.edu.cn/simple" -fi - -cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/requirements.txt -i $PIPSRC - -P_VER_D=`echo "$P_VER"|awk -F '.' '{print $1}'` -P_VER_M=`echo "$P_VER"|awk -F '.' '{print $2}'` -NEW_P_VER=${P_VER_D}.${P_VER_M} - -if [ -f /www/server/mdserver-web/version/r${NEW_P_VER}.txt ];then - cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/version/r${NEW_P_VER}.txt -i $PIPSRC -fi -''' - os.system(update_env) - self.restartMw() - return mw.returnJson(True, '安装更新成功!') - - return mw.returnJson(False, '已经是最新,无需更新!') - except Exception as ex: - # print('updateServer', ex) - return mw.returnJson(False, "连接服务器失败!" + str(ex)) - - # 修复面板 - def repPanel(self, get): - vp = '' - if mw.readFile('/www/server/mdserver-web/class/common.py').find('checkSafe') != -1: - vp = '_pro' - mw.ExecShell("wget -O update.sh " + mw.get_url() + - "/install/update" + vp + ".sh && bash update.sh") - if hasattr(web.ctx.session, 'getCloudPlugin'): - del(web.ctx.session['getCloudPlugin']) - return True diff --git a/class/core/task_api.py b/class/core/task_api.py deleted file mode 100755 index 19d485e02..000000000 --- a/class/core/task_api.py +++ /dev/null @@ -1,103 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 任务页操作 -# --------------------------------------------------------------------------------- - - -import psutil -import time -import os -import sys -import mw -import re -import json -import pwd - - -from flask import request - - -class task_api: - - def __init__(self): - pass - - def countApi(self): - c = mw.M('tasks').where("status!=?", ('1',)).count() - return str(c) - - def listApi(self): - - p = request.form.get('p', '1') - limit = request.form.get('limit', '10').strip() - search = request.form.get('search', '').strip() - - start = (int(p) - 1) * int(limit) - limit_str = str(start) + ',' + str(limit) - - _list = mw.M('tasks').where('', ()).field( - 'id,name,type,status,addtime,start,end').limit(limit_str).order('id desc').select() - _ret = {} - _ret['data'] = _list - - count = mw.M('tasks').where('', ()).count() - _page = {} - _page['count'] = count - _page['tojs'] = 'remind' - _page['p'] = p - - # return data - _ret['count'] = count - _ret['page'] = mw.getPage(_page) - return mw.getJson(_ret) - - def getExecLogApi(self): - file = os.getcwd() + "/tmp/panelExec.log" - v = mw.getLastLine(file, 100) - return v - - def getTaskSpeedApi(self): - tempFile = mw.getRunDir() + '/tmp/panelExec.log' - freshFile = mw.getRunDir() + '/tmp/panelFresh' - - find = mw.M('tasks').where('status=? OR status=?', - ('-1', '0')).field('id,type,name,execstr').find() - if not len(find): - return mw.returnJson(False, '当前没有任务队列在执行-2!') - - mw.triggerTask() - - data = {} - data['name'] = find['name'] - data['execstr'] = find['execstr'] - if find['type'] == 'download': - readLine = "" - for i in range(3): - try: - readLine = mw.readFile(tempFile) - if len(readLine) > 10: - data['msg'] = json.loads(readLine) - data['isDownload'] = True - break - except Exception as e: - if i == 2: - mw.M('tasks').where("id=?", (find['id'],)).save( - 'status', ('0',)) - return mw.returnJson(False, '当前没有任务队列在执行-4:' + str(e)) - time.sleep(0.5) - else: - data['msg'] = mw.getLastLine(tempFile, 10) - data['isDownload'] = False - - data['task'] = mw.M('tasks').where("status!=?", ('1',)).field( - 'id,status,name,type').order("id asc").select() - return mw.getJson(data) diff --git a/class/core/vilidate.py b/class/core/vilidate.py deleted file mode 100755 index 14cf6abfb..000000000 --- a/class/core/vilidate.py +++ /dev/null @@ -1,143 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 验证码操作 -# --------------------------------------------------------------------------------- - - -import random -import math -from PIL import Image, ImageDraw, ImageFont, ImageFilter - - -class vieCode: - __fontSize = 20 # 字体大小 - __width = 120 # 画布宽度 - __heigth = 45 # 画布高度 - __length = 4 # 验证码长度 - __draw = None # 画布 - __img = None # 图片资源 - __code = None # 验证码字符 - __str = None # 自定义验证码字符集 - __inCurve = True # 是否画干扰线 - __inNoise = True # 是否画干扰点 - __type = 2 # 验证码类型 1、纯字母 2、数字字母混合 - __fontPatn = 'class/fonts/2.ttf' # 字体 - - def GetCodeImage(self, size=80, length=4): - '''获取验证码图片 - @param int size 验证码大小 - @param int length 验证码长度 - ''' - # 准备基础数据 - self.__length = length - self.__fontSize = size - self.__width = self.__fontSize * self.__length - self.__heigth = int(self.__fontSize * 1.5) - - # 生成验证码图片 - self.__createCode() - self.__createImage() - self.__createNoise() - self.__printString() - self.__cerateFilter() - - return self.__img, self.__code - - def __cerateFilter(self): - '''模糊处理''' - self.__img = self.__img.filter(ImageFilter.BLUR) - filter = ImageFilter.ModeFilter(8) - self.__img = self.__img.filter(filter) - - def __createCode(self): - '''创建验证码字符''' - # 是否自定义字符集合 - if not self.__str: - # 源文本 - number = "3456789" - srcLetter = "qwertyuipasdfghjkzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM" - srcUpper = srcLetter.upper() - if self.__type == 1: - self.__str = number - else: - self.__str = srcLetter + srcUpper + number - - # 构造验证码 - self.__code = random.sample(self.__str, self.__length) - - def __createImage(self): - '''创建画布''' - bgColor = (random.randint(200, 255), random.randint( - 200, 255), random.randint(200, 255)) - self.__img = Image.new('RGB', (self.__width, self.__heigth), bgColor) - self.__draw = ImageDraw.Draw(self.__img) - - def __createNoise(self): - '''画干扰点''' - if not self.__inNoise: - return - font = ImageFont.truetype(self.__fontPatn, int(self.__fontSize / 1.5)) - for i in range(5): - # 杂点颜色 - noiseColor = (random.randint(150, 200), random.randint( - 150, 200), random.randint(150, 200)) - putStr = random.sample(self.__str, 2) - for j in range(2): - # 绘杂点 - size = (random.randint(-10, self.__width), - random.randint(-10, self.__heigth)) - self.__draw.text(size, putStr[j], font=font, fill=noiseColor) - pass - - def __createCurve(self): - '''画干扰线''' - if not self.__inCurve: - return - x = y = 0 - - # 计算曲线系数 - a = random.uniform(1, self.__heigth / 2) - b = random.uniform(-self.__width / 4, self.__heigth / 4) - f = random.uniform(-self.__heigth / 4, self.__heigth / 4) - t = random.uniform(self.__heigth, self.__width * 2) - xend = random.randint(self.__width / 2, self.__width * 2) - w = (2 * math.pi) / t - - # 画曲线 - color = (random.randint(30, 150), random.randint( - 30, 150), random.randint(30, 150)) - for x in range(xend): - if w != 0: - for k in range(int(self.__heigth / 10)): - y = a * math.sin(w * x + f) + b + self.__heigth / 2 - i = int(self.__fontSize / 5) - while i > 0: - px = x + i - py = y + i + k - self.__draw.point((px, py), color) - i -= i - - def __printString(self): - '''打印验证码字符串''' - font = ImageFont.truetype(self.__fontPatn, self.__fontSize) - x = 0 - # 打印字符到画板 - for i in range(self.__length): - # 设置字体随机颜色 - color = (random.randint(30, 150), random.randint( - 30, 150), random.randint(30, 150)) - # 计算座标 - x = random.uniform(self.__fontSize * i * 0.95, - self.__fontSize * i * 1.1) - y = self.__fontSize * random.uniform(0.3, 0.5) - # 打印字符 - self.__draw.text((x, y), self.__code[i], font=font, fill=color) diff --git a/class/core/vip_api.py b/class/core/vip_api.py deleted file mode 100644 index df4a3061b..000000000 --- a/class/core/vip_api.py +++ /dev/null @@ -1,48 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# VIP用户管理 -# --------------------------------------------------------------------------------- - -import mw - -from flask import request -import requests - - -class vip_api: - - api_url = 'https://wo.midoks.icu/api/wp-json/vip' - - def __init__(self): - pass - - def loginApi(self): - username = request.form.get('username', '') - password = request.form.get('password', '') - password = mw.aesEncrypt(password) - - headers = { - "Content-Type": "application/x-www-form-urlencoded", - } - - print("name:", str(username)) - print("pwd:", str(password)) - args = { - 'name': username, - 'pass': password - } - data = requests.post(self.api_url + '/v1/login', - data=args, headers=headers) - - print(data.text) - - return mw.returnJson(False, "测试中!") diff --git a/class/fonts/2.ttf b/class/fonts/2.ttf deleted file mode 100755 index 3a452b68f..000000000 Binary files a/class/fonts/2.ttf and /dev/null differ diff --git a/class/os/README.md b/class/os/README.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/class/os/win32/README.md b/class/os/win32/README.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/class/plugin/README.md b/class/plugin/README.md deleted file mode 100644 index a4ce3c56f..000000000 --- a/class/plugin/README.md +++ /dev/null @@ -1,5 +0,0 @@ - -- fcgi-client.py -``` -https://pypi.org/project/flup6/1.1.1/#files -``` diff --git a/class/plugin/orm.py b/class/plugin/orm.py deleted file mode 100755 index 89ba623e9..000000000 --- a/class/plugin/orm.py +++ /dev/null @@ -1,140 +0,0 @@ -# coding: utf-8 - -import re -import os -import sys - -import pymysql.cursors - - -class ORM: - __DB_PASS = None - __DB_USER = 'root' - __DB_PORT = 3306 - __DB_NAME = '' - __DB_HOST = 'localhost' - __DB_CONN = None - __DB_CUR = None - __DB_ERR = None - __DB_CNF = '/etc/my.cnf' - __DB_TIMEOUT=1 - __DB_SOCKET = '/www/server/mysql/mysql.sock' - - __DB_CHARSET = "utf8" - - def __Conn(self): - # print(self.__DB_HOST, self.__DB_USER, self.__DB_PASS, self.__DB_SOCKET) - '''连接数据库''' - try: - - if self.__DB_HOST != 'localhost': - try: - self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - database=self.__DB_NAME, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT, - cursorclass=pymysql.cursors.DictCursor) - except Exception as e: - self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - database=self.__DB_NAME, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT, - cursorclass=pymysql.cursors.DictCursor) - elif os.path.exists(self.__DB_SOCKET): - try: - self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - database=self.__DB_NAME, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT, - unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor) - except Exception as e: - self.__DB_HOST = '127.0.0.1' - self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - database=self.__DB_NAME, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT, - unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor) - else: - try: - self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - database=self.__DB_NAME, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT, - cursorclass=pymysql.cursors.DictCursor) - except Exception as e: - self.__DB_HOST = '127.0.0.1' - self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - database=self.__DB_NAME, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT, - cursorclass=pymysql.cursors.DictCursor) - - self.__DB_CUR = self.__DB_CONN.cursor() - return True - except Exception as e: - self.__DB_ERR = e - return False - - def setDbConf(self, conf): - self.__DB_CNF = conf - - def setSocket(self, sock): - self.__DB_SOCKET = sock - - def setCharset(self, charset): - self.__DB_CHARSET = charset - - def setHost(self, host): - self.__DB_HOST = host - - def setPort(self, port): - self.__DB_PORT = port - - def setUser(self, user): - self.__DB_USER = user - - def setPwd(self, pwd): - self.__DB_PASS = pwd - - def getPwd(self): - return self.__DB_PASS - - def setTimeout(self, timeout = 1): - self.__DB_TIMEOUT = timeout - return True - - def setDbName(self, name): - self.__DB_NAME = name - - def execute(self, sql): - # 执行SQL语句返回受影响行 - if not self.__Conn(): - return self.__DB_ERR - try: - result = self.__DB_CUR.execute(sql) - self.__DB_CONN.commit() - self.__Close() - return result - except Exception as ex: - return ex - - def ping(self): - try: - self.__DB_CONN.ping() - except Exception as e: - print(e) - return True - - def query(self, sql): - # 执行SQL语句返回数据集 - if not self.__Conn(): - return self.__DB_ERR - try: - self.__DB_CUR.execute(sql) - result = self.__DB_CUR.fetchall() - # print(result) - # 将元组转换成列表 - # data = map(list, result) - self.__Close() - return result - except Exception as ex: - return ex - - def __Close(self): - # 关闭连接 - self.__DB_CUR.close() - self.__DB_CONN.close() diff --git a/class/plugin/ormDb.py b/class/plugin/ormDb.py deleted file mode 100755 index 14624ccc0..000000000 --- a/class/plugin/ormDb.py +++ /dev/null @@ -1,115 +0,0 @@ -# coding: utf-8 - -import re -import os -import sys - - -class ORM: - __DB_PASS = None - __DB_USER = 'root' - __DB_PORT = 3306 - __DB_HOST = 'localhost' - __DB_CONN = None - __DB_CUR = None - __DB_ERR = None - __DB_CNF = '/etc/my.cnf' - - __DB_SOCKET = '/www/server/mysql/mysql.sock' - __DB_CHARSET = 'utf8' - - def __Conn(self): - '''连接MYSQL数据库''' - try: - - try: - import MySQLdb - except Exception as ex: - self.__DB_ERR = ex - return False - - # print(self.__DB_HOST) - # print(self.__DB_USER) - # print(self.__DB_PASS) - # print(self.__DB_PORT) - # print(self.__DB_CHARSET) - # print(self.__DB_SOCKET) - if os.path.exists(self.__DB_SOCKET): - try: - self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET) - except Exception as e: - print(e) - self.__DB_HOST = '127.0.0.1' - self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET) - else: - try: - self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1) - except Exception as e: - self.__DB_HOST = '127.0.0.1' - self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, - port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1) - - self.__DB_CUR = self.__DB_CONN.cursor() - - return True - except MySQLdb.Error as e: - self.__DB_ERR = e - return False - - def setDbConf(self, conf): - self.__DB_CNF = conf - - def setSocket(self, sock): - self.__DB_SOCKET = sock - - def setCharset(self, charset): - self.__DB_CHARSET = charset - - def setPort(self, port): - self.__DB_PORT = port - - def setPwd(self, pwd): - self.__DB_PASS = pwd - - def getPwd(self): - return self.__DB_PASS - - def setDbName(self, name): - self.__DB_NAME = name - - def setUser(self, user): - self.__DB_USER = user - - def execute(self, sql): - # 执行SQL语句返回受影响行 - if not self.__Conn(): - return self.__DB_ERR - try: - result = self.__DB_CUR.execute(sql) - self.__DB_CONN.commit() - self.__Close() - return result - except Exception as ex: - return ex - - def query(self, sql): - # 执行SQL语句返回数据集 - if not self.__Conn(): - return self.__DB_ERR - try: - self.__DB_CUR.execute(sql) - result = self.__DB_CUR.fetchall() - # 将元组转换成列表 - data = map(list, result) - self.__Close() - return data - except Exception as ex: - return ex - - # 关闭连接 - def __Close(self): - self.__DB_CUR.close() - self.__DB_CONN.close() diff --git a/cli.sh b/cli.sh index a06e18550..fc9ef386f 100755 --- a/cli.sh +++ b/cli.sh @@ -3,31 +3,31 @@ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/hom DIR=$(cd "$(dirname "$0")"; pwd) MDIR=$(dirname "$DIR") +export LC_ALL="en_US.UTF-8" + +# echo $DIR PATH=$PATH:$DIR/bin -if [ -f bin/activate ];then - source bin/activate +if [ -f ${DIR}/bin/activate ];then + source ${DIR}/bin/activate if [ "$?" != "0" ];then echo "load local python env fail!" fi fi -export LC_ALL="en_US.UTF-8" - - mw_start_task() { - isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}') + isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}') if [ "$isStart" == '' ];then echo -e "starting mw-tasks... \c" - cd $DIR && python3 task.py >> ${DIR}/logs/task.log 2>&1 & + cd $DIR && python3 panel_task.py >> ${DIR}/logs/panel_task.log 2>&1 & sleep 0.3 - isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}') + isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}') if [ "$isStart" == '' ];then echo -e "\033[31mfailed\033[0m" echo '------------------------------------------------------' - tail -n 20 $DIR/logs/task.log + tail -n 20 $DIR/logs/panel_task.log echo '------------------------------------------------------' echo -e "\033[31mError: mw-tasks service startup failed.\033[0m" return; @@ -39,34 +39,44 @@ mw_start_task() } mw_start(){ - gunicorn -c setting.py app:app - #安全启动 + # 后台任务 mw_start_task + + cd ${DIR}/web && gunicorn -c setting.py app:app } mw_start_debug(){ - if [ ! -f $DIR/logs/task.log ];then - echo '' > $DIR/logs/task.log + if [ ! -f $DIR/logs/panel_task.log ];then + echo '' > $DIR/logs/panel_task.log fi - python3 task.py >> $DIR/logs/task.log 2>&1 & - port=7200 + python3 panel_task.py >> $DIR/logs/panel_task.log 2>&1 & + port=7200 if [ -f /www/server/mdserver-web/data/port.pl ];then port=$(cat /www/server/mdserver-web/data/port.pl) fi - # gunicorn -b :${port} -k gevent -w 1 app:app - gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + + if [ -f ${DIR}/data/port.pl ];then + port=$(cat ${DIR}/data/port.pl) + fi + cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app + # cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app } -mw_start_debug2(){ - python3 task.py >> $DIR/logs/task.log 2>&1 & - gunicorn -b :7200 -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app +mw_start_panel(){ + port=7200 + if [ -f ${DIR}/data/port.pl ];then + port=$(cat ${DIR}/data/port.pl) + fi + cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app + # cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + } -mw_start_debug3(){ - gunicorn -c setting.py app:app - python3 task.py +mw_start_bgtask(){ + cd ${DIR}/web && gunicorn -c setting.py app:app + cd ${DIR} && python3 panel_task.py } @@ -78,9 +88,8 @@ mw_stop() kill -9 $i > /dev/null 2>&1 done - pids=`ps -ef|grep task.py | grep -v grep |awk '{print $2}'` - arr=($pids) - for p in ${arr[@]} + PIDS=`ps -ef|grep panel_task.py | grep -v grep |awk '{print $2}'` + for p in $PIDS do kill -9 $p > /dev/null 2>&1 done @@ -97,12 +106,12 @@ case "$1" in mw_stop mw_start_debug ;; - 'debug2') + 'panel') mw_stop - mw_start_debug2 + mw_start_panel ;; - 'debug3') - mw_stop - mw_start_debug3 + 'task') + # mw_stop + mw_start_bgtask ;; esac \ No newline at end of file diff --git a/compatibility.md b/compatibility.md index eeb065f1a..37b61457a 100644 --- a/compatibility.md +++ b/compatibility.md @@ -29,24 +29,24 @@ cd /www/server/mdserver-web/plugins/php/versions && bash all_test.sh -| 系统名称 |PHP53 |PHP54 |PHP55 |PHP56 |PHP70 |PHP71 |PHP72 |PHP73 |PHP74 |PHP80 |PHP81 |PHP82 |PHP83 | -| ----------------- |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------| -| CentOS 7.9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | -| CentOS 8.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | -| CentOS 8 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | -| CentOS 9 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | -| Debian 10.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | -| Debian 11.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | -| Debian 12 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | -| Ubuntu 18.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | -| Ubuntu 20.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | -| Ubuntu 22.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | -| Fedora 31 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | -| Fedora 32 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | -| AlmaLinux 9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | -| RockyLinux 8.6 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | -| Arch Linux |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | -| openSUSE 15.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| 系统名称 |PHP53 |PHP54 |PHP55 |PHP56 |PHP70 |PHP71 |PHP72 |PHP73 |PHP74 |PHP80 |PHP81 |PHP82 |PHP83 |PHP84 | +| ----------------- |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------| +| CentOS 7.9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| CentOS 8.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| CentOS 8 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| CentOS 9 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| Debian 10.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| Debian 11.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| Debian 12 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| Ubuntu 18.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| Ubuntu 20.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| Ubuntu 22.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| Fedora 31 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| Fedora 32 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| AlmaLinux 9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| RockyLinux 8.6 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| Arch Linux |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | +| openSUSE 15.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ | diff --git a/data/README.md b/data/README.md new file mode 100644 index 000000000..bc8dc9fc5 --- /dev/null +++ b/data/README.md @@ -0,0 +1,4 @@ +# 自动生成 + +certificate.pem - 证书信息 +privateKey.pem - 私钥 \ No newline at end of file diff --git a/data/control.conf b/data/control.conf deleted file mode 100644 index 8580e7b68..000000000 --- a/data/control.conf +++ /dev/null @@ -1 +0,0 @@ -30 \ No newline at end of file diff --git a/data/json/type.json b/data/json/type.json deleted file mode 100755 index 2943516b5..000000000 --- a/data/json/type.json +++ /dev/null @@ -1,42 +0,0 @@ -[ - { - "title":"全部", - "type":0, - "ps":"" - }, - { - "title":"已安装", - "type":-1, - "ps":"" - }, - { - "title":"运行环境", - "type":1, - "ps":"" - }, - { - "title":"数据软件", - "type":2, - "ps":"" - }, - { - "title":"代码管理", - "type":3, - "ps":"" - }, - { - "title":"系统工具", - "type":4, - "ps":"" - }, - { - "title":"其他插件", - "type":5, - "ps":"" - }, - { - "title":"辅助插件", - "type":6, - "ps":"" - } -] \ No newline at end of file diff --git a/data/recycle_bin.pl b/data/recycle_bin.pl deleted file mode 100755 index 4791ed555..000000000 --- a/data/recycle_bin.pl +++ /dev/null @@ -1 +0,0 @@ -True \ No newline at end of file diff --git a/data/vip/readme.md b/data/vip/readme.md deleted file mode 100644 index cf9aac38d..000000000 --- a/data/vip/readme.md +++ /dev/null @@ -1 +0,0 @@ -存放VIP关键数据 \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile index 934a40943..c3fafd629 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -40,7 +40,7 @@ RUN apt update -y && \ RUN cd /www/server/mdserver-web/plugins/php && \ bash install.sh install 74 && \ cd /www/server/mdserver-web/plugins/openresty && \ - bash install.sh install 1.21.4.1 && \ + bash install.sh install 1.21.4 && \ cd /www/server/mdserver-web/plugins/mysql && \ bash install.sh install 5.6 && \ cd /www/server/mdserver-web/plugins/phpmyadmin && \ diff --git a/logs/README.md b/logs/README.md new file mode 100644 index 000000000..cd3d22536 --- /dev/null +++ b/logs/README.md @@ -0,0 +1 @@ +logs \ No newline at end of file diff --git a/new_cli.sh b/new_cli.sh deleted file mode 100755 index c5dd43844..000000000 --- a/new_cli.sh +++ /dev/null @@ -1,113 +0,0 @@ -#!/bin/bash -PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin -DIR=$(cd "$(dirname "$0")"; pwd) -MDIR=$(dirname "$DIR") - -# echo $DIR - -PATH=$PATH:$DIR/bin -if [ -f ${DIR}/bin/activate ];then - source ${DIR}/bin/activate - - if [ "$?" != "0" ];then - echo "load local python env fail!" - fi -fi - -export LC_ALL="en_US.UTF-8" - - -mw_start_task() -{ - isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}') - if [ "$isStart" == '' ];then - echo -e "starting mw-tasks... \c" - cd $DIR && python3 panel_task.py >> ${DIR}/logs/panel_task.log 2>&1 & - sleep 0.3 - isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}') - if [ "$isStart" == '' ];then - echo -e "\033[31mfailed\033[0m" - echo '------------------------------------------------------' - tail -n 20 $DIR/logs/panel_task.log - echo '------------------------------------------------------' - echo -e "\033[31mError: mw-tasks service startup failed.\033[0m" - return; - fi - echo -e "\033[32mdone\033[0m" - else - echo "starting mw-tasks... mw-tasks (pid $(echo $isStart)) already running" - fi -} - -mw_start(){ - cd web && gunicorn -c setting.py app:app - #安全启动 - mw_start_task -} - - -mw_start_debug(){ - if [ ! -f $DIR/logs/panel_task.log ];then - echo '' > $DIR/logs/panel_task.log - fi - - python3 panel_task.py >> $DIR/logs/panel_task.log 2>&1 & - port=7200 - if [ -f /www/server/mdserver-web/data/port.pl ];then - port=$(cat /www/server/mdserver-web/data/port.pl) - fi - - if [ -f ${DIR}/data/port.pl ];then - port=$(cat ${DIR}/data/port.pl) - fi - # gunicorn -b :${port} -k gevent -w 1 app:app - cd web && gunicorn -b :${port} -w 1 app:app -} - -mw_start_debug2(){ - python3 panel_task.py >> $DIR/logs/panel_task.log 2>&1 & - gunicorn -b :7200 -w 1 app:app -} - -mw_start_debug3(){ - gunicorn -c setting.py app:app - python3 panel_task.py -} - - -mw_stop() -{ - PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'` - for i in $PLIST - do - kill -9 $i > /dev/null 2>&1 - done - - pids=`ps -ef|grep panel_task.py | grep -v grep |awk '{print $2}'` - arr=($pids) - for p in ${arr[@]} - do - kill -9 $p > /dev/null 2>&1 - done -} - -case "$1" in - 'start') mw_start;; - 'stop') mw_stop;; - 'restart') - mw_stop - mw_start - ;; - 'debug') - mw_stop - mw_start_debug - ;; - 'debug2') - mw_stop - mw_start_debug2 - ;; - 'debug3') - mw_stop - mw_start_debug3 - ;; -esac \ No newline at end of file diff --git a/panel_task.py b/panel_task.py index b23038993..7aa3aa43f 100755 --- a/panel_task.py +++ b/panel_task.py @@ -24,110 +24,28 @@ web_dir = os.getcwd() + "/web" os.chdir(web_dir) sys.path.append(web_dir) -from admin import app -from admin import model - import core.mw as mw -import core.db as db +import thisdb +from admin import setup +setup.init() -# print(mw.getPanelDir()) - -# print sys.path - -# cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' -# info = mw.execShell(cmd) -# p = "/usr/local/lib/" + info[0].strip() + "/site-packages" -# sys.path.append(p) - - -global pre, timeoutCount, logPath, isTask, oldEdate, isCheck -pre = 0 -timeoutCount = 0 -isCheck = 0 -oldEdate = None - -g_log_file = mw.getPanelTaskLog() -isTask = mw.getMWLogs() + '/panelTask.pl' - +g_log_file = mw.getPanelTaskExecLog() if not os.path.exists(g_log_file): os.system("touch " + g_log_file) def execShell(cmdstring, cwd=None, timeout=None, shell=True): + cmd = cmdstring + ' > ' + g_log_file + ' 2>&1' + return mw.execShell(cmd) + +def writeLogs(data): + # 写输出日志 try: - global g_log_file - import shlex - import datetime - import subprocess - - if timeout: - end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout) - - cmd = cmdstring + ' > ' + g_log_file + ' 2>&1' - sub = subprocess.Popen(cmd, cwd=cwd, stdin=subprocess.PIPE, shell=shell, bufsize=4096) - while sub.poll() is None: - time.sleep(0.1) - - data = sub.communicate() - # python3 fix 返回byte数据 - if isinstance(data[0], bytes): - t1 = str(data[0], encoding='utf-8') - - if isinstance(data[1], bytes): - t2 = str(data[1], encoding='utf-8') - return (t1, t2) - except Exception as e: - return (None, None) - -def service_cmd(method): - cmd = '/etc/init.d/mw' - if os.path.exists(cmd): - execShell(cmd + ' ' + method) - return - - cmd = mw.getRunDir() + '/scripts/init.d/mw' - if os.path.exists(cmd): - execShell(cmd + ' ' + method) - return - - -def openresty_cmd(method = 'reload'): - # 检查是否安装 - odir = mw.getServerDir() + '/openresty' - if not os.path.exists(odir): - return False - - # systemd - systemd = mw.systemdCfgDir()+'/openresty.service' - if os.path.exists(systemd): - execShell('systemctl ' + method + ' openresty') - return True - - sys_initd = '/etc/init.d/openresty' - if os.path.exists(sys_initd): - os.system(sys_initd + ' ' + method) - return True - - install_initd = mw.getServerDir()+'/openresty/init.d/openresty' - if os.path.exists(install_initd): - os.system(install_initd + ' ' + method) - return True - return False - -def mw_async(f): - def wrapper(*args, **kwargs): - thr = threading.Thread(target=f, args=args, kwargs=kwargs) - thr.start() - return wrapper - - -@mw_async -def restartMw(): - time.sleep(1) - cmd = mw.getRunDir() + '/scripts/init.d/mw reload &' - mw.execShell(cmd) - - + fp = open(g_log_file, 'w+') + fp.write(data) + fp.close() + except: + pass def downloadFile(url, filename): # 下载文件 @@ -146,257 +64,113 @@ def downloadFile(url, filename): if not mw.isAppleSystem(): os.system('chown www.www ' + filename) - writeLogs('done') + writeLogs(filename + ' download success!') except Exception as e: writeLogs(str(e)) return True - def downloadHook(count, blockSize, totalSize): # 下载文件进度回调 - global pre used = count * blockSize - pre1 = int((100.0 * used / totalSize)) - if pre == (100 - pre1): - return - speed = {'total': totalSize, 'used': used, 'pre': pre1} + pre = int((100.0 * used / totalSize)) + speed = {'total': totalSize, 'used': used, 'pre': pre} writeLogs(json.dumps(speed)) - -def writeLogs(data): - # 写输出日志 - try: - fp = open(g_log_file, 'w+') - fp.write(data) - fp.close() - except: - pass - - def runPanelTask(): - try: - bash_list = model.getTaskList(status=-1) - for task in bash_list: - model.setTaskStatus(task['id'], 0) - - run_list = model.getTaskList(status=0) - for run_task in run_list: - start = int(time.time()) - model.setTaskData(run_task['id'], start=start) - model.setTaskStatus(run_task['id'], -1) - if run_task['type'] == 'download': - argv = run_task['cmd'].split('|mw|') - downloadFile(argv[0], argv[1]) - elif run_task['type'] == 'execshell': - execShell(run_task['cmd']) - end = int(time.time()) - model.setTaskData(run_task['id'], end=end) - model.setTaskStatus(run_task['id'], 1) - except Exception as e: - print(str(e)) - # 站点过期检查 - # siteEdate() + siteEdateCheck() + + lock_file = mw.getTriggerTaskLockFile() + try: + if os.path.exists(lock_file): + bash_list = thisdb.getTaskList(status=-1) + for task in bash_list: + thisdb.setTaskStatus(task['id'], 0) + + run_list = thisdb.getTaskList(status=0) + for run_task in run_list: + start = int(time.time()) + thisdb.setTaskData(run_task['id'], start=start) + thisdb.setTaskStatus(run_task['id'], -1) + + if run_task['type'] == 'download': + argv = run_task['cmd'].split('|mw|') + downloadFile(argv[0], argv[1]) + elif run_task['type'] == 'execshell': + execShell(run_task['cmd']) + + end = int(time.time()) + thisdb.setTaskData(run_task['id'], end=end) + thisdb.setTaskStatus(run_task['id'], 1) + + if thisdb.getTaskUnexecutedCount() < 1: + os.remove(lock_file) + except Exception as e: + print('runPanelTask:',mw.getTracebackInfo()) + +# 网站到期处理 +def siteEdateCheck(): + try: + from utils.site import sites as MwSites + website_edate = thisdb.getOption('website_edate', default='0000-00-00') + now_time_ymd = time.strftime('%Y-%m-%d', time.localtime()) + + if website_edate == now_time_ymd: + return False + site_list = thisdb.getSitesEdateList(now_time_ymd) + for site in site_list: + MwSites.instance().stop(site['id']) + thisdb.setOption('website_edate', now_time_ymd) + except Exception as e: + print('siteEdateCheck:',mw.getTracebackInfo()) # 任务队列 def startPanelTask(): try: while True: runPanelTask() - time.sleep(1) + time.sleep(5) except Exception as e: - print(str(e)) - time.sleep(10) + print('startPanelTask:',mw.getTracebackInfo()) + time.sleep(30) startPanelTask() -# 网站到期处理 -def siteEdate(): - global oldEdate - try: - if not oldEdate: - oldEdate = mw.readFile('data/edate.pl') - if not oldEdate: - oldEdate = '0000-00-00' - mEdate = time.strftime('%Y-%m-%d', time.localtime()) - if oldEdate == mEdate: - return False - edateSites = mw.M('sites').where('edate>? AND edate 80: - panel_title = mw.getConfig('title') - ip = mw.getHostAddr() - now_time = mw.getDateFromNow() - msg = now_time + '|节点[' + panel_title + ':' + ip + \ - ']处于高负载[' + str(tmp['used']) + '],请排查原因!' - mw.notifyMessage(msg, '面板监控', 600) - - if not cpuInfo: - tmp['mem'] = sm.getMemUsed() - cpuInfo = tmp - - if cpuInfo['used'] < tmp['used']: - tmp['mem'] = sm.getMemUsed() - cpuInfo = tmp - - # 取当前网络Io - networkIo = sm.psutilNetIoCounters() - if not network_up: - network_up = networkIo[0] - network_down = networkIo[1] - tmp = {} - tmp['upTotal'] = networkIo[0] - tmp['downTotal'] = networkIo[1] - tmp['up'] = round(float((networkIo[0] - network_up) / 1024), 2) - tmp['down'] = round(float((networkIo[1] - network_down) / 1024), 2) - tmp['downPackets'] = networkIo[3] - tmp['upPackets'] = networkIo[2] - - network_up = networkIo[0] - network_down = networkIo[1] - - if not networkInfo: - networkInfo = tmp - if (tmp['up'] + tmp['down']) > (networkInfo['up'] + networkInfo['down']): - networkInfo = tmp - # 取磁盘Io - # if os.path.exists('/proc/diskstats'): - diskio_2 = psutil.disk_io_counters() - if not diskio_1: - diskio_1 = diskio_2 - tmp = {} - tmp['read_count'] = diskio_2.read_count - diskio_1.read_count - tmp['write_count'] = diskio_2.write_count - diskio_1.write_count - tmp['read_bytes'] = diskio_2.read_bytes - diskio_1.read_bytes - tmp['write_bytes'] = diskio_2.write_bytes - diskio_1.write_bytes - tmp['read_time'] = diskio_2.read_time - diskio_1.read_time - tmp['write_time'] = diskio_2.write_time - diskio_1.write_time - - if not diskInfo: - diskInfo = tmp - else: - diskInfo['read_count'] += tmp['read_count'] - diskInfo['write_count'] += tmp['write_count'] - diskInfo['read_bytes'] += tmp['read_bytes'] - diskInfo['write_bytes'] += tmp['write_bytes'] - diskInfo['read_time'] += tmp['read_time'] - diskInfo['write_time'] += tmp['write_time'] - diskio_1 = diskio_2 - - # print diskInfo - if count >= 12: - try: - addtime = int(time.time()) - deltime = addtime - (day * 86400) - - data = (cpuInfo['used'], cpuInfo['mem'], addtime) - sql.table('cpuio').add('pro,mem,addtime', data) - sql.table('cpuio').where("addtime 100: - lpro = 100 - sql.table('load_average').add('pro,one,five,fifteen,addtime', (lpro, load_average[ - 'one'], load_average['five'], load_average['fifteen'], addtime)) - - lpro = None - load_average = None - cpuInfo = None - networkInfo = None - diskInfo = None - count = 0 - reloadNum += 1 - if reloadNum > 1440: - reloadNum = 0 - mw.writeFile('logs/sys_interrupt.pl', - "reload num:" + str(reloadNum)) - restartMw() - except Exception as ex: - print(str(ex)) - mw.writeFile('logs/sys_interrupt.pl', str(ex)) - - del(tmp) + monitor.instance().run() time.sleep(5) - count += 1 except Exception as ex: - print(str(ex)) - mw.writeFile('logs/sys_interrupt.pl', str(ex)) - - restartMw() - + print('systemTask:',mw.getTracebackInfo()) time.sleep(30) systemTask() +def panelPluginStatusCheck(): + # 系统监控任务 + from utils.plugin import plugin + try: + while True: + # start_t = time.time() + plugin.instance().autoCachePluginStatus() + # end_t = time.time() + time.sleep(60) + except Exception as ex: + print('panelPluginStatusCheck:',mw.getTracebackInfo()) + time.sleep(120) + panelPluginStatusCheck() + # -------------------------------------- PHP监控 start --------------------------------------------- # # 502错误检查线程 def check502Task(): try: + check_file = mw.getPanelDir() + '/data/502Task.pl' while True: - if os.path.exists(mw.getPanelDir() + '/data/502Task.pl'): + if os.path.exists(check_file): check502() - time.sleep(30) + time.sleep(10) except: time.sleep(30) check502Task() @@ -410,8 +184,8 @@ def check502(): '82', '83', '84' ] for ver in verlist: - sdir = mw.getServerDir() - php_path = sdir + '/php/' + ver + '/sbin/php-fpm' + server_dir = mw.getServerDir() + php_path = server_dir + '/php/' + ver + '/sbin/php-fpm' if not os.path.exists(php_path): continue if checkPHPVersion(ver): @@ -419,13 +193,14 @@ def check502(): if startPHPVersion(ver): print('检测到PHP-' + ver + '处理异常,已自动修复!') mw.writeLog('PHP守护程序', '检测到PHP-' + ver + '处理异常,已自动修复!') + except Exception as e: - print(str(e)) + mw.writeLog('PHP守护程序', '自动修复异常:'+str(e)) # 处理指定PHP版本 def startPHPVersion(version): - sdir = mw.getServerDir() + server_dir = mw.getServerDir() try: # system phpService = mw.systemdCfgDir() + '/php' + version + '.service' @@ -435,8 +210,8 @@ def startPHPVersion(version): return True # initd - fpm = sdir + '/php/init.d/php' + version - php_path = sdir + '/php/' + version + '/sbin/php-fpm' + fpm = server_dir + '/php/init.d/php' + version + php_path = server_dir + '/php/' + version + '/sbin/php-fpm' if not os.path.exists(php_path): if os.path.exists(fpm): os.remove(fpm) @@ -449,10 +224,9 @@ def startPHPVersion(version): os.system(fpm + ' reload') if checkPHPVersion(version): return True - # 尝试重启服务 cgi = '/tmp/php-cgi-' + version + '.sock' - pid = sdir + '/php/' + version + '/var/run/php-fpm.pid' + pid = server_dir + '/php/' + version + '/var/run/php-fpm.pid' data = mw.execShell("ps -ef | grep php/" + version +" | grep -v grep|grep -v python |awk '{print $2}'") if data[0] != '': os.system("ps -ef | grep php/" + version + " | grep -v grep|grep -v python |awk '{print $2}' | xargs kill ") @@ -469,47 +243,19 @@ def startPHPVersion(version): if os.path.exists(cgi): return True except Exception as e: - print(str(e)) + print('startPHPVersion:',mw.getTracebackInfo()) + mw.writeLog('PHP守护程序', '自动修复异常:'+str(e)) return True -def getFpmConfFile(version): - return mw.getServerDir() + '/php/' + version + '/etc/php-fpm.d/www.conf' - - -def getFpmAddress(version): - fpm_address = '/tmp/php-cgi-{}.sock'.format(version) - php_fpm_file = getFpmConfFile(version) - try: - content = readFile(php_fpm_file) - tmp = re.findall(r"listen\s*=\s*(.+)", content) - if not tmp: - return fpm_address - if tmp[0].find('sock') != -1: - return fpm_address - if tmp[0].find(':') != -1: - listen_tmp = tmp[0].split(':') - if bind: - fpm_address = (listen_tmp[0], int(listen_tmp[1])) - else: - fpm_address = ('127.0.0.1', int(listen_tmp[1])) - else: - fpm_address = ('127.0.0.1', int(tmp[0])) - return fpm_address - except: - return fpm_address - - def checkPHPVersion(version): # 检查指定PHP版本 try: - sock = getFpmAddress(version) + sock = mw.getFpmAddress(version) data = mw.requestFcgiPHP(sock, '/phpfpm_status_' + version + '?json') result = str(data, encoding='utf-8') except Exception as e: result = 'Bad Gateway' - - # print(version,result) # 检查openresty if result.find('Bad Gateway') != -1: return False @@ -519,24 +265,9 @@ def checkPHPVersion(version): # 检查Web服务是否启动 if result.find('Connection refused') != -1: return False - # global isTask - # if os.path.exists(isTask): - # isStatus = mw.readFile(isTask) - # if isStatus == 'True': - # return True - - # # systemd - # systemd = mw.systemdCfgDir() + '/openresty.service' - # if os.path.exists(systemd): - # execShell('systemctl reload openresty') - # return True - # # initd - # initd = '/etc/init.d/openresty' - # if os.path.exists(initd): - # os.system(initd + ' reload') return True -# --------------------------------------PHP监控 end--------------------------------------------- # +# -------------------------------------- PHP监控 end --------------------------------------------- # # --------------------------------------OpenResty Auto Restart Start --------------------------------------------- # @@ -549,11 +280,10 @@ def openrestyAutoRestart(): if not os.path.exists(odir): time.sleep(86400) continue - - openresty_cmd('reload') + mw.opWeb('reload') time.sleep(86400) except Exception as e: - print(str(e)) + mw.writeLog('OpenResty检测', '自动修复异常:'+str(e)) time.sleep(86400) # --------------------------------------OpenResty Auto Restart End --------------------------------------------- # @@ -562,22 +292,23 @@ def openrestyAutoRestart(): def openrestyRestartAtOnce(): - restart_nginx_tip = 'data/restart_nginx.pl' + restart_nginx_tip = mw.getPanelDir()+'/data/restart_nginx.pl' while True: if os.path.exists(restart_nginx_tip): os.remove(restart_nginx_tip) - openresty_cmd('reload') + mw.opWeb('reload') time.sleep(1) # ----------------------------------- OpenResty Restart At Once End ------------------------------------------ # # --------------------------------------Panel Restart Start --------------------------------------------- # def restartPanelService(): - restartTip = 'data/restart.pl' + restart_tip = mw.getPanelDir()+'/data/restart.pl' while True: - if os.path.exists(restartTip): - os.remove(restartTip) - service_cmd('restart_panel') + if os.path.exists(restart_tip): + print("restart panel") + os.remove(restart_tip) + mw.panelCmd('restart_panel') time.sleep(1) # --------------------------------------Panel Restart End --------------------------------------------- # @@ -590,37 +321,39 @@ def setDaemon(t): return t def run(): - # # 系统监控 - # sysTask = threading.Thread(target=systemTask) - # sysTask = setDaemon(sysTask) - # sysTask.start() + # 系统监控 + sysTask = threading.Thread(target=systemTask) + sysTask = setDaemon(sysTask) + sysTask.start() - # # PHP 502错误检查线程 - # php502 = threading.Thread(target=check502Task) - # php502 = setDaemon(php502) - # php502.start() + # PHP 502错误检查线程 + php502 = threading.Thread(target=check502Task) + php502 = setDaemon(php502) + php502.start() - # # OpenResty Restart At Once Start - # oraos = threading.Thread(target=openrestyRestartAtOnce) - # oraos = setDaemon(oraos) - # oraos.start() + # OpenResty Restart At Once Start + oraos = threading.Thread(target=openrestyRestartAtOnce) + oraos = setDaemon(oraos) + oraos.start() + # OpenResty Auto Restart Start + oar = threading.Thread(target=openrestyAutoRestart) + oar = setDaemon(oar) + oar.start() - # # OpenResty Auto Restart Start - # oar = threading.Thread(target=openrestyAutoRestart) - # oar = setDaemon(oar) - # oar.start() + # Panel Plugin Status Check + pps = threading.Thread(target=panelPluginStatusCheck) + pps = setDaemon(pps) + pps.start() - - # # Panel Restart Start - # rps = threading.Thread(target=restartPanelService) - # rps = setDaemon(rps) - # rps.start() + # Panel Restart Start + rps = threading.Thread(target=restartPanelService) + rps = setDaemon(rps) + rps.start() # 面板后台任务 startPanelTask() if __name__ == "__main__": - with app.app_context(): - run() + run() diff --git a/panel_tools.py b/panel_tools.py index ff4fa3d79..786005d18 100755 --- a/panel_tools.py +++ b/panel_tools.py @@ -23,13 +23,9 @@ web_dir = os.getcwd() + "/web" os.chdir(web_dir) sys.path.append(web_dir) +from utils.firewall import Firewall as MwFirewall import core.mw as mw -import core.db as db - -# cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' -# info = mw.execShell(cmd) -# p = "/usr/local/lib/" + info[0].strip() + "/site-packages" -# sys.path.append(p) +import thisdb INIT_DIR = "/etc/rc.d/init.d" if mw.isAppleSystem(): @@ -45,33 +41,54 @@ def mw_input_cmd(msg): in_val = input(msg) return in_val +def getRemainLen(cmd, max_length=100): + cmd_len = len(cmd) + cmd_u8_len = len(cmd.encode('utf-8')) + return max_length-int((cmd_u8_len - cmd_len)/2+cmd_len) def mwcli(mw_input=0): - raw_tip = "======================================================" + panel_dir = mw.getPanelDir() + + raw_tip = "========================================================================" if not mw_input: - print("===============mdserver-web cli tools=================") - print("(1) 重启面板服务") - print("(2) 停止面板服务") - print("(3) 启动面板服务") - print("(4) 重载面板服务") - print("(5) 修改面板端口") - print("(10) 查看面板默认信息") - print("(11) 修改面板密码") - print("(12) 修改面板用户名") - print("(13) 显示面板错误日志") - print("(20) 关闭BasicAuth认证") - print("(21) 解除域名绑定") - print("(22) 解除面板SSL绑定") - print("(23) 开启IPV6支持") - print("(24) 关闭IPV6支持") - print("(25) 开启防火墙SSH端口") - print("(26) 关闭二次验证") - print("(27) 查看防火墙信息") - print("(100) 开启PHP52显示") - print("(101) 关闭PHP52显示") - print("(200) 切换Linux系统软件源") - print("(201) 简单速度测试") - print("(0) 取消") + print("========================mdserver-web cli tools==========================") + cmd_list = [ + '(1) 重启面板服务', + '(2) 停止面板服务', + '(3) 启动面板服务', + '(4) 重载面板服务', + '(5) 修改面板端口', + '(10) 查看面板默认信息', + '(11) 修改面板密码', + '(12) 修改面板用户名', + '(13) 显示面板错误日志', + '(14) 关闭面板访问', + '(15) 开启面板访问', + '(20) 关闭BasicAuth认证', + '(21) 解除域名绑定', + '(22) 解除面板SSL绑定', + '(23) 开启IPV6支持', + '(24) 关闭IPV6支持', + '(25) 开启防火墙SSH端口', + '(26) 关闭二次验证', + '(27) 查看防火墙信息', + '(28) 自动识别防火墙端口到面板', + '(100) 开启PHP52显示', + '(101) 关闭PHP52显示', + '(200) 切换Linux系统软件源', + '(201) 简单速度测试', + '(0) 取消' + ] + cmd_list_num = len(cmd_list) + for index in range(cmd_list_num): + cmd = cmd_list[index] + if index % 2 == 0: + if index == (cmd_list_num-1): + print(cmd) + else: + print(cmd + " " * getRemainLen(cmd, 40), end="") + if index % 2 == 1: + print(cmd) print(raw_tip) try: mw_input = input("请输入命令编号:") @@ -81,8 +98,8 @@ def mwcli(mw_input=0): mw_input = 0 nums = [ - 1, 2, 3, 4, 5, 10, 11, 12, 13, - 20, 21, 22, 23, 24, 25, 26, 27, + 1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 100, 101, 200, 201 ] @@ -103,72 +120,90 @@ def mwcli(mw_input=0): in_port = mw_input_cmd("请输入新的面板端口:") in_port_int = int(in_port.strip()) if in_port_int < 65536 and in_port_int > 0: - import firewall_api - firewall_api.firewall_api().addAcceptPortArgs( - in_port, 'WEB面板[TOOLS修改]', 'port') - mw.writeFile('data/port.pl', in_port) + MwFirewall.instance().addAcceptPort(in_port, 'WEB面板[TOOLS修改]', 'port') + panel_port = panel_dir + '/data/port.pl' + mw.writeFile(panel_port, in_port) os.system(INIT_CMD + " restart_panel") os.system(INIT_CMD + " default") else: - print("|-端口范围在0-65536之间") + mw.echoInfo("端口范围在0-65536之间") return elif mw_input == 10: os.system(INIT_CMD + " default") elif mw_input == 11: input_pwd = mw_input_cmd("请输入新的面板密码:") if len(input_pwd.strip()) < 5: - print("|-错误,密码长度不能小于5位") + mw.echoInfo("错误,密码长度不能小于5位") return set_panel_pwd(input_pwd.strip(), True) elif mw_input == 12: input_user = mw_input_cmd("请输入新的面板用户名(>=5位):") set_panel_username(input_user.strip()) elif mw_input == 13: - os.system('tail -100 ' + mw.getPanelDir() + '/logs/error.log') - elif mw_input == 20: - basic_auth = 'data/basic_auth.json' - if os.path.exists(basic_auth): - os.remove(basic_auth) - os.system(INIT_CMD + " restart") - print("|-关闭basic_auth成功") - elif mw_input == 21: - bind_domain = 'data/bind_domain.pl' - if os.path.exists(bind_domain): - os.remove(bind_domain) - os.system(INIT_CMD + " unbind_domain") - print("|-解除域名绑定成功") - elif mw_input == 22: - ssl_choose = 'ssl/choose.pl' - if os.path.exists(ssl_choose): - os.remove(ssl_choose) - os.system(INIT_CMD + " unbind_ssl") - print("|-解除面板SSL绑定成功") - elif mw_input == 23: - listen_ipv6 = 'data/ipv6.pl' - if not os.path.exists(listen_ipv6): - mw.writeFile(listen_ipv6,'True') - os.system(INIT_CMD + " restart") - print("|-开启IPv6支持了") + os.system('tail -100 ' + panel_dir + '/logs/panel_error.log') + elif mw_input == 14: + admin_close = thisdb.getOption('admin_close') + if admin_close == 'no': + thisdb.setOption('admin_close', 'yes') + mw.echoInfo("关闭面板访问成功!") else: - print("|-已开启IPv6支持!") - elif mw_input == 24: - listen_ipv6 = 'data/ipv6.pl' + mw.echoInfo("已关闭面板访问!") + elif mw_input == 15: + admin_close = thisdb.getOption('admin_close') + if admin_close == 'yes': + thisdb.setOption('admin_close', 'no') + mw.echoInfo("开启面板访问成功!") + else: + mw.echoInfo("已开启面板访问!") + elif mw_input == 20: + basic_auth = thisdb.getOptionByJson('basic_auth', default={'open':False}) + if basic_auth['open']: + basic_auth['open'] = False + thisdb.setOption('basic_auth', json.dumps(basic_auth)) + os.system(INIT_CMD + " restart") + mw.echoInfo("关闭basic_auth成功") + elif mw_input == 21: + panel_domain = thisdb.getOption('panel_domain', default='') + if panel_domain != '': + thisdb.setOption('panel_domain', '') + os.system(INIT_CMD + " unbind_domain") + mw.echoInfo("解除域名绑定成功") + else: + mw.echoInfo("面板未绑定域名!") + elif mw_input == 22: + panel_ssl = thisdb.getOptionByJson('panel_ssl', default={'open':False}) + if panel_ssl['open']: + panel_ssl['open'] = False + thisdb.setOption('panel_ssl', json.dumps(panel_ssl)) + os.system(INIT_CMD + " unbind_ssl") + mw.echoInfo("解除面板SSL绑定成功") + elif mw_input == 23: + listen_ipv6 = panel_dir + '/data/ipv6.pl' if not os.path.exists(listen_ipv6): - print("|-已关闭IPv6支持!") + mw.writeFile(listen_ipv6, 'True') + os.system(INIT_CMD + " restart") + mw.echoInfo("开启IPv6支持了") + else: + mw.echoInfo("已开启IPv6支持!") + elif mw_input == 24: + listen_ipv6 = panel_dir + '/data/ipv6.pl' + if not os.path.exists(listen_ipv6): + mw.echoInfo("已关闭IPv6支持!") else: os.remove(listen_ipv6) os.system(INIT_CMD + " restart") - print("|-关闭IPv6支持了") + mw.echoInfo("关闭IPv6支持了") elif mw_input == 25: open_ssh_port() - print("|-已开启!") + mw.echoInfo("已开启!") elif mw_input == 26: - auth_secret = 'data/auth_secret.pl' - if os.path.exists(auth_secret): - os.remove(auth_secret) - print("|-关闭二次验证成功!") + two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False}) + if two_step_verification['open']: + two_step_verification['open'] = False + thisdb.setOption('two_step_verification', json.dumps(two_step_verification)) + mw.echoInfo("关闭二次验证成功!") else: - print("|-二次验证已关闭!") + mw.echoInfo("二次验证已关闭!") elif mw_input == 27: cmd = 'which ufw' run_cmd = False @@ -184,22 +219,26 @@ def mwcli(mw_input=0): os.system('firewall-cmd --list-all') if not run_cmd: mw.echoInfo("未检测到防火墙!") + elif mw_input == 28: + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().aIF() + mw.echoInfo("执行自动识别防火墙端口到面板成功!") elif mw_input == 100: - php_conf = 'plugins/php/info.json' + php_conf = panel_dir + '/plugins/php/info.json' if os.path.exists(php_conf): cont = mw.readFile(php_conf) cont = re.sub("\"53\"", "\"52\",\"53\"", cont) cont = re.sub("\"5.3.29\"", "\"5.2.17\",\"5.3.29\"", cont) mw.writeFile(php_conf, cont) - print("|-执行PHP52显示成功!") + mw.echoInfo("执行PHP52显示成功!") elif mw_input == 101: - php_conf = 'plugins/php/info.json' + php_conf = panel_dir + '/plugins/php/info.json' if os.path.exists(php_conf): cont = mw.readFile(php_conf) cont = re.sub("\"52\",", "", cont) cont = re.sub("\"5.2.17\",", cont) mw.writeFile(php_conf, cont) - print("|-执行PHP52隐藏成功!") + mw.echoInfo("执行PHP52隐藏成功!") elif mw_input == 200: os.system(INIT_CMD + " mirror") elif mw_input == 201: @@ -207,69 +246,65 @@ def mwcli(mw_input=0): def open_ssh_port(): - import firewall_api + find_ssh_port_cmd = "cat /etc/ssh/sshd_config | grep '^Port \\d*' | tail -1" cmd_data = mw.execShell(find_ssh_port_cmd) ssh_port = cmd_data[0].replace("Port ", '').strip() if ssh_port == '': ssh_port = '22' - print("|-SSH端口: "+ str(ssh_port)) - firewall_api.firewall_api().addAcceptPortArgs(ssh_port, 'SSH远程管理服务', 'port') + mw.echoInfo("SSH端口: "+ str(ssh_port)) + MwFirewall.instance().addAcceptPort(ssh_port, 'SSH远程管理服务', 'port') return True def set_panel_pwd(password, ncli=False): - # 设置面板密码 - import db - sql = db.Sql() - result = sql.table('users').where('id=?', (1,)).setField( - 'password', mw.md5(password)) - username = sql.table('users').where('id=?', (1,)).getField('username') + info = thisdb.getUserByRoot() + thisdb.setUserByRoot(password=password) if ncli: - print("|-用户名: " + username) - print("|-新密码: " + password) + mw.echoInfo("username: " + info['name']) + mw.echoInfo("password: " + password) else: print(username) def show_panel_pwd(): - # 设置面板密码 - import db - sql = db.Sql() - password = sql.table('users').where('id=?', (1,)).getField('password') + # 面板密码展示 + info = thisdb.getUserByRoot() + defailt_pwd_file = mw.getPanelDir()+'/data/default.pl' + pwd = '' + if os.path.exists(defailt_pwd_file): + pwd = mw.readFile(defailt_pwd_file).strip() - file_pwd = '' - if os.path.exists('data/default.pl'): - file_pwd = mw.readFile('data/default.pl').strip() - - if mw.md5(file_pwd) == password: - print('password: ' + file_pwd) + if mw.md5(pwd) == info['password']: + mw.echoInfo('password: ' + pwd) return - print("password has been changed!") + print("*-password has been changed!") + +def show_panel_adminpath(): + admin_path = thisdb.getOption('admin_path') + print('/'+admin_path) def set_panel_username(username=None): # 随机面板用户名 - import db - sql = db.Sql() if username: if len(username) < 5: - print("|-错误,用户名长度不能少于5位") + mw.echoInfo("错误,用户名长度不能少于5位") return if username in ['admin', 'root']: - print("|-错误,不能使用过于简单的用户名") + mw.echoInfo("错误,不能使用过于简单的用户名") return - sql.table('users').where('id=?', (1,)).setField('username', username) - print("|-新用户名: %s" % username) + thisdb.setUserByRoot(name=username) + mw.echoInfo("username: %s" % username) return - username = sql.table('users').where('id=?', (1,)).getField('username') - if username == 'admin': + info = thisdb.getUserByRoot() + if info['name'] == 'admin': username = mw.getRandomString(8).lower() - sql.table('users').where('id=?', (1,)).setField('username', username) - print('username: ' + username) + thisdb.setUserByRoot(name=username) + mw.echoInfo('username: ' + info['name']) def getServerIp(): @@ -278,10 +313,25 @@ def getServerIp(): # "curl --insecure -{} -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text".format(version)) ip = mw.execShell( "curl --insecure -{} -sS --connect-timeout 5 -m 60 https://ip.cachecha.com/?format=text".format(version)) - print(ip[0]) + # print(ip[0]) + return ip[0] -if __name__ == "__main__": +def getPanelSslType(): + scheme = 'http' + panel_ssl = thisdb.getOptionByJson('panel_ssl', default={'open':False}) + if panel_ssl['open']: + scheme = 'https' + return scheme + +def getPanelBindDomain(): + return thisdb.getOption('panel_domain', default='') + + +def main(): + if len(sys.argv) == 1: + print('ERROR: Parameter error!') + exit(-2) method = sys.argv[1] if method == 'panel': set_panel_pwd(sys.argv[2]) @@ -292,8 +342,16 @@ if __name__ == "__main__": set_panel_username() elif method == 'password': show_panel_pwd() + elif method == 'test': + thisdb.getOption('admin_path') + elif method == 'admin_path': + show_panel_adminpath() elif method == 'getServerIp': - getServerIp() + print(getServerIp()) + elif method == 'panel_ssl_type': + print(getPanelSslType()) + elif method == 'panel_bind_domain': + print(getPanelBindDomain()) elif method == "cli": clinum = 0 try: @@ -304,3 +362,6 @@ if __name__ == "__main__": mwcli(clinum) else: print('ERROR: Parameter error') + +if __name__ == "__main__": + main() diff --git a/plugins/acme_pandominassl_apply/index.py b/plugins/acme_pandominassl_apply/index.py index e7ba431a2..ce92ed492 100755 --- a/plugins/acme_pandominassl_apply/index.py +++ b/plugins/acme_pandominassl_apply/index.py @@ -8,8 +8,12 @@ import re import requests import json -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -127,7 +131,7 @@ def status(): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$SERVER_APP}', service_path + '/redis') content = content.replace('{$REDIS_PASS}', mw.getRandomString(10)) @@ -163,7 +167,6 @@ def pSqliteDb(dbname='dnsapi'): def initDreplace(): file_tpl = getInitDTpl() - service_path = os.path.dirname(os.getcwd()) initD_path = getServerDir() + '/init.d' if not os.path.exists(initD_path): @@ -560,7 +563,7 @@ def runHookPy(domain,path): # print(domain,path) run_log = runLog() hook_file = getConf() - cmd = 'cd '+mw.getRunDir() + cmd = 'cd '+mw.getPanelDir() cmd += ' && python3 '+hook_file + ' ' + domain + ' ' + path cmd += ' >> '+ run_log print(cmd) @@ -641,7 +644,7 @@ def getHookIdCmd(): if not data[0]: return data[1] - cmd = "cd "+mw.getRunDir()+" " + cmd = "cd "+mw.getPanelDir()+" " cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_hook_id 1.0 {"id":"'+args['id']+'"}' return mw.returnJson(True, 'ok',cmd) @@ -658,7 +661,7 @@ def runHookId(): return 'run hook '+args['id']+' end' def runHookCmd(): - cmd = "cd "+mw.getRunDir()+" " + cmd = "cd "+mw.getPanelDir()+" " cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_hook' return mw.returnJson(True, 'ok',cmd) @@ -735,7 +738,7 @@ def runSyncCfData(): return '' def runSyncCfCmd(): - cmd = "cd "+mw.getRunDir()+" " + cmd = "cd "+mw.getPanelDir()+" " cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_sync_cf_data' return mw.returnJson(True, 'ok',cmd) @@ -788,7 +791,7 @@ def runSyncDnsPodData(): return '' def runSyncDnsPodCmd(): - cmd = "cd "+mw.getRunDir()+" " + cmd = "cd "+mw.getPanelDir()+" " cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_sync_dnspod_data' return mw.returnJson(True, 'ok',cmd) diff --git a/plugins/acme_pandominassl_apply/install.sh b/plugins/acme_pandominassl_apply/install.sh index 4f4ce632f..42591f085 100755 --- a/plugins/acme_pandominassl_apply/install.sh +++ b/plugins/acme_pandominassl_apply/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 # curl https://get.acme.sh | sh @@ -31,7 +30,7 @@ fi # pip install cloudflare Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/acme_pandominassl_apply echo "${VERSION}" > $serverPath/acme_pandominassl_apply/version.pl diff --git a/plugins/acme_pandominassl_apply/tool_cron.py b/plugins/acme_pandominassl_apply/tool_cron.py index 5e16865d3..5672065fc 100644 --- a/plugins/acme_pandominassl_apply/tool_cron.py +++ b/plugins/acme_pandominassl_apply/tool_cron.py @@ -72,7 +72,7 @@ def createBgTaskByName(name): import crontab_api api = crontab_api.crontab_api() - mw_dir = mw.getRunDir() + mw_dir = mw.getPanelDir() cmd = ''' mw_dir=%s rname=%s diff --git a/plugins/alist/index.py b/plugins/alist/index.py index 3f14b33af..c22f551f4 100755 --- a/plugins/alist/index.py +++ b/plugins/alist/index.py @@ -5,9 +5,14 @@ import io import os import time import re +import json -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -92,18 +97,40 @@ def readConfigTpl(): content = contentReplace(content) return mw.returnJson(True, 'ok', content) +def __release_port(port, ps = '开启端口'): + try: + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().addAcceptPort(port, ps, 'port') + return port + except Exception as e: + return "Release failed {}".format(e) + +def __delete_port(port): + try: + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().delAcceptPortCmd(port, 'tcp') + return port + except Exception as e: + return "Delete failed {}".format(e) + + +def openPort(): + content = mw.readFile(getConf()) + data = json.loads(content) + http_port = data['scheme']['http_port'] + __release_port(http_port, 'alist') + return True def status(): - data = mw.execShell( - "ps aux|grep alist |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'") - + cmd = "ps aux|grep alist |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'" + data = mw.execShell(cmd) if data[0] == '': return 'stop' return 'start' def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName()) return content @@ -111,7 +138,7 @@ def contentReplace(content): def initDreplace(): file_tpl = getInitDTpl() - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getServerDir() initD_path = getServerDir() + '/init.d' if not os.path.exists(initD_path): @@ -124,6 +151,7 @@ def initDreplace(): content = content.replace('{$SERVER_PATH}', service_path) mw.writeFile(file_bin, content) mw.execShell('chmod +x ' + file_bin) + # systemd systemDir = mw.systemdCfgDir() @@ -135,7 +163,7 @@ def initDreplace(): se_content = se_content.replace('{$SERVER_PATH}', service_path) mw.writeFile(systemService, se_content) mw.execShell('systemctl daemon-reload') - + time.sleep(1) return file_bin @@ -247,7 +275,6 @@ def clearCopyTask(): return mw.returnJson(True, '清空成功并重启服务!') def homePage(): - import json content = mw.readFile(getConf()) data = json.loads(content) http_port = data['scheme']['http_port'] diff --git a/plugins/alist/install.sh b/plugins/alist/install.sh index a0b26c1a6..900ab6181 100755 --- a/plugins/alist/install.sh +++ b/plugins/alist/install.sh @@ -10,7 +10,6 @@ serverPath=$(dirname "$rootPath") # cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/alist && bash install.sh install 3.37.4 # cd /www/server/mdserver-web/plugins/alist && bash install.sh install 3.37.4 -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 sysArch=`arch` diff --git a/plugins/alist/js/alist.js b/plugins/alist/js/alist.js index f79fb51f6..e66b4438a 100755 --- a/plugins/alist/js/alist.js +++ b/plugins/alist/js/alist.js @@ -83,6 +83,7 @@ function alistReadme(){ var readme = '
    '; + readme += '
  • 手动开启默认端口:5244
  • '; readme += '
  • 默认admin:admin
  • '; readme += '
  • 手动改密码: cd /www/server/alist && ./alist admin set NEW_PASSWORD
  • '; readme += '
'; diff --git a/plugins/backup_ftp/class/ftp_client.py b/plugins/backup_ftp/class/ftp_client.py index 97df92e32..53a0e7ea8 100644 --- a/plugins/backup_ftp/class/ftp_client.py +++ b/plugins/backup_ftp/class/ftp_client.py @@ -14,8 +14,12 @@ import json import paramiko import ftplib -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw DEBUG = True BLOCK_SIZE = 1024 * 1024 * 2 @@ -136,7 +140,7 @@ class FtpPSClient: "database": "db", "path": "path", } - file_regx = prefix_dict.get(data_type) + "_(.+)_20\d+_\d+\." + file_regx = prefix_dict.get(data_type) + r"_(.+)_20\d+_\d+\." sub_search = re.search(file_regx, file_name) sub_path_name = "" if sub_search: diff --git a/plugins/backup_ftp/index.py b/plugins/backup_ftp/index.py index 34dd3f959..9b292b0db 100755 --- a/plugins/backup_ftp/index.py +++ b/plugins/backup_ftp/index.py @@ -12,9 +12,13 @@ if sys.platform != "darwin": os.chdir("/www/server/mdserver-web") -sys.path.append(os.getcwd() + "/class/core") -import mw -import db +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw +import core.db as db _ver = sys.version_info is_py2 = (_ver[0] == 2) @@ -184,7 +188,6 @@ def findPathName(path, filename): return l def backupAllFunc(stype): - os.chdir(mw.getRunDir()) name = sys.argv[2] num = sys.argv[3] @@ -196,20 +199,15 @@ def backupAllFunc(stype): } backups = [] - sql = db.Sql() - # print("stype:", stype) # 提前获取-清理多余备份 if stype == 'site': - pid = sql.table('sites').where('name=?', (name,)).getField('id') - backups = sql.table('backup').where( - 'type=? and pid=?', ('0', pid)).field('id,filename').select() + pid = mw.M('sites').where('name=?', (name,)).getField('id') + backups = mw.M('backup').where('type=? and pid=?', ('0', pid)).field('id,filename').select() if stype == 'database': db_path = mw.getServerDir() + '/mysql' - pid = mw.M('databases').dbPos(db_path, 'mysql').where( - 'name=?', (name,)).getField('id') - backups = sql.table('backup').where( - 'type=? and pid=?', ('1', pid)).field('id,filename').select() + pid = mw.M('databases').dbPos(db_path, 'mysql').where('name=?', (name,)).getField('id') + backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select() if stype == 'path': backup_dir = mw.getBackupDir() backup_path = backup_dir + '/path' @@ -220,18 +218,15 @@ def backupAllFunc(stype): if stype.find('database_') > -1: plugin_name = stype.replace('database_', '') db_path = mw.getServerDir() + '/' + plugin_name - pid = mw.M('databases').dbPos(db_path, 'mysql').where( - 'name=?', (name,)).getField('id') - backups = sql.table('backup').where( - 'type=? and pid=?', ('1', pid)).field('id,filename').select() + pid = mw.M('databases').dbPos(db_path, 'mysql').where('name=?', (name,)).getField('id') + backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select() args = stype + " " + name + " " + num - cmd = 'python3 ' + mw.getRunDir() + '/scripts/backup.py ' + args + cmd = 'python3 ' + mw.getPanelDir() + '/scripts/backup.py ' + args if stype.find('database_') > -1: plugin_name = stype.replace('database_', '') args = "database " + name + " " + num - cmd = 'python3 ' + mw.getRunDir() + '/plugins/' + plugin_name + \ - '/scripts/backup.py ' + args + cmd = 'python3 ' + mw.getPanelDir() + '/plugins/' + plugin_name + '/scripts/backup.py ' + args os.system(cmd) @@ -251,8 +246,7 @@ def backupAllFunc(stype): _name = 'path_{}'.format(os.path.basename(name)) find_path = mw.getBackupDir() + '/path/'+_name - find_new_file = "ls " + find_path + \ - "_* | grep '.gz' | cut -d \ -f 1 | awk 'END {print}'" + find_new_file = "ls " + find_path + "_* | grep '.gz' | cut -d \\ -f 1 | awk 'END {print}'" filename = mw.execShell(find_new_file)[0].strip() if filename == "": diff --git a/plugins/backup_ftp/install.sh b/plugins/backup_ftp/install.sh index 12f3a5b8c..9d37cbf5d 100755 --- a/plugins/backup_ftp/install.sh +++ b/plugins/backup_ftp/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl sys_os=`uname` @@ -15,7 +14,7 @@ Install_App() { mkdir -p ${serverPath}/backup_ftp echo "${1}" > ${serverPath}/backup_ftp/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' } diff --git a/plugins/clean/index.py b/plugins/clean/index.py index d9c29d5ed..d6d59b36c 100755 --- a/plugins/clean/index.py +++ b/plugins/clean/index.py @@ -5,8 +5,12 @@ import io import os import time -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): diff --git a/plugins/clean/install.sh b/plugins/clean/install.sh index c8cfe95d5..d52b1c1c2 100755 --- a/plugins/clean/install.sh +++ b/plugins/clean/install.sh @@ -10,7 +10,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 if [ -f ${rootPath}/bin/activate ];then @@ -20,19 +19,19 @@ fi Install_app() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/clean cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py start echo "${VERSION}" > $serverPath/clean/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' } Uninstall_app() { cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py stop rm -rf $serverPath/clean - echo "Uninstall_clean" > $install_tmp + echo "Uninstall_clean" } action=$1 diff --git a/plugins/clean/tool_task.py b/plugins/clean/tool_task.py index bf9654e2d..cc762f7c9 100644 --- a/plugins/clean/tool_task.py +++ b/plugins/clean/tool_task.py @@ -6,8 +6,13 @@ import os import time import json -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw +from utils.crontab import crontab as MwCrontab app_debug = False @@ -33,10 +38,9 @@ def getTaskConf(): def getConfigData(): - try: + conf = getTaskConf() + if os.path.exists(conf): return json.loads(mw.readFile(getTaskConf())) - except: - pass return { "task_id": -1, "period": "day-n", @@ -64,10 +68,8 @@ def createBgTaskByName(name): if res and res["id"] == args["task_id"]: print("计划任务已经存在!") return True - import crontab_api - api = crontab_api.crontab_api() - mw_dir = mw.getRunDir() + mw_dir = mw.getPanelDir() cmd = ''' mw_dir=%s rname=%s @@ -94,10 +96,10 @@ logs_file=$plugin_path/${rname}.log 'stype': "toShell", 'sname': '', 'sbody': cmd, - 'urladdress': '', + 'url_address': '', } - task_id = api.add(params) + task_id = MwCrontab.instance().add(params) if task_id > 0: args["task_id"] = task_id args["name"] = name @@ -110,9 +112,7 @@ def removeBgTask(): res = mw.M("crontab").field("id, name").where( "id=?", (cfg["task_id"],)).find() if res and res["id"] == cfg["task_id"]: - import crontab_api - api = crontab_api.crontab_api() - data = api.delete(cfg["task_id"]) + data = MwCrontab.instance().delete(cfg["task_id"]) if data[0]: cfg["task_id"] = -1 mw.writeFile(getTaskConf(), json.dumps(cfg)) diff --git a/plugins/cloudreve/index.py b/plugins/cloudreve/index.py index a312a2028..d01cf0865 100755 --- a/plugins/cloudreve/index.py +++ b/plugins/cloudreve/index.py @@ -6,8 +6,12 @@ import os import time import re -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -103,7 +107,7 @@ def status(): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName()) return content @@ -111,7 +115,7 @@ def contentReplace(content): def initDreplace(): file_tpl = getInitDTpl() - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getServerDir() initD_path = getServerDir() + '/init.d' if not os.path.exists(initD_path): diff --git a/plugins/cloudreve/install.sh b/plugins/cloudreve/install.sh index 0de5e44ef..11ac5d927 100755 --- a/plugins/cloudreve/install.sh +++ b/plugins/cloudreve/install.sh @@ -10,7 +10,6 @@ serverPath=$(dirname "$rootPath") # cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/cloudreve && bash install.sh install 3.8.3 # cd /www/server/mdserver-web/plugins/cloudreve && bash install.sh install 3.8.3 -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 sysArch=`arch` diff --git a/plugins/data_query/index.py b/plugins/data_query/index.py index 9bc0dd431..0d8061b61 100755 --- a/plugins/data_query/index.py +++ b/plugins/data_query/index.py @@ -6,8 +6,12 @@ import os import time import re -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): diff --git a/plugins/data_query/install.sh b/plugins/data_query/install.sh index b3b580f17..7a45a82a2 100755 --- a/plugins/data_query/install.sh +++ b/plugins/data_query/install.sh @@ -7,12 +7,11 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/data_query echo "${VERSION}" > $serverPath/data_query/version.pl diff --git a/plugins/data_query/nosql_memcached.py b/plugins/data_query/nosql_memcached.py index 4b392aad8..b32ef5690 100755 --- a/plugins/data_query/nosql_memcached.py +++ b/plugins/data_query/nosql_memcached.py @@ -11,11 +11,12 @@ import re import json import pymemcache -try: - sys.path.append(os.getcwd() + "/class/core") - import mw -except Exception as e: - import core.mw as mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw def singleton(cls): @@ -68,7 +69,7 @@ class nosqlMemcached(): if not mem_content: return False keys = ["bind", "PORT"] - rep = 'PORT\s*=\s*(.*)' + rep = r'PORT\s*=\s*(.*)' port_re = re.search(rep, mem_content) if port_re: result['port'] = int(port_re.groups()[0].strip()) @@ -105,7 +106,7 @@ class nosqlMemcachedCtr(): item_no = [] for i in m_items: - item_match = b'items:(\d*?):number' + item_match = rb'items:(\d*?):number' item_match_re = re.search(item_match, i) if item_match_re: v = item_match_re.groups()[0].strip() diff --git a/plugins/data_query/nosql_mongodb.py b/plugins/data_query/nosql_mongodb.py index 286c63e45..9b22b9118 100755 --- a/plugins/data_query/nosql_mongodb.py +++ b/plugins/data_query/nosql_mongodb.py @@ -12,11 +12,12 @@ import yaml from bson.objectid import ObjectId from bson.json_util import dumps -try: - sys.path.append(os.getcwd() + "/class/core") - import mw -except Exception as e: - import core.mw as mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw def singleton(cls): _instance = {} diff --git a/plugins/data_query/nosql_redis.py b/plugins/data_query/nosql_redis.py index 65eb675e5..e9935f211 100755 --- a/plugins/data_query/nosql_redis.py +++ b/plugins/data_query/nosql_redis.py @@ -7,16 +7,12 @@ import time import re import redis +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) -try: - sys.path.append(os.getcwd() + "/class/core") - import mw -except Exception as e: - import core.mw as mw - - -# def getPluginName(): -# return 'data_query' +import core.mw as mw def singleton(cls): _instance = {} @@ -47,7 +43,8 @@ class nosqlRedis(): if self.__DB_HOST in ['127.0.0.1', 'localhost']: redis_path = "{}/redis".format(mw.getServerDir()) - if not os.path.exists(redis_path): return False + if not os.path.exists(redis_path): + return False if not self.__DB_LOCAL: self.__DB_PASS = self.__config['requirepass'] @@ -75,7 +72,7 @@ class nosqlRedis(): keys = ["bind", "port", "timeout", "maxclients", "databases", "requirepass", "maxmemory"] for k in keys: v = "" - rep = "\n%s\s+(.+)" % k + rep = r"\n%s\s+(.+)" % k group = re.search(rep, redis_conf) if not group: if k == "maxmemory": diff --git a/plugins/data_query/sql_mysql.py b/plugins/data_query/sql_mysql.py index ee8c83fbf..6179f00e3 100755 --- a/plugins/data_query/sql_mysql.py +++ b/plugins/data_query/sql_mysql.py @@ -8,11 +8,7 @@ import re import pymongo import json -try: - sys.path.append(os.getcwd() + "/class/core") - import mw -except Exception as e: - import core.mw as mw +import core.mw as mw def singleton(cls): _instance = {} @@ -38,24 +34,18 @@ class nosqlMySQL(): __DB_LOCAL = None def __init__(self): - self.__config = self.get_options(None) - + pass + + def setSid(self, sid): + self.__config = self.get_options(sid=sid) def conn(self): - - if self.__DB_HOST in ['127.0.0.1', 'localhost']: - my_path = "{}/mysql".format(mw.getServerDir()) - if not os.path.exists(my_path): return False - - if not self.__DB_LOCAL: - # print(self.__config) - self.__DB_PORT = int(self.__config['port']) - self.__DB_USER = self.__config['username'] - self.__DB_PASS = self.__config['password'] - self.__DB_SOCKET = self.__config['socket'] + self.__DB_PORT = int(self.__config['port']) + self.__DB_USER = self.__config['username'] + self.__DB_PASS = self.__config['password'] + self.__DB_SOCKET = self.__config['socket'] try: - db = mw.getMyORM() db.setPort(self.__DB_PORT) db.setPwd(self.__DB_PASS) @@ -68,42 +58,37 @@ class nosqlMySQL(): self.__DB_ERR = mw.get_error_info() return False - def sqliteDb(self,dbname='databases'): - my_root_path = mw.getServerDir() +'/mysql' + def sqliteDb(self, db_pos_name, dbname='databases'): + mydb_path = mw.getServerDir() +'/'+db_pos_name name = 'mysql' - conn = mw.M(dbname).dbPos(my_root_path, name) + conn = mw.M(dbname).dbPos(mydb_path, name) return conn # 获取配置项 - def get_options(self, get=None): + def get_options(self, sid=None): result = {} - - - my_cnf_path = "{}/mysql/etc/my.cnf".format(mw.getServerDir()) - my_content = mw.readFile(my_cnf_path) - if not my_content: return False - - mysql_pass = self.sqliteDb('config').where('id=?', (1,)).getField('mysql_root') - result['password'] = mysql_pass - result['username'] = 'root' - keys = ["bind_ip", "port"] - + result['socket'] = '' + result['port'] = 3306 result['host'] = '127.0.0.1' - rep = 'port\s*=\s*(.*)' + result['username'] = 'root' - port_re = re.search(rep, my_content) - if port_re: - result['port'] = int(port_re.groups()[0].strip()) - else: - result['port'] = 3306 + if sid in ['mysql', 'mysql-apt', 'mysql-yum']: + my_cnf_path = "{}/{}/etc/my.cnf".format(mw.getServerDir(),sid) - socket_rep = 'socket\s*=\s*(.*)' - socket_re = re.search(socket_rep, my_content) - if socket_re: - result['socket'] = socket_re.groups()[0].strip() - else: - result['socket'] = '' + mydb_content = mw.readFile(my_cnf_path) + if not mydb_content: return False + mysql_pass = self.sqliteDb(sid, 'config').where('id=?', (1,)).getField('mysql_root') + result['password'] = mysql_pass + + rep = r'port\s*=\s*(.*)' + port_re = re.search(rep, mydb_content) + if port_re: + result['port'] = int(port_re.groups()[0].strip()) + socket_rep = r'socket\s*=\s*(.*)' + socket_re = re.search(socket_rep, mydb_content) + if socket_re: + result['socket'] = socket_re.groups()[0].strip() return result def set_host(self, host, port, name, username, password, prefix=''): @@ -118,7 +103,22 @@ class nosqlMySQL(): self.__DB_LOCAL = 1 return self + def getServerList(self): + my_cnf_path = "{}/mysql/etc/my.cnf".format(mw.getServerDir()) + data = [] + local_mysql = "{}/mysql/etc/my.cnf".format(mw.getServerDir()) + if os.path.exists(local_mysql): + data.append({'name':'本地服务器', 'val':'mysql'}) + + local_mysql_apt = "{}/mysql-apt/etc/my.cnf".format(mw.getServerDir()) + if os.path.exists(local_mysql_apt): + data.append({'name':'本地服务器[apt]', 'val':'mysql-apt'}) + + local_mysql_yum = "{}/mysql-yum/etc/my.cnf".format(mw.getServerDir()) + if os.path.exists(local_mysql_yum): + data.append({'name':'本地服务器[yum]', 'val':'mysql-yum'}) + return mw.returnData(True, 'ok', data) @singleton class nosqlMySQLCtr(): @@ -126,8 +126,13 @@ class nosqlMySQLCtr(): def __init__(self): pass - def getInstanceBySid(self, sid = 0): + def getServerList(self, args): instance = nosqlMySQL() + return instance.getServerList() + + def getInstanceBySid(self, sid): + instance = nosqlMySQL() + instance.setSid(sid) return instance def getDbList(self, args): @@ -280,7 +285,372 @@ class nosqlMySQLCtr(): rdata['list'] = result return mw.returnData(True,'ok', rdata) + def getNetRow(self, my_instance): + row = {} + + data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Com_select'") + row['select'] = data['Value'] + + data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Com_insert'") + row['insert'] = data['Value'] + + data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Com_update'") + row['update'] = data['Value'] + + data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Com_delete'") + row['delete'] = data['Value'] + + + + data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Bytes_received'") + row['recv_bytes'] = data['Value'] + + data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Bytes_sent'") + row['send_bytes'] = data['Value'] + return row + + + def getNetList(self, args): + from datetime import datetime + sid = args['sid'] + my_instance = self.getInstanceBySid(sid).conn() + if my_instance is False: + return mw.returnData(False,'无法链接') + + rdata = [] + row = {} + row1 = self.getNetRow(my_instance) + # 等待1秒 + time.sleep(1) + row2 = self.getNetRow(my_instance) + + data = my_instance.find("SHOW GLOBAL VARIABLES LIKE 'max_connections'") + row['max_conn'] = data['Value'] + + data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Threads_connected'") + row['conn'] = data['Value'] + + current_time = datetime.now() + row['current_time'] = current_time.strftime("%Y-%m-%d %H:%M:%S") + + row['select'] = int(row2['select']) - int(row1['select']) + row['insert'] = int(row2['insert']) - int(row1['insert']) + row['update'] = int(row2['update']) - int(row1['update']) + row['delete'] = int(row2['delete']) - int(row1['delete']) + + recv_per_second = int(row2['recv_bytes']) - int(row1['recv_bytes']) + send_per_second = int(row2['send_bytes']) - int(row1['send_bytes']) + + # 将每秒接收和发送数据量从字节转换为兆比特 + row['recv_mbps'] = "{:.2f}".format(recv_per_second * 8 / 1000000) + " MBit/s" + row['send_mbps'] = "{:.2f}".format(send_per_second * 8 / 1000000) + " MBit/s" + + rdata.append(row) + return mw.returnData(True, 'ok', rdata) + + + def getTopnList(self, args): + sid = args['sid'] + my_instance = self.getInstanceBySid(sid).conn() + if my_instance is False: + return mw.returnData(False,'无法链接') + + is_performance_schema = my_instance.find("SELECT @@performance_schema") + if is_performance_schema is None: + return mw.returnData(False,'异常中断,重试!') + + if is_performance_schema["@@performance_schema"] == 0: + msg = "performance_schema参数未开启。\n" + msg += "在my.cnf配置文件里添加performance_schema=1,并重启mysqld进程生效。" + return mw.returnData(False, msg) + + my_instance.execute("SET @sys.statement_truncate_len=4096") + data = my_instance.query("select query,db,last_seen,exec_count,max_latency,avg_latency from sys.statement_analysis order by exec_count desc, last_seen desc limit 20") + if data is None: + return mw.returnData(False, "查询失败!") + + filter_db = args['filter_db'] + if filter_db == 'yes': + new_data = [] + for x in data: + if x['db'] is not None: + new_data.append(x) + return mw.returnData(True, 'ok', new_data) + return mw.returnData(True, 'ok', data) + + # 查看重复或冗余的索引 + def getRedundantIndexes(self, args): + sid = args['sid'] + my_instance = self.getInstanceBySid(sid).conn() + if my_instance is False: + return mw.returnData(False,'无法链接') + + is_performance_schema = my_instance.find("SELECT @@performance_schema") + if is_performance_schema["@@performance_schema"] == 0: + msg = "performance_schema参数未开启。\n" + msg += "在my.cnf配置文件里添加performance_schema=1,并重启mysqld进程生效。" + return mw.returnData(False, msg) + + data = my_instance.query("select table_schema,table_name,redundant_index_name,redundant_index_columns,sql_drop_index from sys.schema_redundant_indexes") + if data is None: + return mw.returnData(False, "查询失败!") + # print(data) + return mw.returnData(True, 'ok', data) + + def redundantIndexesCmd(self, args): + sid = args['sid'] + my_instance = self.getInstanceBySid(sid).conn() + if my_instance is False: + return mw.returnData(False,'无法链接') + + is_performance_schema = my_instance.find("SELECT @@performance_schema") + if is_performance_schema["@@performance_schema"] == 0: + msg = "performance_schema参数未开启。\n" + msg += "在my.cnf配置文件里添加performance_schema=1,并重启mysqld进程生效。" + return mw.returnData(False, msg) + + data = my_instance.query("select table_schema,table_name,redundant_index_name,redundant_index_columns,sql_drop_index from sys.schema_redundant_indexes") + if data is None: + return mw.returnData(False, "查询失败!") + + index = int(args['index']) + + cmd = data[index]['sql_drop_index'] + + my_instance.execute(cmd) + return mw.returnData(True, '执行成功!') + + def getTableInfo(self, args): + from decimal import Decimal + + sid = args['sid'] + my_instance = self.getInstanceBySid(sid).conn() + if my_instance is False: + return mw.returnData(False,'无法链接') + + my_instance.execute("SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''))") + data = my_instance.query( + """ + SELECT t.TABLE_SCHEMA as TABLE_SCHEMA, t.TABLE_NAME as TABLE_NAME, t.ENGINE as ENGINE, + IFNULL(t.DATA_LENGTH/1024/1024/1024, 0) as DATA_LENGTH, + IFNULL(t.INDEX_LENGTH/1024/1024/1024, 0) as INDEX_LENGTH, + IFNULL((DATA_LENGTH+INDEX_LENGTH)/1024/1024/1024, 0) AS TOTAL_LENGTH, + c.column_name AS COLUMN_NAME, c.data_type AS DATA_TYPE, c.COLUMN_TYPE AS COLUMN_TYPE, + t.AUTO_INCREMENT AS AUTO_INCREMENT, locate('unsigned', c.COLUMN_TYPE) = 0 AS IS_SIGNED + FROM information_schema.TABLES t + JOIN information_schema.COLUMNS c ON t.TABLE_SCHEMA = c.TABLE_SCHEMA AND t.table_name=c.table_name + WHERE t.TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys') + GROUP BY TABLE_NAME + ORDER BY TOTAL_LENGTH DESC, AUTO_INCREMENT DESC; + """ + ) + + if data is None: + return mw.returnData(True, 'ok', []) + + for i in range(len(data)): + row = data[i] + + data[i]['DATA_LENGTH'] = round(row['DATA_LENGTH'] or 0, 2) + data[i]['INDEX_LENGTH'] = round(row['INDEX_LENGTH'] or 0, 2) + data[i]['TOTAL_LENGTH'] = round(row['TOTAL_LENGTH'] or 0, 2) + + AUTO_INCREMENT = row['AUTO_INCREMENT'] + DATA_TYPE = row['DATA_TYPE'] + IS_SIGNED = row['IS_SIGNED'] + RESIDUAL_AUTO_INCREMENT = 0 + if AUTO_INCREMENT is not None: + if DATA_TYPE == 'tinyint': + if IS_SIGNED == 0: + RESIDUAL_AUTO_INCREMENT = int(255 - AUTO_INCREMENT) + if IS_SIGNED == 1: + RESIDUAL_AUTO_INCREMENT = int(127 - AUTO_INCREMENT) + + if DATA_TYPE == 'smallint': + if IS_SIGNED == 0: + RESIDUAL_AUTO_INCREMENT = int(65535 - AUTO_INCREMENT) + if IS_SIGNED == 1: + RESIDUAL_AUTO_INCREMENT = int(32767 - AUTO_INCREMENT) + + if DATA_TYPE == 'int': + if IS_SIGNED == 0: + RESIDUAL_AUTO_INCREMENT = int(4294967295 - AUTO_INCREMENT) + if IS_SIGNED == 1: + RESIDUAL_AUTO_INCREMENT = int(2147483647 - AUTO_INCREMENT) + + if DATA_TYPE == 'mediumint': + if IS_SIGNED == 0: + RESIDUAL_AUTO_INCREMENT = int(16777215 - AUTO_INCREMENT) + if IS_SIGNED == 1: + RESIDUAL_AUTO_INCREMENT = int(8388607 - AUTO_INCREMENT) + + if DATA_TYPE == 'bigint': + if IS_SIGNED == 0: + RESIDUAL_AUTO_INCREMENT = Decimal("18446744073709551615") - Decimal(AUTO_INCREMENT) + if IS_SIGNED == 1: + RESIDUAL_AUTO_INCREMENT = Decimal("9223372036854775807") - Decimal(AUTO_INCREMENT) + else: + RESIDUAL_AUTO_INCREMENT = "主键非自增" + data[i]['RESIDUAL_AUTO_INCREMENT'] = RESIDUAL_AUTO_INCREMENT + return mw.returnData(True, 'ok', data) + + # 查看应用端IP连接数总和 + def getConnCount(self, args): + sid = args['sid'] + my_instance = self.getInstanceBySid(sid).conn() + if my_instance is False: + return mw.returnData(False,'无法链接') + + data = my_instance.query( + "SELECT user,db,substring_index(HOST,':',1) AS Client_IP,count(1) AS count FROM information_schema.PROCESSLIST " + "GROUP BY user,db,substring_index(HOST,':',1) ORDER BY COUNT(1) DESC" + ) + + for i in range(len(data)): + if data[i]['db'] is None: + data[i]['db'] = '空' + + if data[i]['Client_IP'] == '': + data[i]['Client_IP'] = '空' + + # data2 = my_instance.query("SELECT USER, COUNT(*) as nums FROM information_schema.PROCESSLIST GROUP BY USER ORDER BY COUNT(*) DESC") + # print(data) + # print(data2) + return mw.returnData(True, 'ok', data) + + # 快速找出没有主键的表 + def getFpkInfo(self, args): + sid = args['sid'] + my_instance = self.getInstanceBySid(sid).conn() + if my_instance is False: + return mw.returnData(False,'无法链接') + + data = my_instance.query( + """ + SELECT t.table_schema, + t.table_name + FROM information_schema.tables t + LEFT JOIN information_schema.key_column_usage k + ON t.table_schema = k.table_schema + AND t.table_name = k.table_name + AND k.constraint_name = 'PRIMARY' + WHERE t.table_schema NOT IN ('mysql', 'information_schema', 'sys', 'performance_schema') + AND k.constraint_name IS NULL + AND t.table_type = 'BASE TABLE'; + """ + ) + return mw.returnData(True, 'ok', data) + + def getLockSql(self, args): + sid = args['sid'] + my_instance = self.getInstanceBySid(sid).conn() + if my_instance is False: + return mw.returnData(False,'无法链接') + + data = my_instance.query( + """ + SELECT + a.trx_id AS trx_id, + a.trx_state AS trx_state, + a.trx_started AS trx_started, + b.id AS processlist_id, + b.info AS info, + b.user AS user, + b.host AS host, + b.db AS db, + b.command AS command, + b.state AS state, + CONCAT('KILL QUERY ', b.id) AS sql_kill_blocking_query + FROM + information_schema.INNODB_TRX a, + information_schema.PROCESSLIST b + WHERE + a.trx_mysql_thread_id = b.id + ORDER BY + a.trx_started + """ + ) + return mw.returnData(True, 'ok', data) + + def killLockPid(self, args): + sid = args['sid'] + my_instance = self.getInstanceBySid(sid).conn() + if my_instance is False: + return mw.returnData(False,'无法链接') + + pid = args['pid'] + my_instance.execute('kill %s' % pid) + return mw.returnData(True, '执行成功!') + + def killAllLock(self, args): + sid = args['sid'] + my_instance = self.getInstanceBySid(sid).conn() + if my_instance is False: + return mw.returnData(False,'无法链接') + + data = self.getLockSql(args) + if data['status']: + pid_data = data['data'] + for x in pid_data: + cmd = 'kill %s' % x['processlist_id'] + # print(cmd) + my_instance.execute(cmd) + return mw.returnData(True, '执行成功!') + + + + def getDeadlockInfo(self, args): + sid = args['sid'] + my_instance = self.getInstanceBySid(sid).conn() + if my_instance is False: + return mw.returnData(False,'无法链接') + + data = my_instance.find("SHOW ENGINE INNODB STATUS") + if data is not None: + innodb_status = data['Status'] + deadlock_info = re.search(r"LATEST DETECTED DEADLOCK.*?WE ROLL BACK TRANSACTION\s+\(\d+\)", innodb_status, re.DOTALL) + if deadlock_info is None: + return mw.returnData(True, 'ok', '无锁表') + return mw.returnData(True, 'ok', deadlock_info.group(0)) + return mw.returnData(True, 'ok', '无锁表') + + def getSlaveStatus(self, args): + sid = args['sid'] + my_instance = self.getInstanceBySid(sid).conn() + if my_instance is False: + return mw.returnData(False,'无法链接') + + slave_info = my_instance.find('SHOW SLAVE STATUS') + if slave_info is None: + return mw.returnData(True, 'ok', '未开启从库!') + + msg = '' + if slave_info['Auto_Position'] != 1: + msg += '你没有开启基于GTID全局事务ID复制,请确保CHANGE MASTER TO MASTER_AUTO_POSITION = 1.\n' + + + if slave_info['Slave_IO_Running'] == 'Yes' and slave_info['Slave_SQL_Running'] == 'Yes': + if slave_info['Seconds_Behind_Master'] == 0: + msg = "同步正常,无延迟" + return mw.returnData(True, 'ok', msg) + else: + return mw.returnData(True, 'ok', '同步正常,但有延迟,延迟时间为:%s' % slave_info['Seconds_Behind_Master']) + else: + msg = '主从复制报错,请检查\nSlave_IO_Running状态值是:%s, | Slave_SQL_Running状态值是:%s\nLast_Error错误信息是:%s\nLast_SQL_Error错误信息是:%s\n' \ + % (slave_info['Slave_IO_Running'], slave_info['Slave_SQL_Running'], slave_info['Last_Error'], slave_info['Last_SQL_Error']) + error_dict = my_instance.find('select LAST_ERROR_NUMBER,LAST_ERROR_MESSAGE,LAST_ERROR_TIMESTAMP from performance_schema.replication_applier_status_by_worker ORDER BY LAST_ERROR_TIMESTAMP desc limit 1') + msg += '错误号是:%s \n' % error_dict['LAST_ERROR_NUMBER'] + msg += '错误信息是:%s \n' % error_dict['LAST_ERROR_MESSAGE'] + msg += '报错时间是:%s \n' % error_dict['LAST_ERROR_TIMESTAMP'] + msg += 'MySQL Replication Health is NOT OK!' + return mw.returnData(True, 'ok', msg) + # ---------------------------------- run ---------------------------------- + +def get_server_list(args): + t = nosqlMySQLCtr() + return t.getServerList(args) + # 获取 mysql 列表 def get_db_list(args): t = nosqlMySQLCtr() @@ -307,6 +677,70 @@ def get_stats_list(args): t = nosqlMySQLCtr() return t.showStatsList(args) +# 查询执行次数最频繁的前N条SQL语句 +def get_topn_list(args): + t = nosqlMySQLCtr() + return t.getTopnList(args) + +# MySQL服务器的QPS/TPS/网络带宽指标 +def get_net_list(args): + t = nosqlMySQLCtr() + return t.getNetList(args) + +# 查看重复或冗余的索引 +def get_redundant_indexes(args): + t = nosqlMySQLCtr() + return t.getRedundantIndexes(args) + +# 删除重复或冗余的索引 +def redundant_indexes_cmd(args): + t = nosqlMySQLCtr() + return t.redundantIndexesCmd(args) + +# 统计库里每个表的大小 +def get_table_info(args): + t = nosqlMySQLCtr() + return t.getTableInfo(args) + +# 查看应用端IP连接数总和 +def get_conn_count(args): + t = nosqlMySQLCtr() + return t.getConnCount(args) + +# 快速找出没有主键的表 +def get_fpk_info(args): + t = nosqlMySQLCtr() + return t.getFpkInfo(args) + +# 查看当前锁阻塞的SQL +def get_lock_sql(args): + t = nosqlMySQLCtr() + return t.getLockSql(args) + +# KILL阻塞SQL +def kill_lock_pid(args): + t = nosqlMySQLCtr() + return t.killLockPid(args) + +# KILL阻塞SQL +def kill_all_lock(args): + t = nosqlMySQLCtr() + return t.killAllLock(args) + +# 查看死锁信息 +def get_deadlock_info(args): + t = nosqlMySQLCtr() + return t.getDeadlockInfo(args) + +# 查看主从复制信息 +def get_slave_status(args): + t = nosqlMySQLCtr() + return t.getSlaveStatus(args) + + + + + # 测试 def test(args): diff --git a/plugins/data_query/static/html/index.html b/plugins/data_query/static/html/index.html index c6bcd3ab0..b07cef4e2 100644 --- a/plugins/data_query/static/html/index.html +++ b/plugins/data_query/static/html/index.html @@ -51,9 +51,11 @@ - + +
+ + +
@@ -78,6 +80,8 @@ +
+
diff --git a/plugins/data_query/static/js/app.js b/plugins/data_query/static/js/app.js index 89cb49b95..bdb7e6c78 100755 --- a/plugins/data_query/static/js/app.js +++ b/plugins/data_query/static/js/app.js @@ -247,11 +247,16 @@ function initTabMemcached(){ }); } +var mysql_timer = null; function initTabMySQL(){ - mysqlGetDbList(); - mysqlProcessList(); - var mysql_timer = setInterval(function(){ + mysqlGetServerList(function(){ + mysqlGetDbList(); + mysqlProcessList(); + }); + + clearInterval(mysql_timer); + mysql_timer = setInterval(function(){ var name = $('#mysql_list_tab .tab-nav span.on').data('name'); mysqlRunMysqlTab(name); @@ -261,12 +266,613 @@ function initTabMySQL(){ } },2000); - $('#mysql_list_tab .tab-nav span').click(function(){ + $('#mysql_list_tab .tab-nav span').unbind('click').click(function(){ $('#mysql_list_tab .tab-nav span').removeClass('on'); $(this).addClass('on'); var name = $(this).data('name'); mysqlRunMysqlTab(name); }); + + mysqlCommonFunc(); +} + +function mysqlCommonFuncMysqlNSQL(){ + function renderSQL(){ + var sid = mysqlGetSid(); + var filter_db = $('#filter_db').is(':checked'); + myPostCBN('get_topn_list',{'sid':sid,'filter_db':filter_db ? 'yes':'no'} ,function(rdata){ + var data = rdata.data; + if (data['status']){ + var items = data.data; + var tbody = ''; + for (var i = 0; i < items.length; i++) { + var t = ''; + t += ''+items[i].query+''; + t += ''+items[i].db+''; + t += ''+items[i].last_seen+''; + t += ''+items[i].exec_count+''; + t += ''+items[i].max_latency+''; + t += ''+items[i].avg_latency+''; + t += ''; + tbody += t; + } + $('#topn_list tbody').html(tbody); + } else { + layer.msg(data.msg,{icon:2}); + } + }); + } + + var sql_timer = null; + layer.open({ + type: 1, + title: "查询执行次数最频繁的前N条SQL语句", + area: ['1200px', '500px'], + closeBtn: 1, + shadeClose: false, + content: '
\ +
\ +
\ + 实时监控\ +
\ + \ + \ +
\ +
\ +
\ + 过滤数据库\ +
\ + \ + \ +
\ +
\ +
\ +
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
SQL数据名最近时间总次数最大时间平均时间
\ +
', + success:function(i,l){ + renderSQL(); + + $('#real_time_label').click(function(){ + sql_timer = setInterval(function(){ + var t = $('#real_time_monitoring').is(':checked'); + if (t){ + renderSQL(); + } else{ + clearInterval(sql_timer); + } + }, 3000); + }); + } + }); +} + +function mysqlCommonFuncMysqlNet(){ + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_net_list',{'sid':sid} ,function(rdata){ + var data = rdata.data; + if (data['status']){ + var items = data.data; + var tbody = ''; + for (var i = 0; i < items.length; i++) { + var t = ''; + t += ''+items[i]['current_time']+''; + t += ''+items[i]['select']+''; + t += ''+items[i]['insert']+''; + t += ''+items[i]['update']+''; + t += ''+items[i]['delete']+''; + t += ''+items[i]['conn']+''; + t += ''+items[i]['max_conn']+''; + t += ''+items[i]['recv_mbps']+''; + t += ''+items[i]['send_mbps']+''; + t += ''; + tbody += t; + } + $('#net_list tbody').html(tbody); + } else { + layer.msg(data.msg,{icon:2}); + } + }); + } + + var sql_timer = null; + layer.open({ + type: 1, + title: "MySQL服务器的QPS/TPS/网络带宽指标", + area: ['750px', '220px'], + closeBtn: 1, + shadeClose: false, + content: '
\ +
\ +
\ + 实时监控\ +
\ + \ + \ +
\ +
\ +
\ +
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
时间SelectInsertUpdateDeleteConnMax_connRecvSend
\ +
', + success:function(i,l){ + renderSQL(); + + $('#real_qps_label').click(function(){ + sql_timer = setInterval(function(){ + var t = $('#real_qps_monitoring').is(':checked'); + if (t){ + renderSQL(); + } else{ + clearInterval(sql_timer); + } + }, 3000); + }); + } + }); +} + +function mysqlCommonFuncRedundantIndexes(){ + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_redundant_indexes',{'sid':sid} ,function(rdata){ + var data = rdata.data; + if (data['status']){ + var items = data.data; + var tbody = ''; + for (var i = 0; i < items.length; i++) { + var t = ''; + t += ''+items[i]['table_schema']+''; + t += ''+items[i]['table_name']+''; + t += ''+items[i]['redundant_index_name']+''; + t += ''+items[i]['redundant_index_columns']+''; + t += ''+items[i]['sql_drop_index']+''; + t += '执行'; + t += ''; + tbody += t; + } + $('#redundant_indexes tbody').html(tbody); + $('#redundant_indexes tbody .exec').click(function(){ + var index = $(this).attr('index'); + myPostCB('redundant_indexes_cmd', {'sid':sid, 'index':index}, function(rdata){ + var data = rdata.data; + showMsg(data.msg,function(){ + if (data.status){ + renderSQL(); + } + },{icon: data.status ? 1 : 2}, 2000); + }); + }); + } else { + layer.msg(data.msg,{icon:2}); + } + }); + } + + layer.open({ + type: 1, + title: "查看重复或冗余的索引", + area: ['1100px', '400px'], + closeBtn: 1, + shadeClose: false, + content: '
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
数据库名表名冗余索引名冗余索引列名删除冗余索引SQL操作
\ +
', + success:function(i,l){ + renderSQL(); + } + }); +} + +function mysqlCommonFuncTableInfo(){ + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_table_info',{'sid':sid} ,function(rdata){ + var data = rdata.data; + if (data['status']){ + var items = data.data; + var tbody = ''; + for (var i = 0; i < items.length; i++) { + var t = ''; + t += ''+items[i]['TABLE_SCHEMA']+''; + t += ''+items[i]['TABLE_NAME']+''; + t += ''+items[i]['ENGINE']+''; + t += ''+items[i]['DATA_LENGTH']+''; + t += ''+items[i]['INDEX_LENGTH']+''; + t += ''+items[i]['TOTAL_LENGTH']+''; + t += ''+items[i]['COLUMN_NAME']+''; + t += ''+items[i]['COLUMN_TYPE']+''; + t += ''+items[i]['AUTO_INCREMENT']+''; + t += ''+items[i]['RESIDUAL_AUTO_INCREMENT']+''; + t += ''; + tbody += t; + } + $('#mysql_data_id tbody').html(tbody); + } else { + layer.msg(data.msg,{icon:2}); + } + }); + } + + layer.open({ + type: 1, + title: "统计库里每个表的大小", + area: ['1200px', '400px'], + closeBtn: 1, + shadeClose: false, + content: '
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
库名表名储存引擎数据大小(GB)索引大小(GB)总计(GB)主键自增字段主键字段属性主键自增当前主键自增剩余
\ +
', + success:function(i,l){ + renderSQL(); + } + }); +} + +function mysqlCommonFuncConnCount(){ + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_conn_count',{'sid':sid} ,function(rdata){ + var data = rdata.data; + if (data['status']){ + var items = data.data; + var tbody = ''; + for (var i = 0; i < items.length; i++) { + var t = ''; + t += ''+items[i]['user']+''; + t += ''+items[i]['db']+''; + t += ''+items[i]['Client_IP']+''; + t += ''+items[i]['count']+''; + t += ''; + tbody += t; + } + $('#app_ip_list tbody').html(tbody); + } else { + layer.msg(data.msg,{icon:2}); + } + }); + } + + var sql_timer = null; + layer.open({ + type: 1, + title: "查看应用端IP连接数总和", + area: ['700px', '420px'], + closeBtn: 1, + shadeClose: false, + content: '
\ +
\ +
\ + 实时监控\ +
\ + \ + \ +
\ +
\ +
\ +
\ + \ + \ + \ + \ + \ + \ + \ + \ +
连接用户数据库名应用端IP数量
\ +
', + success:function(i,l){ + renderSQL(); + + $('#app_ip_label').click(function(){ + sql_timer = setInterval(function(){ + var t = $('#app_ip_monitoring').is(':checked'); + if (t){ + renderSQL(); + } else{ + clearInterval(sql_timer); + } + }, 3000); + }); + } + }); +} + +function mysqlCommonFuncFpkInfo(){ + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_fpk_info',{'sid':sid} ,function(rdata){ + var data = rdata.data; + if (data['status']){ + var items = data.data; + var tbody = ''; + for (var i = 0; i < items.length; i++) { + var t = ''; + t += ''+items[i]['table_schema']+''; + t += ''+items[i]['table_name']+''; + t += ''; + tbody += t; + } + $('#mysql_data_id tbody').html(tbody); + } else { + layer.msg(data.msg,{icon:2}); + } + }); + } + + layer.open({ + type: 1, + title: "快速找出没有主键的表", + area: ['800px', '400px'], + closeBtn: 1, + shadeClose: false, + content: '
\ + \ + \ + \ + \ + \ + \ +
库名表名
\ +
', + success:function(i,l){ + renderSQL(); + } + }); +} + +function mysqlCommonFuncLockSQL(){ + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_lock_sql',{'sid':sid} ,function(rdata){ + var data = rdata.data; + if (data['status']){ + var items = data.data; + var tbody = ''; + for (var i = 0; i < items.length; i++) { + var t = ''; + t += ''+items[i]['trx_id']+''; + t += ''+items[i]['trx_state']+''; + t += ''+items[i]['trx_started']+''; + t += ''+items[i]['processlist_id']+''; + t += ''+items[i]['info']+''; + t += ''+items[i]['user']+''; + t += ''+items[i]['host']+''; + t += ''+items[i]['db']+''; + t += ''+items[i]['command']+''; + t += ''+items[i]['state']+''; + t += ''+items[i]['sql_kill_blocking_query']+''; + t += '执行'; + t += ''; + tbody += t; + } + $('#mysql_data_id tbody').html(tbody); + + $('#mysql_data_id tbody .exec').click(function(){ + var index = $(this).attr('index'); + var pid = items[index]['processlist_id']; + myPostCB('kill_lock_pid', {'sid':sid, 'pid':pid}, function(rdata){ + var data = rdata.data; + showMsg(data.msg,function(){ + if (data.status){ + renderSQL(); + } + },{icon: data.status ? 1 : 2}, 2000); + }); + }); + } else { + layer.msg(data.msg,{icon:2}); + } + }); + } + + layer.open({ + type: 1, + title: "查看当前锁阻塞的SQL", + area: ['1000px', '400px'], + closeBtn: 1, + shadeClose: false, + content: '
\ +
\ + \ +
\ +
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
事务ID事务状态执行时间线程IDInfouserhostdbcommandstatekill操作
\ +
', + success:function(i,l){ + renderSQL(); + + $('#kill_all').unbind('click').click(function(){ + var sid = mysqlGetSid(); + myPostCB('kill_all_lock', {'sid':sid}, function(rdata){ + var data = rdata.data; + showMsg(data.msg,function(){ + if (data.status){ + renderSQL(); + } + },{icon: data.status ? 1 : 2}, 2000); + }); + }); + } + }); +} + +function mysqlCommonFuncDeadlockInfo(){ + + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_deadlock_info',{'sid':sid} ,function(rdata){ + var data = rdata.data; + $('#info_log').html(data.data); + var ob = document.getElementById('info_log'); + ob.scrollTop = ob.scrollHeight; + }); + } + + layer.open({ + type: 1, + title: "查看死锁信息", + area: ['800px', '400px'], + closeBtn: 1, + shadeClose: false, + content: '
\ + \ +
', + success:function(i,l){ + renderSQL(); + } + }); +} + +function mysqlCommonFuncSlaveStatus(){ + + function renderSQL(){ + var sid = mysqlGetSid(); + myPostCBN('get_slave_status',{'sid':sid} ,function(rdata){ + var data = rdata.data; + $('#info_log').html(data.data); + var ob = document.getElementById('info_log'); + ob.scrollTop = ob.scrollHeight; + }); + } + + layer.open({ + type: 1, + title: "查看主从复制信息", + area: ['800px', '400px'], + closeBtn: 1, + shadeClose: false, + content: '
\ + \ +
', + success:function(i,l){ + renderSQL(); + } + }); +} + +function mysqlCommonFunc(){ + $('#mysql_common').unbind('click').click(function(){ + layer.open({ + type: 1, + title: "MySQL常用功能", + area: ['600px', '200px'], + closeBtn: 1, + shadeClose: false, + content: '
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
', + success:function(i,l){ + $('#mysql_top_nsql').click(function(){ + mysqlCommonFuncMysqlNSQL(); + }); + + $('#mysql_net_stat').click(function(){ + mysqlCommonFuncMysqlNet(); + }); + + $('#mysql_redundant_indexes').click(function(){ + mysqlCommonFuncRedundantIndexes(); + }); + + $('#mysql_table_info').click(function(){ + mysqlCommonFuncTableInfo(); + }); + + $('#mysql_conn_count').click(function(){ + mysqlCommonFuncConnCount(); + }); + + $('#mysql_fpk_info').click(function(){ + mysqlCommonFuncFpkInfo(); + }); + + $('#mysql_lock_sql').click(function(){ + mysqlCommonFuncLockSQL(); + }); + + $('#mysql_deadlock_info').click(function(){ + mysqlCommonFuncDeadlockInfo(); + }); + + $('#mysql_slave_status').click(function(){ + mysqlCommonFuncSlaveStatus(); + }); + } + }); + }); } function mysqlRunMysqlTab(name){ @@ -279,7 +885,8 @@ function mysqlRunMysqlTab(name){ // ------------------------- mysql start ------------------------------- function mysqlGetSid(){ - return 0; + return $('#mysql select[name=sid]').val(); + // return 0; } function mysqlGetDbName(){ @@ -308,7 +915,7 @@ function mysqlInitField(f, data){ $('select[name="mysql_field_key"]').html(option_html); - $('#mysql .mysql_find').unbind('click').click(function(){ + $('#mysql_find').unbind('click').click(function(){ var val = $('input[name="mysql_field_value"]').val(); if (val == ''){ layer.msg('搜索不能为空!',{icon:7}); @@ -319,11 +926,40 @@ function mysqlInitField(f, data){ } +function mysqlGetServerList(call_func){ + myPostCBN('get_server_list', {}, function(rdata){ + var rdata = rdata.data; + if (rdata.data.length != 0){ + var items = rdata.data; + var content = ''; + for (var i = 0; i < items.length; i++) { + var t = items[i]; + if (i == 0){ + content += ''; + } else { + content += ''; + } + } + + + $('#mysql select[name=sid]').html(content); + $('#mysql select[name=sid]').change(function(){ + mysqlGetDbList(); + }); + if (typeof(call_func) == 'function'){ + call_func(); + } + closeInstallLayer(); + } else { + showInstallLayer(); + } + }); +} + function mysqlGetDbList(){ var sid = mysqlGetSid(); - myPostCB('get_db_list',{'sid':sid} ,function(rdata){ + myPostCBN('get_db_list',{'sid':sid} ,function(rdata){ if (rdata.data.status){ - var items = rdata.data.data['list']; var content = ''; for (var i = 0; i < items.length; i++) { @@ -341,10 +977,6 @@ function mysqlGetDbList(){ }); mysqlGetTableList(1); - - closeInstallLayer(); - } else { - showInstallLayer(); } }); } @@ -353,7 +985,12 @@ function mysqlGetTableList(p){ // console.log('mysqlGetTableList',p); var sid = mysqlGetSid(); var db = mysqlGetDbName(); - myPostCB('get_table_list',{'sid':sid,'db':db} ,function(rdata){ + + if (!db){ + return; + } + + myPostCBN('get_table_list',{'sid':sid,'db':db} ,function(rdata){ if (rdata.data.status){ var items = rdata.data.data['list']; diff --git a/plugins/docker/index.py b/plugins/docker/index.py index 59488cead..7fa29f9c3 100755 --- a/plugins/docker/index.py +++ b/plugins/docker/index.py @@ -7,8 +7,12 @@ import time import re import json -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw try: import docker @@ -434,7 +438,7 @@ def dockerRemoveImage(): def getImageListFunc(dbname=''): - bkDir = mw.getRootDir() + '/backup/docker' + bkDir = mw.getFatherDir() + '/backup/docker' blist = os.listdir(bkDir) r = [] @@ -448,13 +452,13 @@ def getImageListFunc(dbname=''): def dockerImagePickDir(): - bkDir = mw.getRootDir() + '/backup/docker' + bkDir = mw.getFatherDir() + '/backup/docker' return mw.returnJson(True, 'ok', bkDir) def dockerImagePickList(): - bkDir = mw.getRootDir() + '/backup/docker' + bkDir = mw.getFatherDir() + '/backup/docker' if not os.path.exists(bkDir): os.mkdir(bkDir) @@ -486,7 +490,7 @@ def dockerImagePickSave(): if not data[0]: return data[1] - bkDir = mw.getRootDir() + '/backup/docker/' + bkDir = mw.getFatherDir() + '/backup/docker/' images = args['images'] try: file_name = bkDir + \ @@ -606,8 +610,8 @@ def getDockerCreateInfo(): def __release_port(port): from collections import namedtuple try: - import firewall_api - firewall_api.firewall_api().addAcceptPortArgs(port, 'docker', 'port') + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().addAcceptPort(port, 'docker', 'port') return port except Exception as e: return "Release failed {}".format(e) diff --git a/plugins/docker/install.sh b/plugins/docker/install.sh index 7b9bc7499..75ec2fa1f 100755 --- a/plugins/docker/install.sh +++ b/plugins/docker/install.sh @@ -10,7 +10,6 @@ serverPath=$(dirname "$rootPath") # cd /www/server/mdserver-web/plugins/docker && /bin/bash install.sh uninstall 1.0 # cd /www/server/mdserver-web/plugins/docker && /bin/bash install.sh install 1.0 -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 if [ -f ${rootPath}/bin/activate ];then @@ -42,11 +41,11 @@ Install_Docker() { # which docker # if [ "$?" == "0" ];then - # echo '安装已经完成docker' > $install_tmp + # echo '安装已经完成docker' # exit 0 # fi - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source if [ ! -d $serverPath/docker ];then @@ -59,7 +58,7 @@ Install_Docker() if [ -d $serverPath/docker ];then echo "${VERSION}" > $serverPath/docker/version.pl - echo '安装完成' > $install_tmp + echo '安装Docker完成' cd ${rootPath} && python3 ${rootPath}/plugins/docker/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/docker/index.py initd_install @@ -94,7 +93,7 @@ Uninstall_Docker() # docker-ce rm -rf $serverPath/docker - echo "Uninstall_Docker" > $install_tmp + echo "卸载Docker" } action=$1 diff --git a/plugins/dynamic-tracking/index.py b/plugins/dynamic-tracking/index.py index 12a4d4a93..a54965cdc 100755 --- a/plugins/dynamic-tracking/index.py +++ b/plugins/dynamic-tracking/index.py @@ -6,8 +6,13 @@ import os import time import re -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw + app_debug = False if mw.isAppleSystem(): diff --git a/plugins/dynamic-tracking/install.sh b/plugins/dynamic-tracking/install.sh index 0ddca4b3a..cb0995050 100755 --- a/plugins/dynamic-tracking/install.sh +++ b/plugins/dynamic-tracking/install.sh @@ -10,7 +10,6 @@ serverPath=$(dirname "$rootPath") # cd /www/server/mdserver-web/plugins/dynamic-tracking && /bin/bash install.sh uninstall 1.0 # cd /www/server/mdserver-web/plugins/dynamic-tracking && /bin/bash install.sh install 1.0 -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 if [ -f ${rootPath}/bin/activate ];then @@ -31,7 +30,7 @@ fi Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/dynamic-tracking @@ -57,7 +56,7 @@ Install_App() shell_file=${curPath}/versions/${OSNAME}.sh echo $shell_file if [ ! -f $shell_file ];then - echo '不支持...' > $install_tmp + echo '不支持...' exit 1 fi @@ -67,7 +66,7 @@ Install_App() cp -rf ${curPath}/shell $serverPath/dynamic-tracking echo "${VERSION}" > $serverPath/dynamic-tracking/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' cd ${rootPath} && python3 ${rootPath}/plugins/dynamic-tracking/index.py start # cd ${rootPath} && python3 ${rootPath}/plugins/dynamic-tracking/index.py initd_install @@ -77,7 +76,7 @@ Install_App() Uninstall_App() { rm -rf $serverPath/dynamic-tracking - echo "Uninstall_App" > $install_tmp + echo "Uninstall_App" } action=$1 diff --git a/plugins/dynamic-tracking/versions/centos.sh b/plugins/dynamic-tracking/versions/centos.sh index f71fbaa58..e74a8469f 100644 --- a/plugins/dynamic-tracking/versions/centos.sh +++ b/plugins/dynamic-tracking/versions/centos.sh @@ -7,5 +7,4 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl SYS_ARCH=`arch` \ No newline at end of file diff --git a/plugins/dynamic-tracking/versions/debian.sh b/plugins/dynamic-tracking/versions/debian.sh index c42eff5eb..c87f300fb 100644 --- a/plugins/dynamic-tracking/versions/debian.sh +++ b/plugins/dynamic-tracking/versions/debian.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl SYS_ARCH=`arch` # apt-get install linux-perf diff --git a/plugins/dynamic-tracking/versions/macos.sh b/plugins/dynamic-tracking/versions/macos.sh index 812f66746..f0522e5c3 100644 --- a/plugins/dynamic-tracking/versions/macos.sh +++ b/plugins/dynamic-tracking/versions/macos.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl SYS_ARCH=`arch` # 关于macosx使用dtrace diff --git a/plugins/dynamic-tracking/versions/ubuntu.sh b/plugins/dynamic-tracking/versions/ubuntu.sh index b9dcff40b..b12e95d0f 100644 --- a/plugins/dynamic-tracking/versions/ubuntu.sh +++ b/plugins/dynamic-tracking/versions/ubuntu.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl SYS_ARCH=`arch` # export GOPROXY=https://goproxy.io diff --git a/plugins/fail2ban/index.py b/plugins/fail2ban/index.py index f2917dcdb..6d9994da4 100755 --- a/plugins/fail2ban/index.py +++ b/plugins/fail2ban/index.py @@ -7,8 +7,12 @@ import time import re import json -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -119,7 +123,7 @@ def status(): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) return content @@ -143,7 +147,7 @@ def initJailD(): def initDreplace(): file_tpl = getInitDTpl() - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getServerDir() initD_path = getServerDir() + '/init.d' if not os.path.exists(initD_path): @@ -167,7 +171,6 @@ def initDreplace(): systemService = systemDir + '/' + getPluginName() + '.service' if os.path.exists(systemDir) and not os.path.exists(systemService): systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl' - service_path = mw.getServerDir() content = mw.readFile(systemServiceTpl) content = content.replace('{$SERVER_PATH}', service_path) mw.writeFile(systemService, content) diff --git a/plugins/fail2ban/install.sh b/plugins/fail2ban/install.sh index ced567e74..a07496351 100755 --- a/plugins/fail2ban/install.sh +++ b/plugins/fail2ban/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 # cd /www/server/mdserver-web/plugins/fail2ban && bash install.sh install 1.1.0 @@ -16,10 +15,11 @@ VERSION=$2 Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source - apt install -y fail2ban + which yum && yum install -y fail2ban + which apt && apt install -y fail2ban mkdir -p $serverPath/fail2ban echo "${VERSION}" > $serverPath/fail2ban/version.pl @@ -31,8 +31,11 @@ Install_App() Uninstall_App() { - apt remove -y fail2ban - apt purge -y fail2ban + which apt && apt remove -y fail2ban + which apt && apt purge -y fail2ban + + which yum && yum remove -y fail2ban + which yum && yum purge -y fail2ban if [ -f /usr/lib/systemd/system/fail2ban.service ];then systemctl stop fail2ban diff --git a/plugins/gdrive/class/gdriveclient.py b/plugins/gdrive/class/gdriveclient.py index 8171fc9c1..d71f1e446 100644 --- a/plugins/gdrive/class/gdriveclient.py +++ b/plugins/gdrive/class/gdriveclient.py @@ -9,8 +9,12 @@ import json import io -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw # ----------------------------- import google.oauth2.credentials @@ -211,7 +215,7 @@ class gdriveclient(): print("data_type 类型错误!!!") exit(1) - file_regx = prefix_dict.get(data_type) + "_(.+)_20\d+_\d+\." + file_regx = prefix_dict.get(data_type) + r"_(.+)_20\d+_\d+\." sub_search = re.search(file_regx.lower(), file_name) sub_path_name = "" if sub_search: diff --git a/plugins/gdrive/index.html b/plugins/gdrive/index.html index f0f95cbb0..0a5a01079 100644 --- a/plugins/gdrive/index.html +++ b/plugins/gdrive/index.html @@ -302,7 +302,7 @@
- + \ No newline at end of file diff --git a/plugins/mtproxy/index.py b/plugins/mtproxy/index.py new file mode 100755 index 000000000..c15208253 --- /dev/null +++ b/plugins/mtproxy/index.py @@ -0,0 +1,257 @@ +# coding:utf-8 + +import sys +import io +import os +import time +import re + +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw + +app_debug = False +if mw.isAppleSystem(): + app_debug = True + + +def getPluginName(): + return 'mtproxy' + + +def getPluginDir(): + return mw.getPluginDir() + '/' + getPluginName() + + +def getServerDir(): + return mw.getServerDir() + '/' + getPluginName() + + +def getServiceTpl(): + path = getPluginDir() + "/init.d/" + getPluginName() + ".service.tpl" + return path + + +def getConfEnvTpl(): + path = getPluginDir() + "/conf/mt.toml" + return path + + +def getConfEnv(): + path = getServerDir() + "/mt.toml" + return path + + +def getArgs(): + args = sys.argv[2:] + tmp = {} + args_len = len(args) + + if args_len == 1: + t = args[0].strip('{').strip('}') + t = t.split(':') + tmp[t[0]] = t[1] + elif args_len > 1: + for i in range(len(args)): + t = args[i].split(':') + tmp[t[0]] = t[1] + + return tmp + +def status(): + cmd = "ps -ef|grep mtproxy| grep mtg |grep -v grep | grep -v python | awk '{print $2}'" + data = mw.execShell(cmd) + if data[0] == '': + return 'stop' + return 'start' + + +def getServiceFile(): + systemDir = mw.systemdCfgDir() + return systemDir + '/mtproxy.service' + + +def getMtproxyPort(): + return '8349' + + +def __release_port(port): + from collections import namedtuple + try: + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().addAcceptPort(port, 'mtproxy', 'port') + return port + except Exception as e: + return "Release failed {}".format(e) + +def __delete_port(port): + from collections import namedtuple + try: + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().delAcceptPortCmd(port, 'tcp') + return port + except Exception as e: + return "Delete failed {}".format(e) + +def openPort(): + port = getMtproxyPort() + for i in [port]: + __release_port(i) + return True + +def delPort(): + port = getMtproxyPort() + for i in [port]: + __delete_port(i) + return True + + +def initDreplace(): + + envTpl = getConfEnvTpl() + dstEnv = getConfEnv() + cmd = getServerDir() + '/mtg/mtg generate-secret `head -c 16 /dev/urandom | xxd -ps`' + secret = mw.execShell(cmd) + if not os.path.exists(dstEnv): + env_content = mw.readFile(envTpl) + env_content = env_content.replace('{$PORT}', getMtproxyPort()) + env_content = env_content.replace('{$SECRET}', secret[0].strip()) + mw.writeFile(dstEnv, env_content) + openPort() + + # systemd + systemDir = mw.systemdCfgDir() + systemService = systemDir + '/mtproxy.service' + systemServiceTpl = getServiceTpl() + if os.path.exists(systemDir) and not os.path.exists(systemService): + service_path = mw.getServerDir() + se_content = mw.readFile(systemServiceTpl) + se_content = se_content.replace('{$SERVER_PATH}', service_path) + mw.writeFile(systemService, se_content) + mw.execShell('systemctl daemon-reload') + + return 'ok' + + +def mtOp(method): + file = initDreplace() + + if not mw.isAppleSystem(): + mw.execShell('systemctl daemon-reload') + data = mw.execShell('systemctl ' + method + ' mtproxy') + if data[1] == '': + return 'ok' + return data[1] + + return 'fail' + + +def start(): + return mtOp('start') + + +def stop(): + return mtOp('stop') + + +def restart(): + return mtOp('restart') + + +def reload(): + return mtOp('reload') + + +def initdStatus(): + if mw.isAppleSystem(): + return "Apple Computer does not support" + + shell_cmd = 'systemctl status mtproxy | grep loaded | grep "enabled;"' + data = mw.execShell(shell_cmd) + if data[0] == '': + return 'fail' + return 'ok' + +def initdInstall(): + if mw.isAppleSystem(): + return "Apple Computer does not support" + mw.execShell('systemctl enable mtproxy') + return 'ok' + + +def initdUinstall(): + if mw.isAppleSystem(): + return "Apple Computer does not support" + mw.execShell('systemctl disable mtproxy') + return 'ok' + +def getMtproxyUrl(): + conf = getConfEnv() + content = mw.readFile(conf) + + + rep = r'bind-to\s*=\s*(.*)' + tmp = re.search(rep, content) + bind_to = tmp.groups()[0].strip() + bind_to = bind_to.strip('"') + + rep = r'secret\s*=\s*(.*)' + tmp = re.search(rep, content) + secret = tmp.groups()[0].strip() + secret = secret.strip('"') + + info = bind_to.split(":") + + ip = mw.getLocalIp() + + url = 'tg://proxy?server={0}&port={1}&secret={2}'.format(ip, info[1], secret) + return mw.returnJson(True, 'ok', url) + +def installPreInspection(): + sys = mw.execShell("cat /etc/*-release | grep PRETTY_NAME |awk -F = '{print $2}' | awk -F '\"' '{print $2}'| awk '{print $1}'") + + if sys[1] != '': + return '不支持该系统' + + sys_id = mw.execShell("cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") + + sysName = sys[0].strip().lower() + sysId = sys_id[0].strip() + + if sysName in ('opensuse'): + return '不支持该系统' + + return 'ok' + + +if __name__ == "__main__": + func = sys.argv[1] + if func == 'status': + print(status()) + elif func == 'start': + print(start()) + elif func == 'stop': + print(stop()) + elif func == 'restart': + print(restart()) + elif func == 'reload': + print(reload()) + elif func == 'initd_status': + print(initdStatus()) + elif func == 'initd_install': + print(initdInstall()) + elif func == 'initd_uninstall': + print(initdUinstall()) + elif func == 'install_pre_inspection': + print(installPreInspection()) + elif func == 'conf': + print(getServiceFile()) + elif func == 'conf_env': + print(getConfEnv()) + elif func == 'url': + print(getMtproxyUrl()) + else: + print('error') diff --git a/plugins/mtproxy/info.json b/plugins/mtproxy/info.json new file mode 100755 index 000000000..a68c2a59b --- /dev/null +++ b/plugins/mtproxy/info.json @@ -0,0 +1,19 @@ +{ + "sort": 7, + "ps": "你的纸飞机", + "install_pre_inspection":true, + "name": "mtproxy", + "title": "mtproxy", + "shell": "install.sh", + "versions":["1.0"], + "updates":["1.0"], + "tip": "soft", + "checks": "server/mtproxy", + "path": "server/mtproxy", + "display": 1, + "author": "TelegramMessenger", + "date": "2022-07-09", + "home": "https://github.com/9seconds/mtg", + "type": 0, + "pid": "5" +} \ No newline at end of file diff --git a/plugins/mtproxy/init.d/mtproxy.service.tpl b/plugins/mtproxy/init.d/mtproxy.service.tpl new file mode 100644 index 000000000..03ad10760 --- /dev/null +++ b/plugins/mtproxy/init.d/mtproxy.service.tpl @@ -0,0 +1,14 @@ +[Unit] +Description=MTProxy +After=network.target + +[Service] +Type=simple +WorkingDirectory={$SERVER_PATH}/mtproxy +ExecStart={$SERVER_PATH}/mtproxy/mtg/mtg run {$SERVER_PATH}/mtproxy/mt.toml +RestartSec=3 +Restart=on-failure +AmbientCapabilities=CAP_NET_BIND_SERVICE + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/plugins/mtproxy/install.sh b/plugins/mtproxy/install.sh new file mode 100755 index 000000000..5a32ab58c --- /dev/null +++ b/plugins/mtproxy/install.sh @@ -0,0 +1,141 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + +sysArch=`arch` +sysName=`uname` + + +# cd /www/server/mdserver-web && python3 plugins/mtproxy/index.py url +# cd /www/server/mdserver-web/plugins/mtproxy && /bin/bash install.sh install 1.0 + +bash ${rootPath}/scripts/getos.sh +echo "bash ${rootPath}/scripts/getos.sh" +OSNAME=`cat ${rootPath}/data/osname.pl` + +VERSION_MIN=2.1.7 +VERSION=v${VERSION_MIN} + +sysName=$(uname | tr '[:upper:]' '[:lower:]') + +ARCH=amd64 +get_arch() { + echo "package main +import ( + \"fmt\" + \"runtime\" +) +func main() { fmt.Println(runtime.GOARCH) }" > /tmp/go_arch.go + + ARCH=$(go run /tmp/go_arch.go) + echo "ARCH:${ARCH}" +} + +TARGET_DIR="${serverPath}/mtproxy" + +get_download_url() { + DOWNLOAD_URL="https://github.com/9seconds/mtg/releases/download/$VERSION/mtg-${VERSION_MIN}-${sysName}-${ARCH}.tar.gz" +} + +# download file +download_file() { + url="${1}" + destination="${2}" + + printf "Fetching ${url} \n\n" + + if test -x "$(command -v curl)"; then + code=$(curl --connect-timeout 15 -w '%{http_code}' -L "${url}" -o "${destination}") + elif test -x "$(command -v wget)"; then + code=$(wget -t2 -T15 -O "${destination}" --server-response "${url}" 2>&1 | awk '/^ HTTP/{print $2}' | tail -1) + else + printf "\e[1;31mNeither curl nor wget was available to perform http requests.\e[0m\n" + exit 1 + fi + + if [ "${code}" != 200 ]; then + printf "\e[1;31mRequest failed with code %s\e[0m\n" $code + exit 1 + else + printf "\n\e[1;33mDownload succeeded\e[0m\n" + fi +} + +# /www/server/mtproxy/mtg/mtg run /www/server/mtproxy/mt.toml +Install_app() +{ + mkdir -p ${serverPath}/mtproxy + mkdir -p ${serverPath}/source/mtproxy + echo "${1}" > ${serverPath}/mtproxy/version.pl + + if [ "$OSNAME" == "centos" ]; then + yum install -y golang golang-src + elif [ "$OSNAME" == "amazon" ]; then + yum install -y golang golang-src + elif [ "$OSNAME" == "rocky" ]; then + yum install -y golang golang-src + elif [ "$OSNAME" == "rhel" ]; then + yum install -y golang golang-src + elif [ "$OSNAME" == "opensuse" ]; then + zypper install -y golang golang-src + elif [ "$sysName" == "macos" ]; then + echo "macos" + else + apt install -y golang golang-src + fi + + if [ "$sysName" == "darwin" ]; then + ARCH=arm64 + DOWNLOAD_URL="https://github.com/9seconds/mtg/releases/download/$VERSION/mtg-${VERSION_MIN}-${sysName}-arm64.tar.gz" + elif [ "$sysName" != "macos" ]; then + get_arch + get_download_url + else + echo "else" + fi + + DOWNLOAD_FILE="$(mktemp).tar.gz" + download_file $DOWNLOAD_URL $DOWNLOAD_FILE + + tar -C "$TARGET_DIR" -zxf $DOWNLOAD_FILE + rm -rf $DOWNLOAD_FILE + + if [ -d ${serverPath}/mtproxy/mtg ];then + rm -rf ${serverPath}/mtproxy/mtg + fi + + # cd ${serverPath}/mtproxy + # curl -s https://core.telegram.org/getProxySecret -o proxy-secret + # curl -s https://core.telegram.org/getProxyConfig -o proxy-multi.conf + + + mv ${serverPath}/mtproxy/mtg-${VERSION_MIN}-${sysName}-${ARCH} ${serverPath}/mtproxy/mtg + echo '安装完成' + + #初始化 + cd ${rootPath} && python3 ${rootPath}/plugins/mtproxy/index.py start + cd ${rootPath} && python3 ${rootPath}/plugins/mtproxy/index.py initd_install +} + +Uninstall_app() +{ + if [ -f /usr/lib/systemd/system/mtproxy.service ];then + systemctl stop mtproxy + rm -rf /usr/lib/systemd/system/mtproxy.service + systemctl daemon-reload + fi + rm -rf ${serverPath}/mtproxy + echo '卸载完成' +} + +action=$1 +if [ "${1}" == 'install' ];then + Install_app $2 +else + Uninstall_app $2 +fi diff --git a/plugins/mtproxy/js/mtproxy.js b/plugins/mtproxy/js/mtproxy.js new file mode 100644 index 000000000..748e2db60 --- /dev/null +++ b/plugins/mtproxy/js/mtproxy.js @@ -0,0 +1,61 @@ +function mtPost(method, version, args,callback){ + var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); + + var req_data = {}; + req_data['name'] = 'mtproxy'; + req_data['func'] = method; + req_data['version'] = version; + + if (typeof(args) == 'string'){ + req_data['args'] = JSON.stringify(toArrayObject(args)); + } else { + req_data['args'] = JSON.stringify(args); + } + + $.post('/plugins/run', req_data, function(data) { + layer.close(loadT); + if (!data.status){ + //错误展示10S + layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']}); + return; + } + + if(typeof(callback) == 'function'){ + callback(data); + } + },'json'); +} + +function mtCommonFunc(){ + var con = '

\ + \ +

'; + + $(".soft-man-con").html(con); + + $('#mtproxy_url').click(function(){ + mtPost('url', '', {}, function(rdata){ + var data = $.parseJSON(rdata.data); + + layer.open({ + title: "mtproxy代理信息", + area: ['600px', '180px'], + type:1, + closeBtn: 1, + shadeClose: false, + btn:["复制","取消"], + content: '
\ +
\ +
'+data.data+'
\ +
\ +
', + success:function(){ + copyText(data.data); + }, + yes:function(){ + copyText(data.data); + } + }); + }); + }); +} \ No newline at end of file diff --git a/plugins/mysql-apt/index.py b/plugins/mysql-apt/index.py index 456d9aa11..7a4c242dd 100755 --- a/plugins/mysql-apt/index.py +++ b/plugins/mysql-apt/index.py @@ -9,11 +9,12 @@ import re import json -# reload(sys) -# sys.setdefaultencoding('utf-8') +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) -sys.path.append(os.getcwd() + "/class/core") -import mw +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -134,7 +135,7 @@ def getAuthPolicy(): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$SERVER_APP_PATH}', service_path + '/' + getPluginName()) @@ -837,8 +838,7 @@ def setDbBackup(): def rootPwd(): - return pSqliteDb('config').where( - 'id=?', (1,)).getField('mysql_root') + return pSqliteDb('config').where('id=?', (1,)).getField('mysql_root') def importDbExternal(): @@ -931,7 +931,7 @@ def importDbExternalProgressBar(): file = args['file'] name = args['name'] - import_dir = mw.getRootDir() + '/backup/import/' + import_dir = mw.getFatherDir() + '/backup/import/' file_path = import_dir + file if not os.path.exists(file_path): @@ -2568,7 +2568,7 @@ def trySlaveSyncBugfix(version=''): def getSlaveSyncCmd(version=''): - root = mw.getRunDir() + root = mw.getPanelDir() cmd = 'cd ' + root + ' && python3 ' + root + \ '/plugins/mysql/index.py do_full_sync {"db":"all","sign":""}' return mw.returnJson(True, 'ok', cmd) @@ -3449,7 +3449,7 @@ def fullSync(version=''): status_file = asyncTmpfile() if args['begin'] == '1': - cmd = 'cd ' + mw.getRunDir() + ' && python3 ' + getPluginDir() + \ + cmd = 'cd ' + mw.getPanelDir() + ' && python3 ' + getPluginDir() + \ '/index.py do_full_sync {"db":"' + \ args['db'] + '","sign":"' + sign + '"} &' # print(cmd) @@ -3523,9 +3523,9 @@ def uninstallPreInspection(version): if mw.isDebugMode(): return 'ok' - import plugins_api - plugins_api.plugins_api().removeIndex(getPluginName(), version) - + from utils.plugin import plugin as MwPlugin + MwPlugin.instance().removeIndex(getPluginName(), version) + return "请手动删除MySQL[{}]
rm -rf {}".format(version, getServerDir()) if __name__ == "__main__": diff --git a/plugins/mysql-apt/index_mysql_apt.py b/plugins/mysql-apt/index_mysql_apt.py index a61c2a015..d252c1cea 100644 --- a/plugins/mysql-apt/index_mysql_apt.py +++ b/plugins/mysql-apt/index_mysql_apt.py @@ -8,9 +8,12 @@ import subprocess import re import json -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) +import core.mw as mw if mw.isAppleSystem(): cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' @@ -48,7 +51,7 @@ def getConf(): def getDataDir(): file = getConf() content = mw.readFile(file) - rep = 'datadir\s*=\s*(.*)' + rep = r'datadir\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -56,7 +59,7 @@ def getDataDir(): def getRelayLogName(): file = getConf() content = mw.readFile(file) - rep = 'relay-log\s*=\s*(.*)' + rep = r'relay-log\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -64,7 +67,7 @@ def getRelayLogName(): def getLogBinName(): file = getConf() content = mw.readFile(file) - rep = 'log-bin\s*=\s*(.*)' + rep = r'log-bin\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() diff --git a/plugins/mysql-apt/install.sh b/plugins/mysql-apt/install.sh index 99ec491e5..db63dcd32 100755 --- a/plugins/mysql-apt/install.sh +++ b/plugins/mysql-apt/install.sh @@ -15,7 +15,6 @@ serverPath=$(dirname "$rootPath") # cd /www/server/mdserver-web && python3 /www/server/mdserver-web/plugins/mysql-apt/index.py fix_db_access # cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql/index.py do_full_sync {"db":"xxx","sign":"","begin":1} -install_tmp=${rootPath}/tmp/mw_install.pl action=$1 diff --git a/plugins/mysql-apt/scripts/backup.py b/plugins/mysql-apt/scripts/backup.py index 992219655..a058e5d23 100755 --- a/plugins/mysql-apt/scripts/backup.py +++ b/plugins/mysql-apt/scripts/backup.py @@ -5,22 +5,19 @@ import sys import os +import time +import re if sys.platform != 'darwin': os.chdir('/www/server/mdserver-web') +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) -chdir = os.getcwd() -sys.path.append(chdir + '/class/core') - -# reload(sys) -# sys.setdefaultencoding('utf-8') - - -import mw -import db -import time -import re +import core.mw as mw +import core.db as db ''' DEBUG: @@ -44,7 +41,7 @@ class backupTools: "----------------------------------------------------------------------------") return - backup_path = mw.getRootDir() + '/backup/database/mysql-apt' + backup_path = mw.getFatherDir() + '/backup/database/mysql-apt' if not os.path.exists(backup_path): mw.execShell("mkdir -p " + backup_path) @@ -56,7 +53,7 @@ class backupTools: my_conf_path = db_path + '/etc/my.cnf' mycnf = mw.readFile(my_conf_path) - rep = "\[mysqldump\]\nuser=root" + rep = r"\[mysqldump\]\nuser=root" sea = "[mysqldump]\n" subStr = sea + "user=root\npassword=" + mysql_root + "\n" mycnf = mycnf.replace(sea, subStr) @@ -82,11 +79,9 @@ class backupTools: endDate = time.strftime('%Y/%m/%d %X', time.localtime()) outTime = time.time() - startTime - pid = mw.M('databases').dbPos(db_path, db_name).where( - 'name=?', (name,)).getField('id') + pid = mw.M('databases').dbPos(db_path, db_name).where('name=?', (name,)).getField('id') - mw.M('backup').add('type,name,pid,filename,addtime,size', (1, os.path.basename( - filename), pid, filename, endDate, os.path.getsize(filename))) + mw.M('backup').add('type,name,pid,filename,add_time,size', (1, os.path.basename(filename), pid, filename, endDate, os.path.getsize(filename))) log = "数据库[" + name + "]备份成功,用时[" + str(round(outTime, 2)) + "]秒" mw.writeLog('计划任务', log) print("★[" + endDate + "] " + log) @@ -94,8 +89,7 @@ class backupTools: print("|---文件名:" + filename) # 清理多余备份 - backups = mw.M('backup').where( - 'type=? and pid=?', ('1', pid)).field('id,filename').select() + backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select() num = len(backups) - int(count) if num > 0: @@ -110,8 +104,7 @@ class backupTools: def backupDatabaseAll(self, save): db_path = mw.getServerDir() + '/mysql-apt' db_name = 'mysql' - databases = mw.M('databases').dbPos( - db_path, db_name).field('name').select() + databases = mw.M('databases').dbPos(db_path, db_name).field('name').select() for database in databases: self.backupDatabase(database['name'], save) diff --git a/plugins/mysql-apt/versions/5.7/install.sh b/plugins/mysql-apt/versions/5.7/install.sh index 36af92dc8..46ec670ed 100755 --- a/plugins/mysql-apt/versions/5.7/install.sh +++ b/plugins/mysql-apt/versions/5.7/install.sh @@ -17,7 +17,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl myDir=${serverPath}/source/mysql-apt bash ${rootPath}/scripts/getos.sh @@ -46,7 +45,7 @@ else fi if [ "$ARCH" != "amd64" ];then - echo "暂时不支持该${ARCH}" > $install_tmp + echo "暂时不支持该${ARCH}" exit 0 fi @@ -95,7 +94,7 @@ rm -rf $myDir Install_mysql() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' isApt=`which apt` if [ "$isApt" != "" ];then @@ -105,9 +104,9 @@ Install_mysql() if [ "$?" == "0" ];then mkdir -p $serverPath/mysql-apt echo '5.7' > $serverPath/mysql-apt/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' else - echo "暂时不支持该系统" > $install_tmp + echo "暂时不支持该系统" fi } @@ -120,7 +119,7 @@ Uninstall_mysql() fi rm -rf $serverPath/mysql-apt - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mysql-apt/versions/8.0/install.sh b/plugins/mysql-apt/versions/8.0/install.sh index fe9d64f03..582e3aab5 100755 --- a/plugins/mysql-apt/versions/8.0/install.sh +++ b/plugins/mysql-apt/versions/8.0/install.sh @@ -13,7 +13,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl myDir=${serverPath}/source/mysql-apt bash ${rootPath}/scripts/getos.sh @@ -24,7 +23,7 @@ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk - # 暂时debian12没有标准版,先用11使用 # if [ "$OSNAME" == 'debian' ] && [ "$VERSION_ID" == '12' ] ;then -# echo "暂时不支持该${OSNAME}${VERSION_ID}" > $install_tmp +# echo "暂时不支持该${OSNAME}${VERSION_ID}" # exit 1 # fi @@ -40,7 +39,7 @@ else fi if [ "$ARCH" != "amd64" ];then - echo "暂时不支持该${ARCH}" > $install_tmp + echo "暂时不支持该${ARCH}" exit 1 fi @@ -100,7 +99,7 @@ rm -rf $myDir Install_mysql() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' isApt=`which apt` if [ "$isApt" != "" ];then @@ -110,10 +109,10 @@ Install_mysql() if [ "$?" == "0" ];then mkdir -p $serverPath/mysql-apt echo '8.0' > $serverPath/mysql-apt/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' else echo '8.0' > $serverPath/mysql-apt/version.pl - echo "暂时不支持该系统" > $install_tmp + echo "暂时不支持该系统" fi } @@ -126,7 +125,7 @@ Uninstall_mysql() fi rm -rf $serverPath/mysql-apt - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mysql-apt/versions/8.2/install.sh b/plugins/mysql-apt/versions/8.2/install.sh index 3866488f4..81cade826 100755 --- a/plugins/mysql-apt/versions/8.2/install.sh +++ b/plugins/mysql-apt/versions/8.2/install.sh @@ -14,7 +14,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl myDir=${serverPath}/source/mysql-apt bash ${rootPath}/scripts/getos.sh @@ -26,7 +25,7 @@ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk - # 暂时debian12没有标准版,先用11使用 # if [ "$OSNAME" == 'debian' ] && [ "$VERSION_ID" == '12' ] ;then -# echo "暂时不支持该${OSNAME}${VERSION_ID}" > $install_tmp +# echo "暂时不支持该${OSNAME}${VERSION_ID}" # exit 1 # fi @@ -42,7 +41,7 @@ else fi if [ "$ARCH" != "amd64" ];then - echo "暂时不支持该${ARCH}" > $install_tmp + echo "暂时不支持该${ARCH}" exit 1 fi @@ -101,7 +100,7 @@ rm -rf $myDir Install_mysql() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' isApt=`which apt` if [ "$isApt" != "" ];then @@ -111,10 +110,10 @@ Install_mysql() if [ "$?" == "0" ];then mkdir -p $serverPath/mysql-apt echo '8.2' > $serverPath/mysql-apt/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' else echo '8.2' > $serverPath/mysql-apt/version.pl - echo "暂时不支持该系统" > $install_tmp + echo "暂时不支持该系统" fi } @@ -127,7 +126,7 @@ Uninstall_mysql() fi rm -rf $serverPath/mysql-apt - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mysql-apt/versions/8.3/install.sh b/plugins/mysql-apt/versions/8.3/install.sh index e5b1e449f..316ae77ed 100755 --- a/plugins/mysql-apt/versions/8.3/install.sh +++ b/plugins/mysql-apt/versions/8.3/install.sh @@ -13,7 +13,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl myDir=${serverPath}/source/mysql-apt bash ${rootPath}/scripts/getos.sh @@ -24,7 +23,7 @@ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk - # 暂时debian12没有标准版,先用11使用 # if [ "$OSNAME" == 'debian' ] && [ "$VERSION_ID" == '12' ] ;then -# echo "暂时不支持该${OSNAME}${VERSION_ID}" > $install_tmp +# echo "暂时不支持该${OSNAME}${VERSION_ID}" # exit 1 # fi @@ -40,7 +39,7 @@ else fi if [ "$ARCH" != "amd64" ];then - echo "暂时不支持该${ARCH}" > $install_tmp + echo "暂时不支持该${ARCH}" exit 1 fi @@ -99,7 +98,7 @@ rm -rf $myDir Install_mysql() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' isApt=`which apt` if [ "$isApt" != "" ];then @@ -109,10 +108,10 @@ Install_mysql() if [ "$?" == "0" ];then mkdir -p $serverPath/mysql-apt echo '8.3' > $serverPath/mysql-apt/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' else echo '8.3' > $serverPath/mysql-apt/version.pl - echo "暂时不支持该系统" > $install_tmp + echo "暂时不支持该系统" fi } @@ -125,7 +124,7 @@ Uninstall_mysql() fi rm -rf $serverPath/mysql-apt - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mysql-apt/versions/8.4/install.sh b/plugins/mysql-apt/versions/8.4/install.sh index a3647de16..04124e5b9 100755 --- a/plugins/mysql-apt/versions/8.4/install.sh +++ b/plugins/mysql-apt/versions/8.4/install.sh @@ -13,7 +13,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl myDir=${serverPath}/source/mysql-apt bash ${rootPath}/scripts/getos.sh @@ -24,7 +23,7 @@ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk - # 暂时debian12没有标准版,先用11使用 # if [ "$OSNAME" == 'debian' ] && [ "$VERSION_ID" == '12' ] ;then -# echo "暂时不支持该${OSNAME}${VERSION_ID}" > $install_tmp +# echo "暂时不支持该${OSNAME}${VERSION_ID}" # exit 1 # fi @@ -40,7 +39,7 @@ else fi if [ "$ARCH" != "amd64" ];then - echo "暂时不支持该${ARCH}" > $install_tmp + echo "暂时不支持该${ARCH}" exit 1 fi @@ -102,7 +101,7 @@ rm -rf $myDir Install_mysql() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' isApt=`which apt` if [ "$isApt" != "" ];then @@ -128,7 +127,7 @@ Uninstall_mysql() fi rm -rf $serverPath/mysql-apt - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mysql-apt/versions/9.0/install.sh b/plugins/mysql-apt/versions/9.0/install.sh index 9c6073b0c..e3cf77bae 100755 --- a/plugins/mysql-apt/versions/9.0/install.sh +++ b/plugins/mysql-apt/versions/9.0/install.sh @@ -13,7 +13,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl myDir=${serverPath}/source/mysql-apt bash ${rootPath}/scripts/getos.sh @@ -24,7 +23,7 @@ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk - # 暂时debian12没有标准版,先用11使用 # if [ "$OSNAME" == 'debian' ] && [ "$VERSION_ID" == '12' ] ;then -# echo "暂时不支持该${OSNAME}${VERSION_ID}" > $install_tmp +# echo "暂时不支持该${OSNAME}${VERSION_ID}" # exit 1 # fi @@ -40,7 +39,7 @@ else fi if [ "$ARCH" != "amd64" ];then - echo "暂时不支持该${ARCH}" > $install_tmp + echo "暂时不支持该${ARCH}" exit 1 fi @@ -102,7 +101,7 @@ rm -rf $myDir Install_mysql() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' isApt=`which apt` if [ "$isApt" != "" ];then diff --git a/plugins/mysql-yum/index.py b/plugins/mysql-yum/index.py index 2ce17b7b3..4581ebd86 100755 --- a/plugins/mysql-yum/index.py +++ b/plugins/mysql-yum/index.py @@ -9,18 +9,18 @@ import re import json -# reload(sys) -# sys.setdefaultencoding('utf-8') +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) -sys.path.append(os.getcwd() + "/class/core") -import mw +import core.mw as mw - -if mw.isAppleSystem(): - cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' - info = mw.execShell(cmd) - p = "/usr/local/lib/" + info[0].strip() + "/site-packages" - sys.path.append(p) +# if mw.isAppleSystem(): +# cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' +# info = mw.execShell(cmd) +# p = "/usr/local/lib/" + info[0].strip() + "/site-packages" +# sys.path.append(p) app_debug = False @@ -85,21 +85,21 @@ def getConf(): def getDataDir(): file = getConf() content = mw.readFile(file) - rep = 'datadir\s*=\s*(.*)' + rep = r'datadir\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() def getLogBinName(): file = getConf() content = mw.readFile(file) - rep = 'log-bin\s*=\s*(.*)' + rep = r'log-bin\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() def getPidFile(): file = getConf() content = mw.readFile(file) - rep = 'pid-file\s*=\s*(.*)' + rep = r'pid-file\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -107,7 +107,7 @@ def getPidFile(): def getDbPort(): file = getConf() content = mw.readFile(file) - rep = 'port\s*=\s*(.*)' + rep = r'port\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -115,7 +115,7 @@ def getDbPort(): def getDbServerId(): file = getConf() content = mw.readFile(file) - rep = 'server-id\s*=\s*(.*)' + rep = r'server-id\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -123,7 +123,7 @@ def getDbServerId(): def getSocketFile(): file = getConf() content = mw.readFile(file) - rep = 'socket\s*=\s*(.*)' + rep = r'socket\s*=\s*(.*)' tmp = re.search(rep, content) socket = tmp.groups()[0].strip() @@ -136,14 +136,14 @@ def getSocketFile(): def getErrorLogsFile(): file = getConf() content = mw.readFile(file) - rep = 'log-error\s*=\s*(.*)' + rep = r'log-error\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() def getAuthPolicy(): file = getConf() content = mw.readFile(file) - rep = 'authentication_policy\s*=\s*(.*)' + rep = r'authentication_policy\s*=\s*(.*)' tmp = re.search(rep, content) if tmp: return tmp.groups()[0].strip() @@ -152,7 +152,7 @@ def getAuthPolicy(): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$SERVER_APP_PATH}',service_path + '/' + getPluginName()) @@ -385,7 +385,7 @@ def getErrorLog(): def getShowLogFile(): file = getConf() content = mw.readFile(file) - rep = 'slow-query-log-file\s*=\s*(.*)' + rep = r'slow-query-log-file\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -435,7 +435,7 @@ def initMysql8Data(): return True def getLib64Pos(name=''): - cmd = "ls /usr/lib64 | grep "+name+".so | cut -d \ -f 1 | awk 'END {print}'" + cmd = "ls /usr/lib64 | grep "+name+".so | cut -d \\ -f 1 | awk 'END {print}'" d = mw.execShell(cmd) return '/usr/lib64/'+d[0] @@ -604,7 +604,7 @@ def initdUinstall(): def getMyDbPos(): file = getConf() content = mw.readFile(file) - rep = 'datadir\s*=\s*(.*)' + rep = r'datadir\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -655,7 +655,7 @@ def setMyDbPos(version=''): def getMyPort(): file = getConf() content = mw.readFile(file) - rep = 'port\s*=\s*(.*)' + rep = r'port\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -669,7 +669,7 @@ def setMyPort(): port = args['port'] file = getConf() content = mw.readFile(file) - rep = "port\s*=\s*([0-9]+)\s*\n" + rep = r"port\s*=\s*([0-9]+)\s*\n" content = re.sub(rep, 'port = ' + port + '\n', content) mw.writeFile(file, content) restart() @@ -757,7 +757,7 @@ def setDbStatus(version): s = 'K' if g in emptys: s = '' - rep = '\s*' + g + '\s*=\s*\d+(M|K|k|m|G)?\n' + rep = r'\s*' + g + r'\s*=\s*\d+(M|K|k|m|G)?\n' c = g + ' = ' + args[g] + s + '\n' if content.find(g) != -1: content = re.sub(rep, '\n' + c, content, 1) @@ -930,7 +930,7 @@ def importDbExternalProgressBar(): file = args['file'] name = args['name'] - import_dir = mw.getRootDir() + '/backup/import/' + import_dir = mw.getFatherDir() + '/backup/import/' file_path = import_dir + file if not os.path.exists(file_path): @@ -1349,7 +1349,7 @@ def addDb(): dataAccess = args['dataAccess'].strip() ps = args['ps'].strip() - reg = "^[\w-]+$" + reg = r"^[\w-]+$" if not re.match(reg, args['name']): return mw.returnJson(False, '数据库名称不能带有特殊符号!') checks = ['root', 'mysql', 'test', 'sys', 'performance_schema','information_schema'] @@ -1512,13 +1512,10 @@ def resetDbRootPwd(version): auth_policy = getAuthPolicy() reset_pwd = 'flush privileges;' - reset_pwd = reset_pwd + \ - "UPDATE mysql.user SET authentication_string='' WHERE user='root';" + reset_pwd = reset_pwd + "UPDATE mysql.user SET authentication_string='' WHERE user='root';" reset_pwd = reset_pwd + "flush privileges;" - reset_pwd = reset_pwd + \ - "alter user 'root'@'localhost' IDENTIFIED by '" + pwd + "';" - reset_pwd = reset_pwd + \ - "alter user 'root'@'localhost' IDENTIFIED WITH "+auth_policy+" by '" + pwd + "';" + reset_pwd = reset_pwd + "alter user 'root'@'localhost' IDENTIFIED by '" + pwd + "';" + reset_pwd = reset_pwd + "alter user 'root'@'localhost' IDENTIFIED WITH "+auth_policy+" by '" + pwd + "';" reset_pwd = reset_pwd + "flush privileges;" tmp_file = "/tmp/mysql_init_tmp.log" @@ -2113,7 +2110,7 @@ def addMasterRepSlaveUser(version=''): username = args['username'].strip() password = args['password'].strip() - reg = "^[\w-]+$" + reg = r"^[\w-]+$" if not re.match(reg, username): return mw.returnJson(False, '用户名不能带有特殊符号!') checks = ['root', 'mysql', 'test', 'sys', 'performance_schema','information_schema'] @@ -2554,7 +2551,7 @@ def trySlaveSyncBugfix(version=''): def getSlaveSyncCmd(version=''): - root = mw.getRunDir() + root = mw.getPanelDir() cmd = 'cd ' + root + ' && python3 ' + root + \ '/plugins/mysql/index.py do_full_sync {"db":"all"}' return mw.returnJson(True, 'ok', cmd) @@ -3436,7 +3433,7 @@ def fullSync(version=''): status_file = asyncTmpfile() if args['begin'] == '1': - cmd = 'cd ' + mw.getRunDir() + ' && python3 ' + getPluginDir() + \ + cmd = 'cd ' + mw.getPanelDir() + ' && python3 ' + getPluginDir() + \ '/index.py do_full_sync {"db":"' + \ args['db'] + '","sign":"' + sign + '"} &' # print(cmd) @@ -3490,8 +3487,8 @@ def uninstallPreInspection(version): if mw.isDebugMode(): return 'ok' - import plugins_api - plugins_api.plugins_api().removeIndex(getPluginName(), version) + from utils.plugin import plugin as MwPlugin + MwPlugin.instance().removeIndex(getPluginName(), version) return "请手动删除MySQL[{}]
rm -rf {}".format(version, getServerDir()) diff --git a/plugins/mysql-yum/index_mysql_yum.py b/plugins/mysql-yum/index_mysql_yum.py index 1f7cc3a71..6c10d29f0 100644 --- a/plugins/mysql-yum/index_mysql_yum.py +++ b/plugins/mysql-yum/index_mysql_yum.py @@ -8,16 +8,12 @@ import subprocess import re import json -sys.path.append(os.getcwd() + "/class/core") -import mw - - -if mw.isAppleSystem(): - cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' - info = mw.execShell(cmd) - p = "/usr/local/lib/" + info[0].strip() + "/site-packages" - sys.path.append(p) +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -48,7 +44,7 @@ def getConf(): def getDataDir(): file = getConf() content = mw.readFile(file) - rep = 'datadir\s*=\s*(.*)' + rep = r'datadir\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -56,7 +52,7 @@ def getDataDir(): def getRelayLogName(): file = getConf() content = mw.readFile(file) - rep = 'relay-log\s*=\s*(.*)' + rep = r'relay-log\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -64,7 +60,7 @@ def getRelayLogName(): def getLogBinName(): file = getConf() content = mw.readFile(file) - rep = 'log-bin\s*=\s*(.*)' + rep = r'log-bin\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() diff --git a/plugins/mysql-yum/install.sh b/plugins/mysql-yum/install.sh index 9a1edcaaf..3fb1a908f 100755 --- a/plugins/mysql-yum/install.sh +++ b/plugins/mysql-yum/install.sh @@ -13,8 +13,6 @@ serverPath=$(dirname "$rootPath") # cd /www/server/mdserver-web && python3 /www/server/mdserver-web/plugins/mysql-yum/index.py start 8.0 # cd /www/server/mdserver-web && python3 /www/server/mdserver-web/plugins/mysql-yum/index.py get_master_status 8.4 -install_tmp=${rootPath}/tmp/mw_install.pl - action=$1 type=$2 @@ -30,12 +28,12 @@ fi if [ "${2}" == "" ];then - echo '缺少安装脚本...' > $install_tmp + echo '缺少安装脚本...' exit 0 fi if [ ! -d $curPath/versions/$2 ];then - echo '缺少安装脚本2...' > $install_tmp + echo '缺少安装脚本2...' exit 0 fi diff --git a/plugins/mysql-yum/scripts/backup.py b/plugins/mysql-yum/scripts/backup.py index 1c4dd3cc0..d18caf8e6 100755 --- a/plugins/mysql-yum/scripts/backup.py +++ b/plugins/mysql-yum/scripts/backup.py @@ -5,22 +5,20 @@ import sys import os +import time +import re if sys.platform != 'darwin': os.chdir('/www/server/mdserver-web') -chdir = os.getcwd() -sys.path.append(chdir + '/class/core') +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) -# reload(sys) -# sys.setdefaultencoding('utf-8') - - -import mw -import db -import time -import re +import core.mw as mw +import core.db as db ''' DEBUG: @@ -44,7 +42,7 @@ class backupTools: "----------------------------------------------------------------------------") return - backup_path = mw.getRootDir() + '/backup/database/mysql-yum' + backup_path = mw.getFatherDir() + '/backup/database/mysql-yum' if not os.path.exists(backup_path): mw.execShell("mkdir -p " + backup_path) @@ -58,7 +56,7 @@ class backupTools: mw.backFile(my_cnf) content = mw.readFile(my_cnf) - rep = "\[mysqldump\]\nuser=root" + rep = r"\[mysqldump\]\nuser=root" sea = "[mysqldump]\n" subStr = sea + "user=root\npassword=" + mysql_root + "\n" content = content.replace(sea, subStr) @@ -80,11 +78,9 @@ class backupTools: endDate = time.strftime('%Y/%m/%d %X', time.localtime()) outTime = time.time() - startTime - pid = mw.M('databases').dbPos(db_path, db_name).where( - 'name=?', (name,)).getField('id') + pid = mw.M('databases').dbPos(db_path, db_name).where('name=?', (name,)).getField('id') - mw.M('backup').add('type,name,pid,filename,addtime,size', (1, os.path.basename( - filename), pid, filename, endDate, os.path.getsize(filename))) + mw.M('backup').add('type,name,pid,filename,add_time,size', (1, os.path.basename(filename), pid, filename, endDate, os.path.getsize(filename))) log = "数据库[" + name + "]备份成功,用时[" + str(round(outTime, 2)) + "]秒" mw.writeLog('计划任务', log) print("★[" + endDate + "] " + log) @@ -92,9 +88,7 @@ class backupTools: print("|---文件名:" + filename) # 清理多余备份 - backups = mw.M('backup').where( - 'type=? and pid=?', ('1', pid)).field('id,filename').select() - + backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select() num = len(backups) - int(count) if num > 0: for backup in backups: diff --git a/plugins/mysql-yum/versions/5.7/install.sh b/plugins/mysql-yum/versions/5.7/install.sh index 734b30d65..e94ac9b89 100755 --- a/plugins/mysql-yum/versions/5.7/install.sh +++ b/plugins/mysql-yum/versions/5.7/install.sh @@ -13,7 +13,6 @@ serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl myDir=${serverPath}/source/mysql-yum ARCH=`uname -m` @@ -118,7 +117,7 @@ rm -rf ${myDir} Install_mysql() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/mysql-yum @@ -134,14 +133,14 @@ Install_mysql() rm -rf $myDir echo '5.7' > $serverPath/mysql-yum/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' } Uninstall_mysql() { YUM_UNINSTALL rm -rf $serverPath/mysql-yum - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mysql-yum/versions/8.0/install.sh b/plugins/mysql-yum/versions/8.0/install.sh index 1a3319f3d..27e72cbc4 100755 --- a/plugins/mysql-yum/versions/8.0/install.sh +++ b/plugins/mysql-yum/versions/8.0/install.sh @@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` - -install_tmp=${rootPath}/tmp/mw_install.pl myDir=${serverPath}/source/mysql-yum ARCH=`uname -m` @@ -114,7 +112,7 @@ rm -rf ${myDir} Install_mysql() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/mysql-yum mkdir -p /var/run/mysqld diff --git a/plugins/mysql-yum/versions/8.2/install.sh b/plugins/mysql-yum/versions/8.2/install.sh index 242b0f9a1..08cb36d1e 100755 --- a/plugins/mysql-yum/versions/8.2/install.sh +++ b/plugins/mysql-yum/versions/8.2/install.sh @@ -13,7 +13,6 @@ serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl myDir=${serverPath}/source/mysql-yum ARCH=`uname -m` @@ -115,7 +114,7 @@ rm -rf ${myDir} Install_mysql() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/mysql-yum diff --git a/plugins/mysql-yum/versions/8.3/install.sh b/plugins/mysql-yum/versions/8.3/install.sh index 50ffd5bdd..f7630ad86 100755 --- a/plugins/mysql-yum/versions/8.3/install.sh +++ b/plugins/mysql-yum/versions/8.3/install.sh @@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` - -install_tmp=${rootPath}/tmp/mw_install.pl myDir=${serverPath}/source/mysql-yum ARCH=`uname -m` @@ -115,7 +113,7 @@ rm -rf ${myDir} Install_mysql() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/mysql-yum diff --git a/plugins/mysql-yum/versions/8.4/install.sh b/plugins/mysql-yum/versions/8.4/install.sh index 9c80cb9bc..02bf03981 100755 --- a/plugins/mysql-yum/versions/8.4/install.sh +++ b/plugins/mysql-yum/versions/8.4/install.sh @@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` - -install_tmp=${rootPath}/tmp/mw_install.pl myDir=${serverPath}/source/mysql-yum ARCH=`uname -m` @@ -118,7 +116,7 @@ rm -rf ${myDir} Install_mysql() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/mysql-yum mkdir -p /var/lib/mysql diff --git a/plugins/mysql-yum/versions/9.0/install.sh b/plugins/mysql-yum/versions/9.0/install.sh index 69c82c001..821b6c488 100755 --- a/plugins/mysql-yum/versions/9.0/install.sh +++ b/plugins/mysql-yum/versions/9.0/install.sh @@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` - -install_tmp=${rootPath}/tmp/mw_install.pl myDir=${serverPath}/source/mysql-yum ARCH=`uname -m` @@ -119,7 +117,7 @@ rm -rf ${myDir} Install_mysql() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/mysql-yum mkdir -p /var/lib/mysql diff --git a/plugins/mysql/index.py b/plugins/mysql/index.py index 885536eb3..0cdc59f5c 100755 --- a/plugins/mysql/index.py +++ b/plugins/mysql/index.py @@ -10,11 +10,12 @@ import re import json -# reload(sys) -# sys.setdefaultencoding('utf-8') +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) -sys.path.append(os.getcwd() + "/class/core") -import mw +import core.mw as mw if mw.isAppleSystem(): @@ -138,7 +139,7 @@ def getInitdTpl(version=''): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$SERVER_APP_PATH}', service_path + '/mysql') @@ -953,7 +954,7 @@ def __createUser(dbname, username, password, address): def getDbBackupListFunc(dbname=''): - bkDir = mw.getRootDir() + '/backup/database' + bkDir = mw.getBackupDir() + '/database' blist = os.listdir(bkDir) r = [] @@ -972,7 +973,7 @@ def setDbBackup(): if not data[0]: return data[1] - scDir = mw.getRunDir() + '/scripts/backup.py' + scDir = mw.getPanelDir() + '/scripts/backup.py' cmd = 'python3 ' + scDir + ' database ' + args['name'] + ' 3' os.system(cmd) return mw.returnJson(True, 'ok') @@ -1010,7 +1011,7 @@ def importDbExternal(): file = args['file'] name = args['name'] - import_dir = mw.getRootDir() + '/backup/import/' + import_dir = mw.getFatherDir() + '/backup/import/' file_path = import_dir + file if not os.path.exists(file_path): @@ -1095,7 +1096,7 @@ def importDbExternalProgressBar(): file = args['file'] name = args['name'] - import_dir = mw.getRootDir() + '/backup/import/' + import_dir = mw.getFatherDir() + '/backup/import/' file_path = import_dir + file if not os.path.exists(file_path): @@ -1158,11 +1159,11 @@ def importDbBackup(): file = args['file'] name = args['name'] - file_path = mw.getRootDir() + '/backup/database/' + file - file_path_sql = mw.getRootDir() + '/backup/database/' + file.replace('.gz', '') + file_path = mw.getFatherDir() + '/backup/database/' + file + file_path_sql = mw.getFatherDir() + '/backup/database/' + file.replace('.gz', '') if not os.path.exists(file_path_sql): - cmd = 'cd ' + mw.getRootDir() + '/backup/database && gzip -d ' + file + cmd = 'cd ' + mw.getFatherDir() + '/backup/database && gzip -d ' + file mw.execShell(cmd) local_mode = recognizeDbMode() @@ -1209,11 +1210,11 @@ def importDbBackupProgressBar(): file = args['file'] name = args['name'] - file_path = mw.getRootDir() + '/backup/database/' + file - file_path_sql = mw.getRootDir() + '/backup/database/' + file.replace('.gz', '') + file_path = mw.getFatherDir() + '/backup/database/' + file + file_path_sql = mw.getFatherDir() + '/backup/database/' + file.replace('.gz', '') if not os.path.exists(file_path_sql): - cmd = 'cd ' + mw.getRootDir() + '/backup/database && gzip -d ' + file + cmd = 'cd ' + mw.getFatherDir() + '/backup/database && gzip -d ' + file mw.execShell(cmd) local_mode = recognizeDbMode() @@ -1240,7 +1241,7 @@ def deleteDbBackup(): path = args['path'] full_file = "" - bkDir = mw.getRootDir() + '/backup/database' + bkDir = mw.getFatherDir() + '/backup/database' full_file = bkDir + '/' + args['filename'] if path != "": full_file = path + "/" + args['filename'] @@ -1255,7 +1256,7 @@ def getDbBackupList(): return data[1] r = getDbBackupListFunc(args['name']) - bkDir = mw.getRootDir() + '/backup/database' + bkDir = mw.getFatherDir() + '/backup/database' rr = [] for x in range(0, len(r)): p = bkDir + '/' + r[x] @@ -1278,7 +1279,7 @@ def getDbBackupList(): def getDbBackupImportList(): - bkImportDir = mw.getRootDir() + '/backup/import' + bkImportDir = mw.getFatherDir() + '/backup/import' if not os.path.exists(bkImportDir): os.mkdir(bkImportDir) @@ -2779,7 +2780,7 @@ def trySlaveSyncBugfix(version=''): def getSlaveSyncCmd(version=''): - root = mw.getRunDir() + root = mw.getPanelDir() cmd = 'cd ' + root + ' && python3 ' + root + \ '/plugins/mysql/index.py do_full_sync {"db":"all","sign",""}' return mw.returnJson(True, 'ok', cmd) @@ -3727,7 +3728,7 @@ def fullSync(version=''): status_file = asyncTmpfile() if args['begin'] == '1': - cmd = 'cd ' + mw.getRunDir() + ' && python3 ' + getPluginDir() + \ + cmd = 'cd ' + mw.getPanelDir() + ' && python3 ' + getPluginDir() + \ '/index.py do_full_sync {"db":"' + \ args['db'] + '","sign":"' + sign + '"} &' # print(cmd) @@ -3773,8 +3774,9 @@ def uninstallPreInspection(version): if mw.isDebugMode(): return 'ok' - import plugins_api - plugins_api.plugins_api().removeIndex(getPluginName(), version) + + from utils.plugin import plugin as MwPlugin + MwPlugin.instance().removeIndex(getPluginName(), version) return "请手动删除MySQL[{}]
rm -rf {}".format(version, getServerDir()) diff --git a/plugins/mysql/index_mysql.py b/plugins/mysql/index_mysql.py index 791d9038f..d06228d4b 100644 --- a/plugins/mysql/index_mysql.py +++ b/plugins/mysql/index_mysql.py @@ -8,8 +8,12 @@ import subprocess import re import json -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw if mw.isAppleSystem(): @@ -48,7 +52,7 @@ def getConf(): def getDataDir(): file = getConf() content = mw.readFile(file) - rep = 'datadir\s*=\s*(.*)' + rep = r'datadir\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -56,7 +60,7 @@ def getDataDir(): def getRelayLogName(): file = getConf() content = mw.readFile(file) - rep = 'relay-log\s*=\s*(.*)' + rep = r'relay-log\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -64,7 +68,7 @@ def getRelayLogName(): def getLogBinName(): file = getConf() content = mw.readFile(file) - rep = 'log-bin\s*=\s*(.*)' + rep = r'log-bin\s*=\s*(.*)' tmp = re.search(rep, content) return tmp.groups()[0].strip() diff --git a/plugins/mysql/install.sh b/plugins/mysql/install.sh index 5e9f903cc..2f0a73f67 100755 --- a/plugins/mysql/install.sh +++ b/plugins/mysql/install.sh @@ -16,10 +16,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") - -install_tmp=${rootPath}/tmp/mw_install.pl - - action=$1 type=$2 @@ -32,12 +28,12 @@ else fi if [ "${2}" == "" ];then - echo '缺少安装脚本...' > $install_tmp + echo '缺少安装脚本...' exit 0 fi if [ ! -d $curPath/versions/$2 ];then - echo '缺少安装脚本2...' > $install_tmp + echo '缺少安装脚本2...' exit 0 fi diff --git a/plugins/mysql/js/mysql.js b/plugins/mysql/js/mysql.js index dcb54e786..c5c25eae6 100755 --- a/plugins/mysql/js/mysql.js +++ b/plugins/mysql/js/mysql.js @@ -2041,7 +2041,7 @@ function getFullSyncStatus(db){ myPostN('full_sync_cmd', {'db':db,'sign':''}, function(rdata){ var rdata = $.parseJSON(rdata.data); layer.open({ - title: "手动执行命令CMD", + title: "手动执行命令CMD", area: ['600px', '180px'], type:1, closeBtn: 1, diff --git a/plugins/mysql/versions/5.5/install.sh b/plugins/mysql/versions/5.5/install.sh index 22fc66e21..829de8864 100755 --- a/plugins/mysql/versions/5.5/install.sh +++ b/plugins/mysql/versions/5.5/install.sh @@ -12,7 +12,6 @@ serverPath=$(dirname "$rootPath") sysName=`uname` sysArch=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl mysqlDir=${serverPath}/source/mysql VERSION=5.5.62 @@ -60,7 +59,7 @@ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk - Install_mysql() { mkdir -p ${mysqlDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$sysName" != "Darwin" ];then mkdir -p /var/log/mariadb @@ -164,7 +163,7 @@ Install_mysql() Uninstall_mysql() { rm -rf $serverPath/mysql - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/mysql/versions/5.6/install.sh b/plugins/mysql/versions/5.6/install.sh index e05648ccb..43f5d15f9 100755 --- a/plugins/mysql/versions/5.6/install.sh +++ b/plugins/mysql/versions/5.6/install.sh @@ -53,14 +53,13 @@ fi VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` -install_tmp=${rootPath}/tmp/mw_install.pl mysqlDir=${serverPath}/source/mysql VERSION=5.6.51 Install_mysql() { mkdir -p ${mysqlDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$sysName" != "Darwin" ];then mkdir -p /var/log/mariadb diff --git a/plugins/mysql/versions/5.7/install.sh b/plugins/mysql/versions/5.7/install.sh index 98ac7954d..90d7bc303 100755 --- a/plugins/mysql/versions/5.7/install.sh +++ b/plugins/mysql/versions/5.7/install.sh @@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` - -install_tmp=${rootPath}/tmp/mw_install.pl mysqlDir=${serverPath}/source/mysql VERSION=5.7.44 @@ -61,7 +59,7 @@ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk - Install_mysql() { mkdir -p ${mysqlDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$sysName" != "Darwin" ];then mkdir -p /var/log/mariadb diff --git a/plugins/mysql/versions/8.0/install.sh b/plugins/mysql/versions/8.0/install.sh index a407718c2..31c3da456 100755 --- a/plugins/mysql/versions/8.0/install.sh +++ b/plugins/mysql/versions/8.0/install.sh @@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` - -install_tmp=${rootPath}/tmp/mw_install.pl mysqlDir=${serverPath}/source/mysql _os=`uname` @@ -60,7 +58,7 @@ VERSION=8.0.37 Install_mysql() { mkdir -p ${mysqlDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' # ----- cpu start ------ if [ -z "${cpuCore}" ]; then diff --git a/plugins/mysql/versions/8.2/install.sh b/plugins/mysql/versions/8.2/install.sh index 5bb6b6f88..6aa8fce4f 100755 --- a/plugins/mysql/versions/8.2/install.sh +++ b/plugins/mysql/versions/8.2/install.sh @@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` - -install_tmp=${rootPath}/tmp/mw_install.pl mysqlDir=${serverPath}/source/mysql _os=`uname` @@ -60,7 +58,7 @@ VERSION=8.2.0 Install_mysql() { mkdir -p ${mysqlDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' # ----- cpu start ------ if [ -z "${cpuCore}" ]; then diff --git a/plugins/mysql/versions/8.3/install.sh b/plugins/mysql/versions/8.3/install.sh index 20e9b9dff..a80545d43 100755 --- a/plugins/mysql/versions/8.3/install.sh +++ b/plugins/mysql/versions/8.3/install.sh @@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` - -install_tmp=${rootPath}/tmp/mw_install.pl mysqlDir=${serverPath}/source/mysql _os=`uname` @@ -60,7 +58,7 @@ VERSION=8.3.0 Install_mysql() { mkdir -p ${mysqlDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' # ----- cpu start ------ if [ -z "${cpuCore}" ]; then diff --git a/plugins/mysql/versions/8.4/install.sh b/plugins/mysql/versions/8.4/install.sh index b93081973..f5a51f5fe 100755 --- a/plugins/mysql/versions/8.4/install.sh +++ b/plugins/mysql/versions/8.4/install.sh @@ -12,8 +12,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` - -install_tmp=${rootPath}/tmp/mw_install.pl mysqlDir=${serverPath}/source/mysql _os=`uname` @@ -62,7 +60,7 @@ VERSION=8.4.2 Install_mysql() { mkdir -p ${mysqlDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' # ----- cpu start ------ diff --git a/plugins/mysql/versions/9.0/install.sh b/plugins/mysql/versions/9.0/install.sh index 9699b7e3c..a23fbeca3 100755 --- a/plugins/mysql/versions/9.0/install.sh +++ b/plugins/mysql/versions/9.0/install.sh @@ -13,7 +13,6 @@ serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl mysqlDir=${serverPath}/source/mysql _os=`uname` @@ -62,7 +61,7 @@ VERSION=9.0.1 Install_mysql() { mkdir -p ${mysqlDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' # ----- cpu start ------ diff --git a/plugins/nezha/index.py b/plugins/nezha/index.py index 7f4dc3db4..d79ad4bc2 100755 --- a/plugins/nezha/index.py +++ b/plugins/nezha/index.py @@ -10,8 +10,12 @@ import json from datetime import datetime -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -115,7 +119,7 @@ class App: def contentReplace(self, content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$RUN_USER}', self.getRunUser()) content = content.replace('{$HOME_DIR}', self.getHomeDir()) diff --git a/plugins/nezha/install.sh b/plugins/nezha/install.sh index 01b4af5bf..0a007b9a4 100755 --- a/plugins/nezha/install.sh +++ b/plugins/nezha/install.sh @@ -7,18 +7,16 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl - action=$1 type=$2 if [ "${2}" == "" ];then - echo '缺少安装脚本...' > $install_tmp + echo '缺少安装脚本...' exit 0 fi if [ ! -d $curPath/versions/$2 ];then - echo '缺少安装脚本2...' > $install_tmp + echo '缺少安装脚本2...' exit 0 fi diff --git a/plugins/nezha/versions/0.15.2/install.sh b/plugins/nezha/versions/0.15.2/install.sh index f845ca7dc..4ed401a67 100755 --- a/plugins/nezha/versions/0.15.2/install.sh +++ b/plugins/nezha/versions/0.15.2/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=0.15.2 # bash install.sh install 0.15.2 @@ -82,7 +81,7 @@ download_file() { Install_dashborad(){ - echo '正在安装哪吒监控...' > $install_tmp + echo '正在安装哪吒监控...' mkdir -p $serverPath/source if [ ! -f $TARGET_DIR/nezha ];then @@ -103,7 +102,7 @@ Install_dashborad(){ } Install_agent(){ - echo -e "正在下载监控端" > $install_tmp + echo -e "正在下载监控端" mkdir -p $serverPath/source version=v0.15.1 @@ -137,7 +136,7 @@ Install_App() echo "$VERSION" > $serverPath/nezha/version.pl cd ${rootPath} && python3 ${rootPath}/plugins/nezha/index.py init_cfg fi - echo 'install successful' > $install_tmp + echo 'install successful' } Uninstall_App() @@ -147,7 +146,7 @@ Uninstall_App() cd ${rootPath} && python3 ${rootPath}/plugins/nezha/index.py stop cd ${rootPath} && python3 ${rootPath}/plugins/nezha/index.py stop_agent rm -rf $serverPath/nezha - echo "install fail" > $install_tmp + echo "install fail" } action=$1 diff --git a/plugins/op_load_balance/index.py b/plugins/op_load_balance/index.py index 59a646fb2..262a20b04 100755 --- a/plugins/op_load_balance/index.py +++ b/plugins/op_load_balance/index.py @@ -8,8 +8,12 @@ import subprocess import json import re -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False @@ -73,7 +77,7 @@ def writeConf(data): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$APP_PATH}', app_path) return content diff --git a/plugins/op_load_balance/install.sh b/plugins/op_load_balance/install.sh index d78bf73fb..d34795d33 100755 --- a/plugins/op_load_balance/install.sh +++ b/plugins/op_load_balance/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl action=$1 version=$2 @@ -24,11 +23,11 @@ else fi Install_App(){ - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/op_load_balance echo "${version}" > $serverPath/op_load_balance/version.pl cd ${rootPath} && python3 ${rootPath}/plugins/op_load_balance/index.py start - echo 'install ok' > $install_tmp + echo '安装OP负载均衡成功!' } Uninstall_App(){ diff --git a/plugins/op_waf/index.py b/plugins/op_waf/index.py index 0f14caaf8..92698b3e9 100755 --- a/plugins/op_waf/index.py +++ b/plugins/op_waf/index.py @@ -12,8 +12,12 @@ import subprocess import json import re -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False @@ -245,7 +249,7 @@ def contentReplace(content): service_path = mw.getServerDir() waf_root = getServerDir() waf_path = waf_root + "/waf" - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$WAF_PATH}', waf_path) content = content.replace('{$WAF_ROOT}', waf_root) diff --git a/plugins/op_waf/install.sh b/plugins/op_waf/install.sh index e3a7950c7..dd59aff37 100755 --- a/plugins/op_waf/install.sh +++ b/plugins/op_waf/install.sh @@ -9,8 +9,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl - action=$1 version=$2 sys_os=`uname` @@ -28,7 +26,7 @@ fi Install_App(){ - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source/op_waf mkdir -p $serverPath/op_waf @@ -114,7 +112,7 @@ Install_App(){ fi echo "${version}" > $serverPath/op_waf/version.pl - echo 'install ok' > $install_tmp + echo '安装OP防火墙成功!' cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py start echo "cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py start" diff --git a/plugins/op_waf/tool_task.py b/plugins/op_waf/tool_task.py index 1ee26c173..70d2fd107 100644 --- a/plugins/op_waf/tool_task.py +++ b/plugins/op_waf/tool_task.py @@ -6,8 +6,13 @@ import os import time import json -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw +from utils.crontab import crontab as MwCrontab app_debug = False @@ -33,10 +38,9 @@ def getTaskConf(): def getConfigData(): - try: + conf = getTaskConf() + if os.path.exists(conf): return json.loads(mw.readFile(getTaskConf())) - except: - pass return [] @@ -89,7 +93,7 @@ def createBgTaskByName(name, args): _where1 = args['minute-n'] _minute = '' - mw_dir = mw.getRunDir() + mw_dir = mw.getPanelDir() cmd = ''' mw_dir=%s rname=%s @@ -122,10 +126,10 @@ logs_file=$plugin_path/${rname}.log 'stype': "toShell", 'sname': '', 'sbody': cmd, - 'urladdress': '', + 'url_address': '', } - task_id = cron_api.add(params) + task_id = MwCrontab.instance().add(params) if task_id > 0: cfg["task_id"] = task_id cfg["name"] = name @@ -146,9 +150,7 @@ def removeBgTask(): res = mw.M("crontab").field("id, name").where( "id=?", (cfg["task_id"],)).find() if res and res["id"] == cfg["task_id"]: - import crontab_api - api = crontab_api.crontab_api() - data = api.delete(cfg["task_id"]) + data = MwCrontab.instance().delete(cfg["task_id"]) if data[0]: cfg["task_id"] = -1 cfg_list[x] = cfg diff --git a/plugins/openresty/index.html b/plugins/openresty/index.html index 30803d9f1..086bec6de 100755 --- a/plugins/openresty/index.html +++ b/plugins/openresty/index.html @@ -1,5 +1,5 @@
-
+

服务

diff --git a/plugins/openresty/index.py b/plugins/openresty/index.py index 217634a6f..2a93c65f4 100755 --- a/plugins/openresty/index.py +++ b/plugins/openresty/index.py @@ -8,8 +8,13 @@ import threading import subprocess import re -sys.path.append(os.getcwd() + "/class/core") -import mw + +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -41,18 +46,19 @@ def getInitDFile(): def getArgs(): args = sys.argv[2:] + # print(args) tmp = {} args_len = len(args) if args_len == 1: t = args[0].strip('{').strip('}') - t = t.split(':') + t = t.split(':',2) tmp[t[0]] = t[1] elif args_len > 1: for i in range(len(args)): - t = args[i].split(':') + t = args[i].split(':',2) tmp[t[0]] = t[1] - + # print(tmp) return tmp @@ -127,7 +133,7 @@ def checkAuthEq(file, owner='root'): def confReplace(): - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getServerDir() content = mw.readFile(getConfTpl()) content = content.replace('{$SERVER_PATH}', service_path) @@ -190,7 +196,9 @@ def confReplace(): # vhost vhost_dir = mw.getServerDir() + '/web_conf/nginx/vhost' vhost_tpl_dir = getPluginDir() + '/conf/vhost' - # print(vhost_dir, vhost_tpl_dir) + if not os.path.exists(vhost_dir): + mw.execShell('mkdir -p ' + vhost_dir) + vhost_list = ['0.websocket.conf', '0.nginx_status.conf'] for f in vhost_list: a_conf = vhost_dir + '/' + f @@ -202,7 +210,7 @@ def confReplace(): def initDreplace(): file_tpl = getInitDTpl() - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getServerDir() initD_path = getServerDir() + '/init.d' @@ -398,16 +406,25 @@ def initdUinstall(): mw.execShell('systemctl disable openresty') return 'ok' +def getNgxStatusPort(): + ngx_status_file = mw.getServerDir() + '/web_conf/nginx/vhost/0.nginx_status.conf' + content = mw.readFile(ngx_status_file) + rep = r'listen\s*(.*);' + tmp = re.search(rep, content) + port = tmp.groups()[0].strip() + return port + def runInfo(): op_status = status() if op_status == 'stop': return mw.returnJson(False, "未启动!") + port = getNgxStatusPort() # 取Openresty负载状态 try: - url = 'http://127.0.0.1/nginx_status' - result = mw.httpGet(url, timeout=1) + url = 'http://127.0.0.1:%s/nginx_status' % port + result = mw.httpGet(url, timeout=3) tmp = result.split() data = {} data['active'] = tmp[2] @@ -419,8 +436,7 @@ def runInfo(): data['Waiting'] = tmp[15] return mw.getJson(data) except Exception as e: - - url = 'http://' + mw.getHostAddr() + '/nginx_status' + url = 'http://' + mw.getHostAddr() + ':%s/nginx_status' % port result = mw.httpGet(url) tmp = result.split() data = {} @@ -550,6 +566,13 @@ def installPreInspection(): if __name__ == "__main__": + + version = '1.27.1' + version_pl = getServerDir() + "/version.pl" + if os.path.exists(version_pl): + version = mw.readFile(version_pl) + + func = sys.argv[1] if func == 'status': print(status()) diff --git a/plugins/openresty/info.json b/plugins/openresty/info.json index 8ddc42b42..5f9e808aa 100755 --- a/plugins/openresty/info.json +++ b/plugins/openresty/info.json @@ -12,6 +12,6 @@ "home":"http://openresty.org", "date":"2017-11-24", "pid": "1", - "versions": ["1.17.8","1.19.3","1.21.4","1.25.3"], + "versions": ["1.17.8","1.19.3","1.21.4","1.25.3","1.27.1","rtmp"], "updates": ["1.17.8.2","1.19.3.1","1.21.4.2","1.25.3.2"] } \ No newline at end of file diff --git a/plugins/openresty/install.sh b/plugins/openresty/install.sh index 3c72e5451..7b6f9b7fd 100755 --- a/plugins/openresty/install.sh +++ b/plugins/openresty/install.sh @@ -2,8 +2,8 @@ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin export PATH -# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2 -# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2 +# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4 +# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4 curPath=`pwd` rootPath=$(dirname "$curPath") @@ -15,7 +15,6 @@ action=$1 type=$2 VERSION=$2 -install_tmp=${rootPath}/tmp/mw_install.pl openrestyDir=${serverPath}/source/openresty if id www &> /dev/null ;then @@ -57,5 +56,4 @@ if [ "${action}" == "install" ] && [ -d $serverPath/openresty ];then #初始化 cd ${rootPath} && python3 ${rootPath}/plugins/openresty/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/openresty/index.py initd_install - rm -rf $openrestyDir/openresty-${VERSION} fi diff --git a/plugins/openresty/versions/1.17.8/install.sh b/plugins/openresty/versions/1.17.8/install.sh index d3bf95aab..544c97b9f 100644 --- a/plugins/openresty/versions/1.17.8/install.sh +++ b/plugins/openresty/versions/1.17.8/install.sh @@ -15,8 +15,6 @@ action=$1 type=$2 VERSION=1.17.8.2 - -install_tmp=${rootPath}/tmp/mw_install.pl openrestyDir=${serverPath}/source/openresty Install_openresty() @@ -149,6 +147,11 @@ Install_openresty() if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then rm -rf ${openrestyDir}/libressl-${libresslVersion} fi + + if [ -d $openrestyDir/openresty-${VERSION} ];then + rm -rf $openrestyDir/openresty-${VERSION} + fi + echo '安装完成' } diff --git a/plugins/openresty/versions/1.19.3/install.sh b/plugins/openresty/versions/1.19.3/install.sh index 9934e2a6c..2af69a9b1 100644 --- a/plugins/openresty/versions/1.19.3/install.sh +++ b/plugins/openresty/versions/1.19.3/install.sh @@ -15,8 +15,6 @@ action=$1 type=$2 VERSION=1.19.3.1 - -install_tmp=${rootPath}/tmp/mw_install.pl openrestyDir=${serverPath}/source/openresty Install_openresty() @@ -150,6 +148,10 @@ Install_openresty() if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then rm -rf ${openrestyDir}/libressl-${libresslVersion} fi + + if [ -d $openrestyDir/openresty-${VERSION} ];then + rm -rf $openrestyDir/openresty-${VERSION} + fi echo '安装完成' } diff --git a/plugins/openresty/versions/1.21.4/install.sh b/plugins/openresty/versions/1.21.4/install.sh index 531e38368..0ceaa3a91 100644 --- a/plugins/openresty/versions/1.21.4/install.sh +++ b/plugins/openresty/versions/1.21.4/install.sh @@ -16,7 +16,6 @@ type=$2 VERSION=1.21.4.4 -install_tmp=${rootPath}/tmp/mw_install.pl openrestyDir=${serverPath}/source/openresty Install_openresty() @@ -149,6 +148,10 @@ Install_openresty() if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then rm -rf ${openrestyDir}/libressl-${libresslVersion} fi + + if [ -d $openrestyDir/openresty-${VERSION} ];then + rm -rf $openrestyDir/openresty-${VERSION} + fi echo '安装完成' } diff --git a/plugins/openresty/versions/1.25.3/install.sh b/plugins/openresty/versions/1.25.3/install.sh index 9c5224662..0becee2f0 100644 --- a/plugins/openresty/versions/1.25.3/install.sh +++ b/plugins/openresty/versions/1.25.3/install.sh @@ -16,7 +16,6 @@ type=$2 VERSION=1.25.3.2 -install_tmp=${rootPath}/tmp/mw_install.pl openrestyDir=${serverPath}/source/openresty Install_openresty() @@ -164,6 +163,10 @@ Install_openresty() if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then rm -rf ${openrestyDir}/libressl-${libresslVersion} fi + + if [ -d $openrestyDir/openresty-${VERSION} ];then + rm -rf $openrestyDir/openresty-${VERSION} + fi echo '安装完成' } diff --git a/plugins/openresty/versions/1.27.1/install.sh b/plugins/openresty/versions/1.27.1/install.sh new file mode 100644 index 000000000..5567b2e64 --- /dev/null +++ b/plugins/openresty/versions/1.27.1/install.sh @@ -0,0 +1,183 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH + +# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2 +# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.2 + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + +sysName=`uname` +action=$1 +type=$2 + +VERSION=1.27.1.1 + +openrestyDir=${serverPath}/source/openresty + +Install_openresty() +{ + if [ -d $serverPath/openresty ];then + exit 0 + fi + + # ----- cpu start ------ + if [ -z "${cpuCore}" ]; then + cpuCore="1" + fi + + if [ -f /proc/cpuinfo ];then + cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` + fi + + MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') + if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then + if [ "${cpuCore}" -gt "${MEM_INFO}" ];then + cpuCore="${MEM_INFO}" + fi + else + cpuCore="1" + fi + + if [ "$cpuCore" -gt "2" ];then + cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'` + else + cpuCore="1" + fi + # ----- cpu end ------ + + mkdir -p ${openrestyDir} + echo '正在安装脚本文件...' + + # wget -O openresty-1.21.4.1.tar.gz https://openresty.org/download/openresty-1.21.4.1.tar.gz + if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz https://openresty.org/download/openresty-${VERSION}.tar.gz -T 3 + fi + + DOWNLOAD_SIZE=`wc -c ${openrestyDir}/openresty-${VERSION}.tar.gz | awk '{print $1}'` + if [ "$DOWNLOAD_SIZE" == "0" ];then + echo 'download failed, download again' + rm -rf ${openrestyDir}/openresty-${VERSION}.tar.gz + fi + + # Last Download Method + if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz http://dl.midoks.icu/soft/openresty/openresty-${VERSION}.tar.gz -T 3 + fi + + cd ${openrestyDir} && tar -zxvf openresty-${VERSION}.tar.gz + + OPTIONS='' + + opensslVersion="1.1.1p" + libresslVersion="3.9.1" + pcreVersion='8.38' + if [ "$sysName" == "Darwin" ];then + + if [ ! -f ${openrestyDir}/pcre-${pcreVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/pcre-${pcreVersion}.tar.gz https://netix.dl.sourceforge.net/project/pcre/pcre/${pcreVersion}/pcre-${pcreVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/pcre-${pcreVersion} ];then + cd ${openrestyDir} && tar -zxvf pcre-${pcreVersion}.tar.gz + fi + OPTIONS="${OPTIONS} --with-pcre=${openrestyDir}/pcre-${pcreVersion}" + + + if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then + cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz + fi + OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}" + + # BREW_DIR=`which brew` + # BREW_DIR=${BREW_DIR/\/bin\/brew/} + + # brew info openssl@1.1 | grep /opt/homebrew/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}' + # OPENSSL_LIB_DEPEND_DIR=`brew info openssl@1.1 | grep ${BREW_DIR}/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'` + # OPTIONS="${OPTIONS} --with-openssl=${OPENSSL_LIB_DEPEND_DIR}" + else + if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then + cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz + fi + OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}" + + fi + + if [[ "$VERSION" =~ "1.25.3" ]]; then + OPTIONS="${OPTIONS} --with-http_v3_module" + + if [ ! -f ${openrestyDir}/libressl-${libresslVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/libressl-${libresslVersion}.tar.gz https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${libresslVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/libressl-${libresslVersion} ];then + cd ${openrestyDir} && tar -zxvf libressl-${libresslVersion}.tar.gz + fi + + OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/include" + OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/lib" + fi + + + cd ${openrestyDir}/openresty-${VERSION} && ./configure \ + --prefix=$serverPath/openresty \ + $OPTIONS \ + --with-stream \ + --with-http_v2_module \ + --with-http_ssl_module \ + --with-http_slice_module \ + --with-http_stub_status_module \ + --with-http_sub_module \ + --with-http_realip_module + # --without-luajit-gc64 + # --with-debug + # 用于调式 + + CMD_MAKE=`which gmake` + if [ "$?" == "0" ];then + gmake -j${cpuCore} && gmake install && gmake clean + else + make -j${cpuCore} && make install && make clean + fi + + + if [ -d ${openrestyDir}/pcre-${pcreVersion} ];then + rm -rf ${openrestyDir}/pcre-${pcreVersion} + fi + + if [ -d ${openrestyDir}/openssl-${opensslVersion} ];then + rm -rf ${openrestyDir}/openssl-${opensslVersion} + fi + + if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then + rm -rf ${openrestyDir}/libressl-${libresslVersion} + fi + + if [ -d $openrestyDir/openresty-${VERSION} ];then + rm -rf $openrestyDir/openresty-${VERSION} + fi + echo 'Installation of Openresty completed' +} + +Uninstall_openresty() +{ + echo 'Uninstalling Openresty completed' +} + +action=$1 +if [ "${1}" == 'install' ];then + Install_openresty +else + Uninstall_openresty +fi diff --git a/plugins/openresty/versions/rtmp/install.sh b/plugins/openresty/versions/rtmp/install.sh new file mode 100644 index 000000000..11a8f5d88 --- /dev/null +++ b/plugins/openresty/versions/rtmp/install.sh @@ -0,0 +1,199 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH + +# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/openresty && bash install.sh install 1.27.1.1 +# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.27.1.1 + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + +sysName=`uname` +action=$1 +type=$2 + +VERSION=1.27.1.1 + +openrestyDir=${serverPath}/source/openresty + +Install_openresty() +{ + if [ -d $serverPath/openresty ];then + exit 0 + fi + + # ----- cpu start ------ + if [ -z "${cpuCore}" ]; then + cpuCore="1" + fi + + if [ -f /proc/cpuinfo ];then + cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` + fi + + MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') + if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then + if [ "${cpuCore}" -gt "${MEM_INFO}" ];then + cpuCore="${MEM_INFO}" + fi + else + cpuCore="1" + fi + + if [ "$cpuCore" -gt "2" ];then + cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'` + else + cpuCore="1" + fi + # ----- cpu end ------ + + mkdir -p ${openrestyDir} + echo '正在安装脚本文件...' + + # wget -O openresty-1.21.4.1.tar.gz https://openresty.org/download/openresty-1.21.4.1.tar.gz + if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz https://openresty.org/download/openresty-${VERSION}.tar.gz -T 3 + fi + + DOWNLOAD_SIZE=`wc -c ${openrestyDir}/openresty-${VERSION}.tar.gz | awk '{print $1}'` + if [ "$DOWNLOAD_SIZE" == "0" ];then + echo 'download failed, download again' + rm -rf ${openrestyDir}/openresty-${VERSION}.tar.gz + fi + + # Last Download Method + if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz http://dl.midoks.icu/soft/openresty/openresty-${VERSION}.tar.gz -T 3 + fi + + cd ${openrestyDir} && tar -zxvf openresty-${VERSION}.tar.gz + + OPTIONS='' + + opensslVersion="1.1.1p" + libresslVersion="3.9.1" + pcreVersion='8.38' + if [ "$sysName" == "Darwin" ];then + + if [ ! -f ${openrestyDir}/pcre-${pcreVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/pcre-${pcreVersion}.tar.gz https://netix.dl.sourceforge.net/project/pcre/pcre/${pcreVersion}/pcre-${pcreVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/pcre-${pcreVersion} ];then + cd ${openrestyDir} && tar -zxvf pcre-${pcreVersion}.tar.gz + fi + OPTIONS="${OPTIONS} --with-pcre=${openrestyDir}/pcre-${pcreVersion}" + + + if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then + cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz + fi + OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}" + + # BREW_DIR=`which brew` + # BREW_DIR=${BREW_DIR/\/bin\/brew/} + + # brew info openssl@1.1 | grep /opt/homebrew/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}' + # OPENSSL_LIB_DEPEND_DIR=`brew info openssl@1.1 | grep ${BREW_DIR}/Cellar/openssl@1.1 | cut -d \ -f 1 | awk 'END {print}'` + # OPTIONS="${OPTIONS} --with-openssl=${OPENSSL_LIB_DEPEND_DIR}" + else + if [ ! -f ${openrestyDir}/openssl-${opensslVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/openssl-${opensslVersion}.tar.gz https://www.openssl.org/source/openssl-${opensslVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/openssl-${opensslVersion} ];then + cd ${openrestyDir} && tar -zxvf openssl-${opensslVersion}.tar.gz + fi + OPTIONS="${OPTIONS} --with-openssl=${openrestyDir}/openssl-${opensslVersion}" + + fi + + if [[ "$VERSION" =~ "1.25.3" ]]; then + OPTIONS="${OPTIONS} --with-http_v3_module" + + if [ ! -f ${openrestyDir}/libressl-${libresslVersion}.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/libressl-${libresslVersion}.tar.gz https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${libresslVersion}.tar.gz + fi + + if [ ! -d ${openrestyDir}/libressl-${libresslVersion} ];then + cd ${openrestyDir} && tar -zxvf libressl-${libresslVersion}.tar.gz + fi + + OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/include" + OPTIONS="${OPTIONS} --with-cc-opt=-I${openrestyDir}/libressl-${libresslVersion}/libressl/build/lib" + fi + + # rtmp推流功能 + nginx_rtmp_ver=1.2.2 + if [ ! -f ${openrestyDir}/nginx-rtmp-module.tar.gz ];then + wget --no-check-certificate -O ${openrestyDir}/nginx-rtmp-module.tar.gz https://github.com/arut/nginx-rtmp-module/archive/refs/tags/v${nginx_rtmp_ver}.tar.gz + fi + + if [ ! -d ${openrestyDir}/nginx-rtmp-module.tar.gz ];then + cd ${openrestyDir} && tar -zxvf nginx-rtmp-module.tar.gz + fi + OPTIONS="${OPTIONS} --add-module=${openrestyDir}/nginx-rtmp-module-${nginx_rtmp_ver}" + + + cd ${openrestyDir}/openresty-${VERSION} && ./configure \ + --prefix=$serverPath/openresty \ + $OPTIONS \ + --with-stream \ + --with-http_v2_module \ + --with-http_ssl_module \ + --with-http_slice_module \ + --with-http_stub_status_module \ + --with-http_sub_module \ + --with-http_realip_module + # --without-luajit-gc64 + # --with-debug + # 用于调式 + + CMD_MAKE=`which gmake` + if [ "$?" == "0" ];then + gmake -j${cpuCore} && gmake install && gmake clean + else + make -j${cpuCore} && make install && make clean + fi + + + if [ -d ${openrestyDir}/pcre-${pcreVersion} ];then + rm -rf ${openrestyDir}/pcre-${pcreVersion} + fi + + if [ -d ${openrestyDir}/openssl-${opensslVersion} ];then + rm -rf ${openrestyDir}/openssl-${opensslVersion} + fi + + if [ -d ${openrestyDir}/libressl-${libresslVersion} ];then + rm -rf ${openrestyDir}/libressl-${libresslVersion} + fi + + if [ -d ${openrestyDir}/nginx-rtmp-module-${nginx_rtmp_ver} ];then + rm -rf ${openrestyDir}/nginx-rtmp-module-${nginx_rtmp_ver} + fi + + if [ -d $openrestyDir/openresty-${VERSION} ];then + rm -rf $openrestyDir/openresty-${VERSION} + fi + + echo '安装完成' +} + +Uninstall_openresty() +{ + echo '卸载完成' +} + +action=$1 +if [ "${1}" == 'install' ];then + Install_openresty +else + Uninstall_openresty +fi diff --git a/plugins/pgadmin/index.py b/plugins/pgadmin/index.py index 178d42804..fd973ebff 100755 --- a/plugins/pgadmin/index.py +++ b/plugins/pgadmin/index.py @@ -7,9 +7,12 @@ import time import re import json -sys.path.append(os.getcwd() + "/class/core") -import mw -import site_api +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -59,7 +62,7 @@ def getConf(): def getPort(): file = getConf() content = mw.readFile(file) - rep = 'listen\s*(.*);' + rep = r'listen\s*(.*);' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -85,12 +88,12 @@ def contentReplace(content): cfg = getCfg() service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$APP_PATH}', service_path+'/'+getPluginName()+'/data') port = cfg["port"] - rep = 'listen\s*(.*);' + rep = r'listen\s*(.*);' content = re.sub(rep, "listen " + port + ';', content) return content @@ -133,8 +136,8 @@ def returnCfg(): def __release_port(port): from collections import namedtuple try: - import firewall_api - firewall_api.firewall_api().addAcceptPortArgs(port, 'pgAdmin默认端口', 'port') + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().addAcceptPort(port, 'pgAdmin默认端口', 'port') return port except Exception as e: return "Release failed {}".format(e) @@ -143,8 +146,8 @@ def __release_port(port): def __delete_port(port): from collections import namedtuple try: - import firewall_api - firewall_api.firewall_api().delAcceptPortArgs(port, 'tcp') + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().delAcceptPort(port, 'tcp') return port except Exception as e: return "Release failed {}".format(e) @@ -181,8 +184,9 @@ def getPythonName(): return data[0].strip(); def initPgConfFile(): + pyname = getPythonName() file_tpl = getPluginDir() + '/conf/config_local.py' - dst_file = getServerDir()+'/run/lib/python3.10/site-packages/pgadmin4/config_local.py' + dst_file = getServerDir()+'/run/lib/'+pyname+'/site-packages/pgadmin4/config_local.py' if not os.path.exists(dst_file): service_path = mw.getServerDir() content = mw.readFile(file_tpl) diff --git a/plugins/pgadmin/install.sh b/plugins/pgadmin/install.sh index 8de77dec5..55f861f2a 100755 --- a/plugins/pgadmin/install.sh +++ b/plugins/pgadmin/install.sh @@ -26,8 +26,6 @@ echo "python:$P_VER" # cd /www/server/mdserver-web && python3 plugins/pgadmin/index.py start # cd /www/server/mdserver-web && python3 plugins/pgadmin/index.py stop -install_tmp=${rootPath}/tmp/mw_install.pl - if [ "$sys_os" == "Darwin" ];then BAK='_bak' else @@ -85,7 +83,7 @@ Install_pgadmin() pip install gunicorn # pip install https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v8.10/pip/pgadmin4-8.10-py3-none-any.whl - pip install https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v8.12/pip/pgadmin4-8.12-py3-none-any.whl + pip install https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v8.13/pip/pgadmin4-8.13-py3-none-any.whl cd ${rootPath} && python3 ${rootPath}/plugins/pgadmin/index.py start echo '安装完成' diff --git a/plugins/php-apt/index.py b/plugins/php-apt/index.py index b3906c33d..75647b2eb 100755 --- a/plugins/php-apt/index.py +++ b/plugins/php-apt/index.py @@ -9,14 +9,12 @@ import json import shutil -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) -if mw.isAppleSystem(): - cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' - info = mw.execShell(cmd) - p = "/usr/local/lib/" + info[0].strip() + "/site-packages" - sys.path.append(p) +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -87,7 +85,7 @@ def status(version): def contentReplace(content, version): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$PHP_VERSION}', version) content = content.replace('{$LOCAL_IP}', mw.getLocalIp()) @@ -741,7 +739,7 @@ def getPhpinfo(version): return 'PHP[' + version + ']未启动,不可访问!!!' sock_file = getFpmAddress(version) - root_dir = mw.getRootDir() + '/phpinfo' + root_dir = mw.getFatherDir() + '/phpinfo' mw.execShell("rm -rf " + root_dir) mw.execShell("mkdir -p " + root_dir) @@ -764,16 +762,14 @@ def getLibConf(version): return mw.returnJson(False, '指定PHP版本不存在!') # phpini = mw.readFile(fname) - content = mw.execShell( - 'cat /etc/php/' + version + '/fpm/conf.d/*' + " | grep -v '^;' |tr -s '\n'") + content = mw.execShell('cat /etc/php/' + version + '/fpm/conf.d/*' + " | grep -v '^;' |tr -s '\n'") content = content[0] libpath = getPluginDir() + '/versions/phplib.conf' phplib = json.loads(mw.readFile(libpath)) libs = [] - tasks = mw.M('tasks').where( - "status!=?", ('1',)).field('status,name').select() + tasks = mw.M('tasks').where("status!=?", ('1',)).field('status,name').select() for lib in phplib: lib['task'] = '1' for task in tasks: @@ -784,7 +780,7 @@ def getLibConf(version): if tmp1[0].lower() == lib['name'].lower(): lib['task'] = task['status'] lib['phpversions'] = [] - lib['phpversions'].append(tmp1[1]) + lib['phpversions'].append(tmp1[1].replace('.','')) if content.find(lib['check']) == -1: lib['status'] = False else: @@ -800,13 +796,10 @@ def installLib(version): return data[1] name = args['name'] - execstr = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + \ - version + ' install ' + name - - rettime = time.strftime('%Y-%m-%d %H:%M:%S') - insert_info = (None, '安装PHPAPT[' + name + '-' + version + ']', - 'execshell', '0', rettime, execstr) - mw.M('tasks').add('id,name,type,status,addtime,execstr', insert_info) + cmd = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + version + ' install ' + name + install_name = '安装PHPAPT[' + name + '-' + version + ']' + import thisdb + thisdb.addTask(name=install_name,cmd=cmd) mw.triggerTask() return mw.returnJson(True, '已将下载任务添加到队列!') @@ -819,8 +812,7 @@ def uninstallLib(version): return data[1] name = args['name'] - execstr = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + \ - version + ' uninstall ' + name + execstr = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + version + ' uninstall ' + name data = mw.execShell(execstr) # data[0] == '' and diff --git a/plugins/php-apt/index_php_apt.py b/plugins/php-apt/index_php_apt.py index 97ade4c7d..c6b4301b0 100755 --- a/plugins/php-apt/index_php_apt.py +++ b/plugins/php-apt/index_php_apt.py @@ -8,11 +8,12 @@ import re import json import shutil -# reload(sys) -# sys.setdefaultencoding('utf8') +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) -sys.path.append(os.getcwd() + "/class/core") -import mw +import core.mw as mw if mw.isAppleSystem(): cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' @@ -117,7 +118,7 @@ def getPhpinfo(version): sock_file = getFpmAddress(version) # print(sock_file) - root_dir = mw.getRootDir() + '/phpinfo' + root_dir = mw.getFatherDir() + '/phpinfo' mw.execShell("rm -rf " + root_dir) mw.execShell("mkdir -p " + root_dir) diff --git a/plugins/php-apt/install.sh b/plugins/php-apt/install.sh index 071fe921e..e0f9bed05 100755 --- a/plugins/php-apt/install.sh +++ b/plugins/php-apt/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl if id www &> /dev/null ;then echo "www uid is `id -u www`" @@ -34,12 +33,12 @@ type=$2 apt_ver=${type:0:1}.${type:1:2} if [ "${2}" == "" ];then - echo '缺少安装脚本...' > $install_tmp + echo '缺少安装脚本...' exit 0 fi if [ ! -d $curPath/versions/$2 ];then - echo '缺少安装脚本2...' > $install_tmp + echo '缺少安装脚本2...' exit 0 fi @@ -87,6 +86,8 @@ fi cd ${curPath} && sh -x $curPath/versions/$2/install.sh $1 if [ "${action}" == "install" ] && [ -d ${serverPath}/php-apt/${type} ];then + apt update -y + #初始化 cd ${rootPath} && python3 ${rootPath}/plugins/php-apt/index.py start ${type} cd ${rootPath} && python3 ${rootPath}/plugins/php-apt/index.py restart ${type} diff --git a/plugins/php-apt/js/php.js b/plugins/php-apt/js/php.js index c19640a7b..0fbab5484 100755 --- a/plugins/php-apt/js/php.js +++ b/plugins/php-apt/js/php.js @@ -631,9 +631,9 @@ function phpLibConfig(version){ } if (libs[i]['task'] == '-1' && libs[i].phpversions.indexOf(version) != -1) { - opt = '安装' + opt = '安装.' } else if (libs[i]['task'] == '0' && libs[i].phpversions.indexOf(version) != -1) { - opt = '等待安装...' + opt = '等待.' } else if (libs[i].status) { opt = '卸载' } else { diff --git a/plugins/php-apt/versions/56/install.sh b/plugins/php-apt/versions/56/install.sh index 7fc6788dc..ba3973dc5 100755 --- a/plugins/php-apt/versions/56/install.sh +++ b/plugins/php-apt/versions/56/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl #获取信息和版本 diff --git a/plugins/php-apt/versions/70/install.sh b/plugins/php-apt/versions/70/install.sh index ba1457a76..3631acf80 100755 --- a/plugins/php-apt/versions/70/install.sh +++ b/plugins/php-apt/versions/70/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - #获取信息和版本 # bash /www/server/mdsever-web/scripts/getos.sh diff --git a/plugins/php-apt/versions/71/install.sh b/plugins/php-apt/versions/71/install.sh index 0af724652..c8955a33a 100755 --- a/plugins/php-apt/versions/71/install.sh +++ b/plugins/php-apt/versions/71/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl #获取信息和版本 diff --git a/plugins/php-apt/versions/72/install.sh b/plugins/php-apt/versions/72/install.sh index 66bb3a74b..816f7f4db 100755 --- a/plugins/php-apt/versions/72/install.sh +++ b/plugins/php-apt/versions/72/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl #获取信息和版本 diff --git a/plugins/php-apt/versions/73/install.sh b/plugins/php-apt/versions/73/install.sh index e19cb1bb2..475825e55 100755 --- a/plugins/php-apt/versions/73/install.sh +++ b/plugins/php-apt/versions/73/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl #获取信息和版本 diff --git a/plugins/php-apt/versions/74/install.sh b/plugins/php-apt/versions/74/install.sh index f7852231f..956a864ff 100755 --- a/plugins/php-apt/versions/74/install.sh +++ b/plugins/php-apt/versions/74/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - #获取信息和版本 # bash /www/server/mdsever-web/scripts/getos.sh diff --git a/plugins/php-apt/versions/80/install.sh b/plugins/php-apt/versions/80/install.sh index c8102196d..f30fba31c 100755 --- a/plugins/php-apt/versions/80/install.sh +++ b/plugins/php-apt/versions/80/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - #获取信息和版本 # bash /www/server/mdsever-web/scripts/getos.sh diff --git a/plugins/php-apt/versions/81/install.sh b/plugins/php-apt/versions/81/install.sh index cdd5b943d..124c765ad 100755 --- a/plugins/php-apt/versions/81/install.sh +++ b/plugins/php-apt/versions/81/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - #获取信息和版本 # bash /www/server/mdsever-web/scripts/getos.sh diff --git a/plugins/php-apt/versions/82/install.sh b/plugins/php-apt/versions/82/install.sh index bab78a3d1..b23415303 100755 --- a/plugins/php-apt/versions/82/install.sh +++ b/plugins/php-apt/versions/82/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - #获取信息和版本 # bash /www/server/mdsever-web/scripts/getos.sh diff --git a/plugins/php-apt/versions/83/install.sh b/plugins/php-apt/versions/83/install.sh index d1ee9485a..e0ace4c94 100755 --- a/plugins/php-apt/versions/83/install.sh +++ b/plugins/php-apt/versions/83/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - #获取信息和版本 # bash /www/server/mdsever-web/scripts/getos.sh diff --git a/plugins/php-apt/versions/84/install.sh b/plugins/php-apt/versions/84/install.sh index 17027048b..435102c7a 100755 --- a/plugins/php-apt/versions/84/install.sh +++ b/plugins/php-apt/versions/84/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - #获取信息和版本 # bash /www/server/mdsever-web/scripts/getos.sh diff --git a/plugins/php-apt/versions/common/brotli.sh b/plugins/php-apt/versions/common/brotli.sh new file mode 100755 index 000000000..0cfffab8a --- /dev/null +++ b/plugins/php-apt/versions/common/brotli.sh @@ -0,0 +1,94 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") +sourcePath=${serverPath}/source/php + +actionType=$1 +version=$2 + +sysName=`uname` +LIBNAME=brotli +LIBV=0.15.2 + +extVer=`bash $curPath/lib.sh $version` +extFile=/usr/lib/php/${extVer}/${LIBNAME}.so + +if [ "$sysName" == "Darwin" ];then + BAK='_bak' +else + BAK='' +fi + +Install_lib() +{ + + #cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n' + isInstall=`cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n' |grep "${LIBNAME}.so"` + if [ "${isInstall}" != "" ];then + echo "php-$version 已安装${LIBNAME},请选择其它版本!" + return + fi + + if [ ! -f "$extFile" ];then + + php_lib=$sourcePath/php_lib + mkdir -p $php_lib + if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then + wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz + cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz + fi + cd $php_lib/${LIBNAME}-${LIBV} + + /usr/bin/phpize${version} + ./configure --with-php-config=/usr/bin/php-config${version} + make && make install && make clean + + fi + echo "$extFile checking ..." + if [ ! -f "$extFile" ];then + echo "ERROR!" + return; + fi + + + echo "" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini + echo "[${LIBNAME}]" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini + echo "extension=${LIBNAME}.so" >> /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini + + systemctl restart php${version}-fpm + echo '===========================================================' + echo 'successful!' +} + + +Uninstall_lib() +{ + if [ ! -f "/usr/bin/php-config${version}" ];then + echo "php-$version 未安装,请选择其它版本!" + return + fi + + + if [ -f /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini ];then + rm -rf /etc/php/${version}/fpm/conf.d/${LIBNAME}.ini + rm -rf $extFile + fi + + systemctl restart php${version}-fpm + echo '===============================================' + echo 'successful!' +} + + +if [ "$actionType" == 'install' ];then + Install_lib +elif [ "$actionType" == 'uninstall' ];then + Uninstall_lib +fi \ No newline at end of file diff --git a/plugins/php-apt/versions/common/phalcon.sh b/plugins/php-apt/versions/common/phalcon.sh index 17d4b232c..758ee0961 100755 --- a/plugins/php-apt/versions/common/phalcon.sh +++ b/plugins/php-apt/versions/common/phalcon.sh @@ -50,6 +50,8 @@ elif [[ "$version" == '8.2' ]]; then CMD="${CMD}phalcon5" elif [[ "$version" == '8.3' ]]; then CMD="${CMD}phalcon5" +elif [[ "$version" == '8.4' ]]; then + CMD="${CMD}phalcon5" fi $CMD diff --git a/plugins/php-apt/versions/common/sg11.sh b/plugins/php-apt/versions/common/sg11.sh new file mode 100644 index 000000000..e10e2d3bf --- /dev/null +++ b/plugins/php-apt/versions/common/sg11.sh @@ -0,0 +1,123 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH=$PATH:/opt/homebrew/bin + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") +sourcePath=${serverPath}/source/php + +# https://www.sourceguardian.com/loaders.html + +# support 52-81 + +LIBNAME=sg11 +LIBV=0 +sysName=`uname` +actionType=$1 +version=$2 +# SG_VER=${version:0:1}.${version:1:2} +SG_VER=${version} + +extVer=`bash $curPath/lib.sh $version` +extFile=/usr/lib/php/${extVer}/${LIBNAME}.so + +if [ "$sysName" == "Darwin" ];then + BAK='_bak' +else + BAK='' +fi + +SORT_LIBNAME="10-${LIBNAME}" + +Install_lib() +{ + bash ${rootPath}/scripts/getos.sh + OSNAME=`cat ${rootPath}/data/osname.pl` + + echo "${OSNAME}" + + DEFAULT_OSNAME=linux-x86_64 + SUFFIX_NAME=lin + if [ "$OSNAME" == 'macos' ];then + DEFAULT_OSNAME=macosx + SUFFIX_NAME=dar + fi + + isInstall=`cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n' |grep "${LIBNAME}.so"` + if [ "${isInstall}" != "" ];then + echo "php-$version 已安装${LIBNAME},请选择其它版本!" + return + fi + + + if [ ! -f "$extFile" ];then + + php_lib=$sourcePath/php_lib + mkdir -p $php_lib + mkdir -p $php_lib/sg11 + if [ ! -f $php_lib/sg11_loaders.tar.bz2 ];then + curl -sSLo $php_lib/sg11_loaders.tar.bz2 https://www.sourceguardian.com/loaders/download/loaders.tar.bz2 + echo "cd $php_lib && tar -jxvf $php_lib/sg11_loaders.tar.bz2 -C $php_lib/sg11" + cd $php_lib && tar -jxvf $php_lib/sg11_loaders.tar.bz2 -C $php_lib/sg11 + fi + + + if [ ! -d $php_lib/sg11/macosx ];then + cd $php_lib && tar -jxvf $php_lib/sg11_loaders.tar.bz2 -C $php_lib/sg11 + fi + cd $php_lib/sg11 + # echo "mv $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.lin $extFile" + if [ -f $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.${SUFFIX_NAME} ];then + cp -rf $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.${SUFFIX_NAME} $extFile + else + echo 'Not supported temporarily' + exit + fi + + if [ "$OSNAME" == 'macos' ];then + xattr -c * $extFile + fi + fi + + if [ ! -f "$extFile" ];then + echo "ERROR!" + return + fi + + echo "" >> /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini + echo "[${LIBNAME}]" >> /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini + echo "extension=${LIBNAME}.so" >> /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini + + systemctl restart php${version}-fpm + echo '===========================================================' + echo 'successful!' +} + + +Uninstall_lib() +{ + if [ ! -f "/usr/bin/php-config${version}" ];then + echo "php-$version 未安装,请选择其它版本!" + return + fi + + + if [ -f /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini ];then + rm -rf /etc/php/${version}/fpm/conf.d/${SORT_LIBNAME}.ini + rm -rf $extFile + fi + + systemctl restart php${version}-fpm + echo '===============================================' + echo 'successful!' +} + + +if [ "$actionType" == 'install' ];then + Install_lib +elif [ "$actionType" == 'uninstall' ];then + Uninstall_lib +fi \ No newline at end of file diff --git a/plugins/php-apt/versions/lib.sh b/plugins/php-apt/versions/lib.sh index fed6f11ee..0822ff7c4 100644 --- a/plugins/php-apt/versions/lib.sh +++ b/plugins/php-apt/versions/lib.sh @@ -30,4 +30,6 @@ elif [[ "$version" == '8.2' ]]; then echo '20220829' elif [[ "$version" == '8.3' ]]; then echo '20230831' +elif [[ "$version" == '8.4' ]]; then + echo '20240924' fi \ No newline at end of file diff --git a/plugins/php-apt/versions/phplib.conf b/plugins/php-apt/versions/phplib.conf index d64cde6b0..af51a02a1 100755 --- a/plugins/php-apt/versions/phplib.conf +++ b/plugins/php-apt/versions/phplib.conf @@ -1,4 +1,41 @@ [ + { + "name": "sg11", + "versions": [ + "56", + "70", + "71", + "72", + "73", + "74", + "80", + "81", + "82", + "83" + ], + "type": "脚本解密", + "msg": "用于解密SG11加密脚本!", + "shell": "sg11.sh", + "check": "sg11.so" + }, + { + "name": "ionCube", + "versions": [ + "56", + "70", + "71", + "72", + "73", + "74", + "81", + "82", + "83" + ], + "type": "脚本解密", + "msg": "用于解密ionCube Encoder加密脚本!", + "shell": "ioncube.sh", + "check": "ioncube" + }, { "name": "pdo", "versions": [ @@ -126,24 +163,6 @@ "shell": "zend_optimizer.sh", "check": "ZendOptimizer" }, - { - "name": "ionCube", - "versions": [ - "56", - "70", - "71", - "72", - "73", - "74", - "81", - "82", - "83" - ], - "type": "脚本解密", - "msg": "用于解密ionCube Encoder加密脚本!", - "shell": "ioncube.sh", - "check": "ioncube" - }, { "name": "opcache", "versions": [ @@ -179,6 +198,25 @@ "shell": "mcrypt.sh", "check": "mcrypt" }, + { + "name": "brotli", + "versions": [ + "70", + "71", + "72", + "73", + "74", + "80", + "81", + "82", + "83", + "84" + ], + "type": "压缩", + "msg": "压缩算法", + "shell": "brotli.sh", + "check": "brotli.so" + }, { "name": "bcmath", "versions": [ diff --git a/plugins/php-guard/index.py b/plugins/php-guard/index.py index 15ee87527..6846179e9 100755 --- a/plugins/php-guard/index.py +++ b/plugins/php-guard/index.py @@ -8,11 +8,12 @@ import re import json import shutil -# reload(sys) -# sys.setdefaultencoding('utf8') +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) -sys.path.append(os.getcwd() + "/class/core") -import mw +import core.mw as mw app_debug = False if mw.isAppleSystem(): diff --git a/plugins/php-guard/install.sh b/plugins/php-guard/install.sh index eb6b1c80f..b1edae490 100755 --- a/plugins/php-guard/install.sh +++ b/plugins/php-guard/install.sh @@ -7,17 +7,14 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl - - Install_pg() { - echo '卸载PHP守护中...' + echo '安装PHP守护中...' echo 'True' > ${rootPath}/data/502Task.pl mkdir -p $serverPath/php-guard echo '1.0' > $serverPath/php-guard/version.pl - echo '卸载PHP守护成功!!' + echo '安装PHP守护成功!!' } Uninstall_pg() diff --git a/plugins/php-yum/index.py b/plugins/php-yum/index.py index 2d0c87ad3..6f92b5572 100755 --- a/plugins/php-yum/index.py +++ b/plugins/php-yum/index.py @@ -9,14 +9,12 @@ import json import shutil -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) -if mw.isAppleSystem(): - cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' - info = mw.execShell(cmd) - p = "/usr/local/lib/" + info[0].strip() + "/site-packages" - sys.path.append(p) +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -79,7 +77,7 @@ def status(version): def contentReplace(content, version): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$PHP_VERSION}', version) content = content.replace('{$LOCAL_IP}', mw.getLocalIp()) @@ -90,15 +88,15 @@ def contentReplace(content, version): content = content.replace('{$PHP_USER}', 'nobody') content = content.replace('{$PHP_GROUP}', 'nobody') - rep = 'listen.owner\s*=\s*(.+)\r?\n' + rep = r'listen.owner\s*=\s*(.+)\r?\n' val = ';listen.owner = nobody\n' content = re.sub(rep, val, content) - rep = 'listen.group\s*=\s*(.+)\r?\n' + rep = r'listen.group\s*=\s*(.+)\r?\n' val = ';listen.group = nobody\n' content = re.sub(rep, val, content) - rep = 'user\s*=\s*(.+)\r?\n' + rep = r'user\s*=\s*(.+)\r?\n' val = ';user = nobody\n' content = re.sub(rep, val, content) @@ -309,7 +307,7 @@ def getPhpConf(version): phpini = mw.readFile(getConf(version)) result = [] for g in gets: - rep = g['name'] + '\s*=\s*([0-9A-Za-z_& ~]+)(\s*;?|\r?\n)' + rep = g['name'] + r'\s*=\s*([0-9A-Za-z_& ~]+)(\s*;?|\r?\n)' tmp = re.search(rep, phpini) if not tmp: continue @@ -328,7 +326,7 @@ def submitPhpConf(version): phpini = mw.readFile(filename) for g in gets: if g in args: - rep = g + '\s*=\s*(.+)\r?\n' + rep = g + r'\s*=\s*(.+)\r?\n' val = g + ' = ' + args[g] + '\n' phpini = re.sub(rep, val, phpini) mw.writeFile(filename, phpini) @@ -741,10 +739,10 @@ def setDisableFunc(version): def getPhpinfo(version): stat = status(version) if stat == 'stop': - return 'PHP[' + version + ']未启动,不可访问!!!' + return 'PHP[' + version + ']未启动,不可访问!' sock_file = getFpmAddress(version) - root_dir = mw.getRootDir() + '/phpinfo' + root_dir = mw.getFatherDir() + '/phpinfo' mw.execShell("rm -rf " + root_dir) mw.execShell("mkdir -p " + root_dir) @@ -773,8 +771,7 @@ def getLibConf(version): phplib = json.loads(mw.readFile(libpath)) libs = [] - tasks = mw.M('tasks').where( - "status!=?", ('1',)).field('status,name').select() + tasks = mw.M('tasks').where("status!=?", ('1',)).field('status,name').select() for lib in phplib: lib['task'] = '1' for task in tasks: @@ -801,13 +798,10 @@ def installLib(version): return data[1] name = args['name'] - execstr = "cd " + getPluginDir() + '/versions/' + \ - " && /bin/bash common.sh " + version + ' install ' + name - - rettime = time.strftime('%Y-%m-%d %H:%M:%S') - insert_info = (None, '安装PHPYUM[' + name + '-' + version + ']', - 'execshell', '0', rettime, execstr) - mw.M('tasks').add('id,name,type,status,addtime,execstr', insert_info) + cmd = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + version + ' install ' + name + install_name = '安装PHPYUM[' + name + '-' + version + ']' + import thisdb + thisdb.addTask(name=install_name,cmd=cmd) mw.triggerTask() return mw.returnJson(True, '已将下载任务添加到队列!') @@ -820,8 +814,7 @@ def uninstallLib(version): return data[1] name = args['name'] - execstr = "cd " + getPluginDir() + '/versions/' + \ - " && /bin/bash common.sh " + version + ' uninstall ' + name + execstr = "cd " + getPluginDir() + '/versions/' + " && /bin/bash common.sh " + version + ' uninstall ' + name data = mw.execShell(execstr) # data[0] == '' and diff --git a/plugins/php-yum/index_php_yum.py b/plugins/php-yum/index_php_yum.py index 0600580d6..17bae712b 100755 --- a/plugins/php-yum/index_php_yum.py +++ b/plugins/php-yum/index_php_yum.py @@ -8,17 +8,12 @@ import re import json import shutil -# reload(sys) -# sys.setdefaultencoding('utf8') +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) -sys.path.append(os.getcwd() + "/class/core") -import mw - -if mw.isAppleSystem(): - cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' - info = mw.execShell(cmd) - p = "/usr/local/lib/" + info[0].strip() + "/site-packages" - sys.path.append(p) +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -102,7 +97,7 @@ def getPhpinfo(version): return 'PHP[' + version + ']未启动,不可访问!!!' sock_file = getFpmAddress(version) - root_dir = mw.getRootDir() + '/phpinfo' + root_dir = mw.getFatherDir() + '/phpinfo' mw.execShell("rm -rf " + root_dir) mw.execShell("mkdir -p " + root_dir) diff --git a/plugins/php-yum/install.sh b/plugins/php-yum/install.sh index 65581a900..4ed4d994b 100755 --- a/plugins/php-yum/install.sh +++ b/plugins/php-yum/install.sh @@ -7,8 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl - if id www &> /dev/null ;then echo "www uid is `id -u www`" echo "www shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `" @@ -101,8 +99,8 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php-yum/${type} ];then echo "install PHP-YUM[${type}] extend end" #初始化 - cd ${rootPath} && python3 ${rootPath}/plugins/php-yum/index.py start ${type} - cd ${rootPath} && python3 ${rootPath}/plugins/php-yum/index.py initd_install ${type} + cd ${rootPath} && python3 plugins/php-yum/index.py start ${type} + cd ${rootPath} && python3 plugins/php-yum/index.py initd_install ${type} if [ ! -f /usr/local/bin/composer ];then cd /tmp diff --git a/plugins/php-yum/js/php.js b/plugins/php-yum/js/php.js index 958fadb71..dc4c9d5e9 100755 --- a/plugins/php-yum/js/php.js +++ b/plugins/php-yum/js/php.js @@ -633,9 +633,9 @@ function phpLibConfig(version){ } if (libs[i]['task'] == '-1' && libs[i].phpversions.indexOf(version) != -1) { - opt = '安装' + opt = '安装.' } else if (libs[i]['task'] == '0' && libs[i].phpversions.indexOf(version) != -1) { - opt = '等待安装...' + opt = '等待.' } else if (libs[i].status) { opt = '卸载' } else { diff --git a/plugins/php-yum/versions/74/install.sh b/plugins/php-yum/versions/74/install.sh index 8b3eae5fd..b2098e1e5 100755 --- a/plugins/php-yum/versions/74/install.sh +++ b/plugins/php-yum/versions/74/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl version=7.4.x PHP_VER=74 @@ -30,7 +29,7 @@ Uninstall_php() { yum remove -y php74 php74-php-fpm php74-* rm -rf $serverPath/php-yum/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php-yum/versions/80/install.sh b/plugins/php-yum/versions/80/install.sh index c3e0b37f0..070c2544c 100755 --- a/plugins/php-yum/versions/80/install.sh +++ b/plugins/php-yum/versions/80/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - version=8.0.x PHP_VER=80 @@ -32,7 +30,7 @@ Uninstall_php() { # $serverPath/php-ya/init.d/php${PHP_VER} stop rm -rf $serverPath/php-yum/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php-yum/versions/81/install.sh b/plugins/php-yum/versions/81/install.sh index c0b4bde9e..5877e21df 100755 --- a/plugins/php-yum/versions/81/install.sh +++ b/plugins/php-yum/versions/81/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - version=8.1.x PHP_VER=81 @@ -30,7 +28,7 @@ Uninstall_php() { yum remove -y php81 php81-php-fpm php81-* rm -rf $serverPath/php-yum/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php-yum/versions/82/install.sh b/plugins/php-yum/versions/82/install.sh index 8aa04e5f6..6440c2966 100755 --- a/plugins/php-yum/versions/82/install.sh +++ b/plugins/php-yum/versions/82/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - version=8.2.x PHP_VER=82 @@ -30,7 +28,7 @@ Uninstall_php() { yum remove -y php82 php82-php-fpm php82-* rm -rf $serverPath/php-yum/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php-yum/versions/83/install.sh b/plugins/php-yum/versions/83/install.sh index 59deca624..1a195b8c9 100755 --- a/plugins/php-yum/versions/83/install.sh +++ b/plugins/php-yum/versions/83/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - version=8.3.x PHP_VER=83 @@ -30,7 +28,7 @@ Uninstall_php() { yum remove -y php83 php83-php-fpm php83-* rm -rf $serverPath/php-yum/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php-yum/versions/84/install.sh b/plugins/php-yum/versions/84/install.sh index fcad71440..105495362 100755 --- a/plugins/php-yum/versions/84/install.sh +++ b/plugins/php-yum/versions/84/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - version=8.4.x PHP_VER=84 @@ -30,7 +28,7 @@ Uninstall_php() { yum remove -y php84 php84-php-fpm php84-* rm -rf $serverPath/php-yum/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php-yum/versions/common/bak_brotli.sh b/plugins/php-yum/versions/common/bak_brotli.sh new file mode 100755 index 000000000..a23a344b0 --- /dev/null +++ b/plugins/php-yum/versions/common/bak_brotli.sh @@ -0,0 +1,89 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH=$PATH:/opt/homebrew/bin + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") +sourcePath=${serverPath}/source/php + +actionType=$1 +version=$2 + +sysName=`uname` +LIBNAME=brotli +LIBV=0.15.2 + +SORT_LIBNAME="10-${LIBNAME}" +extVer=`bash $curPath/lib.sh $version` +extFile=/opt/remi/php${version}/root/usr/lib64/php +extSoFile=$extFile/modules/${LIBNAME}.so +cfgDir=/etc/opt/remi/php${version}/php.d +extIni=${cfgDir}/10-${LIBNAME}.ini + +if [ "$sysName" == "Darwin" ];then + BAK='_bak' +else + BAK='' +fi + +Install_lib() +{ + + isInstall=`cat /etc/php/${version}/fpm/conf.d/* | grep -v '^;' |tr -s '\n' |grep "${LIBNAME}.so"` + if [ "${isInstall}" != "" ];then + echo "php-$version 已安装${LIBNAME},请选择其它版本!" + return + fi + + if [ ! -f "$extFile" ];then + + php_lib=$sourcePath/php_lib + mkdir -p $php_lib + if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then + wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz + cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz + fi + cd $php_lib/${LIBNAME}-${LIBV} + + /opt/remi/php${version}/root/usr/bin/phpize + ./configure --with-php-config=/opt/remi/php${version}/root/usr/bin/ + make && make install && make clean + + fi + echo "$extFile checking ..." + if [ ! -f "$extFile" ];then + echo "ERROR!" + return; + fi + + cho "" >> $extIni + echo "[${LIBNAME}]" >> $extIni + echo "extension=${LIBNAME}.so" >> $extIni + + systemctl restart php${version}-fpm + echo '===========================================================' + echo 'successful!' +} + + +Uninstall_lib() +{ + if [ -f $extIni ];then + rm -rf $extIni + fi + + systemctl restart php${version}-fpm + echo '===============================================' + echo 'successful!' +} + + +if [ "$actionType" == 'install' ];then + Install_lib +elif [ "$actionType" == 'uninstall' ];then + Uninstall_lib +fi \ No newline at end of file diff --git a/plugins/php-yum/versions/lib.sh b/plugins/php-yum/versions/lib.sh index ff3358fdc..f7a8a8eb9 100644 --- a/plugins/php-yum/versions/lib.sh +++ b/plugins/php-yum/versions/lib.sh @@ -22,8 +22,38 @@ if [ "$php_status" != "" ];then systemctl ${action} php${version}-php-fpm fi -# if [ -f $php_fpm_service_file ];then -# systemctl ${action} php${version}-php-fpm -# else -# $serverPath/php/init.d/php${version} ${action} -# fi \ No newline at end of file +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + +version=$1 + +if [ "$version" == '5.6' ];then + echo '20131226' +elif [[ "$version" == '7.0' ]]; then + echo '20151012' +elif [[ "$version" == '7.1' ]]; then + echo '20160303' +elif [[ "$version" == '7.2' ]]; then + echo '20170718' +elif [[ "$version" == '7.3' ]]; then + echo '20180731' +elif [[ "$version" == '7.4' ]]; then + echo '20190902' +elif [[ "$version" == '8.0' ]]; then + echo '20200930' +elif [[ "$version" == '8.1' ]]; then + echo '20210902' +elif [[ "$version" == '8.2' ]]; then + echo '20220829' +elif [[ "$version" == '8.3' ]]; then + echo '20230831' +elif [[ "$version" == '8.4' ]]; then + echo '20240924' +fi \ No newline at end of file diff --git a/plugins/php-yum/versions/phplib.conf b/plugins/php-yum/versions/phplib.conf index 262ff5816..72b9f2fa0 100755 --- a/plugins/php-yum/versions/phplib.conf +++ b/plugins/php-yum/versions/phplib.conf @@ -183,6 +183,25 @@ "shell": "mcrypt.sh", "check": "mcrypt" }, + { + "name": "brotli", + "versions": [ + "70", + "71", + "72", + "73", + "74", + "80", + "81", + "82", + "83", + "84" + ], + "type": "压缩", + "msg": "压缩算法", + "shell": "brotli.sh", + "check": "brotli.so" + }, { "name": "bcmath", "versions": [ diff --git a/plugins/php/conf/pathinfo.conf b/plugins/php/conf/pathinfo.conf index 47b573684..fc5ad5219 100644 --- a/plugins/php/conf/pathinfo.conf +++ b/plugins/php/conf/pathinfo.conf @@ -5,4 +5,7 @@ if ($fastcgi_script_name ~ "^(.+?\.php)(/.+)$") { } fastcgi_param SCRIPT_FILENAME $document_root$real_script_name; fastcgi_param SCRIPT_NAME $real_script_name; -fastcgi_param PATH_INFO $path_info; \ No newline at end of file +fastcgi_param PATH_INFO $path_info; + +#修复http3不传HOST问题 +fastcgi_param HTTP_HOST $host; \ No newline at end of file diff --git a/plugins/php/index.py b/plugins/php/index.py index ac905c358..be3b7f287 100755 --- a/plugins/php/index.py +++ b/plugins/php/index.py @@ -11,14 +11,12 @@ import shutil # reload(sys) # sys.setdefaultencoding('utf8') -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) -if mw.isAppleSystem(): - cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' - info = mw.execShell(cmd) - p = "/usr/local/lib/" + info[0].strip() + "/site-packages" - sys.path.append(p) +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -121,7 +119,7 @@ def status(version): def contentReplace(content, version): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$PHP_VERSION}', version) content = content.replace('{$LOCAL_IP}', mw.getLocalIp()) @@ -346,8 +344,8 @@ def phpOp(version, method): def start(version): - mw.execShell( - 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/www/server/lib/icu/lib') + cmd = 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/www/server/lib/icu/lib:/opt/homebrew/lib' + mw.execShell(cmd) return phpOp(version, 'start') @@ -915,7 +913,7 @@ def getPhpinfo(version): return 'PHP[' + version + ']未启动,不可访问!!!' sock_file = getFpmAddress(version) - root_dir = mw.getRootDir() + '/phpinfo' + root_dir = mw.getFatherDir() + '/phpinfo' mw.execShell("rm -rf " + root_dir) mw.execShell("mkdir -p " + root_dir) @@ -941,8 +939,7 @@ def libConfCommon(version): phplib = json.loads(mw.readFile(libpath)) libs = [] - tasks = mw.M('tasks').where( - "status!=?", ('1',)).field('status,name').select() + tasks = mw.M('tasks').where("status!=?", ('1',)).field('status,name').select() for lib in phplib: lib['task'] = '1' for task in tasks: @@ -980,13 +977,10 @@ def installLib(version): return data[1] name = args['name'] - execstr = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + \ - version + ' install ' + name - - rettime = time.strftime('%Y-%m-%d %H:%M:%S') - insert_info = (None, '安装[' + name + '-' + version + ']', - 'execshell', '0', rettime, execstr) - mw.M('tasks').add('id,name,type,status,addtime,execstr', insert_info) + cmd = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + version + ' install ' + name + install_name = '安装[' + name + '-' + version + ']' + import thisdb + thisdb.addTask(name=install_name,cmd=cmd) mw.triggerTask() return mw.returnJson(True, '已将下载任务添加到队列!') @@ -999,8 +993,7 @@ def uninstallLib(version): return data[1] name = args['name'] - execstr = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + \ - version + ' uninstall ' + name + execstr = "cd " + getPluginDir() + "/versions && /bin/bash common.sh " + version + ' uninstall ' + name data = mw.execShell(execstr) # data[0] == '' and diff --git a/plugins/php/index_php.py b/plugins/php/index_php.py index 5ffba3af6..06a90a9c4 100755 --- a/plugins/php/index_php.py +++ b/plugins/php/index_php.py @@ -8,17 +8,12 @@ import re import json import shutil -# reload(sys) -# sys.setdefaultencoding('utf8') +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) -sys.path.append(os.getcwd() + "/class/core") -import mw - -if mw.isAppleSystem(): - cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' - info = mw.execShell(cmd) - p = "/usr/local/lib/" + info[0].strip() + "/site-packages" - sys.path.append(p) +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -116,7 +111,7 @@ def getPhpinfo(version): return 'PHP[' + version + ']未启动,不可访问!!!' sock_file = getFpmAddress(version) - root_dir = mw.getRootDir() + '/phpinfo' + root_dir = mw.getFatherDir() + '/phpinfo' mw.execShell("rm -rf " + root_dir) mw.execShell("mkdir -p " + root_dir) diff --git a/plugins/php/install.sh b/plugins/php/install.sh index 7ce3a2a01..821b4bb3e 100755 --- a/plugins/php/install.sh +++ b/plugins/php/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl if id www &> /dev/null ;then echo "www uid is `id -u www`" @@ -22,12 +21,12 @@ action=$1 type=$2 if [ "${2}" == "" ];then - echo '缺少安装脚本...' > $install_tmp + echo '缺少安装脚本...' exit 0 fi if [ ! -d $curPath/versions/$2 ];then - echo '缺少安装脚本2...' > $install_tmp + echo '缺少安装脚本2...' exit 0 fi @@ -64,7 +63,9 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php/${type} ];then # cd /www/server/mdserver-web/plugins/php/versions/common && bash gd.sh install 56 # cd /www/server/mdserver-web/plugins/php/versions/common && bash openssl.sh install 56 # cd /www/server/mdserver-web/plugins/php/versions/common && bash fileinfo.sh install 81 + cd ${rootPath}/plugins/php/versions/common && bash curl.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash gd.sh install ${type} + cd ${rootPath}/plugins/php/versions/common && bash readline.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash iconv.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash exif.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash intl.sh install ${type} @@ -74,10 +75,9 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php/${type} ];then cd ${rootPath}/plugins/php/versions/common && bash gettext.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash redis.sh install ${type} cd ${rootPath}/plugins/php/versions/common && bash memcached.sh install ${type} - - if [ "${type}" -gt "73" ];then - cd ${rootPath}/plugins/php/versions/common && bash zlib.sh install ${type} - fi + cd ${rootPath}/plugins/php/versions/common && bash pcntl.sh install ${type} + cd ${rootPath}/plugins/php/versions/common && bash zip.sh install ${type} + cd ${rootPath}/plugins/php/versions/common && bash zlib.sh install ${type} echo "install PHP${type} extend end" diff --git a/plugins/php/versions/52/install.sh b/plugins/php/versions/52/install.sh index a4d80eea5..7516e0f04 100755 --- a/plugins/php/versions/52/install.sh +++ b/plugins/php/versions/52/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_ARCH=`arch` version=5.2.17 @@ -16,7 +15,7 @@ PHP_VER=52 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -83,18 +82,11 @@ if [ -f $serverPath/php/${PHP_VER}/bin/php ];then return fi -OPTIONS='' +OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then - OPTIONS='--without-iconv' OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" - OPTIONS="${OPTIONS} --with-curl=${serverPath}/lib/curl" -else - OPTIONS='--without-iconv' - # OPTIONS="--with-iconv=${serverPath}/lib/libiconv" - OPTIONS="${OPTIONS} --with-curl" fi - IS_64BIT=`getconf LONG_BIT` if [ "$IS_64BIT" == "64" ];then OPTIONS="${OPTIONS} --with-libdir=lib64" @@ -113,8 +105,6 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then --prefix=$serverPath/php/${PHP_VER} \ --exec-prefix=$serverPath/php/${PHP_VER} \ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \ - --with-zlib-dir=$serverPath/lib/zlib \ - --enable-zip \ --enable-xml \ --enable-shared \ --with-mysql=mysqlnd \ @@ -163,7 +153,7 @@ Uninstall_php() { $serverPath/php/init.d/php${PHP_VER} stop rm -rf $serverPath/php/${PHP_VER} - echo "uninstall php-${version} ..." > $install_tmp + echo "uninstall php-${version} ..." } action=${1} diff --git a/plugins/php/versions/53/install.sh b/plugins/php/versions/53/install.sh index 48713d8d6..49906af25 100755 --- a/plugins/php/versions/53/install.sh +++ b/plugins/php/versions/53/install.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_ARCH=`arch` version=5.3.29 @@ -16,7 +15,7 @@ PHP_VER=53 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-5.3.29 ..." > $install_tmp +echo "安装php-5.3.29 ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -66,13 +65,9 @@ fi # OPTIONS="${OPTIONS} --with-gd --enable-gd-native-ttf" # OPTIONS="${OPTIONS} --with-jpeg --with-jpeg-dir=/usr/lib" OPTIONS='--without-iconv' + if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" - OPTIONS="${OPTIONS} --with-curl=$(brew --prefix curl)" -else - # OPTIONS=" --with-iconv=${serverPath}/lib/libiconv" - OPTIONS="${OPTIONS} --with-curl" fi IS_64BIT=`getconf LONG_BIT` @@ -89,7 +84,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" @@ -114,12 +109,11 @@ if [ ! -d $serverPath/php/${PHP_VER}/bin ];then --prefix=$serverPath/php/${PHP_VER} \ --exec-prefix=$serverPath/php/${PHP_VER} \ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \ - --with-zlib-dir=$serverPath/lib/zlib \ --enable-mysqlnd \ --with-mysql=mysqlnd \ --with-pdo-mysql=mysqlnd \ --with-mysqli=mysqlnd \ - --enable-zip \ + --enable-mbstring \ --enable-exif \ --enable-hash \ --enable-libxml \ @@ -167,7 +161,7 @@ Uninstall_php() { $serverPath/php/init.d/php53 stop rm -rf $serverPath/php/53 - echo "uninstall php-5.3.29 ..." > $install_tmp + echo "uninstall php-5.3.29 ..." } action=${1} diff --git a/plugins/php/versions/54/install.sh b/plugins/php/versions/54/install.sh index a1ebad15c..5c0012883 100755 --- a/plugins/php/versions/54/install.sh +++ b/plugins/php/versions/54/install.sh @@ -10,14 +10,12 @@ sourcePath=${serverPath}/source sysName=`uname` SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl - version=5.4.45 PHP_VER=54 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -61,20 +59,10 @@ fi OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" - - BREW_DIR=`which brew` - BREW_DIR=${BREW_DIR/\/bin\/brew/} - CURL_DEPEND_DIR=`brew info curl | grep ${BREW_DIR}/Cellar/curl | cut -d \ -f 1 | awk 'END {print}'` - OPTIONS="$OPTIONS --with-curl=${CURL_DEPEND_DIR}" # OPTIONS="${OPTIONS} --with-pcre-dir=${serverPath}/lib/pcre" # OPTIONS="${OPTIONS} --with-external-pcre=${serverPath}/lib/pcre" - else - # OPTIONS="--with-iconv=${serverPath}/lib/libiconv" - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --enable-mbstring" OPTIONS="${OPTIONS} --with-readline" fi @@ -92,7 +80,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" @@ -124,12 +112,11 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then --prefix=$serverPath/php/${PHP_VER} \ --exec-prefix=$serverPath/php/${PHP_VER} \ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \ - --with-zlib-dir=$serverPath/lib/zlib \ --enable-mysqlnd \ --with-mysql=mysqlnd \ --with-pdo-mysql=mysqlnd \ --with-mysqli=mysqlnd \ - --enable-zip \ + --enable-mbstring \ --enable-sockets \ --enable-ftp \ --enable-simplexml \ @@ -182,7 +169,7 @@ Uninstall_php() { $serverPath/php/init.d/php54 stop rm -rf $serverPath/php/54 - echo "卸载php-5.4.45 ..." > $install_tmp + echo "卸载php-5.4.45 ..." } action=${1} diff --git a/plugins/php/versions/55/install.sh b/plugins/php/versions/55/install.sh index 7b8cdef88..4331d545e 100755 --- a/plugins/php/versions/55/install.sh +++ b/plugins/php/versions/55/install.sh @@ -10,14 +10,12 @@ sourcePath=${serverPath}/source sysName=`uname` SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl - version=5.5.38 PHP_VER=55 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-5.5.38 ..." > $install_tmp +echo "安装php-5.5.38 ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -61,13 +59,8 @@ fi OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" - OPTIONS="${OPTIONS} --with-curl=$(brew --prefix curl)" else - # OPTIONS="--with-iconv=${serverPath}/lib/libiconv" - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --enable-mbstring" OPTIONS="${OPTIONS} --with-readline" fi @@ -85,7 +78,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" @@ -119,12 +112,11 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then --prefix=$serverPath/php/${PHP_VER} \ --exec-prefix=$serverPath/php/${PHP_VER} \ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \ - --with-zlib-dir=$serverPath/lib/zlib \ --enable-mysqlnd \ --with-mysql=mysqlnd \ --with-pdo-mysql=mysqlnd \ --with-mysqli=mysqlnd \ - --enable-zip \ + --enable-mbstring \ --enable-simplexml \ --enable-sockets \ --enable-ftp \ @@ -151,7 +143,7 @@ Uninstall_php() { $serverPath/php/init.d/php55 stop rm -rf $serverPath/php/55 - echo "卸载php-5.5.38 ..." > $install_tmp + echo "卸载php-5.5.38 ..." } action=${1} diff --git a/plugins/php/versions/56/install.sh b/plugins/php/versions/56/install.sh index 8224972af..85259ac0b 100755 --- a/plugins/php/versions/56/install.sh +++ b/plugins/php/versions/56/install.sh @@ -12,15 +12,13 @@ sourcePath=${serverPath}/source sysName=`uname` SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl - version=5.6.40 PHP_VER=56 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -65,13 +63,8 @@ fi OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" - OPTIONS="${OPTIONS} --with-curl=$(brew --prefix curl)" OPTIONS="${OPTIONS} --with-zlib-dir=$(brew --prefix zlib)" - # OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)" else - # OPTIONS="--with-iconv=${serverPath}/lib/libiconv" - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --enable-mbstring" OPTIONS="${OPTIONS} --with-readline" fi @@ -89,7 +82,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" @@ -128,12 +121,11 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then --prefix=$serverPath/php/${PHP_VER} \ --exec-prefix=$serverPath/php/${PHP_VER} \ --with-config-file-path=$serverPath/php/56/etc \ - --with-zlib-dir=$serverPath/lib/zlib \ --enable-mysqlnd \ --with-mysql=mysqlnd \ --with-pdo-mysql=mysqlnd \ --with-mysqli=mysqlnd \ - --enable-zip \ + --enable-mbstring \ --enable-simplexml \ --enable-ftp \ --enable-sockets \ @@ -161,7 +153,7 @@ Uninstall_php() { $serverPath/php/init.d/php56 stop rm -rf $serverPath/php/56 - echo "卸载php-${version} ..." > $install_tmp + echo "卸载php-${version} ..." } action=${1} diff --git a/plugins/php/versions/70/install.sh b/plugins/php/versions/70/install.sh index 6f262eadb..2225bc953 100755 --- a/plugins/php/versions/70/install.sh +++ b/plugins/php/versions/70/install.sh @@ -9,14 +9,13 @@ serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl version=7.0.33 PHP_VER=70 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -61,11 +60,8 @@ fi OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" - OPTIONS="${OPTIONS} --with-curl" # OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)" else - # OPTIONS="--with-iconv=${serverPath}/lib/libiconv" - OPTIONS="${OPTIONS} --with-curl" OPTIONS="${OPTIONS} --with-readline" fi @@ -83,7 +79,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" @@ -112,11 +108,9 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then --prefix=$serverPath/php/${PHP_VER} \ --exec-prefix=$serverPath/php/${PHP_VER} \ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \ - --with-zlib-dir=$serverPath/lib/zlib \ --enable-mysqlnd \ --with-mysqli=mysqlnd \ --with-pdo-mysql=mysqlnd \ - --enable-zip \ --enable-mbstring \ --enable-simplexml \ --enable-ftp \ diff --git a/plugins/php/versions/71/install.sh b/plugins/php/versions/71/install.sh index 73f7818e8..9a1983fa9 100755 --- a/plugins/php/versions/71/install.sh +++ b/plugins/php/versions/71/install.sh @@ -10,14 +10,12 @@ sourcePath=${serverPath}/source sysName=`uname` SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl - version=7.1.33 PHP_VER=71 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -63,10 +61,8 @@ OPTIONS='' OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then OPTIONS="${OPTIONS} --with-freetype-dir=${serverPath}/lib/freetype" - OPTIONS="${OPTIONS} --with-curl" OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)" else - OPTIONS="${OPTIONS} --with-curl" OPTIONS="${OPTIONS} --with-readline" fi @@ -84,7 +80,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" @@ -114,11 +110,9 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then --prefix=$serverPath/php/${PHP_VER} \ --exec-prefix=$serverPath/php/${PHP_VER} \ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \ - --with-zlib-dir=$serverPath/lib/zlib \ --enable-mysqlnd \ --with-mysqli=mysqlnd \ --with-pdo-mysql=mysqlnd \ - --enable-zip \ --enable-mbstring \ --enable-simplexml \ --enable-ftp \ @@ -144,7 +138,7 @@ Uninstall_php() { $serverPath/php/init.d/php71 stop rm -rf $serverPath/php/71 - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php/versions/72/install.sh b/plugins/php/versions/72/install.sh index b61f972fa..dbddba573 100755 --- a/plugins/php/versions/72/install.sh +++ b/plugins/php/versions/72/install.sh @@ -9,8 +9,6 @@ serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl - version=7.2.31 PHP_VER=72 @@ -60,14 +58,12 @@ if [ ! -d $sourcePath/php/php${PHP_VER} ];then fi OPTIONS='--without-iconv' -if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-curl" +if [ $sysName == 'Darwin' ]; then + OPTIONS="${OPTIONS} --with-curl=$(brew --prefix curl)" + OPTIONS="${OPTIONS} --with-pcre-dir=$(brew --prefix pcre2)" else - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-zlib-dir=$serverPath/lib/zlib" OPTIONS="${OPTIONS} --with-readline" fi - IS_64BIT=`getconf LONG_BIT` if [ "$IS_64BIT" == "64" ];then OPTIONS="${OPTIONS} --with-libdir=lib64" @@ -82,7 +78,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" @@ -113,7 +109,6 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then --enable-mysqlnd \ --with-mysqli=mysqlnd \ --with-pdo-mysql=mysqlnd \ - --with-libzip \ --enable-mbstring \ --enable-simplexml \ --enable-sockets \ diff --git a/plugins/php/versions/73/install.sh b/plugins/php/versions/73/install.sh index 71834229e..eef39686d 100755 --- a/plugins/php/versions/73/install.sh +++ b/plugins/php/versions/73/install.sh @@ -9,12 +9,6 @@ serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` SYS_ARCH=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl - -function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } -function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } -function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; } -function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; } version=7.3.33 PHP_VER=73 @@ -63,22 +57,19 @@ if [ ! -d $sourcePath/php/php${PHP_VER} ];then mv $sourcePath/php/php-${version} $sourcePath/php/php${PHP_VER} fi -ZIP_OPTION='--enable-zip' -libzip_version=`pkg-config libzip --modversion` -if [ "$?" != "0" ] || version_lt "$libzip_version" "0.11.0" ;then - cd ${rootPath}/plugins/php/lib && /bin/bash libzip.sh - export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig - ZIP_OPTION="--with-libzip=$serverPath/lib/libzip" -fi +# ZIP_OPTION='--enable-zip' +# libzip_version=`pkg-config libzip --modversion` +# if [ "$?" != "0" ] || version_lt "$libzip_version" "0.11.0" ;then +# cd ${rootPath}/plugins/php/lib && /bin/bash libzip.sh +# export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig +# ZIP_OPTION="--with-libzip=$serverPath/lib/libzip" +# fi OPTIONS='--without-iconv' if [ $sysName == 'Darwin' ]; then OPTIONS="${OPTIONS} --with-curl=$(brew --prefix curl)" OPTIONS="${OPTIONS} --with-pcre-dir=$(brew --prefix pcre2)" else - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-zlib-dir=$serverPath/lib/zlib" - OPTIONS="${OPTIONS} ${ZIP_OPTION}" OPTIONS="${OPTIONS} --with-readline" fi @@ -98,7 +89,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" @@ -115,8 +106,8 @@ fi # ----- cpu end ------ if [ ! -d $serverPath/php/${PHP_VER} ];then - cd $sourcePath/php/php${PHP_VER} && ./configure \ - --prefix=$serverPath/php/${PHP_VER} \ + cd $sourcePath/php/php${PHP_VER} + ./configure --prefix=$serverPath/php/${PHP_VER} \ --exec-prefix=$serverPath/php/${PHP_VER} \ --with-config-file-path=$serverPath/php/${PHP_VER}/etc \ --enable-mysqlnd \ diff --git a/plugins/php/versions/74/install.sh b/plugins/php/versions/74/install.sh index 8d77742b7..2520d135f 100755 --- a/plugins/php/versions/74/install.sh +++ b/plugins/php/versions/74/install.sh @@ -8,12 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - -function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } -function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } -function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; } -function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; } version=7.4.26 PHP_VER=74 @@ -80,37 +74,15 @@ fi cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' -if [ $sysName == 'Darwin' ]; then - - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)" - - # BREW_DIR=`which brew` - # BREW_DIR=${BREW_DIR/\/bin\/brew/} - # XML_LIB_DEPEND_DIR=`brew info libxml2 | grep /opt/homebrew/Cellar/libxml2 | cut -d \ -f 1 | awk 'END {print}'` - # XML_LIB_DEPEND_DIR=`brew info libxml2 | grep ${BREW_DIR}/Cellar/libxml2 | cut -d \ -f 1 | awk 'END {print}'` - # OPTIONS="${OPTIONS} --with-libxml=${XML_LIB_DEPEND_DIR}" - -else - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-readline" -fi +# if [ $sysName == 'Darwin' ]; then +# OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)" +# fi IS_64BIT=`getconf LONG_BIT` if [ "$IS_64BIT" == "64" ];then OPTIONS="${OPTIONS} --with-libdir=lib64" fi -echo "$sourcePath/php/php${PHP_VER}" - -ZIP_OPTION='--with-zip' -libzip_version=`pkg-config libzip --modversion` -if version_lt "$libzip_version" "0.11.0" ;then - cd ${rootPath}/plugins/php/lib && /bin/bash libzip.sh - export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig - ZIP_OPTION="--with-zip=$serverPath/lib/libzip" -fi - # ----- cpu start ------ if [ -z "${cpuCore}" ]; then cpuCore="1" @@ -120,7 +92,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" @@ -135,9 +107,9 @@ else cpuCore="1" fi # ----- cpu end ------ + if [ ! -d $serverPath/php/${PHP_VER} ];then cd $sourcePath/php/php${PHP_VER} && make clean - # ./buildconf --force ./configure \ --prefix=$serverPath/php/${PHP_VER} \ --exec-prefix=$serverPath/php/${PHP_VER} \ @@ -145,8 +117,6 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then --enable-mysqlnd \ --with-mysqli=mysqlnd \ --with-pdo-mysql=mysqlnd \ - --with-zlib-dir=$serverPath/lib/zlib \ - $ZIP_OPTION \ --enable-ftp \ --enable-mbstring \ --enable-sockets \ diff --git a/plugins/php/versions/80/install.sh b/plugins/php/versions/80/install.sh index dd67abcf4..9550c8bfa 100755 --- a/plugins/php/versions/80/install.sh +++ b/plugins/php/versions/80/install.sh @@ -8,20 +8,13 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - -function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } -function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } -function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; } -function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; } - version=8.0.30 PHP_VER=80 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -72,13 +65,9 @@ fi cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' + if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-curl" OPTIONS="${OPTIONS} --with-external-pcre=$(brew --prefix pcre2)" -else - # OPTIONS="--with-iconv=${serverPath}/lib/libiconv" - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-readline" fi IS_64BIT=`getconf LONG_BIT` @@ -92,16 +81,6 @@ fi # OPTIONS="${OPTIONS} --with-password-argon2" # fi - -ZIP_OPTION='--with-zip' -libzip_version=`pkg-config libzip --modversion` -if version_lt "$libzip_version" "0.11.0" ;then - cd ${rootPath}/plugins/php/lib && /bin/bash libzip.sh - export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig - ZIP_OPTION="--with-zip=$serverPath/lib/libzip" -fi - - # ----- cpu start ------ if [ -z "${cpuCore}" ]; then cpuCore="1" @@ -111,7 +90,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" @@ -127,7 +106,7 @@ else fi # ----- cpu end ------ -echo "$sourcePath/php/php${PHP_VER}" +# echo "$sourcePath/php/php${PHP_VER}" if [ ! -d $serverPath/php/${PHP_VER} ];then cd $sourcePath/php/php${PHP_VER} @@ -139,8 +118,6 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then --enable-mysqlnd \ --with-mysqli=mysqlnd \ --with-pdo-mysql=mysqlnd \ - --with-zlib-dir=$serverPath/lib/zlib \ - $ZIP_OPTION \ --enable-ftp \ --enable-mbstring \ --enable-sockets \ @@ -165,7 +142,7 @@ Uninstall_php() { $serverPath/php/init.d/php${PHP_VER} stop rm -rf $serverPath/php/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php/versions/81/install.sh b/plugins/php/versions/81/install.sh index e03bc9824..72714ac12 100755 --- a/plugins/php/versions/81/install.sh +++ b/plugins/php/versions/81/install.sh @@ -8,20 +8,13 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl -function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } -function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } -function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; } -function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; } - - -version=8.1.30 +version=8.1.31 PHP_VER=81 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -56,7 +49,7 @@ if [ ! -d $sourcePath/php/php${PHP_VER} ];then fi #检测文件是否损坏. - md5_file_ok=f24a6007f0b25a53cb7fbaee69c85017e0345b62089c2425a0afb7e177192ed1 + md5_file_ok=c4f244d46ba51c72f7d13d4f66ce6a9e9a8d6b669c51be35e01765ba58e7afca if [ -f $sourcePath/php/php-${version}.tar.xz ];then md5_file=`sha256sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'` if [ "${md5_file}" != "${md5_file_ok}" ]; then @@ -72,26 +65,15 @@ fi cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' -if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-curl" -else - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-readline" -fi +# if [ $sysName == 'Darwin' ]; then +# OPTIONS="${OPTIONS} --with-curl" +# fi argon_version=`pkg-config libargon2 --modversion` if [ "$?" == "0" ];then OPTIONS="${OPTIONS} --with-password-argon2" fi -ZIP_OPTION='--with-zip' -libzip_version=`pkg-config libzip --modversion` -if version_lt "$libzip_version" "0.11.0" ;then - cd ${rootPath}/plugins/php/lib && /bin/bash libzip.sh - export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig - ZIP_OPTION="--with-zip=$serverPath/lib/libzip" -fi - # ----- cpu start ------ if [ -z "${cpuCore}" ]; then cpuCore="1" @@ -101,7 +83,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" @@ -121,7 +103,6 @@ echo "$sourcePath/php/php${PHP_VER}" if [ ! -d $serverPath/php/${PHP_VER} ];then cd $sourcePath/php/php${PHP_VER} - ./buildconf --force ./configure \ --prefix=$serverPath/php/${PHP_VER} \ --exec-prefix=$serverPath/php/${PHP_VER} \ @@ -129,8 +110,6 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then --enable-mysqlnd \ --with-mysqli=mysqlnd \ --with-pdo-mysql=mysqlnd \ - --with-zlib-dir=$serverPath/lib/zlib \ - $ZIP_OPTION \ --enable-ftp \ --enable-mbstring \ --enable-sockets \ @@ -155,7 +134,7 @@ Uninstall_php() { $serverPath/php/init.d/php${PHP_VER} stop rm -rf $serverPath/php/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php/versions/82/install.sh b/plugins/php/versions/82/install.sh index a4cb42167..6156f8411 100755 --- a/plugins/php/versions/82/install.sh +++ b/plugins/php/versions/82/install.sh @@ -8,20 +8,13 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl -function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } -function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } -function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; } -function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; } - - -version=8.2.24 +version=8.2.26 PHP_VER=82 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -57,7 +50,7 @@ if [ ! -d $sourcePath/php/php${PHP_VER} ];then fi #检测文件是否损坏. - md5_file_ok=80a5225746a9eb484475b312d4c626c63a88a037d8e56d214f30205e1ba1411a + md5_file_ok=54747400cb4874288ad41a785e6147e2ff546cceeeb55c23c00c771ac125c6ef if [ -f $sourcePath/php/php-${version}.tar.xz ];then md5_file=`sha256sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'` if [ "${md5_file}" != "${md5_file_ok}" ]; then @@ -73,12 +66,6 @@ fi cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' -if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-curl" -else - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-readline" -fi IS_64BIT=`getconf LONG_BIT` if [ "$IS_64BIT" == "64" ];then @@ -99,7 +86,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" @@ -115,20 +102,9 @@ else fi # ----- cpu end ------ -ZIP_OPTION='--with-zip' -libzip_version=`pkg-config libzip --modversion` -if version_lt "$libzip_version" "0.11.0" ;then - cd ${rootPath}/plugins/php/lib && /bin/bash libzip.sh - export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig - ZIP_OPTION="--with-zip=$serverPath/lib/libzip" -fi - - -echo "$sourcePath/php/php${PHP_VER}" - +# echo "$sourcePath/php/php${PHP_VER}" if [ ! -d $serverPath/php/${PHP_VER} ];then cd $sourcePath/php/php${PHP_VER} - ./buildconf --force ./configure \ --prefix=$serverPath/php/${PHP_VER} \ --exec-prefix=$serverPath/php/${PHP_VER} \ @@ -136,8 +112,6 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then --enable-mysqlnd \ --with-mysqli=mysqlnd \ --with-pdo-mysql=mysqlnd \ - --with-zlib-dir=$serverPath/lib/zlib \ - $ZIP_OPTION \ --enable-mbstring \ --enable-ftp \ --enable-sockets \ @@ -162,7 +136,7 @@ Uninstall_php() { $serverPath/php/init.d/php${PHP_VER} stop rm -rf $serverPath/php/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php/versions/83/install.sh b/plugins/php/versions/83/install.sh index 415ebf780..aa072b049 100755 --- a/plugins/php/versions/83/install.sh +++ b/plugins/php/versions/83/install.sh @@ -8,19 +8,13 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl -function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } -function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } -function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; } -function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; } - -version=8.3.12 +version=8.3.14 PHP_VER=83 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -56,7 +50,7 @@ if [ ! -d $sourcePath/php/php${PHP_VER} ];then fi #检测文件是否损坏. - md5_file_ok=f774e28633e26fc8c5197f4dae58ec9e3ff87d1b4311cbc61ab05a7ad24bd131 + md5_file_ok=58b4cb9019bf70c0cbcdb814c7df79b9065059d14cf7dbf48d971f8e56ae9be7 if [ -f $sourcePath/php/php-${version}.tar.xz ];then md5_file=`sha256sum $sourcePath/php/php-${version}.tar.xz | awk '{print $1}'` if [ "${md5_file}" != "${md5_file_ok}" ]; then @@ -72,12 +66,6 @@ fi cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' -if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-curl" -else - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-readline" -fi argon_version=`pkg-config libargon2 --modversion` if [ "$?" == "0" ];then @@ -98,7 +86,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" @@ -114,20 +102,10 @@ else fi # ----- cpu end ------ -ZIP_OPTION='--with-zip' -libzip_version=`pkg-config libzip --modversion` -if version_lt "$libzip_version" "0.11.0" ;then - cd ${rootPath}/plugins/php/lib && /bin/bash libzip.sh - export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig - ZIP_OPTION="--with-zip=$serverPath/lib/libzip" -fi - - echo "$sourcePath/php/php${PHP_VER}" if [ ! -d $serverPath/php/${PHP_VER} ];then cd $sourcePath/php/php${PHP_VER} - ./buildconf --force ./configure \ --prefix=$serverPath/php/${PHP_VER} \ --exec-prefix=$serverPath/php/${PHP_VER} \ @@ -135,8 +113,6 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then --enable-mysqlnd \ --with-mysqli=mysqlnd \ --with-pdo-mysql=mysqlnd \ - --with-zlib-dir=$serverPath/lib/zlib \ - $ZIP_OPTION \ --enable-mbstring \ --enable-ftp \ --enable-sockets \ @@ -161,7 +137,7 @@ Uninstall_php() { $serverPath/php/init.d/php${PHP_VER} stop rm -rf $serverPath/php/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php/versions/84/install.sh b/plugins/php/versions/84/install.sh index d34ec9f11..67a481fb2 100755 --- a/plugins/php/versions/84/install.sh +++ b/plugins/php/versions/84/install.sh @@ -8,19 +8,13 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl -function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } -function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } -function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; } -function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; } - -version=8.4.0RC2 +version=8.4.1 PHP_VER=84 Install_php() { #------------------------ install start ------------------------------------# -echo "安装php-${version} ..." > $install_tmp +echo "安装php-${version} ..." mkdir -p $sourcePath/php mkdir -p $serverPath/php @@ -37,8 +31,7 @@ if [ ! -d $sourcePath/php/php${PHP_VER} ];then if [ ! -f $sourcePath/php/php-${version}.tar.xz ];then - # wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://www.php.net/distributions/php-${version}.tar.xz - wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://downloads.php.net/~calvinb/php-${version}.tar.xz + wget --no-check-certificate -O $sourcePath/php/php-${version}.tar.xz https://www.php.net/distributions/php-${version}.tar.xz fi #检测文件是否损坏. @@ -58,12 +51,10 @@ fi cd $sourcePath/php/php${PHP_VER} OPTIONS='--without-iconv' -if [ $sysName == 'Darwin' ]; then - OPTIONS="${OPTIONS} --with-curl" -else - OPTIONS="${OPTIONS} --with-curl" - OPTIONS="${OPTIONS} --with-readline" -fi + +# if [ $sysName == 'Darwin' ]; then +# OPTIONS="${OPTIONS} --with-curl" +# fi argon_version=`pkg-config libargon2 --modversion` if [ "$?" == "0" ];then @@ -84,7 +75,7 @@ if [ -f /proc/cpuinfo ];then cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` fi -MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') +MEM_INFO=$(which free > /dev/null && free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then if [ "${cpuCore}" -gt "${MEM_INFO}" ];then cpuCore="${MEM_INFO}" @@ -100,20 +91,14 @@ else fi # ----- cpu end ------ -ZIP_OPTION='--with-zip' -libzip_version=`pkg-config libzip --modversion` -if version_lt "$libzip_version" "0.11.0" ;then - cd ${rootPath}/plugins/php/lib && /bin/bash libzip.sh - export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig - ZIP_OPTION="--with-zip=$serverPath/lib/libzip" -fi - +# OPTIONS="${OPTIONS} --enable-debug" +# OPTIONS="${OPTIONS} --enable-dtrace" echo "$sourcePath/php/php${PHP_VER}" if [ ! -d $serverPath/php/${PHP_VER} ];then cd $sourcePath/php/php${PHP_VER} - ./buildconf --force + # ./buildconf --force ./configure \ --prefix=$serverPath/php/${PHP_VER} \ --exec-prefix=$serverPath/php/${PHP_VER} \ @@ -121,8 +106,6 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then --enable-mysqlnd \ --with-mysqli=mysqlnd \ --with-pdo-mysql=mysqlnd \ - --with-zlib-dir=$serverPath/lib/zlib \ - $ZIP_OPTION \ --enable-mbstring \ --enable-ftp \ --enable-sockets \ @@ -147,7 +130,7 @@ Uninstall_php() { $serverPath/php/init.d/php${PHP_VER} stop rm -rf $serverPath/php/${PHP_VER} - echo "卸载php-${version}..." > $install_tmp + echo "卸载php-${version}..." } action=${1} diff --git a/plugins/php/versions/all_mac.sh b/plugins/php/versions/all_mac.sh index a771bd087..0a72317db 100644 --- a/plugins/php/versions/all_mac.sh +++ b/plugins/php/versions/all_mac.sh @@ -30,7 +30,7 @@ ROOT_DIR=$(cd "$(dirname "$0")"; pwd) # echo "php${PHP_VER} -- end" -PHP_VER_LIST=(54 55 56 70 71 72 73 74 80 81 82 83) +PHP_VER_LIST=(54 55 56 70 71 72 73 74 80 81 82 83 84) # PHP_VER_LIST=(81) for PHP_VER in ${PHP_VER_LIST[@]}; do echo "php${PHP_VER} -- start" @@ -41,7 +41,7 @@ for PHP_VER in ${PHP_VER_LIST[@]}; do done cd $DIR -PHP_VER_LIST=(54 55 56 70 71 72 73 74 80 81 82) +PHP_VER_LIST=(54 55 56 70 71 72 73 74 80 81 82 83 84) # yar PHP_EXT_LIST=(ZendGuardLoader pdo mysqlnd sqlite3 openssl opcache mcrypt fileinfo \ exif gd intl pcntl memcache memcached redis imagemagick xdebug \ diff --git a/plugins/php/versions/all_test.sh b/plugins/php/versions/all_test.sh index 98623465a..259c86ee5 100644 --- a/plugins/php/versions/all_test.sh +++ b/plugins/php/versions/all_test.sh @@ -28,7 +28,7 @@ ROOT_DIR=$(cd "$(dirname "$0")"; pwd) # echo "php${PHP_VER} -- end" -PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82 83) +PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82 83 84) # PHP_VER_LIST=(81) for PHP_VER in ${PHP_VER_LIST[@]}; do echo "php${PHP_VER} -- start" @@ -39,7 +39,7 @@ for PHP_VER in ${PHP_VER_LIST[@]}; do done cd $DIR -PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82 83) +PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82 83 84) # yar PHP_EXT_LIST=(ZendGuardLoader pdo mysqlnd sqlite3 openssl opcache mcrypt fileinfo \ exif gd intl pcntl memcache memcached redis imagemagick xdebug \ diff --git a/plugins/php/versions/common/brotli.sh b/plugins/php/versions/common/brotli.sh new file mode 100755 index 000000000..87141bf8d --- /dev/null +++ b/plugins/php/versions/common/brotli.sh @@ -0,0 +1,114 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH=$PATH:/opt/homebrew/bin + +curPath=`pwd` + +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") +sourcePath=${serverPath}/source/php +SYS_ARCH=`arch` +LIBNAME=brotli +LIBV=0.15.2 +sysName=`uname` +actionType=$1 +version=$2 + +if [ "$version" -lt "70" ];then + echo "not need" + exit 1 +fi + +LIB_PATH_NAME=lib/php +if [ -d $serverPath/php/${version}/lib64 ];then + LIB_PATH_NAME=lib64 +fi + +NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts` +extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so + +if [ "$sysName" == "Darwin" ];then + BAK='_bak' +else + BAK='' +fi + + +Install_lib() +{ + isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"` + if [ "${isInstall}" != "" ];then + echo "php-$version 已安装${LIBNAME},请选择其它版本!" + return + fi + + if [ ! -f "$extFile" ];then + php_lib=$sourcePath/php_lib + mkdir -p $php_lib + if [ ! -d $php_lib/${LIBNAME}-${LIBV} ];then + if [ ! -f $php_lib/${LIBNAME}-${LIBV}.tgz ];then + wget --no-check-certificate -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/${LIBNAME}-${LIBV}.tgz + fi + cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz + fi + cd $php_lib/${LIBNAME}-${LIBV} + + OPTIONS='' + if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then + OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu" + fi + + $serverPath/php/$version/bin/phpize + ./configure --enable-brotli \ + --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS + make clean && make && make install && make clean + + cd $php_lib && rm -rf $php_lib/${LIBNAME}-${LIBV} + fi + + if [ ! -f "$extFile" ];then + echo "ERROR!" + return + fi + + echo "" >> $serverPath/php/$version/etc/php.ini + echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini + echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini + + cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart + echo '===========================================================' + echo 'successful!' +} + + +Uninstall_lib() +{ + if [ ! -f "$serverPath/php/$version/bin/php-config" ];then + echo "php-$version 未安装,请选择其它版本!" + return + fi + + if [ ! -f "$extFile" ];then + echo "php-$version 未安装${LIBNAME},请选择其它版本!" + echo "php-$version not install ${LIBNAME}, Plese select other version!" + return + fi + + sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini + sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini + + rm -f $extFile + cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart + echo '===============================================' + echo 'successful!' +} + + +if [ "$actionType" == 'install' ];then + Install_lib +elif [ "$actionType" == 'uninstall' ];then + Uninstall_lib +fi \ No newline at end of file diff --git a/plugins/php/versions/common/curl.sh b/plugins/php/versions/common/curl.sh new file mode 100755 index 000000000..609974209 --- /dev/null +++ b/plugins/php/versions/common/curl.sh @@ -0,0 +1,123 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH=$PATH:/opt/homebrew/bin + +curPath=`pwd` + +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") +sourcePath=${serverPath}/source/php +SYS_ARCH=`arch` + +actionType=$1 +version=$2 + + +LIBNAME=curl +LIBV=0 + +LIB_PATH_NAME=lib/php +if [ -d $serverPath/php/${version}/lib64 ];then + LIB_PATH_NAME=lib64 +fi + +NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts` +extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so + +sysName=`uname` +if [ "$sysName" == "Darwin" ];then + BAK='_bak' +else + BAK='' +fi + + +Install_lib() +{ + + isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"` + if [ "${isInstall}" != "" ];then + echo "php-$version 已安装${LIBNAME},请选择其它版本!" + return + fi + + if [ ! -f "$extFile" ];then + + if [ ! -d $sourcePath/php${version}/ext ];then + cd ${rootPath}/plugins/php && /bin/bash ${rootPath}/plugins/php/versions/${version}/install.sh install + fi + + cd $sourcePath/php${version}/ext/${LIBNAME} + if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then + OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu" + fi + + if [ "$sysName" == "Darwin" ];then + + BREW_DIR=`which brew` + BREW_DIR=${BREW_DIR/\/bin\/brew/} + LIB_DEPEND_DIR=`brew info curl | grep ${BREW_DIR}/Cellar/curl | cut -d \ -f 1 | awk 'END {print}'` + OPTIONS="$OPTIONS --with-curl=$(brew --prefix curl)" + export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$LIB_DEPEND_DIR/lib/pkgconfig + fi + + + $serverPath/php/$version/bin/phpize + echo "./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS" + ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS + + make clean && make && make install && make clean + + if [ -d $sourcePath/php${version} ];then + cd ${sourcePath} && rm -rf $sourcePath/php${version} + fi + fi + + if [ ! -f "$extFile" ];then + echo "ERROR!" + return + fi + + + echo "" >> $serverPath/php/$version/etc/php.ini + echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini + echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini + + cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart + echo '===========================================================' + echo 'successful!' +} + + +Uninstall_lib() +{ + if [ ! -f "$serverPath/php/$version/bin/php-config" ];then + echo "php-$version 未安装,请选择其它版本!" + return + fi + + if [ ! -f "$extFile" ];then + echo "php-$version 未安装${LIBNAME},请选择其它版本!" + return + fi + + echo $serverPath/php/$version/etc/php.ini + sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini + sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini + + rm -f $extFile + cd ${curPath} && bash ${rootPath}/plugins/php/versions/lib.sh $version restart + echo '===============================================' + echo 'successful!' +} + + + +if [ "$actionType" == 'install' ];then + Install_lib +elif [ "$actionType" == 'uninstall' ];then + Uninstall_lib +fi \ No newline at end of file diff --git a/plugins/php/versions/common/gd_old.sh b/plugins/php/versions/common/gd_old.sh index 6cc7ed365..7f4badb82 100755 --- a/plugins/php/versions/common/gd_old.sh +++ b/plugins/php/versions/common/gd_old.sh @@ -89,8 +89,8 @@ Install_lib() --with-png-dir \ --with-webp-dir \ --with-zlib-dir \ - --enable-gd-jis-conv - # --with-freetype-dir=${serverPath}/lib/freetype_old + --enable-gd-jis-conv \ + --with-freetype-dir=${serverPath}/lib/freetype_old # --enable-gd-native-ttf make clean && make && make install && make clean diff --git a/plugins/php/versions/common/igbinary.sh b/plugins/php/versions/common/igbinary.sh index 2ba49314d..5128c9e81 100755 --- a/plugins/php/versions/common/igbinary.sh +++ b/plugins/php/versions/common/igbinary.sh @@ -15,7 +15,7 @@ actionType=$1 version=$2 LIBNAME=igbinary -LIBV=3.2.7 +LIBV=3.2.16 if [ "$version" -lt "70" ];then echo "not need" diff --git a/plugins/php/versions/common/mcrypt.sh b/plugins/php/versions/common/mcrypt.sh index 19fb0aa87..697b69b99 100755 --- a/plugins/php/versions/common/mcrypt.sh +++ b/plugins/php/versions/common/mcrypt.sh @@ -14,7 +14,7 @@ actionType=$1 version=$2 LIBNAME=mcrypt -LIBV=1.0.6 +LIBV=1.0.7 if [ "$version" -lt "72" ];then echo "not need" @@ -23,6 +23,8 @@ fi if [ "$version" == "84" ];then LIBV=1.0.7 + echo "not need" + exit 1 fi @@ -70,7 +72,12 @@ Install_lib() fi if [ "$sysName" == "Darwin" ];then + + BREW_DIR=`which brew` + BREW_DIR=${BREW_DIR/\/bin\/brew/} + LIB_DEPEND_DIR=`brew info mcrypt | grep ${BREW_DIR}/Cellar/mcrypt | cut -d \ -f 1 | awk 'END {print}'` OPTIONS="$OPTIONS --with-mcrypt=$(brew --prefix mcrypt)" + export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$LIB_DEPEND_DIR/lib/pkgconfig fi $serverPath/php/$version/bin/phpize diff --git a/plugins/php/versions/common/mongodb.sh b/plugins/php/versions/common/mongodb.sh index 3f60b9f79..96228c86b 100755 --- a/plugins/php/versions/common/mongodb.sh +++ b/plugins/php/versions/common/mongodb.sh @@ -18,7 +18,7 @@ actionType=$1 version=$2 if [ "$version" -ge '74' ];then - LIBV=1.19.0 + LIBV=1.20.0 fi if [ "$version" == '71' ];then diff --git a/plugins/php/versions/common/openssl.sh b/plugins/php/versions/common/openssl.sh index c710de021..60f447db6 100755 --- a/plugins/php/versions/common/openssl.sh +++ b/plugins/php/versions/common/openssl.sh @@ -46,11 +46,15 @@ Install_lib() cd ${rootPath}/plugins/php/lib && /bin/bash openssl_10.sh fi + if [ "$version" -gt "82" ];then + cd ${rootPath}/plugins/php/lib && /bin/bash openssl.sh + fi + if [ "$sysName" == "Darwin" ] ;then BREW_DIR=`which brew` BREW_DIR=${BREW_DIR/\/bin\/brew/} - LIB_DEPEND_DIR=`brew info openssl@1.1 | grep ${BREW_DIR}/Cellar/openssl | cut -d \ -f 1 | awk 'END {print}'` + LIB_DEPEND_DIR=`brew info openssl@1.0 | grep ${BREW_DIR}/Cellar/openssl | cut -d \ -f 1 | awk 'END {print}'` export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig fi @@ -66,21 +70,54 @@ Install_lib() mv config0.m4 config.m4 fi - # openssl_version=`pkg-config openssl --modversion` - # export PKG_CONFIG_PATH=$serverPath/lib/openssl10/lib/pkgconfig - if [ "$version" -lt "81" ] && [ "$sysName" != "Darwin" ];then - export PKG_CONFIG_PATH=$serverPath/lib/openssl10/lib/pkgconfig - fi - OPTIONS="" if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu" fi + # openssl_version=`pkg-config openssl --modversion` + # export PKG_CONFIG_PATH=$serverPath/lib/openssl10/lib/pkgconfig + if [ "$version" -lt "81" ] && [ "$sysName" != "Darwin" ];then + export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$serverPath/lib/openssl10/lib/pkgconfig + fi + + # Darwin + # otool -L /Users/midoks/Desktop/mwdev/server/php/83/bin/php + # lldb /Users/midoks/Desktop/mwdev/server/php/83/bin/php -r 'phpinfo()' + # otool -L /Users/midoks/Desktop/mwdev/server/php/83/lib/php/extensions/no-debug-non-zts-20230831/openssl.so + # ldd /www/server/php/83/bin/php + + if [ "$version" -lt "84" ] && [ "$sysName" == "Darwin" ];then + BREW_DIR=`which brew` + BREW_DIR=${BREW_DIR/\/bin\/brew/} + LIB_DEPEND_DIR=`brew info openssl@1.0 | grep ${BREW_DIR}/Cellar/openssl@1.0 | cut -d \ -f 1 | awk 'END {print}'` + OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl@1.0)" + export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig + export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include" + export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz" + + echo "$LIB_DEPEND_DIR/lib/pkgconfig" + fi + + if [ "$version" -ge "84" ] && [ "$sysName" == "Darwin" ];then + BREW_DIR=`which brew` + BREW_DIR=${BREW_DIR/\/bin\/brew/} + LIB_DEPEND_DIR=`brew info openssl | grep ${BREW_DIR}/Cellar/openssl | cut -d \ -f 1 | awk 'END {print}'` + OPTIONS="$OPTIONS --with-openssl=$(brew --prefix openssl)" + export PKG_CONFIG_PATH=$LIB_DEPEND_DIR/lib/pkgconfig + export OPENSSL_CFLAGS="-I${LIB_DEPEND_DIR}/include" + export OPENSSL_LIBS="-L/${LIB_DEPEND_DIR}/lib -lssl -lcrypto -lz" + fi + + # if [ "$version" -gt "82" ] && [ "$sysName" == "Darwin" ];then + # export PKG_CONFIG_PATH=$serverPath/lib/openssl/lib/pkgconfig + # fi + + $serverPath/php/$version/bin/phpize - ./configure --with-php-config=$serverPath/php/$version/bin/php-config \ - $OPTIONS \ - --with-openssl + # --with-openssl + echo "./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS" + ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS make clean && make && make install && make clean if [ -d $sourcePath/php${version} ];then diff --git a/plugins/php/versions/common/readline.sh b/plugins/php/versions/common/readline.sh index bde833361..71c13ad56 100755 --- a/plugins/php/versions/common/readline.sh +++ b/plugins/php/versions/common/readline.sh @@ -2,7 +2,7 @@ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin export PATH=$PATH:/opt/homebrew/bin -# cd /www/server/mdserver-web/plugins/php/versions/common && bash readline.sh install 54 +# cd /www/server/mdserver-web/plugins/php/versions/common && bash readline.sh install 81 curPath=`pwd` @@ -21,7 +21,10 @@ version=$2 LIBNAME=readline LIBV=0 - +if [ "$version" -lt "74" ];then + echo "not need" + exit 0 +fi LIB_PATH_NAME=lib/php if [ -d $serverPath/php/${version}/lib64 ];then @@ -47,8 +50,6 @@ Install_lib() return fi - cd ${rootPath}/plugins/php/lib && /bin/bash libedit.sh - if [ ! -f "$extFile" ];then if [ ! -d $sourcePath/php${version}/ext ];then @@ -56,18 +57,23 @@ Install_lib() fi cd $sourcePath/php${version}/ext/${LIBNAME} - + if [ ! -f "config.m4" ];then + mv config0.m4 config.m4 + fi + OPTIONS="" if [ "${SYS_ARCH}" == "aarch64" ] && [ "$version" -lt "56" ];then OPTIONS="$OPTIONS --build=aarch64-unknown-linux-gnu --host=aarch64-unknown-linux-gnu" fi - OPTIONS="$OPTIONS --with-libedit=${serverPath}/lib/libedit" + cd ${rootPath}/plugins/php/lib && /bin/bash libedit.sh + export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${serverPath}/lib/libedit/lib/pkgconfig + OPTIONS="$OPTIONS --with-libedit=${serverPath}/lib/libedit" + cd $sourcePath/php${version}/ext/${LIBNAME} $serverPath/php/$version/bin/phpize ./configure --with-php-config=$serverPath/php/$version/bin/php-config $OPTIONS make clean && make && make install && make clean - fi if [ ! -f "$extFile" ];then diff --git a/plugins/php/versions/common/redis.sh b/plugins/php/versions/common/redis.sh index 8b4214744..0b94f387b 100755 --- a/plugins/php/versions/common/redis.sh +++ b/plugins/php/versions/common/redis.sh @@ -100,7 +100,7 @@ Uninstall_lib() if [ ! -f "$extFile" ];then echo "php-$version 未安装${LIBNAME},请选择其它版本!" - echo "php-$version not install memcache, Plese select other version!" + echo "php-$version not install ${LIBNAME}, Plese select other version!" return fi diff --git a/plugins/php/versions/common/solr.sh b/plugins/php/versions/common/solr.sh index fc0241365..6a08af64f 100755 --- a/plugins/php/versions/common/solr.sh +++ b/plugins/php/versions/common/solr.sh @@ -12,7 +12,7 @@ serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source/php SYS_ARCH=`arch` LIBNAME=solr -LIBV=2.6.0 +LIBV=2.7.0 sysName=`uname` actionType=$1 version=$2 diff --git a/plugins/php/versions/common/swoole.sh b/plugins/php/versions/common/swoole.sh index b0cb4bdfd..9e03436a1 100755 --- a/plugins/php/versions/common/swoole.sh +++ b/plugins/php/versions/common/swoole.sh @@ -15,7 +15,7 @@ actionType=$1 version=$2 LIBNAME=swoole -LIBV=4.8.10 +LIBV=5.1.6 if [ "$version" -lt "70" ];then LIBV=1.10.1 @@ -26,7 +26,7 @@ elif [ "$version" == "71" ];then elif [ "$version" == "72" ] || [ "$version" == "73" ] || [ "$version" == "74" ];then LIBV=4.8.10 elif [ "$version" -gt "74" ];then - LIBV=5.1.1 + LIBV=5.1.6 else echo 'other?' exit 0 diff --git a/plugins/php/versions/common/yaf.sh b/plugins/php/versions/common/yaf.sh index 5c1c2963f..5bb1af208 100755 --- a/plugins/php/versions/common/yaf.sh +++ b/plugins/php/versions/common/yaf.sh @@ -16,22 +16,13 @@ version=$2 sysName=`uname` LIBNAME=yaf -LIBV=3.3.5 +LIBV=3.3.6 -if [ "$version" -lt "70" ];then +if [ "$version" -lt "70" ]; then LIBV=2.3.5 -fi - -if [ "$version" -eq "70" ] || [ "$version" -eq "71" ];then +elif [ "$version" -lt "74" ]; then LIBV=3.2.5 -fi - -if [ "$version" -eq "72" ] || [ "$version" -eq "73" ];then - LIBV=3.2.5 -fi - - -if [ "$version" -gt "74" ];then +elif [ "$version" -lt "80" ]; then LIBV=3.3.5 fi diff --git a/plugins/php/versions/common/yaml.sh b/plugins/php/versions/common/yaml.sh index bd1347398..9c58704a5 100755 --- a/plugins/php/versions/common/yaml.sh +++ b/plugins/php/versions/common/yaml.sh @@ -16,7 +16,7 @@ version=$2 sysName=`uname` LIBNAME=yaml -LIBV=2.2.3 +LIBV=2.2.4 LIB_PATH_NAME=lib/php if [ -d $serverPath/php/${version}/lib64 ];then diff --git a/plugins/php/versions/common/zip.sh b/plugins/php/versions/common/zip.sh index ce0238147..1c4021502 100755 --- a/plugins/php/versions/common/zip.sh +++ b/plugins/php/versions/common/zip.sh @@ -2,8 +2,12 @@ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin export PATH=$PATH:/opt/homebrew/bin -curPath=`pwd` +function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } +function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; } +function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; } +function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; } +curPath=`pwd` appPath=$(dirname "$curPath") rootPath=$(dirname "$curPath") @@ -19,12 +23,6 @@ version=$2 LIBNAME=zip LIBV=0 - -if [ "$version" -lt "72" ];then - echo "not need" - exit 1 -fi - LIB_PATH_NAME=lib/php if [ -d $serverPath/php/${version}/lib64 ];then LIB_PATH_NAME=lib64 @@ -44,7 +42,15 @@ if [ ! -d $serverPath/lib/libzip ];then cd ${rootPath}/plugins/php/lib && /bin/bash libzip.sh fi -export PKG_CONFIG_PATH=/www/server/lib/libzip/lib/pkgconfig +export PKG_CONFIG_PATH=${serverPath}/lib/libzip/lib/pkgconfig + +# ZIP_OPTION='--with-zip' +# libzip_version=`pkg-config libzip --modversion` +# if version_lt "$libzip_version" "0.11.0" ;then +# cd ${rootPath}/plugins/php/lib && /bin/bash libzip.sh +# export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig +# ZIP_OPTION="--with-zip=$serverPath/lib/libzip" +# fi Install_lib() { @@ -65,8 +71,7 @@ Install_lib() cd $sourcePath/php${version}/ext/${LIBNAME} $serverPath/php/$version/bin/phpize - ./configure --with-php-config=$serverPath/php/$version/bin/php-config \ - --with-zip + ./configure --with-php-config=$serverPath/php/$version/bin/php-config --with-zip make clean && make && make install && make clean diff --git a/plugins/php/versions/common/zlib.sh b/plugins/php/versions/common/zlib.sh index 407923fcc..81291da52 100755 --- a/plugins/php/versions/common/zlib.sh +++ b/plugins/php/versions/common/zlib.sh @@ -34,6 +34,7 @@ else BAK='' fi +# --with-zlib-dir=$serverPath/lib/zlib Install_lib() { diff --git a/plugins/php/versions/phplib.conf b/plugins/php/versions/phplib.conf index ca5226d60..f81be4da5 100755 --- a/plugins/php/versions/phplib.conf +++ b/plugins/php/versions/phplib.conf @@ -85,6 +85,25 @@ "shell": "sodium.sh", "check": "sodium.so" }, + { + "name": "brotli", + "versions": [ + "70", + "71", + "72", + "73", + "74", + "80", + "81", + "82", + "83", + "84" + ], + "type": "压缩", + "msg": "压缩算法", + "shell": "brotli.sh", + "check": "brotli.so" + }, { "name": "gmp", "versions": [ @@ -160,8 +179,7 @@ "80", "81", "82", - "83", - "84" + "83" ], "type": "通用扩展", "msg": "加密软件!", @@ -371,6 +389,30 @@ "shell": "gd.sh", "check": "gd.so" }, + { + "name": "curl", + "versions": [ + "52", + "53", + "54", + "55", + "56", + "70", + "71", + "72", + "73", + "74", + "80", + "81", + "82", + "83", + "84" + ], + "type": "通用扩展", + "msg": "通用网络", + "shell": "curl.sh", + "check": "curl.so" + }, { "name": "intl", "versions": [ @@ -395,6 +437,21 @@ "shell": "intl.sh", "check": "intl.so" }, + { + "name": "readline", + "versions": [ + "74", + "80", + "81", + "82", + "83", + "84" + ], + "type": "通用扩展", + "msg": "命令行的支持", + "shell": "readline.sh", + "check": "readline.so" + }, { "name": "memcache", "versions": [ @@ -529,8 +586,7 @@ "80", "81", "82", - "83", - "84" + "83" ], "type": "调试器", "msg": "不多说,不了解的不要安装", @@ -553,8 +609,7 @@ "80", "81", "82", - "83", - "84" + "83" ], "type": "性能分析", "msg": "不多说,不了解的不要安装!", @@ -576,8 +631,7 @@ "80", "81", "82", - "83", - "84" + "83" ], "type": "通用扩展", "msg": "异步、并行、高性能网络通信引擎", @@ -623,8 +677,7 @@ "80", "81", "82", - "83", - "84" + "83" ], "type": "框架", "msg": "Yaf是一个C语言编写的PHP框架", @@ -641,8 +694,7 @@ "80", "81", "82", - "83", - "84" + "83" ], "type": "通用扩展", "msg": "YAML-1.1解析器和发射器", @@ -717,8 +769,7 @@ "80", "81", "82", - "83", - "84" + "83" ], "type": "大数据", "msg": "SOLR全文搜索服务", @@ -751,8 +802,21 @@ { "name": "zip", "versions": [ + "52", + "53", + "54", + "55", + "56", + "70", + "71", "72", - "83" + "73", + "74", + "80", + "81", + "82", + "83", + "84" ], "type": "压缩", "msg": "压缩组件", @@ -762,6 +826,15 @@ { "name": "zlib", "versions": [ + "52", + "53", + "54", + "55", + "56", + "70", + "71", + "72", + "73", "74", "80", "81", diff --git a/plugins/phpmyadmin/index.py b/plugins/phpmyadmin/index.py index eac12be55..123c99dab 100755 --- a/plugins/phpmyadmin/index.py +++ b/plugins/phpmyadmin/index.py @@ -7,9 +7,13 @@ import time import re import json -sys.path.append(os.getcwd() + "/class/core") -import mw -import site_api +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw +from utils.site import sites as MwSites app_debug = False if mw.isAppleSystem(): @@ -63,7 +67,7 @@ def getConfInc(): def getPort(): file = getConf() content = mw.readFile(file) - rep = 'listen\s*(.*);' + rep = r'listen\s*(.*);' tmp = re.search(rep, content) return tmp.groups()[0].strip() @@ -85,7 +89,7 @@ def getHomePage(): def getPhpVer(expect=55): - v = site_api.site_api().getPhpVersion() + v = MwSites.instance().getPhpVersion() is_find = False for i in range(len(v)): t = str(v[i]['version']) @@ -118,7 +122,7 @@ def contentReplace(content): blowfish_secret = tmp[0].strip() # print php_ver php_conf_dir = mw.getServerDir() + '/web_conf/php/conf' - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$PHP_CONF_PATH}', php_conf_dir) content = content.replace('{$PHP_VER}', php_ver) @@ -142,7 +146,7 @@ def contentReplace(content): content = content.replace('{$PMA_PATH}', cfg['path']) port = cfg["port"] - rep = 'listen\s*(.*);' + rep = r'listen\s*(.*);' content = re.sub(rep, "listen " + port + ';', content) return content @@ -192,8 +196,8 @@ def status(): def __release_port(port): from collections import namedtuple try: - import firewall_api - firewall_api.firewall_api().addAcceptPortArgs(port, 'phpMyAdmin默认端口', 'port') + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().addAcceptPort(port, 'phpMyAdmin默认端口', 'port') return port except Exception as e: return "Release failed {}".format(e) @@ -202,8 +206,8 @@ def __release_port(port): def __delete_port(port): from collections import namedtuple try: - import firewall_api - firewall_api.firewall_api().delAcceptPortArgs(port, 'tcp') + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().delAcceptPortCmd(port, 'tcp') return port except Exception as e: return "Release failed {}".format(e) diff --git a/plugins/phpmyadmin/install.sh b/plugins/phpmyadmin/install.sh index 43d28fdef..9dfb0b45a 100755 --- a/plugins/phpmyadmin/install.sh +++ b/plugins/phpmyadmin/install.sh @@ -7,9 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl - - if [ -f ${rootPath}/bin/activate ];then source ${rootPath}/bin/activate fi diff --git a/plugins/postgresql/index.py b/plugins/postgresql/index.py index c7217219d..6ce89e219 100755 --- a/plugins/postgresql/index.py +++ b/plugins/postgresql/index.py @@ -17,8 +17,12 @@ import json # ps -ef | grep -v grep| grep run_info | awk '{print $2}' | xargs kill -9 # vi /etc/sysconfig/network-scripts/ifcfg-eth0 -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw if mw.isAppleSystem(): @@ -143,7 +147,7 @@ def getInitdTpl(version=''): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$APP_PATH}', service_path + '/postgresql') return content @@ -717,8 +721,7 @@ def importDbBackup(): pwd = pSqliteDb('config').where('id=?', (1,)).getField('pg_root') - mysql_cmd = mw.getRootDir() + '/server/mysql/bin/mysql -uroot -p' + pwd + \ - ' ' + name + ' < ' + file_path_sql + mysql_cmd = mw.getFatherDir() + '/server/mysql/bin/mysql -uroot -p' + pwd + ' ' + name + ' < ' + file_path_sql # print(mysql_cmd) os.system(mysql_cmd) diff --git a/plugins/postgresql/install.sh b/plugins/postgresql/install.sh index 6e8af5591..8d9b534ca 100755 --- a/plugins/postgresql/install.sh +++ b/plugins/postgresql/install.sh @@ -9,8 +9,6 @@ serverPath=$(dirname "$rootPath") # cd /www/server/mdserver-web/plugins/postgresql && bash install.sh install 16 -install_tmp=${rootPath}/tmp/mw_install.pl - action=$1 type=$2 diff --git a/plugins/postgresql/versions/14/install.sh b/plugins/postgresql/versions/14/install.sh index 43ae5035b..f61f065e1 100755 --- a/plugins/postgresql/versions/14/install.sh +++ b/plugins/postgresql/versions/14/install.sh @@ -10,7 +10,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl postgreDir=${serverPath}/source/postgresql VERSION=14.12 @@ -20,7 +19,7 @@ VERSION=14.12 Install_App() { mkdir -p ${postgreDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if id postgres &> /dev/null ;then echo "postgres uid is `id -u postgres`" diff --git a/plugins/postgresql/versions/15/install.sh b/plugins/postgresql/versions/15/install.sh index a36706732..76acaff37 100755 --- a/plugins/postgresql/versions/15/install.sh +++ b/plugins/postgresql/versions/15/install.sh @@ -10,9 +10,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl postgreDir=${serverPath}/source/postgresql - VERSION=15.7 # su - postgres -c "/www/server/postgresql/bin/pg_ctl start -D /www/server/postgresql/data" @@ -20,7 +18,7 @@ VERSION=15.7 Install_App() { mkdir -p ${postgreDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if id postgres &> /dev/null ;then echo "postgres uid is `id -u postgres`" diff --git a/plugins/postgresql/versions/16/install.sh b/plugins/postgresql/versions/16/install.sh index 3392612ee..052e11873 100755 --- a/plugins/postgresql/versions/16/install.sh +++ b/plugins/postgresql/versions/16/install.sh @@ -10,7 +10,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl postgreDir=${serverPath}/source/postgresql VERSION=16.4 @@ -20,7 +19,7 @@ VERSION=16.4 Install_App() { mkdir -p ${postgreDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if id postgres &> /dev/null ;then echo "postgres uid is `id -u postgres`" diff --git a/plugins/postgresql/versions/17/install.sh b/plugins/postgresql/versions/17/install.sh index 3e2f53152..c103a3c06 100755 --- a/plugins/postgresql/versions/17/install.sh +++ b/plugins/postgresql/versions/17/install.sh @@ -10,17 +10,16 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl postgreDir=${serverPath}/source/postgresql -VERSION=17.0 +VERSION=17.2 # su - postgres -c "/www/server/postgresql/bin/pg_ctl start -D /www/server/postgresql/data" Install_App() { mkdir -p ${postgreDir} - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if id postgres &> /dev/null ;then echo "postgres uid is `id -u postgres`" diff --git a/plugins/pureftp/index.py b/plugins/pureftp/index.py index 6bb40ff74..0dc6a00e3 100755 --- a/plugins/pureftp/index.py +++ b/plugins/pureftp/index.py @@ -6,8 +6,12 @@ import os import time import shutil -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -76,7 +80,7 @@ def status(): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) return content @@ -84,8 +88,8 @@ def contentReplace(content): def ftp_release_port(port): from collections import namedtuple try: - import firewall_api - firewall_api.firewall_api().addAcceptPortArgs(port, 'pure-ftpd', 'port') + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().addAcceptPort(port, 'pure-ftpd', 'port') return port except Exception as e: return "Release failed {}".format(e) @@ -100,7 +104,7 @@ def openFtpPort(): def initDreplace(): file_tpl = getInitDTpl() - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getServerDir() initD_path = getServerDir() + '/init.d' if not os.path.exists(initD_path): @@ -140,10 +144,9 @@ def initDreplace(): systemServiceTpl = getPluginDir() + '/init.d/pureftp.service.tpl' if os.path.exists(systemDir): - # and not os.path.exists(systemService) - se_content = mw.readFile(systemServiceTpl) - se_content = se_content.replace('{$SERVER_PATH}', service_path) - mw.writeFile(systemService, se_content) + content = mw.readFile(systemServiceTpl) + content = content.replace('{$SERVER_PATH}', service_path) + mw.writeFile(systemService, content) mw.execShell('systemctl daemon-reload') return file_bin @@ -394,10 +397,9 @@ def modFtpPort(): return '端口范围不正确!' file = file = getServerDir() + '/etc/pure-ftpd.conf' conf = mw.readFile(file) - rep = u"\n#?\s*Bind\s+[0-9]+\.[0-9]+\.[0-9]+\.+[0-9]+,([0-9]+)" + rep = r"\n#?\s*Bind\s+[0-9]+\.[0-9]+\.[0-9]+\.+[0-9]+,([0-9]+)" # preg_match(rep,conf,tmp) - conf = re.sub( - rep, "\nBind 0.0.0.0," + port, conf) + conf = re.sub(rep, "\nBind 0.0.0.0," + port, conf) mw.writeFile(file, conf) restart() return 'ok' diff --git a/plugins/pureftp/install.sh b/plugins/pureftp/install.sh index 961fab27a..874c46668 100755 --- a/plugins/pureftp/install.sh +++ b/plugins/pureftp/install.sh @@ -7,8 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl - if [ -f ${rootPath}/bin/activate ];then source ${rootPath}/bin/activate @@ -103,12 +101,12 @@ Install_pureftp() if [ -d ${serverPath}/pureftp ];then echo "${1}" > ${serverPath}/pureftp/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' cd ${rootPath} && python3 ${rootPath}/plugins/pureftp/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/pureftp/index.py initd_install else - echo '安装失败' > $install_tmp + echo '安装失败' fi } @@ -128,7 +126,7 @@ Uninstall_pureftp() rm -rf ${serverPath}/pureftp userdel ftp groupdel ftp - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/redis/index.py b/plugins/redis/index.py index 74c117897..902006d5f 100755 --- a/plugins/redis/index.py +++ b/plugins/redis/index.py @@ -6,8 +6,12 @@ import os import time import re -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -118,7 +122,7 @@ def status(): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName()) content = content.replace('{$REDIS_PASS}', mw.getRandomString(10)) @@ -129,7 +133,7 @@ def contentReplace(content): def initDreplace(): file_tpl = getInitDTpl() - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getServerDir() initD_path = getServerDir() + '/init.d' if not os.path.exists(initD_path): @@ -146,6 +150,7 @@ def initDreplace(): # log dataLog = getServerDir() + '/data' if not os.path.exists(dataLog): + mw.execShell('mkdir -p ' + dataLog) mw.execShell('chmod +x ' + file_bin) # config replace @@ -154,8 +159,7 @@ def initDreplace(): if not os.path.exists(dst_conf_init): conf_content = mw.readFile(getConfTpl()) conf_content = conf_content.replace('{$SERVER_PATH}', service_path) - conf_content = conf_content.replace( - '{$REDIS_PASS}', mw.getRandomString(10)) + conf_content = conf_content.replace('{$REDIS_PASS}', mw.getRandomString(10)) mw.writeFile(dst_conf, conf_content) mw.writeFile(dst_conf_init, 'ok') @@ -166,9 +170,9 @@ def initDreplace(): if os.path.exists(systemDir) and not os.path.exists(systemService): systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl' service_path = mw.getServerDir() - se_content = mw.readFile(systemServiceTpl) - se_content = se_content.replace('{$SERVER_PATH}', service_path) - mw.writeFile(systemService, se_content) + content = mw.readFile(systemServiceTpl) + content = content.replace('{$SERVER_PATH}', service_path) + mw.writeFile(systemService, content) mw.execShell('systemctl daemon-reload') return file_bin @@ -220,7 +224,7 @@ def getPort(): conf = getServerDir() + '/redis.conf' content = mw.readFile(conf) - rep = r"^(" + r'port' + r')\s*([.0-9A-Za-z_& ~]+)' + rep = r"^(port)\s*([.0-9A-Za-z_& ~]+)" tmp = re.search(rep, content, re.M) if tmp: return tmp.groups()[1] @@ -232,7 +236,7 @@ def getRedisCmd(): requirepass = "" conf = getServerDir() + '/redis.conf' content = mw.readFile(conf) - rep = r"^(requirepass" + r')\s*([.0-9A-Za-z_& ~]+)' + rep = r"^(requirepass)\s*([.0-9A-Za-z_& ~]+)" tmp = re.search(rep, content, re.M) if tmp: requirepass = tmp.groups()[1] @@ -242,12 +246,10 @@ def getRedisCmd(): # findDebian = mw.execShell('cat /etc/issue |grep Debian') # if findDebian[0] != '': # default_ip = mw.getLocalIp() - cmd = getServerDir() + "/bin/redis-cli -h " + \ - default_ip + ' -p ' + port + " " + cmd = getServerDir() + "/bin/redis-cli -h " + default_ip + ' -p ' + port + " " if requirepass != "": - cmd = getServerDir() + '/bin/redis-cli -h ' + default_ip + \ - ' -p ' + port + ' -a "' + requirepass + '" ' + cmd = getServerDir() + '/bin/redis-cli -h ' + default_ip + ' -p ' + port + ' -a "' + requirepass + '" ' return cmd diff --git a/plugins/redis/info.json b/plugins/redis/info.json index 4793a12d7..9ede6b27c 100755 --- a/plugins/redis/info.json +++ b/plugins/redis/info.json @@ -4,8 +4,8 @@ "name": "redis", "title": "Redis", "shell": "install.sh", - "versions":["4.0.14","5.0.9","6.0.9","6.2.9","7.0.15","7.2.5"], - "updates":["4.0.14","5.0.9","6.0.9","6.2.9","7.0.15","7.2.5"], + "versions":["4.0.14","5.0.14","6.0.20","6.2.16","7.0.15","7.2.6", "7.4.1"], + "updates":["4.0.14","5.0.14","6.0.20","6.2.16","7.0.15","7.2.6", "7.4.1"], "tip": "soft", "checks": "server/redis", "path": "server/redis", diff --git a/plugins/redis/install.sh b/plugins/redis/install.sh index 444726156..0ec2ecd72 100755 --- a/plugins/redis/install.sh +++ b/plugins/redis/install.sh @@ -18,12 +18,11 @@ serverPath=$(dirname "$rootPath") # /Users/midoks/Desktop/mwdev/server/redis/bin/redis-cli -h 127.0.0.1 -p 6399 # /www/server/redis/bin/redis-cli -h 127.0.0.1 -p 6399 -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/source/redis diff --git a/plugins/rsyncd/index.py b/plugins/rsyncd/index.py index 708031edb..a35c14a6a 100755 --- a/plugins/rsyncd/index.py +++ b/plugins/rsyncd/index.py @@ -7,8 +7,13 @@ import json import re import sys -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw + app_debug = False if mw.isAppleSystem(): @@ -114,9 +119,10 @@ def getLsyncdLog(): def __release_port(port): + from collections import namedtuple try: - import firewall_api - firewall_api.firewall_api().addAcceptPortArgs(port, 'RSYNC同步', 'port') + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().addAcceptPort(port, 'RSYNC同步', 'port') return port except Exception as e: return "Release failed {}".format(e) @@ -341,15 +347,15 @@ def getRecListData(): n = i + 1 reg = '' if n == flist_len: - reg = '\\[' + flist[i] + '\\](.*)\\[?' + reg = r'\[' + flist[i] + r'\](.*)\[?' else: - reg = '\\[' + flist[i] + '\\](.*)\\[' + flist[n] + '\\]' + reg = r'\[' + flist[i] + r'\](.*)\[' + flist[n] + r'\]' t1 = re.search(reg, content, re.S) if t1: args = t1.groups()[0] # print('args start', args, 'args_end') - t2 = re.findall('\\s*(.*)\\s*\\=\\s*?(.*)?', args, re.M | re.I) + t2 = re.findall(r'\s*(.*)\s*\=\s*?(.*)?', args, re.M | re.I) for i in range(len(t2)): tmp[t2[i][0].strip()] = t2[i][1].strip() ret_list.append(tmp) @@ -456,13 +462,12 @@ def delRecBy(name): next_name = reclist[x + 1]['name'] reg = '' if is_end: - reg = '\\[' + name + '\\]\\s*(.*)' + reg = r'\[' + name + r'\]\s*(.*)' else: - reg = '\\[' + name + '\\]\\s*(.*)\\s*\\[' + next_name + '\\]' + reg = r'\\[' + name + r'\]\s*(.*)\s*\[' + next_name + r'\]' conre = re.search(reg, content, re.S) - content = content.replace( - "[" + name + "]\n" + conre.groups()[0], '') + content = content.replace("[" + name + "]\n" + conre.groups()[0], '') mw.writeFile(path, content) except Exception as e: return False diff --git a/plugins/rsyncd/install.sh b/plugins/rsyncd/install.sh index d339a5866..a8a13d86a 100755 --- a/plugins/rsyncd/install.sh +++ b/plugins/rsyncd/install.sh @@ -28,11 +28,11 @@ else useradd -g www -s /bin/bash www fi -echo $OSNAME -install_tmp=${rootPath}/tmp/mw_install.pl +# echo $OSNAME + Install_rsyncd() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' if [ "$OSNAME" == "debian" ] || [ "$OSNAME" == "ubuntu" ];then @@ -55,7 +55,7 @@ Install_rsyncd() mkdir -p $serverPath/rsyncd/send echo '2.0' > $serverPath/rsyncd/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' cd ${rootPath} && python3 ${rootPath}/plugins/rsyncd/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/rsyncd/index.py initd_install } @@ -83,7 +83,7 @@ Uninstall_rsyncd() fi rm -rf $serverPath/rsyncd - echo "卸载完成" > $install_tmp + echo "卸载完成" } action=$1 diff --git a/plugins/rsyncd/tool_task.py b/plugins/rsyncd/tool_task.py index 0d332ea9b..e38a29626 100644 --- a/plugins/rsyncd/tool_task.py +++ b/plugins/rsyncd/tool_task.py @@ -6,8 +6,15 @@ import os import time import json -sys.path.append(os.getcwd() + "/class/core") -import mw + +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw +from utils.crontab import crontab as MwCrontab + app_debug = False @@ -33,10 +40,9 @@ def getTaskConf(): def getConfigData(): - try: - return json.loads(mw.readFile(getTaskConf())) - except: - pass + conf = getTaskConf() + if os.path.exists(conf): + return json.loads(mw.readFile(conf)) return [] @@ -63,13 +69,10 @@ def createBgTaskByName(name, args): return True if "task_id" in cfg.keys() and cfg["task_id"] > 0: - res = mw.M("crontab").field("id, name").where( - "id=?", (cfg["task_id"],)).find() + res = mw.M("crontab").field("id, name").where("id=?", (cfg["task_id"],)).find() if res and res["id"] == cfg["task_id"]: print("计划任务已经存在!") return True - import crontab_api - api = crontab_api.crontab_api() period = args['period'] _hour = '' @@ -108,10 +111,10 @@ logs_file=$plugin_path/send/${rname}/run.log 'stype': "toShell", 'sname': '', 'sbody': cmd, - 'urladdress': '', + 'url_address': '', } - task_id = api.add(params) + task_id = MwCrontab.instance().add(params) if task_id > 0: cfg["task_id"] = task_id cfg["name"] = name @@ -126,12 +129,9 @@ def removeBgTask(): for x in range(len(cfg_list)): cfg = cfg_list[x] if "task_id" in cfg.keys() and cfg["task_id"] > 0: - res = mw.M("crontab").field("id, name").where( - "id=?", (cfg["task_id"],)).find() + res = mw.M("crontab").field("id, name").where("id=?", (cfg["task_id"],)).find() if res and res["id"] == cfg["task_id"]: - import crontab_api - api = crontab_api.crontab_api() - data = api.delete(cfg["task_id"]) + data = MwCrontab.instance().delete(cfg["task_id"]) if data[0]: cfg["task_id"] = -1 cfg_list[x] = cfg diff --git a/plugins/simpleping/index.py b/plugins/simpleping/index.py index 6565cf2f4..6fc8e92fa 100755 --- a/plugins/simpleping/index.py +++ b/plugins/simpleping/index.py @@ -6,8 +6,12 @@ import os import time import re -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -131,7 +135,7 @@ def status(): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$SERVER_APP}', service_path + '/simpleping') return content @@ -141,7 +145,7 @@ def contentReplace(content): def initDreplace(): file_tpl = getInitDTpl() - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getServerDir() initD_path = getServerDir() + '/init.d' if not os.path.exists(initD_path): @@ -162,7 +166,6 @@ def initDreplace(): systemService = systemDir + '/' + getPluginName() + '.service' if os.path.exists(systemDir) and not os.path.exists(systemService): systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl' - service_path = mw.getServerDir() content = mw.readFile(systemServiceTpl) content = content.replace('{$SERVER_PATH}', service_path) mw.writeFile(systemService, content) diff --git a/plugins/simpleping/install.sh b/plugins/simpleping/install.sh index 647106bbd..7a39c14de 100755 --- a/plugins/simpleping/install.sh +++ b/plugins/simpleping/install.sh @@ -14,11 +14,9 @@ VERSION=$2 # 开启可以PING # sysctl -w net.ipv4.ping_group_range="0 2147483647" -install_tmp=${rootPath}/tmp/mw_install.pl - Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/source/simpleping diff --git a/plugins/sphinx/index.py b/plugins/sphinx/index.py index ade4fca3d..981a4d4db 100755 --- a/plugins/sphinx/index.py +++ b/plugins/sphinx/index.py @@ -8,8 +8,12 @@ import re import string import subprocess -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -94,7 +98,7 @@ def readConfigTpl(): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$SERVER_APP}', service_path + '/sphinx') return content @@ -125,7 +129,7 @@ def mkdirAll(): def initDreplace(): file_tpl = getInitDTpl() - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getServerDir() initD_path = getServerDir() + '/init.d' if not os.path.exists(initD_path): @@ -151,7 +155,6 @@ def initDreplace(): systemService = systemDir + '/sphinx.service' systemServiceTpl = getPluginDir() + '/init.d/sphinx.service.tpl' if os.path.exists(systemDir) and not os.path.exists(systemService): - service_path = mw.getServerDir() se_content = mw.readFile(systemServiceTpl) se_content = se_content.replace('{$SERVER_PATH}', service_path) mw.writeFile(systemService, se_content) diff --git a/plugins/sphinx/install.sh b/plugins/sphinx/install.sh index 17dc0f5f2..277029f12 100755 --- a/plugins/sphinx/install.sh +++ b/plugins/sphinx/install.sh @@ -9,7 +9,6 @@ serverPath=$(dirname "$rootPath") sysName=`uname` sysArch=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl if [ -f ${rootPath}/bin/activate ];then source ${rootPath}/bin/activate diff --git a/plugins/sphinx/tool_cron.py b/plugins/sphinx/tool_cron.py index 063573d37..84c6b11a6 100644 --- a/plugins/sphinx/tool_cron.py +++ b/plugins/sphinx/tool_cron.py @@ -6,8 +6,14 @@ import os import time import json -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw +from utils.crontab import crontab as MwCrontab + app_debug = False @@ -35,12 +41,10 @@ def getTaskDeltaConf(): conf = getServerDir() + "/cron_delta_config.json" return conf - def getConfigData(): - try: + conf = getTaskConf() + if os.path.exists(conf): return json.loads(mw.readFile(getTaskConf())) - except: - pass return { "task_id": -1, "period": "day-n", @@ -50,10 +54,9 @@ def getConfigData(): } def getConfigDeltaData(): - try: + conf = getTaskDeltaConf() + if os.path.exists(conf): return json.loads(mw.readFile(getTaskDeltaConf())) - except: - pass return { "task_id": -1, "period": "minute-n", @@ -80,15 +83,12 @@ def createBgTaskByName(name): return True if "task_id" in args and args["task_id"] > 0: - res = mw.M("crontab").field("id, name").where( - "id=?", (args["task_id"],)).find() + res = mw.M("crontab").field("id, name").where("id=?", (args["task_id"],)).find() if res and res["id"] == args["task_id"]: print("计划任务已经存在!") return True - import crontab_api - api = crontab_api.crontab_api() - mw_dir = mw.getRunDir() + mw_dir = mw.getPanelDir() cmd = ''' mw_dir=%s rname=%s @@ -115,10 +115,10 @@ logs_file=$plugin_path/${rname}.log 'stype': "toShell", 'sname': '', 'sbody': cmd, - 'urladdress': '', + 'url_address': '', } - task_id = api.add(params) + task_id = MwCrontab.instance().add(params) if task_id > 0: args["task_id"] = task_id args["name"] = name @@ -132,15 +132,12 @@ def createBgTaskDeltaByName(name): return True if "task_id" in args and args["task_id"] > 0: - res = mw.M("crontab").field("id, name").where( - "id=?", (args["task_id"],)).find() + res = mw.M("crontab").field("id, name").where("id=?", (args["task_id"],)).find() if res and res["id"] == args["task_id"]: print("计划任务已经存在!") return True - import crontab_api - api = crontab_api.crontab_api() - mw_dir = mw.getRunDir() + mw_dir = mw.getPanelDir() cmd = ''' mw_dir=%s rname=%s @@ -167,10 +164,10 @@ logs_file=$plugin_path/${rname}.log 'stype': "toShell", 'sname': '', 'sbody': cmd, - 'urladdress': '', + 'url_address': '', } - task_id = api.add(params) + task_id = MwCrontab.instance().add(params) if task_id > 0: args["task_id"] = task_id args["name"] = name @@ -183,9 +180,7 @@ def removeBgTask(): res = mw.M("crontab").field("id, name").where( "id=?", (cfg["task_id"],)).find() if res and res["id"] == cfg["task_id"]: - import crontab_api - api = crontab_api.crontab_api() - data = api.delete(cfg["task_id"]) + data = MwCrontab.instance().delete(cfg["task_id"]) if data[0]: cfg["task_id"] = -1 mw.writeFile(getTaskConf(), json.dumps(cfg)) @@ -198,9 +193,7 @@ def removeDeltaBgTask(): res = mw.M("crontab").field("id, name").where( "id=?", (cfg["task_id"],)).find() if res and res["id"] == cfg["task_id"]: - import crontab_api - api = crontab_api.crontab_api() - data = api.delete(cfg["task_id"]) + data = MwCrontab.instance().delete(cfg["task_id"]) if data[0]: cfg["task_id"] = -1 mw.writeFile(getTaskDeltaConf(), json.dumps(cfg)) diff --git a/plugins/supervisor/index.py b/plugins/supervisor/index.py index efc9c6293..485992fce 100755 --- a/plugins/supervisor/index.py +++ b/plugins/supervisor/index.py @@ -6,8 +6,12 @@ import os import time import re -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -85,7 +89,7 @@ def initDreplace(): systemService = systemDir + '/supervisor.service' systemServiceTpl = getPluginDir() + '/init.d/supervisor.service' - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getServerDir() if not os.path.exists(confD): os.mkdir(confD) @@ -103,7 +107,7 @@ def initDreplace(): mw.writeFile(conf, conf_content) if os.path.exists(systemDir) and not os.path.exists(systemService): - activate_file = mw.getRunDir() + '/bin/activate' + activate_file = mw.getPanelDir() + '/bin/activate' if os.path.exists(activate_file): supervisord_bin = mw.execShell( 'source ' + activate_file + '&& which supervisord')[0].strip() diff --git a/plugins/supervisor/install.sh b/plugins/supervisor/install.sh index eeafc2985..96ece61ac 100755 --- a/plugins/supervisor/install.sh +++ b/plugins/supervisor/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 # /www/server/mdserver-web/bin/supervisorctl -c /www/server/supervisor/supervisor.conf @@ -42,8 +41,8 @@ Install_app() echo "${VERSION}" > $serverPath/supervisor/version.pl - cd ${rootPath} && python3 ${rootPath}/plugins/supervisor/index.py start - cd ${rootPath} && python3 ${rootPath}/plugins/supervisor/index.py initd_install + cd ${rootPath} && python3 plugins/supervisor/index.py start + cd ${rootPath} && python3 plugins/supervisor/index.py initd_install echo '安装完成[supervisor]' } diff --git a/plugins/swap/index.py b/plugins/swap/index.py index dddc1aac8..db79fa596 100755 --- a/plugins/swap/index.py +++ b/plugins/swap/index.py @@ -5,8 +5,12 @@ import io import os import time -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -75,7 +79,7 @@ def getInitDTpl(): def initDreplace(): file_tpl = getInitDTpl() - service_path = os.path.dirname(os.getcwd()) + service_path = mw.getServerDir() initD_path = getServerDir() + '/init.d' if not os.path.exists(initD_path): @@ -97,12 +101,11 @@ def initDreplace(): if os.path.exists(systemDir) and not os.path.exists(systemService): swapon_bin = mw.execShell('which swapon')[0].strip() swapoff_bin = mw.execShell('which swapoff')[0].strip() - service_path = mw.getServerDir() - se_content = mw.readFile(systemServiceTpl) - se_content = se_content.replace('{$SERVER_PATH}', service_path) - se_content = se_content.replace('{$SWAPON_BIN}', swapon_bin) - se_content = se_content.replace('{$SWAPOFF_BIN}', swapoff_bin) - mw.writeFile(systemService, se_content) + content = mw.readFile(systemServiceTpl) + content = content.replace('{$SERVER_PATH}', service_path) + content = content.replace('{$SWAPON_BIN}', swapon_bin) + content = content.replace('{$SWAPOFF_BIN}', swapoff_bin) + mw.writeFile(systemService, content) mw.execShell('systemctl daemon-reload') return file_bin diff --git a/plugins/swap/install.sh b/plugins/swap/install.sh index 875345c3d..1f698fd13 100755 --- a/plugins/swap/install.sh +++ b/plugins/swap/install.sh @@ -8,8 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") SYSOS=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl - VERSION=$2 # cd /www/server/mdserver-web/plugins/swap && /bin/bash install.sh install 1.1 @@ -20,7 +18,7 @@ Install_swap() exit 0 fi - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/swap echo "${VERSION}" > $serverPath/swap/version.pl @@ -34,7 +32,7 @@ Install_swap() swapon $serverPath/swap/swapfile fi - echo '安装完成' > $install_tmp + echo '安装完成' cd ${rootPath} && python3 ${rootPath}/plugins/swap/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/swap/index.py initd_install @@ -59,7 +57,7 @@ Uninstall_swap() rm -rf $serverPath/swap - echo "Uninstall_swap" > $install_tmp + echo "Uninstall_swap" } action=$1 diff --git a/plugins/sys-opt/index.py b/plugins/sys-opt/index.py index 0b97057be..a9a66c583 100755 --- a/plugins/sys-opt/index.py +++ b/plugins/sys-opt/index.py @@ -7,8 +7,12 @@ import json import re import sys -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw def getPluginName(): @@ -48,7 +52,7 @@ def checkArgs(data, ck=[]): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$SERVER_APP}', service_path + '/sys-opt') return content diff --git a/plugins/sys-opt/install.sh b/plugins/sys-opt/install.sh index d6370cc71..03e7c82ff 100755 --- a/plugins/sys-opt/install.sh +++ b/plugins/sys-opt/install.sh @@ -8,23 +8,19 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") - -install_tmp=${rootPath}/tmp/mw_install.pl - - Install_sysopt() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/sys-opt echo '1.0' > $serverPath/sys-opt/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' } Uninstall_sysopt() { rm -rf $serverPath/sys-opt - echo "卸载完成" > $install_tmp + echo "卸载完成" } action=$1 diff --git a/plugins/system_safe/install.sh b/plugins/system_safe/install.sh index 7179c2499..d053a898b 100755 --- a/plugins/system_safe/install.sh +++ b/plugins/system_safe/install.sh @@ -7,17 +7,16 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl SYSOS=`uname` VERSION=$2 APP_NAME=system_safe Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/system_safe echo "$VERSION" > $serverPath/system_safe/version.pl - echo 'install complete' > $install_tmp + echo 'install complete' #初始化 cd ${serverPath}/mdserver-web && python3 plugins/system_safe/system_safe.py start $VERSION @@ -36,7 +35,7 @@ Uninstall_App() fi rm -rf $serverPath/system_safe - echo "uninstall completed" > $install_tmp + echo "uninstall completed" } action=$1 diff --git a/plugins/system_safe/system_safe.py b/plugins/system_safe/system_safe.py index b454a8685..62efb4e06 100755 --- a/plugins/system_safe/system_safe.py +++ b/plugins/system_safe/system_safe.py @@ -10,8 +10,13 @@ import psutil from datetime import datetime sys.dont_write_bytecode = True -sys.path.append(os.getcwd() + "/class/core") -import mw + +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -573,7 +578,7 @@ class App: # 转换时间格式 def __to_date(self, date_str): - tmp = re.split('\s+', date_str) + tmp = re.split(r'\s+', date_str) s_date = str(datetime.now().year) + '-' + \ self.__months.get(tmp[0]) + '-' + tmp[1] + ' ' + tmp[2] time_array = time.strptime(s_date, "%Y-%m-%d %H:%M:%S") diff --git a/plugins/tamper_proof_py/index.py b/plugins/tamper_proof_py/index.py index 446dee0c3..82c06070b 100755 --- a/plugins/tamper_proof_py/index.py +++ b/plugins/tamper_proof_py/index.py @@ -10,8 +10,12 @@ import psutil from datetime import datetime sys.dont_write_bytecode = True -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): diff --git a/plugins/tamper_proof_py/install.sh b/plugins/tamper_proof_py/install.sh index efa2d12f7..f3b963984 100755 --- a/plugins/tamper_proof_py/install.sh +++ b/plugins/tamper_proof_py/install.sh @@ -13,17 +13,16 @@ if [ -f ${rootPath}/bin/activate ];then fi pip install pyinotify -install_tmp=${rootPath}/tmp/mw_install.pl SYSOS=`uname` VERSION=$2 APP_NAME=tamper_proof_py Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/tamper_proof_py echo "$VERSION" > $serverPath/tamper_proof_py/version.pl - echo 'install complete' > $install_tmp + echo 'install complete' #初始化 cd ${serverPath}/mdserver-web && python3 plugins/tamper_proof_py/index.py start $VERSION @@ -41,7 +40,7 @@ Uninstall_App() fi rm -rf $serverPath/tamper_proof_py - echo "uninstall completed" > $install_tmp + echo "uninstall completed" } action=$1 diff --git a/plugins/tamper_proof_py/tamper_proof_service.py b/plugins/tamper_proof_py/tamper_proof_service.py index 61645db3a..8c020b836 100644 --- a/plugins/tamper_proof_py/tamper_proof_service.py +++ b/plugins/tamper_proof_py/tamper_proof_service.py @@ -12,8 +12,12 @@ import psutil import threading import datetime -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw class MyEventHandler(pyinotify.ProcessEvent): diff --git a/plugins/task_manager/index.py b/plugins/task_manager/index.py index f2f22f90e..90173fb97 100755 --- a/plugins/task_manager/index.py +++ b/plugins/task_manager/index.py @@ -6,8 +6,12 @@ import os import time import re -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): diff --git a/plugins/task_manager/install.sh b/plugins/task_manager/install.sh index 1884c74a8..896b1af89 100755 --- a/plugins/task_manager/install.sh +++ b/plugins/task_manager/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 # python3 plugins/task_manager/task_manager_index.py @@ -20,7 +19,7 @@ fi Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/task_manager if [ -f /usr/bin/apt ]; then diff --git a/plugins/task_manager/js/task_manager.js b/plugins/task_manager/js/task_manager.js index 6c3f7f36e..16eab7e58 100644 --- a/plugins/task_manager/js/task_manager.js +++ b/plugins/task_manager/js/task_manager.js @@ -506,25 +506,25 @@ function get_resource_list() { buildRealProcess() var tbody_tr = createProcessTable(true); var tbody = '\ - \ - 应用名称\ - PID\ - 线程\ - 用户\ - CPU\ - 内存\ - io读\ - io写\ - 上行\ - 下行\ - 连接\ - 状态\ - 操作\ - \ - \ - ' + tbody_tr + '' - $('#taskResourceTable').html(tbody); - $(".table-cont").css("height", "220px"); + \ + 应用名称\ + PID\ + 线程\ + 用户\ + CPU\ + 内存\ + io读\ + io写\ + 上行\ + 下行\ + 连接\ + 状态\ + 操作\ + \ + \ + ' + tbody_tr + ''; + $('#taskResourceTable').html(tbody); + $(".table-cont").css("height", "220px"); }) $("#load_average").html('') diff --git a/plugins/task_manager/task_manager_index.py b/plugins/task_manager/task_manager_index.py index 9790b19b9..22a44fb91 100755 --- a/plugins/task_manager/task_manager_index.py +++ b/plugins/task_manager/task_manager_index.py @@ -11,11 +11,12 @@ import json from typing import List, Dict -try: - sys.path.append(os.getcwd() + "/class/core") - import mw -except Exception as e: - import core.mw as mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False diff --git a/plugins/tgbot/index.py b/plugins/tgbot/index.py index 95c1cbf19..0be333a8e 100755 --- a/plugins/tgbot/index.py +++ b/plugins/tgbot/index.py @@ -8,8 +8,12 @@ import re import json import base64 -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): diff --git a/plugins/tgbot/install.sh b/plugins/tgbot/install.sh index cec3fab83..31d91f8e0 100755 --- a/plugins/tgbot/install.sh +++ b/plugins/tgbot/install.sh @@ -7,9 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") - -install_tmp=${rootPath}/tmp/mw_install.pl - VERSION=$2 # pip3 install ccxt @@ -22,7 +19,7 @@ pip3 install telebot Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/tgbot echo "${VERSION}" > $serverPath/tgbot/version.pl @@ -31,7 +28,7 @@ Install_App() cd ${rootPath} && python3 ${rootPath}/plugins/tgbot/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/tgbot/index.py initd_install - echo '安装完成' > $install_tmp + echo '安装完成' } Uninstall_App() @@ -48,7 +45,7 @@ Uninstall_App() fi rm -rf $serverPath/tgbot - echo "Uninstall_redis" > $install_tmp + echo "Uninstall_redis" } action=$1 diff --git a/plugins/tgbot/startup/extend/init_cmd.py b/plugins/tgbot/startup/extend/init_cmd.py index 75048aef1..1efb672d5 100644 --- a/plugins/tgbot/startup/extend/init_cmd.py +++ b/plugins/tgbot/startup/extend/init_cmd.py @@ -9,8 +9,12 @@ import json import base64 import threading -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw import telebot from telebot import types diff --git a/plugins/tgbot/startup/extend/push_ad.py b/plugins/tgbot/startup/extend/push_ad.py index 97a660cbf..b8b5839bc 100644 --- a/plugins/tgbot/startup/extend/push_ad.py +++ b/plugins/tgbot/startup/extend/push_ad.py @@ -9,8 +9,12 @@ import json import base64 import threading -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw import telebot from telebot import types @@ -41,12 +45,38 @@ def send_msg(bot, tag='ad', trigger_time=300): mw.writeFile(lock_file, json.dumps(lock_data)) # 信号只在一个周期内执行一次|end + # 跨链混币器Cce.Cash💰低手续费💰隔断溯源 | 10/m | next,6/10 | @hu ge + # TTKCDN 无视移动墙/GFW/SNI阻断 TG第一性价比 | 10/m | @ssdpflood # https://t.me/gjgzs2022 | 22/m | @GJ_gzs # https://zhaoziyuan1.cc | web | 15/m | 2m | next,12/15 | @baleite - # 综合包网/NG接口开户 | 28/m | 6m | next,10/28 | @aabbcx888 + # 综合包网/NG接口开户 | 28/m | 6m | next,4/28 | x # 实名认证/过人脸🕵️‍♀️各种账号处理✅ | 30/m| next,12/30 | @nngzs # 桃花资源采集| 13/m| next,1/13 | @xiaolizi1122 keyboard = [ + [ + types.InlineKeyboardButton( + text="TTKCDN 性价比最高的商业CDN", url='https://t.me/ssdpflood') + ], + [ + types.InlineKeyboardButton( + text="TTKCDN 过河南郑州/泉州等地区阻断", url='https://t.me/ssdpflood') + ], + [ + types.InlineKeyboardButton( + text="TTKCDN 私人定制 属于你自己的独立CDN", url='https://t.me/ssdpflood') + ], + [ + types.InlineKeyboardButton( + text="TTKCDN 全系主动防御 全球威胁情报网", url='https://t.me/ssdpflood') + ], + [ + types.InlineKeyboardButton( + text="跨链混币器Cce.Cash💰低手续费💰隔断溯源", url='https://cce.cash/#/main/home?MW') + ], + [ + types.InlineKeyboardButton( + text="TTKCDN 无视移动墙/GFW/SNI阻断 TG第一性价比", url='https://t.me/ssdpflood') + ], [ types.InlineKeyboardButton( text="桃花资源采集🚀 ", url='https://thzy.me') @@ -81,7 +111,7 @@ def send_msg(bot, tag='ad', trigger_time=300): types.InlineKeyboardButton( text="@ME", url='tg://user?id=5568699210'), types.InlineKeyboardButton( - text="100RMB/M", url='tg://user?id=5568699210') + text="100RMB/M[已满]", url='tg://user?id=5568699210') ] ] markup = types.InlineKeyboardMarkup(keyboard) diff --git a/plugins/tgbot/startup/extend/push_bbs_ntid.py b/plugins/tgbot/startup/extend/push_bbs_ntid.py index 4a49ba3ab..88411fe38 100644 --- a/plugins/tgbot/startup/extend/push_bbs_ntid.py +++ b/plugins/tgbot/startup/extend/push_bbs_ntid.py @@ -9,8 +9,12 @@ import json import base64 import threading -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw import telebot from telebot import types diff --git a/plugins/tgbot/startup/extend/push_notice_msg.py b/plugins/tgbot/startup/extend/push_notice_msg.py index 0fea5ff39..1937ee2ac 100644 --- a/plugins/tgbot/startup/extend/push_notice_msg.py +++ b/plugins/tgbot/startup/extend/push_notice_msg.py @@ -9,8 +9,12 @@ import json import base64 import threading -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw import telebot from telebot import types @@ -50,12 +54,38 @@ def send_msg(bot, tag='ad', trigger_time=300): mw.writeFile(lock_file, json.dumps(lock_data)) # 信号只在一个周期内执行一次|end + # 跨链混币器Cce.Cash💰低手续费💰隔断溯源 | 10/m | next,6/10 | @hu ge + # TTKCDN | 10/m | @ssdpflood | 5 # https://t.me/gjgzs2022 | 22/m | @GJ_gzs # https://zhaoziyuan1.cc | web | 15/m | 2m | next,12/15 | @baleite # 综合包网/NG接口开户 | 28/m | 6m | next,4/28 | x # 实名认证/过人脸🕵️‍♀️各种账号处理✅ | 30/m| next,12/30 | @nngzs # 桃花资源采集| 13/m| next,1/13 | @xiaolizi1122 keyboard = [ + [ + types.InlineKeyboardButton( + text="TTKCDN 性价比最高的商业CDN", url='https://t.me/ssdpflood') + ], + [ + types.InlineKeyboardButton( + text="TTKCDN 过河南郑州/泉州等地区阻断", url='https://t.me/ssdpflood') + ], + [ + types.InlineKeyboardButton( + text="TTKCDN 私人定制 属于你自己的独立CDN", url='https://t.me/ssdpflood') + ], + [ + types.InlineKeyboardButton( + text="TTKCDN 全系主动防御 全球威胁情报网", url='https://t.me/ssdpflood') + ], + [ + types.InlineKeyboardButton( + text="跨链混币器Cce.Cash💰低手续费💰隔断溯源", url='https://cce.cash/#/main/home?MW') + ], + [ + types.InlineKeyboardButton( + text="TTKCDN 无视移动墙/GFW/SNI阻断 TG第一性价比", url='https://t.me/ssdpflood') + ], [ types.InlineKeyboardButton( text="桃花资源采集🚀 ", url='https://thzy.me') @@ -90,7 +120,7 @@ def send_msg(bot, tag='ad', trigger_time=300): types.InlineKeyboardButton( text="@ME", url='tg://user?id=5568699210'), types.InlineKeyboardButton( - text="100RMB/M", url='tg://user?id=5568699210') + text="100RMB/M[已满]", url='tg://user?id=5568699210') ] ] markup = types.InlineKeyboardMarkup(keyboard) diff --git a/plugins/tgbot/startup/extend/receive_faq.py b/plugins/tgbot/startup/extend/receive_faq.py index 175090e17..cbb1a2760 100644 --- a/plugins/tgbot/startup/extend/receive_faq.py +++ b/plugins/tgbot/startup/extend/receive_faq.py @@ -7,8 +7,12 @@ import json import base64 import threading -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw import telebot from telebot import types diff --git a/plugins/tgbot/startup/extend/receive_music163_search.py b/plugins/tgbot/startup/extend/receive_music163_search.py index 8ba6b8000..f3a872021 100644 --- a/plugins/tgbot/startup/extend/receive_music163_search.py +++ b/plugins/tgbot/startup/extend/receive_music163_search.py @@ -9,6 +9,12 @@ import json import base64 import threading +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw import telebot from telebot import types diff --git a/plugins/tgbot/startup/tgbot.py b/plugins/tgbot/startup/tgbot.py index 75fc6d1ef..60b4a5546 100644 --- a/plugins/tgbot/startup/tgbot.py +++ b/plugins/tgbot/startup/tgbot.py @@ -9,8 +9,12 @@ import json import base64 import threading -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw import telebot diff --git a/plugins/tgbot/startup/tgpush.py b/plugins/tgbot/startup/tgpush.py index fcf34a811..57e2cd6f4 100644 --- a/plugins/tgbot/startup/tgpush.py +++ b/plugins/tgbot/startup/tgpush.py @@ -9,8 +9,12 @@ import json import base64 import threading -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw import telebot diff --git a/plugins/tgclient/index.py b/plugins/tgclient/index.py index a10030ab9..ec2eb7238 100755 --- a/plugins/tgclient/index.py +++ b/plugins/tgclient/index.py @@ -8,8 +8,12 @@ import re import json import base64 -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): diff --git a/plugins/tgclient/install.sh b/plugins/tgclient/install.sh index cf06a2d04..6757ba902 100755 --- a/plugins/tgclient/install.sh +++ b/plugins/tgclient/install.sh @@ -7,9 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") - -install_tmp=${rootPath}/tmp/mw_install.pl - VERSION=$2 # pip3 install ccxt @@ -21,7 +18,7 @@ pip3 install telethon Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source mkdir -p $serverPath/tgclient echo "${VERSION}" > $serverPath/tgclient/version.pl @@ -30,7 +27,7 @@ Install_App() cd ${rootPath} && python3 ${rootPath}/plugins/tgclient/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/tgclient/index.py initd_install - echo '安装完成' > $install_tmp + echo '安装完成' } Uninstall_App() @@ -47,7 +44,7 @@ Uninstall_App() fi rm -rf $serverPath/tgclient - echo "Uninstall_redis" > $install_tmp + echo "Uninstall_redis" } action=$1 diff --git a/plugins/varnish/index.py b/plugins/varnish/index.py index 9b761e997..5336dc01a 100755 --- a/plugins/varnish/index.py +++ b/plugins/varnish/index.py @@ -5,8 +5,12 @@ import io import os import time -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -46,7 +50,7 @@ def getInitDTpl(): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$SERVER_APP}', service_path + '/varnish') return content diff --git a/plugins/varnish/install.sh b/plugins/varnish/install.sh index d47114112..2ce3ad69f 100755 --- a/plugins/varnish/install.sh +++ b/plugins/varnish/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 @@ -18,7 +17,7 @@ OSNAME_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F Install_varnish() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/source if [ "${OSNAME}" == "macos" ]; then @@ -38,7 +37,7 @@ Install_varnish() mkdir -p $serverPath/varnish echo "1.0" > $serverPath/varnish/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' cd ${rootPath} && python3 ${rootPath}/plugins/varnish/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/varnish/index.py initd_install @@ -63,7 +62,7 @@ Uninstall_varnish() echo "I won't support it" fi rm -rf $serverPath/varnish - echo "uninstall varnish" > $install_tmp + echo "uninstall varnish" } action=$1 diff --git a/plugins/webhook/index.py b/plugins/webhook/index.py index 4fb14d030..15189f727 100755 --- a/plugins/webhook/index.py +++ b/plugins/webhook/index.py @@ -7,8 +7,12 @@ import time import re import json -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): diff --git a/plugins/webhook/install.sh b/plugins/webhook/install.sh index e3018b0a9..6743e6162 100755 --- a/plugins/webhook/install.sh +++ b/plugins/webhook/install.sh @@ -7,22 +7,20 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl - VERSION=1.0 Install_App() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/webhook echo "${VERSION}" > $serverPath/webhook/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' } Uninstall_App() { rm -rf $serverPath/webhook - echo "Uninstall_App" > $install_tmp + echo "Uninstall_App" } action=$1 diff --git a/plugins/webhook/webhook_index.py b/plugins/webhook/webhook_index.py index 5b2777da6..dc60912d3 100755 --- a/plugins/webhook/webhook_index.py +++ b/plugins/webhook/webhook_index.py @@ -7,8 +7,12 @@ import time import re import json -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): diff --git a/plugins/webssh/index.py b/plugins/webssh/index.py index ad624bc32..4aacad870 100755 --- a/plugins/webssh/index.py +++ b/plugins/webssh/index.py @@ -7,8 +7,12 @@ import json import re import sys -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw class App(): diff --git a/plugins/webssh/install.sh b/plugins/webssh/install.sh index 6c7ecedec..31782e34c 100755 --- a/plugins/webssh/install.sh +++ b/plugins/webssh/install.sh @@ -8,24 +8,21 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") - -install_tmp=${rootPath}/tmp/mw_install.pl - VERSION=$2 Install_webssh() { - echo '正在安装脚本文件...' > $install_tmp + echo '正在安装脚本文件...' mkdir -p $serverPath/webssh echo "${VERSION}" > $serverPath/webssh/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' } Uninstall_webssh() { rm -rf $serverPath/webssh - echo "卸载完成" > $install_tmp + echo "卸载完成" } action=$1 diff --git a/plugins/webstats/index.py b/plugins/webstats/index.py index 3c272cce5..26e164152 100755 --- a/plugins/webstats/index.py +++ b/plugins/webstats/index.py @@ -7,8 +7,12 @@ import time import json import re -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False @@ -1394,6 +1398,10 @@ def installPreInspection(): return "请先安装OpenResty" return 'ok' +def uninstallPreInspection(): + stop() + return 'ok' + if __name__ == "__main__": func = sys.argv[1] if func == 'status': @@ -1408,6 +1416,8 @@ if __name__ == "__main__": print(reload()) elif func == 'install_pre_inspection': print(installPreInspection()) + elif func == 'uninstall_pre_inspection': + print(uninstallPreInspection()) elif func == 'run_info': print(runInfo()) elif func == 'get_global_conf': diff --git a/plugins/webstats/info.json b/plugins/webstats/info.json index 5623f449f..5d31a1cdc 100755 --- a/plugins/webstats/info.json +++ b/plugins/webstats/info.json @@ -16,10 +16,11 @@ "name": "webstats", "title": "网站统计", "shell": "install.sh", - "versions":["0.2.5"], + "versions":"0.2.5", "tip": "soft", "install_pre_inspection":true, - "checks": "server/webstats", + "uninstall_pre_inspection":true, + "checks": "server/webstats/version.pl", "path": "server/webstats", "display": 1, "author": "midoks", diff --git a/plugins/webstats/install.sh b/plugins/webstats/install.sh index 771f8d290..bbf82e3fd 100755 --- a/plugins/webstats/install.sh +++ b/plugins/webstats/install.sh @@ -12,7 +12,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 sys_os=`uname` @@ -49,7 +48,6 @@ Install_App() echo '正在安装脚本文件...' mkdir -p $serverPath/source/webstats mkdir -p $serverPath/webstats - echo "${VERSION}" > $serverPath/webstats/version.pl # 下载源码安装包 # curl -O $serverPath/source/webstats/lua-5.1.5.tar.gz https://www.lua.org/ftp/lua-5.1.5.tar.gz @@ -132,8 +130,9 @@ Install_App() if [ -f $serverPath/source/webstats/GeoLite2-City.mmdb ];then cp -rf $serverPath/source/webstats/GeoLite2-City.mmdb $serverPath/webstats/GeoLite2-City.mmdb fi - - cd $rootPath && python3 ${rootPath}/plugins/webstats/index.py start + + cd $rootPath && python3 plugins/webstats/index.py start + echo "${VERSION}" > $serverPath/webstats/version.pl echo '网站统计安装完成' @@ -148,7 +147,7 @@ Install_App() Uninstall_App() { - cd $rootPath && python3 ${rootPath}/plugins/webstats/index.py stop + cd $rootPath && python3 plugins/webstats/index.py stop rm -rf $serverPath/webstats echo "网站统计卸载完成" } diff --git a/plugins/webstats/tool_migrate.py b/plugins/webstats/tool_migrate.py index 926cd6474..ddc66e884 100644 --- a/plugins/webstats/tool_migrate.py +++ b/plugins/webstats/tool_migrate.py @@ -6,9 +6,13 @@ import os import time import json -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) +import core.mw as mw +from utils.crontab import crontab as MwCrontab app_debug = False if mw.isAppleSystem(): @@ -27,11 +31,6 @@ def getServerDir(): return mw.getServerDir() + '/' + getPluginName() -def getTaskConf(): - conf = getServerDir() + "/task_config.json" - return conf - - def getConf(): conf = getServerDir() + "/lua/config.json" return conf @@ -122,7 +121,7 @@ def migrateSiteHotLogs(site_name, query_date): if field is None: field = "\'\'" elif type(field) == str: - field = "\'" + field.replace("\'", "\”") + "\'" + field = "\'" + field.replace("\'", "\"") + "\'" params += str(field) insert_sql = "insert into web_logs(" + \ _columns + ") values(" + params + ")" diff --git a/plugins/webstats/tool_task.py b/plugins/webstats/tool_task.py index 72b65366a..95f923e28 100644 --- a/plugins/webstats/tool_task.py +++ b/plugins/webstats/tool_task.py @@ -6,8 +6,14 @@ import os import time import json -sys.path.append(os.getcwd() + "/class/core") -import mw + +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw +from utils.crontab import crontab as MwCrontab app_debug = False @@ -33,10 +39,9 @@ def getTaskConf(): def getConfigData(): - try: + conf = getTaskConf() + if os.path.exists(conf): return json.loads(mw.readFile(getTaskConf())) - except: - pass return { "task_id": -1, "task_list": ["migrate_hot_logs"], @@ -53,16 +58,13 @@ def createBgTask(): return True if "task_id" in cfg.keys() and cfg["task_id"] > 0: - res = mw.M("crontab").field("id, name").where( - "id=?", (cfg["task_id"],)).find() + res = mw.M("crontab").field("id, name").where("id=?", (cfg["task_id"],)).find() if res and res["id"] == cfg["task_id"]: print("计划任务已经存在!") return True - import crontab_api - api = crontab_api.crontab_api() - cmd = "cd " + mw.getRunDir() + " && nice -n 10 python3 " + \ - getPluginDir() + "/tool_task.py execute" + + cmd = "cd " + mw.getPanelDir() + " && nice -n 10 python3 " + getPluginDir() + "/tool_task.py execute" params = { 'name': name, 'type': 'day', @@ -75,10 +77,10 @@ def createBgTask(): 'stype': "toShell", 'sname': '', 'sbody': cmd, - 'urladdress': '', + 'url_address': '', } - task_id = api.add(params) + task_id = MwCrontab.instance().add(params) if task_id > 0: cfg["task_id"] = task_id mw.writeFile(getTaskConf(), json.dumps(cfg)) @@ -90,11 +92,8 @@ def removeBgTask(): res = mw.M("crontab").field("id, name").where( "id=?", (cfg["task_id"],)).find() if res and res["id"] == cfg["task_id"]: - import crontab_api - api = crontab_api.crontab_api() - - data = api.delete(cfg["task_id"]) - if data[0]: + data = MwCrontab.instance().delete(cfg["task_id"]) + if data['status']: # print(data[1]) cfg["task_id"] = -1 mw.writeFile(getTaskConf(), json.dumps(cfg)) diff --git a/plugins/webstats/webstats_index.py b/plugins/webstats/webstats_index.py index 411c8297d..5c5083519 100644 --- a/plugins/webstats/webstats_index.py +++ b/plugins/webstats/webstats_index.py @@ -8,11 +8,13 @@ import json import re -try: - sys.path.append(os.getcwd() + "/class/core") - import mw -except Exception as e: - import core.mw as mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw +from utils.crontab import crontab as MwCrontab app_debug = False diff --git a/plugins/xhprof/index.py b/plugins/xhprof/index.py index cbe06fd73..a2861b17a 100755 --- a/plugins/xhprof/index.py +++ b/plugins/xhprof/index.py @@ -6,9 +6,13 @@ import os import time import re -sys.path.append(os.getcwd() + "/class/core") -import mw -import site_api +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw +from utils.site import sites as MwSites app_debug = False if mw.isAppleSystem(): @@ -70,7 +74,7 @@ def getHomePage(): def getPhpVer(expect=56): import json - v = site_api.site_api().getPhpVersion() + v = MwSites.instance().getPhpVersion() for i in range(len(v)): t = int(v[i]['version']) if (t >= expect): @@ -93,7 +97,7 @@ def contentReplace(content): service_path = mw.getServerDir() php_ver = getCachePhpVer() # print php_ver - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$PHP_VER}', php_ver) return content @@ -102,7 +106,7 @@ def contentReplace(content): def contentReplacePHP(content, version): service_path = mw.getServerDir() # print php_ver - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$PHP_VER}', version) return content diff --git a/plugins/xhprof/install.sh b/plugins/xhprof/install.sh index 745164925..45eabd0e1 100755 --- a/plugins/xhprof/install.sh +++ b/plugins/xhprof/install.sh @@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl sys_os=`uname` Install_xh() { @@ -18,7 +17,7 @@ Install_xh() fi echo "${1}" > ${serverPath}/xhprof/version.pl - echo '安装完成' > $install_tmp + echo '安装完成' if [ "$sys_os" != "Darwin" ];then cd $rootPath && python3 ${rootPath}/plugins/xhprof/index.py start @@ -37,7 +36,7 @@ Uninstall_xh() if [ -f ${serverPath}/web_conf/nginx/vhost/xhprof.conf ];then rm -f ${serverPath}/web_conf/nginx/vhost/xhprof.conf fi - echo '卸载完成' > $install_tmp + echo '卸载完成' } action=$1 diff --git a/plugins/xui/ico.png b/plugins/xui/ico.png new file mode 100644 index 000000000..b7f2362c5 Binary files /dev/null and b/plugins/xui/ico.png differ diff --git a/plugins/xui/index.html b/plugins/xui/index.html new file mode 100755 index 000000000..81e32de12 --- /dev/null +++ b/plugins/xui/index.html @@ -0,0 +1,25 @@ +
+
+
+

服务

+

自启动

+

常用功能

+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/plugins/xui/index.py b/plugins/xui/index.py new file mode 100755 index 000000000..0fc9d7764 --- /dev/null +++ b/plugins/xui/index.py @@ -0,0 +1,223 @@ +# coding:utf-8 + +import sys +import io +import os +import time +import re + +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw + +app_debug = False +if mw.isAppleSystem(): + app_debug = True + + +def getPluginName(): + return 'xui' + + +def getPluginDir(): + return mw.getPluginDir() + '/' + getPluginName() + + +def getServerDir(): + return mw.getServerDir() + '/' + getPluginName() + + +def getArgs(): + args = sys.argv[2:] + tmp = {} + args_len = len(args) + + if args_len == 1: + t = args[0].strip('{').strip('}') + t = t.split(':') + tmp[t[0]] = t[1] + elif args_len > 1: + for i in range(len(args)): + t = args[i].split(':') + tmp[t[0]] = t[1] + + return tmp + +def status(): + cmd = "ps -ef|grep x-ui |grep -v grep | grep -v python | awk '{print $2}'" + data = mw.execShell(cmd) + if data[0] == '': + return 'stop' + return 'start' + + +def getServiceFile(): + systemDir = mw.systemdCfgDir() + return systemDir + '/xui.service' + + +def getXuiPort(): + return '8349' + + +def __release_port(port): + from collections import namedtuple + try: + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().addAcceptPort(port, 'xui', 'port') + return port + except Exception as e: + return "Release failed {}".format(e) + +def __delete_port(port): + from collections import namedtuple + try: + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().delAcceptPortCmd(port, 'tcp') + return port + except Exception as e: + return "Delete failed {}".format(e) + + +def pSqliteDb(dbname='databases'): + conn = mw.M(dbname).dbPos('/etc/x-ui', 'x-ui') + return conn + +def initDreplace(): + return 'ok' + + +def xuiOp(method): + file = initDreplace() + + if not mw.isAppleSystem(): + mw.execShell('systemctl daemon-reload') + data = mw.execShell('systemctl ' + method + ' x-ui') + if data[1] == '': + return 'ok' + return data[1] + + return 'fail' + + +def start(): + openPort() + return xuiOp('start') + + +def stop(): + closePort() + return xuiOp('stop') + + +def restart(): + return xuiOp('restart') + + +def reload(): + return redisOp('reload') + + +def initdStatus(): + if mw.isAppleSystem(): + return "Apple Computer does not support" + + shell_cmd = 'systemctl status x-ui | grep loaded | grep "enabled;"' + data = mw.execShell(shell_cmd) + if data[0] == '': + return 'fail' + return 'ok' + +def initdInstall(): + if mw.isAppleSystem(): + return "Apple Computer does not support" + mw.execShell('systemctl enable x-ui') + return 'ok' + + +def initdUinstall(): + if mw.isAppleSystem(): + return "Apple Computer does not support" + mw.execShell('systemctl disable x-ui') + return 'ok' + +def openPort(): + setting = pSqliteDb('settings') + port_data = setting.field('id,key,value').where("key=?", ('webPort',)).find() + port = port_data['value'] + __release_port(port) + +def closePort(): + setting = pSqliteDb('settings') + port_data = setting.field('id,key,value').where("key=?", ('webPort',)).find() + port = port_data['value'] + __delete_port(port) + +def getXuiInfo(): + + data = {} + user = pSqliteDb('users') + info = user.field('username,password').where("id=?", (1,)).find() + + setting = pSqliteDb('settings') + port_data = setting.field('id,key,value').where("key=?", ('webPort',)).find() + path_data = setting.field('id,key,value').where("key=?", ('webBasePath',)).find() + + if path_data is not None: + data['path'] = path_data['value'] + else: + data['path'] = '/' + + data['username'] = info['username'] + data['password'] = info['password'] + data['port'] = port_data['value'] + + data['ip'] = mw.getHostAddr() + return mw.returnJson(True, 'ok', data) + +def installPreInspection(): + sys = mw.execShell("cat /etc/*-release | grep PRETTY_NAME |awk -F = '{print $2}' | awk -F '\"' '{print $2}'| awk '{print $1}'") + + if sys[1] != '': + return '不支持该系统' + + sys_id = mw.execShell("cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") + + sysName = sys[0].strip().lower() + sysId = sys_id[0].strip() + + if sysName in ('opensuse'): + return '不支持该系统' + + return 'ok' + + +if __name__ == "__main__": + func = sys.argv[1] + if func == 'status': + print(status()) + elif func == 'start': + print(start()) + elif func == 'stop': + print(stop()) + elif func == 'restart': + print(restart()) + elif func == 'reload': + print(reload()) + elif func == 'initd_status': + print(initdStatus()) + elif func == 'initd_install': + print(initdInstall()) + elif func == 'initd_uninstall': + print(initdUinstall()) + elif func == 'install_pre_inspection': + print(installPreInspection()) + elif func == 'conf': + print(getServiceFile()) + elif func == 'info': + print(getXuiInfo()) + else: + print('error') diff --git a/plugins/xui/info.json b/plugins/xui/info.json new file mode 100755 index 000000000..4959bfc54 --- /dev/null +++ b/plugins/xui/info.json @@ -0,0 +1,19 @@ +{ + "sort": 7, + "ps": "支持多协议多用户的xray面板", + "install_pre_inspection":true, + "name": "xui", + "title": "xui", + "shell": "install.sh", + "versions":["1.0"], + "updates":["1.0"], + "tip": "soft", + "checks": "server/xui", + "path": "server/xui", + "display": 1, + "author": "FranzKafkaYu", + "date": "2024-12-03", + "home": "https://github.com/FranzKafkaYu/x-ui", + "type": 0, + "pid": "5" +} \ No newline at end of file diff --git a/plugins/xui/install.sh b/plugins/xui/install.sh new file mode 100755 index 000000000..5569db5bd --- /dev/null +++ b/plugins/xui/install.sh @@ -0,0 +1,47 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + +sysArch=`arch` +sysName=`uname` + + +# cd /www/server/mdserver-web && python3 plugins/xui/index.py info +# cd /www/server/mdserver-web/plugins/xui && /bin/bash install.sh install 1.0 + +VERSION=$2 +Install_app() +{ + mkdir -p $serverPath/source + mkdir -p $serverPath/source/xui + mkdir -p $serverPath/xui + + echo "$VERSION" > $serverPath/xui/version.pl + + bash ${curPath}/install_xui.sh + + cd ${rootPath} && python3 plugins/xui/index.py start + echo '安装完成' +} + +Uninstall_app() +{ + cd ${rootPath} && python3 plugins/xui/index.py stop + + rm -rf $serverPath/xui + echo 'y' | x-ui uninstall + rm /usr/bin/x-ui -f + echo '卸载完成' +} + +action=$1 +if [ "${1}" == 'install' ];then + Install_app $2 +else + Uninstall_app $2 +fi diff --git a/plugins/xui/install_xui.sh b/plugins/xui/install_xui.sh new file mode 100644 index 000000000..0309c9460 --- /dev/null +++ b/plugins/xui/install_xui.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env bash + +red='\033[0;31m' +green='\033[0;32m' +yellow='\033[0;33m' +plain='\033[0m' + +cur_dir=$(pwd) + +# check root +[[ $EUID -ne 0 ]] && echo -e "${red}错误:${plain} 必须使用root用户运行此脚本!\n" && exit 1 + +# check os +if [[ -f /etc/redhat-release ]]; then + release="centos" +elif cat /etc/issue | grep -Eqi "debian"; then + release="debian" +elif cat /etc/issue | grep -Eqi "ubuntu"; then + release="ubuntu" +elif cat /etc/issue | grep -Eqi "centos|red hat|redhat"; then + release="centos" +elif cat /proc/version | grep -Eqi "debian"; then + release="debian" +elif cat /proc/version | grep -Eqi "ubuntu"; then + release="ubuntu" +elif cat /proc/version | grep -Eqi "centos|red hat|redhat"; then + release="centos" +else + echo -e "${red}未检测到系统版本,请联系脚本作者!${plain}\n" && exit 1 +fi + +arch=$(arch) + +if [[ $arch == "x86_64" || $arch == "x64" || $arch == "s390x" || $arch == "amd64" ]]; then + arch="amd64" +elif [[ $arch == "aarch64" || $arch == "arm64" ]]; then + arch="arm64" +else + arch="amd64" + echo -e "${red}检测架构失败,使用默认架构: ${arch}${plain}" +fi + +echo "架构: ${arch}" + +if [ $(getconf WORD_BIT) != '32' ] && [ $(getconf LONG_BIT) != '64' ]; then + echo "本软件不支持 32 位系统(x86),请使用 64 位系统(x86_64),如果检测有误,请联系作者" + exit -1 +fi + +os_version="" + +# os version +if [[ -f /etc/os-release ]]; then + os_version=$(awk -F'[= ."]' '/VERSION_ID/{print $3}' /etc/os-release) +fi +if [[ -z "$os_version" && -f /etc/lsb-release ]]; then + os_version=$(awk -F'[= ."]+' '/DISTRIB_RELEASE/{print $2}' /etc/lsb-release) +fi + +if [[ x"${release}" == x"centos" ]]; then + if [[ ${os_version} -le 6 ]]; then + echo -e "${red}请使用 CentOS 7 或更高版本的系统!${plain}\n" && exit 1 + fi +elif [[ x"${release}" == x"ubuntu" ]]; then + if [[ ${os_version} -lt 16 ]]; then + echo -e "${red}请使用 Ubuntu 16 或更高版本的系统!${plain}\n" && exit 1 + fi +elif [[ x"${release}" == x"debian" ]]; then + if [[ ${os_version} -lt 8 ]]; then + echo -e "${red}请使用 Debian 8 或更高版本的系统!${plain}\n" && exit 1 + fi +fi + +install_base() { + if [[ x"${release}" == x"centos" ]]; then + yum install wget curl tar jq -y + else + apt install wget curl tar jq -y + fi +} + +#This function will be called when user installed x-ui out of sercurity +config_after_install() { + echo -e "${yellow}出于安全考虑,安装/更新完成后需要强制修改端口与账户密码${plain}" + if [[ ! -f "/etc/x-ui/x-ui.db" ]]; then + local usernameTemp=$(head -c 6 /dev/urandom | base64) + local passwordTemp=$(head -c 6 /dev/urandom | base64) + local portTemp=$(echo $RANDOM) + /usr/local/x-ui/x-ui setting -username ${usernameTemp} -password ${passwordTemp} + /usr/local/x-ui/x-ui setting -port ${portTemp} + echo -e "检测到您属于全新安装,出于安全考虑已自动为您生成随机用户与端口:" + echo -e "###############################################" + echo -e "${green}面板登录用户名:${usernameTemp}${plain}" + echo -e "${green}面板登录用户密码:${passwordTemp}${plain}" + echo -e "${red}面板登录端口:${portTemp}${plain}" + echo -e "###############################################" + echo -e "${red}如您遗忘了面板登录相关信息,可在安装完成后输入x-ui,输入选项7查看面板登录信息${plain}" + else + echo -e "${red}当前属于版本升级,保留之前设置项,登录方式保持不变,可输入x-ui后键入数字7查看面板登录信息${plain}" + fi +} + +install_x-ui() { + systemctl stop x-ui + cd /usr/local/ + + if [ $# == 0 ]; then + last_version=$(curl -Lsk "https://api.github.com/repos/FranzKafkaYu/x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + if [[ ! -n "$last_version" ]]; then + echo -e "${red}检测 x-ui 版本失败,可能是超出 Github API 限制,请稍后再试,或手动指定 x-ui 版本安装${plain}" + exit 1 + fi + echo -e "检测到 x-ui 最新版本:${last_version},开始安装" + wget -N --no-check-certificate -O /usr/local/x-ui-linux-${arch}.tar.gz https://github.com/FranzKafkaYu/x-ui/releases/download/${last_version}/x-ui-linux-${arch}.tar.gz + if [[ $? -ne 0 ]]; then + echo -e "${red}下载 x-ui 失败,请确保你的服务器能够下载 Github 的文件${plain}" + exit 1 + fi + else + last_version=$1 + url="https://github.com/FranzKafkaYu/x-ui/releases/download/${last_version}/x-ui-linux-${arch}.tar.gz" + echo -e "开始安装 x-ui v$1" + wget -N --no-check-certificate -O /usr/local/x-ui-linux-${arch}.tar.gz ${url} + if [[ $? -ne 0 ]]; then + echo -e "${red}下载 x-ui v$1 失败,请确保此版本存在${plain}" + exit 1 + fi + fi + + if [[ -e /usr/local/x-ui/ ]]; then + rm /usr/local/x-ui/ -rf + fi + + tar zxvf x-ui-linux-${arch}.tar.gz + rm x-ui-linux-${arch}.tar.gz -f + cd x-ui + chmod +x x-ui bin/xray-linux-${arch} + cp -f x-ui.service /etc/systemd/system/ + wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/FranzKafkaYu/x-ui/main/x-ui.sh + chmod +x /usr/local/x-ui/x-ui.sh + chmod +x /usr/bin/x-ui + config_after_install + #echo -e "如果是全新安装,默认网页端口为 ${green}54321${plain},用户名和密码默认都是 ${green}admin${plain}" + #echo -e "请自行确保此端口没有被其他程序占用,${yellow}并且确保 54321 端口已放行${plain}" + # echo -e "若想将 54321 修改为其它端口,输入 x-ui 命令进行修改,同样也要确保你修改的端口也是放行的" + #echo -e "" + #echo -e "如果是更新面板,则按你之前的方式访问面板" + #echo -e "" + systemctl daemon-reload + systemctl enable x-ui + systemctl start x-ui +} + +echo -e "${green}开始安装${plain}" +install_base +install_x-ui $1 \ No newline at end of file diff --git a/plugins/xui/js/xui.js b/plugins/xui/js/xui.js new file mode 100644 index 000000000..e9bb21e61 --- /dev/null +++ b/plugins/xui/js/xui.js @@ -0,0 +1,64 @@ +function xuiPost(method, version, args,callback){ + var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); + + var req_data = {}; + req_data['name'] = 'xui'; + req_data['func'] = method; + req_data['version'] = version; + + if (typeof(args) == 'string'){ + req_data['args'] = JSON.stringify(toArrayObject(args)); + } else { + req_data['args'] = JSON.stringify(args); + } + + $.post('/plugins/run', req_data, function(data) { + layer.close(loadT); + if (!data.status){ + //错误展示10S + layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']}); + return; + } + + if(typeof(callback) == 'function'){ + callback(data); + } + },'json'); +} + +function xuiCommonFunc(){ + + xuiPost('info', '', {}, function(rdata){ + var rdata = $.parseJSON(rdata.data); + var info = rdata['data']; + var con = '

\ + 用户名\ + \ +

'; + + con += '

\ + 密码\ + \ +

'; + con += '

\ + 端口\ + \ +

'; + + con += '

\ + 路径\ + \ +

'; + + con += '

\ + \ +

'; + + $(".soft-man-con").html(con); + $('#open_url').click(function(){ + var url = 'http://' + info.ip + ':' + info.port + info.path; + window.open(url); + copyText(url); + }); + }); +} \ No newline at end of file diff --git a/plugins/zabbix/index.py b/plugins/zabbix/index.py index e7901729b..7be515276 100755 --- a/plugins/zabbix/index.py +++ b/plugins/zabbix/index.py @@ -6,8 +6,12 @@ import os import time import re -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -93,7 +97,7 @@ def getInstallVerion(): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$ZABBIX_ROOT}', '/usr/share/zabbix') content = content.replace('{$ZABBIX_PORT}', '18888') @@ -334,10 +338,10 @@ def initAgentConf(): def openPort(): try: - import firewall_api - firewall_api.firewall_api().addAcceptPortArgs('18888', 'zabbix-web', 'port') - firewall_api.firewall_api().addAcceptPortArgs('10051', 'zabbix-server', 'port') - firewall_api.firewall_api().addAcceptPortArgs('10050', 'zabbix-agent', 'port') + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().addAcceptPort('18888', 'zabbix-web', 'port') + MwFirewall.instance().addAcceptPort('10051', 'zabbix-server', 'port') + MwFirewall.instance().addAcceptPort('10050', 'zabbix-agent', 'port') return port except Exception as e: return "Release failed {}".format(e) diff --git a/plugins/zabbix/install.sh b/plugins/zabbix/install.sh index a49c4689f..349265683 100755 --- a/plugins/zabbix/install.sh +++ b/plugins/zabbix/install.sh @@ -14,7 +14,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 sysName=`uname` diff --git a/plugins/zabbix/versions/6.0/alma.sh b/plugins/zabbix/versions/6.0/alma.sh index 5858a76d3..d432a18e4 100644 --- a/plugins/zabbix/versions/6.0/alma.sh +++ b/plugins/zabbix/versions/6.0/alma.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/6.0/centos.sh b/plugins/zabbix/versions/6.0/centos.sh index 562d58e70..5ddb82e7a 100644 --- a/plugins/zabbix/versions/6.0/centos.sh +++ b/plugins/zabbix/versions/6.0/centos.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/6.0/debian.sh b/plugins/zabbix/versions/6.0/debian.sh index 27915ea75..3df4767ee 100644 --- a/plugins/zabbix/versions/6.0/debian.sh +++ b/plugins/zabbix/versions/6.0/debian.sh @@ -8,7 +8,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl + debian_suffix= if [ "$SYS_ARCH" == "aarch64" ];then debian_suffix="-arm64" diff --git a/plugins/zabbix/versions/6.0/opensuse.sh b/plugins/zabbix/versions/6.0/opensuse.sh index 748fc705d..3b316159b 100644 --- a/plugins/zabbix/versions/6.0/opensuse.sh +++ b/plugins/zabbix/versions/6.0/opensuse.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/6.0/rocky.sh b/plugins/zabbix/versions/6.0/rocky.sh index 2b3348512..b9e8cfbec 100644 --- a/plugins/zabbix/versions/6.0/rocky.sh +++ b/plugins/zabbix/versions/6.0/rocky.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/6.0/ubuntu.sh b/plugins/zabbix/versions/6.0/ubuntu.sh index a65f647e7..b564164a0 100644 --- a/plugins/zabbix/versions/6.0/ubuntu.sh +++ b/plugins/zabbix/versions/6.0/ubuntu.sh @@ -9,8 +9,8 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_ARCH=`arch` + SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` ubuntu_suffix= diff --git a/plugins/zabbix/versions/7.0/alma.sh b/plugins/zabbix/versions/7.0/alma.sh index dd1cc523c..d53b56da3 100644 --- a/plugins/zabbix/versions/7.0/alma.sh +++ b/plugins/zabbix/versions/7.0/alma.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/7.0/centos.sh b/plugins/zabbix/versions/7.0/centos.sh index db9c4a3b0..8bfddec8c 100644 --- a/plugins/zabbix/versions/7.0/centos.sh +++ b/plugins/zabbix/versions/7.0/centos.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/7.0/debian.sh b/plugins/zabbix/versions/7.0/debian.sh index a7abe09cd..3492f1786 100644 --- a/plugins/zabbix/versions/7.0/debian.sh +++ b/plugins/zabbix/versions/7.0/debian.sh @@ -10,8 +10,6 @@ sourcePath=${serverPath}/source sysName=`uname` sysArch=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl - debian_suffix= if [ "$sysArch" == "aarch64" ];then debian_suffix="-arm64" diff --git a/plugins/zabbix/versions/7.0/opensuse.sh b/plugins/zabbix/versions/7.0/opensuse.sh index 805f6419c..6b093949f 100644 --- a/plugins/zabbix/versions/7.0/opensuse.sh +++ b/plugins/zabbix/versions/7.0/opensuse.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/7.0/rocky.sh b/plugins/zabbix/versions/7.0/rocky.sh index 8bc6c05a9..7d19a292f 100644 --- a/plugins/zabbix/versions/7.0/rocky.sh +++ b/plugins/zabbix/versions/7.0/rocky.sh @@ -8,7 +8,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl + SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix/versions/7.0/ubuntu.sh b/plugins/zabbix/versions/7.0/ubuntu.sh index 7bc4eb851..1e34aa830 100644 --- a/plugins/zabbix/versions/7.0/ubuntu.sh +++ b/plugins/zabbix/versions/7.0/ubuntu.sh @@ -11,7 +11,6 @@ sourcePath=${serverPath}/source sysName=`uname` sysArch=`arch` -install_tmp=${rootPath}/tmp/mw_install.pl ubuntu_suffix= if [ "$sysArch" == "aarch64" ];then ubuntu_suffix="-arm64" diff --git a/plugins/zabbix_agent/index.py b/plugins/zabbix_agent/index.py index 2b48cd444..d87b1b689 100755 --- a/plugins/zabbix_agent/index.py +++ b/plugins/zabbix_agent/index.py @@ -6,8 +6,12 @@ import os import time import re -sys.path.append(os.getcwd() + "/class/core") -import mw +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) + +import core.mw as mw app_debug = False if mw.isAppleSystem(): @@ -79,7 +83,7 @@ def status(): def contentReplace(content): service_path = mw.getServerDir() - content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$ROOT_PATH}', mw.getFatherDir()) content = content.replace('{$SERVER_PATH}', service_path) return content @@ -128,8 +132,8 @@ def initDreplace(): def openPort(): try: - import firewall_api - firewall_api.firewall_api().addAcceptPortArgs('10050', 'zabbix-agent', 'port') + from utils.firewall import Firewall as MwFirewall + MwFirewall.instance().addAcceptPort('10050', 'zabbix-agent', 'port') return port except Exception as e: return "Release failed {}".format(e) diff --git a/plugins/zabbix_agent/install.sh b/plugins/zabbix_agent/install.sh index 0bbaf897b..955c8c072 100755 --- a/plugins/zabbix_agent/install.sh +++ b/plugins/zabbix_agent/install.sh @@ -15,7 +15,6 @@ rootPath=$(dirname "$curPath") rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") -install_tmp=${rootPath}/tmp/mw_install.pl VERSION=$2 sysName=`uname` diff --git a/plugins/zabbix_agent/versions/6.0/debian.sh b/plugins/zabbix_agent/versions/6.0/debian.sh index 80283fa0b..dd284d4e2 100644 --- a/plugins/zabbix_agent/versions/6.0/debian.sh +++ b/plugins/zabbix_agent/versions/6.0/debian.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix_agent/versions/7.0/alma.sh b/plugins/zabbix_agent/versions/7.0/alma.sh index 39f54df27..0c72c43b7 100644 --- a/plugins/zabbix_agent/versions/7.0/alma.sh +++ b/plugins/zabbix_agent/versions/7.0/alma.sh @@ -8,7 +8,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl + SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix_agent/versions/7.0/centos.sh b/plugins/zabbix_agent/versions/7.0/centos.sh index a456ee761..8c4e391d5 100644 --- a/plugins/zabbix_agent/versions/7.0/centos.sh +++ b/plugins/zabbix_agent/versions/7.0/centos.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix_agent/versions/7.0/debian.sh b/plugins/zabbix_agent/versions/7.0/debian.sh index 3278f253c..f29fb7992 100644 --- a/plugins/zabbix_agent/versions/7.0/debian.sh +++ b/plugins/zabbix_agent/versions/7.0/debian.sh @@ -8,7 +8,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl + SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix_agent/versions/7.0/opensuse.sh b/plugins/zabbix_agent/versions/7.0/opensuse.sh index 6ec3cd19c..2b08e20a5 100644 --- a/plugins/zabbix_agent/versions/7.0/opensuse.sh +++ b/plugins/zabbix_agent/versions/7.0/opensuse.sh @@ -8,7 +8,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl + SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix_agent/versions/7.0/rocky.sh b/plugins/zabbix_agent/versions/7.0/rocky.sh index c7107c7d6..9d1f8e95c 100644 --- a/plugins/zabbix_agent/versions/7.0/rocky.sh +++ b/plugins/zabbix_agent/versions/7.0/rocky.sh @@ -8,7 +8,6 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/plugins/zabbix_agent/versions/7.0/ubuntu.sh b/plugins/zabbix_agent/versions/7.0/ubuntu.sh index e12309ca5..98d7c1cf6 100644 --- a/plugins/zabbix_agent/versions/7.0/ubuntu.sh +++ b/plugins/zabbix_agent/versions/7.0/ubuntu.sh @@ -9,7 +9,7 @@ rootPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath") sourcePath=${serverPath}/source sysName=`uname` -install_tmp=${rootPath}/tmp/mw_install.pl + SYS_VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` diff --git a/requirements.txt b/requirements.txt index 20e997e8a..d04fe1935 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ setuptools>=33.1.1 -Werkzeug>=1.0.1,<3.0.0 +Werkzeug>=1.0.1 wheel>=0.37.1 gunicorn>=21.2.0 requests>=2.27.1 urllib3>=1.21.1 -flask>=2.0.3,<3.0.0 +flask>=2.0.3 flask-session==0.3.2 flask-helper==0.19 flask-bcrypt==1.0.1 @@ -12,20 +12,21 @@ flask-caching>=1.10.1 cache==1.0.3 gevent>=22.10.2 gevent-websocket==0.10.1 +eventlet>=0.24.1 psutil==5.9.1 chardet==3.0.4 -SQLAlchemy==2.* -Flask-SQLAlchemy==3.1.* -Flask-Migrate==4.* +SQLAlchemy>=1.4.54 +Flask-SQLAlchemy>=2.5.1 +#Flask-Migrate==4.* #Flask-Security-Too==5.5.*; python_version >= '3.10' #Flask-Security-Too==5.4.*; python_version <= '3.9' pyOpenSSL==23.2.0 cryptography>=40.0.2 configparser==5.2.0 -python-engineio==4.3.2 +python-engineio>=4.3.2 python-socketio>=4.2.0 -flask-socketio==5.2.0 -flask-sockets==0.2.1 +flask-socketio>=5.2.0 +flask-sockets>=0.2.1 zmq==0.0.0 paramiko>=2.8.0 pymongo diff --git a/route/__init__.py b/route/__init__.py deleted file mode 100755 index bffc16e33..000000000 --- a/route/__init__.py +++ /dev/null @@ -1,628 +0,0 @@ -# coding:utf-8 - -import sys -import io -import os -import time -import shutil -import uuid -import json -import traceback -import socket - -# reload(sys) -# sys.setdefaultencoding('utf-8') -import paramiko -from datetime import timedelta - -from flask import Flask -from flask import render_template -from flask import make_response -from flask import Response -from flask import session -from flask import request -from flask import redirect -from flask import url_for -from flask import render_template_string, abort -from flask_caching import Cache -from flask_session import Session -# from flask_compress import Compress - -from whitenoise import WhiteNoise - -sys.path.append(os.getcwd() + "/class/core") - -import db -import mw -import config_api - -import common -common.init() - -app = Flask(__name__, template_folder='templates/default') -app.config.version = config_api.config_api().getVersion() -# Compress(app) - -app.wsgi_app = WhiteNoise( - app.wsgi_app, root="route/static/", prefix="static/", max_age=604800) - -cache = Cache(config={'CACHE_TYPE': 'simple'}) -cache.init_app(app, config={'CACHE_TYPE': 'simple'}) - -try: - from flask_sqlalchemy import SQLAlchemy - app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/py_mw_session.db' - app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True - app.config['SESSION_TYPE'] = 'sqlalchemy' - app.config['SESSION_SQLALCHEMY'] = sdb - app.config['SESSION_SQLALCHEMY_TABLE'] = 'session' - sdb = SQLAlchemy(app) - sdb.create_all() -except: - app.config['SESSION_TYPE'] = 'filesystem' - app.config['SESSION_FILE_DIR'] = '/tmp/py_mw_session_' + str(sys.version_info[0]) - app.config['SESSION_FILE_THRESHOLD'] = 1024 - app.config['SESSION_FILE_MODE'] = 384 - -app.secret_key = uuid.UUID(int=uuid.getnode()).hex[-12:] -app.config['SESSION_PERMANENT'] = True -app.config['SESSION_USE_SIGNER'] = True -app.config['SESSION_KEY_PREFIX'] = 'MW_:' -app.config['SESSION_COOKIE_NAME'] = "MW_VER_1" -app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=31) - -if mw.isAppleSystem(): - app.config['DEBUG'] = True - -# Session(app) - -# 设置BasicAuth -basic_auth_conf = 'data/basic_auth.json' -app.config['BASIC_AUTH_OPEN'] = False -if os.path.exists(basic_auth_conf): - try: - ba_conf = json.loads(mw.readFile(basic_auth_conf)) - # print(ba_conf) - app.config['BASIC_AUTH_USERNAME'] = ba_conf['basic_user'] - app.config['BASIC_AUTH_PASSWORD'] = ba_conf['basic_pwd'] - app.config['BASIC_AUTH_OPEN'] = ba_conf['open'] - app.config['BASIC_AUTH_FORCE'] = True - except Exception as e: - print(e) - -# socketio -from flask_socketio import SocketIO, emit, send -socketio = SocketIO() -socketio.init_app(app) - -# sockets -from flask_sockets import Sockets -sockets = Sockets(app) - -# from gevent.pywsgi import WSGIServer -# from geventwebsocket.handler import WebSocketHandler -# http_server = WSGIServer(('0.0.0.0', '7200'), app, -# handler_class=WebSocketHandler) -# http_server.serve_forever() - -# debug macosx dev -if mw.isDebugMode(): - app.debug = True - app.config.version = app.config.version + str(time.time()) - -# ---------- error function start ----------------- - - -def getErrorNum(key, limit=None): - key = mw.md5(key) - num = cache.get(key) - if not num: - num = 0 - if not limit: - return num - if limit > num: - return True - return False - - -def setErrorNum(key, empty=False, expire=3600): - key = mw.md5(key) - num = cache.get(key) - if not num: - num = 0 - else: - if empty: - cache.delete(key) - return True - cache.set(key, num + 1, expire) - return True -# ---------- error function end ----------------- - - -def funConvert(fun): - block = fun.split('_') - func = block[0] - for x in range(len(block) - 1): - suf = block[x + 1].title() - func += suf - return func - - -def sendAuthenticated(): - # 发送http认证信息 - request_host = mw.getHostAddr() - result = Response( - '', 401, {'WWW-Authenticate': 'Basic realm="%s"' % request_host.strip()}) - if not 'login' in session and not 'admin_auth' in session: - session.clear() - return result - - -@app.before_request -def requestCheck(): - # Flask请求勾子 - if app.config['BASIC_AUTH_OPEN']: - auth = request.authorization - if request.path in ['/download', '/hook', '/down']: - return - - if not auth: - return sendAuthenticated() - salt = '_md_salt' - if mw.md5(auth.username.strip() + salt) != app.config['BASIC_AUTH_USERNAME'] \ - or mw.md5(auth.password.strip() + salt) != app.config['BASIC_AUTH_PASSWORD']: - return sendAuthenticated() - - domain_check = mw.checkDomainPanel() - if domain_check: - return domain_check - - -@app.after_request -def requestAfter(response): - response.headers['soft'] = 'mdserver-web' - response.headers['mw-version'] = app.config.version - return response - - -def isLogined(): - if 'login' in session and 'username' in session and session['login'] == True: - userInfo = mw.M('users').where("id=?", (1,)).field('id,username,password').find() - # print(userInfo) - if userInfo['username'] != session['username']: - return False - - now_time = int(time.time()) - - if 'overdue' in session and now_time > session['overdue']: - # 自动续期 - session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60 - return False - - if 'tmp_login_expire' in session and now_time > int(session['tmp_login_expire']): - session.clear() - return False - - return True - - # if os.path.exists('data/api_login.txt'): - # content = mw.readFile('data/api_login.txt') - # session['login'] = True - # session['username'] = content - # os.remove('data/api_login.txt') - return False - - -def publicObject(toObject, func, action=None, get=None): - name = funConvert(func) + 'Api' - try: - if hasattr(toObject, name): - efunc = 'toObject.' + name + '()' - data = eval(efunc) - return data - data = {'msg': '404,not find api[' + name + ']', "status": False} - return mw.getJson(data) - except Exception as e: - # API发生错误记录 - if mw.isDebugMode(): - print(traceback.print_exc()) - data = {'msg': '访问异常:' + str(mw.getTracebackInfo()) + '!', "status": False} - return mw.getJson(data) - - -# @app.route("/debug") -# def debug(): -# print(sys.version_info) -# print(session) -# os = mw.getOs() -# print(os) -# return mw.getLocalIp() - -@app.route("/.well-known/acme-challenge/") -def wellknow(val=None): - # 申请面板ssl使用 - f = mw.getRunDir() + "/tmp/.well-known/acme-challenge/" + val - if os.path.exists(f): - return mw.readFile(f) - return '' - - -@app.route("/hook", methods=['POST', 'GET']) -def webhook(): - # 仅针对webhook插件 - - # 兼容获取关键数据 - access_key = request.args.get('access_key', '').strip() - if access_key == '': - access_key = request.form.get('access_key', '').strip() - - params = request.args.get('params', '').strip() - if params == '': - params = request.form.get('params', '').strip() - - input_args = { - 'access_key': access_key, - 'params': params, - } - - wh_install_path = mw.getServerDir() + '/webhook' - if not os.path.exists(wh_install_path): - return mw.returnJson(False, '请先安装WebHook插件!') - - package = os.getcwd() + "/plugins/webhook" - if not package in sys.path: - sys.path.append(package) - - try: - import webhook_index - return webhook_index.runShellArgs(input_args) - except Exception as e: - return str(e) - - -@app.route('/close') -def close(): - if not os.path.exists('data/close.pl'): - return redirect('/') - data = {} - data['cmd'] = 'rm -rf ' + mw.getRunDir() + '/data/close.pl' - return render_template('close.html', data=data) - - -@app.route("/code") -def code(): - import vilidate - vie = vilidate.vieCode() - codeImage = vie.GetCodeImage(80, 4) - # try: - # from cStringIO import StringIO - # except: - # from StringIO import StringIO - - out = io.BytesIO() - codeImage[0].save(out, "png") - session['code'] = mw.md5(''.join(codeImage[1]).lower()) - - img = Response(out.getvalue(), headers={'Content-Type': 'image/png'}) - return make_response(img) - - -@app.route("/check_login", methods=['POST']) -def checkLogin(): - if isLogined(): - return "true" - return "false" - - -@app.route("/verify_login", methods=['POST']) -def verifyLogin(): - username = request.form.get('username', '').strip() - password = request.form.get('password', '').strip() - password = mw.md5(password) - - userInfo = mw.M('users').where("id=?", (1,)).field('id,username,password').find() - if userInfo['username'] != username or userInfo['password'] != password: - return mw.returnJson(-1, "密码错误?") - - auth = request.form.get('auth', '').strip() - - import pyotp - auth_file = 'data/auth_secret.pl' - if os.path.exists(auth_file): - content = mw.readFile(auth_file) - sec = mw.deDoubleCrypt('mdserver-web', content) - totp = pyotp.TOTP(sec) - if totp.verify(auth): - userInfo = mw.M('users').where("id=?", (1,)).field('id,username,password').find() - session['login'] = True - session['username'] = userInfo['username'] - session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60 - return mw.returnJson(1, '二次验证成功!') - - return mw.returnJson(-1, '二次验证失败!') - - -@app.route("/do_login", methods=['POST']) -def doLogin(): - login_cache_count = 5 - login_cache_limit = cache.get('login_cache_limit') - - filename = 'data/close.pl' - if os.path.exists(filename): - return mw.returnJson(False, '面板已经关闭!') - - username = request.form.get('username', '').strip() - password = request.form.get('password', '').strip() - code = request.form.get('code', '').strip() - # print(session) - if 'code' in session: - if session['code'] != mw.md5(code): - if login_cache_limit == None: - login_cache_limit = 1 - else: - login_cache_limit = int(login_cache_limit) + 1 - - if login_cache_limit >= login_cache_count: - mw.writeFile(filename, 'True') - return mw.returnJson(False, '面板已经关闭!') - - cache.set('login_cache_limit', login_cache_limit, timeout=10000) - login_cache_limit = cache.get('login_cache_limit') - code_msg = mw.getInfo("验证码错误,您还可以尝试[{1}]次!", (str( - login_cache_count - login_cache_limit))) - mw.writeLog('用户登录', code_msg) - return mw.returnJson(False, code_msg) - - userInfo = mw.M('users').where("id=?", (1,)).field('id,username,password').find() - password = mw.md5(password) - - if userInfo['username'] != username or userInfo['password'] != password: - msg = "密码错误,帐号:{1},密码:{2},登录IP:{3}", (( - '****', '******', request.remote_addr)) - - if login_cache_limit == None: - login_cache_limit = 1 - else: - login_cache_limit = int(login_cache_limit) + 1 - - if login_cache_limit >= login_cache_count: - mw.writeFile(filename, 'True') - return mw.returnJson(False, '面板已经关闭!') - - cache.set('login_cache_limit', login_cache_limit, timeout=10000) - login_cache_limit = cache.get('login_cache_limit') - mw.writeLog('用户登录', mw.getInfo(msg)) - return mw.returnJson(-1, mw.getInfo("用户名或密码错误,您还可以尝试[{1}]次!", (str(login_cache_count - login_cache_limit)))) - - - - cache.delete('login_cache_limit') - # 二次验证密钥 - auth_secret = 'data/auth_secret.pl' - if os.path.exists(auth_secret): - return mw.returnJson(2, '绑定二次验证了...') - - session['login'] = True - session['username'] = userInfo['username'] - session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60 - - # fix 跳转时,数据消失,可能是跨域问题 - # mw.writeFile('data/api_login.txt', userInfo['username']) - return mw.returnJson(1, '登录成功,正在跳转...') - - -@app.errorhandler(404) -def page_unauthorized(error): - return render_template_string('404 not found', error_info=error), 404 - - -def get_admin_safe(): - path = 'data/admin_path.pl' - if os.path.exists(path): - cont = mw.readFile(path) - cont = cont.strip().strip('/') - return (True, cont) - return (False, '') - - -def admin_safe_path(path, req, data, pageFile): - if path != req and not isLogined(): - if data['status_code'] == '0': - return render_template('path.html') - else: - return Response(status=int(data['status_code'])) - - if not isLogined(): - return render_template('login.html', data=data) - - if not req in pageFile: - return redirect('/') - - return render_template(req + '.html', data=data) - - -def login_temp_user(token): - if len(token) != 48: - return '错误的参数!' - - skey = mw.getClientIp() + '_temp_login' - if not getErrorNum(skey, 10): - return '连续10次验证失败,禁止1小时' - - stime = int(time.time()) - data = mw.M('temp_login').where('state=? and expire>?',(0, stime)).field('id,token,salt,expire,addtime').find() - if not data: - setErrorNum(skey) - return '验证失败!' - - if stime > int(data['expire']): - setErrorNum(skey) - return "过期" - - r_token = mw.md5(token + data['salt']) - if r_token != data['token']: - setErrorNum(skey) - return '验证失败!' - - userInfo = mw.M('users').where("id=?", (1,)).field('id,username').find() - session['login'] = True - session['username'] = userInfo['username'] - session['tmp_login'] = True - session['tmp_login_id'] = str(data['id']) - session['tmp_login_expire'] = int(data['expire']) - session['uid'] = data['id'] - - login_addr = mw.getClientIp() + ":" + str(request.environ.get('REMOTE_PORT')) - mw.writeLog('用户登录', "登录成功,帐号:{1},登录IP:{2}", - (userInfo['username'], login_addr)) - mw.M('temp_login').where('id=?', (data['id'],)).update( - {"login_time": stime, 'state': 1, 'login_addr': login_addr}) - - # print(session) - return redirect('/') - - -@app.route('/api//', methods=['POST', 'GET']) -def api(reqClass=None, reqAction=None, reqData=None): - comReturn = common.local() - if comReturn: - return comReturn - - import config_api - isOk, data = config_api.config_api().checkPanelToken() - if not isOk: - return mw.returnJson(False, '未开启API') - - request_time = request.form.get('request_time', '') - request_token = request.form.get('request_token', '') - request_ip = request.remote_addr - request_ip = request_ip.replace('::ffff:', '') - - # print(request_time, request_token) - if not mw.inArray(data['limit_addr'], request_ip): - return mw.returnJson(False, 'IP校验失败,您的访问IP为[' + request_ip + ']') - - local_token = mw.deCrypt(data['token'], data['token_crypt']) - token_md5 = mw.md5(str(request_time) + mw.md5(local_token)) - - if not (token_md5 == request_token): - return mw.returnJson(False, '密钥错误') - - if reqClass == None: - return mw.returnJson(False, '请指定请求方法类') - - if reqAction == None: - return mw.returnJson(False, '请指定请求方法') - - classFile = ('config_api', 'crontab_api', 'files_api', 'firewall_api', - 'plugins_api', 'system_api', 'site_api', 'task_api', - 'logs_api') - className = reqClass + '_api' - if not className in classFile: - return mw.returnJson(False, 'external api request error') - - eval_str = "__import__('" + className + "')." + className + '()' - newInstance = eval(eval_str) - - try: - return publicObject(newInstance, reqAction) - except Exception as e: - return mw.getTracebackInfo() - - -@app.route('//', methods=['POST', 'GET']) -@app.route('//', methods=['POST', 'GET']) -@app.route('/', methods=['POST', 'GET']) -@app.route('/', methods=['POST', 'GET']) -def index(reqClass=None, reqAction=None, reqData=None): - - comReturn = common.local() - if comReturn: - return comReturn - - # 页面请求 - if reqAction == None: - import config_api - data = config_api.config_api().get() - - if reqClass == None: - reqClass = 'index' - - pageFile = ('config', 'control', 'crontab', 'files', 'logs', 'firewall', - 'index', 'plugins', 'login', 'system', 'site', 'cert', 'ssl', 'task', 'soft') - - if reqClass == 'login': - token = request.args.get('tmp_token', '').strip() - if token != '': - return login_temp_user(token) - - # 设置了安全路径 - ainfo = get_admin_safe() - - # 登录页 - if reqClass == 'login': - - signout = request.args.get('signout', '') - if signout == 'True': - session.clear() - session['login'] = False - session['overdue'] = 0 - - if ainfo[0]: - return admin_safe_path(ainfo[1], reqClass, data, pageFile) - - return render_template('login.html', data=data) - - if ainfo[0]: - return admin_safe_path(ainfo[1], reqClass, data, pageFile) - - if not reqClass in pageFile: - return redirect('/') - - if not isLogined(): - return redirect('/login') - - return render_template(reqClass + '.html', data=data) - - if not isLogined(): - return 'error request!' - - # API请求 - classFile = ('config_api', 'crontab_api', 'files_api', 'logs_api', 'firewall_api', - 'plugins_api', 'system_api', 'site_api', 'task_api', 'vip_api') - className = reqClass + '_api' - if not className in classFile: - return "api error request" - - eval_str = "__import__('" + className + "')." + className + '()' - newInstance = eval(eval_str) - - return publicObject(newInstance, reqAction) - - -##################### ssh start ########################### - -@socketio.on('webssh_websocketio') -def webssh_websocketio(data): - if not isLogined(): - emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) - return - - import ssh_terminal - shell_client = ssh_terminal.ssh_terminal.instance() - shell_client.run(request.sid, data) - return - - -@socketio.on('webssh') -def webssh(data): - if not isLogined(): - emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) - return None - - import ssh_local - shell = ssh_local.ssh_local.instance() - shell.run(data) - return - -##################### ssh end ########################### diff --git a/route/static/app/config.js b/route/static/app/config.js deleted file mode 100755 index 31f8b2077..000000000 --- a/route/static/app/config.js +++ /dev/null @@ -1,1457 +0,0 @@ -/** op **/ -// $(".set-submit").click(function(){ -// var data = $("#set_config").serialize(); -// layer.msg('正在保存配置...',{icon:16,time:0,shade: [0.3, '#000']}); -// $.post('/config/set',data,function(rdata){ -// layer.closeAll(); -// layer.msg(rdata.msg,{icon:rdata.status?1:2}); -// if(rdata.status){ -// setTimeout(function(){ -// window.location.href = ((window.location.protocol.indexOf('https') != -1)?'https://':'http://') + rdata.data.host + window.location.pathname; -// },2500); -// } -// },'json'); -// }); - -$('input[name="webname"]').change(function(){ - var webname = $(this).val(); - $('.btn_webname').removeAttr('disabled'); - $('.btn_webname').unbind().click(function(){ - $.post('/config/set_webname','webname='+webname, function(rdata){ - showMsg(rdata.msg,function(){window.location.reload();},{icon:rdata.status?1:2},2000); - },'json'); - }); -}); - - -$('input[name="host_ip"]').change(function(){ - var host_ip = $(this).val(); - $('.btn_host_ip').removeAttr('disabled'); - $('.btn_host_ip').unbind().click(function(){ - $.post('/config/set_ip','host_ip='+host_ip, function(rdata){ - showMsg(rdata.msg,function(){window.location.reload();},{icon:rdata.status?1:2},2000); - },'json'); - }); -}); - -$('input[name="port"]').change(function(){ - var port = $(this).val(); - var old_port = $(this).data('port'); - $('.btn_port').removeAttr('disabled'); - $('.btn_port').unbind().click(function(){ - $.post('/config/set_port','port='+port, function(rdata){ - showMsg(rdata.msg,function(){ - window.location.href = window.location.href.replace(old_port,port); - // window.location.reload(); - },{icon:rdata.status?1:2},5000); - },'json'); - }); -}); - -$('input[name="sites_path"]').change(function(){ - var sites_path = $(this).val(); - $('.btn_sites_path').removeAttr('disabled'); - $('.btn_sites_path').unbind().click(function(){ - $.post('/config/set_www_dir','sites_path='+sites_path, function(rdata){ - showMsg(rdata.msg,function(){window.location.reload();},{icon:rdata.status?1:2},2000); - },'json'); - }); -}); - - -$('input[name="backup_path"]').change(function(){ - var backup_path = $(this).val(); - $('.btn_backup_path').removeAttr('disabled'); - $('.btn_backup_path').unbind().click(function(){ - $.post('/config/set_backup_dir','backup_path='+backup_path, function(rdata){ - showMsg(rdata.msg,function(){window.location.reload();},{icon:rdata.status?1:2},2000); - },'json'); - }); -}); - - -$('input[name="bind_domain"]').change(function(){ - var domain = $(this).val(); - $('.btn_bind_domain').removeAttr('disabled'); - $('.btn_bind_domain').unbind().click(function(){ - $.post('/config/set_panel_domain','domain='+domain, function(rdata){ - showMsg(rdata.msg,function(){ - window.location.href = rdata.data; - },{icon:rdata.status?1:2},5000); - },'json'); - }); -}); - -$('input[name="bind_ssl"]').click(function(){ - var panel_ssl = $(this).prop("checked"); - $(this).prop("checked",!panel_ssl); - - //开启证书 - if (panel_ssl){ - // - layer.open({ - type:1, - closeBtn: 1, - title:"开启SSL证书", - area: ['600px','440px'], - btn: ["开启SSL证书访问"], - maxmin:false, - shadeClose: true, - content: '
\ -
\ -

【开启SSL证书】保护面板访问安全

\ -
\ -
    \ -
  • 自签证书访问步骤:
  • \ -
  • 1. 部署SSL证书
  • \ -
  • 2. 浏览器地址栏修改为https://访问
  • \ -
  • 3. 如提醒风险(正常现象)点击【高级】或【详情】
  • \ -
  • 4.【继续访问】或【接收风险并继续】
  • \ -
\ -
\ -
\ - 类型\ -
\ - \ -
\ -
\ -
    \ -
  • 开启后导致面板不能访问,可以点击查看
  • \ -
  • 自签证书不被浏览器信任,显示不安全是正常现象
  • \ -
\ -
\ -
', - yes: function(){ - - var cert_type = $('select[name=cert_type]').val(); - $.post('/config/set_panel_local_ssl',{'cert_type':cert_type}, function(rdata){ - // console.log(rdata); - var to_https = window.location.href.replace('http','https'); - showMsg(rdata.msg,function(){ - if (rdata.status){ - window.location.href = to_https; - } - },{icon:rdata.status?1:2},5000); - },'json'); - - } - }); - } else { - //关闭SSL - layer.open({ - type:1, - closeBtn: 1, - title:"关闭SSL证书", - area: ['480px','280px'], - btn: ["确定","取消"], - shadeClose: true, - content: '
\ -
\ -
关闭SSL极易被抓包攻击导致账号密码泄露,请勿关闭
\ -
\ -
\ -
请手动输入【我要关闭】,完成验证
\ - \ -
\ -
', - yes: function(index){ - var val = $('#prompt_input_box').val(); - if (val != '我要关闭'){ - layer.msg("关闭SSL失败!"); - return; - } - - $.post('/config/close_panel_ssl',{}, function(rdata){ - var to_http = window.location.href.replace('https','http'); - showMsg(rdata.msg,function(){ - if (rdata.status){ - window.location.href = to_http; - } - },{icon:rdata.status?1:2},5000); - },'json'); - } - }); - } - -}); - -/** op **/ - - -// VIP -- start -function setVipInfo(){ - layer.open({ - type: 1, - area: "400px", - title: 'VIP登录', - closeBtn: 1, - shift: 5, - btn:["登录","关闭"], - shadeClose: false, - content: "
\ -
\ - 用户名\ -
\ -
\ -
\ - 密码\ -
\ -
\ -
", - yes:function(index){ - var pdata = {}; - - pdata['username'] = $('input[name="username"]').val(); - pdata['password'] = $('input[name="password"]').val(); - - if (pdata['username'] == ''){ - layer.msg('用户名不能为空!', {icon:2}); - return false; - } - - if (pdata['password'] == ''){ - layer.msg('密码不能为空!', {icon:2}); - return false; - } - - $.post('/vip/login',{'username':pdata['username'], 'password':pdata['password']},function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){ - layer.close(index); - } - },{icon:rdata.status?1:2},2000); - },'json'); - }, - }); -} -// VIP -- end - - -//关闭面板 -function closePanel(){ - layer.confirm('关闭面板会导致您无法访问面板 ,您真的要关闭Linux面板吗?',{title:'关闭面板',closeBtn:2,icon:13,cancel:function(){ - $("#closePl").prop("checked",false); - }}, function() { - $.post('/config/close_panel','',function(rdata){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - setTimeout(function(){ - window.location.reload(); - },1000); - },'json'); - },function(){ - $("#closePl").prop("checked",false); - }); -} - -//开发模式 -function debugMode(){ - var loadT = layer.msg('正在发送请求,请稍候...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post('/config/open_debug', {}, function (rdata) { - layer.close(loadT); - showMsg(rdata.msg, function(){ - window.location.reload(); - } ,{icon:rdata.status?1:2}, 1000); - },'json'); -} - - -function modifyAuthPath() { - var auth_path = $("#admin_path").val(); - layer.open({ - type: 1, - area: "500px", - title: "修改安全入口", - closeBtn: 1, - shift: 5, - btn:['提交','关闭'], - shadeClose: false, - content: '
\ -
\ - 入口地址\ -
\ - \ -
\ -
\ -
', - yes:function(index){ - var auth_path = $("input[name='auth_path_set']").val(); - if (auth_path == '/' || auth_path == ''){ - layer.confirm('警告,关闭安全入口等于直接暴露你的后台地址在外网,十分危险, 您真的要这样更改吗?',{title:'安全入口修改',closeBtn:1,icon:13, - cancel:function(){ - }}, function() { - var loadT = layer.msg(lan.config.config_save, { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post('/config/set_admin_path', { admin_path: auth_path }, function (rdata) { - showMsg(rdata.msg, function(){ - layer.close(index); - layer.close(loadT); - $("#admin_path").val(auth_path); - },{ icon: rdata.status ? 1 : 2 }, 2000); - },'json'); - }); - return; - } else { - var loadT = layer.msg(lan.config.config_save, { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post('/config/set_admin_path', { admin_path: auth_path }, function (rdata) { - showMsg(rdata.msg, function(){ - layer.close(index); - layer.close(loadT); - $("#admin_path").val(auth_path); - },{ icon: rdata.status ? 1 : 2 }, 2000); - },'json'); - } - } - }); -} - -function setPassword(a) { - if(a == 1) { - p1 = $("#p1").val(); - p2 = $("#p2").val(); - if(p1 == "" || p1.length < 8) { - layer.msg('面板密码不能少于8位!', {icon: 2}); - return - } - - //准备弱口令匹配元素 - var checks = ['admin888','123123123','12345678','45678910','87654321','asdfghjkl','password','qwerqwer']; - pchecks = 'abcdefghijklmnopqrstuvwxyz1234567890'; - for(var i=0;i\ -
\ - 密码\ -
\ -
\ -
\ - 重复\ -
\ -
\ -
\ - 随机\ - \ - \ -
\ -
" - }); -} - - -function randPwd(){ - var pwd = randomStrPwd(12); - $("#p1").val(pwd); - $("#p2").val(pwd); - layer.msg(lan.bt.pass_rep_ps,{time:2000}) -} - -function setUserName(a) { - if(a == 1) { - p1 = $("#p1").val(); - p2 = $("#p2").val(); - if(p1 == "" || p1.length < 3) { - layer.msg('用户名长度不能少于3位', {icon: 2}); - return; - } - if(p1 != p2) { - layer.msg('两次输入的用户名不一致', {icon: 2}); - return; - } - $.post("/config/set_name", "name1=" + encodeURIComponent(p1) + "&name2=" + encodeURIComponent(p2), function(b) { - if(b.status) { - layer.closeAll(); - layer.msg(b.msg, {icon: 1}); - $("input[name='username_']").val(p1) - } else { - layer.msg(b.msg, {icon: 2}); - } - },'json'); - return - } - layer.open({ - type: 1, - area: "290px", - title: '修改面板用户名', - closeBtn: 1, - shift: 5, - shadeClose: false, - content: "
\ -
用户名\ -
\ -
\ -
\ - 重复\ -
\ -
\ -
\ - \ - \ -
\ -
" - }) -} - -function setTimezone(){ - layer.open({ - type: 1, - area: ["400px","200px"], - title: '设置服务器时区', - closeBtn: 1, - shift: 5, - shadeClose: false, - btn:["确定","取消","同步"], - content: "
\ -
\ - 时区\ -
\ - \ -
\ -
\ -
", - success:function(){ - var tbody = ''; - $.post('/config/get_timezone_list', {}, function (rdata) { - for (var i = 0; i < rdata.length; i++) { - - if (rdata[i] == 'Asia/Shanghai'){ - tbody += ''; - } else { - tbody += ''; - } - - } - $('select[name="timezone"]').append(tbody); - },'json'); - }, - yes:function(index){ - var loadT = layer.msg("正在设置时区...", { icon: 16, time: 0, shade: [0.3, '#000'] }); - var timezone = $('select[name="timezone"]').val(); - $.post('/config/set_timezone', { timezone: timezone }, function (rdata) { - showMsg(rdata.msg, function(){ - layer.close(index); - layer.close(loadT); - location.reload(); - },{ icon: rdata.status ? 1 : 2 }, 2000); - },'json'); - }, - btn3:function(){ - var loadT = layer.msg('正在同步时间...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/sync_date','',function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - setTimeout(function(){window.location.reload();},1500); - },'json'); - } - }) -} - - -function setIPv6() { - var loadT = layer.msg('正在配置,请稍候...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post('/config/set_ipv6_status', {}, function (rdata) { - layer.close(loadT); - layer.msg(rdata.msg, {icon:rdata.status?1:2}); - setTimeout(function(){window.location.reload();},5000); - },'json'); -} - - -//设置面板SSL -function setPanelSSL(){ - var status = $("#sshswitch").prop("checked")==true?1:0; - var msg = $("#panelSSL").attr('checked')?'关闭SSL后,必需使用http协议访问面板,继续吗?':'危险!此功能不懂别开启!\ -
  • 必须要用到且了解此功能才决定自己是否要开启!
  • \ -
  • 面板SSL是自签证书,不被浏览器信任,显示不安全是正常现象
  • \ -
  • 开启后导致面板不能访问,可以点击下面链接了解解决方法
  • \ -

    \ - \ -

    '; - layer.confirm(msg,{title:'设置面板SSL',closeBtn:1,icon:3,area:'550px',cancel:function(){ - $("#panelSSL").prop("checked", status == 0?false:true); - }},function(){ - if(window.location.protocol.indexOf('https') == -1){ - if(!$("#checkSSL").prop('checked')){ - layer.msg(lan.config.ssl_ps,{icon:2}); - return false; - } - } - var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/set_panel_ssl','',function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:5}); - if(rdata.status === true){ - $.post('/system/restart','',function (rdata) { - layer.close(loadT); - layer.msg(rdata.msg); - setTimeout(function(){ - window.location.href = ((window.location.protocol.indexOf('https') != -1)?'http://':'https://') + window.location.host + window.location.pathname; - },3000); - },'json'); - } - },'json'); - },function(){ - if(status == 0){ - $("#panelSSL").prop("checked",false); - }else{ - $("#panelSSL").prop("checked",true); - } - }); -} - -function setNotifyApi(tag, obj){ - var enable = $(obj).prop("checked"); - // console.log(tag,obj,enable); - $.post('/config/set_notify_enable', {'tag':tag, 'enable':enable},function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){} - } ,{icon:rdata.status?1:2}, 1000); - },'json'); -} - -function getTgbot(){ - var loadT = layer.msg('正在获取TgBot信息...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/get_notify',{},function(data){ - layer.close(loadT); - - var app_token = ''; - var chat_id = ''; - - if (data.status){ - if (typeof(data['data']['tgbot']) !='undefined'){ - app_token = data['data']['tgbot']['data']['app_token']; - chat_id = data['data']['tgbot']['data']['chat_id']; - } - } - - layer.open({ - type: 1, - area: "500px", - title: 'TgBot配置', - closeBtn: 1, - shift: 5, - btn:["确定","关闭","验证"], - shadeClose: false, - content: "
    \ -
    \ - APP_TOKEN\ -
    \ -
    \ -
    \ - CHAT_ID\ -
    \ -
    \ -
    ", - yes:function(index){ - var pdata = {}; - pdata['app_token'] = $('input[name="app_token"]').val(); - pdata['chat_id'] = $('input[name="chat_id"]').val(); - - if (pdata['app_token'] == ''){ - layer.msg('app_token不能为空!', {icon:2}); - return false; - } - - if (pdata['chat_id'] == ''){ - layer.msg('chat_id不能为空!', {icon:2}); - return false; - } - - $.post('/config/set_notify',{'tag':'tgbot', 'data':JSON.stringify(pdata)},function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){ - layer.close(index); - } - },{icon:rdata.status?1:2},2000); - }); - }, - - btn3:function(index){ - var pdata = {}; - pdata['app_token'] = $('input[name="app_token"]').val(); - pdata['chat_id'] = $('input[name="chat_id"]').val(); - - if (pdata['app_token'] == ''){ - layer.msg('app_token不能为空!', {icon:2}); - return false; - } - - if (pdata['chat_id'] == ''){ - layer.msg('chat_id不能为空!', {icon:2}); - return false; - } - - $.post('/config/set_notify_test',{'tag':'tgbot', 'data':JSON.stringify(pdata)},function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){ - layer.close(index); - } - },{icon:rdata.status?1:2},2000); - }); - return false; - } - }); - }); -} - -function getEmailCfg(){ - var loadT = layer.msg('正在获取邮件配置信息...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/get_notify',{},function(data){ - layer.close(loadT); - - var smtp_host = 'smtp.163.com'; - var smtp_port = '25'; - var username = 'admin'; - var password = ''; - var to_mail_addr = ''; - - var smtp_ssl_no = 'checked'; - var smtp_ssl_yes = ''; - - if (data.status){ - if (typeof(data['data']['email']) !='undefined'){ - smtp_host = data['data']['email']['data']['smtp_host']; - smtp_port = data['data']['email']['data']['smtp_port']; - username = data['data']['email']['data']['username']; - password = data['data']['email']['data']['password']; - to_mail_addr = data['data']['email']['data']['to_mail_addr']; - - - var smtp_ssl = data['data']['email']['data']['smtp_ssl']; - if (smtp_ssl == 'ssl'){ - smtp_ssl_no = ''; - smtp_ssl_yes = 'checked'; - } - } - } - - layer.open({ - type: 1, - area: "500px", - title: '邮件配置', - closeBtn: 1, - shift: 5, - btn:["确定","关闭","验证"], - shadeClose: false, - content: "
    \ -
    \ - SMTP服务器\ -
    \ -
    \ -
    \ - SMTP安全\ -
    \ - \ - \ -
    \ -
    \ -
    \ - SMTP端口\ -
    \ -
    \ -
    \ - 用户名\ -
    \ -
    \ -
    \ - 授权码\ -
    \ -
    \ -
    \ - 发送地址\ -
    \ -
    \ -
    \ - 验证测试\ -
    \ -
    \ -
    \ -
    ", - yes:function(index){ - var pdata = {}; - pdata['smtp_host'] = $('input[name="smtp_host"]').val(); - pdata['smtp_port'] = $('input[name="smtp_port"]').val(); - pdata['smtp_ssl'] = $('input[name="smtp_ssl"]:checked').val(); - pdata['username'] = $('input[name="username"]').val(); - pdata['password'] = $('input[name="password"]').val(); - pdata['to_mail_addr'] = $('input[name="to_mail_addr"]').val(); - - if (pdata['smtp_host'] == ''){ - layer.msg('SMTP服务器不能为空!', {icon:2}); - return false; - } - - if (pdata['smtp_port'] == ''){ - layer.msg('SMTP端口不能为空!', {icon:2}); - return false; - } - - if (pdata['username'] == ''){ - layer.msg('用户名不能为空!', {icon:2}); - return false; - } - - if (pdata['password'] == ''){ - layer.msg('授权码不能为空!', {icon:2}); - return false; - } - - if (pdata['to_mail_addr'] == ''){ - layer.msg('发送地址不能为空!', {icon:2}); - return false; - } - - $.post('/config/set_notify',{'tag':'email', 'data':JSON.stringify(pdata)},function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){ - layer.close(index); - } - },{icon:rdata.status?1:2},2000); - }); - }, - - btn3:function(index){ - var pdata = {}; - pdata['smtp_host'] = $('input[name="smtp_host"]').val(); - pdata['smtp_port'] = $('input[name="smtp_port"]').val(); - pdata['smtp_ssl'] = $('input[name="smtp_ssl"]:checked').val(); - pdata['username'] = $('input[name="username"]').val(); - pdata['password'] = $('input[name="password"]').val(); - pdata['to_mail_addr'] = $('input[name="to_mail_addr"]').val(); - pdata['mail_test'] = $('textarea[name="mail_test"]').val(); - - - if (pdata['smtp_host'] == ''){ - layer.msg('SMTP服务器不能为空!', {icon:2}); - return false; - } - - if (pdata['smtp_port'] == ''){ - layer.msg('SMTP端口不能为空!', {icon:2}); - return false; - } - - if (pdata['username'] == ''){ - layer.msg('用户名不能为空!', {icon:2}); - return false; - } - - if (pdata['password'] == ''){ - layer.msg('授权码不能为空!', {icon:2}); - return false; - } - - if (pdata['to_mail_addr'] == ''){ - layer.msg('发送地址不能为空!', {icon:2}); - return false; - } - - if (pdata['mail_test'] == ''){ - layer.msg('验证测试不能为空!', {icon:2}); - return false; - } - $.post('/config/set_notify_test',{'tag':'email', 'data':JSON.stringify(pdata)},function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){ - layer.close(index); - } - },{icon:rdata.status?1:2},2000); - }); - return false; - } - }); - }); -} - -function getPanelSSL(){ - var loadT = layer.msg('正在获取证书信息...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/get_panel_ssl',{},function(cert){ - layer.close(loadT); - - // console.log(cert); - var choose = cert['choose']; - var choose_local = ''; - var choose_nginx = ''; - - if (choose == 'local'){ - cert = cert['local']; - choose_local = 'selected="selected"'; - } else if (choose == 'nginx') { - cert = cert['nginx']; - choose_nginx = 'selected="selected"'; - } else { - cert = cert['local']; - } - - var cert_data = ''; - - //
    \ - // 强制HTTPS:\ - // \ - // \ - // \ - // \ - //
    \ - if (cert['info']){ - cert_data = "
    \ -
    证书品牌:\ - "+cert['info']['issuer']+"
    \ -
    到期时间:\ - 剩余"+cert['info']['endtime']+"天到期
    \ -
    \ -
    \ -
    认证域名:\ - "+cert['info']['subject']+"
    \ -
    "; - } - - // \ - // \ - var certBody = '
    \ -
    \ - '+cert_data+'\ -
    \ -
    密钥(KEY)
    \ - \ -
    \ -
    证书(PEM格式)
    \ - \ -
    \ -
    \ -
    \ - \ - \ - \ -
    \ -
    \ -
      \ -
    • 粘贴您的*.key以及*.pem内容,然后保存即可。
    • \ -
    • 如果浏览器提示证书链不完整,请检查是否正确拼接PEM证书
    • PEM格式证书 = 域名证书.crt + 根证书(root_bundle).crt
    • \ -
    \ -
    ' - layer.open({ - type: 1, - area: "600px", - title: '自定义面板证书', - closeBtn: 1, - shift: 5, - shadeClose: false, - content:certBody, - success:function(layero, layer_id){ - - - //保存SSL - $('.save-panel-ssl').click(function(){ - var data = { - privateKey:$("#key").val(), - certPem:$("#csr").val() - } - - layer.confirm('选择保存面板SSL方式?', - { - title:'提示', - shade:0.001, - btn: ['本地SSL', '取消'],//'OpenResty' - btn3:function(){ - data['choose'] = 'nginx'; - var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/save_panel_ssl',data,function(rdata){ - layer.close(loadT); - if(rdata.status){ - layer.closeAll(); - } - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - }, - }, - function(index) { - data['choose'] = 'local'; - var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/save_panel_ssl',data,function(rdata){ - layer.close(loadT); - if(rdata.status){ - layer.closeAll(); - } - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - }, - function(index) { - layer.close(index); - }); - }); - - //删除SSL - $('.del-panel-ssl').click(function(){ - - layer.confirm('选择删除面板SSL方式?', - { - title:'提示', - shade:0.001, - btn: ['本地SSL', '取消'],//, 'OpenResty' - btn3:function(){ - var data = {}; - data['choose'] = 'nginx'; - var loadT = layer.msg('正在删除面板SSL【nginx】...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/del_panel_ssl',data,function(rdata){ - layer.close(loadT); - if(rdata.status){ - layer.closeAll(); - } - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - }, - }, - function(index) { - var data = {}; - data['choose'] = 'local'; - var loadT = layer.msg('正在删除面板SSL【本地】...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/del_panel_ssl',data,function(rdata){ - console.log(rdata); - layer.close(loadT); - showMsg(rdata.msg, function(){ - if(rdata.status){ - location.href = rdata.data; - } - },{icon:rdata.status?1:2},3000); - },'json'); - }, - function(index) { - layer.close(index); - }); - - - }); - - // // 设置面板SSL的Http - // $('.set_panel_http_to_https').click(function(){ - // var https = $('#toHttps').prop('checked'); - // $.post('/config/set_panel_http_to_https',{'https':https},function(rdata){ - // layer.close(loadT); - // if(rdata.status){ - // layer.closeAll(); - // } - // layer.msg(rdata.msg,{icon:rdata.status?1:2}); - // },'json'); - // }); - - // //申请Lets证书 - // $('.apply-lets-ssl').click(function(){ - // showSpeedWindow('正在申请...', 'site.get_let_logs', function(layers,index){ - // $.post('/config/apply_panel_acme_ssl',{},function(rdata){ - // layer.close(loadT); - // if(rdata.status){ - // layer.close(index); - // var tdata = rdata['data']; - // $('.ssl_issue').text(tdata['info']['issuer']); - // $('.ssl_endtime').text("剩余"+tdata['info']['endtime']+"天到期"); - // $('.ssl_subject').text(tdata['info']['subject']); - - // $('textarea[name="key"]').val(tdata['info']['privateKey']); - // $('textarea[name="csr"]').val(tdata['info']['certPem']); - // } - // layer.msg(rdata.msg,{icon:rdata.status?1:2}); - // },'json'); - // }); - // }); - } - }); - },'json'); -} - - -function removeTempAccess(id){ - $.post('/config/remove_temp_login', {id:id}, function(rdata){ - showMsg(rdata.msg, function(){ - setTempAccessReq(); - },{ icon: rdata.status ? 1 : 2 }, 2000); - },'json'); -} - -function getTempAccessLogsReq(id){ - $.post('/config/get_temp_login_logs', {id:id}, function(rdata){ - var tbody = ''; - for (var i = 0; i < rdata.data.length; i++) { - - tbody += ''; - tbody += '' + (rdata.data[i]['type']) +''; - tbody += '' + rdata.data[i]['addtime'] +''; - tbody += ''+ rdata.data[i]['log'] +''; - tbody += ''; - } - $('#logs_list').html(tbody); - - },'json'); -} - -function getTempAccessLogs(id){ - - layer.open({ - area: ['700px', '250px'], - title: '临时授权管理', - closeBtn:1, - shift: 0, - type: 1, - content: "
    \ - \ -
    \ - \ - \ - \ - \ - \ -
    操作类型操作时间日志
    \ -
    \ -
    ", - success:function(){ - getTempAccessLogsReq(id); - $('.refresh_log').click(function(){ - getTempAccessLogsReq(id); - }); - } - }); -} - -function setTempAccessReq(page){ - if (typeof(page) == 'undefined'){ - page = 1; - } - - $.post('/config/get_temp_login', {page:page}, function(rdata){ - if ( typeof(rdata.status) !='undefined' && !rdata.status){ - showMsg(rdata.msg,function(){ - layer.closeAll(); - },{icon:2}, 2000); - return; - } - - var tbody = ''; - for (var i = 0; i < rdata.data.length; i++) { - - tbody += ''; - tbody += '' + (rdata.data[i]['login_addr']||'未登陆') +''; - - tbody += ''; - switch (parseInt(rdata.data[i]['state'])) { - case 0: - tbody += '待使用'; - break; - case 1: - tbody += '已使用'; - break; - case -1: - tbody += '已过期'; - break; - } - tbody += ''; - - tbody += '' + (getLocalTime(rdata.data[i]['login_time'])||'未登陆') +''; - tbody += '' + getLocalTime(rdata.data[i]['expire']) +''; - - tbody += ''; - - if (rdata.data[i]['state'] == '1' ){ - tbody += '操作日志'; - } else{ - tbody += '删除'; - } - - tbody += ''; - - tbody += ''; - } - - $('#temp_login_view_tbody').html(tbody); - $('.temp_login_view_page').html(rdata.page); - },'json'); -} - -function setStatusCode(o){ - var code = $(o).data('code'); - layer.open({ - type: 1, - area: ['420px', '220px'], - title: "设置未认证时的响应状态", - closeBtn: 1, - shift: 5, - btn:['提交','关闭'], - shadeClose: false, - content: '
    \ -
    \ - 相应状态\ -
    \ - \ -
    \ -
    \ -
    • 用于未登录且未正确输入安全入口时的响应,用于隐藏面板特征
    \ -
    ', - success:function(){ - var msg_list = [ - {'code':'0','msg':'默认-安全入口错误提示'}, - {'code':'403','msg':'403-拒绝访问'}, - {'code':'404','msg':'404-页面不存在'}, - {'code':'416','msg':'416-无效的请求'}, - {'code':'408','msg':'408-客户端超时'}, - {'code':'400','msg':'400-客户端请求错误'}, - {'code':'401','msg':'401-未授权访问'}, - ]; - - var tbody = ''; - for(i in msg_list){ - if (msg_list[i]['code'] == code){ - tbody += ''; - } else{ - tbody += ''; - } - - } - $('select[name="status_code"]').append(tbody); - }, - yes:function(index){ - var loadT = layer.msg("正在设置未认证时的响应状态", { icon: 16, time: 0, shade: [0.3, '#000'] }); - var status_code = $('select[name="status_code"]').val(); - $.post('/config/set_status_code', { status_code: status_code }, function (rdata) { - showMsg(rdata.msg, function(){ - layer.close(index); - layer.close(loadT); - location.reload(); - },{ icon: rdata.status ? 1 : 2 }, 2000); - },'json'); - } - }); -} - -function setTempAccess(){ - layer.open({ - area: ['700px', '250px'], - title: '临时授权管理', - closeBtn:1, - shift: 0, - type: 1, - content: "
    \ - \ -
    \ - \ - \ - \ - \ - \ -
    登录IP状态登录时间过期时间操作
    \ - \ -
    \ -
    ", - success:function(){ - setTempAccessReq(); - - $('.create_temp_login').click(function(){ - layer.confirm('注意1:滥用临时授权可能导致安全风险。
    注意2:请勿在公共场合发布临时授权连接

    即将创建临时授权连接,继续吗?', - { - title:'风险提示', - closeBtn:1, - icon:13, - }, function(create_temp_login_layer) { - $.post('/config/set_temp_login', {}, function(rdata){ - layer.close(create_temp_login_layer); - setTempAccessReq(); - layer.open({ - area: '570px', - title: '创建临时授权', - shift: 0, - type: 1, - content: "
    \ -
    \ - 临时授权地址\ -
    \ - \ -
    \ -
    \ -
    \ -
      \ -
    • 临时授权生成后1小时内使用有效,为一次性授权,使用后立即失效
    • \ -
    • 使用临时授权登录面板后1小时内拥有面板所有权限,请勿在公共场合发布临时授权连接
    • \ -
    • 授权连接信息仅在此处显示一次,若在使用前忘记,请重新生成
    • \ -
    \ -
    ", - success:function(){ - var temp_link = "".concat(location.origin, "/login?tmp_token=").concat(rdata.token); - $('#temp_link').val(temp_link); - - copyText(temp_link); - $('.btn-copy-temp-link').click(function(){ - copyText(temp_link); - }); - } - }); - },'json'); - }); - }); - } - }); -} - -//二次验证 -function setAuthBind(){ - $.post('/config/get_auth_secret', {}, function(rdata){ - console.log(rdata); - var tip = layer.open({ - area: ['500px', '355px'], - title: '二次验证设置', - closeBtn:1, - shift: 0, - type: 1, - content: '
    \ -
    \ - 绑定密钥\ -
    \ - \ - \ -
    \ -
    \ -
    \ - 二维码\ -
    \ -
    \ -
      \ -
    \ -
    ', - success:function(layero,index){ - - $('input[name="secret"]').val(rdata.data['secret']); - $('.qrcode').qrcode({ text: rdata.data['url']}); - - $('.reset_secret').click(function(){ - layer.confirm('您确定要重置当前密钥吗?
    重置密钥后,已关联密钥产品,将失效,请重新添加新密钥至产品。',{title:'重置密钥',closeBtn:2,icon:13,cancel:function(){ - }}, function() { - $.post('/config/get_auth_secret', {'reset':"1"},function(rdata){ - showMsg("接口密钥已生成,重置密钥后,已关联密钥产品,将失效,请重新添加新密钥至产品。", function(){ - $('input[name="secret"]').val(rdata.data['secret']); - $('.qrcode').html('').qrcode({ text: rdata.data['url']}); - } ,{icon:1}, 2000); - },'json'); - }); - }); - }, - }); - - },'json'); -} - -function setAuthSecretApi(){ - var cfg_panel_auth = $('#cfg_panel_auth').prop("checked"); - $.post('/config/set_auth_secret', {'op_type':"2"},function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.data == 1){ - setAuthBind(); - } - } ,{icon:rdata.status?1:2}, 1000); - },'json'); -} - -function setBasicAuthTip(callback){ - var tip = layer.open({ - area: ['500px', '385px'], - title: '开启BasicAuth认证提示', - closeBtn:0, - shift: 0, - type: 1, - content: '
    \ -
    \ -

    风险操作!此功能不懂请勿开启!

    \ -
    \ -
      \ -
    • 必须要用到且了解此功能才决定自己是否要开启!
    • \ -
    • 开启后,以任何方式访问面板,将先要求输入BasicAuth用户名和密码
    • \ -
    • 开启后,能有效防止面板被扫描发现,但并不能代替面板本身的帐号密码
    • \ -
    • 请牢记BasicAuth密码,一但忘记将无法访问面板
    • \ -
    • 如忘记密码,可在SSH通过mw命令来关闭BasicAuth验证
    • \ -
    \ -
    \ - \ - \ -
    \ -
    ', - btn:["确定","取消"], - yes:function(l,index){ - is_agree = $('#agreement_more').prop("checked"); - if (is_agree){ - layer.close(tip); - callback(); - } - return is_agree; - }, - btn2: function(index, layero){ - $('#cfg_basic_auth').prop("checked", false); - } - }); -} - -function setBasicAuth(){ - - var basic_auth = $('#cfg_basic_auth').prop("checked"); - if (!basic_auth){ - setBasicAuthTip(function(){ - var tip = layer.open({ - area: ['500px', '385px'], - title: '配置BasicAuth认证', - closeBtn:1, - shift: 0, - type: 1, - content: '
    \ -
    \ - 用户名\ -
    \ -
    \ -
    \ - 密码\ -
    \ -
    \ -
    \ - \ -
    \ -
    \ -
      \ -
    • 注意:请不要在这里使用您的常用密码,这可能导致密码泄漏!
    • \ -
    • 开启后,以任何方式访问面板,将先要求输入BasicAuth用户名和密码
    • \ -
    • 开启后,能有效防止面板被扫描发现,但并不能代替面板本身的帐号密码
    • \ -
    • 请牢记BasicAuth密码,一但忘记将无法访问面板
    • 如忘记密码,可在SSH通过mw命令来关闭BasicAuth验证
    • \ -
    \ -
    ', - success:function(){ - $('.save_auth_cfg').click(function(){ - var basic_user = $('input[name="basic_user"]').val(); - var basic_pwd = $('input[name="basic_pwd"]').val(); - $.post('/config/set_basic_auth', {'basic_user':basic_user,'basic_pwd':basic_pwd},function(rdata){ - showMsg(rdata.msg, function(){ - window.location.reload(); - } ,{icon:rdata.status?1:2}, 2000); - },'json'); - }); - }, - cancel:function(){ - $('#cfg_basic_auth').prop("checked", false); - }, - }); - }); - } else { - layer.confirm('关闭BasicAuth认证后,面板登录将不再验证BasicAuth基础认证,这将会导致面板安全性下降,继续操作!', - {btn: ['确定', '取消'], title: "是否关闭BasicAuth认证?", icon:13}, function (index) { - var basic_user = ''; - var basic_pwd = ''; - $.post('/config/set_basic_auth', {'is_open':'false'},function(rdata){ - showMsg(rdata.msg, function(){ - layer.close(index); - window.location.reload(); - } ,{icon:rdata.status?1:2}, 2000); - },'json'); - },function(){ - $('#cfg_basic_auth').prop("checked", true); - }); - } -} - -function showPanelApi(){ - $.post('/config/get_panel_token', '', function(rdata){ - var tip = layer.open({ - area: ['500px', '355px'], - title: '配置面板API', - closeBtn:1, - shift: 0, - type: 1, - content: '
    \ -
    \ - 接口密钥\ -
    \ - \ - \ -
    \ -
    \ -
    \ - IP白名单
    (每行1个)
    \ -
    \ -
    \ -
    \ - \ -
    \ -
    \ -
      \ -
    • 开启API后,必需在IP白名单列表中的IP才能访问面板API接口
    • \ -
    • 请不要在生产环境开启,这可能增加服务器安全风险;
    • \ -
    \ -
    ', - success:function(layero,index){ - - $('input[name="token"]').val(rdata.data.token); - $('textarea[name="api_limit_addr"]').val(rdata.data.limit_addr); - - - $('.reset_token').click(function(){ - layer.confirm('您确定要重置当前密钥吗?
    重置密钥后,已关联密钥产品,将失效,请重新添加新密钥至产品。',{title:'重置密钥',closeBtn:2,icon:13,cancel:function(){ - }}, function() { - $.post('/config/set_panel_token', {'op_type':"1"},function(rdata){ - showMsg("接口密钥已生成,重置密钥后,已关联密钥产品,将失效,请重新添加新密钥至产品。", function(){ - $('input[name="token"]').val(rdata.data); - } ,{icon:1}, 2000); - },'json'); - }); - }); - - $('.save_api').click(function(){ - var limit_addr = $('textarea[name="api_limit_addr"]').val(); - $.post('/config/set_panel_token', {'op_type':"3",'limit_addr':limit_addr},function(rdata){ - showMsg(rdata.msg, function(){ - } ,{icon:rdata.status?1:2}, 2000); - },'json'); - }); - }, - }); - },'json'); -} - - -function setPanelApi(){ - var cfg_panel_api = $('#cfg_panel_api').prop("checked"); - $.post('/config/set_panel_token', {'op_type':"2"},function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){ - showPanelApi(); - } - } ,{icon:rdata.status?1:2}, 1000); - },'json'); -} diff --git a/route/static/app/control.js b/route/static/app/control.js deleted file mode 100755 index 4b68cd96b..000000000 --- a/route/static/app/control.js +++ /dev/null @@ -1,867 +0,0 @@ -//默认显示7天周期图表 - -setTimeout(function(){ - Wday(0,'getload'); -},100); -setTimeout(function(){ - Wday(0,'cpu'); -},200); -setTimeout(function(){ - Wday(0,'mem'); -},500); -setTimeout(function(){ - Wday(0,'disk'); -},100); -setTimeout(function(){ - Wday(0,'network'); -},1500); - - -$(".searcTime .st").click(function(){ - var status = $(this).data('status'); - if (status == 'show'){ - $(this).next().hide(); - $(this).data('status','hide'); - } else{ - $(this).next().show(); - $(this).data('status','show'); - } -}); - -//自定义时间-切换 -$(".searcTime .st").hover(function(){ - $(this).data('status','show'); - $(this).next().show(); -},function(){ - // $(this).next().hide(); - // $(this).next().hover(function(){ - // $(this).show(); - // },function(){ - // $(this).hide(); - // }) -}) - -$(".searcTime .gt").click(function(){ - $(this).addClass("on").siblings().removeClass("on"); -}) - - -//渲染日期时间范围 start -var render_dlist = [ - 'loadbtn_rtime', - 'cpubtn_rtime', - 'membtn_rtime', - 'diskbtn_rtime', - 'networkbtn_rtime' -]; - -for (var i = 0; i < render_dlist.length; i++) { - - laydate.render({ - elem: '#'+render_dlist[i] - ,type: 'datetime' - ,range: true - }); - - - var b = getBeforeDate(28).replaceAll('/','-') + " 00:00:00"; - var e = getBeforeDate(0).replaceAll('/','-') + " 23:59:59"; - - $('#'+render_dlist[i]).val(b + ' - ' + e); -} -//渲染日期时间范围 end - - -$('.sbtn').click(function(){ - $(".searcTime .st").next().hide(); - - var rtime = $(this).parent().find(".rtime").val(); - var rarr = rtime.split(' - '); - - var b = (new Date(rarr[0]).getTime())/1000; - var e = (new Date(rarr[1]).getTime())/1000; - - b = Math.round(b); - e = Math.round(e); - - if ($(this).hasClass('loadbtn')){ - getload(b,e); - } else if ($(this).hasClass('cpubtn')){ - cpu(b,e); - }else if ($(this).hasClass('membtn')){ - mem(b,e); - } else if ($(this).hasClass('diskbtn')){ - disk(b,e); - } -}); - -//指定天数 -function Wday(day,name){ - var now = (new Date().getTime())/1000; - if(day==0){ - var b = (new Date(getToday() + " 00:00:01").getTime())/1000; - b = Math.round(b); - var e = Math.round(now); - } - if(day==1){ - var b = (new Date(getBeforeDate(day) + " 00:00:01").getTime())/1000; - var e = (new Date(getBeforeDate(day) + " 23:59:59").getTime())/1000; - b = Math.round(b); - e = Math.round(e); - } - else{ - var b = (new Date(getBeforeDate(day) + " 00:00:01").getTime())/1000; - b = Math.round(b); - var e = Math.round(now); - } - switch (name){ - case "cpu": - cpu(b, e); - break; - case "mem": - mem(b, e); - break; - case "disk": - disk(b, e); - break; - case "network": - network(b, e); - break; - case "getload": - getload(b, e); - break; - } -} - -function getToday(){ - var mydate = new Date(); - var str = "" + mydate.getFullYear() + "/"; - str += (mydate.getMonth()+1) + "/"; - str += mydate.getDate(); - return str; -} - -getStatus(); -//取监控状态 -function getStatus(){ - loadT = layer.msg('正在读取,请稍候...',{icon:16,time:0}) - $.post('/system/set_control','type=-1',function(rdata){ - layer.close(loadT); - - if(rdata.status){ - $("#openJK").html(""); - } else { - $("#openJK").html(""); - } - - if(rdata.stat_all_status){ - $("#statAll").html(""); - } else{ - $("#statAll").html(""); - } - - $("#save_day").val(rdata.day); - - },'json'); -} - - -//设置监控状态 -function setControl(act, value=false){ - - if (act == 'openjk'){ - var type = $("#ctswitch").prop('checked')?'0':'1'; - var day = $("#save_day").val(); - if(day < 1){ - layer.msg('保存天数不合法!',{icon:2}); - return; - } - } else if (act == 'stat'){ - var type = $("#stat_witch").prop('checked')?'2':'3'; - } else if (act == 'save_day'){ - var type = $("#ctswitch").prop('checked')?'1':'0'; - var day = $("#save_day").val(); - - if(type == 0){ - layer.msg('先开启监控!',{icon:2}); - return; - } - - if(day < 1){ - layer.msg('保存天数不合法!',{icon:2}); - return; - } - } - - loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0}) - $.post('/system/set_control','type='+type+'&day='+day,function(rdata){ - showMsg(rdata.msg, function(){ - layer.close(loadT); - },{icon:rdata.status?1:2}) - },'json'); -} - - -//清理记录 -function closeControl(){ - layer.confirm('您真的清空所有监控记录吗?',{title:'清空记录',icon:3,closeBtn:1}, function() { - loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0}) - $.post('/system/set_control','type=del',function(rdata){ - showMsg(rdata.msg, function(){ - layer.close(loadT); - },{icon:rdata.status?1:2}) - },'json'); - }); -} - - -//定义周期时间 -function getBeforeDate(n){ - var n = n; - var d = new Date(); - var year = d.getFullYear(); - var mon=d.getMonth()+1; - var day=d.getDate(); - if(day <= n){ - if(mon>1) { - mon = mon-1; - } else { - year = year-1; - mon = 12; - } - } - d.setDate(d.getDate()-n); - year = d.getFullYear(); - mon=d.getMonth()+1; - day=d.getDate(); - s = year+"/"+(mon<10?('0'+mon):mon)+"/"+(day<10?('0'+day):day); - return s; -} -//cpu -function cpu(b,e){ - $.get('/system/get_cpu_io?start='+b+'&end='+e,function(rdata){ - var myChartCpu = echarts.init(document.getElementById('cupview')); - var xData = []; - var yData = []; - //var zData = []; - - for(var i = 0; i < rdata.length; i++){ - xData.push(rdata[i].addtime); - yData.push(rdata[i].pro); - //zData.push(rdata[i].mem); - } - option = { - tooltip: { - trigger: 'axis', - axisPointer: { type: 'cross' }, - formatter: '{b}
    {a}: {c}%' - }, - xAxis: { - type: 'category', - boundaryGap: false, - data: xData, - axisLine:{ lineStyle:{ color:"#666"} } - }, - yAxis: { - type: 'value', - name: lan.public.pre, - boundaryGap: [0, '100%'], - min:0, - max: 100, - splitLine:{ lineStyle:{ color:"#ddd" } }, - axisLine:{ lineStyle:{ color:"#666" } } - }, - dataZoom: [{ - type: 'inside', - start: 0, - end: 100, - zoomLock:true - }, { - start: 0, - end: 100, - handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z', - handleSize: '80%', - handleStyle: { - color: '#fff', - shadowBlur: 3, - shadowColor: 'rgba(0, 0, 0, 0.6)', - shadowOffsetX: 2, - shadowOffsetY: 2 - } - }], - series: [ - { - name:'CPU', - type:'line', - smooth:true, - symbol: 'none', - sampling: 'average', - itemStyle: { normal: { color: 'rgb(0, 153, 238)' } }, - data: yData - } - ] - }; - myChartCpu.setOption(option); - window.addEventListener("resize",function(){ - myChartCpu.resize(); - }); - },'json'); -} - -//内存 -function mem(b,e){ - $.get('/system/get_cpu_io?start='+b+'&end='+e,function(rdata){ - var myChartMen = echarts.init(document.getElementById('memview')); - var xData = []; - //var yData = []; - var zData = []; - - for(var i = 0; i < rdata.length; i++){ - xData.push(rdata[i].addtime); - //yData.push(rdata[i].pro); - zData.push(rdata[i].mem); - } - option = { - tooltip: { - trigger: 'axis', - axisPointer: { type: 'cross' }, - formatter: '{b}
    {a}: {c}%' - }, - xAxis: { - type: 'category', - boundaryGap: false, - data: xData, - axisLine:{ lineStyle:{ color:"#666" } } - }, - yAxis: { - type: 'value', - name: lan.public.pre, - boundaryGap: [0, '100%'], - min:0, - max: 100, - splitLine:{ lineStyle:{ color:"#ddd" } }, - axisLine:{ lineStyle:{ color:"#666" } } - }, - dataZoom: [{ - type: 'inside', - start: 0, - end: 100, - zoomLock:true - }, { - start: 0, - end: 100, - handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z', - handleSize: '80%', - handleStyle: { - color: '#fff', - shadowBlur: 3, - shadowColor: 'rgba(0, 0, 0, 0.6)', - shadowOffsetX: 2, - shadowOffsetY: 2 - } - }], - series: [ - { - name:lan.index.process_mem, - type:'line', - smooth:true, - symbol: 'none', - sampling: 'average', - itemStyle: { normal: { color: 'rgb(0, 153, 238)' } }, - data: zData - } - ] - }; - myChartMen.setOption(option); - window.addEventListener("resize",function(){ - myChartMen.resize(); - }); - },'json'); -} - -//磁盘io -function disk(b,e){ - $.get('/system/get_disk_io?start='+b+'&end='+e,function(rdata){ - var myChartDisk = echarts.init(document.getElementById('diskview')); - var rData = []; - var wData = []; - var xData = []; - //var yData = []; - //var zData = []; - - for(var i = 0; i < rdata.length; i++){ - rData.push((rdata[i].read_bytes/1024/60).toFixed(3)); - wData.push((rdata[i].write_bytes/1024/60).toFixed(3)); - xData.push(rdata[i].addtime); - //yData.push(rdata[i].read_count); - //zData.push(rdata[i].write_count); - } - option = { - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'cross' - }, - formatter:"时间:{b0}
    {a0}: {c0} Kb/s
    {a1}: {c1} Kb/s", - }, - legend: { - data:['读取字节数','写入字节数'] - }, - xAxis: { - type: 'category', - boundaryGap: false, - data: xData, - axisLine:{ lineStyle:{ color:"#666" }} - }, - yAxis: { - type: 'value', - name: '单位:KB/s', - boundaryGap: [0, '100%'], - splitLine:{ lineStyle:{ color:"#ddd"} }, - axisLine:{ lineStyle:{ color:"#666" } } - }, - dataZoom: [{ - type: 'inside', - start: 0, - end: 100, - zoomLock:true - }, { - start: 0, - end: 100, - handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z', - handleSize: '80%', - handleStyle: { - color: '#fff', - shadowBlur: 3, - shadowColor: 'rgba(0, 0, 0, 0.6)', - shadowOffsetX: 2, - shadowOffsetY: 2 - } - }], - series: [ - { - name:'读取字节数', - type:'line', - smooth:true, - symbol: 'none', - sampling: 'average', - itemStyle: { normal: { color: 'rgb(255, 70, 131)' } }, - data: rData - }, - { - name:'写入字节数', - type:'line', - smooth:true, - symbol: 'none', - sampling: 'average', - itemStyle: { normal: { color: 'rgba(46, 165, 186, .7)'} }, - data: wData - } - ] - }; - myChartDisk.setOption(option); - window.addEventListener("resize",function(){ - myChartDisk.resize(); - }); - },'json'); -} - -//网络Io -function network(b,e){ - $.get('/system/get_network_io?start='+b+'&end='+e,function(rdata){ - var myChartNetwork = echarts.init(document.getElementById('network')); - var aData = []; - var bData = []; - var cData = []; - var dData = []; - var xData = []; - var yData = []; - var zData = []; - - for(var i = 0; i < rdata.length; i++){ - aData.push(rdata[i].total_up); - bData.push(rdata[i].total_down); - cData.push(rdata[i].down_packets); - dData.push(rdata[i].up_packets); - xData.push(rdata[i].addtime); - yData.push(rdata[i].up); - zData.push(rdata[i].down); - } - option = { - tooltip: { - trigger: 'axis', - axisPointer: { type: 'cross' } - }, - legend: { - data:[lan.index.net_up,lan.index.net_down] - }, - xAxis: { - type: 'category', - boundaryGap: false, - data: xData, - axisLine:{ lineStyle:{ color:"#666" } } - }, - yAxis: { - type: 'value', - name: lan.index.unit+':KB/s', - boundaryGap: [0, '100%'], - splitLine:{ lineStyle:{ color:"#ddd" } }, - axisLine:{ lineStyle:{ color:"#666" } } - }, - dataZoom: [{ - type: 'inside', - start: 0, - end: 100, - zoomLock:true - }, { - start: 0, - end: 100, - handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z', - handleSize: '80%', - handleStyle: { - color: '#fff', - shadowBlur: 3, - shadowColor: 'rgba(0, 0, 0, 0.6)', - shadowOffsetX: 2, - shadowOffsetY: 2 - } - }], - series: [ - { - name:lan.index.net_up, - type:'line', - smooth:true, - symbol: 'none', - sampling: 'average', - itemStyle: { normal: { color: 'rgb(255, 140, 0)' } }, - data: yData - }, - { - name:lan.index.net_down, - type:'line', - smooth:true, - symbol: 'none', - sampling: 'average', - itemStyle: { normal: { color: 'rgb(30, 144, 255)' } }, - data: zData - } - ] - }; - myChartNetwork.setOption(option); - window.addEventListener("resize",function(){ - myChartNetwork.resize(); - }); - },'json'); -} -//负载 -function getload_old(b,e){ - $.get('/system/get_load_average?start='+b+'&end='+e,function(rdata){ - var myChartgetload = echarts.init(document.getElementById('getloadview')); - var aData = []; - var bData = []; - var xData = []; - var yData = []; - var zData = []; - - for(var i = 0; i < rdata.length; i++){ - xData.push(rdata[i].addtime); - yData.push(rdata[i].pro); - zData.push(rdata[i].one); - aData.push(rdata[i].five); - bData.push(rdata[i].fifteen); - } - option = { - tooltip: { - trigger: 'axis' - }, - calculable: true, - legend: { - data:['系统资源使用率','1分钟','5分钟','15分钟'], - selectedMode: 'single', - }, - xAxis: { - type: 'category', - boundaryGap: false, - data: xData, - axisLine:{ - lineStyle:{ - color:"#666" - } - } - }, - yAxis: { - type: 'value', - name: '', - boundaryGap: [0, '100%'], - splitLine:{ - lineStyle:{ - color:"#ddd" - } - }, - axisLine:{ - lineStyle:{ - color:"#666" - } - } - }, - dataZoom: [{ - type: 'inside', - start: 0, - end: 100, - zoomLock:true - }, { - start: 0, - end: 100, - handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z', - handleSize: '80%', - handleStyle: { - color: '#fff', - shadowBlur: 3, - shadowColor: 'rgba(0, 0, 0, 0.6)', - shadowOffsetX: 2, - shadowOffsetY: 2 - } - }], - series: [ - { - name:'系统资源使用率', - type:'line', - smooth:true, - symbol: 'none', - sampling: 'average', - itemStyle: { - normal: { - color: 'rgb(255, 140, 0)' - } - }, - data: yData - }, - { - name:'1分钟', - type:'line', - smooth:true, - symbol: 'none', - sampling: 'average', - itemStyle: { - normal: { - color: 'rgb(30, 144, 255)' - } - }, - data: zData - }, - { - name:'5分钟', - type:'line', - smooth:true, - symbol: 'none', - sampling: 'average', - itemStyle: { - normal: { - color: 'rgb(0, 178, 45)' - } - }, - data: aData - }, - { - name:'15分钟', - type:'line', - smooth:true, - symbol: 'none', - sampling: 'average', - itemStyle: { - normal: { - color: 'rgb(147, 38, 255)' - } - }, - data: bData - } - ] - }; - myChartgetload.setOption(option); - window.addEventListener("resize",function(){ - myChartgetload.resize(); - }); - },'json'); -} -//系统负载 -function getload(b,e){ - $.get('/system/get_load_average?start='+b+'&end='+e,function(rdata){ - var myChartgetload = echarts.init(document.getElementById('getloadview')); - var aData = []; - var bData = []; - var xData = []; - var yData = []; - var zData = []; - - for(var i = 0; i < rdata.length; i++){ - xData.push(rdata[i].addtime); - yData.push(rdata[i].pro); - zData.push(rdata[i].one); - aData.push(rdata[i].five); - bData.push(rdata[i].fifteen); - } - option = { - animation: false, - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'cross' - } - }, - legend: { - data:['1分钟','5分钟','15分钟'], - right:'16%', - top:'10px' - }, - axisPointer: { - link: {xAxisIndex: 'all'}, - lineStyle: { - color: '#aaaa', - width: 1 - } - }, - grid: [{ // 直角坐标系内绘图网格 - top: '60px', - left: '5%', - right: '55%', - width: '40%', - height: 'auto' - }, - { - top: '60px', - left: '55%', - width: '40%', - height: 'auto' - } - ], - xAxis: [ - - { // 直角坐标系grid的x轴 - type: 'category', - axisLine: { - lineStyle: { - color: '#666' - } - }, - data: xData - }, - { // 直角坐标系grid的x轴 - type: 'category', - gridIndex: 1, - axisLine: { - lineStyle: { - color: '#666' - } - }, - data: xData - }, - ], - yAxis: [{ - scale: true, - name: '资源使用率%', - splitLine: { // y轴网格显示 - show: true, - lineStyle:{ - color:"#ddd" - } - }, - nameTextStyle: { // 坐标轴名样式 - color: '#666', - fontSize: 12, - align: 'left' - }, - axisLine:{ - lineStyle:{ - color: '#666', - } - } - }, - { - scale: true, - name: '负载详情', - gridIndex: 1, - splitLine: { // y轴网格显示 - show: true, - lineStyle:{ - color:"#ddd" - } - }, - nameTextStyle: { // 坐标轴名样式 - color: '#666', - fontSize: 12, - align: 'left' - }, - axisLine:{ - lineStyle:{ - color: '#666', - } - } - }, - ], - dataZoom: [{ - type: 'inside', - start: 0, - end: 100, - xAxisIndex:[0,1], - zoomLock:true - }, { - xAxisIndex: [0, 1], - type: 'slider', - start: 0, - end: 100, - handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z', - handleSize: '80%', - handleStyle: { - color: '#fff', - shadowBlur: 3, - shadowColor: 'rgba(0, 0, 0, 0.6)', - shadowOffsetX: 2, - shadowOffsetY: 2 - }, - left:'5%', - right:'5%' - }], - series: [ - { - name: '资源使用率%', - type: 'line', - lineStyle: { normal: { width: 2, color: 'rgb(255, 140, 0)'}}, - itemStyle: {normal: { color: 'rgb(255, 140, 0)' }}, - data: yData - }, - { - xAxisIndex: 1, - yAxisIndex: 1, - name: '1分钟', - type: 'line', - lineStyle: { normal: {width: 2,color: 'rgb(30, 144, 255)' }}, - itemStyle: { normal: { color: 'rgb(30, 144, 255)'} }, - data: zData - }, - { - xAxisIndex: 1, - yAxisIndex: 1, - name: '5分钟', - type: 'line', - lineStyle: { normal: { width: 2, color: 'rgb(0, 178, 45)'} }, - itemStyle: { normal: { color: 'rgb(0, 178, 45)' } }, - data: aData - }, - { - xAxisIndex: 1, - yAxisIndex: 1, - name: '15分钟', - type: 'line', - lineStyle: { normal: { width: 2, color: 'rgb(147, 38, 255)'}}, - itemStyle: { normal: { color: 'rgb(147, 38, 255)' } }, - data: bData - } - ], - textStyle: { color: '#666',fontSize: 12} - } - myChartgetload.setOption(option); - window.addEventListener("resize",function(){ - myChartgetload.resize(); - }) - },'json'); -} \ No newline at end of file diff --git a/route/static/app/crontab.js b/route/static/app/crontab.js deleted file mode 100755 index 8db9e9fe1..000000000 --- a/route/static/app/crontab.js +++ /dev/null @@ -1,1131 +0,0 @@ -var num = 0; -//查看任务日志 -function getLogs(id){ - - var reqTimer = null; - var reqCount = 0; - - var tips = layer.msg('正在获取,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - var req_log_args = 'id='+id; - function requestLogs(layerIndex){ - - $.post('/crontab/logs', req_log_args, function(rdata){ - - if (reqCount == 0){ - layer.close(tips); - } - - if(!rdata.status) { - layer.close(layerIndex); - layer.msg(rdata.msg,{icon:2, time:2000}); - clearInterval(reqTimer); - return; - }; - - if (rdata.msg == ''){ - rdata.msg = '暂无数据!'; - } - - $("#crontab_log").html(rdata.msg); - //滚动到最低 - var ob = document.getElementById('crontab_log'); - ob.scrollTop = ob.scrollHeight; - reqCount++; - },'json'); - - } - - - layer.open({ - type:1, - title:"任务执行日志", - area: ['60%','500px'], - shadeClose:false, - btn:["清空","关闭"], - closeBtn:1, - end: function(){ - if (reqTimer){ - clearInterval(reqTimer); - } - }, - content:'
    ' - +'
    '
    -			// +'
    ' - // +'' - // +'' - // +'
    ' - +'
    ', - success:function(index,layer_index){ - requestLogs(layer_index); - reqTimer = setInterval(function(){ - requestLogs(layer_index); - },3000); - }, - - yes:function(index){ - clearInterval(reqTimer); - closeLogs(id); - layer.close(index); - }, - }); -} - - -function getBackupName(hook_data, name){ - for (var i = 0; i < hook_data.length; i++) { - if (hook_data[i]['name'] == name){ - return hook_data[i]['title']; - } - } - return name; -} - -function getCronData(page){ - var load = layer.msg(lan.public.the,{icon:16,time:0,shade: [0.3, '#000']}); - $.post("/crontab/list?p="+page,'', function(rdata){ - layer.close(load); - var cbody = ""; - if(rdata == ""){ - cbody=""+lan.crontab.task_empty+""; - } else { - for(var i=0;i正常' - :'停用'; - - - var cron_save = '--'; - if (rdata.data[i]['save'] != ''){ - cron_save = rdata.data[i]['save']+'份'; - } - - var cron_backupto = '-'; - if (rdata.data[i]['stype'] == 'site' || rdata.data[i]['stype']=='path' || rdata.data[i]['stype']=='database' || rdata.data[i]['stype'].indexOf('database_')>-1 ){ - cron_backupto = '本地磁盘'; - if (rdata.data[i]['backup_to'] != 'localhost'){ - cron_backupto = getBackupName(rdata['backup_hook'],rdata.data[i]['backup_to']); - } - } - - cbody += "\ - "+rdata.data[i].name+"\ - "+status+"\ - "+rdata.data[i].type+"\ - "+rdata.data[i].cycle+"\ - "+cron_save +"\ - "+cron_backupto+"\ - "+rdata.data[i].addtime+"\ - \ - 执行 | \ - 编辑 | \ - 日志 | \ - 删除\ - \ - "; - } - } - $('#cronbody').html(cbody); - $('#softPage').html(rdata.list); - },'json'); -} - -// 设置计划任务状态 -function setTaskStatus(id,status){ - var confirm = layer.confirm(status == '0'?'计划任务暂停后将无法继续运行,您真的要停用这个计划任务吗?':'该计划任务已停用,是否要启用这个计划任务', {title:'提示',icon:3,closeBtn:1},function(index) { - if (index > 0) { - var loadT = layer.msg('正在设置状态,请稍后...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/crontab/set_cron_status',{id:id},function(rdata){ - - if (!rdata.status){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - return; - } - - showMsg(rdata.msg,function(){ - layer.close(loadT); - layer.close(confirm); - getCronData(1); - },{icon:rdata.status?1:2},2000); - - },'json'); - } - }); -} - -//执行任务脚本 -function startTask(id){ - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - var data='id='+id; - $.post('/crontab/start_task',data,function(rdata){ - showMsg(rdata.msg, function(){ - },{icon:rdata.status?1:2,time:2000}); - },'json'); -} - - -//清空日志 -function closeLogs(id){ - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - var data='id='+id; - $.post('/crontab/del_logs',data,function(rdata){ - showMsg(rdata.msg, function(){ - // layer.closeAll(); - },{icon:rdata.status?1:2,time:2000}); - },'json'); -} - - -//删除 -function planDel(id,name){ - safeMessage(lan.get('del',[name]),'您确定要删除该任务吗?',function(){ - var load = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - var data='id='+id; - $.post('/crontab/del',data,function(rdata){ - showMsg(rdata.msg, function(){ - layer.closeAll(); - getCronData(1); - },{icon:rdata.status?1:2,time:2000}); - },'json'); - }); -} - -function isURL(str_url){ - var strRegex = '^(https|http|ftp|rtsp|mms)?://.+'; - var re=new RegExp(strRegex); - if (re.test(str_url)){ - return (true); - }else{ - return (false); - } -} - - -//提交 -function planAdd(){ - var name = $(".planname input[name='name']").val(); - if(name == ''){ - $(".planname input[name='name']").focus(); - layer.msg('任务名称不能为空!',{icon:2}); - return; - } - $("#cronConfig input[name='name']").val(name); - - var type = $(".plancycle").find("b").attr("val"); - $("#cronConfig input[name='type']").val(type); - - - var is1; - var is2 = 1; - switch(type){ - case 'day-n': - is1=31; - break; - case 'hour-n': - is1=23; - break; - case 'minute-n': - is1=59; - break; - case 'month': - is1=31; - break; - } - - var where1 = $('#excode_week b').attr('val'); - $("#cronConfig input[name='where1']").val(where1); - - if(where1 > is1 || where1 < is2){ - $("#ptime input[name='where1']").focus(); - layer.msg('表单不合法,请重新输入!',{icon:2}); - return; - } - - var hour = $("#ptime input[name='hour']").val(); - if(hour > 23 || hour < 0){ - $("#ptime input[name='hour']").focus(); - layer.msg('小时值不合法!',{icon:2}); - return; - } - $("#cronConfig input[name='hour']").val(hour); - var minute = $("#ptime input[name='minute']").val(); - if(minute > 59 || minute < 0){ - $("#ptime input[name='minute']").focus(); - layer.msg('分钟值不合法!',{icon:2}); - return; - } - $("#cronConfig input[name='minute']").val(minute); - - var save = $("#save").val(); - if(save < 0){ - layer.msg('不能有负数!',{icon:2}); - return; - } - - $("#cronConfig input[name='save']").val(save); - $("#cronConfig input[name='week']").val($(".planweek").find("b").attr("val")); - - var sType = $(".planjs").find("b").attr("val"); - var sBody = encodeURIComponent($("#implement textarea[name='sBody']").val()); - - if (sType == 'toShell'){ - if(sBody == ''){ - $("#implement textarea[name='sBody']").focus(); - layer.msg('脚本代码不能为空!',{icon:2}); - return; - } - } - - if(sType == 'toFile'){ - if($("#viewfile").val() == ''){ - layer.msg('请选择脚本文件!',{icon:2}); - return; - } - } - - var urladdress = $("#urladdress").val(); - if(sType == 'toUrl'){ - if(!isURL(urladdress)){ - layer.msg('URL地址不正确!',{icon:2}); - $("implement textarea[name='urladdress']").focus(); - return; - } - } - // urladdress = encodeURIComponent(urladdress); - $("#cronConfig input[name='urladdress']").val(urladdress); - $("#cronConfig input[name='sType']").val(sType); - $("#cronConfig textarea[name='sBody']").val(decodeURIComponent(sBody)); - - if(sType == 'site' || sType == 'database' || sType == 'path'){ - var backupTo = $(".planBackupTo").find("b").attr("val"); - $("#backupTo").val(backupTo); - } - - var sName = $("#sName").attr("val"); - - // if(sName == 'backupAll'){ - // var alist = $("ul[aria-labelledby='backdata'] li a"); - // var dataList = new Array(); - // for(var i=1;i 59){ -// if(hour >= 23) hour = 0; -// $("#cronConfig input[name='hour']").val(hour+1); -// minute = 5; -// } -// $("#cronConfig input[name='minute']").val(minute); -// $("#cronConfig input[name='name']").val(sTitle + '['+dataList[0]+']'); -// $("#cronConfig input[name='sName']").val(dataList[0]); -// var pdata = $("#cronConfig").serialize() + '&sBody=&urladdress='; -// $.ajax({ -// type:'POST', -// url:'/crontab/add', -// data:pdata, -// async: true, -// success:function(frdata){ -// layer.close(loadT); -// if(frdata.status){ -// successCount++; -// getCronData(1); -// }else{ -// if(!errorMsg){ -// errorMsg = '

    '+lan.crontab.backup_all_err+'

    '; -// } -// errorMsg += '
  • '+dataList[0]+' -> '+frdata.msg+'
  • ' -// } - -// dataList.splice(0,1); -// allAddCrontab(dataList,successCount,errorMsg); -// } -// }); -// } - -initDropdownMenu(); -function initDropdownMenu(){ - $(".dropdown ul li a").click(function(){ - var txt = $(this).text(); - var type = $(this).attr("value"); - $(this).parents(".dropdown").find("button b").text(txt).attr("val",type); - switch(type){ - case 'day': - closeOpt(); - toHour(); - toMinute(); - break; - case 'day-n': - closeOpt(); - toWhere1('天'); - toHour(); - toMinute(); - break; - case 'hour': - closeOpt(); - toMinute(); - break; - case 'hour-n': - closeOpt(); - toWhere1('小时'); - toMinute(); - break; - case 'minute-n': - closeOpt(); - toWhere1('分钟'); - break; - case 'week': - closeOpt(); - toWeek(); - toHour(); - toMinute(); - break; - case 'month': - closeOpt(); - toWhere1('日'); - toHour(); - toMinute(); - break; - case 'toFile': - toFile(); - break; - case 'toShell': - toShell(); - $(".controls").html('脚本内容'); - break; - case 'rememory': - rememory(); - $(".controls").html('提示'); - break; - case 'site': - toBackup('sites'); - $(".controls").html('备份网站'); - break; - case 'database_mariadb': - case 'database_mongodb': - case 'database_postgresql': - case 'database_mysql-apt': - case 'database_mysql-yum': - case 'database': - toBackup(type); - $(".controls").html('备份数据库'); - break; - case 'path': - toBackup('path'); - $(".controls").html('备份目录'); - break; - case 'logs': - toLogsHtml('logs'); - $(".controls").html('切割网站'); - break; - case 'toUrl': - toUrl(); - $(".controls").html('URL地址'); - break; - } - }); -} - - -//备份 -function toLogsHtml(type){ - var sMsg = ""; - switch(type){ - case 'sites': - sMsg = '备份网站'; - sType = "sites"; - break; - case 'database_mariadb': - case 'database_mongodb': - case 'database_postgresql': - case 'database_mysql-apt': - case 'database_mysql-yum': - case 'database': - sMsg = '备份数据库'; - suffix = type.replace('database','') - if (suffix != ''){ - suffix = suffix.replace('_','') - sMsg = '备份数据库['+suffix+']'; - } - sType = type; - break; - case 'logs': - sMsg = '切割日志'; - sType = "logs"; - break; - case 'path': - sMsg = '备份目录'; - sType = "path"; - break; - } - var data = 'type='+sType; - - $.post('/crontab/get_data_list',data,function(rdata){ - $(".planname input[name='name']").attr('readonly','true').css({"background-color":"#f6f6f6","color":"#666"}); - var sOpt = ""; - if(rdata.data.length == 0){ - layer.msg(lan.public.list_empty,{icon:2}) - return; - } - - for(var i=0;i'+rdata.data[i].name+'['+rdata.data[i].ps+']'; - } - - - if (sType != 'path'){ - sOpt = '
  • 所有
  • ' + sOpt; - } - - var orderOpt = ''; - for (var i=0;i'+rdata.orderOpt[i].title+'' - } - - - var changeDir = ''; - if (sType == 'path'){ - changeDir = ''; - } - - var sBody = '\ - '+ changeDir +'\ -
    \ -
    保留最新
    \ - \ - 份\ -
    '; - $("#implement").html(sBody); - getselectname(); - - $('.changePathDir').click(function(){ - changePathCallback($('#sName').val(),function(select_dir){ - $(".planname input[name='name']").val('备份目录['+select_dir+']'); - $('#implement .sname b').attr('val',select_dir).text(select_dir); - }); - }); - - - $(".dropdown ul li a").click(function(){ - var sName = $("#sName").attr("val"); - if(!sName) return; - $(".planname input[name='name']").val(sMsg+'['+sName+']'); - }); - },'json'); - -} - -//备份 -function toBackup(type){ - var sMsg = ""; - switch(type){ - case 'sites': - sMsg = '备份网站'; - sType = "sites"; - break; - case 'database_mariadb': - case 'database_mongodb': - case 'database_postgresql': - case 'database_mysql-apt': - case 'database_mysql-yum': - case 'database': - sMsg = '备份数据库'; - suffix = type.replace('database','') - if (suffix != ''){ - suffix = suffix.replace('_','') - sMsg = '备份数据库['+suffix+']'; - } - sType = type; - break; - case 'logs': - sMsg = '切割日志'; - sType = "logs"; - break; - case 'path': - sMsg = '备份目录'; - sType = "path"; - break; - } - var data = 'type='+sType; - - $.post('/crontab/get_data_list',data,function(rdata){ - $(".planname input[name='name']").attr('readonly','true').css({"background-color":"#f6f6f6","color":"#666"}); - var sOpt = ""; - if(rdata.data.length == 0){ - layer.msg(lan.public.list_empty,{icon:2}) - return; - } - - for(var i=0;i'+rdata.data[i].name+'['+rdata.data[i].ps+']'; - } - - - if (sType != 'path'){ - sOpt = '
  • 所有
  • ' + sOpt; - } - - var orderOpt = ''; - for (var i=0;i'+rdata.orderOpt[i].title+'' - } - - - var changeDir = ''; - if (sType == 'path'){ - changeDir = ''; - } - - var sBody = '\ - '+ changeDir +'\ -
    备份到
    \ - \ -
    保留最新
    \ - \ - 份\ -
    '; - $("#implement").html(sBody); - getselectname(); - - $('.changePathDir').click(function(){ - changePathCallback($('#sName').val(),function(select_dir){ - $(".planname input[name='name']").val('备份目录['+select_dir+']'); - $('#implement .sname b').attr('val',select_dir).text(select_dir); - }); - }); - - - $(".dropdown ul li a").click(function(){ - var sName = $("#sName").attr("val"); - if(!sName) return; - $(".planname input[name='name']").val(sMsg+'['+sName+']'); - }); - },'json'); - -} - - -// 编辑计划任务 -function editTaskInfo(id){ - layer.msg('正在获取,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/crontab/get_crond_find',{id:id},function(rdata){ - layer.closeAll(); - // console.log('get_crond_find:', rdata); - var sTypeName = '',sTypeDom = '',cycleName = '',cycleDom = '',weekName = '',weekDom = '',sNameName ='',sNameDom = '',backupsName = '',backupsDom =''; - obj = { - from:{ - id:rdata.id, - name: rdata.name, - type: rdata['type'], - stype: rdata.stype, - where1: rdata.where1, - hour: rdata.where_hour, - minute: rdata.where_minute, - week: rdata.where1, - sbody: rdata.sbody, - sname: rdata.sname, - backup_to: rdata.backup_to, - save: rdata.save, - urladdress: rdata.urladdress, - }, - sTypeArray:[['toShell','Shell脚本'],['site','备份网站'],['database','备份数据库'],['logs','日志切割'],['path','备份目录'],['rememory','释放内存'],['toUrl','访问URL']], - cycleArray:[['day','每天'],['day-n','N天'],['hour','每小时'],['hour-n','N小时'],['minute-n','N分钟'],['week','每星期'],['month','每月']], - weekArray:[[1,'周一'],[2,'周二'],[3,'周三'],[4,'周四'],[5,'周五'],[6,'周六'],[7,'周日']], - sNameArray:[], - backupsArray:[], - create:function(callback){ - if (obj.from['stype'].indexOf('database_')>-1){ - name = obj.from['stype'].replace('database_',''); - sTypeName = '备份数据库['+name+']'; - sTypeDom += '
  • '+ sTypeName +'
  • '; - } else { - for(var i = 0; i '+ obj['sTypeArray'][i][1] +''; - } - } - - for(var i = 0; i '+ obj['cycleArray'][i][1] +''; - } - - for(var i = 0; i '+ obj['weekArray'][i][1] +''; - } - - if(obj.from.stype == 'site' || obj.from.stype == 'database' || obj.from.stype == 'path' || obj.from.stype == 'logs' || obj.from['stype'].indexOf('database_')>-1){ - $.post('/crontab/get_data_list',{type:obj.from.stype},function(rdata){ - // console.log(rdata); - obj.sNameArray = rdata.data; - obj.sNameArray.unshift({name:'ALL',ps:'所有'}); - obj.backupsArray = rdata.orderOpt; - obj.backupsArray.unshift({title:'服务器磁盘',name:'localhost'}); - for(var i = 0; i '+ obj['sNameArray'][i]['ps'] +''; - } - for(var i = 0; i '+ obj['backupsArray'][i]['title'] +''; - } - callback(); - },'json'); - }else{ - callback(); - } - } - }; - obj.create(function(){ - - var changeDir = ''; - if (obj.from.stype == 'path'){ - changeDir = ''; - } - - layer.open({ - type:1, - title:'编辑计划任务-['+rdata.name+']', - area: ['850px','440px'], - skin:'layer-create-content', - shadeClose:false, - closeBtn:1, - content:'
    \ -
    \ - 任务类型\ - \ -
    \ -
    \ - 任务名称\ -
    \ -
    \ -
    \ - 执行周期\ - \ -
    \ - \ -
    日
    \ -
    时
    \ -
    分
    \ -
    \ -
    \ - \ -
    \ - 脚本内容\ -
    \ -
    \ -
    \ - 提示\ -
    释放PHP、MYSQL、PURE-FTPD、OpenResty的内存占用,建议在每天半夜执行!
    \ -
    \ -
    \ - URL地址\ -
    \ -
    \ -
    \ -
    保存编辑
    \ -
    \ -
    ', - - success:function(){ - - $('.changePathDir').click(function(){ - changePathCallback($('#sName').val(),function(select_dir){ - $('input[name="name"]').val('备份目录['+select_dir+']'); - $('.sName_btn .sname b').attr('val',select_dir).text(select_dir); - obj.from.sname = select_dir; - }); - }); - - if(obj.from.stype == 'toShell'){ - $('.site_list').hide(); - } else if (obj.from.stype == 'rememory') { - $('.site_list').hide(); - } else if ( obj.from.stype == 'toUrl'){ - $('.site_list').hide(); - } else { - $('.site_list').show(); - } - - - - obj.from.minute = $('.minute_create').val(); - obj.from.hour = $('.hour_create').val(); - obj.from.where1 = $('.where1_create').val(); - - $('.sName_create').blur(function () { - obj.from.name = $(this).val(); - }); - $('.where1_create').blur(function () { - obj.from.where1 = $(this).val(); - }); - - $('.hour_create').blur(function () { - obj.from.hour = $(this).val(); - }); - - $('.minute_create').blur(function () { - obj.from.minute = $(this).val(); - }); - - $('.save_create').blur(function () { - obj.from.save = $(this).val(); - }); - - $('.sBody_create').blur(function () { - obj.from.sbody = $(this).val(); - }); - $('.url_create').blur(function () { - obj.from.urladdress = $(this).val(); - }); - - $('[aria-labelledby="cycle"] a').unbind().click(function () { - $('.cycle_btn').find('b').attr('val',$(this).attr('value')).html($(this).html()); - var type = $(this).attr('value'); - switch(type){ - case 'day': - $('.week_btn').hide(); - $('.where1_input').hide(); - $('.hour_input').show().find('input').val('1'); - $('.minute_input').show().find('input').val('30'); - obj.from.week = ''; - obj.from.type = ''; - obj.from.hour = 1; - obj.from.minute = 30; - break; - case 'day-n': - $('.week_btn').hide(); - $('.where1_input').show().find('input').val('1'); - $('.hour_input').show().find('input').val('1'); - $('.minute_input').show().find('input').val('30'); - obj.from.week = ''; - obj.from.where1 = 1; - obj.from.hour = 1; - obj.from.minute = 30; - break; - case 'hour': - $('.week_btn').hide(); - $('.where1_input').hide(); - $('.hour_input').hide(); - $('.minute_input').show().find('input').val('30'); - obj.from.week = ''; - obj.from.where1 = ''; - obj.from.hour = ''; - obj.from.minute = 30; - break; - case 'hour-n': - $('.week_btn').hide(); - $('.where1_input').hide(); - $('.hour_input').show().find('input').val('1'); - $('.minute_input').show().find('input').val('30'); - obj.from.week = ''; - obj.from.where1 = ''; - obj.from.hour = 1; - obj.from.minute = 30; - break; - case 'minute-n': - $('.week_btn').hide(); - $('.where1_input').hide(); - $('.hour_input').hide(); - $('.minute_input').show(); - obj.from.week = ''; - obj.from.where1 = ''; - obj.from.hour = ''; - obj.from.minute = 30; - console.log(obj.from); - break; - case 'week': - $('.week_btn').show(); - $('.where1_input').hide(); - $('.hour_input').show(); - $('.minute_input').show(); - obj.from.week = 1; - obj.from.where1 = ''; - obj.from.hour = 1; - obj.from.minute = 30; - break; - case 'month': - $('.week_btn').hide(); - $('.where1_input').show(); - $('.hour_input').show(); - $('.minute_input').show(); - obj.from.week = ''; - obj.from.where1 = 1; - obj.from.hour = 1; - obj.from.minute = 30; - break; - } - obj.from.type = $(this).attr('value'); - }); - - $('[aria-labelledby="week"] a').unbind().click(function () { - $('.week_btn').find('b').attr('val',$(this).attr('value')).html($(this).html()); - obj.from.week = $(this).attr('value'); - }); - - $('[aria-labelledby="backupTo"] a').unbind().click(function () { - $('.backup_btn').find('b').attr('val',$(this).attr('value')).html($(this).html()); - obj.from.backup_to = $(this).attr('value'); - }); - $('.plan-submits').unbind().click(function(){ - if(obj.from.type == 'hour-n'){ - obj.from.where1 = obj.from.hour; - obj.from.hour = ''; - } else if(obj.from.type == 'minute-n') { - obj.from.where1 = obj.from.minute; - obj.from.minute = ''; - } - var loadT = layer.msg('正在保存编辑内容,请稍后...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/crontab/modify_crond',obj.from,function(rdata){ - - if (!rdata.status){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - return; - } - - showMsg(rdata.msg, function(){ - layer.closeAll(); - getCronData(1); - initDropdownMenu(); - },{icon:rdata.status?1:2}, 2000); - - },'json'); - }); - } - ,cancel: function(){ - initDropdownMenu(); - } - }); - }); - },'json'); -} - - -//下拉菜单名称 -function getselectname(){ - $(".dropdown ul li a").click(function(){ - var txt = $(this).text(); - var type = $(this).attr("value"); - $(this).parents(".dropdown").find("button b").text(txt).attr("val",type); - }); -} -//清理 -function closeOpt(){ - $("#ptime").html(''); -} -//星期 -function toWeek(){ - var mBody = ''; - $("#ptime").html(mBody); - getselectname(); -} -//指定1 -function toWhere1(ix){ - var mBody ='
    \ - \ - '+ix+'\ -
    '; - $("#ptime").append(mBody); -} -//小时 -function toHour(){ - var mBody = '
    \ - \ - 小时\ -
    '; - $("#ptime").append(mBody); -} - -//分钟 -function toMinute(){ - var mBody = '
    \ - \ - 分钟\ -
    '; - $("#ptime").append(mBody); - -} - -//从文件 -function toFile(){ - var tBody = '\ - '; - $("#implement").html(tBody); - $(".planname input[name='name']").removeAttr('readonly style').val(""); -} - -//从脚本 -function toShell(){ - var tBody = ""; - $("#implement").html(tBody); - $(".planname input[name='name']").removeAttr('readonly style').val(""); -} - -//从脚本 -function toUrl(){ - var tBody = ""; - $("#implement").html(tBody); - $(".planname input[name='name']").removeAttr('readonly style').val(""); -} - -//释放内存 -function rememory(){ - $(".planname input[name='name']").removeAttr('readonly style').val(""); - $(".planname input[name='name']").val(lan.crontab.mem); - $("#implement").html(lan.crontab.mem_ps); - return; -} -//上传 -function fileupload(){ - $("#sFile").change(function(){ - $("#viewfile").val($("#sFile").val()); - }); - $("#sFile").click(); -} \ No newline at end of file diff --git a/route/static/app/files.js b/route/static/app/files.js deleted file mode 100755 index 5fad6c602..000000000 --- a/route/static/app/files.js +++ /dev/null @@ -1,1864 +0,0 @@ -// console.log(lan); - -//判断磁盘数量超出宽度 -function isDiskWidth(){ - var comlistWidth = $("#comlist").width(); - var bodyWidth = $(".file-box").width(); - if(comlistWidth + 530 > bodyWidth){ - $("#comlist").css({"width":bodyWidth-530+"px","height":"34px","overflow":"auto"}); - } - else{ - $("#comlist").removeAttr("style"); - } -} - -//打开回收站 -function recycleBin(type){ - $.post('/files/get_recycle_bin','',function(data){ - // console.log(rdata); - var rdata = data['data']; - var body = '' - switch(type){ - case 1: - for(var i=0;i 20) shortwebname = shortwebname.substring(0, 20) + "..."; - if(shortpath.length > 20) shortpath = shortpath.substring(0, 20) + "..."; - body += '\ - '+shortwebname+'\ - '+shortpath+'\ - '+toSize(rdata.dirs[i].size)+'\ - '+getLocalTime(rdata.dirs[i].time)+'\ - \ - '+lan.files.recycle_bin_re+'\ - | '+lan.files.recycle_bin_del+'\ - \ - '; - } - for(var i=0;i 20) shortwebname = shortwebname.substring(0, 20) + "..."; - if(shortpath.length > 20) shortpath = shortpath.substring(0, 20) + "..."; - body += '\ - '+shortwebname.replace('BTDB_','')+'\ - mysql://'+shortpath.replace('BTDB_','')+'\ - -\ - '+getLocalTime(rdata.files[i].time)+'\ - \ - '+lan.files.recycle_bin_re+'\ - | '+lan.files.recycle_bin_del+'\ - \ - '; - - continue; - } - var shortwebname = rdata.files[i].name.replace(/'/,"\\'"); - var shortpath = rdata.files[i].dname; - if(shortwebname.length > 20) shortwebname = shortwebname.substring(0, 20) + "..."; - if(shortpath.length > 20) shortpath = shortpath.substring(0, 20) + "..."; - body += '\ - '+shortwebname+'\ - '+shortpath+'\ - '+toSize(rdata.files[i].size)+'\ - '+getLocalTime(rdata.files[i].time)+'\ - \ - '+lan.files.recycle_bin_re+'\ - | '+lan.files.recycle_bin_del+'\ - \ - ' - } - $("#RecycleBody").html(body); - return; - break; - case 2: - for(var i=0;i 20) shortwebname = shortwebname.substring(0, 20) + "..."; - if(shortpath.length > 20) shortpath = shortpath.substring(0, 20) + "..."; - body += '\ - '+shortwebname+'\ - '+shortpath+'\ - '+toSize(rdata.dirs[i].size)+'\ - '+getLocalTime(rdata.dirs[i].time)+'\ - \ - '+lan.files.recycle_bin_re+'\ - | '+lan.files.recycle_bin_del+'\ - \ - ' - } - $("#RecycleBody").html(body); - return; - break; - case 3: - for(var i=0;i 20) shortwebname = shortwebname.substring(0, 20) + "..."; - if(shortpath.length > 20) shortpath = shortpath.substring(0, 20) + "..."; - body += '\ - '+shortwebname+'\ - '+shortpath+'\ - '+toSize(rdata.files[i].size)+'\ - '+getLocalTime(rdata.files[i].time)+'\ - \ - '+lan.files.recycle_bin_re+'\ - | '+lan.files.recycle_bin_del+'\ - \ - '; - } - $("#RecycleBody").html(body); - return; - break; - case 4: - for(var i=0;i 20) shortwebname = shortwebname.substring(0, 20) + "..."; - if(shortpath.length > 20) shortpath = shortpath.substring(0, 20) + "..."; - body += '\ - '+shortwebname+'\ - '+shortpath+'\ - '+toSize(rdata.files[i].size)+'\ - '+getLocalTime(rdata.files[i].time)+'\ - \ - '+lan.files.recycle_bin_re+'\ - | '+lan.files.recycle_bin_del+'\ - \ - '; - } - } - $("#RecycleBody").html(body); - return; - break; - case 5: - for(var i=0;i 20) shortwebname = shortwebname.substring(0, 20) + "..."; - if(shortpath.length > 20) shortpath = shortpath.substring(0, 20) + "..."; - body += '\ - '+shortwebname+'\ - '+shortpath+'\ - '+toSize(rdata.files[i].size)+'\ - '+getLocalTime(rdata.files[i].time)+'\ - \ - '+lan.files.recycle_bin_re+'\ - | '+lan.files.recycle_bin_del+'\ - \ - ' - } - } - $("#RecycleBody").html(body); - return; - } - - - var tablehtml = '
    \ -
    \ - 文件回收站\ -
    \ - \ - \ -
    \ -
    \ - 注意:关闭回收站,删除的文件无法恢复!\ - \ -
    \ -
    \ -
    \ -

    全部

    \ -

    文件夹

    \ -

    文件

    \ -

    图片

    \ -

    文档

    \ -
    \ -
    \ -
    \ - \ - \ - \ - \ - \ - \ - \ - \ - '+body+'\ -
    文件名原位置大小删除时间操作
    '; - if(type == 'open'){ - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: ['80%','606px'], - title: lan.files.recycle_bin_title, - content: tablehtml - }); - - if(window.location.href.indexOf("database") != -1){ - recycleBin(6); - $(".re-con-menu p:last-child").addClass("on").siblings().removeClass("on"); - }else{ - recycleBin(1); - } - } - $(".re-con-menu p").click(function(){ - $(this).addClass("on").siblings().removeClass("on"); - }) - },'json'); -} - -//去扩展名不处理 -function getFileName(name){ - var text = name.split("/"); - var n = text.length-1; - text = text[n]; - return text; -} - -//判断图片文件 -function reisImage(fileName){ - var exts = ['jpg','jpeg','png','bmp','gif','tiff','ico']; - for(var i=0; i正在删除,请稍候...
    ",{icon:16,time:0,shade: [0.3, '#000']}); - setTimeout(function(){ - getSpeed('.myspeed'); - },1000); - $.post('/files/close_recycle_bin', '', function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:5}); - $("#RecycleBody").html(''); - },'json'); - }); -} - - -//回收站开关 -function setRecycleBin(db){ - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - var data = {} - if(db == 1){ - data = {db:db}; - } - $.post('/files/recycle_bin',data,function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:5}); - },'json'); -} - -function openFilename(obj){ - var path = $(obj).attr('data-path'); - var ext = getSuffixName(path); - - // console.log(path,ext); - if (inArray(ext,['html','htm','php','txt','md','js','css','scss','json','c','h','pl','py','java','log','conf','sh','json','ini'])){ - onlineEditFile(0, path); - } - - if (inArray(ext,['png','jpeg','jpg','gif','webp','bmp','ico'])){ - getImage(path); - } - - if (inArray(ext,['svg'])){ - var url = '/files/download?filename='+path; - layer.open({ - type:1, - closeBtn: 1, - title:"SVG预览", - area: ['600px','500px'], - maxmin:true, - shadeClose: true, - content: '' - }); - } -} - -function searchFile(p){ - getFiles(p); -} - -//处理排序 -function listFileOrder(skey, obj){ - var or = getCookie('file_order'); - var orderType = 'desc'; - if(or){ - var or_arr = or.split('|'); - if(or.split('|')[1] == 'desc'){ - orderType = 'asc'; - } else if (or_arr[1] == 'asc'){ - orderType = 'none'; - } else { - orderType = 'desc'; - } - } - setCookie('file_order',skey + '|' + orderType); - getFiles(1); - // console.log(obj,orderType); - // if(orderType == 'desc'){ - // $(obj).find(".glyphicon-triangle-top").remove(); - // $(obj).append(""); - // } else { - // $(obj).find(".glyphicon-triangle-bottom").remove(); - // $(obj).append(""); - // } -} - -function makeFilePage(showRow, page = ''){ - var rows = ['10','50','100','200','500','1000','2000']; - var rowOption = ''; - for(var i=0;i'+rows[i]+''; - } - - //分页 - $("#filePage").html(page); - $("#filePage div").append("每页条"); - $("#filePage .Pcount").css("left","16px"); -} - -//取数据 -function getFiles(Path) { - if(isNaN(Path)){ - var p = 1; - } else { - var p = Path; - Path = getCookie('open_dir_path'); - } - - var args = {}; - args['p'] = p; - var post = {}; - post['path'] = Path; - - var file_row = $.cookie('file_row'); - if(!file_row) { - file_row = '100'; - } - args['row'] = file_row; - - var body = ''; - var totalSize = 0; - - var search = ''; - var search_file = $("#search_file").val(); - - if(search_file.length > 0){ - args['search'] = search_file; - } - - var search_all = ''; - var all = $('#search_all').hasClass('active'); - if(all){ - args['all'] = 'yes'; - } - - var file_order = $.cookie('file_order'); - if (file_order){ - post['order'] = file_order.replace('|',' '); - } - - var query_str = toUrlParam(args); - - var loadT = layer.load(); - $.post('/files/get_dir?' + query_str, post, function(rdata) { - layer.close(loadT); - - //构建分页 - makeFilePage(file_row,rdata.PAGE); - - if(rdata.DIR == null) { - rdata.DIR = []; - } - - for (var i = 0; i < rdata.DIR.length; i++) { - - var fmp = rdata.DIR[i].split(";"); - var cnametext =fmp[0] + fmp[5]; - - fmp[0] = fmp[0].replace(/'/, "\\'"); - if(cnametext.length>20){ - cnametext = cnametext.substring(0,20) + '...'; - } - - if(isChineseChar(cnametext)){ - if(cnametext.length>10){ - cnametext = cnametext.substring(0,10) + '...'; - } - } - - var timetext ='--'; - if(getCookie('rank') == 'a'){ - //列表展示 - $("#set_list").addClass("active"); - $("#set_icon").removeClass("active"); - body += "\ - \ - \ - " + cnametext + "\ - "+toSize(fmp[1])+"\ - "+getLocalTime(fmp[2])+"\ - "+fmp[3]+"\ - "+fmp[4]+"\ - \ - 复制 | \ - 剪切 | \ - 重命名 | \ - 权限 | \ - 压缩 | \ - 删除\ - \ - "; - } else { - //图标展示 - $("#set_icon").addClass("active"); - $("#set_list").removeClass("active"); - body += ""; - } - } - - for (var i = 0; i < rdata.FILES.length; i++) { - if(rdata.FILES[i] == null) continue; - var fmp = rdata.FILES[i].split(";"); - var bodyZip = ''; - var download = ''; - var cnametext =fmp[0] + fmp[5]; - fmp[0] = fmp[0].replace(/'/,"\\'"); - - if(isChineseChar(cnametext)){ - if(cnametext.length>16){ - cnametext = cnametext.substring(0,16) + '...'; - } - } else{ - if( cnametext.length > 48 ){ - cnametext = cnametext.substring(0,48) + '...'; - } - } - - var displayCompress = 1; - if(isCompressFile(fmp[0])){ - bodyZip = "解压 | "; - } - - if(isText(fmp[0])){ - bodyZip = "编辑 | "; - } - - if(isImage(fmp[0])){ - download = "预览 | "; - } else { - download = "下载 | "; - } - - totalSize += parseInt(fmp[1]); - if(getCookie("rank")=="a"){ - body += "\ - \ - " + cnametext + "\ - " + (toSize(fmp[1])) + "\ - " + ((fmp[2].length > 11)?fmp[2]:getLocalTime(fmp[2])) + "\ - "+fmp[3]+"\ - "+fmp[4]+"\ - \ - 复制 | \ - 剪切 | \ - 重命名 | \ - 权限 | \ - 压缩 | " - + bodyZip - + download - + "删除\ - \ - "; - } - else{ - body += ""; - } - } - var dirInfo = '(共{1}个目录与{2}个文件,大小:'.replace('{1}',rdata.DIR.length+'').replace('{2}',rdata.FILES.length+'')+'' - + (toSize(totalSize))+'获取)'; - $("#dir_info").html(dirInfo); - if( getCookie('rank') == 'a' ){ - - // console.log(post['order']); - var size_icon = ''; - if (post['order'] == 'size desc'){ - size_icon = ''; - } else if (post['order'] == 'size asc'){ - size_icon = ''; - } else { - size_icon = ''; - } - - var mtime_icon = ''; - if (post['order'] == 'mtime desc'){ - mtime_icon = ''; - } else if (post['order'] == 'mtime asc'){ - mtime_icon = ''; - } else { - mtime_icon = ''; - } - - var fname_icon = ''; - if (post['order'] == 'fname desc'){ - fname_icon = ''; - } else if (post['order'] == 'fname asc'){ - fname_icon = ''; - } else { - fname_icon = ''; - } - - var tablehtml = '\ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - '+body+'\ -
    文件名'+fname_icon+'大小'+size_icon+'修改时间'+mtime_icon+'权限所有者操作
    '; - $("#fileCon").removeClass("fileList").html(tablehtml); - $("#tipTools").width($("#fileCon").width()); - } else { - $("#fileCon").addClass("fileList").html(body); - $("#tipTools").width($("#fileCon").width()); - } - $("#DirPathPlace input").val(rdata.PATH); - var BarTools = '
    \ - \ - \ -
    '; - if (rdata.PATH != '/') { - BarTools += ' '; - } - setCookie('open_dir_path',rdata.PATH); - BarTools += ' \ - '; - var copyName = getCookie('copyFileName'); - var cutName = getCookie('cutFileName'); - var isPaste = (copyName == 'null') ? cutName : copyName; - // console.log('isPaste:',isPaste); - //--- - if (isPaste != 'null' && isPaste != undefined) { - BarTools += ' '; - } - - $("#Batch").html(''); - var batchTools = ''; - var isBatch = getCookie('BatchSelected'); - if (isBatch == 1 || isBatch == '1') { - batchTools += ' '; - } - $("#Batch").html(batchTools); - - $("#setBox").prop("checked", false); - - $("#BarTools").html(BarTools); - - $("input[name=id]").click(function(){ - if($(this).prop("checked")) { - $(this).prop("checked", true); - $(this).parents("tr").addClass("ui-selected"); - } - else{ - $(this).prop("checked", false); - $(this).parents("tr").removeClass("ui-selected"); - } - showSeclect(); - }); - - $("#setBox").click(function() { - if ($(this).prop("checked")) { - $("input[name=id]").prop("checked", true); - $("#filesBody > tr").addClass("ui-selected"); - - } else { - $("input[name=id]").prop("checked", false); - $("#filesBody > tr").removeClass("ui-selected"); - } - showSeclect(); - }); - //阻止冒泡 - $("#filesBody .btlink").click(function(e){ - e.stopPropagation(); - }); - $("input[name=id]").dblclick(function(e){ - e.stopPropagation(); - }); - - - //禁用右键 - $("#fileCon").bind("contextmenu",function(e){ - return false; - }); - bindselect(); - - // //绑定右键 - $("#fileCon").mousedown(function(e){ - var count = totalFile(); - if(e.which == 3) { - if(count>1){ - rightMenuClickAll(e); - } else { - return; - } - } - }); - - $(".folderBox,.folderBoxTr").mousedown(function(e){ - // console.log(e); - var count = totalFile(); - if(e.which == 3) { - if(count <= 1){ - var a = $(this); - var option = rightMenuClick(a.attr("filetype"),a.attr("data-path"),a.find("input").val()); - a.contextify(option); - } else{ - rightMenuClickAll(e); - } - } - }); - - //每页行数 - $(".showRow").change(function(){ - setCookie('file_row',$(this).val()); - getFiles(p); - }); - pathPlaceBtn(rdata.PATH); - },'json'); - // setTimeout(function(){getCookie('open_dir_path');},200); -} - - -//统计选择数量 -function totalFile(){ - var el = $("input[name='id']"); - var len = el.length; - var count = 0; - for(var i=0;i 1){ - batchTools = '\ - \ - \ - \ - '; - }else{ - //setCookie('BatchSelected', null); - } - $("#Batch").html(batchTools); -} - -//滚动条事件 -$(window).scroll(function () { - if($(window).scrollTop() > 16){ - $("#tipTools").css({"position":"fixed","top":"0","left":"195px","box-shadow":"0 1px 10px 3px #ccc"}); - }else{ - $("#tipTools").css({"position":"absolute","top":"0","left":"0","box-shadow":"none"}); - } -}); -$("#tipTools").width($(".file-box").width()); -$("#PathPlaceBtn").width($(".file-box").width()-700); -$("#DirPathPlace input").width($(".file-box").width()-700); -if($(window).width()<1160){ - $("#PathPlaceBtn").width(290); -} -window.onresize = function(){ - $("#tipTools").width($(".file-box").width()-30); - $("#PathPlaceBtn").width($(".file-box").width()-700); - $("#DirPathPlace input").width($(".file-box").width()-700); - if($(window).width()<1160){ - $("#PathPlaceBtn,#DirPathPlace input").width(290); - } - pathLeft(); - isDiskWidth(); -} - -//批量操作 -function batch(type,access){ - var path = $("#DirPathPlace input").val(); - var el = document.getElementsByTagName('input'); - var len = el.length; - var data='path='+path+'&type='+type; - var name = 'data'; - var datas = []; - - var oldType = getCookie('BatchPaste'); - - for(var i=0;i正在处理,请稍候...
    ",{icon:16,time:0,shade: [0.3, '#000']}); - setTimeout(function(){getSpeed('.myspeed');},1000); - // console.log(data); - $.post('/files/set_batch_data',data,function(rdata){ - layer.close(myloadT); - getFiles(path); - layer.msg(rdata.msg,{icon:1}); - },'json'); -} - -//批量粘贴 -function batchPaste(){ - var path = $("#DirPathPlace input").val(); - var type = getCookie('BatchPaste'); - var data = 'type='+type+'&path='+path; - - $.post('/files/check_exists_files',{dfile:path},function(rdata){ - var result = rdata['data']; - if(result.length > 0){ - var tbody = ''; - for(var i=0;i'+toSize(result[i].size)+''+getLocalTime(result[i].mtime)+''; - } - var mbody = '
    \ - '+tbody+'\ -
    文件名大小最后修改时间
    '; - safeMessage('即将覆盖以下文件',mbody,function(){ - batchPasteTo(data,path); - }); - $(".layui-layer-page").css("width","500px"); - }else{ - batchPasteTo(data,path); - } - },'json'); -} - -function batchPasteTo(data,path){ - myloadT = layer.msg("
    正在处理,请稍候...
    ",{icon:16,time:0,shade: [0.3, '#000']}); - setTimeout(function(){getSpeed('.myspeed');},1000); - $.post('/files/batch_paste',data,function(rdata){ - layer.close(myloadT); - setCookie('BatchSelected', null); - getFiles(path); - layer.msg(rdata.msg,{icon:1}); - },'json'); -} - - -function getSuffixName(fileName){ - var extArr = fileName.split("."); - var exts = ['folder-unempty','sql','c','cpp','cs','flv','css','js', - 'htm','html','java','log','mht','url','xml','ai','bmp','cdr','gif','ico', - 'jpeg','jpg','JPG','png','psd','webp','ape','avi','mkv','mov','mp3','mp4', - 'mpeg','mpg','rm','rmvb','swf','wav','webm','wma','wmv','rtf','docx','fdf','potm', - 'pptx','txt','xlsb','xlsx','7z','cab','iso','rar','zip','gz','bt','file','apk','bookfolder', - 'folder','folder-empty','fromchromefolder','documentfolder','fromphonefolder', - 'mix','musicfolder','picturefolder','videofolder','sefolder','access','mdb','accdb', - 'fla','doc','docm','dotx','dotm','dot','pdf', - 'ppt','pptm','pot','xls','csv','xlsm','scss','svg','pl','py','php','md','json','sh','conf']; - var extLastName = extArr[extArr.length - 1]; - for(var i=0; i tr").hover(function(){ - $(this).addClass("hover"); - },function(){ - $(this).removeClass("hover"); - }).click(function(){ - $(this).addClass("on").siblings().removeClass("on"); - }); -} - -//取磁盘 -function getDisk() { - var LBody = ''; - $.get('/system/disk_info', function(rdata) { - for (var i = 0; i < rdata.length; i++) { - LBody += "\ -  " + (rdata[i].path=='/'?lan.files.path_root:rdata[i].path) + "(" + rdata[i].size[2] + ")"; - } - var trash = '\ -  回收站'; - $("#comlist").html(LBody+trash); - isDiskWidth(); - },'json'); -} - -//返回上一级 -function backDir() { - var str = $("#DirPathPlace input").val().replace('//','/'); - if(str.substr(str.length-1,1) == '/'){ - str = str.substr(0,str.length-1); - } - var Path = str.split("/"); - var back = '/'; - if (Path.length > 2) { - var count = Path.length - 1; - for (var i = 0; i < count; i++) { - back += Path[i] + '/'; - } - if(back.substr(back.length-1,1) == '/'){ - back = back.substr(0,back.length-1); - } - getFiles(back); - } else { - back += Path[0]; - getFiles(back); - } - setTimeout('pathPlaceBtn(getCookie("open_dir_path"));',200); -} -//新建文件 -function createFile(type, path) { - if (type == 1) { - var fileName = $("#newFileName").val(); - layer.msg(lan.public.the, { - icon: 16, - time: 10000 - }); - $.post('/files/create_file', 'path=' + encodeURIComponent(path + '/' + fileName), function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, { - icon: rdata.status ? 1 : 2 - }); - if(rdata.status){ - getFiles($("#DirPathPlace input").val()); - onlineEditFile(0,path + '/' + fileName); - } - },'json'); - return; - } - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '320px', - title: '新建空白文件', - content: '
    \ -
    \ - \ -
    \ -
    \ - \ - \ -
    \ -
    ', - success:function(){ - $("#newFileName").focus().keyup(function(e){ - if(e.keyCode == 13) $("#createFileBtn").click(); - }); - } - }); - -} -//新建目录 -function createDir(type, path) { - if (type == 1) { - var dirName = $("#newDirName").val(); - layer.msg('正在处理,请稍候...', { - icon: 16, - time: 10000 - }); - $.post('/files/create_dir', 'path=' + encodeURIComponent(path + '/' + dirName), function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, { - icon: rdata.status ? 1 : 2 - }); - getFiles($("#DirPathPlace input").val()); - },'json'); - return; - } - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '320px', - title: '新建目录', - content: '
    \ -
    \ - \ -
    \ -
    \ - \ - \ -
    \ -
    ', - success:function(){ - $("#newDirName").focus().keyup(function(e){ - if(e.keyCode == 13) $("#createDirBtn").click(); - }); - } - }); - -} - -//删除文件 -function deleteFile(fileName){ - layer.confirm(lan.get('recycle_bin_confirm',[fileName]),{title:'删除文件',closeBtn:2,icon:3},function(){ - layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/files/delete', 'path=' + encodeURIComponent(fileName), function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, { - icon: rdata.status ? 1 : 2, - }); - getFiles($("#DirPathPlace input").val()); - },'json'); - }); -} - -//删除目录 -function deleteDir(dirName){ - layer.confirm(lan.get('recycle_bin_confirm_dir',[dirName]),{title:'删除目录',closeBtn:2,icon:3},function(){ - layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/files/delete_dir', 'path=' + encodeURIComponent(dirName), function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, { - icon: rdata.status ? 1 : 2 - }); - getFiles($("#DirPathPlace input").val()); - },'json'); - }); -} -//批量删除文件 -function allDeleteFileSub(data,path){ - layer.confirm('您确实要把这些文件放入回收站吗?',{title:'批量删除文件',closeBtn:2,icon:3},function(){ - layer.msg("
    正在处理,请稍候...
    ",{icon:16,time:0,shade: [0.3, '#000']}); - setTimeout(function(){getSpeed('.myspeed');},1000); - $.post('/files/set_batch_data',data,function(rdata){ - layer.closeAll(); - getFiles(path); - layer.msg(rdata.msg,{icon:1}); - },'json'); - }); -} - -//重载文件列表 -function reloadFiles(){ - setInterval(function(){ - var path = $("#DirPathPlace input").val(); - getFiles(path); - },3000); -} - -//下载文件 -function downloadFile(action){ - - if(action == 1){ - var fUrl = $("#mUrl").val(); - fUrl = encodeURIComponent(fUrl); - - var fpath = $("#dpath").val(); - fname = encodeURIComponent($("#dfilename").val()); - - if (fUrl == "" ){ - layer.msg("URL地址不能为空!",{icon:2}); - return; - } - - layer.closeAll(); - layer.msg(lan.files.down_task,{time:0,icon:16,shade: [0.3, '#000']}); - - $.post('/files/download_file','path='+fpath+'&url='+fUrl+'&filename='+fname,function(rdata){ - layer.closeAll(); - getFiles(fpath); - getTaskCount(); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - return; - } - - var path = $("#DirPathPlace input").val(); - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '500px', - btn:["确定","关闭"], - title: lan.files.down_title, - content: '
    \ -
    \ - URL地址:\ - \ -
    \ -
    \ - 下载到:\ - \ -
    \ -
    \ - 文件名:\ - \ -
    \ -
    ', - success:function(){ - $("#mUrl").keyup(function(){ - durl = $(this).val(); - tmp = durl.split('/'); - $("#dfilename").val(tmp[tmp.length-1]); - }); - }, - yes:function(){ - downloadFile(1); - } - }); -} - -//重命名 -function reName(type, fileName) { - if (type == 1) { - var path = $("#DirPathPlace input").val(); - var newFileName = encodeURIComponent(path + '/' + $("#newFileName").val()); - var oldFileName = encodeURIComponent(path + '/' + fileName); - layer.msg(lan.public.the, { - icon: 16, - time: 10000 - }); - $.post('/files/mv_file', 'sfile=' + oldFileName + '&dfile=' + newFileName, function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, { - icon: rdata.status ? 1 : 2 - }); - getFiles(path); - },'json'); - return; - } - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '320px', - title: '重命名', - btn:["确定","取消"], - content: '
    \ -
    \ - \ -
    \ -
    ', - success:function(){ - $("#newFileName").focus().keyup(function(e){ - if(e.keyCode == 13) $(".layui-layer-btn0").click(); - }); - }, - yes:function(){ - reName(1,fileName.replace(/'/,"\\'")); - } - }); - -} -//剪切 -function cutFile(fileName) { - var path = $("#DirPathPlace input").val(); - setCookie('cutFileName', fileName); - setCookie('copyFileName', null); - layer.msg('已剪切', { - icon: 1, - time: 1000, - }); - setTimeout(function(){ - getFiles(path); - },1000); -} -//复制 -function copyFile(fileName) { - var path = $("#DirPathPlace input").val(); - setCookie('copyFileName', fileName); - setCookie('cutFileName', null); - layer.msg('已复制', { - icon: 1, - time: 1000, - }); - - setTimeout(function(){ - getFiles(path); - },1000); -} -//粘贴 -function pasteFile(fileName) { - var path = $("#DirPathPlace input").val(); - var copyName = getCookie('copyFileName'); - var cutName = getCookie('cutFileName'); - var filename = copyName; - if(cutName != 'null' && cutName != undefined) filename=cutName; - filename = filename.split('/').pop(); - $.post('/files/check_exists_files',{dfile:path,filename:filename},function(result){ - if(result.length > 0){ - var tbody = ''; - for(var i=0;i'+toSize(result[i].size)+''+getLocalTime(result[i].mtime)+''; - } - var mbody = '
    \ - '+tbody+'\ -
    文件名大小最后修改时间
    '; - safeMessage('即将覆盖以下文件',mbody,function(){ - pasteTo(path,copyName,cutName,fileName); - }); - } else { - pasteTo(path,copyName,cutName,fileName); - } - },'json'); -} - - -function pasteTo(path,copyName,cutName,fileName){ - if (copyName != 'null' && copyName != undefined) { - layer.msg(lan.files.copy_the, { - icon: 16, - time: 0,shade: [0.3, '#000'] - }); - - //同目录下 - if (copyName == path +'/'+ fileName){ - fileName = '__copy__'+ fileName; - } - - $.post('/files/copy_file', 'sfile=' + encodeURIComponent(copyName) + '&dfile=' + encodeURIComponent(path +'/'+ fileName), function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, { - icon: rdata.status ? 1 : 2 - }); - getFiles(path); - },'json'); - setCookie('copyFileName', null); - setCookie('cutFileName', null); - return; - } - - if (cutName != 'null' && cutName != undefined) { - layer.msg(lan.files.mv_the, { - icon: 16, - time: 0,shade: [0.3, '#000'] - }); - $.post('/files/mv_file', 'sfile=' + encodeURIComponent(cutName) + '&dfile=' + encodeURIComponent(path + '/'+fileName), function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); - getFiles(path); - },'json'); - setCookie('copyFileName', null); - setCookie('cutFileName', null); - } -} - - -//压缩目录 -function zip(dirName,submits) { - var path = $("#DirPathPlace input").val(); - if(submits != undefined){ - if(dirName.indexOf(',') == -1){ - tmp = $("#sfile").val().split('/'); - sfile = encodeURIComponent(tmp[tmp.length-1]); - }else{ - sfile = encodeURIComponent(dirName); - } - - dfile = encodeURIComponent($("#dfile").val()); - layer.closeAll(); - layer.msg(lan.files.zip_the, {icon: 16,time: 0,shade: [0.3, '#000']}); - $.post('/files/zip', 'sfile=' + sfile + '&dfile=' + dfile + '&type=tar&path='+encodeURIComponent(path), function(rdata) { - layer.closeAll(); - if(rdata == null || rdata == undefined){ - layer.msg(lan.files.zip_ok,{icon:1}); - getFiles(path) - reloadFiles(); - return; - } - layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); - if(rdata.status) getFiles(path); - },'json'); - return - } - - param = dirName; - if(dirName.indexOf(',') != -1){ - tmp = path.split('/'); - dirName = path + '/' + tmp[tmp.length-1]; - } - - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '650px', - title: lan.files.zip_title, - content: '
    ' - +'
    ' - +'' - +''+lan.files.zip_to+'\ - \ - ' - +'
    ' - +'
    ' - +'' - +'' - +'
    ' - +'
    ', - success:function(){ - $("#dfile").change(function(){ - var dfile = $(this).val() - tmp = dfile.split('.'); - if(tmp[tmp.length-1] != 'gz'){ - var path = $("#DirPathPlace input").val(); - tmp = path.split('/'); - dfile += '/' + tmp[tmp.length-1] + '.tar.gz' - $(this).val(dfile.replace(/\/\//g,'/')) - } - }); - } - }); - -} - -//解压目录 -function unZip(fileName, type) { - var path = $("#DirPathPlace input").val(); - if(type.length ==3){ - var sfile = encodeURIComponent($("#sfile").val()); - var dfile = encodeURIComponent($("#dfile").val()); - var coding = $("select[name='coding']").val(); - var tip = layer.msg(lan.files.unzip_the, {icon: 16,time: 0,shade: [0.3, '#000']}); - $.post('/files/unzip', 'sfile=' + sfile + '&dfile=' + dfile +'&type=' + type + '&path='+encodeURIComponent(path), function(rdata) { - layer.close(tip); - showMsg(rdata.msg, function(){ - getFiles(path); - },{icon: rdata.status ? 1 : 2},2000); - },'json'); - return; - } - - type = (type == 1) ? 'tar':'zip'; - var umpass = ''; - if(type == 'zip'){ - umpass = '
    \ - '+lan.files.zip_pass_title+'\ - \ -
    '; - } - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '490px', - title: '解压文件', - content: '
    ' - +'
    ' - +''+lan.files.unzip_name+'
    ' - +'
    '+lan.files.unzip_to+'
    ' - + umpass +'
    '+lan.files.unzip_coding+'' - +'
    ' - +'
    ' - +'' - +'' - +'
    ' - +'
    ' - }); -} - -function isCompressFile(fileName){ - var ext = fileName.split('.'); - var extName = ext[ext.length-1].toLowerCase(); - var support = ['zip','gz','tgz','rar']; - for (x in support) { - if (support[x]==extName){ - return true; - } - } - return false; -} - -function unCompressFile(fileName, type = 0){ - // 解压文件 - var path = $("#DirPathPlace input").val(); - if(type == 3){ - var sfile = encodeURIComponent($("#sfile").val()); - var dfile = encodeURIComponent($("#dfile").val()); - var coding = $("select[name='coding']").val(); - var tip = layer.msg('正在解压,请稍候...', {icon: 16,time: 0,shade: [0.3, '#000']}); - $.post('/files/uncompress', 'sfile=' + sfile + '&dfile=' + dfile +'&type=' + type + '&path='+encodeURIComponent(path), function(rdata) { - layer.close(tip); - showMsg(rdata.msg, function(){ - layer.closeAll(); - getFiles(path); - },{icon: rdata.status ? 1 : 2},2000); - },'json'); - return; - } - - // var umpass = '
    \ - // 解压密码\ - // \ - //
    '; - layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '490px', - title: '解压文件', - content: '
    \ -
    \ - 文件名\ - \ -
    \ -
    \ - 解压到\ - \ -
    \ -
    \ - 编码\ - \ -
    \ -
    \ - \ - \ -
    \ -
    ' - }); -} - -//是否压缩文件 -function isZip(fileName){ - var ext = fileName.split('.'); - var extName = ext[ext.length-1].toLowerCase(); - if( extName == 'zip') return 0; - if( extName == 'gz' || extName == 'tgz') return 1; - return -1; -} - -//是否文本文件 -function isText(fileName){ - var exts = ['rar','zip','tar.gz','gz','iso','xsl','doc','xdoc','jpeg', - 'jpg','png','gif','bmp','tiff','exe','so','7z','bz']; - return isExts(fileName,exts)?false:true; -} - -//是否图片文件 -function isImage(fileName){ - var exts = ['jpg','jpeg','png','bmp','gif','tiff','ico']; - return isExts(fileName,exts); -} - -//是否为指定扩展名 -function isExts(fileName,exts){ - var ext = fileName.split('.'); - if(ext.length < 2) return false; - var extName = ext[ext.length-1].toLowerCase(); - for(var i=0;i' - }); -} - -//获取文件数据 -function getFileBytes(fileName, fileSize){ - window.open('/files/download?filename='+encodeURIComponent(fileName)); -} - - -//上传文件 -function uploadFiles(){ - var path = $("#DirPathPlace input").val()+"/"; - layer.open({ - type:1, - closeBtn: 1, - title:lan.files.up_title, - area: ['500px','300px'], - shadeClose:false, - content:'
    \ - \ - \ - \ - \ - \ - \ - 文件编码:\ - \ - \ - \ -
      \ -
      ' - }); - uploadStart(); -} - -//设置权限 -function setChmod(action,fileName){ - if(action == 1){ - var chmod = $("#access").val(); - var chown = $("#chown").val(); - var data = 'filename='+ encodeURIComponent(fileName)+'&user='+chown+'&access='+chmod; - var loadT = layer.msg('正在设置...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/files/set_file_access',data,function(rdata){ - layer.close(loadT); - if(rdata.status) layer.closeAll(); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - var path = $("#DirPathPlace input").val(); - getFiles(path) - },'json'); - return; - } - - var toExec = fileName == lan.files.all?'batch(3,1)':'setChmod(1,\''+fileName+'\')'; - $.post('/files/file_access','filename='+encodeURIComponent(fileName),function(rdata){ - // console.log(rdata); - var sys_users = rdata.sys_users; - var own_html = ''; - var is_find_own_option = false; - for (var i = 0; i < sys_users.length; i++) { - var own = sys_users[i]; - if (rdata.chown==own){ - is_find_own_option = true; - own_html += ''; - } else { - own_html += ''; - } - } - if (!is_find_own_option){ - own_html += ''; - } - - layer.open({ - type:1, - closeBtn: 1, - title: '设置权限['+fileName+']', - area: '400px', - shadeClose:false, - content:'
      \ -
      \ - 所有者\ -

      读取

      \ -

      写入

      \ -

      执行

      \ -
      \ -
      \ - 用户组\ -

      读取

      \ -

      写入

      \ -

      执行

      \ -
      \ -
      \ - 公共\ -

      读取

      \ -

      写入

      \ -

      执行

      \ -
      \ -
      权限,\ - 所有者\ -
      \ -
      \ - \ - \ -
      \ -
      ' - }); - - onAccess(); - $("#access").keyup(function(){ - onAccess(); - }); - - $("input[type=checkbox]").change(function(){ - var idName = ['owner','group','public']; - var onacc = ''; - for(var n=0;n idName.length) continue; - if(onacc > 7) $("#access").val(access.substr(0,access.length-1)); - switch(onacc){ - case '1': - $("#"+idName[i]+"_x").prop('checked',true); - break; - case '2': - $("#"+idName[i]+"_w").prop('checked',true); - break; - case '3': - $("#"+idName[i]+"_x").prop('checked',true); - $("#"+idName[i]+"_w").prop('checked',true); - break; - case '4': - $("#"+idName[i]+"_r").prop('checked',true); - break; - case '5': - $("#"+idName[i]+"_r").prop('checked',true); - $("#"+idName[i]+"_x").prop('checked',true); - break; - case '6': - $("#"+idName[i]+"_r").prop('checked',true); - $("#"+idName[i]+"_w").prop('checked',true); - break; - case '7': - $("#"+idName[i]+"_r").prop('checked',true); - $("#"+idName[i]+"_w").prop('checked',true); - $("#"+idName[i]+"_x").prop('checked',true); - break; - } - } -} - - -//右键菜单 -function rightMenuClick(type,path,name){ - // console.log(type,path,name); - var displayZip = isZip(type); - var options = {items:[ - {text: "复制", onclick: function() {copyFile(path)}}, - {text: "剪切", onclick: function() {cutFile(path)}}, - {text: "重命名", onclick: function() {reName(0,name)}}, - {text: lan.files.file_menu_auth, onclick: function() {setChmod(0,path)}}, - {text: lan.files.file_menu_zip, onclick: function() {zip(path)}}, - ]}; - if(type == "dir"){ - options.items.push({text: lan.files.file_menu_del, onclick: function() { - deleteDir(path)} - }); - } - else if(isText(type)){ - options.items.push({text: lan.files.file_menu_edit, onclick: function() { - onlineEditFile(0,path); - }},{text: lan.files.file_menu_down, onclick: function() { - getFileBytes(path); - }},{ text: lan.files.file_menu_del, onclick: function() { - deleteFile(path); - }}); - } - else if(displayZip != -1){ - options.items.push({text: lan.files.file_menu_unzip, onclick: function() { - unZip(path,displayZip); - }},{text: lan.files.file_menu_down, onclick: function() { - getFileBytes(path); - }},{text: lan.files.file_menu_del, onclick: function() { - deleteFile(path); - }}); - } - else if(isImage(type)){ - options.items.push({text: lan.files.file_menu_img, onclick: function() { - getImage(path); - }},{text: lan.files.file_menu_down, onclick: function() { - getFileBytes(path); - }},{text: lan.files.file_menu_del, onclick: function() { - deleteFile(path); - }}); - } - else{ - options.items.push({text: lan.files.file_menu_down, onclick: function() { - getFileBytes(path); - }},{text: lan.files.file_menu_del, onclick: function() { - deleteFile(path); - }}); - } - return options; -} - -//右键批量操作 -function rightMenuClickAll(e){ - var menu = $("#rmenu"); - var windowWidth = $(window).width(), - windowHeight = $(window).height(), - menuWidth = menu.outerWidth(), - menuHeight = menu.outerHeight(), - x = (menuWidth + e.clientX < windowWidth) ? e.clientX : windowWidth - menuWidth, - y = (menuHeight + e.clientY < windowHeight) ? e.clientY : windowHeight - menuHeight; - - menu.css('top', y) - .css('left', x) - .css('position', 'fixed') - .css("z-index","1") - .show(); -} -//取目录大小 -function getPathSize(){ - var path = encodeURIComponent($("#DirPathPlace input").val()); - layer.msg("正在计算,请稍候...",{icon:16,time:0,shade: [0.3, '#000']}) - $.post("/files/get_dir_size","path="+path,function(rdata){ - layer.closeAll(); - $("#pathSize").text(rdata.msg); - },'json'); -} - -$("body").not(".def-log").click(function(){ - $("#rmenu").hide() -}); - -//指定路径 -$("#DirPathPlace input").keyup(function(e){ - if(e.keyCode == 13) { - var fpath = $(this).val(); - fpath = filterPath(fpath); - getFiles(fpath); - } -}); - -function pathPlaceBtn(path){ - var html = ''; - var title = ''; - if(path == '/'){ - html = '
    • '+lan.files.path_root+'
    • '; - } else { - var dst_path = path.split("/"); - for(var i = 0; i'+dst_path[i]+''; - } - } - - html = '
        '+html+'
      '; - $("#PathPlaceBtn").html(html); - $("#PathPlaceBtn ul li a").click(function(e){ - var go_path = $(this).attr("title"); - if(go_path.length>1){ - if(go_path.substr(go_path.length-1,go_path.length) =='/'){ - go_path = go_path.substr(0,go_path.length-1); - } - } - getFiles(go_path); - e.stopPropagation(); - }); - pathLeft(); -} -//计算当前目录偏移 -function pathLeft(){ - var UlWidth = $("#PathPlaceBtn ul").width(); - var SpanPathWidth = $("#PathPlaceBtn").width() - 50; - var Ml = UlWidth - SpanPathWidth; - if(UlWidth > SpanPathWidth ){ - $("#PathPlaceBtn ul").css("left",-Ml); - } - else{ - $("#PathPlaceBtn ul").css("left",0); - } -} - -//路径快捷点击 -$("#PathPlaceBtn").on("click", function(e){ - if($("#DirPathPlace").is(":hidden")){ - $("#DirPathPlace").css("display","inline"); - $("#DirPathPlace input").focus(); - $(this).hide(); - }else{ - $("#DirPathPlace").hide(); - $(this).css("display","inline"); - } - $(document).one("click", function(){ - $("#DirPathPlace").hide(); - $("#PathPlaceBtn").css("display","inline"); - }); - e.stopPropagation(); -}); -$("#DirPathPlace").on("click", function(e){ - e.stopPropagation(); -}); diff --git a/route/static/app/firewall.js b/route/static/app/firewall.js deleted file mode 100755 index 19f7d6875..000000000 --- a/route/static/app/firewall.js +++ /dev/null @@ -1,370 +0,0 @@ -setTimeout(function(){ - getSshInfo(); -},500); - -setTimeout(function(){ - showAccept(1); -},1000); - - -$(function(){ - // start - $.post('/firewall/get_www_path',function(data){ - var html ='日志目录\ - 0KB\ - '; - $('#firewall_weblog').html(html); - - $.post('/files/get_dir_size','path='+data['path'], function(rdata){ - $("#logSize").html(rdata.msg); - },'json'); - },'json'); - // end -}); - -function closeLogs(){ - $.post('/files/close_logs','',function(rdata){ - $("#logSize").html(rdata.msg); - layer.msg('已清理!',{icon:1}); - },'json'); -} - -$("#firewalldType").change(function(){ - var type = $(this).val(); - var w = '120px'; - var p = '端口'; - var t = '放行'; - var m = '说明: 支持放行端口范围,如: 3000:3500'; - if(type == 'address'){ - w = '150px'; - p = '欲屏蔽的IP地址'; - t = '屏蔽'; - m = '说明: 支持屏蔽IP段,如: 192.168.0.0/24'; - } - $("#AcceptPort").css("width",w); - $("#AcceptPort").attr('placeholder',p); - $("#toAccept").html(t); - $("#f-ps").html(m); -}); - - -function sshMgr(){ - - $.post('/firewall/get_ssh_info', '', function(rdata){ - var ssh_status = rdata.status ? 'checked':''; - var pass_prohibit_status = rdata.pass_prohibit_status ? 'checked':''; - var con = '
      \ -
      \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
      名称状态
      启动SSH\ -
      \ - \ - \ -
      \ -
      禁止密码登陆\ -
      \ - \ - \ -
      \ -
      \ -
      \ -
      '; - layer.open({ - type: 1, - title: "SSH管理", - area: ['300px', '230px'], - closeBtn: 1, - shadeClose: false, - content: '
      '+con+'
      ', - success:function(){ - }, - }); - - },'json'); -} - - -function getSshInfo(){ - $.post('/firewall/get_ssh_info', '', function(rdata){ - if(!rdata.status){ - $("#mstscPort").attr('disabled','disabled'); - } - - $("#mstscPort").val(rdata.port); - var isPint = ''; - if(rdata.ping){ - isPing = ""; - }else{ - isPing = ""; - } - $("#is_ping").html(isPing); - - // console.log(rdata.firewall_status); - var fStatus = ''; - if (rdata.firewall_status){ - fStatus = ""; - }else{ - fStatus = ""; - } - $("#firewall_status").html(fStatus); - - showAccept(1); - - },'json'); -} - - -/** - * 修改远程端口 - */ - -function mstsc(port) { - layer.confirm('更改远程端口时,将会注消所有已登录帐户,您真的要更改远程端口吗?', {title: '远程端口'}, function(index) { - var data = "port=" + port; - var loadT = layer.load({ - shade: true, - shadeClose: false - }); - $.post('/firewall/set_ssh_port', data, function(ret) { - layer.msg(ret.msg,{icon:ret.status?1:2}) - layer.close(loadT); - getSshInfo(); - },'json'); - }); -} - -/** - * 更改禁ping状态 - * @param {Int} state 0.禁ping 1.可ping - */ -function ping(status){ - var msg = status == 1 ? '禁PING后不影响服务器正常使用,但无法ping通服务器,您真的要禁PING吗?' : '解除禁PING状态可能会被黑客发现您的服务器,您真的要解禁吗?'; - layer.confirm(msg,{title:'是否禁ping',closeBtn:2,cancel:function(){ - if(status == 1){ - $("#noping").prop("checked",true); - } else { - $("#noping").prop("checked",false); - } - }},function(){ - layer.msg('正在处理,请稍候...',{icon:16,time:20000}); - $.post('/firewall/set_ping','status='+status, function(data) { - layer.closeAll(); - if (data['status'] == true) { - if(status == 1){ - layer.msg(data['msg'], {icon: 1}); - } else { - layer.msg('已解除禁PING', {icon: 1}); - } - setTimeout(function(){window.location.reload();},3000); - } else { - layer.msg('连接服务器失败', {icon: 2}); - } - },'json'); - },function(){ - if(status == 1){ - $("#noping").prop("checked",true); - } else { - $("#noping").prop("checked",false); - } - }); -} - - - -/** - * 更改防火墙状态 - * @param {Int} state 0,开启 1.禁用 - */ -function firewall(status){ - var msg = status == 1 ? '禁用防火墙会增加服务器不安全性,您真的要禁用防火墙吗?' : '开启防火墙,增加服务器安全!'; - layer.confirm(msg,{title:'是否开启防火墙!',closeBtn:2,cancel:function(){ - if(status == 1){ - $("#firewall_status").prop("checked",true); - } else { - $("#firewall_status").prop("checked",false); - } - }},function(){ - layer.msg('正在处理,请稍候...',{icon:16,time:20000}); - $.post('/firewall/set_fw','status='+status, function(data) { - layer.closeAll(); - if (data['status'] == true) { - layer.msg(data['msg'], {icon: 1}); - setTimeout(function(){window.location.reload();},3000); - } else { - layer.msg('连接服务器失败', {icon: 2}); - } - },'json'); - },function(){ - if(status == 1){ - $("#firewall_status").prop("checked",true); - } else { - $("#firewall_status").prop("checked",false); - } - }); -} - - -/** - * 设置远程服务状态 - * @param {Int} state 0.启用 1.关闭 - */ -function setMstscStatus(){ - status = $("#sshswitch").prop("checked")==true?1:0; - var msg = status==1?'停用SSH服务的同时也将注销所有已登录用户,继续吗?':'确定启用SSH服务吗?'; - layer.confirm(msg,{title:'警告',closeBtn:2,cancel:function(){ - if(status == 0){ - $("#sshswitch").prop("checked",false); - } else { - $("#sshswitch").prop("checked",true); - } - }},function(index){ - if(index > 0){ - layer.msg('正在处理,请稍候...',{icon:16,time:20000}); - $.post('/firewall/set_ssh_status','status='+status,function(rdata){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - } - },function(){ - if(status == 0){ - $("#sshswitch").prop("checked",false); - } else { - $("#sshswitch").prop("checked",true); - } - }); -} - -/** - * 设置远程服务状态 - * @param {Int} state 0.启用 1.关闭 - */ -function setSshPassStatus(){ - status = $("#pass_status").prop("checked")==true?1:0; - var msg = status==1?'开启密码登陆,继续吗?':'确定禁止密码登陆吗?'; - layer.confirm(msg,{title:'警告',closeBtn:2,cancel:function(){ - if(status == 0){ - $("#pass_status").prop("checked",false); - } else { - $("#pass_status").prop("checked",true); - } - }},function(index){ - if(index > 0){ - layer.msg('正在处理,请稍候...',{icon:16,time:20000}); - $.post('/firewall/set_ssh_pass_status','status='+status,function(rdata){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - } - },function(){ - if(status == 0){ - $("#pass_status").prop("checked",false); - } else { - $("#pass_status").prop("checked",true); - } - }); -} - -/** - * 取回数据 - * @param {Int} page 分页号 - */ -function showAccept(page,search) { - search = search == undefined ? '':search; - var loadT = layer.load(); - $.post('/firewall/get_list','limit=10&p=' + page+"&search="+search, function(data) { - layer.close(loadT); - var body = ''; - for (var i = 0; i < data.data.length; i++) { - var status = ''; - switch(data.data[i].status){ - case 0: - status = '未使用'; - break; - case 1: - status = '外网不通'; - break; - default: - status = '正常'; - break; - } - body += "\ - " + data.data[i].id + "\ - " + data.data[i].protocol + "\ - " + (data.data[i].port.indexOf('.') == -1?'放行端口'+':['+data.data[i].port+']':'屏蔽IP'+':['+data.data[i].port+']') + "\ - " + status + "\ - " + data.data[i].addtime + "\ - " + data.data[i].ps + "\ - 删除\ - "; - } - $("#firewallBody").html(body); - $("#firewallPage").html(data.page); - },'json'); -} - -//添加放行 -function addAcceptPort(){ - var type = $("#firewalldType").val(); - var port = $("#AcceptPort").val(); - var ps = $("#Ps").val(); - var protocol = $('select[name="protocol"]').val(); - var action = "add_drop_address"; - if(type == 'port'){ - ports = port.split(':'); - for(var i=0;i 65535 ){ - layer.msg('端口范围不合法!',{icon:5}); - return; - } - } - action = "add_accept_port"; - } - - - if(ps.length < 1){ - layer.msg('备注/说明 不能为空!',{icon:2}); - $("#Ps").focus(); - return; - } - var loadT = layer.msg('正在添加,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}) - $.post('/firewall/'+action,'port='+port+"&ps="+ps+'&type='+type+'&protocol='+protocol,function(rdata){ - layer.close(loadT); - if(rdata.status == true || rdata.status == 'true'){ - layer.msg(rdata.msg,{icon:1}); - showAccept(1); - $("#AcceptPort").val(''); - $("#Ps").val(''); - }else{ - layer.msg(rdata.msg,{icon:2}); - } - - $("#AcceptPort").attr('value',''); - $("#Ps").attr('value',''); - },'json'); -} - -//删除放行 -function delAcceptPort(id, port,protocol) { - var action = "del_drop_address"; - if(port.indexOf('.') == -1){ - action = "del_accept_port"; - } - - layer.confirm(lan.get('confirm_del',[port]), {title: '删除防火墙规则',closeBtn:2}, function(index) { - var loadT = layer.msg('正在删除,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}) - $.post("/firewall/"+action, "id=" + id + "&port=" + port+'&protocol='+protocol, function(ret) { - layer.close(loadT); - layer.msg(ret.msg,{icon:ret.status?1:2}) - showAccept(1); - },'json'); - }); -} diff --git a/route/static/app/index.js b/route/static/app/index.js deleted file mode 100755 index c623ad35e..000000000 --- a/route/static/app/index.js +++ /dev/null @@ -1,1463 +0,0 @@ -//获取负载 -function getLoad(data) { - $("#LoadList .mask").html("获取中.."); - setCookie('one', data.one); - setCookie('five', data.five); - setCookie('fifteen', data.fifteen); - var transformLeft, transformRight, LoadColor, Average, Occupy, AverageText, conterError = ''; - var index = $("#LoadList"); - if (Average == undefined) Average = data.one; - setCookie('conterError', conterError); - Occupy = Math.round((Average / data.max) * 100); - if (Occupy > 100) Occupy = 100; - if (Occupy <= 30) { - LoadColor = '#20a53a'; - AverageText = '运行流畅'; - } else if (Occupy <= 70) { - LoadColor = '#6ea520'; - AverageText = '运行正常'; - } else if (Occupy <= 90) { - LoadColor = '#ff9900'; - AverageText = '运行缓慢'; - } else { - LoadColor = '#dd2f00'; - AverageText = '运行堵塞'; - } - index.find('.circle').css("background", LoadColor); - index.find('.mask').css({ "color": LoadColor }); - $("#LoadList .mask").html("%"); - $('#Load').html(Occupy); - $('#LoadState').html('' + AverageText + ''); - setImg(); -} - -$('#LoadList .circle').click(function() { - // getNet(); -}); - -$('#LoadList .mask').hover(function() { - var one, five, fifteen; - var that = this; - one = getCookie('one'); - five = getCookie('five'); - fifteen = getCookie('fifteen'); - var text = '最近1分钟平均负载:' + one + '
      最近5分钟平均负载:' + five + '
      最近15分钟平均负载:' + fifteen + ''; - layer.tips(text, that, { time: 0, tips: [1, '#999'] }); -}, function() { - layer.closeAll('tips'); -}); - - -function showCpuTips(rdata){ - $('#cpuChart .mask').unbind().hover(function() { - var cpuText = ''; - if (rdata.cpu[2].length == 1){ - var cpuUse = parseFloat(rdata.cpu[2][0] == 0 ? 0 : rdata.cpu[2][0]).toFixed(1); - cpuText += 'CPU-1:' + cpuUse + '%'; - } else { - for (var i = 1; i < rdata.cpu[2].length + 1; i++) { - var cpuUse = parseFloat(rdata.cpu[2][i - 1] == 0 ? 0 : rdata.cpu[2][i - 1]).toFixed(1); - if (i % 2 != 0) { - cpuText += 'CPU-' + i + ':' + cpuUse + '% | '; - } else { - cpuText += 'CPU-' + i + ':' + cpuUse + '%'; - cpuText += '\n'; - } - } - } - layer.tips(rdata.cpu[3] + "
      " + rdata.cpu[5] + "个物理CPU," + (rdata.cpu[4]) + "个物理核心," + rdata.cpu[1] + "个逻辑核心
      " + cpuText, this, { time: 0, tips: [1, '#999'] }); - }, function() { - layer.closeAll('tips'); - }); -} - - -function rocket(sum, m) { - var n = sum - m; - $(".mem-release").find(".mask span").text(n); -} - -//释放内存 -function reMemory() { - setTimeout(function() { - $(".mem-release").find('.mask').css({ 'color': '#20a53a', 'font-size': '14px' }).html('1' + lan.index.memre_ok_0 + ' '); - $.post('/system/rememory', '', function(rdata) { - var percent = getPercent(rdata.memRealUsed, rdata.memTotal); - var memText = Math.round(rdata.memRealUsed) + "/" + Math.round(rdata.memTotal) + " (MB)"; - percent = Math.round(percent); - $(".mem-release").find('.mask').css({ 'color': '#20a53a', 'font-size': '14px' }).html("" + percent + "" + lan.index.memre_ok); - setCookie("mem-before", memText); - var memNull = Math.round(getCookie("memRealUsed") - rdata.memRealUsed); - setTimeout(function() { - if (memNull > 0) { - $(".mem-release").find('.mask').css({ 'color': '#20a53a', 'font-size': '14px', 'line-height': '22px', 'padding-top': '22px' }).html("" + percent + "" + lan.index.memre_ok_1 + "
      " + memNull + "MB"); - } else { - $(".mem-release").find('.mask').css({ 'color': '#20a53a', 'font-size': '14px' }).html("" + percent + "" + lan.index.memre_ok_2); - } - $(".mem-release").removeClass("mem-action"); - $("#memory").text(memText); - setCookie("memRealUsed", rdata.memRealUsed); - }, 1000); - setTimeout(function() { - $(".mem-release").find('.mask').removeAttr("style").html("" + percent + "%"); - $(".mem-release").find(".mem-re-min").show(); - }, 2000) - },'json'); - }, 2000); -} - -function getPercent(num, total) { - num = parseFloat(num); - total = parseFloat(total); - if (isNaN(num) || isNaN(total)) { - return "-"; - } - return total <= 0 ? "0%" : (Math.round(num / total * 10000) / 100.00); -} - -function getDiskInfo() { - $.get('/system/disk_info', function(rdata) { - var dBody; - for (var i = 0; i < rdata.length; i++) { - var LoadColor = setcolor(parseInt(rdata[i].size[3].replace('%', '')), false, 75, 90, 95); - - //判断inode信息是否存在 - var inodes = ''; - if ( typeof(rdata[i]['inodes']) !=='undefined' ){ - inodes = '
      ' + rdata[i].size[3].replace('%', '') + '%
      '; - - var ipre = parseInt(rdata[i].inodes[3].replace('%', '')); - if (ipre > 95) { - $("#messageError").show(); - $("#messageError").append('

      分区[' + rdata[i].path + ']当前Inode使用率超过' + ipre + '%,当使用率满100%时将无法在此分区创建文件,请及时清理![清理垃圾]

      '); - } - } - - if (rdata[i].path == '/' || rdata[i].path == '/www') { - if (rdata[i].size[2].indexOf('M') != -1) { - $("#messageError").show(); - $("#messageError").append('

      ' + lan.get('diskinfo_span_1', [rdata[i].path]) + '[清理垃圾]

      '); - } - } - - dBody = '
    • ' + - '

      ' + rdata[i].path + '

      ' + - '
      ' + - '
      ' + - '
      ' + - '
      ' + - '
      ' + - '
      ' + - '
      '+ inodes +'
      ' + - '

      ' + rdata[i].size[1] + '/' + rdata[i].size[0] + '

      ' + - '
    • ' - $("#systemInfoList").append(dBody); - setImg(); - } - },'json'); -} - -//清理垃圾 -function clearSystem() { - var loadT = layer.msg('正在清理系统垃圾 ', { icon: 16, time: 0, shade: [0.3, "#000"] }); - $.get('/system?action=ClearSystem', function(rdata) { - layer.close(loadT); - layer.msg('清理完成,共清理[' + rdata[0] + ']个文件,释放[' + toSize(rdata[1]) + ']磁盘空间!', { icon: 1 }); - }); -} - -function setMemImg(info){ - - var memRealUsed = toSize(info.memRealUsed); - var memTotal = toSize(info.memTotal); - - var memRealUsedVal = memRealUsed.split(' ')[0]; - var memTotalVal = memTotal.split(' ')[0]; - var unit = memTotal.split(' ')[1]; - - var mem_txt = memRealUsedVal + '/' + memTotalVal + ' ('+ unit +')'; - setCookie("mem-before", mem_txt); - $("#memory").html(mem_txt); - - var memPre = Math.floor(info.memRealUsed / (info.memTotal / 100)); - $("#left").html(memPre); - setcolor(memPre, "#left", 75, 90, 95); - - var memFree = info.memTotal - info.memRealUsed; - if (memFree/(1024*1024) < 64) { - $("#messageError").show(); - $("#messageError").append('

      当前可用物理内存小于64M,这可能导致MySQL自动停止,站点502等错误,请尝试释放内存!

      ') - } -} - -function getInfo() { - $.get("/system/system_total", function(info) { - - setMemImg(info); - - $("#info").html(info.system); - $("#running").html(info.time); - var _system = info.system; - if(_system.indexOf("Windows") != -1){ - $(".ico-system").addClass("ico-windows"); - } else if(_system.indexOf("CentOS") != -1) { - $(".ico-system").addClass("ico-centos"); - } else if(_system.indexOf("Ubuntu") != -1) { - $(".ico-system").addClass("ico-ubuntu"); - } else if(_system.indexOf("Debian") != -1) { - $(".ico-system").addClass("ico-debian"); - } else if(_system.indexOf("Fedora") != -1) { - $(".ico-system").addClass("ico-fedora"); - } else if(_system.indexOf("Mac") != -1){ - $(".ico-system").addClass("ico-mac"); - } else { - $(".ico-system").addClass("ico-linux"); - } - $("#core").html(info.cpuNum + ' 核心'); - $("#state").html(info.cpuRealUsed); - setcolor(info.cpuRealUsed, "#state", 30, 70, 90); - - - // if (info.isuser > 0) { - // $("#messageError").show(); - // $("#messageError").append('

      ' + lan.index.user_warning + ' [不可忽略] [立即修改]

      ') - // } - setImg(); - },'json'); -} - - -function setcolor(pre, s, s1, s2, s3) { - var LoadColor; - if (pre <= s1) { - LoadColor = '#20a53a'; - } else if (pre <= s2) { - LoadColor = '#6ea520'; - } else if (pre <= s3) { - LoadColor = '#ff9900'; - } else { - LoadColor = '#dd2f00'; - } - if (s == false) { - return LoadColor; - } - var co = $(s).parent('.mask'); - co.css("color", LoadColor); - co.parent('.circle').css("background", LoadColor); -} - - -function getNet() { - var up, down; - $.get("/system/network", function(net) { - - console.log(net); - - $("#InterfaceSpeed").html(lan.index.interfacespeed + ": 1.0Gbps"); - $("#upSpeed").html(toSize(net.up)); - $("#downSpeed").html(toSize(net.down)); - $("#downAll").html(toSize(net.downTotal)); - $("#downAll").attr('title', lan.index.package + ':' + net.downPackets) - $("#upAll").html(toSize(net.upTotal)); - $("#upAll").attr('title', lan.index.package + ':' + net.upPackets) - $("#core").html(net.cpu[1] + " " + lan.index.cpu_core); - $("#state").html(net.cpu[0]); - setcolor(net.cpu[0], "#state", 30, 70, 90); - setCookie("upNet", net.up); - setCookie("downNet", net.down); - - //负载 - getLoad(net.load); - - //内存 - setMemImg(net.mem); - - //绑定hover事件 - setImg(); - showCpuTips(net); - - },'json'); -} - -//网络IO -function netImg() { - - var xData = []; - var yData = []; - var zData = []; - - function getTime() { - var now = new Date(); - var hour = now.getHours(); - var minute = now.getMinutes(); - var second = now.getSeconds(); - if (minute < 10) { - minute = "0" + minute; - } - if (second < 10) { - second = "0" + second; - } - var nowdate = hour + ":" + minute + ":" + second; - return nowdate; - } - - function ts(m) { return m < 10 ? '0' + m : m } - - function format(sjc) { - var time = new Date(sjc); - var h = time.getHours(); - var mm = time.getMinutes(); - var s = time.getSeconds(); - return ts(h) + ':' + ts(mm) + ':' + ts(s); - } - - var default_unit = 'KB/s'; - function addData(shift) { - xData.push(getTime()); - - if (getCookie("upNet") > getCookie("downNet") ){ - tmp = getCookie("upNet"); - } else { - tmp = getCookie("downNet"); - } - var tmpSize = toSize(tmp); - default_unit = tmpSize.split(' ')[1] + '/s'; - - - var upNetTmp = toSize(getCookie("upNet")); - var downNetTmp = toSize(getCookie("downNet")); - - var upNetTmpSize = upNetTmp.split(' ')[0]; - var downNetTmp = downNetTmp.split(' ')[0]; - - yData.push(upNetTmpSize); - zData.push(downNetTmp); - if (shift) { - xData.shift(); - yData.shift(); - zData.shift(); - } - } - for (var i = 8; i >= 0; i--) { - var time = (new Date()).getTime(); - xData.push(format(time - (i * 3 * 1000))); - yData.push(0); - zData.push(0); - } - // 指定图表的配置项和数据 - var option = { - title: { - text: "接口流量实时", - left: 'center', - textStyle: { - color: '#888888', - fontStyle: 'normal', - fontFamily: "宋体", - fontSize: 16, - } - }, - tooltip: { - trigger: 'axis' - }, - legend: { - data: [lan.index.net_up, lan.index.net_down], - bottom: '2%' - }, - xAxis: { - type: 'category', - boundaryGap: false, - data: xData, - axisLine: { - lineStyle: { - color: "#666" - } - } - }, - yAxis: { - name: '单位 '+ default_unit, - splitLine: { - lineStyle: { color: "#eee" } - }, - axisLine: { - lineStyle: { color: "#666" } - } - }, - series: [{ - name: '上行', - type: 'line', - data: yData, - smooth: true, - showSymbol: false, - symbol: 'circle', - symbolSize: 6, - areaStyle: { - normal: { - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ - offset: 0, - color: 'rgba(255, 140, 0,0.5)' - }, { - offset: 1, - color: 'rgba(255, 140, 0,0.8)' - }], false) - } - }, - itemStyle: { - normal: { - color: '#f7b851' - } - }, - lineStyle: { - normal: { - width: 1 - } - } - }, - { - name: '下行', - type: 'line', - data: zData, - smooth: true, - showSymbol: false, - symbol: 'circle', - symbolSize: 6, - areaStyle: { - normal: { - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ - offset: 0, - color: 'rgba(30, 144, 255,0.5)', - }, { - offset: 1, - color: 'rgba(30, 144, 255,0.8)', - }], false) - } - }, - itemStyle: { - normal: { - color: '#52a9ff', - } - }, - lineStyle: { - normal: { - width: 1, - } - } - }] - }; - - var echartsNetImg = echarts.init(document.getElementById('netImg')); - setInterval(function() { - getNet(); - addData(true); - echartsNetImg.setOption({ - yAxis: { - name: '单位 '+ default_unit, - splitLine: { lineStyle: { color: "#eee" } }, - axisLine: { lineStyle: { color: "#666" } } - }, - xAxis: { - data: xData - }, - series: [{ - name: lan.index.net_up, - data: yData - }, { - name: lan.index.net_down, - data: zData - }] - }); - }, 3000); - - // 使用刚指定的配置项和数据显示图表。 - echartsNetImg.setOption(option); - window.addEventListener("resize", function() { - echartsNetImg.resize(); - }); -} - - -function setImg() { - $('.circle').each(function(index, el) { - var num = $(this).find('span').text() * 3.6; - if (num <= 180) { - $(this).find('.left').css('transform', "rotate(0deg)"); - $(this).find('.right').css('transform', "rotate(" + num + "deg)"); - } else { - $(this).find('.right').css('transform', "rotate(180deg)"); - $(this).find('.left').css('transform', "rotate(" + (num - 180) + "deg)"); - }; - }); - - $('.diskbox .mask').unbind().hover(function() { - layer.closeAll('tips'); - var that = this; - var conterError = $(this).attr("data"); - layer.tips(conterError, that, { time: 0, tips: [1, '#999'] }); - }, function() { - layer.closeAll('tips'); - }); -} - -// 检查更新 -setTimeout(function() { - $.get('/system/update_server?type=check', function(rdata) { - if (rdata.status == false) return; - if (rdata.data != undefined) { - $("#toUpdate").html('更新'); - $('#toUpdate a').html('更新.'); - $('#toUpdate a').css("position","relative"); - return; - } - },'json').error(function() { - }); -}, 3000); - - -//检查更新 -function checkUpdate() { - var loadT = layer.msg(lan.index.update_get, { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.get('/system/update_server?type=check', function(rdata) { - layer.close(loadT); - - if (rdata.data == 'download'){ - updateStatus();return; - } - - if (rdata.status === false) { - layer.confirm(rdata.msg, { title: lan.index.update_check, icon: 1, closeBtn: 1, btn: [lan.public.know, lan.public.close] }); - return; - } - layer.msg(rdata.msg, { icon: 1 }); - if (rdata.data != undefined) updateMsg(); - },'json'); -} - -function updateMsg(){ - $.get('/system/update_server?type=info',function(rdata){ - - if (rdata.data == 'download'){ - updateStatus();return; - } - - var v = rdata.data.version; - var v_info = ''; - if (v.split('.').length>3){ - v_info = "测试版本"; - } else { - v_info = "正式版本"; - } - - layer.open({ - type:1, - title:v_info + '升级到['+rdata.data.version+']', - area: '400px', - shadeClose:false, - closeBtn:2, - content:'
      ' - +'

      '+rdata.data.content+'

      ' - +'
      ' - +'' - +'' - +'
      ' - +'
      ' - }); - },'json'); -} - - -//开始升级 -function updateVersion(version) { - var loadT = layer.msg('正在升级面板..', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.get('/system/update_server?type=update&version='+version, function(rdata) { - - layer.closeAll(); - if (rdata.status === false) { - layer.msg(rdata.msg, { icon: 5, time: 5000 }); - return; - } - layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); - if (rdata.status) { - $("#btversion").html(version); - $("#toUpdate").html(''); - } - },'json').error(function() { - layer.msg('更新失败,请重试!', { icon: 2 }); - setTimeout(function() { - window.location.reload(); - }, 3000); - }); -} - -function pluginIndexService(pname,pfunc, callback){ - $.post('/plugins/run', {name:'openresty', func:pfunc}, function(data) { - if (!data.status){ - layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']}); - return; - } - - if(typeof(callback) == 'function'){ - callback(data); - } - },'json'); -} - -//重启服务器 -function reBoot() { - layer.open({ - type: 1, - title: '重启服务器或者面板', - area: '330px', - closeBtn: 1, - shadeClose: false, - content: '' - }); - - $('.rebt-con a').click(function () { - var type = $(this).attr('data-id'); - switch (type) { - case 'panel': - layer.confirm('即将重启面板服务,继续吗?', { title: '重启面板服务', closeBtn: 1, icon: 3 }, function () { - var loadT = layer.load(); - $.post('/system/restart','',function (rdata) { - layer.close(loadT); - layer.msg(rdata.msg); - setTimeout(function () { window.location.reload(); }, 3000); - },'json'); - }); - break; - case 'server': - var rebootbox = layer.open({ - type: 1, - title: '安全重启服务器', - area: ['500px', '280px'], - closeBtn: 1, - shadeClose: false, - content: "
      \ -
      \ -

      注意,若您的服务器是一个容器,请取消。

      \ -
      \ -

      安全重启有利于保障文件安全,将执行以下操作:

      \ -

      1.停止Web服务

      \ -

      2.停止MySQL服务

      \ -

      3.开始重启服务器

      \ -

      4.等待服务器启动

      \ -
      \ -
      \ -
      \ - \ - \ -
      \ -
      " - }); - setTimeout(function () { - $(".btn-reboot").click(function () { - rebootbox.close(); - }) - $(".WSafeRestart").click(function () { - var body = '
      '; - $(".bt-window-restart").html(body); - $(".SafeRestartCode").append("

      正在停止Web服务

      "); - pluginIndexService('openresty', 'stop', function (r1) { - $(".SafeRestartCode p").addClass('c9'); - $(".SafeRestartCode").append("

      正在停止MySQL服务...

      "); - pluginIndexService('mysql','stop', function (r2) { - $(".SafeRestartCode p").addClass('c9'); - $(".SafeRestartCode").append("

      开始重启服务器...

      "); - $.post('/system/restart_server', '',function (rdata) { - $(".SafeRestartCode p").addClass('c9'); - $(".SafeRestartCode").append("

      等待服务器启动...

      "); - var sEver = setInterval(function () { - $.get("/system/system_total", function(info) { - clearInterval(sEver); - $(".SafeRestartCode p").addClass('c9'); - $(".SafeRestartCode").append("

      服务器重启成功!...

      "); - setTimeout(function () { - layer.closeAll(); - }, 3000); - }) - }, 3000); - }) - }) - }) - }) - }, 100); - break; - } - }); -} - -//修复面板 -function repPanel() { - layer.confirm(lan.index.rep_panel_msg, { title: lan.index.rep_panel_title, closeBtn: 1, icon: 3 }, function() { - var loadT = layer.msg(lan.index.rep_panel_the, { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.get('/system?action=RepPanel', function(rdata) { - layer.close(loadT); - layer.msg(lan.index.rep_panel_ok, { icon: 1 }); - }).error(function() { - layer.close(loadT); - layer.msg(lan.index.rep_panel_ok, { icon: 1 }); - }); - }); -} - -//获取警告信息 -function getWarning() { - $.get('/ajax?action=GetWarning', function(wlist) { - var num = 0; - for (var i = 0; i < wlist.data.length; i++) { - if (wlist.data[i].ignore_count >= wlist.data[i].ignore_limit) continue; - if (wlist.data[i].ignore_time != 0 && (wlist.time - wlist.data[i].ignore_time) < wlist.data[i].ignore_timeout) continue; - var btns = ''; - for (var n = 0; n < wlist.data[i].btns.length; n++) { - if (wlist.data[i].btns[n].type == 'javascript') { - btns += ' ' + wlist.data[i].btns[n].title + '' - } else { - btns += ' ' + wlist.data[i].btns[n].title + '' - } - } - $("#messageError").append('

      ' + wlist.data[i].body + btns + '

      '); - num++; - } - if (num > 0) $("#messageError").show(); - }); -} - -//按钮调用 -function warningTo(to_url, def) { - var loadT = layer.msg(lan.public.the_get, { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post(to_url, {}, function(rdata) { - layer.close(loadT); - layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); - if (rdata.status && def) setTimeout(function() { location.reload(); }, 1000); - },'json'); -} - -function setSafeHide() { - setCookie('safeMsg', '1'); - $("#safeMsg").remove(); -} - -//查看报告 -function showDanger(num, port) { - var atxt = "因未使用安全隔离登录,所有IP都可以尝试连接,存在较高风险,请立即处理。"; - if (port == "22") { - atxt = "因未修改SSH默认22端口,且未使用安全隔离登录,所有IP都可以尝试连接,存在较高风险,请立即处理。"; - } - layer.open({ - type: 1, - area: ['720px', '410px'], - title: '安全提醒(如你想放弃任何安全提醒通知,请删除宝塔安全登录插件)', - closeBtn: 1, - shift: 5, - content: '
      \ - \ - \ - \ - \ - \ - \ - \ - \ -
      风险类型:暴力破解 说明
      累计遭遇攻击总数:' + num + ' 详细(数据直接来源本服务器日志)
      风险等级:较高风险
      风险描述:' + atxt + '
      可参考解决方案:

      方案一:修改SSH默认端口,修改SSH验证方式为数字证书,清除近期登陆日志。

      方案二:购买宝塔企业运维版,一键部署安全隔离服务,高效且方便。

      \ -
      ' - }); - $(".showDanger td").css("padding", "8px"); -} - -function pluginInit(){ - $.post('/plugins/init', function(data){ - if (!data.status){ - return false; - } - - var rdata = data.data; - var plugin_list = ''; - - for (var i = 0; i < rdata.length; i++) { - var ver = rdata[i]['versions']; - var select_list = ''; - if (typeof(ver)=='string'){ - select_list = ''; - } else { - for (var vi = 0; vi < ver.length; vi++) { - - if (ver[vi] == rdata[i]['default_ver']){ - select_list += ''; - } else { - select_list += ''; - } - } - } - - var pn_checked = ''; - if (rdata[i]['name'] == 'swap'){ - var pn_checked = ''; - } - - plugin_list += '
    • \ - \ - \ - \ - '+pn_checked+'
    • '; - } - - layer.open({ - type: 1, - title: '推荐安装', - area: ["320px", "400px"], - closeBtn: 2, - shadeClose: false, - content:"\ -
      \ -
      \ -

      推荐以下一键套件,或在软件管理按需选择。

      \ - \ -
      \ -
      \ -

      经典LNMP

      \ -
      \ -
        " + plugin_list + "
      \ -
      一键安装
      \ -
      \ -
      \ -
      ", - success:function(l,index){ - $('.rec-box-con .onekey').click(function(){ - var post_data = []; - for (var i = 0; i < rdata.length; i++) { - var key_ver = '#select_'+rdata[i]['name']; - var key_checked = '#data_'+rdata[i]['name']; - - var val_checked = $(key_checked).prop("checked"); - if (val_checked){ - - var tmp = {}; - var val_key = $(key_ver).val(); - - tmp['version'] = val_key; - tmp['name'] = rdata[i]['name']; - post_data.push(tmp); - } - } - - $.post('/plugins/init_install', 'list='+JSON.stringify(post_data), function(data){ - showMsg(data.msg, function(){ - if (data.status){ - layer.closeAll(); - messageBox(); - } - },{ icon: data.status ? 1 : 2 },2000); - },'json'); - }); - }, - cancel:function(){ - layer.confirm('是否不再显示推荐安装套件?', {btn : ['确定', '取消'],title: "不再显示推荐?"}, function() { - $.post('/files/create_dir', 'path=/www/server/php', function(rdata) { - layer.closeAll(); - },'json'); - }); - } - }); - },'json'); -} - -function loadKeyDataCount(){ - var plist = ['mysql', 'gogs', 'gitea']; - for (var i = 0; i < plist.length; i++) { - pname = plist[i]; - function call(pname){ - $.post('/plugins/run', {name:pname, func:'get_total_statistics'}, function(data) { - try { - var rdata = $.parseJSON(data['data']); - } catch(e){ - return; - } - if (!rdata['status']){ - return; - } - var html = '
    • \ -

      '+pname+'

      \ - \ -
    • '; - $('#index_overview').append(html); - },'json'); - } - call(pname); - } -} - -$(function() { - $(".mem-release").hover(function() { - $(this).addClass("shine_green"); - if (!($(this).hasClass("mem-action"))) { - $(this).find(".mem-re-min").hide(); - $(this).find(".mask").css({ "color": "#d2edd8" }); - $(this).find(".mem-re-con").css({ "display": "block" }); - $(this).find(".mem-re-con").animate({ "top": "0", opacity: 1 }); - $("#memory").text('内存释放'); - } - }, function() { - $(this).removeClass("shine_green"); - $(this).find(".mask").css({ "color": "#20a53a" }); - $(this).find(".mem-re-con").css({ "top": "15px", opacity: 1, "display": "none" }); - $("#memory").text(getCookie("mem-before")); - $(this).find(".mem-re-min").hide(); - }).click(function() { - $(this).find(".mem-re-min").hide(); - if (!($(this).hasClass("mem-action"))) { - reMemory(); - var btlen = $(".mem-release").find(".mask span").text(); - $(this).addClass("mem-action"); - $(this).find(".mask").css({ "color": "#20a53a" }); - $(this).find(".mem-re-con").animate({ "top": "-400px", opacity: 0 }); - $(this).find(".pie_right .right").css({ "transform": "rotate(3deg)" }); - for (var i = 0; i < btlen; i++) { - setTimeout("rocket(" + btlen + "," + i + ")", i * 30); - } - } - }); - - $("select[name='network-io'],select[name='disk-io']").change(function () { - var key = $(this).val(), type = $(this).attr('name'); - if (type == 'network-io') { - if (key == 'ALL') { - key = ''; - } - setCookie('network_io_key', key); - } else { - if (key == 'ALL') { - key = ''; - } - setCookie('disk_io_key', key); - } - }); - - $('.tabs-nav span').click(function () { - var indexs = $(this).index(); - $(this).addClass('active').siblings().removeClass('active'); - $('.tabs-content .tabs-item:eq(' + indexs + ')').addClass('tabs-active').siblings().removeClass('tabs-active'); - $('.tabs-down select:eq(' + indexs + ')').removeClass('hide').siblings().addClass('hide'); - switch (indexs) { - case 0: - index.net.table.resize(); - break; - case 1: - index.iostat.table.resize(); - break; - } - }) -}); - -var index = { - common:{ - ts:function (m) { return m < 10 ? '0' + m : m }, - format:function (sjc) { - var time = new Date(sjc); - var h = time.getHours(); - var mm = time.getMinutes(); - var s = time.getSeconds(); - return h+ ':' + mm + ':' +s; - }, - getTime:function () { - var now = new Date(); - var hour = now.getHours(); - var minute = now.getMinutes(); - var second = now.getSeconds(); - if (minute < 10) { - minute = "0" + minute; - } - if (second < 10) { - second = "0" + second; - } - var nowdate = hour + ":" + minute + ":" + second; - return nowdate; - }, - }, - net: { - table: null, - data: { - xData: [], - yData: [], - zData: [] - }, - default_unit : 'KB/s', - init_select : false, - init: function(){ - for (var i = 8; i >= 0; i--) { - var time = (new Date()).getTime(); - index.net.data.xData.push(index.common.format(time - (i * 3 * 1000))); - index.net.data.yData.push(0); - index.net.data.zData.push(0); - } - - index.net.table = echarts.init(document.getElementById('netImg')); - var option = index.net.defaultOption(); - index.net.table.setOption(option); - - window.addEventListener("resize", function () { - index.net.table.resize(); - }); - }, - render:function(){ - index.net.table.setOption({ - yAxis: { - name: '单位 '+ index.net.default_unit, - splitLine: { lineStyle: { color: "#eee" } }, - axisLine: { lineStyle: { color: "#666" } } - }, - xAxis: { - data: index.net.data.xData - }, - series: [{ - name: lan.index.net_up, - data: index.net.data.yData - }, { - name: lan.index.net_down, - data: index.net.data.zData - }] - }); - }, - defaultOption:function(){ - var option = { - title: { - text: "", - left: 'center', - textStyle: { - color: '#888888', - fontStyle: 'normal', - fontFamily: "宋体", - fontSize: 16, - } - }, - tooltip: { - trigger: 'axis', - formatter :function (config) { - var _config = config, _tips = "时间:" + _config[0].axisValue + "
      "; - for (var i = 0; i < config.length; i++) { - if (typeof config[i].data == "undefined") { - return false; - } - _tips += '  ' + config[i].seriesName + ':' + config[i].data + ' '+ index.net.default_unit + '
      ' - } - return _tips; - } - - }, - legend: { - data: [lan.index.net_up, lan.index.net_down], - bottom: '2%' - }, - xAxis: { - type: 'category', - boundaryGap: false, - data: index.net.data.xData, - axisLine: { - lineStyle: { - color: "#666" - } - } - }, - yAxis: { - name: '单位 '+ index.net.default_unit, - splitLine: { - lineStyle: { color: "#eee" } - }, - axisLine: { - lineStyle: { color: "#666" } - } - }, - series: [{ - name: '上行', - type: 'line', - data: index.net.data.yData, - smooth: true, - showSymbol: false, - symbol: 'circle', - symbolSize: 6, - areaStyle: { - normal: { - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ - offset: 0, - color: 'rgba(255, 140, 0,0.5)' - }, { - offset: 1, - color: 'rgba(255, 140, 0,0.8)' - }], false) - } - }, - itemStyle: { - normal: { - color: '#f7b851' - } - }, - lineStyle: { - normal: { - width: 1 - } - } - }, - { - name: '下行', - type: 'line', - data: index.net.data.zData, - smooth: true, - showSymbol: false, - symbol: 'circle', - symbolSize: 6, - areaStyle: { - normal: { - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ - offset: 0, - color: 'rgba(30, 144, 255,0.5)', - }, { - offset: 1, - color: 'rgba(30, 144, 255,0.8)', - }], false) - } - }, - itemStyle: { - normal: { - color: '#52a9ff', - } - }, - lineStyle: { - normal: { - width: 1, - } - } - }] - }; - return option; - }, - - - add: function (up, down) { - var _net = this; - var limit = 8; - var d = new Date() - if (_net.data.xData.length >= limit) _net.data.xData.splice(0, 1); - if (_net.data.yData.length >= limit) _net.data.yData.splice(0, 1); - if (_net.data.zData.length >= limit) _net.data.zData.splice(0, 1); - - _net.data.xData.push(d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds()); - - if (up>down){ - var upTmp = toSizePos(up); - var upTmpSize = upTmp['name'].split(' ')[0]; - index.net.default_unit = upTmp['name'].split(' ')[1] + '/s'; - - var downTmpSize = toSizePos(down,upTmp['pos'])['name'].split(' ')[0]; - // console.log('up',upTmp['pos'],toSizePos(down, upTmp['pos']),downTmpSize); - - _net.data.zData.push(downTmpSize); - _net.data.yData.push(upTmpSize); - } else { - - var downTmp = toSizePos(down); - var downTmpSize = downTmp['name'].split(' ')[0]; - index.net.default_unit = downTmp['name'].split(' ')[1] + '/s'; - - var upTmpSize = toSizePos(up, downTmp['pos'])['name'].split(' ')[0]; - // console.log('down',downTmp['pos'],toSizePos(up, downTmp['pos']),upTmpSize); - - _net.data.zData.push(downTmpSize); - _net.data.yData.push(upTmpSize); - } - - }, - renderSelect:function(net){ - // console.log(net); - if (!index.net.init_select){ - var option = ''; - var network = net.network; - var network_io_key = getCookie('network_io_key'); - - for (var name in network) { - if (name == 'ALL'){ - option += ''; - } else if (network_io_key == name){ - option += ''; - } else { - option += ''; - } - } - $('select[name="network-io"]').html(option); - index.net.init_select = true; - } - } - }, - - iostat:{ - table: null, - data: { - xData: [], - yData: [], - zData: [], - tipsData: [] - }, - init_select:false, - default_unit : 'MB/s', - init:function(){ - for (var i = 8; i >= 0; i--) { - var time = (new Date()).getTime(); - index.iostat.data.xData.push(index.common.format(time - (i * 3 * 1000))); - index.iostat.data.yData.push(0); - index.iostat.data.zData.push(0); - index.iostat.data.tipsData.push({}); - } - - index.iostat.table = echarts.init(document.getElementById('ioStat')); - var option = index.iostat.defaultOption(); - index.iostat.table.setOption(option); - - window.addEventListener("resize", function () { - index.iostat.table.resize(); - }); - }, - - render:function(){ - index.iostat.table.setOption({ - tooltip: { - trigger: 'axis', - formatter :function (config) { - var _config = config, _tips = "时间:" + _config[0].axisValue + "
      ", options = { - read_bytes: '读取字节数', - read_count: '读取次数 ', - read_merged_count: '合并读取次数', - read_time: '读取延迟', - write_bytes: '写入字节数', - write_count: '写入次数', - write_merged_count: '合并写入次数', - write_time: '写入延迟', - }, data = index.iostat.data.tipsData[config[0].dataIndex], list = ['read_count', 'write_count', 'read_merged_count', 'write_merged_count', 'read_time', 'write_time',]; - for (var i = 0; i < config.length; i++) { - if (typeof config[i].data == "undefined") { - return false; - } - _tips += '  ' + config[i].seriesName + ':' + config[i].data + ' MB/s' + '
      ' - } - $.each(list, function (index, item) { - - if (typeof data[item] != 'undefined'){ - _tips += '  ' + options[item] + ':' + data[item] + (item.indexOf('time') > -1 ? ' ms' : ' 次/秒') + '
      '; - } - }) - return _tips; - } - }, - yAxis: { - name: '单位 '+ index.iostat.default_unit, - splitLine: { lineStyle: { color: "#eee" } }, - axisLine: { lineStyle: { color: "#666" } } - }, - xAxis: { - data: index.iostat.data.xData - }, - series: [{ - name: "读取", - data: index.iostat.data.yData - }, { - name: "写入", - data: index.iostat.data.zData - }] - }); - }, - defaultOption:function(){ - var option = { - title: { - text: "", - left: 'center', - textStyle: { - color: '#888888', - fontStyle: 'normal', - fontFamily: "宋体", - fontSize: 16, - } - }, - tooltip: { - trigger: 'axis' - }, - legend: { - data: ["读取", "写入"], - bottom: '2%' - }, - xAxis: { - type: 'category', - boundaryGap: false, - data: index.iostat.data.xData, - axisLine: { - lineStyle: { - color: "#666" - } - } - }, - yAxis: { - name: '单位 '+ index.iostat.default_unit, - splitLine: { - lineStyle: { color: "#eee" } - }, - axisLine: { - lineStyle: { color: "#666" } - } - }, - series: [{ - name: '读取', - type: 'line', - data: index.iostat.data.yData, - smooth: true, - showSymbol: false, - symbol: 'circle', - areaStyle: { - normal: { - color: 'rgb(255, 70, 131)' - } - }, - itemStyle: { - normal: { - color: 'rgb(255, 70, 131)' - } - }, - lineStyle: { - normal: { - width: 1, - } - } - }, - { - name: '写入', - type: 'line', - data: index.iostat.data.zData, - smooth: true, - showSymbol: false, - symbol: 'circle', - symbolSize: 6, - areaStyle: { - normal: { - color: 'rgba(46, 165, 186, .7)' - } - }, - itemStyle: { - normal: { - color: 'rgba(46, 165, 186, .7)' - } - }, - lineStyle: { - normal: { - width: 1, - } - } - }] - }; - return option; - }, - - renderSelect:function(data){ - if (!index.iostat.init_select){ - var option = ''; - var iostat = data.iostat; - var disk_io_key = getCookie('disk_io_key'); - - for (var name in iostat) { - if (name == 'ALL'){ - option += ''; - } else if (disk_io_key == name){ - option += ''; - } else { - option += ''; - } - } - $('select[name="disk-io"]').html(option); - index.iostat.init_select = true; - } - }, - add: function (read, write, data) { - var _iostat = this; - var limit = 8; - var d = new Date() - if (_iostat.data.xData.length >= limit) _iostat.data.xData.splice(0, 1); - if (_iostat.data.yData.length >= limit) _iostat.data.yData.splice(0, 1); - if (_iostat.data.zData.length >= limit) _iostat.data.zData.splice(0, 1); - if (_iostat.data.tipsData.length >= limit) _iostat.data.tipsData.splice(0, 1); - - - var readTmpSize = toSizeMB(read).split(' ')[0]; - var writeTmpSize = toSizeMB(write).split(' ')[0]; - - _iostat.data.zData.push(writeTmpSize); - _iostat.data.yData.push(readTmpSize); - _iostat.data.tipsData.push(data); - _iostat.data.xData.push(d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds()); - }, - - }, - getData:function(){ - - $.get("/system/network", function(net) { - - //网络IO - var network_io_key = getCookie('network_io_key'); - var network_data = net.network; - var network_select = network_data['ALL']; - if (network_io_key && network_io_key != ''){ - network_select = network_data[network_io_key]; - } - - index.net.add(network_select.up,network_select.down); - index.net.render(); - index.net.renderSelect(net); - - $("#upSpeed").html(toSize(network_select.up)); - $("#downSpeed").html(toSize(network_select.down)); - - $("#downAll").html(toSize(network_select.downTotal)); - $("#downAll").attr('title','数据包:' + network_select.downPackets) - $("#upAll").html(toSize(network_select.upTotal)); - $("#upAll").attr('title','数据包:' + network_select.upPackets) - - - //磁盘IO - var disk_io_key = getCookie('disk_io_key'); - var iostat_data = net.iostat; - var iostat_select = iostat_data['ALL']; - if (disk_io_key && disk_io_key != ''){ - iostat_select = iostat_data[disk_io_key]; - } - - index.iostat.add(iostat_select.read_bytes,iostat_select.write_bytes, iostat_select); - index.iostat.render(); - index.iostat.renderSelect(net); - - $("#readBytes").html(toSize(iostat_select.read_bytes)); - $("#writeBytes").html(toSize(iostat_select.write_bytes)); - $("#diskIops").html(iostat_select.read_count+":"+iostat_select.write_count+ " 次"); - $("#diskTime").html(iostat_select.read_time+":"+iostat_select.write_time +" ms"); - - - $("#core").html(net.cpu[1] + " " + lan.index.cpu_core); - $("#state").html(net.cpu[0]); - - setcolor(net.cpu[0], "#state", 30, 70, 90); - //负载 - getLoad(net.load); - //内存 - setMemImg(net.mem); - //绑定hover事件 - setImg(); - showCpuTips(net); - - },'json'); - }, - task:function(){ - // index.getData(); - setInterval(function() { - index.getData(); - }, 3000); - }, - init: function(){ - index.net.init(); - index.iostat.init(); - index.task(); - } -} diff --git a/route/static/app/logs.js b/route/static/app/logs.js deleted file mode 100644 index c61f3d7b1..000000000 --- a/route/static/app/logs.js +++ /dev/null @@ -1,205 +0,0 @@ - -$(document).ready(function(){ - logsLoad(); -}); - -function changeLogsViewH(){ - var l = $(window).height(); - $('.container-fluid .tab-view-box').css('height',l-80-40); - - $('#panelLogs').css('height',l-80-40-50); - - $('#logAudit .logAuditTab').css('height',l-80-40-50); - $('#logAudit .logAuditContent').css('height',l-80-40-50); - -} - -function logsLoad(){ - changeLogsViewH(); - $(window).resize(function(){ - changeLogsViewH(); - }); - - getLogs(1); -} - - - -$('#cutTab .tabs-item').click(function(){ - var type = $(this).data('name'); - - $('#cutTab .tabs-item').removeClass('active'); - $(this).addClass('active'); - - - $('.tab-view-box .tab-con').addClass('hide').removeClass('show').removeClass('w-full'); - $('#'+type).addClass('show').addClass('w-full'); - - switch(type){ - case 'panelLogs': - getLogs(1); - break; - case 'logAudit': - getAuditLogsFiles(); - break; - } - -}); - - -$('#panelLogs .refresh').click(function(){ - getLogs(1); -}); - -$('#panelLogs .clear').click(function(){ - delLogs(1); -}); - - -function getAuditLogsFiles(){ - var loadT = layer.msg('正在获取日志审计列表...', { icon: 16, time: 0, shade: 0.3 }); - $.post('/logs/get_audit_logs_files',{}, function(data) { - layer.close(loadT); - var option = ''; - for (var i = 0; i < data.length; i++) { - var tip = data[i]['name'] +' - '+data[i]['title'] + '(' + toSize(data[i]['size']) + ')'; - if (i==0){ - option += '
      '+tip+'
      '; - } else { - option += '
      '+tip+'
      '; - } - } - $("#logAudit .logAuditTab").html(option); - - getAuditFile(data[0]['name']); - $('#logAudit .logAuditItem').click(function(){ - $('#logAudit .logAuditItem').removeClass('active'); - $(this).addClass('active'); - getAuditFile($(this).data('file')); - }); - - },'json'); -} - - -function getAuditFile(log_name){ - var loadT = layer.msg('正在获取日志审计内容...', { icon: 16, time: 0, shade: 0.3 }); - $.post('/logs/get_audit_file',{log_name:log_name}, function(data) { - layer.close(loadT); - // console.log(data); - try{ - if (typeof(data) == 'object'){ - var plist = data.data; - - var pre_html ='
      \ -
      \ -
      \ - \ -
      \ - \ -
      \ -
      \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
      用户来源端口时间
      root117.139.193.29pts/02023-08-25 13:27 still logged in
      \ -
      \ -
      \ -
      \ -
      \ -
      \ -
      \ -
      ' - - // var pre_html = '\ - // \ - // \ - //
      时间角色事件
      '; - $('#logAudit .logAuditContent').html(pre_html); - - if (plist.length>0){ - var tmp = plist[0]; - var thead = ''; - tbody += '' - for (var i in tmp) { - tbody+=''+ i + ''; - } - tbody += ''; - $('#logAudit .logAuditContent thead').html(tbody); - } - - - var tbody = ''; - for (var i = 0; i < plist.length; i++) { - tbody += ''; - for (var vv in plist[i]) { - tbody+= ''+ plist[i][vv] + '' - } - tbody += ''; - } - $('#logAudit .logAuditContent tbody').html(tbody); - - $('#logAudit .refresh').click(function(){ - getAuditFile(log_name); - }); - } - - if (typeof(data) == 'string'){ - var cc = '
      \ -
      '+data+'
      \ -
      '; - $('#logAudit .logAuditContent').html(cc); - } - } catch (e) { - layer.msg(str(e),{icon:2,time:10000,shade: [0.3, '#000']}); - } - }); -} - -function getLogs(page,search) { - search = search == undefined ? '':search; - var loadT = layer.load(); - $.post('/logs/get_log_list','limit=10&p=' + page+"&search="+search, function(data) { - layer.close(loadT); - var body = ''; - for (var i = 0; i < data.data.length; i++) { - body += "\ - " + data.data[i].id + "\ - " + data.data[i].type + "\ - " + data.data[i].log + "\ - " + data.data[i].addtime + "\ - "; - } - $("#operationLog tbody").html(body); - $("#panelLogs .page").html(data.page); - },'json'); -} - -function delLogs(){ - layer.confirm('即将清空面板日志,继续吗?',{title:'清空日志',closeBtn:2},function(){ - var loadT = layer.msg('正在清理,请稍候...',{icon:16}); - $.post('/logs/del_panel_logs','',function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - getLogs(1); - },'json'); - }); -} \ No newline at end of file diff --git a/route/static/app/public.js b/route/static/app/public.js deleted file mode 100755 index 56fb17891..000000000 --- a/route/static/app/public.js +++ /dev/null @@ -1,2784 +0,0 @@ - -$(document).ready(function() { - $(".sub-menu a.sub-menu-a").click(function() { - $(this).next(".sub").slideToggle("slow").siblings(".sub:visible").slideUp("slow"); - }); -}); - -function toSize(a) { - var d = [" B", " KB", " MB", " GB", " TB", " PB"]; - var e = 1024; - for(var b = 0; b < d.length; b++) { - if(a < e) { - return(b == 0 ? a : a.toFixed(2)) + d[b] - } - a /= e; - } -} - -function toSizePos(a, pos = 0) { - var d = [" B", " KB", " MB", " GB", " TB", " PB"]; - var e = 1024; - var r = {}; - for(var b = 0; b < d.length; b++) { - if (pos > 0){ - if (b == pos){ - r['name'] = (b == 0 ? a : a.toFixed(2)) + d[b]; - r['pos'] = b; - return r - } - } else { - if( a < e) { - r['name'] = (b == 0 ? a : a.toFixed(2)) + d[b]; - r['pos'] = b; - return r - } - } - a /= e; - } -} - -function toSizeMB(a) { - var d = [" KB", " MB"]; - var e = 1024; - var i = 0; - for(var b = 0; b < d.length; b++) { - a /= e; - i = b; - } - return a.toFixed(2) + d[i] -} - -function toTrim(x) { - return x.replace(/^\s+|\s+$/gm,''); -} - -function inArray(f, arr){ - for (var i = 0; i < arr.length; i++) { - if (f == arr[i]) { - return true; - } - } - return false; -} - -//表格头固定 -function tableFixed(name) { - var tableName = document.querySelector('#' + name); - tableName.addEventListener('scroll', scrollHandle); -} - -function escapeHTML(a){ - a = "" + a; - return a.replace(/&/g, "&").replace(//g, ">").replace(/"/g, '"'). - replace(/'/g,"‘").replace(/\(/g,"(").replace(/\</g,"<"). - replace(/\>/g,">").replace(/`/g,"`").replace(/=/g,"="); -} - -function scrollHandle(e) { - var scrollTop = this.scrollTop; - //this.querySelector('thead').style.transform = 'translateY(' + scrollTop + 'px)'; - $(this).find("thead").css({ "transform": "translateY(" + scrollTop + "px)", "position": "relative", "z-index": "1" }); -} - - -//转换单们到MB -function toSizeM(byteLen) { - var a = parseInt(byteLen) / 1024 / 1024; - return a || 0; -} - -//字节单位转换MB -function toSizeG(bytes){ - var c = 1024 * 1024; - var b = 0; - if(bytes > 0){ - var b = (bytes/c).toFixed(2); - } - return b; -} - -//to unixtime -function toUnixTime(txt){ - var unix = new Date(Date.parse(txt.replace(/-/g,'/'))).getTime(); - return unix/1000; - } - -function randomStrPwd(b) { - b = b || 32; - // &!@% - var c = "AaBbCcDdEeFfGHhiJjKkLMmNnPpRSrTsWtXwYxZyz2345678"; - var a = c.length; - var d = ""; - for(i = 0; i < b; i++) { - d += c.charAt(Math.floor(Math.random() * a)) - } - return d -} - -function getRandomString(len) { - len = len || 32; - var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; // 默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1 - var maxPos = chars.length; - var pwd = ''; - for (i = 0; i < len; i++) { - pwd += chars.charAt(Math.floor(Math.random() * maxPos)); - } - return pwd; -} - -//验证IP地址 -function isValidIP(ip) { - var reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/ - return reg.test(ip); -} - -function isContains(str, substr) { - return str.indexOf(substr) >= 0; -} - - -function filterPath(path){ - var path_arr = path.split('/'); - var path_new = []; - for (var i = 0; i < path_arr.length; i++) { - if (path_arr[i]!=''){ - path_new.push(path_arr[i]); - } - } - var rdata = "/"+path_new.join('/'); - return rdata; -} - -function msgTpl(msg, args){ - if (typeof args == 'string'){ - return msg.replace('{1}', args); - } else if (typeof args == 'object'){ - for (var i = 0; i < args.length; i++) { - rep = '{' + (i + 1) + '}'; - msg = msg.replace(rep, args[i]); - } - } - return msg; -} - -function refresh() { - window.location.reload() -} - -function mwsPost(path, args, callback){ - $.post(path, args, function(rdata){ - if(typeof(callback) == 'function'){ - callback(rdata); - } - },'json'); -} - -function syncPost(path, args){ - var retData; - $.ajax({ - type : 'post', - url : path, - data : args, - async : false, - dataType:'json', - success : function(data){ - retData = data; - } - }); - return retData; -} - -function repeatPwd(a) { - $("#MyPassword").val(randomStrPwd(a)) -} - -$(".menu-icon").click(function() { - $(".sidebar-scroll").toggleClass("sidebar-close"); - $(".main-content").toggleClass("main-content-open"); - if($(".sidebar-close")) { - $(".sub-menu").find(".sub").css("display", "none"); - } -}); - -var Upload, percentage; -Date.prototype.format = function(b) { - var c = { - "M+": this.getMonth() + 1, - "d+": this.getDate(), - "h+": this.getHours(), - "m+": this.getMinutes(), - "s+": this.getSeconds(), - "q+": Math.floor((this.getMonth() + 3) / 3), - S: this.getMilliseconds() - }; - if(/(y+)/.test(b)) { - b = b.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)) - } - for(var a in c) { - if(new RegExp("(" + a + ")").test(b)) { - b = b.replace(RegExp.$1, RegExp.$1.length == 1 ? c[a] : ("00" + c[a]).substr(("" + c[a]).length)) - } - } - return b -}; - -function getLocalTime(a) { - a = a.toString(); - if(a.length > 10) { - a = a.substring(0, 10) - } - return new Date(parseInt(a) * 1000).format("yyyy/MM/dd hh:mm:ss") -} - -function getFormatTime(tm, format) { - if (format == undefined) format = "yyyy/MM/dd hh:mm:ss"; - tm = tm.toString(); - if (tm.length > 10) { - tm = tm.substring(0, 10); - } - var data = new Date(parseInt(tm) * 1000); - var o = { - "M+": data.getMonth() + 1, //month - "d+": data.getDate(), //day - "h+": data.getHours(), //hour - "m+": data.getMinutes(), //minute - "s+": data.getSeconds(), //second - "q+": Math.floor((data.getMonth() + 3) / 3), //quarter - "S": data.getMilliseconds() //millisecond - } - if (/(y+)/.test(format)) format = format.replace(RegExp.$1, - (data.getFullYear() + "").substr(4 - RegExp.$1.length)); - for (var k in o) - if (new RegExp("(" + k + ")").test(format)) - format = format.replace(RegExp.$1,RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)); - return format; -} - - - -function changePathCallback(default_dir, callback) { - - var c = layer.open({ - type: 1, - area: "650px", - title: '选择目录', - closeBtn: 1, - shift: 5, - shadeClose: false, - content: "
      \ -
      \ - \ -
      当前路径:
      \ -
      \ -
      \ -
      \ -
      计算机
      \ -
      \ -
      \ -
        \ -
        \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
        文件名修改时间权限所有者
        \ -
        \ -
        \ -
        \ -
        \ -
        \ - \ - \ - \ -
        ", - success:function(layero,layer_index){ - $('.btn-close').click(function(){ - layer.close(layer_index); - }); - - $('.btn-choose').click(function(){ - var a = $("#PathPlace").find("span").text(); - a = a.replace(new RegExp(/(\\)/g), "/"); - a_len = a.length; - if (a[a_len-1] == '/'){ - a = a.substr(0,a_len-1); - } - callback(a); - layer.close(layer_index); - }); - } - }); - getDiskList(default_dir); - activeDisk(); -} - -function changePath(d) { - setCookie('SetId', d); - setCookie('SetName', ''); - var c = layer.open({ - type: 1, - area: "650px", - title: '选择目录', - closeBtn: 1, - shift: 5, - shadeClose: false, - content: "\ -
        \ -
        \ - \ -
        当前路径:
        \ -
        \ -
        \ -
        计算机
        \ -
        \ -
          \ -
          \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
          文件名修改时间权限所有者
          \ -
          \ -
          \ -
          \ -
          \ -
          \ - \ - \ - \ -
          " - }); - setCookie("changePath", c); - var b = $("#" + d).val(); - tmp = b.split("."); - if(tmp[tmp.length - 1] == "gz") { - tmp = b.split("/"); - b = ""; - for(var a = 0; a < tmp.length - 1; a++) { - b += "/" + tmp[a] - } - setCookie("SetName", tmp[tmp.length - 1]) - } - b = b.replace(/\/\//g, "/"); - getDiskList(b); - activeDisk(); -} - -function getDiskList(b) { - var d = ""; - var a = ""; - var c = "path=" + b + "&disk=True"; - $.post("/files/get_dir", c, function(h) { - if(h.DISK != undefined) { - for(var f = 0; f < h.DISK.length; f++) { - a += "
           " + h.DISK[f].path + "
          " - } - $("#changecomlist").html(a) - } - for(var f = 0; f < h.DIR.length; f++) { - var g = h.DIR[f].split(";"); - var e = g[0]; - - if(isChineseChar(e)) { - if(e.length > 10) { - e = e.substring(0, 10) + "..."; - } - } else{ - if(e.length > 20) { - e = e.substring(0, 20) + "..."; - } - } - - d += "\ - \ - " + e + "" + getLocalTime(g[2]) + "\ - " + g[3] + "\ - " + g[4] + "\ - X\ - "; - } - if(h.FILES != null && h.FILES != "") { - for(var f = 0; f < h.FILES.length; f++) { - var g = h.FILES[f].split(";"); - var e = g[0]; - if(isChineseChar(e)) { - if(e.length > 10) { - e = e.substring(0, 10) + "..." - } - } else{ - if(e.length > 20) { - e = e.substring(0, 20) + "..." - } - } - - d += "\ - " + e + "\ - " + getLocalTime(g[2]) + "\ - " + g[3] + "\ - " + g[4] + "\ - \ - "; - } - } - $(".default").hide(); - $(".file-list").show(); - $("#tbody").html(d); - if(h.PATH.substr(h.PATH.length - 1, 1) != "/") { - h.PATH += "/"; - } - $("#PathPlace").find("span").html(h.PATH); - activeDisk(); - return; - },'json'); -} - -function createFolder() { - var a = "\ - \ - \ -   \ - "; - if($("#tbody tr").length == 0) { - $("#tbody").append(a) - } else { - $("#tbody tr:first-child").before(a) - } - $(".newFolderName").focus(); - $("#nameOk").click(function() { - var c = $("#newFolderName").val(); - var b = $("#PathPlace").find("span").text(); - newTxt = b.replace(new RegExp(/(\/\/)/g), "/") + c; - var d = "path=" + newTxt; - $.post("/files/create_dir", d, function(e) { - if(e.status == true) { - layer.msg(e.msg, { - icon: 1 - }) - } else { - layer.msg(e.msg, { - icon: 2 - }) - } - getDiskList(b); - },'json'); - }); - $("#nameNOk").click(function() { - $(this).parents("tr").remove() - }) -} - -function NewDelFile(c) { - var a = $("#PathPlace").find("span").text(); - newTxt = c.replace(new RegExp(/(\/\/)/g), "/"); - var b = "path=" + newTxt + "&empty=True"; - $.post("/files/delete_dir", b, function(d) { - if(d.status == true) { - layer.msg(d.msg, { - icon: 1 - }) - } else { - layer.msg(d.msg, { - icon: 2 - }) - } - getDiskList(a); - },'json'); -} - -function activeDisk() { - var a = $("#PathPlace").find("span").text().substring(0, 1); - switch(a) { - case "C": - $(".path-con-left dd:nth-of-type(1)").css("background", "#eee").siblings().removeAttr("style"); - break; - case "D": - $(".path-con-left dd:nth-of-type(2)").css("background", "#eee").siblings().removeAttr("style"); - break; - case "E": - $(".path-con-left dd:nth-of-type(3)").css("background", "#eee").siblings().removeAttr("style"); - break; - case "F": - $(".path-con-left dd:nth-of-type(4)").css("background", "#eee").siblings().removeAttr("style"); - break; - case "G": - $(".path-con-left dd:nth-of-type(5)").css("background", "#eee").siblings().removeAttr("style"); - break; - case "H": - $(".path-con-left dd:nth-of-type(6)").css("background", "#eee").siblings().removeAttr("style"); - break; - default: - $(".path-con-left dd").removeAttr("style") - } -} - -function backMyComputer() { - $(".default").show(); - $(".file-list").hide(); - $("#PathPlace").find("span").html(""); - activeDisk(); -} - -function backFile() { - var c = $("#PathPlace").find("span").text(); - if(c.substr(c.length - 1, 1) == "/") { - c = c.substr(0, c.length - 1) - } - var d = c.split("/"); - var a = ""; - if(d.length > 1) { - var e = d.length - 1; - for(var b = 0; b < e; b++) { - a += d[b] + "/" - } - getDiskList(a.replace("//", "/")) - } else { - a = d[0] - } - if(d.length == 1) {} -} - -function getfilePath() { - var a = $("#PathPlace").find("span").text(); - a = a.replace(new RegExp(/(\\)/g), "/"); - a_len = a.length; - if (a[a_len-1] == '/'){ - a = a.substr(0,a_len-1); - } - - $("#" + getCookie("SetId")).val(a + getCookie("SetName")); - layer.close(getCookie("changePath")); - return a; -} - -function setCookie(a, c) { - var b = 30; - var d = new Date(); - d.setTime(d.getTime() + b * 24 * 60 * 60 * 1000); - document.cookie = a + "=" + escape(c) + ";path=/;expires=" + d.toGMTString(); -} - -function getCookie(b) { - var a, c = new RegExp("(^| )" + b + "=([^;]*)(;|$)"); - if(a = document.cookie.match(c)) { - return unescape(a[2]) - } else { - return null - } -} - -function autoHeight() { - var a = $("body").height() - 40; - $(".main-content").css("min-height", a); -} - -function showMsg(msg, callback ,icon, time){ - - if (typeof time == 'undefined'){ - time = 2000; - } - - if (typeof icon == 'undefined'){ - icon = {}; - } - - var loadT = layer.msg(msg, icon); - setTimeout(function() { - layer.close(loadT); - if (typeof callback == 'function'){ - callback(); - } - }, time); -} - -function openPath(a) { - setCookie("open_dir_path", a); - window.location.href = "/files/"; -} - -function onlineEditFile(k, f, callback) { - if(k != 0) { - var l = $("#PathPlace input").val(); - var h = encodeURIComponent($("#textBody").val()); - var a = $("select[name=encoding]").val(); - var loadT = layer.msg('正在保存,请稍候...', {icon: 16,time: 0}); - $.post("/files/save_body", "data=" + h + "&path=" + encodeURIComponent(f) + "&encoding=" + a, function(data) { - if(k == 1) { - layer.close(loadT); - } - layer.msg(data.msg, {icon: data.status ? 1 : 2}); - if (data.status && typeof(callback) == 'function'){ - callback(k, f); - } - },'json'); - return - } - var e = layer.msg('正在读取文件,请稍候...', {icon: 16,time: 0}); - var g = f.split("."); - var b = g[g.length - 1]; - var d; - switch(b) { - case "html": - var j = { - name: "htmlmixed", - scriptTypes: [{ - matches: /\/x-handlebars-template|\/x-mustache/i, - mode: null - }, { - matches: /(text|application)\/(x-)?vb(a|script)/i, - mode: "vbscript" - }] - }; - d = j; - break; - case "htm": - var j = { - name: "htmlmixed", - scriptTypes: [{ - matches: /\/x-handlebars-template|\/x-mustache/i, - mode: null - }, { - matches: /(text|application)\/(x-)?vb(a|script)/i, - mode: "vbscript" - }] - }; - d = j; - break; - case "js": - d = "text/javascript"; - break; - case "json": - d = "application/ld+json"; - break; - case "css": - d = "text/css"; - break; - case "php": - d = "application/x-httpd-php"; - break; - case "tpl": - d = "application/x-httpd-php"; - break; - case "xml": - d = "application/xml"; - break; - case "sql": - d = "text/x-sql"; - break; - case "conf": - d = "text/x-nginx-conf"; - break; - default: - var j = { - name: "htmlmixed", - scriptTypes: [ - {matches: /\/x-handlebars-template|\/x-mustache/i,mode: null}, - {matches: /(text|application)\/(x-)?vb(a|script)/i,mode: "vbscript"} - ] - }; - d = j; - } - $.post("/files/get_body", "path=" + encodeURIComponent(f), function(s) { - if(s.status === false){ - layer.msg(s.msg,{icon:5}); - return; - } - layer.close(e); - var u = ["utf-8", "GBK", "GB2312", "BIG5"]; - var n = ""; - var m = ""; - var o = ""; - for(var p = 0; p < u.length; p++) { - m = s.data.encoding == u[p] ? "selected" : ""; - n += '"; - } - var code_mirror = null; - var r = layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: ["90%", "90%"], - btn:['保存','关闭'], - title: "在线编辑[" + f + "]", - content: '
          \ -
          \ -

          提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换!\ - \ -

          \ - '; - $(".taskcon").html(lbody); - var ob = document.getElementById('exec_log'); - ob.scrollTop = ob.scrollHeight; - }); -} - -/** - * 获取时分秒 - * @param {Number} seconds 总秒数 - * @param {String} dateFormat 返回的日期格式,默认为'H:i:s' - */ -function getSFM(seconds, dateFormat = 'H:i:s') { - - var obj = {}; - obj.H = Number.parseInt(seconds / 3600); - obj.i = Number.parseInt((seconds - obj.H * 3600) / 60); - obj.s = Number.parseInt(seconds - obj.H * 3600 - obj.i * 60); - if (obj.H < 10) { - obj.H = '0' + obj.H; - } - if (obj.i < 10) { - obj.i = '0' + obj.i; - } - if (obj.s < 10) { - obj.s = '0' + obj.s; - } - - // 3.解析 - var rs = dateFormat.replace('H', obj.H).replace('i', obj.i).replace('s', obj.s); - return rs; -} - -function remind(a){ - a = a == undefined ? 1 : a; - $.post("/task/list", "table=tasks&result=2,4,6,8&limit=10&p=" + a, function(g) { - var e = ''; - var f = false; - var task_count = 0; - for(var d = 0; d < g.data.length; d++) { - var status = g.data[d].status; - var status_text = '已经完成'; - var cos_text = ''; - if (status == '1'){ - status_text = '完成'; - cos_text = '耗时['+getSFM(g.data[d].end - g.data[d].start)+']' - } else if (status == '0'){ - status_text = '正在处理'; - cos_text = '等待中..'; - } else if (status == '-1'){ - status_text = '安装中'; - cos_text = '..'; - } - - e += '\ - \ - \ -
          '+g.data[d].name+'\ - 【'+status_text+'】\ - '+cos_text+'\ -
          \ - \ - '+g.data[d].addtime+'\ - '; - } - var con = '
          \ - \ - \ - \ - \ - \ - \ - '+e+'\ -
          '+lan.bt.task_name+''+lan.bt.task_time+'
          \ -
          \ -
          \ - \ -
          \ -
          '; - - $(".task_count").text(task_count); - $(".msg_count").text(g.count); - $(".taskcon").html(con); - $("#taskPage").html(g.page); - - $("#Rs-checkAll").click(function(){ - if($(this).prop("checked")){ - $("#remind").find("input").prop("checked",true); - } else { - $("#remind").find("input").prop("checked",false); - } - }); - },'json'); -} - -function getReloads() { - var a = 0; - var mm = $(".bt-w-menu .bgw").html() - if(mm == undefined || mm.indexOf(lan.bt.task_list) == -1) { - clearInterval(speed); - a = 0; - speed = null; - return - } - if(speed) {return;} - speed = setInterval(function() { - var mm = $(".bt-w-menu .bgw").html() - if(mm == undefined || mm.indexOf(lan.bt.task_list) == -1) { - clearInterval(speed); - speed = null; - a = 0; - return - } - a++; - $.post('/task/get_task_speed', '', function(h) { - if(h.task == undefined) { - $(".cmdlist").html(lan.bt.task_not_list); - return; - } - var b = ""; - var d = ""; - $("#task").text(h.task.length); - $(".task_count").text(h.task.length); - for(var g = 0; g < h.task.length; g++) { - if(h.task[g].status == "-1") { - if(h.task[g].type != "download") { - var c = ""; - var f = h.msg.split("\n"); - for(var e = 0; e < f.length; e++) { - c += f[e] + "
          " - } - if(h.task[g].name.indexOf("扫描") != -1) { - b = "
        • \ - " + h.task[g].name + "\ - 正在扫描 | 关闭\ - \ -
          " + c + "
          \ -
        • "; - } else { - b = "
        • \ - " + h.task[g].name + "\ - 正在安装 | 关闭\ -
          " + c + "
          \ -
        • "; - } - } else { - b = "
        • \ -
          \ - " + h.task[g].name + "" + (toSize(h.msg.used) + "/" + toSize(h.msg.total)) + "\ - " + h.msg.pre + "%\ - "+lan.bt.task_downloading+" | "+lan.public.close+"\ -
        • " - } - } else { - d += "
        • " + h.task[g].name + "等待 | 删除
        • ' - } - } - $(".cmdlist").html(b + d); - $(".cmd").html(c); - try{ - if($(".cmd")[0].scrollHeight) $(".cmd").scrollTop($(".cmd")[0].scrollHeight); - }catch(e){ - return; - } - },'json').error(function(){}); - }, 1000); -} - -//检查选中项 -function RscheckSelect(){ - setTimeout(function(){ - var checkList = $("#remind").find("input"); - var count = 0; - for(var i=0;i 0){ - $(".buttongroup .btn").removeAttr("disabled"); - }else{ - $(".rs-del,.rs-read").attr("disabled","disabled"); - } - },5); -} - - -function tasklist(a){ - var con='
            \ - 若任务长时间未执行,请尝试在首页点【重启面板】来重置任务队列'; - $(".taskcon").html(con); - a = a == undefined ? 1 : a; - $.post("/task/list", "tojs=getTaskList&table=tasks&limit=10&p=" + a, function(g) { - var e = ""; - var b = ""; - var c = ""; - var f = false; - var task_count =0; - for(var d = 0; d < g.data.length; d++) { - switch(g.data[d].status) { - case "-1": - f = true; - if(g.data[d].type != "download") { - b = "
          • \ - " + g.data[d].name + "\ - 正在安装 | 关闭\ -
            \
            -						
          • "; - } else { - b = "
          • \ -
            " + g.data[d].name + "0.0M/12.5M\ - 0%下载中 | 关闭\ -
          • "; - } - task_count++; - break; - case "0": - c += "
          • \ - " + g.data[d].name + "\ - 等待 | "+lan.public.del+"\ -
          • "; - task_count++; - break; - } - } - - - $(".task_count").text(task_count); - $(".cmdlist").html(b + c); - getReloads(); - return f - },'json'); -} - -function activeDisk() { - var a = $("#PathPlace").find("span").text().substring(0, 1); - switch(a) { - case "C": - $(".path-con-left dd:nth-of-type(1)").css("background", "#eee").siblings().removeAttr("style"); - break; - case "D": - $(".path-con-left dd:nth-of-type(2)").css("background", "#eee").siblings().removeAttr("style"); - break; - case "E": - $(".path-con-left dd:nth-of-type(3)").css("background", "#eee").siblings().removeAttr("style"); - break; - case "F": - $(".path-con-left dd:nth-of-type(4)").css("background", "#eee").siblings().removeAttr("style"); - break; - case "G": - $(".path-con-left dd:nth-of-type(5)").css("background", "#eee").siblings().removeAttr("style"); - break; - case "H": - $(".path-con-left dd:nth-of-type(6)").css("background", "#eee").siblings().removeAttr("style"); - break; - default: - $(".path-con-left dd").removeAttr("style"); - } -} - - -function backMyComputer() { - $(".default").show(); - $(".file-list").hide(); - $("#PathPlace").find("span").html(""); - activeDisk(); -} - -//检查登陆状态 -function check_login(){ - $.post('/check_login',{},function(rdata){ - if(rdata === true) return; - }); -} - -//登陆跳转 -function to_login(){ - layer.confirm('您的登陆状态已过期,请重新登陆!',{title:'会话已过期',icon:2,closeBtn: 1,shift: 5},function(){ - location.reload(); - }); -} -//表格头固定 -function table_fixed(name){ - var tableName = document.querySelector('#'+name); - tableName.addEventListener('scroll',scroll_handle); -} -function scroll_handle(e){ - var scrollTop = this.scrollTop; - $(this).find("thead").css({"transform":"translateY("+scrollTop+"px)","position":"relative","z-index":"1"}); -} - - -$(function(){ -/// -setInterval(function(){check_login();},6000); -/// -}); - -function asyncLoadImage(obj, url){ - - if (typeof(url) == 'undefined'){ - return; - } - - function loadImage(obj,url,callback){ - var img = new Image(); - img.src = url; - - if(img.complete){ - callback.call(img,obj); - return; - } - img.onload = function(){ - callback.call(img,obj); - } - } - - function showImage(obj){ - obj.src = this.src; - } - loadImage(obj, url, showImage); -} - -function loadImage(){ - $('img').each(function(i){ - // console.log($(this).attr('data-src')); - if ($(this).attr('data-src') != ''){ - asyncLoadImage(this, $(this).attr('data-src')); - } - }); -} - -var socket, gterm; -function webShell() { - var termCols = 83; - var termRows = 21; - var sendTotal = 0; - if(!socket)socket = io.connect(); - var term = new Terminal({ cols: termCols, rows: termRows, screenKeys: true, useStyle: true}); - - term.open(); - term.setOption('cursorBlink', true); - term.setOption('fontSize', 14); - gterm = term; - - socket.on('server_response', function (data) { - term.write(data.data); - if (data.data == '\r\n登出\r\n' || - data.data == '登出\r\n' || - data.data == '\r\nlogout\r\n' || - data.data == 'logout\r\n') { - setTimeout(function () { - layer.closeAll(); - term.destroy(); - clearInterval(interval); - }, 500); - } - }); - - $(window).unload(function(){ -   term.destroy(); - clearInterval(interval); - }); - - if (socket) { - socket.emit('webssh', ''); - interval = setInterval(function () { - socket.emit('webssh', ''); - }, 500); - } - - term.on('data', function (data) { - socket.emit('webssh', data); - }); - - - var term_box = layer.open({ - type: 1, - title: "本地终端", - area: ['685px','463px'], - closeBtn: 1, - shadeClose: false, - content: '
            \ -
            \ - \ - \ -
              \ -
            • 此处为'+ _name + version +'主配置文件,若您不了解配置规则,请勿随意修改。
            • \ -
            '; - - - var loadT = layer.msg('配置文件路径获取中...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/plugins/run', {name:_name, func:func_name,version:version},function (data) { - layer.close(loadT); - - try{ - var jdata = $.parseJSON(data.data); - if (!jdata['status']){ - layer.msg(jdata.msg,{icon:0,time:2000,shade: [0.3, '#000']}); - return; - } - }catch(err){/*console.log(err);*/} - - $(".soft-man-con").html(con); - - var loadT2 = layer.msg('文件内容获取中...',{icon:16,time:0,shade: [0.3, '#000']}); - var fileName = data.data; - $.post('/files/get_body', 'path=' + fileName, function(rdata) { - layer.close(loadT2); - if (!rdata.status){ - layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']}); - return; - } - $("#textBody").empty().text(rdata.data.data); - $(".CodeMirror").remove(); - var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), { - extraKeys: { - "Ctrl-Space": "autocomplete", - "Ctrl-F": "findPersistent", - "Ctrl-H": "replaceAll", - "Ctrl-S": function() { - $("#textBody").text(editor.getValue()); - pluginConfigSave(fileName); - } - }, - lineNumbers: true, - matchBrackets:true, - }); - editor.focus(); - $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0}); - $("#onlineEditFileBtn").click(function(){ - $("#textBody").text(editor.getValue()); - pluginConfigSave(fileName); - }); - },'json'); - },'json'); -} - - -//配置修改模版 --- start -function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_func, save_callback_func){ - if ( typeof(version) == 'undefined' ){ - version = ''; - } - - var func_name = 'conf'; - if ( typeof(func) != 'undefined' ){ - func_name = func; - } - - var _config_tpl_func = 'config_tpl'; - if ( typeof(config_tpl_func) != 'undefined' ){ - _config_tpl_func = config_tpl_func; - } - - var _read_config_tpl_func = 'read_config_tpl'; - if ( typeof(read_config_tpl_func) != 'undefined' ){ - _read_config_tpl_func = read_config_tpl_func; - } - - - var con = '

            提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换!

            \ - \ - \ - \ -
              \ -
            • 此处为【'+ _name + version +'】主配置文件,若您不了解配置规则,请勿随意修改。
            • \ -
            '; - $(".soft-man-con").html(con); - - function getFileName(file){ - var list = file.split('/'); - var f = list[list.length-1]; - return f - } - - var fileName = ''; - $.post('/plugins/run',{name:_name, func:_config_tpl_func,version:version}, function(data){ - var rdata = $.parseJSON(data.data); - for (var i = 0; i < rdata.length; i++) { - $('#config_tpl').append(''); - } - - $('#config_tpl').change(function(){ - var selected = $(this).val(); - if (selected != '0'){ - var loadT = layer.msg('配置模版获取中...',{icon:16,time:0,shade: [0.3, '#000']}); - - var _args = JSON.stringify({file:selected}); - $.post('/plugins/run', {name:_name, func:_read_config_tpl_func,version:version,args:_args}, function(data){ - layer.close(loadT); - var rdata = $.parseJSON(data.data); - if (!rdata.status){ - layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']}); - return; - } - - $("#textBody").empty().text(rdata.data); - $(".CodeMirror").remove(); - var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), { - extraKeys: { - "Ctrl-Space": "autocomplete", - "Ctrl-F": "findPersistent", - "Ctrl-H": "replaceAll", - "Ctrl-S": function() { - $("#textBody").text(editor.getValue()); - pluginConfigSave(fileName,save_callback_func); - } - }, - lineNumbers: true, - matchBrackets:true, - }); - editor.focus(); - $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0}); - $("#onlineEditFileBtn").unbind('click'); - $("#onlineEditFileBtn").click(function(){ - $("#textBody").text(editor.getValue()); - pluginConfigSave(fileName, save_callback_func); - }); - },'json'); - } - }); - - },'json'); - - var loadT = layer.msg('配置文件路径获取中...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/plugins/run', {name:_name, func:func_name,version:version}, function (data) { - layer.close(loadT); - - var loadT2 = layer.msg('文件内容获取中...',{icon:16,time:0,shade: [0.3, '#000']}); - fileName = data.data; - $.post('/files/get_body', 'path=' + fileName, function(rdata) { - layer.close(loadT2); - if (!rdata.status){ - layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']}); - return; - } - $("#textBody").empty().text(rdata.data.data); - $(".CodeMirror").remove(); - var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), { - extraKeys: { - "Ctrl-Space": "autocomplete", - "Ctrl-F": "findPersistent", - "Ctrl-H": "replaceAll", - "Ctrl-S": function() { - $("#textBody").text(editor.getValue()); - pluginConfigSave(fileName,save_callback_func); - } - }, - lineNumbers: true, - matchBrackets:true, - }); - editor.focus(); - $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0}); - $("#onlineEditFileBtn").click(function(){ - $("#textBody").text(editor.getValue()); - pluginConfigSave(fileName,save_callback_func); - }); - },'json'); - },'json'); -} - -//配置模版列表 --- start -function pluginConfigListTpl(_name, version, config_tpl_func, read_config_tpl_func){ - if ( typeof(version) == 'undefined' ){ - version = ''; - } - - var _config_tpl_func = 'config_tpl'; - if ( typeof(config_tpl_func) != 'undefined' ){ - _config_tpl_func = config_tpl_func; - } - - var _read_config_tpl_func = 'read_config_tpl'; - if ( typeof(read_config_tpl_func) != 'undefined' ){ - _read_config_tpl_func = read_config_tpl_func; - } - - - var con = '

            提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换!

            \ - \ - \ - \ -
              \ -
            • 此处为'+ _name + version +'主配置文件,若您不了解配置规则,请勿随意修改。
            • \ -
            '; - $(".soft-man-con").html(con); - - function getFileName(file){ - var list = file.split('/'); - var f = list[list.length-1]; - return f - } - - function loadTextBody(fileName){ - $.post('/files/get_body', 'path=' + fileName, function(rdata) { - if (!rdata.status){ - layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']}); - return; - } - $("#textBody").empty().text(rdata.data.data); - $(".CodeMirror").remove(); - var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), { - extraKeys: { - "Ctrl-Space": "autocomplete", - "Ctrl-F": "findPersistent", - "Ctrl-H": "replaceAll", - "Ctrl-S": function() { - $("#textBody").text(editor.getValue()); - pluginConfigSave(fileName); - } - }, - lineNumbers: true, - matchBrackets:true, - }); - editor.focus(); - $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0}); - $("#onlineEditFileBtn").click(function(){ - $("#textBody").text(editor.getValue()); - pluginConfigSave(fileName); - }); - },'json'); - } - - - - var fileName = ''; - $.post('/plugins/run',{name:_name, func:_config_tpl_func,version:version}, function(data){ - var rdata = $.parseJSON(data.data); - for (var i = 0; i < rdata.length; i++) { - $('#config_tpl').append(''); - } - - if (rdata.length>0){ - loadTextBody(rdata[0]); - } - - $('#config_tpl').change(function(){ - var selected = $(this).val(); - fileName = selected; - - var loadT = layer.msg('配置模版获取中...',{icon:16,time:0,shade: [0.3, '#000']}); - - var _args = JSON.stringify({file:selected}); - $.post('/plugins/run', {name:_name, func:_read_config_tpl_func,version:version,args:_args}, function(data){ - layer.close(loadT); - var rdata = $.parseJSON(data.data); - if (!rdata.status){ - layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']}); - return; - } - - $("#textBody").empty().text(rdata.data); - $(".CodeMirror").remove(); - var editor = CodeMirror.fromTextArea(document.getElementById("textBody"), { - extraKeys: { - "Ctrl-Space": "autocomplete", - "Ctrl-F": "findPersistent", - "Ctrl-H": "replaceAll", - "Ctrl-S": function() { - $("#textBody").text(editor.getValue()); - pluginConfigSave(fileName); - } - }, - lineNumbers: true, - matchBrackets:true, - }); - editor.focus(); - $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0}); - $("#onlineEditFileBtn").unbind('click'); - $("#onlineEditFileBtn").click(function(){ - $("#textBody").text(editor.getValue()); - pluginConfigSave(fileName); - }); - },'json'); - - }); - - },'json'); -} - - -//配置保存 -function pluginConfigSave(fileName, callback) { - var data = encodeURIComponent($("#textBody").val()); - var encoding = 'utf-8'; - var loadT = layer.msg('保存中...', {icon: 16,time: 0}); - $.post('/files/save_body', 'data=' + data + '&path=' + fileName + '&encoding=' + encoding, function(rdata) { - layer.close(loadT); - - showMsg(rdata.msg, function(){ - if ( rdata.status && typeof(callback) == 'function'){ - callback(); - } - },{icon: rdata.status ? 1 : 2}); - - },'json'); -} - -function pluginInitD(_name, _version, _suffix_name=''){ - if (typeof _version == 'undefined'){ - _version = ''; - } - - var default_name = 'initd_status'; - if ( _suffix_name != '' ){ - default_name = 'initd_status_'+_suffix_name; - } - - var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); - $.post('/plugins/run', {name:_name, func:default_name, version : _version}, function(data) { - layer.close(loadT); - if( !data.status ){ - layer.msg(data.msg,{icon:0,time:3000,shade: [0.3, '#000']}); - return; - } - if( data.data!='ok' && data.data!='fail' ){ - layer.msg(data.data,{icon:0,time:3000,shade: [0.3, '#000']}); - return; - } - if (data.data == 'ok'){ - pluginSetInitD(_name, _version, true, _suffix_name); - } else { - pluginSetInitD(_name, _version, false, _suffix_name); - } - },'json'); -} - -function pluginSetInitD(_name, _version, status,_suffix_name=''){ - - var default_name = (status?'initd_uninstall':'initd_install'); - if ( _suffix_name != '' ){ - default_name = default_name + '_' + _suffix_name; - } - - var serviceCon ='

            当前状态:'+(status ? '已加载' : '未加载' )+ - '

            \ - \ -
            '; - $(".soft-man-con").html(serviceCon); -} - -function pluginOpInitD(a, _version, b, _suffix_name='') { - var c = "name=" + a + "&func=" + b + "&version="+_version; - var d = ""; - b = b.split('_'+_suffix_name)[0]; - switch(b) { - case "initd_install":d = '加载';break; - case "initd_uninstall":d = '卸载';break; - } - - _ver = _version; - if(_version != ''){ - _ver = '【' + _version + '】'; - } - - layer.confirm( msgTpl('您真的要{1}{2}{3}服务吗?', [d,a,_ver]), {icon:3,closeBtn: 1}, function() { - var e = layer.msg(msgTpl('正在{1}{2}{3}服务,请稍候...',[d,a,_ver]), {icon: 16,time: 0}); - $.post("/plugins/run", c, function(g) { - layer.close(e); - var f = g.data == 'ok' ? msgTpl('{1}{3}服务已{2}',[a,d,_ver]) : msgTpl('{1}{3}服务{2}失败!',[a,d,_ver]); - layer.msg(f, {icon: g.data == 'ok' ? 1 : 2}); - - if ( b == 'initd_install' && g.data == 'ok' ) { - pluginSetInitD(a, _version, true); - }else{ - pluginSetInitD(a, _version, false); - } - if(g.data != 'ok') { - layer.msg(g.data, {icon: 2,time: 0,shade: 0.3,shadeClose: true}); - } - },'json').error(function() { - layer.close(e); - layer.msg('系统异常!', {icon: 0}); - }); - }) -} - -function pluginLogs(_name, version, func, line){ - - var _this = this; - if ( typeof(version) == 'undefined' ){ - version = ''; - } - - var func_name = 'error_log'; - if ( typeof(func) != 'undefined' ){ - func_name = func; - } - - var file_line = 100; - if ( typeof(line) != 'undefined' ){ - file_line = line; - } - - - var loadT = layer.msg('日志路径获取中...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/plugins/run', {name:_name, func:func_name, version:version},function (data) { - layer.close(loadT); - - try{ - var jdata = $.parseJSON(data.data); - if (!jdata['status']){ - layer.msg(jdata.msg,{icon:0,time:2000,shade: [0.3, '#000']}); - return; - } - }catch(err){/*console.log(err);*/} - - - var loadT2 = layer.msg('文件内容获取中...',{icon:16,time:0,shade: [0.3, '#000']}); - var fileName = data.data; - $.post('/files/get_last_body', 'path=' + fileName+'&line='+file_line, function(rdata) { - layer.close(loadT2); - if (!rdata.status){ - layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']}); - return; - } - - if(rdata.data == '') { - rdata.data = '当前没有日志!'; - } - - - var h = parseInt($('.bt-w-menu').css('height')) - 40; - var ebody = ''; - $(".soft-man-con").html(ebody); - var ob = document.getElementById('info_log'); - ob.scrollTop = ob.scrollHeight; - },'json'); - },'json'); -} - - -function pluginRollingLogs(_name, version, func, _args, line){ - if ( typeof(version) == 'undefined' ){ - version = ''; - } - - var func_name = 'error_log'; - if ( typeof(func) != 'undefined' ){ - func_name = func; - } - - var file_line = 100; - if ( typeof(line) != 'undefined' ){ - file_line = line; - } - - var reqTimer = null; - - function requestLogs(fileName){ - $.post('/files/get_last_body', 'path=' + fileName+'&line='+file_line, function(rdata) { - if (!rdata.status){ - return; - } - - if(rdata.data == '') { - rdata.data = '当前没有日志!'; - } - var ebody = ''; - $("#plugins_rolling_logs").html(ebody); - var ob = document.getElementById('roll_info_log'); - ob.scrollTop = ob.scrollHeight; - },'json'); - } - - - layer.open({ - type: 1, - title: _name + '日志', - area: '640px', - end: function(){ - if (reqTimer){ - clearInterval(reqTimer); - } - }, - content:'
            \ - \ -
            ', - success:function(){ - $.post('/plugins/run', {name:_name, func:func_name, version:version, args:_args},function (data) { - var fileName = data.data; - requestLogs(fileName); - reqTimer = setInterval(function(){ - requestLogs(fileName); - },1000); - },'json'); - } - }); -} - - -function pluginStandAloneLogs(_name, version, func, _args, line){ - if ( typeof(version) == 'undefined' ){ - version = ''; - } - - var func_name = 'error_log'; - if ( typeof(func) != 'undefined' ){ - func_name = func; - } - - var file_line = 100; - if ( typeof(line) != 'undefined' ){ - file_line = line; - } - - - layer.open({ - type: 1, - title: _name + '日志', - area: '640px', - content:'
            \ - \ -
            ' - }); - - $.post('/plugins/run', {name:_name, func:func_name, version:version,args:_args},function (data) { - var fileName = data.data; - $.post('/files/get_last_body', 'path=' + fileName+'&line='+file_line, function(rdata) { - if (!rdata.status){ - return; - } - - if(rdata.data == '') { - rdata.data = '当前没有日志!'; - } - var ebody = ''; - $("#plugins_stand_alone_logs").html(ebody); - var ob = document.getElementById('plugins_stand_alone_logs'); - ob.scrollTop = ob.scrollHeight; - },'json'); - },'json'); -} -/*** 其中功能,针对插件通过库使用 end ***/ - - -$(function() { - autoHeight(); -}); -$(window).resize(function() { - autoHeight(); -}); \ No newline at end of file diff --git a/route/static/app/site.js b/route/static/app/site.js deleted file mode 100755 index 7a03629a0..000000000 --- a/route/static/app/site.js +++ /dev/null @@ -1,2923 +0,0 @@ - - -$("#site_search_input").keyup(function(event){ - if(event.keyCode == 13) { - getWeb(1, -1, $(this).val()); - } -}); - -$('#site_search').click(function(){ - getWeb(1, -1, $('#site_search_input').val()); -}); - -/** - * 取回网站数据列表 - * @param {Number} page 当前页 - * @param {String} search 搜索条件 - */ - function getWeb(page, type_id, search) { - if ( typeof(search) == 'undefined' ){ - search = $('#site_search_input').val(); - } - - var page = page == undefined ? '1':page; - var order = getCookie('order'); - if(order){ - order = '&order=' + order; - } else { - order = ''; - } - - var type = ''; - if ( typeof(type_id) == 'undefined' ){ - type = '&type_id=-1'; - } else { - type = '&type_id='+type_id; - } - - var sUrl = '/site/list'; - var pdata = 'limit=10&p=' + page + '&search=' + search + order + type; - var loadT = layer.load(); - //取回数据 - $.post(sUrl, pdata, function(data) { - layer.close(loadT); - //构造数据列表 - var body = ''; - $("#webBody").html(body); - for (var i = 0; i < data.data.length; i++) { - //当前站点状态 - if (data.data[i].status == '正在运行' || data.data[i].status == '1') { - var status = "运行中"; - } else { - var status = "已停止"; - } - - //是否有备份 - if (data.data[i].backup_count > 0) { - var backup = "有备份"; - } else { - var backup = "无备份"; - } - //是否设置有效期 - var web_end_time = (data.data[i].edate == "0000-00-00") ? '永久': data.data[i].edate; - //表格主体 - var shortwebname = data.data[i].name; - var shortpath = data.data[i].path; - if(data.data[i].name.length > 30) { - shortwebname = data.data[i].name.substring(0, 30) + "..."; - } - if(data.data[i].path.length > 30){ - shortpath = data.data[i].path.substring(0, 30) + "..."; - } - var idname = data.data[i].name.replace(/\./g,'_'); - - body = "\ - " + shortwebname + "\ - " + status + "\ - " + backup + "\ - " + shortpath + "\ - " + web_end_time + "\ - " + data.data[i].ps + "\ - \ - 设置\ - | 删除\ - " - - $("#webBody").append(body); - //setEdate(data.data[i].id,data.data[i].edate); - //设置到期日期 - function getDate(a) { - var dd = new Date(); - dd.setTime(dd.getTime() + (a == undefined || isNaN(parseInt(a)) ? 0 : parseInt(a)) * 86400000); - var y = dd.getFullYear(); - var m = dd.getMonth() + 1; - var d = dd.getDate(); - return y + "-" + (m < 10 ? ('0' + m) : m) + "-" + (d < 10 ? ('0' + d) : d); - } - $('#webBody').on('click','#site_'+ data.data[i].id,function(){ - var _this = $(this); - var id = $(this).attr('data-ids'); - laydate.render({ - elem: '#site_'+ id //指定元素 - ,min:getDate(1) - ,max:'2099-12-31' - ,vlue:getDate(365) - ,type:'date' - ,format :'yyyy-MM-dd' - ,trigger:'click' - ,btns:['perpetual', 'confirm'] - ,theme:'#20a53a' - ,done:function(dates){ - if(_this.html() == '永久'){ - dates = '0000-00-00'; - } - var loadT = layer.msg(lan.site.saving_txt, { icon: 16, time: 0, shade: [0.3, "#000"]}); - $.post('/site/set_end_date','id='+id+'&edate='+dates,function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:5}); - },'json'); - } - }); - this.click(); - }); - } - if(body.length < 10){ - body = "当前没有站点数据"; - // $(".dataTables_paginate").hide(); - $("#webBody").html(body); - } - //输出数据列表 - $(".btn-more").hover(function(){ - $(this).addClass("open"); - },function(){ - $(this).removeClass("open"); - }); - - //输出分页 - $("#webPage").html(data.page); - // $("#webPage").html('
            站点分类:
            '); - - $(".btlinkbed").click(function(){ - var dataid = $(this).attr("data-id"); - var databak = $(this).text(); - if(databak == null){ - databak = ''; - } - $(this).hide().after(""); - $(".baktext").focus(); - }); - - readerTableChecked(); - },'json'); -} - - -function getBakPost(b) { - $(".baktext").hide().prev().show(); - var id = $(".baktext").attr("data-id"); - var page = $(".baktext").attr("data-page"); - var a = $(".baktext").val(); - if(a == "") { - a = '空'; - } - setWebPs(b, id, a,page); - $("a[data-id='" + id + "']").html(a); - $(".baktext").remove(); -} - -function setWebPs(b, id, ps,page) { - var d = layer.load({shade: true,shadeClose: false}); - var ps = 'ps=' + ps; - $.post('/site/set_ps', 'id=' + id + "&" + ps, function(data) { - if(data['status']) { - getWeb(page); - layer.closeAll(); - layer.msg('修改成功!', {icon: 1}); - } else { - layer.closeAll(); - layer.msg('修改失败!', {icon: 2}); - } - },'json'); -} - -//创建站点前,检查服务是否开启 -function webAdd(type){ - loading = layer.msg('正在检查是否开启OpenResty服务!',{icon:16,time:0,shade: [0.3, "#000"]}) - $.post('/site/check_web_status', function(data){ - layer.close(loading); - if (data.status){ - webAddPage(type) - } else { - layer.msg(data.msg,{icon:0,time:3000,shade: [0.3, "#000"]}) - } - },'json'); -} - -//添加站点 -function webAddPage(type) { - - if (type == 1) { - var array; - var str=""; - var domainlist=''; - var domain = array = $("#mainDomain").val().replace('http://','').replace('https://','').split("\n"); - var webport=[]; - var checkDomain = domain[0].split('.'); - if(checkDomain.length < 1){ - layer.msg(lan.site.domain_err_txt,{icon:2}); - return; - } - for(var i=1; i
            "; - layer.open({ - type: 1, - skin: 'demo-class', - area: '640px', - title: '添加网站', - closeBtn: 1, - shift: 0, - shadeClose: false, - content: "\ -
            \ - "+lan.site.domain+"\ -
            \ - \ -
            \ -
            \ -
            \ - 备注\ -
            \ - \ -
            \ -
            \ -
            \ - 根目录\ -
            \ - \ - \ -
            \ -
            \ - "+php_version+"\ -
            \ - \ - \ -
            \ - ", - }); - - $(function() { - var placeholder = "
            "+lan.site.domain_help+"
            "; - $('#mainDomain').after(placeholder); - $(".placeholder").click(function(){ - $(this).hide(); - $('#mainDomain').focus(); - }) - $('#mainDomain').focus(function() { - $(".placeholder").hide(); - }); - - $('#mainDomain').blur(function() { - if($(this).val().length==0){ - $(".placeholder").show(); - } - }); - - //验证PHP版本 - $("select[name='version']").change(function(){ - if($(this).val() == '52'){ - var msgerr = 'PHP5.2在您的站点有漏洞时有跨站风险,请尽量使用PHP5.3以上版本!'; - $('#php_w').text(msgerr); - }else{ - $('#php_w').text(''); - } - }) - - $('#mainDomain').on('input', function() { - var array; - var res,ress; - var str = $(this).val().replace('http://','').replace('https://',''); - var len = str.replace(/[^\x00-\xff]/g, "**").length; - array = str.split("\n"); - ress =array[0].split(":")[0]; - res = ress.replace(new RegExp(/([-.])/g), '_'); - if(res.length > 15){ - res = res.substr(0,15); - } - - var placeholder = $("#inputPath").attr('placeholder'); - $("#inputPath").val(placeholder+'/'+ress); - - if(res.length > 15){ - res = res.substr(0,15); - } - - $("#Wbeizhu").val(ress); - }) - - //备注 - $('#Wbeizhu').on('input', function() { - var str = $(this).val(); - var len = str.replace(/[^\x00-\xff]/g, "**").length; - if (len > 20) { - str = str.substring(0, 20); - $(this).val(str); - layer.msg('不能超出20个字符!', { - icon: 0 - }); - } - }) - //获取当前时间时间戳,截取后6位 - var timestamp = new Date().getTime().toString(); - var dtpw = timestamp.substring(7); - }); - }, 'json'); -} - -//修改网站目录 -function webPathEdit(id){ - $.post('/site/get_dir_user_ini','&id='+id, function(data){ - var userini = data['data']; - var webpath = userini['path']; - var siteName = userini['name']; - var runPath = userini['runPath']['runPath']; - var userinicheckeds = userini.userini?'checked':''; - var logscheckeds = userini.logs?'checked':''; - var opt = '' - var selected = ''; - for(var i=0;i'+ userini.runPath.dirs[i] +'' - } - var webPathHtml = "
            \ -
            \ - \ - \ -
            \ -
            \ - 网站目录\ - \ - \ - \ -
            \ -
            \ - 运行目录\ - \ - \ -
            \ -
              \ -
            • 部分程序需要指定二级目录作为运行目录,如ThinkPHP5,Laravel
            • \ -
            • 选择您的运行目录,点保存即可
            • \ -
            " - +'
            ' - +'密码访问' - +'' - +'' - +'' - +'
            ' - +'
            ' - +'

            授权账号

            ' - +'

            访问密码

            ' - +'

            重复密码

            ' - +'

            ' - +'
            ' - +'
            '; - - $("#webedit-con").html(webPathHtml); - $("#userini").change(function(){ - $.post('/site/set_dir_user_ini',{ - 'path':webpath, - 'runPath':runPath, - },function(userini){ - layer.msg(userini.msg+'

            注意:设置防跨站需要重启PHP才能生效!

            ',{icon:userini.status?1:2}); - tryRestartPHP(siteName); - },'json'); - }); - - $("#logs").change(function(){ - var loadT = layer.msg("正在设置中...",{icon:16,time:10000,shade: [0.3, '#000']}); - $.post('/site/logs_open','id='+id, function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - }); - - },'json'); -} - -//是否设置访问密码 -function pathSafe(id){ - var isPass = $('#pathSafe').prop('checked'); - if(!isPass){ - $(".user_pw").show(); - } else { - var loadT = layer.msg(lan.public.the,{icon:16,time:10000,shade: [0.3, '#000']}); - $.post('/site/close_has_pwd',{id:id},function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - $(".user_pw").hide(); - },'json'); - } -} - -//设置访问密码 -function setPathSafe(id){ - var username = $("#username_get").val(); - var pass1 = $("#password_get_1").val(); - var pass2 = $("#password_get_2").val(); - if(pass1 != pass2){ - layer.msg('两次输入的密码不一致!',{icon:2}); - return; - } - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:10000,shade: [0.3, '#000']}); - $.post('/site/set_has_pwd',{id:id,username:username,password:pass1},function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); -} - -//提交运行目录 -function setSiteRunPath(id){ - var NewPath = $("#runPath").val(); - var loadT = layer.msg(lan.public.the,{icon:16,time:10000,shade: [0.3, '#000']}); - $.post('/site/set_site_run_path','id='+id+'&runPath='+NewPath,function(rdata){ - layer.close(loadT); - var ico = rdata.status?1:2; - layer.msg(rdata.msg,{icon:ico}); - },'json'); -} - -//提交网站目录 -function setSitePath(id){ - var NewPath = $("#inputPath").val(); - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:10000,shade: [0.3, '#000']}); - $.post('/site/set_path','id='+id+'&path='+NewPath,function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); -} - -//修改网站备注 -function webBakEdit(id){ - $.post("/data?action=getKey','table=sites&key=ps&id="+id,function(rdata){ - var webBakHtml = "
            \ -
            \ - \ -
            \ - \ -

            \ -
            \ -
            "; - $("#webedit-con").html(webBakHtml); - }); -} - - -//设置默认文档 -function setIndexEdit(id){ - $.post('/site/get_index','id='+id,function(data){ - var rdata = data['index']; - rdata = rdata.replace(new RegExp(/(,)/g), "\n"); - var setIndexHtml = "
            \ -
            \ - \ - \ -
            \ -
              \ -
            • 默认文档,每行一个,优先级由上至下。
            • \ -
            \ -
            "; - $("#webedit-con").html(setIndexHtml); - },'json'); -} - -/** - * 停止一个站点 - * @param {Int} wid 网站ID - * @param {String} wname 网站名称 - */ -function webStop(wid, wname) { - layer.confirm('站点停用后将无法访问,您真的要停用这个站点吗?', {icon:3,closeBtn:2},function(index) { - if (index > 0) { - var loadT = layer.load(); - $.post("/site/stop","id=" + wid + "&name=" + wname, function(ret) { - layer.msg(ret.msg,{icon:ret.status?1:2}) - layer.close(loadT); - getWeb(1); - },'json'); - } - }); -} - -/** - * 启动一个网站 - * @param {Number} wid 网站ID - * @param {String} wname 网站名称 - */ -function webStart(wid, wname) { - layer.confirm('即将启动站点,您真的要启用这个站点吗?',{icon:3,closeBtn:2}, function(index) { - if (index > 0) { - var loadT = layer.load() - $.post("/site/start","id=" + wid + "&name=" + wname, function(ret) { - layer.msg(ret.msg,{icon:ret.status?1:2}) - layer.close(loadT); - getWeb(1); - },'json'); - } - }); -} - -/** - * 删除一个网站 - * @param {Number} wid 网站ID - * @param {String} wname 网站名称 - */ -function webDelete(wid, wname){ - var thtml = "
            \ - \ -
            "; - var info = '是否要删除同名根目录'; - safeMessage('删除站点'+"["+wname+"]",info, function(){ - var path=''; - if($("#delpath").is(":checked")){ - path='&path=1'; - } - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:10000,shade: [0.3, '#000']}); - $.post("/site/delete","id=" + wid + "&webname=" + wname + path, function(ret){ - layer.closeAll(); - layer.msg(ret.msg,{icon:ret.status?1:2}) - getWeb(1); - },'json'); - },thtml); -} - - -//批量删除 -function allDeleteSite(){ - var checkList = $("input[name=id]"); - var dataList = new Array(); - for(var i=0;i"+lan.site.all_del_info+"\ -
            "; - safeMessage(lan.site.all_del_site,""+lan.get('del_all_site',[dataList.length])+"",function(){ - layer.closeAll(); - var path = ''; - if($("#delpath").is(":checked")){ - path='&path=1'; - } - syncDeleteSite(dataList, 0,'',path); - },thtml); -} - -//模拟同步开始批量删除 -function syncDeleteSite(dataList,successCount,errorMsg,path){ - if(dataList.length < 1) { - showMsg(lan.get('del_all_site_ok',[successCount]), function(){ - // location.reload(); - },{icon:1}); - return; - } - var loadT = layer.msg(lan.get('del_all_task_the',[dataList[0].name]),{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/delete', 'id='+dataList[0].id+'&webname='+dataList[0].name+path , function(rdata){ - layer.close(loadT); - if(rdata.status){ - successCount++; - $("input[title='"+dataList[0].name+"']").parents("tr").remove(); - } else { - if(!errorMsg){ - errorMsg = '

            '+lan.site.del_err+':

            '; - } - errorMsg += '
          • '+dataList[0].name+' -> '+rdata.msg+'
          • ' - } - dataList.splice(0,1); - syncDeleteSite(dataList,successCount,errorMsg,path); - },'json'); -} - - -/** - * 域名管理 - * @param {Int} id 网站ID - */ -function domainEdit(id, name, msg, status) { - $.post('/site/get_domain' ,{pid:id}, function(domain) { - - var echoHtml = ""; - for (var i = 0; i < domain.length; i++) { - echoHtml += "\ - " + domain[i].name + "\ - " + domain[i].port + "\ - \ - "; - } - var bodyHtml = "\ - \ - \ -
            \ - \ - \ - " + echoHtml + "\ -
            "+lan.site.domain+"端口操作
            \ -
            "; - $("#webedit-con").html(bodyHtml); - if(msg != undefined){ - layer.msg(msg,{icon:status?1:5}); - } - var placeholder = "
            每行填写一个域名,默认为80端口
            泛解析添加方法 *.domain.com
            如另加端口格式为 www.domain.com:88
            "; - $('#newdomain').after(placeholder); - $(".placeholder").click(function(){ - $(this).hide(); - $('#newdomain').focus(); - }) - $('#newdomain').focus(function() { - $(".placeholder").hide(); - }); - - $('#newdomain').blur(function() { - if($(this).val().length==0){ - $(".placeholder").show(); - } - }); - $("#newdomain").on("input",function(){ - var str = $(this).val(); - if(isChineseChar(str)) { - $('.btn-zhm').show(); - } else{ - $('.btn-zhm').hide(); - } - }) - //checkDomain(); - },'json'); -} - -//编辑域名/端口 -function cancelSend(){ - $(".changeDomain,.changePort").hide().prev().show(); - $(".changeDomain,.changePort").remove(); -} -//遍历域名 -function checkDomain() { - $("#checkDomain tr").each(function() { - var $this = $(this); - var domain = $(this).find("td:first-child").text(); - $(this).find("td:first-child").append(""); - }); -} - -/** - * 添加域名 - * @param {Int} id 网站ID - * @param {String} webname 主域名 - */ -function domainAdd(id, webname,type) { - var Domain = $("#newdomain").val().split("\n"); - - var domainlist = ''; - for(var i=0; i下载 | "; - body += ""+frdata.data[i].name+"\ - " + (toSize(frdata.data[i].size)) + "\ - " + frdata.data[i].addtime + "\ - "+ ftpdown + "删除\ - " - } - - var ftpdown = ''; - frdata.page = frdata.page.replace(/'/g,'"').replace(/getBackup\(/g,"getBackup(" + id + ",0,"); - - if(name == 0){ - var sBody = "\ - \ - "+body+"\ -
            文件名称文件大小打包时间操作
            " - $("#webBackupList").html(sBody); - $(".page").html(frdata.page); - return; - } - layer.closeAll(); - layer.open({ - type: 1, - skin: 'demo-class', - area: '700px', - title: '打包备份', - closeBtn: 1, - shift: 0, - shadeClose: false, - content: "
            \ - \ -
            \ - \ - \ - \ - \ - " + body + "\ -
            文件名称文件大小打包时间操作
            \ -
            " + frdata.page + "
            \ -
            \ -
            " - }); - },'json'); -} - -function goSet(num) { - //取选中对象 - var el = document.getElementsByTagName('input'); - var len = el.length; - var data = ''; - var a = ''; - var count = 0; - //构造POST数据 - for (var i = 0; i < len; i++) { - if (el[i].checked == true && el[i].value != 'on') { - data += a + count + '=' + el[i].value; - a = '&'; - count++; - } - } - //判断操作类别 - if(num==1){ - reAdd(data); - } - else if(num==2){ - shift(data); - } -} - - -//设置默认文档 -function setIndex(id){ - var quanju = (id==undefined)?lan.site.public_set:lan.site.local_site; - var data=id==undefined?"":"id="+id; - $.post('/site?action=GetIndex',data,function(rdata){ - rdata= rdata.replace(new RegExp(/(,)/g), "\n"); - layer.open({ - type: 1, - area: '500px', - title: lan.site.setindex, - closeBtn: 1, - shift: 5, - shadeClose: true, - content:"
            " - +"
            " - +" "+lan.site.default_doc+"" - +"
            " - +" " - +"

            "+quanju+lan.site.default_doc_help+"

            " - +"
            " - +"
            " - +"
            " - +" " - +" " - +"
            " - +"
            " - }); - }); -} - -//设置默认站点 -function setDefaultSite(){ - var name = $("#default_site").val(); - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_default_site','name='+name,function(rdata){ - layer.closeAll(); - layer.msg(rdata.msg,{icon:rdata.status?1:5}); - },'json'); -} - - -//默认站点 -function getDefaultSite(){ - $.post('/site/get_default_site','',function(rdata){ - var opt = ''; - var selected = ''; - for(var i=0;i' + rdata.sites[i].name + ''; - } - - layer.open({ - type: 1, - area: '530px', - title: '设置默认站点', - closeBtn: 1, - shift: 5, - shadeClose: true, - content:'
            \ -

            \ - 默认站点\ - \ -

            \ -
              \ -
            • 设置默认站点后,所有未绑定的域名和IP都被定向到默认站点
            • \ -
            • 可有效防止恶意解析
            • \ -
            \ -
            \ - \ - \ -
            \ -
            ' - }); - },'json'); -} - -function setPHPVer(){ - $.post('/site/get_cli_php_version','',function(rdata){ - if(typeof(rdata['status'])!='undefined'){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - return; - } - - var opt = ''; - var selected = ''; - for(var i=0;i-1){ - continue; - } - - if (rdata.versions[i].version.indexOf("apt")>-1){ - continue; - } - - opt += ''; - } - - var phpver_layer = layer.open({ - type: 1, - area: '530px', - title: '设置PHP-CLI(命令行)版本', - closeBtn: 1, - shift: 5, - shadeClose: true, - btn:["确定","取消"], - content:'
            \ -

            \ - PHP-CLI版本\ - \ -

            \ -
              \ -
            • 此处可设置命令行运行php时使用的PHP版本
            • \ -
            • 安装新的PHP版本后此处需要重新设置
            • \ -
            \ -
            ', - yes:function(layero,index){ - var version = $("#default_ver").val(); - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_cli_php_version','version='+version,function(rdata){ - layer.close(loadT); - showMsg(rdata.msg,function(){ - if (rdata.status){ - layer.close(phpver_layer); - } - },{icon:rdata.status?1:5},2000); - },'json'); - }, - }); - },'json'); -} - -function setIndexList(id){ - var Dindex = $("#Dindex").val().replace(new RegExp(/(\n)/g), ","); - if(id == undefined ){ - var data="id=&index="+Dindex; - } else{ - var data="id="+id+"&index="+Dindex; - } - var loadT= layer.load(2); - $.post('/site/set_index',data,function(rdata){ - layer.close(loadT); - var ico = rdata.status? 1:5; - layer.msg(rdata.msg,{icon:ico}); - },'json'); -} - - -/*站点修改*/ -function webEdit(id,website,endTime,addtime){ - layer.open({ - type: 1, - area: '700px', - title: '站点修改['+website+'] -- 添加时间['+addtime+']', - closeBtn: 1, - shift: 0, - content: "
            \ -
            \ -

            域名管理

            \ -

            子目录绑定

            \ -

            网站目录

            \ -

            流量限制

            \ -

            伪静态

            \ -

            默认文档

            \ -

            配置文件

            \ -

            SSL

            \ -

            PHP版本

            \ -

            重定向

            \ -

            反向代理

            \ -

            防盗链

            \ -

            响应日志

            \ -

            错误日志

            \ -
            \ -
            \ -
            ", - success:function(){ - //域名输入提示 - var placeholder = "
            每行填写一个域名,默认为80端口
            泛解析添加方法 *.domain.com
            如另加端口格式为 www.domain.com:88
            "; - $('#newdomain').after(placeholder); - $(".placeholder").click(function(){ - $(this).hide(); - $('#newdomain').focus(); - }); - - $('#newdomain').focus(function() { - $(".placeholder").hide(); - }); - - $('#newdomain').blur(function() { - if($(this).val().length == 0){ - $(".placeholder").show(); - } - }); - - //切换 - $(".bt-w-menu p").click(function(){ - $(this).addClass("bgw").siblings().removeClass("bgw"); - }); - - domainEdit(id,website); - } - }); -} - -//取网站日志 -function getSiteLogs(siteName){ - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/get_logs',{siteName:siteName},function(logs){ - layer.close(loadT); - if(logs.status !== true){ - logs.msg = ''; - } - if (logs.msg == '') logs.msg = '当前没有日志.'; - var phpCon = ''; - $("#webedit-con").html(phpCon); - var ob = document.getElementById('error_log'); - ob.scrollTop = ob.scrollHeight; - },'json'); -} - -//取网站错误日志 -function getSiteErrorLogs(siteName){ - var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/get_error_logs',{siteName:siteName},function(logs){ - // console.log(logs); - layer.close(loadT); - if(logs.status !== true){ - logs.msg = ''; - } - if (logs.msg == '') logs.msg = '当前没有日志.'; - var phpCon = ''; - $("#webedit-con").html(phpCon); - var ob = document.getElementById('error_log'); - ob.scrollTop = ob.scrollHeight; - },'json'); -} - - -//防盗链 -function security(id,name){ - var loadT = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/get_security',{id:id,name:name},function(rdata){ - console.log(rdata); - layer.close(loadT); - var mbody = '
            ' - +'

            URL后缀

            ' - +'

            许可域名

            ' - +'
            ' - +'
            ' - +'
              ' - +'
            • 默认允许资源被直接访问,即不限制HTTP_REFERER为空的请求
            • ' - +'
            • 多个URL后缀与域名请使用逗号(,)隔开,如: png,jpeg,zip,js
            • ' - +'
            • 当触发防盗链时,将直接返回404状态
            • ' - +'
            ' - +'
            ' - $("#webedit-con").html(mbody); - },'json'); -} - -//设置防盗链 -function setSecurity(name,id, none){ - setTimeout(function(){ - var data = { - fix:$("input[name='sec_fix']").val(), - domains:$("input[name='sec_domains']").val(), - status:$("input[name='sec_status']").prop("checked"), - none:$("input[name='sec_none_status']").prop("checked"), - name:name, - id:id - } - var loadT = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_security',data,function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - if(rdata.status) setTimeout(function(){security(id,name);},1000); - },'json'); - },100); -} - - -//流量限制 -function limitNet(id){ - $.post('/site/get_limit_net','id='+id, function(rdata){ - var status_selected = rdata.perserver != 0?'checked':''; - if(rdata.perserver == 0){ - rdata.perserver = 300; - rdata.perip = 25; - rdata.limit_rate = 512; - } - var limitList = "" - +"" - +"" - +"" - +"" - +"" - +"" - var body = "
            " - +'

            ' - +"

            "+lan.site.limit_net_9+":

            " - +"

            "+lan.site.limit_net_10+":

            " - +"

            "+lan.site.limit_net_12+":

            " - +"

            "+lan.site.limit_net_14+":

            " - +"" - +"
            " - +"
            • "+lan.site.limit_net_11+"
            • "+lan.site.limit_net_13+"
            • "+lan.site.limit_net_15+"
            " - $("#webedit-con").html(body); - - $("select[name='limit']").change(function(){ - var type = $(this).val(); - perserver = 300; - perip = 25; - limit_rate = 512; - switch(type){ - case '1': - perserver = 300; - perip = 25; - limit_rate = 512; - break; - case '2': - perserver = 200; - perip = 10; - limit_rate = 1024; - break; - case '3': - perserver = 50; - perip = 3; - limit_rate = 2048; - break; - case '4': - perserver = 500; - perip = 10; - limit_rate = 2048; - break; - case '5': - perserver = 400; - perip = 15; - limit_rate = 1024; - break; - case '6': - perserver = 60; - perip = 10; - limit_rate = 512; - break; - case '7': - perserver = 150; - perip = 4; - limit_rate = 1024; - break; - } - - $("input[name='perserver']").val(perserver); - $("input[name='perip']").val(perip); - $("input[name='limit_rate']").val(limit_rate); - }); - },'json'); -} - - -//保存流量限制配置 -function saveLimitNet(id, type){ - var isChecked = $("input[name='status']").attr('checked'); - if(isChecked == undefined || type == 1 ){ - var data = 'id='+id+'&perserver='+$("input[name='perserver']").val()+'&perip='+$("input[name='perip']").val()+'&limit_rate='+$("input[name='limit_rate']").val(); - var loadT = layer.msg(lan.public.config,{icon:16,time:10000}) - $.post('/site/save_limit_net',data,function(rdata){ - layer.close(loadT); - limitNet(id); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - }else{ - var loadT = layer.msg(lan.public.config,{icon:16,time:10000}) - $.post('/site/close_limit_net',{id:id},function(rdata){ - layer.close(loadT); - limitNet(id); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - } -} - - -//子目录绑定 -function dirBinding(id){ - $.post('/site/get_dir_binding',{'id':id},function(data){ - var rdata = data['data']; - var echoHtml = ''; - for(var i=0;i"+rdata.binding[i].port+""+rdata.binding[i].path+"伪静态 | 删除"; - } - - var dirList = ''; - for(var n=0;n"+rdata.dirs[n]+""; - } - - var body = "
            " - + "域名:" - + "子目录:" - + "" - + "
            " - + "
            " - + "" - + "" + echoHtml + "" - + "
            域名端口子目录操作
            "; - - $("#webedit-con").html(body); - },'json'); -} - -//子目录伪静态 -function setDirRewrite(id){ - $.post('/site/get_dir_bind_rewrite','id='+id,function(rdata){ - if(!rdata.status){ - var confirmObj = layer.confirm('你真的要为这个子目录创建独立的伪静态规则吗?',{icon:3,closeBtn:2},function(){ - $.post('/site/get_dir_bind_rewrite','id='+id+'&add=1',function(rdata){ - layer.close(confirmObj); - showRewrite(rdata); - },'json'); - }); - return; - } - showRewrite(rdata); - },'json'); -} - -//显示伪静态 -function showRewrite(rdata){ - var rList = ''; - for(var i=0;i"+rdata.rlist[i]+""; - } - var webBakHtml = "
            \ -
            \ - \ - \ -
            \ - \ -
              \ -
            • 请选择您的应用,若设置伪静态后,网站无法正常访问,请尝试设置回default
            • \ -
            • 您可以对伪静态规则进行修改,修改完后保存即可。
            • \ -
            \ -
            "; - layer.open({ - type: 1, - area: '500px', - title: '配置伪静态规则', - closeBtn: 1, - shift: 5, - shadeClose: true, - content:webBakHtml, - success:function(){ - - $("#myRewrite").change(function(){ - var rewriteName = $(this).val(); - $.post('/files/get_body','path='+rdata['rewrite_dir']+'/'+rewriteName+'.conf',function(fileBody){ - $("#rewriteBody").val(fileBody.data.data); - },'json'); - }); - - $('#setRewriteBtn').click(function(){ - var data = $("#rewriteBody").val(); - setRewrite(rdata.filename, encodeURIComponent(data)); - }); - } - }); -} - -//添加子目录绑定 -function addDirBinding(id){ - var domain = $("input[name='domain']").val(); - var dirName = $("select[name='dirName']").val(); - if(domain == '' || dirName == '' || dirName == null){ - layer.msg(lan.site.d_s_empty,{icon:2}); - return; - } - - var data = 'id='+id+'&domain='+domain+'&dirName='+dirName; - $.post('/site/add_dir_bind',data,function(rdata){ - dirBinding(id); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); -} - -//删除子目录绑定 -function delDirBind(id,siteId){ - layer.confirm(lan.site.s_bin_del,{icon:3,closeBtn:2},function(){ - $.post('/site/del_dir_bind','id='+id,function(rdata){ - dirBinding(siteId); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - }); -} - -//301重定向 -function to301(siteName, type, obj){ - - // 设置 页面展示 - if(type == 1) { - obj = { - to: 'http://', - from: '', - r_type: '', - type: 1, - type: 'path', - keep_path: 1 - } - var redirect_form = layer.open({ - type: 1, - skin: 'demo-class', - area: '650px', - title: type == 1 ? '创建重定向' : '修改重定向[' + obj.redirectname + ']', - closeBtn: 1, - shift: 5, - shadeClose: false, - content: "
            " + - "
            " + - "
            " + - "保留URI参数" + - "" + - "
            " + - "
            " + - "
            " + - "重定向类型" + - "
            " + - "" + - "重定向方式" + - "
            " + - "
            " + - "
            " + - "重定向源" + - "
            " + - "" + - "目标URL" + - "" + - "
            " + - "
            " + - "
            " + - "
            " + - "" - }); - setTimeout(function() { - $('.btn-colse-prosy').click(function() { - layer.close(redirect_form); - }); - $('.btn-submit-redirect').click(function() { - var keep_path = $('[name="keep_path"]').prop('checked') ? 1 : 0; - var r_type = $('[name="r_type"]').val(); - var type = $('[name="type"]').val(); - var from = $('[name="from"]').val(); - var to = $('[name="to"]').val(); - - $.post('/site/set_redirect', { - siteName: siteName, - type: type, - r_type: r_type, - from: from, - to: to, - keep_path: keep_path - }, function(res) { - res = JSON.parse(res); - if (res.status) { - layer.close(redirect_form); - to301(siteName) - } else { - layer.msg(res.msg, { - icon: 2 - }); - } - }); - }); - }, 100); - } - - if (type == 2) { - $.post('/site/del_redirect', { - siteName: siteName, - id: obj, - }, function(res) { - res = JSON.parse(res); - if (res.status == true) { - layer.msg('删除成功', {time: 1000,icon: 1}); - to301(siteName); - } else { - layer.msg(res.msg, {time: 1000,icon: 2}); - } - }); - return - } - - if (type == 3) { - var laoding = layer.load(); - var data = {siteName: siteName,id: obj}; - $.post('/site/get_redirect_conf', data, function(res) { - layer.close(laoding); - res = JSON.parse(res); - if (res.status == true) { - var mBody = "
            \ - \ -
            \ -
              \ -
            • 此处为重定向配置文件,若您不了解配置规则,请勿随意修改.
            • \ -
            \ -
            \ -
            "; - var editor; - var index = layer.open({ - type: 1, - title: '编辑配置文件', - closeBtn: 1, - shadeClose: true, - area: ['500px', '500px'], - btn: ['提交','关闭'], - content: mBody, - success: function () { - editor = CodeMirror.fromTextArea(document.getElementById("configRedirectBody"), { - extraKeys: {"Ctrl-Space": "autocomplete"}, - lineNumbers: true, - matchBrackets:true, - }); - editor.focus(); - $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0}); - $("#onlineEditFileBtn").unbind('click'); - }, - yes:function(index,layero){ - $("#configRedirectBody").empty().text(editor.getValue()); - var load = layer.load(); - var data = { - siteName: siteName, - id: obj, - config: editor.getValue(), - }; - $.post('/site/save_redirect_conf', data, function(res) { - layer.close(load) - if (res.status == true) { - layer.msg('保存成功', {icon: 1}); - layer.close(index); - } else { - layer.msg(res.msg, {time: 3000,icon: 2}); - } - },'json'); - return true; - }, - }); - - } else { - layer.msg('请求错误!!', {time: 3000,icon: 2}); - } - }); - return - } - - var body = '
            \ -
            \ - \ -
            \ -
            \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
            重定向类型重定向方式保留URL参数操作
            \ -
            \ -
            '; - $("#webedit-con").html(body); - - var loadT = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/get_redirect','siteName='+siteName, function(res) { - layer.close(loadT); - $("#md-301-loading").remove(); - if (res.status === true) { - let data = res.data.result; - data.forEach(function(item){ - lan_r_type = item.r_type == 0 ? "永久重定向" : "临时重定向" - keep_path = item.keep_path == 0 ? "不保留" : "保留" - let tmp = '\ - '+item.r_from+'\ - '+lan_r_type+'\ - '+keep_path+'\ - 详细 | 删除\ - '; - $("#md-301-body").append(tmp); - }) - } else { - layer.msg(res.msg, {icon:2}); - } - },'json'); -} - - -//反向代理 -function toProxy(siteName, type, obj) { - // 设置 页面展示 - if(type == 1) { - var proxy_title = "创建反向代理"; - if (typeof(obj) != 'undefined'){ - proxy_title = "编辑反向代理"; - } - - layer.open({ - type: 1, - area: '650px', - title: proxy_title, - closeBtn: 1, - shift: 5, - shadeClose: false, - btn: ['提交','关闭'], - content: "
            \ -
            \ - 开启代理\ -
            \ - \ - \ -
            \ - 是否缓存\ - \ - \ -
            \ -
            \ -
            \ -
            \ - 名称\ -
            \ - \ -
            \ -
            \ - \ -
            \ - 代理目录\ -
            \ - \ -
            \ -
            \ -
            \ - 目标URL\ -
            \ - \ - 发送域名\ - \ -
            \ -
            \ - \ -
            \ -
              \ -
            • 代理目录:访问这个目录时将会把目标URL的内容返回并显示
            • \ -
            • 目标URL:可以填写你需要代理的站点,目标URL必须为可正常访问的URL,否则将返回错误
            • \ -
            • 发送域名:将域名添加到请求头传递到代理服务器,默认为目标URL域名,若设置不当可能导致代理无法正常运行
            • \ -
            \ -
            \ -
            ", - success:function(){ - - if (typeof(obj) != 'undefined'){ - // console.log(obj); - $('input[name="name"]').val(obj['name']).attr('readonly','readonly').addClass('disabled'); - if (obj['open_cache'] == 'on'){ - $("input[name='open_cache']").prop("checked",true); - $('#cache_time').show(); - } - - $('input[name="from"]').val(obj['from']); - $('input[name="to"]').val(obj['to']); - - var url = obj['to']; - var ip_reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/; - url = url.replace(/^http[s]?:\/\//, ''); - url = url.replace(/(:|\?|\/|\\)(.*)$/, ''); - if (ip_reg.test(url)) { - $("[name='host']").val('$host'); - } else { - $("[name='host']").val(url); - } - - $('input[name="id"]').val(obj['id']); - $('input[name="cache_time"]').val(obj['cache_time']); - } - - - $('input[name="to"]').on('keyup', function(){ - var url = $(this).val(); - var ip_reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/; - url = url.replace(/^http[s]?:\/\//, ''); - url = url.replace(/(:|\?|\/|\\)(.*)$/, ''); - if (ip_reg.test(url)) { - $("[name='host']").val('$host'); - } else { - $("[name='host']").val(url); - } - }); - - $("#open_proxy").click(function(){ - var status = $("input[name='open_proxy']").prop("checked")==true?1:0; - if(status==1){ - $("input[name='open_proxy']").prop("checked",false); - }else{ - $("input[name='open_proxy']").prop("checked",true); - } - }); - - $('#open_cache').click(function(){ - var status = $("input[name='open_cache']").prop("checked")==true?1:0; - if(status==1){ - $('#cache_time').hide(); - $("input[name='open_cache']").prop("checked",false); - }else{ - $('#cache_time').show(); - $("input[name='open_cache']").prop("checked",true); - } - }); - }, - yes:function(index,layer_ro){ - var data = $('#form_proxy').serializeArray(); - var t = {}; - t['name'] = 'siteName'; - t['value'] = siteName; - data.push(t); - - // console.log(data); - var loading = layer.msg('正在'+proxy_title+'...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_proxy',data, function(res) { - layer.close(loading); - if (!res.status){ - layer.msg(res.msg, {icon: 2,time:10000}); - return; - } - - showMsg(proxy_title+"成功!",function(){ - layer.close(index); - toProxy(siteName); - },{icon: 1, time:2000}); - },'json'); - } - }); - } - - if (type == 2) { - var loading = layer.msg('正在删除中...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/del_proxy', {siteName: siteName,id: obj,}, function(res) { - layer.close(loading); - if (res.status == true) { - showMsg('删除成功', function(){ - toProxy(siteName); - },{time: 1000,icon: 1}); - } else { - layer.msg(res.msg, {time: 1000,icon: 2}); - } - },'json'); - return - } - - - if (type == 3) { - var laoding = layer.load(); - var data = {siteName: siteName,id: obj}; - $.post('/site/get_proxy_conf', data, function(res) { - layer.close(laoding); - if (!res.status){ - layer.msg('请求错误!!', {time: 3000,icon: 2}); - return; - } - - var mBody = "
            \ - \ -
            \ -
              \ -
            • 此处为反向代理配置文件,若您不了解配置规则,请勿随意修改.
            • \ -
            \ -
            \ -
            "; - var editor; - var index = layer.open({ - type: 1, - title: '编辑配置文件', - closeBtn: 1, - shadeClose: true, - area: ['500px', '500px'], - btn: ['提交','关闭'], - content: mBody, - success: function () { - editor = CodeMirror.fromTextArea(document.getElementById("configProxyBody"), { - extraKeys: {"Ctrl-Space": "autocomplete"}, - lineNumbers: true, - matchBrackets:true, - }); - editor.focus(); - $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0}); - $("#onlineEditFileBtn").unbind('click'); - }, - yes:function(index,layero){ - $("#configProxyBody").empty().text(editor.getValue()); - var load = layer.load(); - var data = { - siteName: siteName, - id: obj, - config: editor.getValue(), - }; - - $.post('/site/save_proxy_conf', data, function(res) { - layer.close(load) - if (res.status == true) { - layer.msg('保存成功', {icon: 1}); - layer.close(index); - } else { - layer.msg(res.msg, {time: 3000,icon: 2}); - } - },'json'); - return true; - }, - }); - },'json'); - return; - } - - if (type == 10 || type == 11) { - //[11]启动 或 停止[10] - status = type==10 ? '0' : '1'; - var loading = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_proxy_status', {siteName: siteName,'status':status,'id':obj}, function(rdata) { - layer.close(loading); - if (!rdata.status){ - layer.msg(res.msg, {time: 3000,icon: 2}); - return; - } - - showMsg("设置成功",function(){ - toProxy(siteName); - },{icon: 1,time:2000}); - },'json'); - return; - } - - if (type == 20 || type == 21) { - //[20] 开始缓存 或 [21] 停止缓存 - var status = type == 20 ? 'on' : ''; - obj['open_cache'] = status; - obj['siteName'] = siteName; - - var loading = layer.msg('正在提交请求...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_proxy',obj, function(rdata) { - layer.close(loading); - if (!rdata.status){ - layer.msg(rdata.msg, {icon: 2,time:2000}); - return; - } - - showMsg("设置成功!",function(){ - toProxy(siteName); - },{icon: 1, time:2000}); - },'json'); - return; - } - - var body = '
            \ -
            \ - \ -
            \ -
            \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
            名称代理目录目标地址缓存状态操作
            \ -
            \ -
            '; - $("#webedit-con").html(body); - - var loading = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); - $.post("/site/get_proxy_list", {siteName: siteName},function (res) { - layer.close(loading); - if (!res.status){ - layer.msg(res.msg, {icon:2}); - return; - } - - var data = res.data.result; - for (var i = 0; i < data.length; i++) { - var item = data[i]; - - var switchProxy = ''; - if (!item['status']){ - switchProxy = ''; - } - - var openCache = '未开启'; - if (item['open_cache'] == 'on'){ - openCache = '已开启'; - } - - let tmp = '\ - '+item.name+'\ - '+item.from+'\ - '+item.to+'\ - '+openCache+'\ - '+switchProxy+'\ - \ - 详细 |\ - 编辑 |\ - 删除\ - \ - '; - $("#md-301-body").append(tmp); - } - - $('#md-301-body .detail').click(function(){ - var index = $(this).data('index'); - toProxy(siteName, 3 ,data[index]['id']); - }); - - $('#md-301-body .edit').click(function(){ - var index = $(this).data('index'); - toProxy(siteName, 1 ,data[index]); - }); - - $('#md-301-body .delete').click(function(){ - var index = $(this).data('index'); - toProxy(siteName, 2 ,data[index]['id']); - }); - - $('#md-301-body .cache').click(function(){ - var index = $(this).data('index'); - if ($(this).hasClass('on')){ - toProxy(siteName, 21 ,data[index]); - } else{ - toProxy(siteName, 20 ,data[index]); - } - }); - - },'json'); -///////// -} - -//证书夹 -function sslAdmin(siteName){ - var loadT = layer.msg('正在提交任务...',{icon:16,time:0,shade: [0.3, '#000']}); - $.get('/site/get_cert_list',function(data){ - layer.close(loadT); - var rdata = data['data']; - var tbody = ''; - for(var i=0;i\ - '+rdata[i].dns.join('
            ')+'\ - '+rdata[i].notAfter+'\ - '+rdata[i].issuer.split(' ')[0]+'\ - 部署 | 删除\ - ' - } - var txt = '
            \ - \ -
            \ - '+tbody+'\ -
            域名信任名称到期时间品牌操作
            '; - $(".tab-con").html(txt); - },'json'); -} - -//删除证书 -function removeSsl(certName){ - safeMessage('删除证书','您真的要从证书夹删除证书吗?',function(){ - var loadT = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/remove_cert',{certName:certName},function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - $("#ssl_admin").click(); - },'json'); - }); -} - -//从证书夹部署 -function setCertSsl(certName,siteName){ - var loadT = layer.msg('正在部署证书...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_cert_to_site',{certName:certName,siteName:siteName},function(rdata){ - layer.close(loadT); - showMsg(rdata.msg, function(){ - $(".tab-nav span:first-child").click(); - },{icon:rdata.status?1:2},2000); - },'json'); -} - -//ssl -function setSSL(id,siteName){ - var sslHtml = '\ -
            \ - 当前证书 - [未部署SSL]\ - Let\'s Encrypt\ - ACME\ - 证书夹' - + '
            \ -
            ' - + '
            '; - $("#webedit-con").html(sslHtml); - $(".tab-nav span").click(function(){ - $(this).addClass("on").siblings().removeClass("on"); - }); - opSSL('now',id,siteName); -} - -//设置httpToHttps -function httpToHttps(siteName){ - var isHttps = $("#toHttps").prop('checked'); - if(isHttps){ - layer.confirm('关闭强制HTTPS后需要清空浏览器缓存才能看到效果,继续吗?',{icon:3,title:"关闭强制HTTPS"},function(){ - $.post('/site/close_to_https','siteName='+siteName,function(rdata){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - }); - }else{ - $.post('/site/http_to_https','siteName='+siteName,function(rdata){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); - } -} - - -function deleteSSL(type,id,siteName){ - $.post('/site/delete_ssl','site_name='+siteName+'&ssl_type='+type,function(rdata){ - showMsg(rdata.msg, function(){ - opSSL(type,id,siteName); - },{icon:rdata.status?1:2}, 2000); - },'json'); -} - -function deploySSL(type,id,siteName){ - $.post('/site/deploy_ssl','site_name='+siteName+'&ssl_type='+type,function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){ - $('#now_ssl').click(); - } else{ - opSSL(type,id,siteName); - } - },{icon:rdata.status?1:2}, 2000); - },'json'); -} - -function renewSSL(type,id,siteName){ - showSpeedWindow('正在续签...', 'site.get_let_logs', function(layers,index){ - $.post('/site/renew_ssl','site_name='+siteName+'&ssl_type='+type,function(rdata){ - showMsg(rdata.msg, function(){ - if (rdata.status){ - layer.close(index); - opSSL(type,id,siteName); - } - },{icon:rdata.status?1:2}, 2000); - },'json'); - }); -} - -function fileCheck(){ - -} - -function dnsCheck(){ - -} - -//SSL -function opSSL(type, id, siteName, callback){ - - var now = '
            \ - \ -
            \ -
            密钥(KEY)
            \ -
            证书(PEM格式)
            \ -
            \ -
            \ - \ -
            \ -
            \ -
              \ -
            • 粘贴您的*.key以及*.pem内容,然后保存即可。
            • \ -
            • 如果浏览器提示证书链不完整,请检查是否正确拼接PEM证书
            • PEM格式证书 = 域名证书.crt + 根证书(root_bundle).crt
            • \ -
            • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
            • \ -
            '; - - // \ - // \ - var lets = '
            \ -
            \ -
            \ -
            \ - 验证方式\ -
            \ - \ - \ -
            \ -
            \ -
            \ -
            \ -
            \ - \ - \ -
            \ -
            \ -
            \ -
            \ - 管理员邮箱\ - \ -
            \ -
            \ - 域名\ -
              \ -
              \ -
              \ - \ -
              \ -
                \ -
              • 申请之前,请确保域名已解析,如未解析会导致审核失败
              • \ -
              • Let\'s Encrypt免费证书,有效期3个月,支持多域名。默认会自动续签
              • \ -
              • 若您的站点使用了CDN或301重定向会导致续签失败
              • \ -
              • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
              • \ -
              \ -
              '; - - // \ - // \ - var acme = '
              \ -
              \ -
              \ -
              \ - 验证方式\ -
              \ - \ - \ -
              \ -
              \ -
              \ -
              \ -
              \ - \ - \ -
              \ -
              \ -
              \ -
              \ - 管理员邮箱\ - \ -
              \ -
              \ - 域名\ -
                \ -
                \ -
                \ - \ -
                \ -
                  \ -
                • 申请之前,请确保域名已解析,如未解析会导致审核失败
                • \ -
                • 由ACME免费申请证书,有效期3个月,支持多域名。默认会自动续签
                • \ -
                • 若您的站点使用了CDN或301重定向会导致续签失败
                • \ -
                • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
                \ - \ -
                '; - - - switch(type){ - case 'lets': - $(".tab-con").html(lets); - $.post('/site/get_ssl', 'site_name='+siteName+'&ssl_type=lets', function(data){ - var rdata = data['data']; - if(rdata.csr == false){ - $.post('/site/get_site_domains','id='+id, function(rdata) { - var data = rdata['data']; - var opt=''; - for(var i=0;i'+data.domains[i].name+'' - } - } - $("input[name='admin_email']").val(data.email); - $("#ymlist").html(opt); - $("#ymlist li input").click(function(e){ - e.stopPropagation(); - }) - $("#ymlist li").click(function(){ - var o = $(this).find("input"); - if(o.prop("checked")){ - o.prop("checked",false) - } - else{ - o.prop("checked",true); - } - }) - $(".letsApply").click(function(){ - var c = $("#ymlist input[type='checkbox']"); - var str = []; - var domains = ''; - for(var i=0; i\ -
                证书(PEM格式)
                \ - \ -
                \ - \ - \ - \ -
                \ - \ -
                  \ -
                • 已为您自动生成Let\'s Encrypt免费证书
                • \ -
                • 由Let\'s Encrypt免费申请证书,有效期3个月,支持多域名。默认会自动续签
                • \ -
                • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
                • \ -
                '; - $(".tab-con").html(lets); - - if (rdata['cert_data']){ - var issuer = rdata['cert_data']['issuer'].split(" "); - var domains = rdata['cert_data']['dns'].join("、"); - - var cert_data = "
                \ -
                证书品牌:"+issuer[0]+"
                \ -
                到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
                \ -
                \ -
                \ -
                认证域名:"+domains+"
                \ -
                "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - },'json'); - break; - case 'acme': - $(".tab-con").html(acme); - $.post('/site/get_ssl', 'site_name='+siteName+'&ssl_type=acme', function(data){ - var rdata = data['data']; - if(rdata.csr == false){ - $.post('/site/get_site_domains','id='+id, function(rdata) { - var data = rdata['data']; - var opt=''; - for(var i=0;i'+data.domains[i].name - +''; - } - } - $("input[name='admin_email']").val(data.email); - $("#ymlist").html(opt); - $("#ymlist li input").click(function(e){ - e.stopPropagation(); - }) - $("#ymlist li").click(function(){ - var o = $(this).find("input"); - if(o.prop("checked")){ - o.prop("checked",false) - } - else{ - o.prop("checked",true); - } - }) - $(".letsApply").click(function(){ - var c = $("#ymlist input[type='checkbox']"); - var str = []; - var domains = ''; - for(var i=0; i\ -
                证书(PEM格式)
                \ - \ -
                \ - \ - \ -
                \ - \ -
                  \ -
                • 已为您自动生成ACME免费证书
                • \ -
                • 由ACME免费申请证书,有效期3个月,支持多域名。默认会自动续签
                • \ -
                • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
                • \ -
                '; - $(".tab-con").html(acme); - - if (rdata['cert_data']){ - var issuer = rdata['cert_data']['issuer'].split(" "); - var domains = rdata['cert_data']['dns'].join("、"); - - var cert_data = "
                \ -
                证书品牌:"+issuer[0]+"
                \ -
                到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
                \ -
                \ -
                \ -
                认证域名:"+domains+"
                \ -
                "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - },'json'); - break; - case 'now': - $(".tab-con").html(now); - var key = ''; - var csr = ''; - var loadT = layer.msg('正在提交任务...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('site/get_ssl','site_name='+siteName,function(data){ - layer.close(loadT); - var rdata = data['data']; - - if (rdata['cert_data']){ - var issuer = rdata['cert_data']['issuer'].split(" "); - var domains = rdata['cert_data']['dns'].join("、"); - - var cert_data = "
                \ -
                证书品牌:"+issuer[0]+"
                \ -
                到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
                \ -
                \ -
                \ -
                认证域名:"+domains+"
                \ -
                强制HTTPS:\ - \ -
                \ -
                "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - - if(rdata.key == false){ - rdata.key = ''; - } else { - $(".ssl-btn").append(''); - } - - if(rdata.csr == false){ - rdata.csr = ''; - } - $("#key").val(rdata.key); - $("#csr").val(rdata.csr); - - $("#toHttps").attr('checked',rdata.httpTohttps); - if(rdata.status){ - $('.warning_info').css('display','none'); - - $(".ssl-btn").append(""); - $('#now_ssl').html('当前证书 - [已部署SSL]'); - } else{ - $('.warning_info').css('display','block'); - $('#now_ssl').html('当前证书 - [未部署SSL]'); - } - - - if (typeof (callback) != 'undefined'){ - callback(rdata); - } - },'json'); - break; - default: - layer.msg("错误类型", {icon:5}); - break; - } -} - - -//开启与关闭SSL -function ocSSL(action,siteName){ - var loadT = layer.msg('正在获取证书列表,请稍后..',{icon:16,time:0,shade: [0.3, '#000']}); - $.post("/site/"+action,'siteName='+siteName+'&updateOf=1',function(rdata){ - layer.close(loadT) - - if(!rdata.status){ - if(!rdata.out){ - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - setSSL(siteName); - return; - } - data = "

                证书获取失败:


                " - for(var i=0;i" - + "

                错误类型: "+rdata.out[i].Type+"

                " - + "

                详情: "+rdata.out[i].Detail+"

                " - + "
                "; - } - layer.msg(data,{icon:2,time:0,shade:0.3,shadeClose:true}); - return; - } - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - if(action == 'close_ssl_conf'){ - layer.msg('已关闭SSL,请务必清除浏览器缓存后再访问站点!',{icon:1,time:5000}); - } - $(".tab-nav .on").click(); - },'json'); -} - -//生成SSL -function newSSL(siteName, id, domains){ - showSpeedWindow('正在申请...', 'site.get_let_logs', function(layers,index){ - var force = ''; - if ($("#checkDomain").prop("checked")){ - force = '&force=true'; - } - var email = $("input[name='admin_email']").val(); - $.post('/site/create_let','siteName='+siteName+'&domains='+domains+'&email='+email + force,function(rdata){ - layer.close(index); - if(rdata.status){ - showMsg(rdata.msg, function(){ - $(".tab-nav span:first-child").click(); - },{icon:1}, 2000); - return; - } - layer.msg(rdata.msg,{icon:2,area:'500px',time:0,shade:0.3,shadeClose:true}); - },'json'); - }); -} - -function newAcmeSSL(siteName, id, domains){ - showSpeedWindow('正在由ACME申请...', 'site.get_acme_logs', function(layers,index){ - var force = ''; - if($("#checkDomain").prop("checked")){ - force = '&force=true'; - } - var email = $("input[name='admin_email']").val(); - $.post('/site/create_acme','siteName='+siteName+'&domains='+domains+'&email='+email + force,function(rdata){ - layer.close(index); - if(rdata.status){ - showMsg(rdata.msg, function(){ - $(".tab-nav span:first-child").click(); - },{icon:1}, 2000); - return; - } - layer.msg(rdata.msg,{icon:2,area:'500px',time:0,shade:0.3,shadeClose:true}); - },'json'); - }); -} - -//保存SSL -function saveSSL(siteName){ - var data = 'type=1&siteName='+siteName+'&key='+encodeURIComponent($("#key").val())+'&csr='+encodeURIComponent($("#csr").val()); - var loadT = layer.msg(lan.site.saving_txt,{icon:16,time:20000,shade: [0.3, '#000']}) - $.post('/site/set_ssl',data,function(rdata){ - layer.close(loadT); - if(rdata.status){ - layer.msg(rdata.msg,{icon:1}); - $(".ssl-btn").find(".btn-default").remove(); - $(".ssl-btn").append(""); - } else { - layer.msg(rdata.msg,{icon:2,time:0,shade:0.3,shadeClose:true}); - } - },'json'); -} - -//PHP版本 -function phpVersion(siteName){ - $.post('/site/get_site_php_version','siteName='+siteName,function(version){ - // console.log(version); - if(version.status === false){ - layer.msg(version.msg,{icon:5}); - return; - } - $.post('/site/get_php_version',function(rdata){ - var versionSelect = "
                \ -
                \ - PHP版本\ -
                \ - \ - \ -
                \ - \ -
                \ -
                  \ -
                • 请根据您的程序需求选择版本
                • \ -
                • 若非必要,请尽量不要使用PHP5.2,这会降低您的服务器安全性;
                • \ -
                • PHP7不支持mysql扩展,默认安装mysqli以及mysql-pdo。
                • \ -
                \ -
                \ - "; - $("#webedit-con").html(versionSelect); - //验证PHP版本 - $("select[name='phpVersion']").change(function(){ - if($(this).val() == '52'){ - var msgerr = 'PHP5.2在您的站点有漏洞时有跨站风险,请尽量使用PHP5.3以上版本!'; - $('#php_w').text(msgerr); - }else{ - $('#php_w').text(''); - } - }) - },'json'); - },'json'); -} - - -//设置PHP版本 -function setPHPVersion(siteName){ - var data = 'version='+$("#phpVersion").val()+'&siteName='+siteName; - var loadT = layer.msg('正在保存...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_php_version',data,function(rdata){ - layer.close(loadT); - layer.msg(rdata.msg,{icon:rdata.status?1:2}); - },'json'); -} - -//配置文件 -function configFile(webSite){ - var info = syncPost('/site/get_host_conf', {siteName:webSite}); - $.post('/files/get_body','path='+info['host'],function(rdata){ - var mBody = "
                \ - \ -
                \ - \ -
                  \ -
                • 此处为站点主配置文件,若您不了解配置规则,请勿随意修改.
                • \ -
                \ -
                \ -
                "; - $("#webedit-con").html(mBody); - var editor = CodeMirror.fromTextArea(document.getElementById("configBody"), { - extraKeys: {"Ctrl-Space": "autocomplete"}, - lineNumbers: true, - matchBrackets:true, - }); - $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0}); - $("#SaveConfigFileBtn").click(function(){ - $("#configBody").empty(); - $("#configBody").text(editor.getValue()); - saveConfigFile(webSite,rdata.data.encoding, info['host']); - }) - },'json'); -} - -//保存配置文件 -function saveConfigFile(webSite,encoding,path){ - var data = 'encoding='+encoding+'&data='+encodeURIComponent($("#configBody").val())+'&path='+path; - var loadT = layer.msg('保存中...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/save_host_conf',data,function(rdata){ - layer.close(loadT); - if(rdata.status){ - layer.msg(rdata.msg,{icon:1}); - }else{ - layer.msg(rdata.msg,{icon:2,time:0,shade:0.3,shadeClose:true}); - } - },'json'); -} - -//伪静态 -function rewrite(siteName){ - $.post("/site/get_rewrite_list", 'siteName='+siteName,function(rdata){ - var info = syncPost('/site/get_rewrite_conf', {siteName:siteName}); - var filename = info['rewrite']; - $.post('/files/get_body','path='+filename,function(fileBody){ - var centent = fileBody['data']['data']; - var rList = ''; - for(var i=0;i"; - } else { - rList += ""; - } - } - var webBakHtml = "
                \ -
                \ - \ -
                \ - \ - \ -
                  \ -
                • 请选择您的应用,若设置伪静态后,网站无法正常访问,请尝试设置回default
                • \ -
                • 您可以对伪静态规则进行修改,修改完后保存即可。
                • \ -
                \ -
                "; - $("#webedit-con").html(webBakHtml); - - var editor = CodeMirror.fromTextArea(document.getElementById("rewriteBody"), { - extraKeys: {"Ctrl-Space": "autocomplete"}, - lineNumbers: true, - matchBrackets:true, - }); - - $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0}); - $("#SetRewriteBtn").click(function(){ - $("#rewriteBody").empty(); - $("#rewriteBody").text(editor.getValue()); - setRewrite(filename, encodeURIComponent(editor.getValue())); - }); - $("#SetRewriteBtnTel").click(function(){ - $("#rewriteBody").empty(); - $("#rewriteBody").text(editor.getValue()); - setRewriteTel(); - }); - - $("#myRewrite").change(function(){ - var rewriteName = $(this).val(); - if(rewriteName == '0'){ - rpath = filename; - }else{ - var info = syncPost('/site/get_rewrite_tpl', {tplname:rewriteName}); - if (!info['status']){ - layer.msg(info['msg']); - return; - } - rpath = info['data']; - } - - $.post('/files/get_body','path='+rpath,function(fileBody){ - $("#rewriteBody").val(fileBody['data']['data']); - editor.setValue(fileBody['data']['data']); - },'json'); - }); - },'json'); - },'json'); -} - - -//设置伪静态 -function setRewrite(filename,data){ - var data = 'data='+data+'&path='+filename+'&encoding=utf-8'; - var loadT = layer.msg(lan.site.saving_txt,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_rewrite',data,function(rdata){ - layer.close(loadT); - if(rdata.status){ - layer.msg(rdata.msg,{icon:1}); - }else{ - layer.msg(rdata.msg,{icon:2,time:0,shade:0.3,shadeClose:true}); - } - },'json'); -} -var aindex = null; - -//保存为模板 -function setRewriteTel(act){ - aindex = layer.open({ - type: 1, - shift: 5, - closeBtn: 1, - area: '320px', //宽高 - title: '保存为Rewrite模板', - btn:[lan.public.ok,lan.public.cancel], - content: '
                \ -
                \ - \ -
                \ -
                ', - success:function(index){ - $("#rewriteName").focus().keyup(function(e){ - if(e.keyCode == 13) $("#rewriteNameBtn").click(); - }); - }, - yes:function(index){ - name = $("#rewriteName").val(); - if(name == ''){ - layer.msg(lan.site.template_empty,{icon:5}); - return; - } - var data = 'data='+encodeURIComponent($("#rewriteBody").val())+'&name='+name; - var loadT = layer.msg(lan.site.saving_txt,{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/site/set_rewrite_tpl',data,function(rdata){ - layer.close(loadT); - layer.close(index); - layer.msg(rdata.msg, {icon:rdata.status?1:5}); - },'json'); - return; - } - }); -} -//修改默认页 -function siteDefaultPage(){ - stype = getCookie('serverType'); - layer.open({ - type: 1, - area: '460px', - title: '修改默认页', - closeBtn: 1, - shift: 0, - content: '
                \ - \ - \ - \ - \ -
                ' - }); -} - -function changeDefault(type){ - $.post('/site/get_site_doc','type='+type, function(rdata){ - showMsg('操作成功!',function(){ - if (rdata.status){ - vhref = rdata.data.path; - onlineEditFile(0,vhref); - } - },{icon:rdata.status?1:2}); - },'json'); -} - -function getClassType(){ - var select = $('.site_type > select'); - $.post('/site/get_site_types',function(rdata){ - $(select).html(''); - $(select).append(''); - for (var i = 0; i'+rdata[i]['name']+''); - } - - $(select).bind('change',function(){ - var select_id = $(this).val(); - getWeb(1,select_id, ''); - }) - },'json'); -} -getClassType(); - -function setClassType(){ - $.post('/site/get_site_types',function(rdata){ - var list = ''; - for (var i = 0; i\ - 编辑 | 删除\ - '; - } - - layer.open({ - type: 1, - area: '350px', - title: '网站分类管理', - closeBtn: 1, - shift: 0, - content: '
                \ -
                \ -
                \ -
                \ -
                \ - \ - \ - '+list+'\ -
                名称操作
                \ -
                \ -
                ' - }); - },'json'); -} - -function addClassType(){ - var name = $("input[name=type_name]").val(); - $.post('/site/add_site_type','name='+name, function(rdata){ - showMsg(rdata.msg,function(){ - if (rdata.status){ - layer.closeAll(); - setClassType(); - getClassType(); - } - },{icon:rdata.status?1:2}); - },'json'); -} - -function removeClassType(id,name){ - if (id == 0){ - layer.msg('默认分类不可删除/不可编辑!',{icon:2}); - return; - } - layer.confirm('是否确定删除分类?',{title: '删除分类【'+ name +'】' }, function(){ - $.post('/site/remove_site_type','id='+id, function(rdata){ - showMsg(rdata.msg,function(){ - if (rdata.status){ - layer.closeAll(); - setClassType(); - getClassType(); - } - },{icon:rdata.status?1:2}); - },'json'); - }); -} - -function editClassType(id,name){ - if (id == 0){ - layer.msg('默认分类不可删除/不可编辑!',{icon:2}); - return; - } - - layer.open({ - type: 1, - area: '350px', - title: '修改分类管理【' + name + '】', - closeBtn: 1, - shift: 0, - content: "
                \ -
                \ - 分类名称\ -
                \ -
                \ -
                \ - \ -
                \ -
                " - }); - - $('#site_type_mod').unbind().click(function(){ - var _name = $('input[name=site_type_mod]').val(); - $.post('/site/modify_site_type_name','id='+id+'&name='+_name, function(rdata){ - showMsg(rdata.msg,function(){ - if (rdata.status){ - layer.closeAll(); - setClassType(); - getClassType(); - } - },{icon:rdata.status?1:2}); - },'json'); - - }); -} - - -function moveClassTYpe(){ - $.post('/site/get_site_types',function(rdata){ - var option = ''; - for (var i = 0; i'+rdata[i]['name']+''; - } - - layer.open({ - type: 1, - area: '350px', - title: '设置站点分类', - closeBtn: 1, - shift: 0, - content: '
                \ -
                \ -
                默认站点\ -
                \ - \ -
                \ -
                \ -
                \ -
                \ -
                ' - }); - },'json'); -} - - -function setSizeClassType(){ - var data = {}; - data['id'] = $('select[name=type_id]').val(); - var ids = []; - $('table').find('td').find('input').each(function(i,obj){ - checked = $(this).prop('checked'); - if (checked) { - ids.push($(this).val()); - } - }); - data['site_ids'] = JSON.stringify(ids); - $.post('/site/set_site_type',data, function(rdata){ - showMsg(rdata.msg,function(){ - if (rdata.status){ - layer.closeAll(); - } - },{icon:rdata.status?1:2}); - },'json'); -} - - -// 尝试重启PHP -function tryRestartPHP(siteName){ - $.post('/site/get_site_php_version','siteName='+siteName,function(data){ - var phpversion = data.phpversion; - - if (phpversion == "00"){ - return - } - - var php_sign = 'php'; - if (phpversion.indexOf('yum') > -1){ - php_sign = 'php-yum'; - phpversion = phpversion.replace('yum',''); - } - - if (phpversion.indexOf('apt') > -1){ - php_sign = 'php-apt'; - phpversion = phpversion.replace('apt',''); - } - - var reqData = {name: php_sign, func:'restart'} - reqData['version'] = phpversion; - - // console.log(reqData); - var loadT = layer.msg('尝试自动重启PHP['+phpversion+']...', { icon: 16, time: 0, shade: 0.3 }); - $.post('/plugins/run', reqData, function(data) { - layer.close(loadT); - layer.msg(data.msg,{icon:data.status?1:2,time:3000,shade: [0.3, '#000']}); - },'json'); - },'json'); -} \ No newline at end of file diff --git a/route/static/app/soft.js b/route/static/app/soft.js deleted file mode 100755 index eacbfcf5f..000000000 --- a/route/static/app/soft.js +++ /dev/null @@ -1,517 +0,0 @@ - - -//重置插件弹出框宽度 -function resetPluginWinWidth(width){ - $("div[id^='layui-layer'][class*='layui-layer-page']").width(width); -} - -//重置插件弹出框宽度 -function resetPluginWinHeight(height){ - $("div[id^='layui-layer'][class*='layui-layer-page']").height(height); - $(".bt-form .bt-w-main").height(height-42); -} - -//软件管理窗口 -function softMain(name, title, version) { - - var _title = title.replace('-'+version,'') - - var loadT = layer.msg("正在处理,请稍后...", { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.get('/plugins/setting?name='+name, function(rdata) { - layer.close(loadT); - layer.open({ - type: 1, - area: '640px', - title: _title + '【' + version + "】管理", - closeBtn: 1, - shift: 0, - content: rdata - }); - $(".bt-w-menu p").click(function() { - $(this).addClass("bgw").siblings().removeClass("bgw"); - }); - - //version to - $(".plugin_version").attr('version',version).hide(); - }); -} - -//取软件列表 -function getSList(isdisplay) { - if (isdisplay !== true) { - var loadT = layer.msg('正在获取列表...', { icon: 16, time: 0, shade: [0.3, '#000'] }) - } - if (!isdisplay || isdisplay === true) - isdisplay = getCookie('p' + getCookie('soft_type')); - if (isdisplay == true || isdisplay == 'true') isdisplay = 1; - - var search = $("#SearchValue").val(); - if (search != '') { - search = '&search=' + search; - } - var type = ''; - var istype = getCookie('soft_type'); - if (istype == 'undefined' || istype == 'null' || !istype) { - istype = '0'; - } - - type = '&type=' + istype; - var page = ''; - if (isdisplay) { - page = '&p=' + isdisplay; - setCookie('p' + getCookie('soft_type'), isdisplay); - } - - var condition = (search + type + page).slice(1); - $.get('/plugins/list?' + condition, '', function(rdata) { - layer.close(loadT); - var tBody = ''; - var sBody = ''; - var pBody = ''; - - for (var i = 0; i < rdata.type.length; i++) { - var c = ''; - if (istype == rdata.type[i].type) { - c = 'class="on"'; - } - tBody += '' + rdata.type[i].title + ''; - } - - $(".softtype").html(tBody); - $("#softPage").html(rdata.list); - $("#softPage .Pcount").css({ "position": "absolute", "left": "0" }) - - $(".task").text(rdata.data[rdata.length - 1]); - for (var i = 0; i < rdata.data.length; i++) { - var plugin = rdata.data[i]; - var len = plugin.versions.length; - var version_info = ''; - var version = ''; - var softPath = ''; - var titleClick = ''; - var state = ''; - var indexshow = ''; - var checked = ''; - - checked = plugin.display ? 'checked' : ''; - - if (typeof(plugin.versions) == "string" ){ - version_info += plugin.versions + '|'; - } else { - for (var j = 0; j < len; j++) { - version_info += plugin.versions[j] + '|'; - } - } - if (version_info != '') { - version_info = version_info.substring(0, version_info.length - 1); - } - - var handle = '安装'; - - if (plugin.setup == true) { - - var mupdate = '';//(plugin.versions[n] == plugin.updates[n]) '' : '更新 | '; - // if (plugin.versions[n] == '') mupdate = ''; - handle = mupdate + '设置 | 卸载'; - titleClick = 'onclick="softMain(\'' + plugin.name + '\',\'' + plugin.title + '\',\'' + plugin.setup_version + '\')" style="cursor:pointer"'; - - softPath = ''; - if (plugin.coexist){ - indexshow = '
                \ - \ - \ -
                '; - } else { - indexshow = '
                \ - \ - \ -
                '; - } - - if (plugin.status == true) { - state = '' - } else { - state = '' - } - } - - if (plugin.task == '-2') { - handle = '正在卸载...'; - } else if (plugin.task == '-1') { - handle = '正在安装...'; - } else if (plugin.task == '0') { - handle = '等待中...'; - } - - var plugin_title = plugin.title; - if (plugin.setup && !plugin.coexist){ - plugin_title = plugin.title + ' ' + plugin.setup_version; - } - - icon_link = "/plugins/file?name="+plugin.name+"&f=ico.png"; - if (plugin.icon != ''){ - icon_link = "/plugins/file?name="+plugin.name+"&f="+plugin.icon; - } - - sBody += '' + - ''+ - '' + plugin_title + '' + - '' + plugin.ps + '' + - '' + softPath + '' + - '' + state + '' + - '' + indexshow + '' + - '' + handle + '' + - ''; - } - - sBody += pBody; - - - $("#softList").html(sBody); - $(".menu-sub span").click(function() { - setCookie('soft_type', $(this).attr('typeid')); - $(this).addClass("on").siblings().removeClass("on"); - getSList(); - }); - - loadImage(); - },'json'); -} - -function installPreInspection(name, ver, callback){ - var loading = layer.msg('正在检查安装环境...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post("/plugins/run", {'name':name,'version':ver,'func':'install_pre_inspection'}, function(rdata) { - layer.close(loading); - if (rdata.status){ - if (rdata.data == 'ok'){ - callback(); - } else { - layer.msg(rdata.data, { icon: 2 }); - } - } else { - layer.msg(rdata.data, { icon: rdata.status ? 1 : 2 }); - } - },'json'); -} - -function runInstall(data){ - var loadT = layer.msg('正在添加到安装器...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post("/plugins/install", data, function(rdata) { - layer.closeAll(); - layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); - getSList(); - },'json'); -} - -function addVersion(name, ver, type, obj, title, install_pre_inspection) { - var option = ''; - var titlename = title.replace("-"+ver,""); - if (ver.indexOf('|') >= 0){ - var veropt = ver.split("|"); - var selectVersion = ''; - for (var i = 0; i < veropt.length; i++) { - selectVersion += ''; - } - option = ""; - } else { - option = '【' + titlename + '】 ' + ver + ''; - } - - layer.open({ - type: 1, - title: "【"+titlename + "】软件安装", - area: '350px', - closeBtn: 1, - shadeClose: true, - btn: ['提交','关闭'], - content: "
                \ -
                安装版本:" + option + "
                \ -
                ", - success:function(){ - $('.fangshi input').click(function() { - $(this).attr('checked', 'checked').parent().siblings().find("input").removeAttr('checked'); - }); - installTips(); - }, - yes:function(index,layero){ - var info = $("#selectVersion").val().toLowerCase(); - if (info == ''){ - info = $("#selectVersion").attr('val').toLowerCase(); - } - var info_split = info.split(' '); - var name = info_split[0]; - var version = info_split[1]; - - var type = $('.fangshi').prop("checked") ? '1' : '0'; - var request_args = "name=" + name + "&version=" + version + "&type=" + type; - - if (install_pre_inspection){ - //安装检查 - installPreInspection(name, version, function(){ - runInstall(request_args); - flySlow('layui-layer-btn0'); - }); - return; - } - - runInstall(request_args); - flySlow('layui-layer-btn0'); - } - }); -} - -//卸载软件 -function uninstallPreInspection(name, title, ver, callback){ - var loading = layer.msg('正在检查卸载环境...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post("/plugins/run", {'name':name,'version':ver,'func':'uninstall_pre_inspection'}, function(rdata) { - layer.close(loading); - if (rdata.status){ - if (rdata.data == 'ok'){ - if (typeof(callback) == 'function'){ - callback(); - } - } else { - layer.msg(rdata.data, { icon: 2 , time: 6666}); - } - } else { - layer.msg(rdata.data, { icon: rdata.status ? 1 : 2, time: 6666 }); - } - },'json'); -} - - -function runUninstallVersion(name, title, version){ - var title = title.replace("-"+version,""); - layer.confirm(msgTpl('您真的要卸载【{1}-{2}】吗?', [title, version]), { icon: 3, closeBtn: 1 }, function() { - var data = 'name=' + name + '&version=' + version; - var loadT = layer.msg('正在处理,请稍候...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post('/plugins/uninstall', data, function(rdata) { - layer.close(loadT) - getSList(); - layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); - },'json'); - }); -} - - -function uninstallVersion(name, title, version, uninstall_pre_inspection) { - if (uninstall_pre_inspection) { - uninstallPreInspection(name,title,version,function(){ - runUninstallVersion(name,title,version); - }); - } else { - runUninstallVersion(name,title,version); - } -} - - -//首页显示 -function toIndexDisplay(name, version, coexist) { - - var status = $("#index_" + name).prop("checked") ? "0" : "1"; - if (coexist == "true") { - var verinfo = version.replace(/\./, ""); - status = $("#index_" + name + verinfo).prop("checked") ? "0" : "1"; - } - - var data = "name=" + name + "&status=" + status + "&version=" + version; - $.post("/plugins/set_index", data, function(rdata) { - if (rdata.status) { - layer.msg(rdata.msg, { icon: 1 }) - } else { - layer.msg(rdata.msg, { icon: 2 }) - } - },'json'); -} - -function indexListHtml(callback){ - - - // init - $("#indexsoft").html(''); - var index_soft = ''; - for (var i = 0; i < 12; i++) { - index_soft += '
                '; - } - $("#indexsoft").html(index_soft); - - // var loadT = layer.msg('正在获取列表...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.get('/plugins/index_list', function(rdata) { - // layer.close(loadT); - $("#indexsoft").html(''); - var con = ''; - for (var i = 0; i < rdata.length; i++) { - var plugin = rdata[i]; - var len = plugin.versions.length; - var version_info = ''; - - if (typeof plugin.versions == "string"){ - version_info += plugin.versions + '|'; - } else { - for (var j = 0; j < len; j++) { - version_info += plugin.versions[j] + '|'; - } - } - if (version_info != '') { - version_info = version_info.substring(0, version_info.length - 1); - } - - if (plugin.status == true) { - state = '' - } else { - state = '' - } - - var name = plugin.title + ' ' + plugin.setup_version + ' '; - var data_id = plugin.name + '-' + plugin.setup_version; - if (plugin.coexist){ - name = plugin.title + ' '; - data_id = plugin.name + '-' + plugin.versions; - } - - con += '
                \ - \ -
                \ -
                \ -
                ' + name + state + '
                \ -
                \ -
                '; - - // loadImage(); - } - - $("#indexsoft").html(con); - //软件位置移动 - var softboxlen = $("#indexsoft > div").length; - var softboxsum = 12; - var softboxcon = ''; - var softboxn = softboxlen; - if (softboxlen <= softboxsum) { - for (var i = 0; i < softboxsum - softboxlen; i++) { - softboxcon += '
                '; - } - $("#indexsoft").append(softboxcon); - } - - if (typeof callback=='function'){ - callback(); - } - },'json'); -} - - -//首页软件列表 -function indexSoft() { - indexListHtml(function(){ - $("#indexsoft").dragsort({ - dragSelector: ".spanmove", - dragBetween: true, - dragEnd: saveOrder, - placeHolderTemplate: "
                " - }); - }); - - function saveOrder() { - var data = $("#indexsoft > div").map(function() { return $(this).attr("data-id"); }).get(); - tmp = []; - for(i in data){ - // console.log(data[i]); - if (data[i] != ''){ - tmp.push($.trim(data[i])); - } - } - var ssort = tmp.join("|"); - $("input[name=list1SortOrder]").val(ssort); - $.post("/plugins/index_sort", 'ssort=' + ssort, function(rdata) { - if (!rdata.status){ - showMsg('设置失败:'+ rdata.msg, function(){ - indexListHtml(); - }, { icon: 16, time: 0, shade: [0.3, '#000'] }); - } - },'json'); - }; -} - - -function importPluginOpen(){ - $("#update_zip").on("change", function () { - var files = $("#update_zip")[0].files; - if (files.length == 0) { - return; - } - importPlugin(files[0]); - $("#update_zip").val('') - }); - $("#update_zip").click(); -} - - -function importPlugin(file){ - var formData = new FormData(); - formData.append("plugin_zip", file); - $.ajax({ - url: "/plugins/update_zip", - type: "POST", - data: formData, - processData: false, - dataType:'json', - contentType: false, - success: function (data) { - if (data.status === false) { - layer.msg(data.msg, { icon: 2 }); - return; - } - var loadT = layer.open({ - type: 1, - area: "500px", - title: "安装第三方插件包", - closeBtn: 1, - shift: 5, - shadeClose: false, - content: '\ -
                \ - \ -
                  \ -
                • 此为第三方开发的插件,无法验证其可靠性!
                • \ -
                • 安装过程可能需要几分钟时间,请耐心等候!
                • \ -
                • 如果已存在此插件,将被替换!
                • \ -
                \ -
                \ -
                ' - }); - - },error: function (responseStr) { - layer.msg('上传失败2!:' + responseStr, { icon: 2 }); - } - }); -} - - -function importPluginInstall(plugin_name, tmp_path) { - layer.msg('正在安装,这可能需要几分钟时间...', { icon: 16, time: 0, shade: [0.3, '#000'] }); - $.post('/plugins/input_zip', { plugin_name: plugin_name, tmp_path: tmp_path }, function (rdata) { - layer.closeAll() - if (rdata.status) { - getSList(true); - } - setTimeout(function () { layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }) }, 1000); - },'json'); -} - -$(function() { - if (window.document.location.pathname == '/soft/') { - setInterval(function() { getSList(); }, 8000); - } -}); \ No newline at end of file diff --git a/route/static/app/upload.js b/route/static/app/upload.js deleted file mode 100755 index cdeaddd4b..000000000 --- a/route/static/app/upload.js +++ /dev/null @@ -1,238 +0,0 @@ -function MyAjax() { - this.serverdata = this.erromsg = this.timeout = this.stop = this.xmlhttp = !1, this.transit = !0 -} -MyAjax.prototype.handle = function(d, f) { - if(4 == d.readyState) { - if(this.stop === !0) { - return - } - if(this.transit = !0, f.timeout && f.async && (clearTimeout(this.timeout), this.timeout = !1), 200 == d.status) { - var e = this.serverdata = d.responseText.replace(/(^\s*)|(\s*$)/g, ""); - "function" == typeof f.success && f.success(e) - } else { - this.erromsg = d.status, "function" == typeof f.error && f.error(d.status) - } - } else { - if(0 == d.readyState) { - if(this.stop === !0) { - return - } - f.timeout && f.async && (clearTimeout(this.timeout), this.timeout = !1), this.erromsg = d.readyState, this.transit = !0, "function" == typeof f.error && f.error(d.readyState) - } - } -}, MyAjax.prototype.out = function(b) { - this.transit = !0, this.erromsg = 504, this.stop = !0, "function" == typeof b.error && b.error(504) -}, MyAjax.prototype.carry = function(j) { - var i, h, g, l; - if(j.lock && !this.transit) { - return !1 - } - this.transit = !1, this.stop = this.erromsg = !1, i = window.XMLHttpRequest ? new XMLHttpRequest : new ActiveXObject("Microsoft.XMLHTTP"), j.type = j.type.toUpperCase(), h = function() {}, "string" == typeof j.data ? (j.data = j.data.replace(/(^\s*)|(\s*$)/g, ""), h = function() { - i.setRequestHeader("Content-Type", "application/x-www-form-urlencoded") - }) : "[object FormData]" !== Object.prototype.toString.call(j.data) ? (j.data = "", h = function() { - i.setRequestHeader("Content-Type", "application/x-www-form-urlencoded") - }) : ("function" == typeof j.progress && i.upload.addEventListener("progress", j.progress, !1), j.type = "POST"), g = "" == j.data ? [null, ""] : [j.data, "?" + j.data], l = this, "function" == typeof j.complete && j.complete(), j.timeout && j.async && (this.timeout = setTimeout(function() { - l.out(j) - }, j.timeout)), j.async === !0 && (i.onreadystatechange = function() { - l.handle(i, j) - }); - try { - switch(j.type) { - case "POST": - i.open("POST", j.url, j.async), h(); - break; - default: - i.open("GET", j.url + g[1], j.async), j.cache === !0 || i.setRequestHeader("If-Modified-Since", "0") - } - } catch(k) { - return this.erromsg = 505, j.timeout && j.async && (clearTimeout(this.timeout), this.timeout = !1), this.transit = !0, "function" == typeof j.error && j.error(505), void 0 - } - i.send(g[0]), j.async === !1 && l.handle(i, j) -}; - -function uploadStart(d) { - var a = function(e) { - this.uptype = e.UpType; - this.url = e.url; - this.oldurl = e.url; - this.filessize = e.FilesSize, this.MaxUpNum = e.MaxUpNum, this.str = new MyAjax(); - this.file_input = document.getElementById("file_input"); - this.opt = document.getElementById("opt"); - this.up = document.getElementById("up"); - this.up_box = document.getElementById("up_box"); - this.filesalllength = this.FilesArrayLength = this.up_box_li = FileProgress = 0; - this.FilesArray = new Array(); - this.num = 0 - }; - a.prototype = { - SelectFile: function() { - if(this.FilesArrayLength === 0) { - this.up_box.innerHTML = ""; - this.un() - } - var h = this.file_input.files, - e, g, f = h.length; - if(this.filesalllength + f > this.MaxUpNum) { - f = this.MaxUpNum - this.filesalllength; - layer.msg(lan.get('update_num',[this.MaxUpNum]), { - icon: 5 - }) - } - for(var j = 0; j < f; j++) { - e = h[j]; - g = e.name.split("."); - g = Object.prototype.toString.call(g) === "[object Array]" ? g[g.length - 1] : ""; - if(d) { - if(g != "sql" && g != "zip" && g != "gz" && g != "tgz") { - layer.msg(lan.upload.file_type_err, { - icon: 5 - }); - return - } - } - if(!e) { - this.up_box.insertAdjacentHTML("beforeEnd", "
              • " + e.name + ""+lan.upload.file_err+"
              • ") - } else { - if(this.uptype.length > 0 && this.uptype.indexOf(g.toLowerCase()) === -1) { - this.up_box.insertAdjacentHTML("beforeEnd", "
              • " + e.name + ""+lan.upload.file_type_err+"
              • ") - } else { - if(e.size <= 0) { - this.up_box.insertAdjacentHTML("beforeEnd", "
              • " + e.name + ""+lan.upload.file_err_empty+"
              • ") - } else { - this.up_box.insertAdjacentHTML("beforeEnd", "
              • " + e.name + "" + (toSize(e.size)) + ""+lan.upload.up_sleep+"
              • "); - this.FilesArray.push([e, (this.filesalllength - 1 < 0 ? 0 : this.filesalllength) + j]) - } - } - } - } - this.filesalllength += f; - this.FilesArrayLength = this.FilesArray.length - }, - read: function() { - if(this.filesalllength == 0) { - layer.msg(lan.upload.select_file, { - icon: 5 - }); - return - } - this.url = this.oldurl + "&codeing=" + document.getElementById("fileCodeing").value; - if(this.FilesArrayLength > 0) { - this.opt.disabled = true; - this.up.disabled = true; - this.file_input.disabled = true; - this.up_box_li = this.up_box.getElementsByTagName("li"); - this.ready(this.FilesArray, 0, this.FilesArrayLength - 1) - } else { - layer.msg(an.upload.select_empty, { - icon: 5 - }) - } - }, - un: function() { - this.opt.disabled = this.up.disabled = this.file_input.disabled; - this.filesalllength = this.FilesArrayLength = this.up_box_li = 0; - this.FilesArray = new Array() - }, - ready: function(i, f, h) { - if(f > h) { - this.un(); - return - } - try { - var g = new FormData(); - g.append("zunfile", i[f][0]) - } catch(e) { - this.opt.disabled = true; - this.up.disabled = true; - this.file_input.disabled = true; - layer.msg(lan.upload.ie_err, { - icon: 5 - }) - } - if(i.length > 1) { - $("#totalProgress").html("

                "+lan.upload.up_the+ this.num + "/" + i.length + "

                "); - $(".cancel").css("visibility", "hidden") - } - this.send(g, i, f, h) - }, - SetTxt: function(f, g, h) { - var e = this.up_box_li[f].getElementsByTagName("em")[0]; - e.style.color = h; - e.innerHTML = g - }, - send: function(f, i, e, g) { - if(!this.up_box_li[e].getElementsByTagName("em")[0]) { - this.ready(i, e + 1, g); - this.num++; - return - } - var h = this; - this.FileProgress = 0; - this.str.carry({ - url: this.url, - data: f, - type: "get", - timeout: 86400000, - async: true, - lock: true, - complete: false, - progress: function(j) { - h.FileProgress = Math.floor(j.loaded / j.total * 100) + "%"; - if(h.FileProgress == "100%") { - h.FileProgress = '正在保存..'; - } - h.SetTxt(i[e][1], '上传进度:' + h.FileProgress, "#005100") - }, - success: function(j) { - h.str.serverdata = false; - h.SetTxt(i[e][1], '已上传成功', "#005100"); - h.ready(i, e + 1, g); - h.num++; - if(i.length > 1) { - var k = (h.num == i.length) ? '上传完成': '已上传'; - $("#totalProgress").html("

                " + k + h.num + "/" + i.length + "

                ") - } - if(h.num == i.length) { - c.opt.disabled = false; - c.up.disabled = false; - c.file_input.disabled = false; - h.num = 0 - } - if(!d) { - getFiles(getCookie("open_dir_path")); - } else{ - d(); - } - }, - error: function(j) { - h.SetTxt(i[e][1], lan.upload.up_err, "red"); - h.str.serverdata = false; - h.ready(i, e + 1, g) - }, - cache: false - }) - } - }; - try { - var c = new a({ - UpType: new Array(), - FilesSize: 5242880000, - MaxUpNum: 100, - url: "/files/upload_file?path=" + encodeURIComponent(document.getElementById("input-val").value) - }); - c.opt.addEventListener("click", function() { - c.file_input.click() - }, false); - c.up.addEventListener("click", function() { - c.read(); - }, false); - c.file_input.addEventListener("change", function() { - c.SelectFile(); - }, false) - } catch(b) { - c.opt.disabled = true; - c.up.disabled = true; - c.file_input.disabled = true; - layer.msg('抱歉,IE 6/7/8 不支持请更换浏览器再上传', {icon: 5}); - } -}; \ No newline at end of file diff --git a/route/static/bootstrap-3.3.5/css/bootstrap-theme.min.css b/route/static/bootstrap-3.3.5/css/bootstrap-theme.min.css deleted file mode 100755 index 61358b13d..000000000 --- a/route/static/bootstrap-3.3.5/css/bootstrap-theme.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-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-image:-o-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-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)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} \ No newline at end of file diff --git a/route/static/bootstrap-3.3.5/css/bootstrap.min.css b/route/static/bootstrap-3.3.5/css/bootstrap.min.css deleted file mode 100755 index 0336abcd4..000000000 --- a/route/static/bootstrap-3.3.5/css/bootstrap.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before,.glyphicon-True:before{content:"\e072"}.glyphicon-pause:before,.glyphicon-False:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:none}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-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-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-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-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-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{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-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{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-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{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:none}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:none}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#555;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#fff;background-color:#10952a;border-color:#398439}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#fff;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#20a53a;border-color:#20a53a}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#10952a;border-color:#255625}.btn-success:hover{color:#fff;background-color:#10952a;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#10952a;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#20a53a;border-color:#20a53a}.btn-success .badge{color:#20a53a;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#20a53a}.label-success[href]:focus,.label-success[href]:hover{background-color:#10952a}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-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-image:-o-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-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);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#20a53a}.progress-striped .progress-bar-success{background-image:-webkit-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-image:-o-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-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)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-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-image:-o-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-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)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-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-image:-o-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-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)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-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-image:-o-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-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)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file diff --git a/route/static/bootstrap-3.3.5/css/index.html b/route/static/bootstrap-3.3.5/css/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/bootstrap-3.3.5/css/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.eot b/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.eot deleted file mode 100755 index b93a4953f..000000000 Binary files a/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.eot and /dev/null differ diff --git a/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.svg b/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.svg deleted file mode 100755 index f9635d9c1..000000000 --- a/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf b/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf deleted file mode 100755 index 1413fc609..000000000 Binary files a/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf and /dev/null differ diff --git a/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff b/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff deleted file mode 100755 index 9e612858f..000000000 Binary files a/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff and /dev/null differ diff --git a/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff2 b/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff2 deleted file mode 100755 index 64539b54c..000000000 Binary files a/route/static/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff2 and /dev/null differ diff --git a/route/static/bootstrap-3.3.5/fonts/index.html b/route/static/bootstrap-3.3.5/fonts/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/bootstrap-3.3.5/fonts/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/bootstrap-3.3.5/index.html b/route/static/bootstrap-3.3.5/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/bootstrap-3.3.5/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/build/addons/attach/attach.js b/route/static/build/addons/attach/attach.js deleted file mode 100755 index 847cb62e8..000000000 --- a/route/static/build/addons/attach/attach.js +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright (c) 2014 The xterm.js authors. All rights reserved. - * @license MIT - * - * Implements the attach method, that attaches the terminal to a WebSocket stream. - */ - -(function (attach) { - if (typeof exports === 'object' && typeof module === 'object') { - /* - * CommonJS environment - */ - module.exports = attach(require('../../Terminal').Terminal); - } else if (typeof define == 'function') { - /* - * Require.js is available - */ - define(['../../xterm'], attach); - } else { - /* - * Plain browser environment - */ - attach(window.Terminal); - } -})(function (Terminal) { - 'use strict'; - - var exports = {}; - - /** - * Attaches the given terminal to the given socket. - * - * @param {Terminal} term - The terminal to be attached to the given socket. - * @param {WebSocket} socket - The socket to attach the current terminal. - * @param {boolean} bidirectional - Whether the terminal should send data - * to the socket as well. - * @param {boolean} buffered - Whether the rendering of incoming data - * should happen instantly or at a maximum - * frequency of 1 rendering per 10ms. - */ - exports.attach = function (term, socket, bidirectional, buffered) { - bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional; - term.socket = socket; - - term._flushBuffer = function () { - term.write(term._attachSocketBuffer); - term._attachSocketBuffer = null; - }; - - term._pushToBuffer = function (data) { - if (term._attachSocketBuffer) { - term._attachSocketBuffer += data; - } else { - term._attachSocketBuffer = data; - setTimeout(term._flushBuffer, 10); - } - }; - - var myTextDecoder; - - term._getMessage = function (ev) { - var str; - if (typeof ev.data === "object") { - if (ev.data instanceof ArrayBuffer) { - if (!myTextDecoder) { - myTextDecoder = new TextDecoder(); - } - - str = myTextDecoder.decode( ev.data ); - } - else { - throw "TODO: handle Blob?"; - } - } - - if (buffered) { - term._pushToBuffer(str || ev.data); - } else { - term.write(str || ev.data); - } - }; - - term._sendData = function (data) { - socket.send(data); - }; - - socket.addEventListener('message', term._getMessage); - - if (bidirectional) { - term.on('data', term._sendData); - } - - socket.addEventListener('close', term.detach.bind(term, socket)); - socket.addEventListener('error', term.detach.bind(term, socket)); - }; - - /** - * Detaches the given terminal from the given socket - * - * @param {Terminal} term - The terminal to be detached from the given socket. - * @param {WebSocket} socket - The socket from which to detach the current - * terminal. - */ - exports.detach = function (term, socket) { - term.off('data', term._sendData); - - socket = (typeof socket == 'undefined') ? term.socket : socket; - - if (socket) { - socket.removeEventListener('message', term._getMessage); - } - - delete term.socket; - }; - - /** - * Attaches the current terminal to the given socket - * - * @param {WebSocket} socket - The socket to attach the current terminal. - * @param {boolean} bidirectional - Whether the terminal should send data - * to the socket as well. - * @param {boolean} buffered - Whether the rendering of incoming data - * should happen instantly or at a maximum - * frequency of 1 rendering per 10ms. - */ - Terminal.prototype.attach = function (socket, bidirectional, buffered) { - return exports.attach(this, socket, bidirectional, buffered); - }; - - /** - * Detaches the current terminal from the given socket. - * - * @param {WebSocket} socket - The socket from which to detach the current - * terminal. - */ - Terminal.prototype.detach = function (socket) { - return exports.detach(this, socket); - }; - - return exports; -}); diff --git a/route/static/build/addons/attach/index.html b/route/static/build/addons/attach/index.html deleted file mode 100755 index 4a41fbde2..000000000 --- a/route/static/build/addons/attach/index.html +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - -
                - -

                - xterm.js: socket attach -

                -

                - Attach the terminal to a WebSocket terminal stream with ease. Perfect for attaching to your - Docker containers. -

                -

                - Socket information -

                -
                - - -
                -
                - -
                - - - \ No newline at end of file diff --git a/route/static/build/addons/attach/package.json b/route/static/build/addons/attach/package.json deleted file mode 100755 index e8f6b2ca2..000000000 --- a/route/static/build/addons/attach/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "xterm.attach", - "main": "attach.js", - "private": true -} diff --git a/route/static/build/addons/fit/fit.js b/route/static/build/addons/fit/fit.js deleted file mode 100755 index 01e0c2f21..000000000 --- a/route/static/build/addons/fit/fit.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2014 The xterm.js authors. All rights reserved. - * @license MIT - * - * Fit terminal columns and rows to the dimensions of its DOM element. - * - * ## Approach - * - * Rows: Truncate the division of the terminal parent element height by the - * terminal row height. - * Columns: Truncate the division of the terminal parent element width by the - * terminal character width (apply display: inline at the terminal - * row and truncate its width with the current number of columns). - */ - -(function (fit) { - if (typeof exports === 'object' && typeof module === 'object') { - /* - * CommonJS environment - */ - module.exports = fit(require('../../Terminal').Terminal); - } else if (typeof define == 'function') { - /* - * Require.js is available - */ - define(['../../xterm'], fit); - } else { - /* - * Plain browser environment - */ - fit(window.Terminal); - } -})(function (Terminal) { - var exports = {}; - - exports.proposeGeometry = function (term) { - if (!term.element.parentElement) { - return null; - } - var parentElementStyle = window.getComputedStyle(term.element.parentElement); - var parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')); - var parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')) - 17); - var elementStyle = window.getComputedStyle(term.element); - var elementPaddingVer = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')); - var elementPaddingHor = parseInt(elementStyle.getPropertyValue('padding-right')) + parseInt(elementStyle.getPropertyValue('padding-left')); - var availableHeight = parentElementHeight - elementPaddingVer; - var availableWidth = parentElementWidth - elementPaddingHor; - var geometry = { - cols: Math.floor(availableWidth / term.charMeasure.width), - rows: Math.floor(availableHeight / Math.floor(term.charMeasure.height * term.getOption('lineHeight'))) - }; - - return geometry; - }; - - exports.fit = function (term) { - // Wrap fit in a setTimeout as charMeasure needs time to get initialized - // after calling Terminal.open - setTimeout(function () { - var geometry = exports.proposeGeometry(term); - - if (geometry) { - // Force a full render - if (term.rows !== geometry.rows || term.cols !== geometry.cols) { - term.renderer.clear(); - term.resize(geometry.cols, geometry.rows); - } - } - }, 0); - }; - - Terminal.prototype.proposeGeometry = function () { - return exports.proposeGeometry(this); - }; - - Terminal.prototype.fit = function () { - return exports.fit(this); - }; - - return exports; -}); diff --git a/route/static/build/addons/fit/package.json b/route/static/build/addons/fit/package.json deleted file mode 100755 index aec1bbd6d..000000000 --- a/route/static/build/addons/fit/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "xterm.fit", - "main": "fit.js", - "private": true -} diff --git a/route/static/build/addons/fullscreen/fullscreen.css b/route/static/build/addons/fullscreen/fullscreen.css deleted file mode 100755 index 37229932f..000000000 --- a/route/static/build/addons/fullscreen/fullscreen.css +++ /dev/null @@ -1,10 +0,0 @@ -.xterm.fullscreen { - position: fixed; - top: 0; - bottom: 0; - left: 0; - right: 0; - width: auto; - height: auto; - z-index: 255; -} diff --git a/route/static/build/addons/fullscreen/fullscreen.js b/route/static/build/addons/fullscreen/fullscreen.js deleted file mode 100755 index f1dc56a26..000000000 --- a/route/static/build/addons/fullscreen/fullscreen.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) 2014 The xterm.js authors. All rights reserved. - * @license MIT - */ - -(function (fullscreen) { - if (typeof exports === 'object' && typeof module === 'object') { - /* - * CommonJS environment - */ - module.exports = fullscreen(require('../../Terminal').Terminal); - } else if (typeof define == 'function') { - /* - * Require.js is available - */ - define(['../../xterm'], fullscreen); - } else { - /* - * Plain browser environment - */ - fullscreen(window.Terminal); - } -})(function (Terminal) { - var exports = {}; - - /** - * Toggle the given terminal's fullscreen mode. - * @param {Terminal} term - The terminal to toggle full screen mode - * @param {boolean} fullscreen - Toggle fullscreen on (true) or off (false) - */ - exports.toggleFullScreen = function (term, fullscreen) { - var fn; - - if (typeof fullscreen == 'undefined') { - fn = (term.element.classList.contains('fullscreen')) ? 'remove' : 'add'; - } else if (!fullscreen) { - fn = 'remove'; - } else { - fn = 'add'; - } - - term.element.classList[fn]('fullscreen'); - }; - - Terminal.prototype.toggleFullscreen = function (fullscreen) { - exports.toggleFullScreen(this, fullscreen); - }; - - return exports; -}); diff --git a/route/static/build/addons/fullscreen/package.json b/route/static/build/addons/fullscreen/package.json deleted file mode 100755 index 9c359a052..000000000 --- a/route/static/build/addons/fullscreen/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "xterm.fullscreen", - "main": "fullscreen.js", - "private": true -} diff --git a/route/static/build/addons/search/search.js b/route/static/build/addons/search/search.js deleted file mode 100755 index 088e28347..000000000 --- a/route/static/build/addons/search/search.js +++ /dev/null @@ -1,116 +0,0 @@ -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0; y--) { - result = this._findInLine(term, y); - if (result) { - break; - } - } - if (!result) { - for (var y = this._terminal.buffer.ybase + this._terminal.rows - 1; y > startRow; y--) { - result = this._findInLine(term, y); - if (result) { - break; - } - } - } - return this._selectResult(result); - }; - SearchHelper.prototype._findInLine = function (term, y) { - var lowerStringLine = this._terminal.buffer.translateBufferLineToString(y, true).toLowerCase(); - var lowerTerm = term.toLowerCase(); - var searchIndex = lowerStringLine.indexOf(lowerTerm); - if (searchIndex >= 0) { - return { - term: term, - col: searchIndex, - row: y - }; - } - }; - SearchHelper.prototype._selectResult = function (result) { - if (!result) { - return false; - } - this._terminal.selectionManager.setSelection(result.col, result.row, result.term.length); - this._terminal.scrollLines(result.row - this._terminal.buffer.ydisp, false); - return true; - }; - return SearchHelper; -}()); -exports.SearchHelper = SearchHelper; - - - -},{}],2:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var SearchHelper_1 = require("./SearchHelper"); -(function (addon) { - if (typeof window !== 'undefined' && 'Terminal' in window) { - addon(window.Terminal); - } - else if (typeof exports === 'object' && typeof module === 'object') { - module.exports = addon(require('../../Terminal').Terminal); - } - else if (typeof define === 'function') { - define(['../../xterm'], addon); - } -})(function (Terminal) { - Terminal.prototype.findNext = function (term) { - if (!this._searchHelper) { - this.searchHelper = new SearchHelper_1.SearchHelper(this); - } - return this.searchHelper.findNext(term); - }; - Terminal.prototype.findPrevious = function (term) { - if (!this._searchHelper) { - this.searchHelper = new SearchHelper_1.SearchHelper(this); - } - return this.searchHelper.findPrevious(term); - }; -}); - - - -},{"../../Terminal":undefined,"./SearchHelper":1}]},{},[2]) -//# sourceMappingURL=search.js.map diff --git a/route/static/build/addons/search/search.js.map b/route/static/build/addons/search/search.js.map deleted file mode 100755 index baa06e886..000000000 --- a/route/static/build/addons/search/search.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"search.js","sources":["../../../src/addons/search/search.ts","../../../src/addons/search/SearchHelper.ts","../../../node_modules/browser-pack/_prelude.js"],"sourcesContent":["/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { SearchHelper } from './SearchHelper';\n\ndeclare var exports: any;\ndeclare var module: any;\ndeclare var define: any;\ndeclare var require: any;\ndeclare var window: any;\n\n(function (addon) {\n if (typeof window !== 'undefined' && 'Terminal' in window) {\n /**\n * Plain browser environment\n */\n addon(window.Terminal);\n } else if (typeof exports === 'object' && typeof module === 'object') {\n /**\n * CommonJS environment\n */\n module.exports = addon(require('../../Terminal').Terminal);\n } else if (typeof define === 'function') {\n /**\n * Require.js is available\n */\n define(['../../xterm'], addon);\n }\n})((Terminal: any) => {\n /**\n * Find the next instance of the term, then scroll to and select it. If it\n * doesn't exist, do nothing.\n * @param term Tne search term.\n * @return Whether a result was found.\n */\n Terminal.prototype.findNext = function(term: string): boolean {\n if (!this._searchHelper) {\n this.searchHelper = new SearchHelper(this);\n }\n return (this.searchHelper).findNext(term);\n };\n\n /**\n * Find the previous instance of the term, then scroll to and select it. If it\n * doesn't exist, do nothing.\n * @param term Tne search term.\n * @return Whether a result was found.\n */\n Terminal.prototype.findPrevious = function(term: string): boolean {\n if (!this._searchHelper) {\n this.searchHelper = new SearchHelper(this);\n }\n return (this.searchHelper).findPrevious(term);\n };\n});\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\n// import { ITerminal } from '../../Interfaces';\n// import { translateBufferLineToString } from '../../utils/BufferLine';\n\ninterface ISearchResult {\n term: string;\n col: number;\n row: number;\n}\n\n/**\n * A class that knows how to search the terminal and how to display the results.\n */\nexport class SearchHelper {\n constructor(private _terminal: any) {\n // TODO: Search for multiple instances on 1 line\n // TODO: Don't use the actual selection, instead use a \"find selection\" so multiple instances can be highlighted\n // TODO: Highlight other instances in the viewport\n // TODO: Support regex, case sensitivity, etc.\n }\n\n /**\n * Find the next instance of the term, then scroll to and select it. If it\n * doesn't exist, do nothing.\n * @param term Tne search term.\n * @return Whether a result was found.\n */\n public findNext(term: string): boolean {\n if (!term || term.length === 0) {\n return false;\n }\n\n let result: ISearchResult;\n\n let startRow = this._terminal.buffer.ydisp;\n if (this._terminal.selectionManager.selectionEnd) {\n // Start from the selection end if there is a selection\n startRow = this._terminal.selectionManager.selectionEnd[1];\n }\n\n // Search from ydisp + 1 to end\n for (let y = startRow + 1; y < this._terminal.buffer.ybase + this._terminal.rows; y++) {\n result = this._findInLine(term, y);\n if (result) {\n break;\n }\n }\n\n // Search from the top to the current ydisp\n if (!result) {\n for (let y = 0; y < startRow; y++) {\n result = this._findInLine(term, y);\n if (result) {\n break;\n }\n }\n }\n\n // Set selection and scroll if a result was found\n return this._selectResult(result);\n }\n\n /**\n * Find the previous instance of the term, then scroll to and select it. If it\n * doesn't exist, do nothing.\n * @param term Tne search term.\n * @return Whether a result was found.\n */\n public findPrevious(term: string): boolean {\n if (!term || term.length === 0) {\n return false;\n }\n\n let result: ISearchResult;\n\n let startRow = this._terminal.buffer.ydisp;\n if (this._terminal.selectionManager.selectionStart) {\n // Start from the selection end if there is a selection\n startRow = this._terminal.selectionManager.selectionStart[1];\n }\n\n // Search from ydisp + 1 to end\n for (let y = startRow - 1; y >= 0; y--) {\n result = this._findInLine(term, y);\n if (result) {\n break;\n }\n }\n\n // Search from the top to the current ydisp\n if (!result) {\n for (let y = this._terminal.buffer.ybase + this._terminal.rows - 1; y > startRow; y--) {\n result = this._findInLine(term, y);\n if (result) {\n break;\n }\n }\n }\n\n // Set selection and scroll if a result was found\n return this._selectResult(result);\n }\n\n /**\n * Searches a line for a search term.\n * @param term Tne search term.\n * @param y The line to search.\n * @return The search result if it was found.\n */\n private _findInLine(term: string, y: number): ISearchResult {\n const lowerStringLine = this._terminal.buffer.translateBufferLineToString(y, true).toLowerCase();\n const lowerTerm = term.toLowerCase();\n const searchIndex = lowerStringLine.indexOf(lowerTerm);\n if (searchIndex >= 0) {\n return {\n term,\n col: searchIndex,\n row: y\n };\n }\n }\n\n /**\n * Selects and scrolls to a result.\n * @param result The result to select.\n * @return Whethera result was selected.\n */\n private _selectResult(result: ISearchResult): boolean {\n if (!result) {\n return false;\n }\n this._terminal.selectionManager.setSelection(result.col, result.row, result.term.length);\n this._terminal.scrollLines(result.row - this._terminal.buffer.ydisp, false);\n return true;\n }\n}\n",null],"names":[],"mappings":"AEAA;;;ADiBA;AACA;AAAA;AAKA;AAQA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAQA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AA1Ha;;;;;;;ADZb;AAQA;AACA;AAIA;AACA;AAAA;AAIA;AACA;AAAA;AAIA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AACA;AACA;AACA;AACA;AACA;;;"} \ No newline at end of file diff --git a/route/static/build/addons/terminado/package.json b/route/static/build/addons/terminado/package.json deleted file mode 100755 index 9af1851bc..000000000 --- a/route/static/build/addons/terminado/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "xterm.terminado", - "main": "terminado.js", - "private": true -} diff --git a/route/static/build/addons/terminado/terminado.js b/route/static/build/addons/terminado/terminado.js deleted file mode 100755 index 702351b9c..000000000 --- a/route/static/build/addons/terminado/terminado.js +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Copyright (c) 2016 The xterm.js authors. All rights reserved. - * @license MIT - * - * This module provides methods for attaching a terminal to a terminado - * WebSocket stream. - */ - -(function (attach) { - if (typeof exports === 'object' && typeof module === 'object') { - /* - * CommonJS environment - */ - module.exports = attach(require('../../Terminal').Terminal); - } else if (typeof define == 'function') { - /* - * Require.js is available - */ - define(['../../xterm'], attach); - } else { - /* - * Plain browser environment - */ - attach(window.Terminal); - } -})(function (Terminal) { - 'use strict'; - - var exports = {}; - - /** - * Attaches the given terminal to the given socket. - * - * @param {Terminal} term - The terminal to be attached to the given socket. - * @param {WebSocket} socket - The socket to attach the current terminal. - * @param {boolean} bidirectional - Whether the terminal should send data - * to the socket as well. - * @param {boolean} buffered - Whether the rendering of incoming data - * should happen instantly or at a maximum - * frequency of 1 rendering per 10ms. - */ - exports.terminadoAttach = function (term, socket, bidirectional, buffered) { - bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional; - term.socket = socket; - - term._flushBuffer = function () { - term.write(term._attachSocketBuffer); - term._attachSocketBuffer = null; - }; - - term._pushToBuffer = function (data) { - if (term._attachSocketBuffer) { - term._attachSocketBuffer += data; - } else { - term._attachSocketBuffer = data; - setTimeout(term._flushBuffer, 10); - } - }; - - term._getMessage = function (ev) { - var data = JSON.parse(ev.data) - if( data[0] == "stdout" ) { - if (buffered) { - term._pushToBuffer(data[1]); - } else { - term.write(data[1]); - } - } - }; - - term._sendData = function (data) { - socket.send(JSON.stringify(['stdin', data])); - }; - - term._setSize = function (size) { - socket.send(JSON.stringify(['set_size', size.rows, size.cols])); - }; - - socket.addEventListener('message', term._getMessage); - - if (bidirectional) { - term.on('data', term._sendData); - } - term.on('resize', term._setSize); - - socket.addEventListener('close', term.terminadoDetach.bind(term, socket)); - socket.addEventListener('error', term.terminadoDetach.bind(term, socket)); - }; - - /** - * Detaches the given terminal from the given socket - * - * @param {Xterm} term - The terminal to be detached from the given socket. - * @param {WebSocket} socket - The socket from which to detach the current - * terminal. - */ - exports.terminadoDetach = function (term, socket) { - term.off('data', term._sendData); - - socket = (typeof socket == 'undefined') ? term.socket : socket; - - if (socket) { - socket.removeEventListener('message', term._getMessage); - } - - delete term.socket; - }; - - /** - * Attaches the current terminal to the given socket - * - * @param {WebSocket} socket - The socket to attach the current terminal. - * @param {boolean} bidirectional - Whether the terminal should send data - * to the socket as well. - * @param {boolean} buffered - Whether the rendering of incoming data - * should happen instantly or at a maximum - * frequency of 1 rendering per 10ms. - */ - Terminal.prototype.terminadoAttach = function (socket, bidirectional, buffered) { - return exports.terminadoAttach(this, socket, bidirectional, buffered); - }; - - /** - * Detaches the current terminal from the given socket. - * - * @param {WebSocket} socket - The socket from which to detach the current - * terminal. - */ - Terminal.prototype.terminadoDetach = function (socket) { - return exports.terminadoDetach(this, socket); - }; - - return exports; -}); diff --git a/route/static/build/addons/winptyCompat/winptyCompat.js b/route/static/build/addons/winptyCompat/winptyCompat.js deleted file mode 100755 index 89c0d8795..000000000 --- a/route/static/build/addons/winptyCompat/winptyCompat.js +++ /dev/null @@ -1,29 +0,0 @@ -(function (addon) { - if (typeof window !== 'undefined' && 'Terminal' in window) { - addon(window.Terminal); - } - else if (typeof exports === 'object' && typeof module === 'object') { - module.exports = addon(require('../../Terminal').Terminal); - } - else if (typeof define === 'function') { - define(['../../xterm'], addon); - } -})(function (Terminal) { - Terminal.prototype.winptyCompatInit = function () { - var _this = this; - var isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0; - if (!isWindows) { - return; - } - this.on('lineFeed', function () { - var line = _this.buffer.lines.get(_this.buffer.ybase + _this.buffer.y - 1); - var lastChar = line[_this.cols - 1]; - if (lastChar[3] !== 32) { - var nextLine = _this.buffer.lines.get(_this.buffer.ybase + _this.buffer.y); - nextLine.isWrapped = true; - } - }); - }; -}); - -//# sourceMappingURL=winptyCompat.js.map diff --git a/route/static/build/addons/winptyCompat/winptyCompat.js.map b/route/static/build/addons/winptyCompat/winptyCompat.js.map deleted file mode 100755 index 826e69714..000000000 --- a/route/static/build/addons/winptyCompat/winptyCompat.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/addons/winptyCompat/winptyCompat.ts"],"names":[],"mappings":"AAQA,CAAC,UAAU,KAAK;IACd,EAAE,CAAC,CAAC,OAAO,MAAM,KAAK,WAAW,IAAI,UAAU,IAAI,MAAM,CAAC,CAAC,CAAC;QAI1D,KAAK,CAAO,MAAO,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC;QAIrE,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC7D,CAAC;IAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC;QAIxC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC,CAAC;IACjC,CAAC;AACH,CAAC,CAAC,CAAC,UAAC,QAAa;IACf,QAAQ,CAAC,SAAS,CAAC,gBAAgB,GAAG;QAAA,iBAyBrC;QAvBC,IAAM,SAAS,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC1F,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,CAAC;QACT,CAAC;QAYD,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE;YAClB,IAAM,IAAI,GAAG,KAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,KAAI,CAAC,MAAM,CAAC,KAAK,GAAG,KAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC1E,IAAM,QAAQ,GAAG,IAAI,CAAC,KAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;YACrC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,EAAY,CAAC,CAAC,CAAC;gBACjC,IAAM,QAAQ,GAAG,KAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,KAAI,CAAC,MAAM,CAAC,KAAK,GAAG,KAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBACpE,QAAS,CAAC,SAAS,GAAG,IAAI,CAAC;YACnC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","file":"winptyCompat.js","sourceRoot":"."} \ No newline at end of file diff --git a/route/static/build/addons/zmodem/demo/app.js b/route/static/build/addons/zmodem/demo/app.js deleted file mode 100755 index 13a40a6d2..000000000 --- a/route/static/build/addons/zmodem/demo/app.js +++ /dev/null @@ -1,87 +0,0 @@ -var express = require('express'); -var app = express(); -var expressWs = require('express-ws')(app); -var os = require('os'); -var pty = require('node-pty'); - -var terminals = {}, - logs = {}; - -app.use('/build', express.static(__dirname + '/../../../../build')); -app.use('/demo', express.static(__dirname + '/../../../../demo')); -app.use('/zmodemjs', express.static(__dirname + '/../../../../node_modules/zmodem.js/dist')); - -app.get('/', function(req, res){ - res.sendFile(__dirname + '/index.html'); -}); - -app.get('/style.css', function(req, res){ - res.sendFile(__dirname + '/style.css'); -}); - -app.get('/main.js', function(req, res){ - res.sendFile(__dirname + '/main.js'); -}); - -app.post('/terminals', function (req, res) { - var cols = parseInt(req.query.cols), - rows = parseInt(req.query.rows), - term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], { - encoding: null, - name: 'xterm-color', - cols: cols || 80, - rows: rows || 24, - cwd: process.env.PWD, - env: process.env - }); - - console.log('Created terminal with PID: ' + term.pid); - terminals[term.pid] = term; - logs[term.pid] = ''; - term.on('data', function(data) { - logs[term.pid] += data; - }); - res.send(term.pid.toString()); - res.end(); -}); - -app.post('/terminals/:pid/size', function (req, res) { - var pid = parseInt(req.params.pid), - cols = parseInt(req.query.cols), - rows = parseInt(req.query.rows), - term = terminals[pid]; - - term.resize(cols, rows); - console.log('Resized terminal ' + pid + ' to ' + cols + ' cols and ' + rows + ' rows.'); - res.end(); -}); - -app.ws('/terminals/:pid', function (ws, req) { - var term = terminals[parseInt(req.params.pid)]; - console.log('Connected to terminal ' + term.pid); - ws.send(logs[term.pid]); - - term.on('data', function(data) { - try { - ws.send(data); - } catch (ex) { - // The WebSocket is not open, ignore - } - }); - ws.on('message', function(msg) { - term.write(msg); - }); - ws.on('close', function () { - term.kill(); - console.log('Closed terminal ' + term.pid); - // Clean things up - delete terminals[term.pid]; - delete logs[term.pid]; - }); -}); - -var port = process.env.PORT || 3000, - host = os.platform() === 'win32' ? '127.0.0.1' : '0.0.0.0'; - -console.log('App listening to http://' + host + ':' + port); -app.listen(port, host); diff --git a/route/static/build/addons/zmodem/demo/index.html b/route/static/build/addons/zmodem/demo/index.html deleted file mode 100755 index 0b38cf4f1..000000000 --- a/route/static/build/addons/zmodem/demo/index.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - xterm.js demo - - - - - - - - - - - - - - - - -

                xterm.js: xterm, in the browser

                - -
                - -
                - - - - - - - - - -
                - -
                -

                Actions

                -

                - - -

                -
                -
                -

                Options

                -

                - -

                -

                - -

                -

                - -

                -

                - -

                -

                - -

                -

                - -

                -
                -

                Size

                -
                -
                - - -
                -
                - - -
                -
                -
                -
                -

                Attention: The demo is a barebones implementation and is designed for xterm.js evaluation purposes only. Exposing the demo to the public as is would introduce security risks for the host.

                -

                * ZMODEM file transfers are supported via an addon. To try it out, install lrzsz onto the remote peer, then run rz to send from your browser or sz <file> to send from the remote peer.

                - - - diff --git a/route/static/build/addons/zmodem/demo/main.js b/route/static/build/addons/zmodem/demo/main.js deleted file mode 100755 index aca441355..000000000 --- a/route/static/build/addons/zmodem/demo/main.js +++ /dev/null @@ -1,383 +0,0 @@ -"use strict"; - -var term, - protocol, - socketURL, - socket, - pid; - -var terminalContainer = document.getElementById('terminal-container'), - actionElements = { - findNext: document.querySelector('#find-next'), - findPrevious: document.querySelector('#find-previous') - }, - optionElements = { - cursorBlink: document.querySelector('#option-cursor-blink'), - cursorStyle: document.querySelector('#option-cursor-style'), - scrollback: document.querySelector('#option-scrollback'), - tabstopwidth: document.querySelector('#option-tabstopwidth'), - bellStyle: document.querySelector('#option-bell-style') - }, - colsElement = document.getElementById('cols'), - rowsElement = document.getElementById('rows'); - -function setTerminalSize() { - var cols = parseInt(colsElement.value, 10); - var rows = parseInt(rowsElement.value, 10); - var viewportElement = document.querySelector('.xterm-viewport'); - var scrollBarWidth = viewportElement.offsetWidth - viewportElement.clientWidth; - var width = (cols * term.charMeasure.width + 20 /*room for scrollbar*/).toString() + 'px'; - var height = (rows * term.charMeasure.height).toString() + 'px'; - - terminalContainer.style.width = width; - terminalContainer.style.height = height; - term.resize(cols, rows); -} - -colsElement.addEventListener('change', setTerminalSize); -rowsElement.addEventListener('change', setTerminalSize); - -actionElements.findNext.addEventListener('keypress', function (e) { - if (e.key === "Enter") { - e.preventDefault(); - term.findNext(actionElements.findNext.value); - } -}); -actionElements.findPrevious.addEventListener('keypress', function (e) { - if (e.key === "Enter") { - e.preventDefault(); - term.findPrevious(actionElements.findPrevious.value); - } -}); - -optionElements.cursorBlink.addEventListener('change', function () { - term.setOption('cursorBlink', optionElements.cursorBlink.checked); -}); -optionElements.cursorStyle.addEventListener('change', function () { - term.setOption('cursorStyle', optionElements.cursorStyle.value); -}); -optionElements.bellStyle.addEventListener('change', function () { - term.setOption('bellStyle', optionElements.bellStyle.value); -}); -optionElements.scrollback.addEventListener('change', function () { - term.setOption('scrollback', parseInt(optionElements.scrollback.value, 10)); -}); -optionElements.tabstopwidth.addEventListener('change', function () { - term.setOption('tabStopWidth', parseInt(optionElements.tabstopwidth.value, 10)); -}); - -createTerminal(); - -function createTerminal() { - // Clean terminal - while (terminalContainer.children.length) { - terminalContainer.removeChild(terminalContainer.children[0]); - } - term = new Terminal({ - cursorBlink: optionElements.cursorBlink.checked, - scrollback: parseInt(optionElements.scrollback.value, 10), - tabStopWidth: parseInt(optionElements.tabstopwidth.value, 10) - }); - term.on('resize', function (size) { - if (!pid) { - return; - } - var cols = size.cols, - rows = size.rows, - url = '/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows; - - fetch(url, {method: 'POST'}); - }); - protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://'; - socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/'; - - term.open(terminalContainer); - term.fit(); - - // fit is called within a setTimeout, cols and rows need this. - setTimeout(function () { - colsElement.value = term.cols; - rowsElement.value = term.rows; - - // Set terminal size again to set the specific dimensions on the demo - setTerminalSize(); - - fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) { - - res.text().then(function (pid) { - window.pid = pid; - socketURL += pid; - socket = new WebSocket(socketURL); - socket.onopen = runRealTerminal; - socket.onclose = runFakeTerminal; - socket.onerror = runFakeTerminal; - - term.zmodemAttach(socket, { - noTerminalWriteOutsideSession: true, - } ); - - term.on("zmodemRetract", () => { - start_form.style.display = "none"; - start_form.onsubmit = null; - }); - - term.on("zmodemDetect", (detection) => { - function do_zmodem() { - term.detach(); - let zsession = detection.confirm(); - - var promise; - - if (zsession.type === "receive") { - promise = _handle_receive_session(zsession); - } - else { - promise = _handle_send_session(zsession); - } - - promise.catch( console.error.bind(console) ).then( () => { - term.attach(socket); - } ); - } - - if (_auto_zmodem()) { - do_zmodem(); - } - else { - start_form.style.display = ""; - start_form.onsubmit = function(e) { - start_form.style.display = "none"; - - if (document.getElementById("zmstart_yes").checked) { - do_zmodem(); - } - else { - detection.deny(); - } - }; - } - }); - }); - }); - }, 0); -} - -//---------------------------------------------------------------------- -// UI STUFF - -function _show_file_info(xfer) { - var file_info = xfer.get_details(); - - document.getElementById("name").textContent = file_info.name; - document.getElementById("size").textContent = file_info.size; - document.getElementById("mtime").textContent = file_info.mtime; - document.getElementById("files_remaining").textContent = file_info.files_remaining; - document.getElementById("bytes_remaining").textContent = file_info.bytes_remaining; - - document.getElementById("mode").textContent = "0" + file_info.mode.toString(8); - - var xfer_opts = xfer.get_options(); - ["conversion", "management", "transport", "sparse"].forEach( (lbl) => { - document.getElementById(`zfile_${lbl}`).textContent = xfer_opts[lbl]; - } ); - - document.getElementById("zm_file").style.display = ""; -} -function _hide_file_info() { - document.getElementById("zm_file").style.display = "none"; -} - -function _save_to_disk(xfer, buffer) { - return Zmodem.Browser.save_to_disk(buffer, xfer.get_details().name); -} - -var skipper_button = document.getElementById("zm_progress_skipper"); -var skipper_button_orig_text = skipper_button.textContent; - -function _show_progress() { - skipper_button.disabled = false; - skipper_button.textContent = skipper_button_orig_text; - - document.getElementById("bytes_received").textContent = 0; - document.getElementById("percent_received").textContent = 0; - - document.getElementById("zm_progress").style.display = ""; -} - -function _update_progress(xfer) { - var total_in = xfer.get_offset(); - - document.getElementById("bytes_received").textContent = total_in; - - var percent_received = 100 * total_in / xfer.get_details().size; - document.getElementById("percent_received").textContent = percent_received.toFixed(2); -} - -function _hide_progress() { - document.getElementById("zm_progress").style.display = "none"; -} - -var start_form = document.getElementById("zm_start"); - -function _auto_zmodem() { - return document.getElementById("zmodem-auto").checked; -} - -// END UI STUFF -//---------------------------------------------------------------------- - -function _handle_receive_session(zsession) { - zsession.on("offer", function(xfer) { - current_receive_xfer = xfer; - - _show_file_info(xfer); - - var offer_form = document.getElementById("zm_offer"); - - function on_form_submit() { - offer_form.style.display = "none"; - - //START - //if (offer_form.zmaccept.value) { - if (_auto_zmodem() || document.getElementById("zmaccept_yes").checked) { - _show_progress(); - - var FILE_BUFFER = []; - xfer.on("input", (payload) => { - _update_progress(xfer); - FILE_BUFFER.push( new Uint8Array(payload) ); - }); - xfer.accept().then( - () => { - _save_to_disk(xfer, FILE_BUFFER); - }, - console.error.bind(console) - ); - } - else { - xfer.skip(); - } - //END - } - - if (_auto_zmodem()) { - on_form_submit(); - } - else { - offer_form.onsubmit = on_form_submit; - offer_form.style.display = ""; - } - } ); - - var promise = new Promise( (res) => { - zsession.on("session_end", () => { - _hide_file_info(); - _hide_progress(); - res(); - } ); - } ); - - zsession.start(); - - return promise; -} - -function _handle_send_session(zsession) { - var choose_form = document.getElementById("zm_choose"); - choose_form.style.display = ""; - - var file_el = document.getElementById("zm_files"); - - var promise = new Promise( (res) => { - file_el.onchange = function(e) { - choose_form.style.display = "none"; - - var files_obj = file_el.files; - - Zmodem.Browser.send_files( - zsession, - files_obj, - { - on_offer_response(obj, xfer) { - if (xfer) _show_progress(); - //console.log("offer", xfer ? "accepted" : "skipped"); - }, - on_progress(obj, xfer) { - _update_progress(xfer); - }, - on_file_complete(obj) { - //console.log("COMPLETE", obj); - _hide_progress(); - }, - } - ).then(_hide_progress).then( - zsession.close.bind(zsession), - console.error.bind(console) - ).then( () => { - _hide_file_info(); - _hide_progress(); - res(); - } ); - }; - } ); - - return promise; -} - -//This is here to allow canceling of an in-progress ZMODEM transfer. -var current_receive_xfer; - -//Called from HTML directly. -function skip_current_file() { - current_receive_xfer.skip(); - - skipper_button.disabled = true; - skipper_button.textContent = "Waiting for server to acknowledge skip …"; -} - -function runRealTerminal() { - term.attach(socket); - - term._initialized = true; -} - -function runFakeTerminal() { - if (term._initialized) { - return; - } - - term._initialized = true; - - var shellprompt = '$ '; - - term.prompt = function () { - term.write('\r\n' + shellprompt); - }; - - term.writeln('Welcome to xterm.js'); - term.writeln('This is a local terminal emulation, without a real terminal in the back-end.'); - term.writeln('Type some keys and commands to play around.'); - term.writeln(''); - term.prompt(); - - term.on('key', function (key, ev) { - var printable = ( - !ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey - ); - - if (ev.keyCode == 13) { - term.prompt(); - } else if (ev.keyCode == 8) { - // Do not delete the prompt - if (term.x > 2) { - term.write('\b \b'); - } - } else if (printable) { - term.write(key); - } - }); - - term.on('paste', function (data, ev) { - term.write(data); - }); -} diff --git a/route/static/build/addons/zmodem/zmodem.js b/route/static/build/addons/zmodem/zmodem.js deleted file mode 100755 index aeb2fd5f0..000000000 --- a/route/static/build/addons/zmodem/zmodem.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * - * Allow xterm.js to handle ZMODEM uploads and downloads. - * - * This addon is a wrapper around zmodem.js. It adds the following to the - * Terminal class: - * - * - function `zmodemAttach(, )` - creates a Zmodem.Sentry - * on the passed WebSocket object. The Object passed is optional and - * can contain: - * - noTerminalWriteOutsideSession: Suppress writes from the Sentry - * object to the Terminal while there is no active Session. This - * is necessary for compatibility with, for example, the - * `attach.js` addon. - * - * - event `zmodemDetect` - fired on Zmodem.Sentry’s `on_detect` callback. - * Passes the zmodem.js Detection object. - * - * - event `zmodemRetract` - fired on Zmodem.Sentry’s `on_retract` callback. - * - * You’ll need to provide logic to handle uploads and downloads. - * See zmodem.js’s documentation for more details. - * - * **IMPORTANT:** After you confirm() a zmodem.js Detection, if you have - * used the `attach` or `terminado` addons, you’ll need to suspend their - * operation for the duration of the ZMODEM session. (The demo does this - * via `detach()` and a re-`attach()`.) - */ -(function (addon) { - if (typeof exports === 'object' && typeof module === 'object') { - /* - * CommonJS environment - */ - module.exports = addon(require('../../Terminal').Terminal); - } else if (typeof define == 'function') { - /* - * Require.js is available - */ - define(['../../xterm'], addon); - } else { - /* - * Plain browser environment - */ - addon(window.Terminal); - } -})(function _zmodemAddon(Terminal) { - Object.assign( - Terminal.prototype, - { - zmodemAttach: function zmodemAttach(ws, opts) { - var term = this; - - if (!opts) opts = {}; - - var senderFunc = function _ws_sender_func(octets) { - ws.send( new Uint8Array(octets) ); - }; - - var zsentry; - - function _shouldWrite() { - return !!zsentry.get_confirmed_session() || !opts.noTerminalWriteOutsideSession; - } - - zsentry = new Zmodem.Sentry( { - to_terminal: function _to_terminal(octets) { - if (_shouldWrite()) { - term.write( - String.fromCharCode.apply(String, octets) - ); - } - }, - - sender: senderFunc, - - on_retract: function _on_retract() { - term.emit("zmodemRetract"); - }, - - on_detect: function _on_detect(detection) { - term.emit("zmodemDetect", detection); - }, - } ); - - function handleWSMessage(evt) { - - //In testing with xterm.js’s demo the first message was - //always text even if the rest were binary. While that - //may be specific to xterm.js’s demo, ultimately we - //should reject anything that isn’t binary. - if (typeof evt.data === "string") { - if (_shouldWrite()) { - term.write(evt.data); - } - } - else { - zsentry.consume(evt.data); - } - } - - ws.binaryType = "arraybuffer"; - ws.addEventListener("message", handleWSMessage); - }, - - zmodemBrowser: Zmodem.Browser, - } - ); -}); diff --git a/route/static/build/xterm.css b/route/static/build/xterm.css deleted file mode 100755 index c668af403..000000000 --- a/route/static/build/xterm.css +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) 2014 The xterm.js authors. All rights reserved. - * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) - * https://github.com/chjj/term.js - * @license MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * Originally forked from (with the author's permission): - * Fabrice Bellard's javascript vt100 for jslinux: - * http://bellard.org/jslinux/ - * Copyright (c) 2011 Fabrice Bellard - * The original design remains. The terminal itself - * has been extended to include xterm CSI codes, among - * other features. - */ - -/** - * Default styles for xterm.js - */ - -.xterm { - font-family: courier-new, courier, monospace; - font-feature-settings: "liga" 0; - position: relative; - user-select: none; - -ms-user-select: none; - -webkit-user-select: none; -} - -.xterm.focus, -.xterm:focus { - outline: none; -} - -.xterm .xterm-helpers { - position: absolute; - top: 0; - /** - * The z-index of the helpers must be higher than the canvases in order for - * IMEs to appear on top. - */ - z-index: 10; -} - -.xterm .xterm-helper-textarea { - /* - * HACK: to fix IE's blinking cursor - * Move textarea out of the screen to the far left, so that the cursor is not visible. - */ - position: absolute; - opacity: 0; - left: -9999em; - top: 0; - width: 0; - height: 0; - z-index: -10; - /** Prevent wrapping so the IME appears against the textarea at the correct position */ - white-space: nowrap; - overflow: hidden; - resize: none; -} - -.xterm .composition-view { - /* TODO: Composition position got messed up somewhere */ - background: #333; - color: #FFF; - display: none; - position: absolute; - white-space: nowrap; - z-index: 1; -} - -.xterm .composition-view.active { - display: block; -} - -.xterm .xterm-viewport { - /* On OS X this is required in order for the scroll bar to appear fully opaque */ - background-color: #333; - overflow-y: scroll; -} - -.xterm canvas { - position: absolute; - left: 0; - top: 0; -} - -.xterm .xterm-scroll-area { - visibility: hidden; -} - -.xterm .xterm-char-measure-element { - display: inline-block; - visibility: hidden; - position: absolute; - left: -9999em; -} - -.xterm.enable-mouse-events { - /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */ - cursor: default; -} - -.xterm:not(.enable-mouse-events) { - cursor: text; -} diff --git a/route/static/build/xterm.js b/route/static/build/xterm.js deleted file mode 100755 index 80b0ba5d3..000000000 --- a/route/static/build/xterm.js +++ /dev/null @@ -1,6241 +0,0 @@ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Terminal = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o this._terminal.rows; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(Buffer.prototype, "isCursorInViewport", { - get: function () { - var absoluteY = this.ybase + this.y; - var relativeY = absoluteY - this.ydisp; - return (relativeY >= 0 && relativeY < this._terminal.rows); - }, - enumerable: true, - configurable: true - }); - Buffer.prototype._getCorrectBufferLength = function (rows) { - if (!this._hasScrollback) { - return rows; - } - var correctBufferLength = rows + this._terminal.options.scrollback; - return correctBufferLength > exports.MAX_BUFFER_SIZE ? exports.MAX_BUFFER_SIZE : correctBufferLength; - }; - Buffer.prototype.fillViewportRows = function () { - if (this._lines.length === 0) { - var i = this._terminal.rows; - while (i--) { - this.lines.push(this._terminal.blankLine()); - } - } - }; - Buffer.prototype.clear = function () { - this.ydisp = 0; - this.ybase = 0; - this.y = 0; - this.x = 0; - this._lines = new CircularList_1.CircularList(this._getCorrectBufferLength(this._terminal.rows)); - this.scrollTop = 0; - this.scrollBottom = this._terminal.rows - 1; - this.setupTabStops(); - }; - Buffer.prototype.resize = function (newCols, newRows) { - var newMaxLength = this._getCorrectBufferLength(newRows); - if (newMaxLength > this._lines.maxLength) { - this._lines.maxLength = newMaxLength; - } - if (this._lines.length > 0) { - if (this._terminal.cols < newCols) { - var ch = [this._terminal.defAttr, ' ', 1, 32]; - for (var i = 0; i < this._lines.length; i++) { - if (this._lines.get(i) === undefined) { - this._lines.set(i, this._terminal.blankLine(undefined, undefined, newCols)); - } - while (this._lines.get(i).length < newCols) { - this._lines.get(i).push(ch); - } - } - } - var addToY = 0; - if (this._terminal.rows < newRows) { - for (var y = this._terminal.rows; y < newRows; y++) { - if (this._lines.length < newRows + this.ybase) { - if (this.ybase > 0 && this._lines.length <= this.ybase + this.y + addToY + 1) { - this.ybase--; - addToY++; - if (this.ydisp > 0) { - this.ydisp--; - } - } - else { - this._lines.push(this._terminal.blankLine(undefined, undefined, newCols)); - } - } - } - } - else { - for (var y = this._terminal.rows; y > newRows; y--) { - if (this._lines.length > newRows + this.ybase) { - if (this._lines.length > this.ybase + this.y + 1) { - this._lines.pop(); - } - else { - this.ybase++; - this.ydisp++; - } - } - } - } - if (newMaxLength < this._lines.maxLength) { - var amountToTrim = this._lines.length - newMaxLength; - if (amountToTrim > 0) { - this._lines.trimStart(amountToTrim); - this.ybase = Math.max(this.ybase - amountToTrim, 0); - this.ydisp = Math.max(this.ydisp - amountToTrim, 0); - } - this._lines.maxLength = newMaxLength; - } - if (this.y >= newRows) { - this.y = newRows - 1; - } - if (addToY) { - this.y += addToY; - } - if (this.x >= newCols) { - this.x = newCols - 1; - } - this.scrollTop = 0; - } - this.scrollBottom = newRows - 1; - }; - Buffer.prototype.translateBufferLineToString = function (lineIndex, trimRight, startCol, endCol) { - if (startCol === void 0) { startCol = 0; } - if (endCol === void 0) { endCol = null; } - var lineString = ''; - var line = this.lines.get(lineIndex); - if (!line) { - return ''; - } - var startIndex = startCol; - endCol = endCol || line.length; - var endIndex = endCol; - for (var i = 0; i < line.length; i++) { - var char = line[i]; - lineString += char[exports.CHAR_DATA_CHAR_INDEX]; - if (char[exports.CHAR_DATA_WIDTH_INDEX] === 0) { - if (startCol >= i) { - startIndex--; - } - if (endCol >= i) { - endIndex--; - } - } - else { - if (char[exports.CHAR_DATA_CHAR_INDEX].length > 1) { - if (startCol > i) { - startIndex += char[exports.CHAR_DATA_CHAR_INDEX].length - 1; - } - if (endCol > i) { - endIndex += char[exports.CHAR_DATA_CHAR_INDEX].length - 1; - } - } - } - } - if (trimRight) { - var rightWhitespaceIndex = lineString.search(/\s+$/); - if (rightWhitespaceIndex !== -1) { - endIndex = Math.min(endIndex, rightWhitespaceIndex); - } - if (endIndex <= startIndex) { - return ''; - } - } - return lineString.substring(startIndex, endIndex); - }; - Buffer.prototype.setupTabStops = function (i) { - if (i != null) { - if (!this.tabs[i]) { - i = this.prevStop(i); - } - } - else { - this.tabs = {}; - i = 0; - } - for (; i < this._terminal.cols; i += this._terminal.options.tabStopWidth) { - this.tabs[i] = true; - } - }; - Buffer.prototype.prevStop = function (x) { - if (x == null) { - x = this.x; - } - while (!this.tabs[--x] && x > 0) - ; - return x >= this._terminal.cols ? this._terminal.cols - 1 : x < 0 ? 0 : x; - }; - Buffer.prototype.nextStop = function (x) { - if (x == null) { - x = this.x; - } - while (!this.tabs[++x] && x < this._terminal.cols) - ; - return x >= this._terminal.cols ? this._terminal.cols - 1 : x < 0 ? 0 : x; - }; - return Buffer; -}()); -exports.Buffer = Buffer; - - - -},{"./utils/CircularList":30}],2:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var Buffer_1 = require("./Buffer"); -var EventEmitter_1 = require("./EventEmitter"); -var BufferSet = (function (_super) { - __extends(BufferSet, _super); - function BufferSet(_terminal) { - var _this = _super.call(this) || this; - _this._terminal = _terminal; - _this._normal = new Buffer_1.Buffer(_this._terminal, true); - _this._normal.fillViewportRows(); - _this._alt = new Buffer_1.Buffer(_this._terminal, false); - _this._activeBuffer = _this._normal; - _this.setupTabStops(); - return _this; - } - Object.defineProperty(BufferSet.prototype, "alt", { - get: function () { - return this._alt; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(BufferSet.prototype, "active", { - get: function () { - return this._activeBuffer; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(BufferSet.prototype, "normal", { - get: function () { - return this._normal; - }, - enumerable: true, - configurable: true - }); - BufferSet.prototype.activateNormalBuffer = function () { - this._alt.clear(); - this._activeBuffer = this._normal; - this.emit('activate', this._normal); - }; - BufferSet.prototype.activateAltBuffer = function () { - this._alt.fillViewportRows(); - this._activeBuffer = this._alt; - this.emit('activate', this._alt); - }; - BufferSet.prototype.resize = function (newCols, newRows) { - this._normal.resize(newCols, newRows); - this._alt.resize(newCols, newRows); - }; - BufferSet.prototype.setupTabStops = function (i) { - this._normal.setupTabStops(i); - this._alt.setupTabStops(i); - }; - return BufferSet; -}(EventEmitter_1.EventEmitter)); -exports.BufferSet = BufferSet; - - - -},{"./Buffer":1,"./EventEmitter":7}],3:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.wcwidth = (function (opts) { - var COMBINING_BMP = [ - [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], - [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], - [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603], - [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670], - [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], - [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], - [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902], - [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], - [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981], - [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], - [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], - [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], - [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], - [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], - [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], - [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D], - [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], - [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], - [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC], - [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], - [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D], - [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], - [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], - [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC], - [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], - [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], - [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97], - [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], - [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039], - [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F], - [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], - [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], - [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], - [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922], - [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], - [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34], - [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], - [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF], - [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063], - [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F], - [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B], - [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], - [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB], - ]; - var COMBINING_HIGH = [ - [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], - [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169], - [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], - [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F], - [0xE0100, 0xE01EF] - ]; - function bisearch(ucs, data) { - var min = 0; - var max = data.length - 1; - var mid; - if (ucs < data[0][0] || ucs > data[max][1]) - return false; - while (max >= min) { - mid = (min + max) >> 1; - if (ucs > data[mid][1]) - min = mid + 1; - else if (ucs < data[mid][0]) - max = mid - 1; - else - return true; - } - return false; - } - function wcwidthBMP(ucs) { - if (ucs === 0) - return opts.nul; - if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) - return opts.control; - if (bisearch(ucs, COMBINING_BMP)) - return 0; - if (isWideBMP(ucs)) { - return 2; - } - return 1; - } - function isWideBMP(ucs) { - return (ucs >= 0x1100 && (ucs <= 0x115f || - ucs === 0x2329 || - ucs === 0x232a || - (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs !== 0x303f) || - (ucs >= 0xac00 && ucs <= 0xd7a3) || - (ucs >= 0xf900 && ucs <= 0xfaff) || - (ucs >= 0xfe10 && ucs <= 0xfe19) || - (ucs >= 0xfe30 && ucs <= 0xfe6f) || - (ucs >= 0xff00 && ucs <= 0xff60) || - (ucs >= 0xffe0 && ucs <= 0xffe6))); - } - function wcwidthHigh(ucs) { - if (bisearch(ucs, COMBINING_HIGH)) - return 0; - if ((ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd)) { - return 2; - } - return 1; - } - var control = opts.control | 0; - var table = null; - function init_table() { - var CODEPOINTS = 65536; - var BITWIDTH = 2; - var ITEMSIZE = 32; - var CONTAINERSIZE = CODEPOINTS * BITWIDTH / ITEMSIZE; - var CODEPOINTS_PER_ITEM = ITEMSIZE / BITWIDTH; - table = (typeof Uint32Array === 'undefined') - ? new Array(CONTAINERSIZE) - : new Uint32Array(CONTAINERSIZE); - for (var i = 0; i < CONTAINERSIZE; ++i) { - var num = 0; - var pos = CODEPOINTS_PER_ITEM; - while (pos--) - num = (num << 2) | wcwidthBMP(CODEPOINTS_PER_ITEM * i + pos); - table[i] = num; - } - return table; - } - return function (num) { - num = num | 0; - if (num < 32) - return control | 0; - if (num < 127) - return 1; - var t = table || init_table(); - if (num < 65536) - return t[num >> 4] >> ((num & 15) << 1) & 3; - return wcwidthHigh(num); - }; -})({ nul: 0, control: 0 }); - - - -},{}],4:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CHARSETS = {}; -exports.DEFAULT_CHARSET = exports.CHARSETS['B']; -exports.CHARSETS['0'] = { - '`': '\u25c6', - 'a': '\u2592', - 'b': '\u0009', - 'c': '\u000c', - 'd': '\u000d', - 'e': '\u000a', - 'f': '\u00b0', - 'g': '\u00b1', - 'h': '\u2424', - 'i': '\u000b', - 'j': '\u2518', - 'k': '\u2510', - 'l': '\u250c', - 'm': '\u2514', - 'n': '\u253c', - 'o': '\u23ba', - 'p': '\u23bb', - 'q': '\u2500', - 'r': '\u23bc', - 's': '\u23bd', - 't': '\u251c', - 'u': '\u2524', - 'v': '\u2534', - 'w': '\u252c', - 'x': '\u2502', - 'y': '\u2264', - 'z': '\u2265', - '{': '\u03c0', - '|': '\u2260', - '}': '\u00a3', - '~': '\u00b7' -}; -exports.CHARSETS['A'] = { - '#': '£' -}; -exports.CHARSETS['B'] = null; -exports.CHARSETS['4'] = { - '#': '£', - '@': '¾', - '[': 'ij', - '\\': '½', - ']': '|', - '{': '¨', - '|': 'f', - '}': '¼', - '~': '´' -}; -exports.CHARSETS['C'] = - exports.CHARSETS['5'] = { - '[': 'Ä', - '\\': 'Ö', - ']': 'Å', - '^': 'Ü', - '`': 'é', - '{': 'ä', - '|': 'ö', - '}': 'å', - '~': 'ü' - }; -exports.CHARSETS['R'] = { - '#': '£', - '@': 'à', - '[': '°', - '\\': 'ç', - ']': '§', - '{': 'é', - '|': 'ù', - '}': 'è', - '~': '¨' -}; -exports.CHARSETS['Q'] = { - '@': 'à', - '[': 'â', - '\\': 'ç', - ']': 'ê', - '^': 'î', - '`': 'ô', - '{': 'é', - '|': 'ù', - '}': 'è', - '~': 'û' -}; -exports.CHARSETS['K'] = { - '@': '§', - '[': 'Ä', - '\\': 'Ö', - ']': 'Ü', - '{': 'ä', - '|': 'ö', - '}': 'ü', - '~': 'ß' -}; -exports.CHARSETS['Y'] = { - '#': '£', - '@': '§', - '[': '°', - '\\': 'ç', - ']': 'é', - '`': 'ù', - '{': 'à', - '|': 'ò', - '}': 'è', - '~': 'ì' -}; -exports.CHARSETS['E'] = - exports.CHARSETS['6'] = { - '@': 'Ä', - '[': 'Æ', - '\\': 'Ø', - ']': 'Å', - '^': 'Ü', - '`': 'ä', - '{': 'æ', - '|': 'ø', - '}': 'å', - '~': 'ü' - }; -exports.CHARSETS['Z'] = { - '#': '£', - '@': '§', - '[': '¡', - '\\': 'Ñ', - ']': '¿', - '{': '°', - '|': 'ñ', - '}': 'ç' -}; -exports.CHARSETS['H'] = - exports.CHARSETS['7'] = { - '@': 'É', - '[': 'Ä', - '\\': 'Ö', - ']': 'Å', - '^': 'Ü', - '`': 'é', - '{': 'ä', - '|': 'ö', - '}': 'å', - '~': 'ü' - }; -exports.CHARSETS['='] = { - '#': 'ù', - '@': 'à', - '[': 'é', - '\\': 'ç', - ']': 'ê', - '^': 'î', - '_': 'è', - '`': 'ô', - '{': 'ä', - '|': 'ö', - '}': 'ü', - '~': 'û' -}; - - - -},{}],5:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var CompositionHelper = (function () { - function CompositionHelper(textarea, compositionView, terminal) { - this.textarea = textarea; - this.compositionView = compositionView; - this.terminal = terminal; - this.isComposing = false; - this.isSendingComposition = false; - this.compositionPosition = { start: null, end: null }; - } - CompositionHelper.prototype.compositionstart = function () { - this.isComposing = true; - this.compositionPosition.start = this.textarea.value.length; - this.compositionView.textContent = ''; - this.compositionView.classList.add('active'); - }; - CompositionHelper.prototype.compositionupdate = function (ev) { - var _this = this; - this.compositionView.textContent = ev.data; - this.updateCompositionElements(); - setTimeout(function () { - _this.compositionPosition.end = _this.textarea.value.length; - }, 0); - }; - CompositionHelper.prototype.compositionend = function () { - this.finalizeComposition(true); - }; - CompositionHelper.prototype.keydown = function (ev) { - if (this.isComposing || this.isSendingComposition) { - if (ev.keyCode === 229) { - return false; - } - else if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) { - return false; - } - else { - this.finalizeComposition(false); - } - } - if (ev.keyCode === 229) { - this.handleAnyTextareaChanges(); - return false; - } - return true; - }; - CompositionHelper.prototype.finalizeComposition = function (waitForPropogation) { - var _this = this; - this.compositionView.classList.remove('active'); - this.isComposing = false; - this.clearTextareaPosition(); - if (!waitForPropogation) { - this.isSendingComposition = false; - var input = this.textarea.value.substring(this.compositionPosition.start, this.compositionPosition.end); - this.terminal.handler(input); - } - else { - var currentCompositionPosition_1 = { - start: this.compositionPosition.start, - end: this.compositionPosition.end, - }; - this.isSendingComposition = true; - setTimeout(function () { - if (_this.isSendingComposition) { - _this.isSendingComposition = false; - var input = void 0; - if (_this.isComposing) { - input = _this.textarea.value.substring(currentCompositionPosition_1.start, currentCompositionPosition_1.end); - } - else { - input = _this.textarea.value.substring(currentCompositionPosition_1.start); - } - _this.terminal.handler(input); - } - }, 0); - } - }; - CompositionHelper.prototype.handleAnyTextareaChanges = function () { - var _this = this; - var oldValue = this.textarea.value; - setTimeout(function () { - if (!_this.isComposing) { - var newValue = _this.textarea.value; - var diff = newValue.replace(oldValue, ''); - if (diff.length > 0) { - _this.terminal.handler(diff); - } - } - }, 0); - }; - CompositionHelper.prototype.updateCompositionElements = function (dontRecurse) { - var _this = this; - if (!this.isComposing) { - return; - } - if (this.terminal.buffer.isCursorInViewport) { - var cellHeight = Math.ceil(this.terminal.charMeasure.height * this.terminal.options.lineHeight); - var cursorTop = this.terminal.buffer.y * cellHeight; - var cursorLeft = this.terminal.buffer.x * this.terminal.charMeasure.width; - this.compositionView.style.left = cursorLeft + 'px'; - this.compositionView.style.top = cursorTop + 'px'; - this.compositionView.style.height = cellHeight + 'px'; - this.compositionView.style.lineHeight = cellHeight + 'px'; - var compositionViewBounds = this.compositionView.getBoundingClientRect(); - this.textarea.style.left = cursorLeft + 'px'; - this.textarea.style.top = cursorTop + 'px'; - this.textarea.style.width = compositionViewBounds.width + 'px'; - this.textarea.style.height = compositionViewBounds.height + 'px'; - this.textarea.style.lineHeight = compositionViewBounds.height + 'px'; - } - if (!dontRecurse) { - setTimeout(function () { return _this.updateCompositionElements(true); }, 0); - } - }; - ; - CompositionHelper.prototype.clearTextareaPosition = function () { - this.textarea.style.left = ''; - this.textarea.style.top = ''; - }; - ; - return CompositionHelper; -}()); -exports.CompositionHelper = CompositionHelper; - - - -},{}],6:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var C0; -(function (C0) { - C0.NUL = '\x00'; - C0.SOH = '\x01'; - C0.STX = '\x02'; - C0.ETX = '\x03'; - C0.EOT = '\x04'; - C0.ENQ = '\x05'; - C0.ACK = '\x06'; - C0.BEL = '\x07'; - C0.BS = '\x08'; - C0.HT = '\x09'; - C0.LF = '\x0a'; - C0.VT = '\x0b'; - C0.FF = '\x0c'; - C0.CR = '\x0d'; - C0.SO = '\x0e'; - C0.SI = '\x0f'; - C0.DLE = '\x10'; - C0.DC1 = '\x11'; - C0.DC2 = '\x12'; - C0.DC3 = '\x13'; - C0.DC4 = '\x14'; - C0.NAK = '\x15'; - C0.SYN = '\x16'; - C0.ETB = '\x17'; - C0.CAN = '\x18'; - C0.EM = '\x19'; - C0.SUB = '\x1a'; - C0.ESC = '\x1b'; - C0.FS = '\x1c'; - C0.GS = '\x1d'; - C0.RS = '\x1e'; - C0.US = '\x1f'; - C0.SP = '\x20'; - C0.DEL = '\x7f'; -})(C0 = exports.C0 || (exports.C0 = {})); -; - - - -},{}],7:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var EventEmitter = (function () { - function EventEmitter() { - this._events = this._events || {}; - } - EventEmitter.prototype.on = function (type, listener) { - this._events[type] = this._events[type] || []; - this._events[type].push(listener); - }; - EventEmitter.prototype.off = function (type, listener) { - if (!this._events[type]) { - return; - } - var obj = this._events[type]; - var i = obj.length; - while (i--) { - if (obj[i] === listener || obj[i].listener === listener) { - obj.splice(i, 1); - return; - } - } - }; - EventEmitter.prototype.removeAllListeners = function (type) { - if (this._events[type]) { - delete this._events[type]; - } - }; - EventEmitter.prototype.once = function (type, listener) { - function on() { - var args = Array.prototype.slice.call(arguments); - this.off(type, on); - listener.apply(this, args); - } - on.listener = listener; - this.on(type, on); - }; - EventEmitter.prototype.emit = function (type) { - var args = []; - for (var _i = 1; _i < arguments.length; _i++) { - args[_i - 1] = arguments[_i]; - } - if (!this._events[type]) { - return; - } - var obj = this._events[type]; - for (var i = 0; i < obj.length; i++) { - obj[i].apply(this, args); - } - }; - EventEmitter.prototype.listeners = function (type) { - return this._events[type] || []; - }; - EventEmitter.prototype.destroy = function () { - this._events = {}; - }; - return EventEmitter; -}()); -exports.EventEmitter = EventEmitter; - - - -},{}],8:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var EscapeSequences_1 = require("./EscapeSequences"); -var Charsets_1 = require("./Charsets"); -var Buffer_1 = require("./Buffer"); -var Types_1 = require("./renderer/Types"); -var CharWidth_1 = require("./CharWidth"); -var InputHandler = (function () { - function InputHandler(_terminal) { - this._terminal = _terminal; - } - InputHandler.prototype.addChar = function (char, code) { - if (char >= ' ') { - var ch_width = CharWidth_1.wcwidth(code); - if (this._terminal.charset && this._terminal.charset[char]) { - char = this._terminal.charset[char]; - } - var row = this._terminal.buffer.y + this._terminal.buffer.ybase; - if (!ch_width && this._terminal.buffer.x) { - if (this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1]) { - if (!this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][Buffer_1.CHAR_DATA_WIDTH_INDEX]) { - if (this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2]) { - this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2][Buffer_1.CHAR_DATA_CHAR_INDEX] += char; - this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2][3] = char.charCodeAt(0); - } - } - else { - this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][Buffer_1.CHAR_DATA_CHAR_INDEX] += char; - this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][3] = char.charCodeAt(0); - } - this._terminal.updateRange(this._terminal.buffer.y); - } - return; - } - if (this._terminal.buffer.x + ch_width - 1 >= this._terminal.cols) { - if (this._terminal.wraparoundMode) { - this._terminal.buffer.x = 0; - this._terminal.buffer.y++; - if (this._terminal.buffer.y > this._terminal.buffer.scrollBottom) { - this._terminal.buffer.y--; - this._terminal.scroll(true); - } - else { - this._terminal.buffer.lines.get(this._terminal.buffer.y).isWrapped = true; - } - } - else { - if (ch_width === 2) - return; - } - } - row = this._terminal.buffer.y + this._terminal.buffer.ybase; - if (this._terminal.insertMode) { - for (var moves = 0; moves < ch_width; ++moves) { - var removed = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).pop(); - if (removed[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 0 - && this._terminal.buffer.lines.get(row)[this._terminal.cols - 2] - && this._terminal.buffer.lines.get(row)[this._terminal.cols - 2][Buffer_1.CHAR_DATA_WIDTH_INDEX] === 2) { - this._terminal.buffer.lines.get(row)[this._terminal.cols - 2] = [this._terminal.curAttr, ' ', 1, ' '.charCodeAt(0)]; - } - this._terminal.buffer.lines.get(row).splice(this._terminal.buffer.x, 0, [this._terminal.curAttr, ' ', 1, ' '.charCodeAt(0)]); - } - } - this._terminal.buffer.lines.get(row)[this._terminal.buffer.x] = [this._terminal.curAttr, char, ch_width, char.charCodeAt(0)]; - this._terminal.buffer.x++; - this._terminal.updateRange(this._terminal.buffer.y); - if (ch_width === 2) { - this._terminal.buffer.lines.get(row)[this._terminal.buffer.x] = [this._terminal.curAttr, '', 0, undefined]; - this._terminal.buffer.x++; - } - } - }; - InputHandler.prototype.bell = function () { - this._terminal.bell(); - }; - InputHandler.prototype.lineFeed = function () { - if (this._terminal.convertEol) { - this._terminal.buffer.x = 0; - } - this._terminal.buffer.y++; - if (this._terminal.buffer.y > this._terminal.buffer.scrollBottom) { - this._terminal.buffer.y--; - this._terminal.scroll(); - } - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x--; - } - this._terminal.emit('lineFeed'); - }; - InputHandler.prototype.carriageReturn = function () { - this._terminal.buffer.x = 0; - }; - InputHandler.prototype.backspace = function () { - if (this._terminal.buffer.x > 0) { - this._terminal.buffer.x--; - } - }; - InputHandler.prototype.tab = function () { - this._terminal.buffer.x = this._terminal.buffer.nextStop(); - }; - InputHandler.prototype.shiftOut = function () { - this._terminal.setgLevel(1); - }; - InputHandler.prototype.shiftIn = function () { - this._terminal.setgLevel(0); - }; - InputHandler.prototype.insertChars = function (params) { - var param = params[0]; - if (param < 1) - param = 1; - var row = this._terminal.buffer.y + this._terminal.buffer.ybase; - var j = this._terminal.buffer.x; - var ch = [this._terminal.eraseAttr(), ' ', 1, 32]; - while (param-- && j < this._terminal.cols) { - this._terminal.buffer.lines.get(row).splice(j++, 0, ch); - this._terminal.buffer.lines.get(row).pop(); - } - }; - InputHandler.prototype.cursorUp = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.y -= param; - if (this._terminal.buffer.y < 0) { - this._terminal.buffer.y = 0; - } - }; - InputHandler.prototype.cursorDown = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.y += param; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x--; - } - }; - InputHandler.prototype.cursorForward = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.x += param; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } - }; - InputHandler.prototype.cursorBackward = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x--; - } - this._terminal.buffer.x -= param; - if (this._terminal.buffer.x < 0) { - this._terminal.buffer.x = 0; - } - }; - InputHandler.prototype.cursorNextLine = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.y += param; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - this._terminal.buffer.x = 0; - }; - InputHandler.prototype.cursorPrecedingLine = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.y -= param; - if (this._terminal.buffer.y < 0) { - this._terminal.buffer.y = 0; - } - this._terminal.buffer.x = 0; - }; - InputHandler.prototype.cursorCharAbsolute = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.x = param - 1; - }; - InputHandler.prototype.cursorPosition = function (params) { - var col; - var row = params[0] - 1; - if (params.length >= 2) { - col = params[1] - 1; - } - else { - col = 0; - } - if (row < 0) { - row = 0; - } - else if (row >= this._terminal.rows) { - row = this._terminal.rows - 1; - } - if (col < 0) { - col = 0; - } - else if (col >= this._terminal.cols) { - col = this._terminal.cols - 1; - } - this._terminal.buffer.x = col; - this._terminal.buffer.y = row; - }; - InputHandler.prototype.cursorForwardTab = function (params) { - var param = params[0] || 1; - while (param--) { - this._terminal.buffer.x = this._terminal.buffer.nextStop(); - } - }; - InputHandler.prototype.eraseInDisplay = function (params) { - var j; - switch (params[0]) { - case 0: - this._terminal.eraseRight(this._terminal.buffer.x, this._terminal.buffer.y); - j = this._terminal.buffer.y + 1; - for (; j < this._terminal.rows; j++) { - this._terminal.eraseLine(j); - } - break; - case 1: - this._terminal.eraseLeft(this._terminal.buffer.x, this._terminal.buffer.y); - j = this._terminal.buffer.y; - while (j--) { - this._terminal.eraseLine(j); - } - break; - case 2: - j = this._terminal.rows; - while (j--) - this._terminal.eraseLine(j); - break; - case 3: - var scrollBackSize = this._terminal.buffer.lines.length - this._terminal.rows; - if (scrollBackSize > 0) { - this._terminal.buffer.lines.trimStart(scrollBackSize); - this._terminal.buffer.ybase = Math.max(this._terminal.buffer.ybase - scrollBackSize, 0); - this._terminal.buffer.ydisp = Math.max(this._terminal.buffer.ydisp - scrollBackSize, 0); - this._terminal.emit('scroll', 0); - } - break; - } - }; - InputHandler.prototype.eraseInLine = function (params) { - switch (params[0]) { - case 0: - this._terminal.eraseRight(this._terminal.buffer.x, this._terminal.buffer.y); - break; - case 1: - this._terminal.eraseLeft(this._terminal.buffer.x, this._terminal.buffer.y); - break; - case 2: - this._terminal.eraseLine(this._terminal.buffer.y); - break; - } - }; - InputHandler.prototype.insertLines = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - var row = this._terminal.buffer.y + this._terminal.buffer.ybase; - var scrollBottomRowsOffset = this._terminal.rows - 1 - this._terminal.buffer.scrollBottom; - var scrollBottomAbsolute = this._terminal.rows - 1 + this._terminal.buffer.ybase - scrollBottomRowsOffset + 1; - while (param--) { - this._terminal.buffer.lines.splice(scrollBottomAbsolute - 1, 1); - this._terminal.buffer.lines.splice(row, 0, this._terminal.blankLine(true)); - } - this._terminal.updateRange(this._terminal.buffer.y); - this._terminal.updateRange(this._terminal.buffer.scrollBottom); - }; - InputHandler.prototype.deleteLines = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - var row = this._terminal.buffer.y + this._terminal.buffer.ybase; - var j; - j = this._terminal.rows - 1 - this._terminal.buffer.scrollBottom; - j = this._terminal.rows - 1 + this._terminal.buffer.ybase - j; - while (param--) { - this._terminal.buffer.lines.splice(row, 1); - this._terminal.buffer.lines.splice(j, 0, this._terminal.blankLine(true)); - } - this._terminal.updateRange(this._terminal.buffer.y); - this._terminal.updateRange(this._terminal.buffer.scrollBottom); - }; - InputHandler.prototype.deleteChars = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - var row = this._terminal.buffer.y + this._terminal.buffer.ybase; - var ch = [this._terminal.eraseAttr(), ' ', 1, 32]; - while (param--) { - this._terminal.buffer.lines.get(row).splice(this._terminal.buffer.x, 1); - this._terminal.buffer.lines.get(row).push(ch); - } - this._terminal.updateRange(this._terminal.buffer.y); - }; - InputHandler.prototype.scrollUp = function (params) { - var param = params[0] || 1; - while (param--) { - this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollTop, 1); - this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollBottom, 0, this._terminal.blankLine()); - } - this._terminal.updateRange(this._terminal.buffer.scrollTop); - this._terminal.updateRange(this._terminal.buffer.scrollBottom); - }; - InputHandler.prototype.scrollDown = function (params) { - var param = params[0] || 1; - while (param--) { - this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollBottom, 1); - this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollTop, 0, this._terminal.blankLine()); - } - this._terminal.updateRange(this._terminal.buffer.scrollTop); - this._terminal.updateRange(this._terminal.buffer.scrollBottom); - }; - InputHandler.prototype.eraseChars = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - var row = this._terminal.buffer.y + this._terminal.buffer.ybase; - var j = this._terminal.buffer.x; - var ch = [this._terminal.eraseAttr(), ' ', 1, 32]; - while (param-- && j < this._terminal.cols) { - this._terminal.buffer.lines.get(row)[j++] = ch; - } - }; - InputHandler.prototype.cursorBackwardTab = function (params) { - var param = params[0] || 1; - while (param--) { - this._terminal.buffer.x = this._terminal.buffer.prevStop(); - } - }; - InputHandler.prototype.charPosAbsolute = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.x = param - 1; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } - }; - InputHandler.prototype.HPositionRelative = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.x += param; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } - }; - InputHandler.prototype.repeatPrecedingCharacter = function (params) { - var param = params[0] || 1; - var line = this._terminal.buffer.lines.get(this._terminal.buffer.ybase + this._terminal.buffer.y); - var ch = line[this._terminal.buffer.x - 1] || [this._terminal.defAttr, ' ', 1, 32]; - while (param--) { - line[this._terminal.buffer.x++] = ch; - } - }; - InputHandler.prototype.sendDeviceAttributes = function (params) { - if (params[0] > 0) { - return; - } - if (!this._terminal.prefix) { - if (this._terminal.is('xterm') || this._terminal.is('rxvt-unicode') || this._terminal.is('screen')) { - this._terminal.send(EscapeSequences_1.C0.ESC + '[?1;2c'); - } - else if (this._terminal.is('linux')) { - this._terminal.send(EscapeSequences_1.C0.ESC + '[?6c'); - } - } - else if (this._terminal.prefix === '>') { - if (this._terminal.is('xterm')) { - this._terminal.send(EscapeSequences_1.C0.ESC + '[>0;276;0c'); - } - else if (this._terminal.is('rxvt-unicode')) { - this._terminal.send(EscapeSequences_1.C0.ESC + '[>85;95;0c'); - } - else if (this._terminal.is('linux')) { - this._terminal.send(params[0] + 'c'); - } - else if (this._terminal.is('screen')) { - this._terminal.send(EscapeSequences_1.C0.ESC + '[>83;40003;0c'); - } - } - }; - InputHandler.prototype.linePosAbsolute = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.y = param - 1; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - }; - InputHandler.prototype.VPositionRelative = function (params) { - var param = params[0]; - if (param < 1) { - param = 1; - } - this._terminal.buffer.y += param; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x--; - } - }; - InputHandler.prototype.HVPosition = function (params) { - if (params[0] < 1) - params[0] = 1; - if (params[1] < 1) - params[1] = 1; - this._terminal.buffer.y = params[0] - 1; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - this._terminal.buffer.x = params[1] - 1; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } - }; - InputHandler.prototype.tabClear = function (params) { - var param = params[0]; - if (param <= 0) { - delete this._terminal.buffer.tabs[this._terminal.buffer.x]; - } - else if (param === 3) { - this._terminal.buffer.tabs = {}; - } - }; - InputHandler.prototype.setMode = function (params) { - if (params.length > 1) { - for (var i = 0; i < params.length; i++) { - this.setMode([params[i]]); - } - return; - } - if (!this._terminal.prefix) { - switch (params[0]) { - case 4: - this._terminal.insertMode = true; - break; - case 20: - break; - } - } - else if (this._terminal.prefix === '?') { - switch (params[0]) { - case 1: - this._terminal.applicationCursor = true; - break; - case 2: - this._terminal.setgCharset(0, Charsets_1.DEFAULT_CHARSET); - this._terminal.setgCharset(1, Charsets_1.DEFAULT_CHARSET); - this._terminal.setgCharset(2, Charsets_1.DEFAULT_CHARSET); - this._terminal.setgCharset(3, Charsets_1.DEFAULT_CHARSET); - break; - case 3: - this._terminal.savedCols = this._terminal.cols; - this._terminal.resize(132, this._terminal.rows); - break; - case 6: - this._terminal.originMode = true; - break; - case 7: - this._terminal.wraparoundMode = true; - break; - case 12: - break; - case 66: - this._terminal.log('Serial port requested application keypad.'); - this._terminal.applicationKeypad = true; - this._terminal.viewport.syncScrollArea(); - break; - case 9: - case 1000: - case 1002: - case 1003: - this._terminal.x10Mouse = params[0] === 9; - this._terminal.vt200Mouse = params[0] === 1000; - this._terminal.normalMouse = params[0] > 1000; - this._terminal.mouseEvents = true; - this._terminal.element.classList.add('enable-mouse-events'); - this._terminal.selectionManager.disable(); - this._terminal.log('Binding to mouse events.'); - break; - case 1004: - this._terminal.sendFocus = true; - break; - case 1005: - this._terminal.utfMouse = true; - break; - case 1006: - this._terminal.sgrMouse = true; - break; - case 1015: - this._terminal.urxvtMouse = true; - break; - case 25: - this._terminal.cursorHidden = false; - break; - case 1049: - case 47: - case 1047: - this._terminal.buffers.activateAltBuffer(); - this._terminal.selectionManager.setBuffer(this._terminal.buffer); - this._terminal.viewport.syncScrollArea(); - this._terminal.showCursor(); - break; - case 2004: - this._terminal.bracketedPasteMode = true; - break; - } - } - }; - InputHandler.prototype.resetMode = function (params) { - if (params.length > 1) { - for (var i = 0; i < params.length; i++) { - this.resetMode([params[i]]); - } - return; - } - if (!this._terminal.prefix) { - switch (params[0]) { - case 4: - this._terminal.insertMode = false; - break; - case 20: - break; - } - } - else if (this._terminal.prefix === '?') { - switch (params[0]) { - case 1: - this._terminal.applicationCursor = false; - break; - case 3: - if (this._terminal.cols === 132 && this._terminal.savedCols) { - this._terminal.resize(this._terminal.savedCols, this._terminal.rows); - } - delete this._terminal.savedCols; - break; - case 6: - this._terminal.originMode = false; - break; - case 7: - this._terminal.wraparoundMode = false; - break; - case 12: - break; - case 66: - this._terminal.log('Switching back to normal keypad.'); - this._terminal.applicationKeypad = false; - this._terminal.viewport.syncScrollArea(); - break; - case 9: - case 1000: - case 1002: - case 1003: - this._terminal.x10Mouse = false; - this._terminal.vt200Mouse = false; - this._terminal.normalMouse = false; - this._terminal.mouseEvents = false; - this._terminal.element.classList.remove('enable-mouse-events'); - this._terminal.selectionManager.enable(); - break; - case 1004: - this._terminal.sendFocus = false; - break; - case 1005: - this._terminal.utfMouse = false; - break; - case 1006: - this._terminal.sgrMouse = false; - break; - case 1015: - this._terminal.urxvtMouse = false; - break; - case 25: - this._terminal.cursorHidden = true; - break; - case 1049: - case 47: - case 1047: - this._terminal.buffers.activateNormalBuffer(); - this._terminal.selectionManager.setBuffer(this._terminal.buffer); - this._terminal.refresh(0, this._terminal.rows - 1); - this._terminal.viewport.syncScrollArea(); - this._terminal.showCursor(); - break; - case 2004: - this._terminal.bracketedPasteMode = false; - break; - } - } - }; - InputHandler.prototype.charAttributes = function (params) { - if (params.length === 1 && params[0] === 0) { - this._terminal.curAttr = this._terminal.defAttr; - return; - } - var l = params.length; - var flags = this._terminal.curAttr >> 18; - var fg = (this._terminal.curAttr >> 9) & 0x1ff; - var bg = this._terminal.curAttr & 0x1ff; - var p; - for (var i = 0; i < l; i++) { - p = params[i]; - if (p >= 30 && p <= 37) { - fg = p - 30; - } - else if (p >= 40 && p <= 47) { - bg = p - 40; - } - else if (p >= 90 && p <= 97) { - p += 8; - fg = p - 90; - } - else if (p >= 100 && p <= 107) { - p += 8; - bg = p - 100; - } - else if (p === 0) { - flags = this._terminal.defAttr >> 18; - fg = (this._terminal.defAttr >> 9) & 0x1ff; - bg = this._terminal.defAttr & 0x1ff; - } - else if (p === 1) { - flags |= Types_1.FLAGS.BOLD; - } - else if (p === 4) { - flags |= Types_1.FLAGS.UNDERLINE; - } - else if (p === 5) { - flags |= Types_1.FLAGS.BLINK; - } - else if (p === 7) { - flags |= Types_1.FLAGS.INVERSE; - } - else if (p === 8) { - flags |= Types_1.FLAGS.INVISIBLE; - } - else if (p === 2) { - flags |= Types_1.FLAGS.DIM; - } - else if (p === 22) { - flags &= ~Types_1.FLAGS.BOLD; - flags &= ~Types_1.FLAGS.DIM; - } - else if (p === 24) { - flags &= ~Types_1.FLAGS.UNDERLINE; - } - else if (p === 25) { - flags &= ~Types_1.FLAGS.BLINK; - } - else if (p === 27) { - flags &= ~Types_1.FLAGS.INVERSE; - } - else if (p === 28) { - flags &= ~Types_1.FLAGS.INVISIBLE; - } - else if (p === 39) { - fg = (this._terminal.defAttr >> 9) & 0x1ff; - } - else if (p === 49) { - bg = this._terminal.defAttr & 0x1ff; - } - else if (p === 38) { - if (params[i + 1] === 2) { - i += 2; - fg = this._terminal.matchColor(params[i] & 0xff, params[i + 1] & 0xff, params[i + 2] & 0xff); - if (fg === -1) - fg = 0x1ff; - i += 2; - } - else if (params[i + 1] === 5) { - i += 2; - p = params[i] & 0xff; - fg = p; - } - } - else if (p === 48) { - if (params[i + 1] === 2) { - i += 2; - bg = this._terminal.matchColor(params[i] & 0xff, params[i + 1] & 0xff, params[i + 2] & 0xff); - if (bg === -1) - bg = 0x1ff; - i += 2; - } - else if (params[i + 1] === 5) { - i += 2; - p = params[i] & 0xff; - bg = p; - } - } - else if (p === 100) { - fg = (this._terminal.defAttr >> 9) & 0x1ff; - bg = this._terminal.defAttr & 0x1ff; - } - else { - this._terminal.error('Unknown SGR attribute: %d.', p); - } - } - this._terminal.curAttr = (flags << 18) | (fg << 9) | bg; - }; - InputHandler.prototype.deviceStatus = function (params) { - if (!this._terminal.prefix) { - switch (params[0]) { - case 5: - this._terminal.send(EscapeSequences_1.C0.ESC + '[0n'); - break; - case 6: - this._terminal.send(EscapeSequences_1.C0.ESC + '[' - + (this._terminal.buffer.y + 1) - + ';' - + (this._terminal.buffer.x + 1) - + 'R'); - break; - } - } - else if (this._terminal.prefix === '?') { - switch (params[0]) { - case 6: - this._terminal.send(EscapeSequences_1.C0.ESC + '[?' - + (this._terminal.buffer.y + 1) - + ';' - + (this._terminal.buffer.x + 1) - + 'R'); - break; - case 15: - break; - case 25: - break; - case 26: - break; - case 53: - break; - } - } - }; - InputHandler.prototype.softReset = function (params) { - this._terminal.cursorHidden = false; - this._terminal.insertMode = false; - this._terminal.originMode = false; - this._terminal.wraparoundMode = true; - this._terminal.applicationKeypad = false; - this._terminal.viewport.syncScrollArea(); - this._terminal.applicationCursor = false; - this._terminal.buffer.scrollTop = 0; - this._terminal.buffer.scrollBottom = this._terminal.rows - 1; - this._terminal.curAttr = this._terminal.defAttr; - this._terminal.buffer.x = this._terminal.buffer.y = 0; - this._terminal.charset = null; - this._terminal.glevel = 0; - this._terminal.charsets = [null]; - }; - InputHandler.prototype.setCursorStyle = function (params) { - var param = params[0] < 1 ? 1 : params[0]; - switch (param) { - case 1: - case 2: - this._terminal.setOption('cursorStyle', 'block'); - break; - case 3: - case 4: - this._terminal.setOption('cursorStyle', 'underline'); - break; - case 5: - case 6: - this._terminal.setOption('cursorStyle', 'bar'); - break; - } - var isBlinking = param % 2 === 1; - this._terminal.setOption('cursorBlink', isBlinking); - }; - InputHandler.prototype.setScrollRegion = function (params) { - if (this._terminal.prefix) - return; - this._terminal.buffer.scrollTop = (params[0] || 1) - 1; - this._terminal.buffer.scrollBottom = (params[1] && params[1] <= this._terminal.rows ? params[1] : this._terminal.rows) - 1; - this._terminal.buffer.x = 0; - this._terminal.buffer.y = 0; - }; - InputHandler.prototype.saveCursor = function (params) { - this._terminal.buffer.savedX = this._terminal.buffer.x; - this._terminal.buffer.savedY = this._terminal.buffer.y; - }; - InputHandler.prototype.restoreCursor = function (params) { - this._terminal.buffer.x = this._terminal.buffer.savedX || 0; - this._terminal.buffer.y = this._terminal.buffer.savedY || 0; - }; - return InputHandler; -}()); -exports.InputHandler = InputHandler; - - - -},{"./Buffer":1,"./CharWidth":3,"./Charsets":4,"./EscapeSequences":6,"./renderer/Types":27}],9:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var Types_1 = require("./Types"); -var MouseZoneManager_1 = require("./input/MouseZoneManager"); -var EventEmitter_1 = require("./EventEmitter"); -var protocolClause = '(https?:\\/\\/)'; -var domainCharacterSet = '[\\da-z\\.-]+'; -var negatedDomainCharacterSet = '[^\\da-z\\.-]+'; -var domainBodyClause = '(' + domainCharacterSet + ')'; -var tldClause = '([a-z\\.]{2,6})'; -var ipClause = '((\\d{1,3}\\.){3}\\d{1,3})'; -var localHostClause = '(localhost)'; -var portClause = '(:\\d{1,5})'; -var hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?'; -var pathClause = '(\\/[\\/\\w\\.\\-%~]*)*'; -var queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;~\\=\\.\\-]*'; -var queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?'; -var hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?'; -var negatedPathCharacterSet = '[^\\/\\w\\.\\-%]+'; -var bodyClause = hostClause + pathClause + queryStringClause + hashFragmentClause; -var start = '(?:^|' + negatedDomainCharacterSet + ')('; -var end = ')($|' + negatedPathCharacterSet + ')'; -var strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end); -var HYPERTEXT_LINK_MATCHER_ID = 0; -var Linkifier = (function (_super) { - __extends(Linkifier, _super); - function Linkifier(_terminal) { - var _this = _super.call(this) || this; - _this._terminal = _terminal; - _this._linkMatchers = []; - _this._nextLinkMatcherId = HYPERTEXT_LINK_MATCHER_ID; - _this._rowsToLinkify = { - start: null, - end: null - }; - _this.registerLinkMatcher(strictUrlRegex, null, { matchIndex: 1 }); - return _this; - } - Linkifier.prototype.attachToDom = function (mouseZoneManager) { - this._mouseZoneManager = mouseZoneManager; - }; - Linkifier.prototype.linkifyRows = function (start, end) { - var _this = this; - if (!this._mouseZoneManager) { - return; - } - if (!this._rowsToLinkify.start) { - this._rowsToLinkify.start = start; - this._rowsToLinkify.end = end; - } - else { - this._rowsToLinkify.start = this._rowsToLinkify.start < start ? this._rowsToLinkify.start : start; - this._rowsToLinkify.end = this._rowsToLinkify.end > end ? this._rowsToLinkify.end : end; - } - this._mouseZoneManager.clearAll(start, end); - if (this._rowsTimeoutId) { - clearTimeout(this._rowsTimeoutId); - } - this._rowsTimeoutId = setTimeout(function () { return _this._linkifyRows(); }, Linkifier.TIME_BEFORE_LINKIFY); - }; - Linkifier.prototype._linkifyRows = function () { - this._rowsTimeoutId = null; - for (var i = this._rowsToLinkify.start; i <= this._rowsToLinkify.end; i++) { - this._linkifyRow(i); - } - this._rowsToLinkify.start = null; - this._rowsToLinkify.end = null; - }; - Linkifier.prototype.setHypertextLinkHandler = function (handler) { - this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].handler = handler; - }; - Linkifier.prototype.setHypertextValidationCallback = function (callback) { - this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].validationCallback = callback; - }; - Linkifier.prototype.registerLinkMatcher = function (regex, handler, options) { - if (options === void 0) { options = {}; } - if (this._nextLinkMatcherId !== HYPERTEXT_LINK_MATCHER_ID && !handler) { - throw new Error('handler must be defined'); - } - var matcher = { - id: this._nextLinkMatcherId++, - regex: regex, - handler: handler, - matchIndex: options.matchIndex, - validationCallback: options.validationCallback, - hoverTooltipCallback: options.tooltipCallback, - hoverLeaveCallback: options.leaveCallback, - priority: options.priority || 0 - }; - this._addLinkMatcherToList(matcher); - return matcher.id; - }; - Linkifier.prototype._addLinkMatcherToList = function (matcher) { - if (this._linkMatchers.length === 0) { - this._linkMatchers.push(matcher); - return; - } - for (var i = this._linkMatchers.length - 1; i >= 0; i--) { - if (matcher.priority <= this._linkMatchers[i].priority) { - this._linkMatchers.splice(i + 1, 0, matcher); - return; - } - } - this._linkMatchers.splice(0, 0, matcher); - }; - Linkifier.prototype.deregisterLinkMatcher = function (matcherId) { - for (var i = 1; i < this._linkMatchers.length; i++) { - if (this._linkMatchers[i].id === matcherId) { - this._linkMatchers.splice(i, 1); - return true; - } - } - return false; - }; - Linkifier.prototype._linkifyRow = function (rowIndex) { - var absoluteRowIndex = this._terminal.buffer.ydisp + rowIndex; - if (absoluteRowIndex >= this._terminal.buffer.lines.length) { - return; - } - var text = this._terminal.buffer.translateBufferLineToString(absoluteRowIndex, false); - for (var i = 0; i < this._linkMatchers.length; i++) { - this._doLinkifyRow(rowIndex, text, this._linkMatchers[i]); - } - }; - Linkifier.prototype._doLinkifyRow = function (rowIndex, text, matcher, offset) { - var _this = this; - if (offset === void 0) { offset = 0; } - var result = []; - var isHttpLinkMatcher = matcher.id === HYPERTEXT_LINK_MATCHER_ID; - var match = text.match(matcher.regex); - if (!match || match.length === 0) { - return; - } - var uri = match[typeof matcher.matchIndex !== 'number' ? 0 : matcher.matchIndex]; - var index = text.indexOf(uri); - if (matcher.validationCallback) { - matcher.validationCallback(uri, function (isValid) { - if (_this._rowsTimeoutId) { - return; - } - if (isValid) { - _this._addLink(offset + index, rowIndex, uri, matcher); - } - }); - } - else { - this._addLink(offset + index, rowIndex, uri, matcher); - } - var remainingStartIndex = index + uri.length; - var remainingText = text.substr(remainingStartIndex); - if (remainingText.length > 0) { - this._doLinkifyRow(rowIndex, remainingText, matcher, offset + remainingStartIndex); - } - }; - Linkifier.prototype._addLink = function (x, y, uri, matcher) { - var _this = this; - this._mouseZoneManager.add(new MouseZoneManager_1.MouseZone(x + 1, x + 1 + uri.length, y + 1, function (e) { - if (matcher.handler) { - return matcher.handler(e, uri); - } - window.open(uri, '_blank'); - }, function (e) { - _this.emit(Types_1.LinkHoverEventTypes.HOVER, { x: x, y: y, length: uri.length }); - _this._terminal.element.style.cursor = 'pointer'; - }, function (e) { - _this.emit(Types_1.LinkHoverEventTypes.TOOLTIP, { x: x, y: y, length: uri.length }); - if (matcher.hoverTooltipCallback) { - matcher.hoverTooltipCallback(e, uri); - } - }, function () { - _this.emit(Types_1.LinkHoverEventTypes.LEAVE, { x: x, y: y, length: uri.length }); - _this._terminal.element.style.cursor = ''; - if (matcher.hoverLeaveCallback) { - matcher.hoverLeaveCallback(); - } - })); - }; - Linkifier.TIME_BEFORE_LINKIFY = 200; - return Linkifier; -}(EventEmitter_1.EventEmitter)); -exports.Linkifier = Linkifier; - - - -},{"./EventEmitter":7,"./Types":14,"./input/MouseZoneManager":17}],10:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var EscapeSequences_1 = require("./EscapeSequences"); -var Charsets_1 = require("./Charsets"); -var normalStateHandler = {}; -normalStateHandler[EscapeSequences_1.C0.BEL] = function (parser, handler) { return handler.bell(); }; -normalStateHandler[EscapeSequences_1.C0.LF] = function (parser, handler) { return handler.lineFeed(); }; -normalStateHandler[EscapeSequences_1.C0.VT] = normalStateHandler[EscapeSequences_1.C0.LF]; -normalStateHandler[EscapeSequences_1.C0.FF] = normalStateHandler[EscapeSequences_1.C0.LF]; -normalStateHandler[EscapeSequences_1.C0.CR] = function (parser, handler) { return handler.carriageReturn(); }; -normalStateHandler[EscapeSequences_1.C0.BS] = function (parser, handler) { return handler.backspace(); }; -normalStateHandler[EscapeSequences_1.C0.HT] = function (parser, handler) { return handler.tab(); }; -normalStateHandler[EscapeSequences_1.C0.SO] = function (parser, handler) { return handler.shiftOut(); }; -normalStateHandler[EscapeSequences_1.C0.SI] = function (parser, handler) { return handler.shiftIn(); }; -normalStateHandler[EscapeSequences_1.C0.ESC] = function (parser, handler) { return parser.setState(ParserState.ESCAPED); }; -var escapedStateHandler = {}; -escapedStateHandler['['] = function (parser, terminal) { - terminal.params = []; - terminal.currentParam = 0; - parser.setState(ParserState.CSI_PARAM); -}; -escapedStateHandler[']'] = function (parser, terminal) { - terminal.params = []; - terminal.currentParam = 0; - parser.setState(ParserState.OSC); -}; -escapedStateHandler['P'] = function (parser, terminal) { - terminal.params = []; - terminal.currentParam = 0; - parser.setState(ParserState.DCS); -}; -escapedStateHandler['_'] = function (parser, terminal) { - parser.setState(ParserState.IGNORE); -}; -escapedStateHandler['^'] = function (parser, terminal) { - parser.setState(ParserState.IGNORE); -}; -escapedStateHandler['c'] = function (parser, terminal) { - terminal.reset(); -}; -escapedStateHandler['E'] = function (parser, terminal) { - terminal.buffer.x = 0; - terminal.index(); - parser.setState(ParserState.NORMAL); -}; -escapedStateHandler['D'] = function (parser, terminal) { - terminal.index(); - parser.setState(ParserState.NORMAL); -}; -escapedStateHandler['M'] = function (parser, terminal) { - terminal.reverseIndex(); - parser.setState(ParserState.NORMAL); -}; -escapedStateHandler['%'] = function (parser, terminal) { - terminal.setgLevel(0); - terminal.setgCharset(0, Charsets_1.DEFAULT_CHARSET); - parser.setState(ParserState.NORMAL); - parser.skipNextChar(); -}; -escapedStateHandler[EscapeSequences_1.C0.CAN] = function (parser) { return parser.setState(ParserState.NORMAL); }; -var csiParamStateHandler = {}; -csiParamStateHandler['?'] = function (parser) { return parser.setPrefix('?'); }; -csiParamStateHandler['>'] = function (parser) { return parser.setPrefix('>'); }; -csiParamStateHandler['!'] = function (parser) { return parser.setPrefix('!'); }; -csiParamStateHandler['0'] = function (parser) { return parser.setParam(parser.getParam() * 10); }; -csiParamStateHandler['1'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 1); }; -csiParamStateHandler['2'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 2); }; -csiParamStateHandler['3'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 3); }; -csiParamStateHandler['4'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 4); }; -csiParamStateHandler['5'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 5); }; -csiParamStateHandler['6'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 6); }; -csiParamStateHandler['7'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 7); }; -csiParamStateHandler['8'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 8); }; -csiParamStateHandler['9'] = function (parser) { return parser.setParam(parser.getParam() * 10 + 9); }; -csiParamStateHandler['$'] = function (parser) { return parser.setPostfix('$'); }; -csiParamStateHandler['"'] = function (parser) { return parser.setPostfix('"'); }; -csiParamStateHandler[' '] = function (parser) { return parser.setPostfix(' '); }; -csiParamStateHandler['\''] = function (parser) { return parser.setPostfix('\''); }; -csiParamStateHandler[';'] = function (parser) { return parser.finalizeParam(); }; -csiParamStateHandler[EscapeSequences_1.C0.CAN] = function (parser) { return parser.setState(ParserState.NORMAL); }; -var csiStateHandler = {}; -csiStateHandler['@'] = function (handler, params, prefix) { return handler.insertChars(params); }; -csiStateHandler['A'] = function (handler, params, prefix) { return handler.cursorUp(params); }; -csiStateHandler['B'] = function (handler, params, prefix) { return handler.cursorDown(params); }; -csiStateHandler['C'] = function (handler, params, prefix) { return handler.cursorForward(params); }; -csiStateHandler['D'] = function (handler, params, prefix) { return handler.cursorBackward(params); }; -csiStateHandler['E'] = function (handler, params, prefix) { return handler.cursorNextLine(params); }; -csiStateHandler['F'] = function (handler, params, prefix) { return handler.cursorPrecedingLine(params); }; -csiStateHandler['G'] = function (handler, params, prefix) { return handler.cursorCharAbsolute(params); }; -csiStateHandler['H'] = function (handler, params, prefix) { return handler.cursorPosition(params); }; -csiStateHandler['I'] = function (handler, params, prefix) { return handler.cursorForwardTab(params); }; -csiStateHandler['J'] = function (handler, params, prefix) { return handler.eraseInDisplay(params); }; -csiStateHandler['K'] = function (handler, params, prefix) { return handler.eraseInLine(params); }; -csiStateHandler['L'] = function (handler, params, prefix) { return handler.insertLines(params); }; -csiStateHandler['M'] = function (handler, params, prefix) { return handler.deleteLines(params); }; -csiStateHandler['P'] = function (handler, params, prefix) { return handler.deleteChars(params); }; -csiStateHandler['S'] = function (handler, params, prefix) { return handler.scrollUp(params); }; -csiStateHandler['T'] = function (handler, params, prefix) { - if (params.length < 2 && !prefix) { - handler.scrollDown(params); - } -}; -csiStateHandler['X'] = function (handler, params, prefix) { return handler.eraseChars(params); }; -csiStateHandler['Z'] = function (handler, params, prefix) { return handler.cursorBackwardTab(params); }; -csiStateHandler['`'] = function (handler, params, prefix) { return handler.charPosAbsolute(params); }; -csiStateHandler['a'] = function (handler, params, prefix) { return handler.HPositionRelative(params); }; -csiStateHandler['b'] = function (handler, params, prefix) { return handler.repeatPrecedingCharacter(params); }; -csiStateHandler['c'] = function (handler, params, prefix) { return handler.sendDeviceAttributes(params); }; -csiStateHandler['d'] = function (handler, params, prefix) { return handler.linePosAbsolute(params); }; -csiStateHandler['e'] = function (handler, params, prefix) { return handler.VPositionRelative(params); }; -csiStateHandler['f'] = function (handler, params, prefix) { return handler.HVPosition(params); }; -csiStateHandler['g'] = function (handler, params, prefix) { return handler.tabClear(params); }; -csiStateHandler['h'] = function (handler, params, prefix) { return handler.setMode(params); }; -csiStateHandler['l'] = function (handler, params, prefix) { return handler.resetMode(params); }; -csiStateHandler['m'] = function (handler, params, prefix) { return handler.charAttributes(params); }; -csiStateHandler['n'] = function (handler, params, prefix) { return handler.deviceStatus(params); }; -csiStateHandler['p'] = function (handler, params, prefix) { - switch (prefix) { - case '!': - handler.softReset(params); - break; - } -}; -csiStateHandler['q'] = function (handler, params, prefix, postfix) { - if (postfix === ' ') { - handler.setCursorStyle(params); - } -}; -csiStateHandler['r'] = function (handler, params) { return handler.setScrollRegion(params); }; -csiStateHandler['s'] = function (handler, params) { return handler.saveCursor(params); }; -csiStateHandler['u'] = function (handler, params) { return handler.restoreCursor(params); }; -csiStateHandler[EscapeSequences_1.C0.CAN] = function (handler, params, prefix, postfix, parser) { return parser.setState(ParserState.NORMAL); }; -var ParserState; -(function (ParserState) { - ParserState[ParserState["NORMAL"] = 0] = "NORMAL"; - ParserState[ParserState["ESCAPED"] = 1] = "ESCAPED"; - ParserState[ParserState["CSI_PARAM"] = 2] = "CSI_PARAM"; - ParserState[ParserState["CSI"] = 3] = "CSI"; - ParserState[ParserState["OSC"] = 4] = "OSC"; - ParserState[ParserState["CHARSET"] = 5] = "CHARSET"; - ParserState[ParserState["DCS"] = 6] = "DCS"; - ParserState[ParserState["IGNORE"] = 7] = "IGNORE"; -})(ParserState = exports.ParserState || (exports.ParserState = {})); -var Parser = (function () { - function Parser(_inputHandler, _terminal) { - this._inputHandler = _inputHandler; - this._terminal = _terminal; - this._state = ParserState.NORMAL; - } - Parser.prototype.parse = function (data) { - var l = data.length; - var j; - var cs; - var ch; - var code; - var low; - var cursorStartX = this._terminal.buffer.x; - var cursorStartY = this._terminal.buffer.y; - if (this._terminal.debug) { - this._terminal.log('data: ' + data); - } - this._position = 0; - if (this._terminal.surrogate_high) { - data = this._terminal.surrogate_high + data; - this._terminal.surrogate_high = ''; - } - for (; this._position < l; this._position++) { - ch = data[this._position]; - code = data.charCodeAt(this._position); - if (0xD800 <= code && code <= 0xDBFF) { - low = data.charCodeAt(this._position + 1); - if (isNaN(low)) { - this._terminal.surrogate_high = ch; - continue; - } - code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; - ch += data.charAt(this._position + 1); - } - if (0xDC00 <= code && code <= 0xDFFF) - continue; - switch (this._state) { - case ParserState.NORMAL: - if (ch in normalStateHandler) { - normalStateHandler[ch](this, this._inputHandler); - } - else { - this._inputHandler.addChar(ch, code); - } - break; - case ParserState.ESCAPED: - if (ch in escapedStateHandler) { - escapedStateHandler[ch](this, this._terminal); - break; - } - switch (ch) { - case '(': - case ')': - case '*': - case '+': - case '-': - case '.': - switch (ch) { - case '(': - this._terminal.gcharset = 0; - break; - case ')': - this._terminal.gcharset = 1; - break; - case '*': - this._terminal.gcharset = 2; - break; - case '+': - this._terminal.gcharset = 3; - break; - case '-': - this._terminal.gcharset = 1; - break; - case '.': - this._terminal.gcharset = 2; - break; - } - this._state = ParserState.CHARSET; - break; - case '/': - this._terminal.gcharset = 3; - this._state = ParserState.CHARSET; - this._position--; - break; - case 'N': - break; - case 'O': - break; - case 'n': - this._terminal.setgLevel(2); - break; - case 'o': - this._terminal.setgLevel(3); - break; - case '|': - this._terminal.setgLevel(3); - break; - case '}': - this._terminal.setgLevel(2); - break; - case '~': - this._terminal.setgLevel(1); - break; - case '7': - this._inputHandler.saveCursor(); - this._state = ParserState.NORMAL; - break; - case '8': - this._inputHandler.restoreCursor(); - this._state = ParserState.NORMAL; - break; - case '#': - this._state = ParserState.NORMAL; - this._position++; - break; - case 'H': - this._terminal.tabSet(); - this._state = ParserState.NORMAL; - break; - case '=': - this._terminal.log('Serial port requested application keypad.'); - this._terminal.applicationKeypad = true; - if (this._terminal.viewport) { - this._terminal.viewport.syncScrollArea(); - } - this._state = ParserState.NORMAL; - break; - case '>': - this._terminal.log('Switching back to normal keypad.'); - this._terminal.applicationKeypad = false; - if (this._terminal.viewport) { - this._terminal.viewport.syncScrollArea(); - } - this._state = ParserState.NORMAL; - break; - default: - this._state = ParserState.NORMAL; - this._terminal.error('Unknown ESC control: %s.', ch); - break; - } - break; - case ParserState.CHARSET: - if (ch in Charsets_1.CHARSETS) { - cs = Charsets_1.CHARSETS[ch]; - if (ch === '/') { - this.skipNextChar(); - } - } - else { - cs = Charsets_1.DEFAULT_CHARSET; - } - this._terminal.setgCharset(this._terminal.gcharset, cs); - this._terminal.gcharset = null; - this._state = ParserState.NORMAL; - break; - case ParserState.OSC: - if (ch === EscapeSequences_1.C0.ESC || ch === EscapeSequences_1.C0.BEL) { - if (ch === EscapeSequences_1.C0.ESC) - this._position++; - this._terminal.params.push(this._terminal.currentParam); - switch (this._terminal.params[0]) { - case 0: - case 1: - case 2: - if (this._terminal.params[1]) { - this._terminal.title = this._terminal.params[1]; - this._terminal.handleTitle(this._terminal.title); - } - break; - case 3: - break; - case 4: - case 5: - break; - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - case 18: - case 19: - break; - case 46: - break; - case 50: - break; - case 51: - break; - case 52: - break; - case 104: - case 105: - case 110: - case 111: - case 112: - case 113: - case 114: - case 115: - case 116: - case 117: - case 118: - break; - } - this._terminal.params = []; - this._terminal.currentParam = 0; - this._state = ParserState.NORMAL; - } - else { - if (!this._terminal.params.length) { - if (ch >= '0' && ch <= '9') { - this._terminal.currentParam = - this._terminal.currentParam * 10 + ch.charCodeAt(0) - 48; - } - else if (ch === ';') { - this._terminal.params.push(this._terminal.currentParam); - this._terminal.currentParam = ''; - } - } - else { - this._terminal.currentParam += ch; - } - } - break; - case ParserState.CSI_PARAM: - if (ch in csiParamStateHandler) { - csiParamStateHandler[ch](this); - break; - } - this.finalizeParam(); - this._state = ParserState.CSI; - case ParserState.CSI: - if (ch in csiStateHandler) { - if (this._terminal.debug) { - this._terminal.log("CSI " + (this._terminal.prefix ? this._terminal.prefix : '') + " " + (this._terminal.params ? this._terminal.params.join(';') : '') + " " + (this._terminal.postfix ? this._terminal.postfix : '') + " " + ch); - } - csiStateHandler[ch](this._inputHandler, this._terminal.params, this._terminal.prefix, this._terminal.postfix, this); - } - else { - this._terminal.error('Unknown CSI code: %s.', ch); - } - this._state = ParserState.NORMAL; - this._terminal.prefix = ''; - this._terminal.postfix = ''; - break; - case ParserState.DCS: - if (ch === EscapeSequences_1.C0.ESC || ch === EscapeSequences_1.C0.BEL) { - if (ch === EscapeSequences_1.C0.ESC) - this._position++; - var pt = void 0; - var valid = void 0; - switch (this._terminal.prefix) { - case '': - break; - case '$q': - pt = this._terminal.currentParam; - valid = false; - switch (pt) { - case '"q': - pt = '0"q'; - break; - case '"p': - pt = '61"p'; - break; - case 'r': - pt = '' - + (this._terminal.buffer.scrollTop + 1) - + ';' - + (this._terminal.buffer.scrollBottom + 1) - + 'r'; - break; - case 'm': - pt = '0m'; - break; - default: - this._terminal.error('Unknown DCS Pt: %s.', pt); - pt = ''; - break; - } - this._terminal.send(EscapeSequences_1.C0.ESC + 'P' + +valid + '$r' + pt + EscapeSequences_1.C0.ESC + '\\'); - break; - case '+p': - break; - case '+q': - pt = this._terminal.currentParam; - valid = false; - this._terminal.send(EscapeSequences_1.C0.ESC + 'P' + +valid + '+r' + pt + EscapeSequences_1.C0.ESC + '\\'); - break; - default: - this._terminal.error('Unknown DCS prefix: %s.', this._terminal.prefix); - break; - } - this._terminal.currentParam = 0; - this._terminal.prefix = ''; - this._state = ParserState.NORMAL; - } - else if (!this._terminal.currentParam) { - if (!this._terminal.prefix && ch !== '$' && ch !== '+') { - this._terminal.currentParam = ch; - } - else if (this._terminal.prefix.length === 2) { - this._terminal.currentParam = ch; - } - else { - this._terminal.prefix += ch; - } - } - else { - this._terminal.currentParam += ch; - } - break; - case ParserState.IGNORE: - if (ch === EscapeSequences_1.C0.ESC || ch === EscapeSequences_1.C0.BEL) { - if (ch === EscapeSequences_1.C0.ESC) - this._position++; - this._state = ParserState.NORMAL; - } - break; - } - } - if (this._terminal.buffer.x !== cursorStartX || this._terminal.buffer.y !== cursorStartY) { - this._terminal.emit('cursormove'); - } - return this._state; - }; - Parser.prototype.setState = function (state) { - this._state = state; - }; - Parser.prototype.setPrefix = function (prefix) { - this._terminal.prefix = prefix; - }; - Parser.prototype.setPostfix = function (postfix) { - this._terminal.postfix = postfix; - }; - Parser.prototype.setParam = function (param) { - this._terminal.currentParam = param; - }; - Parser.prototype.getParam = function () { - return this._terminal.currentParam; - }; - Parser.prototype.finalizeParam = function () { - this._terminal.params.push(this._terminal.currentParam); - this._terminal.currentParam = 0; - }; - Parser.prototype.skipNextChar = function () { - this._position++; - }; - return Parser; -}()); -exports.Parser = Parser; - - - -},{"./Charsets":4,"./EscapeSequences":6}],11:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var MouseHelper_1 = require("./utils/MouseHelper"); -var Browser = require("./utils/Browser"); -var EventEmitter_1 = require("./EventEmitter"); -var SelectionModel_1 = require("./SelectionModel"); -var Buffer_1 = require("./Buffer"); -var DRAG_SCROLL_MAX_THRESHOLD = 50; -var DRAG_SCROLL_MAX_SPEED = 15; -var DRAG_SCROLL_INTERVAL = 50; -var WORD_SEPARATORS = ' ()[]{}\'"'; -var NON_BREAKING_SPACE_CHAR = String.fromCharCode(160); -var ALL_NON_BREAKING_SPACE_REGEX = new RegExp(NON_BREAKING_SPACE_CHAR, 'g'); -var SelectionMode; -(function (SelectionMode) { - SelectionMode[SelectionMode["NORMAL"] = 0] = "NORMAL"; - SelectionMode[SelectionMode["WORD"] = 1] = "WORD"; - SelectionMode[SelectionMode["LINE"] = 2] = "LINE"; -})(SelectionMode || (SelectionMode = {})); -var SelectionManager = (function (_super) { - __extends(SelectionManager, _super); - function SelectionManager(_terminal, _buffer, _charMeasure) { - var _this = _super.call(this) || this; - _this._terminal = _terminal; - _this._buffer = _buffer; - _this._charMeasure = _charMeasure; - _this._enabled = true; - _this._initListeners(); - _this.enable(); - _this._model = new SelectionModel_1.SelectionModel(_terminal); - _this._activeSelectionMode = SelectionMode.NORMAL; - return _this; - } - SelectionManager.prototype._initListeners = function () { - var _this = this; - this._mouseMoveListener = function (event) { return _this._onMouseMove(event); }; - this._mouseUpListener = function (event) { return _this._onMouseUp(event); }; - this._buffer.lines.on('trim', function (amount) { return _this._onTrim(amount); }); - }; - SelectionManager.prototype.disable = function () { - this.clearSelection(); - this._enabled = false; - }; - SelectionManager.prototype.enable = function () { - this._enabled = true; - }; - SelectionManager.prototype.setBuffer = function (buffer) { - this._buffer = buffer; - this.clearSelection(); - }; - Object.defineProperty(SelectionManager.prototype, "selectionStart", { - get: function () { return this._model.finalSelectionStart; }, - enumerable: true, - configurable: true - }); - Object.defineProperty(SelectionManager.prototype, "selectionEnd", { - get: function () { return this._model.finalSelectionEnd; }, - enumerable: true, - configurable: true - }); - Object.defineProperty(SelectionManager.prototype, "hasSelection", { - get: function () { - var start = this._model.finalSelectionStart; - var end = this._model.finalSelectionEnd; - if (!start || !end) { - return false; - } - return start[0] !== end[0] || start[1] !== end[1]; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(SelectionManager.prototype, "selectionText", { - get: function () { - var start = this._model.finalSelectionStart; - var end = this._model.finalSelectionEnd; - if (!start || !end) { - return ''; - } - var startRowEndCol = start[1] === end[1] ? end[0] : null; - var result = []; - result.push(this._buffer.translateBufferLineToString(start[1], true, start[0], startRowEndCol)); - for (var i = start[1] + 1; i <= end[1] - 1; i++) { - var bufferLine = this._buffer.lines.get(i); - var lineText = this._buffer.translateBufferLineToString(i, true); - if (bufferLine.isWrapped) { - result[result.length - 1] += lineText; - } - else { - result.push(lineText); - } - } - if (start[1] !== end[1]) { - var bufferLine = this._buffer.lines.get(end[1]); - var lineText = this._buffer.translateBufferLineToString(end[1], true, 0, end[0]); - if (bufferLine.isWrapped) { - result[result.length - 1] += lineText; - } - else { - result.push(lineText); - } - } - var formattedResult = result.map(function (line) { - return line.replace(ALL_NON_BREAKING_SPACE_REGEX, ' '); - }).join(Browser.isMSWindows ? '\r\n' : '\n'); - return formattedResult; - }, - enumerable: true, - configurable: true - }); - SelectionManager.prototype.clearSelection = function () { - this._model.clearSelection(); - this._removeMouseDownListeners(); - this.refresh(); - }; - SelectionManager.prototype.refresh = function (isNewSelection) { - var _this = this; - if (!this._refreshAnimationFrame) { - this._refreshAnimationFrame = window.requestAnimationFrame(function () { return _this._refresh(); }); - } - if (Browser.isLinux && isNewSelection) { - var selectionText = this.selectionText; - if (selectionText.length) { - this.emit('newselection', this.selectionText); - } - } - }; - SelectionManager.prototype._refresh = function () { - this._refreshAnimationFrame = null; - this.emit('refresh', { start: this._model.finalSelectionStart, end: this._model.finalSelectionEnd }); - }; - SelectionManager.prototype.selectAll = function () { - this._model.isSelectAllActive = true; - this.refresh(); - this.emit('selection'); - }; - SelectionManager.prototype._onTrim = function (amount) { - var needsRefresh = this._model.onTrim(amount); - if (needsRefresh) { - this.refresh(); - } - }; - SelectionManager.prototype._getMouseBufferCoords = function (event) { - var coords = this._terminal.mouseHelper.getCoords(event, this._terminal.element, this._charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows, true); - if (!coords) { - return null; - } - coords[0]--; - coords[1]--; - coords[1] += this._terminal.buffer.ydisp; - return coords; - }; - SelectionManager.prototype._getMouseEventScrollAmount = function (event) { - var offset = MouseHelper_1.MouseHelper.getCoordsRelativeToElement(event, this._terminal.element)[1]; - var terminalHeight = this._terminal.rows * Math.ceil(this._charMeasure.height * this._terminal.options.lineHeight); - if (offset >= 0 && offset <= terminalHeight) { - return 0; - } - if (offset > terminalHeight) { - offset -= terminalHeight; - } - offset = Math.min(Math.max(offset, -DRAG_SCROLL_MAX_THRESHOLD), DRAG_SCROLL_MAX_THRESHOLD); - offset /= DRAG_SCROLL_MAX_THRESHOLD; - return (offset / Math.abs(offset)) + Math.round(offset * (DRAG_SCROLL_MAX_SPEED - 1)); - }; - SelectionManager.prototype.shouldForceSelection = function (event) { - return Browser.isMac ? event.altKey : event.shiftKey; - }; - SelectionManager.prototype.onMouseDown = function (event) { - if (event.button === 2 && this.hasSelection) { - return; - } - if (event.button !== 0) { - return; - } - if (!this._enabled) { - if (!this.shouldForceSelection(event)) { - return; - } - event.stopPropagation(); - } - event.preventDefault(); - this._dragScrollAmount = 0; - if (this._enabled && event.shiftKey) { - this._onIncrementalClick(event); - } - else { - if (event.detail === 1) { - this._onSingleClick(event); - } - else if (event.detail === 2) { - this._onDoubleClick(event); - } - else if (event.detail === 3) { - this._onTripleClick(event); - } - } - this._addMouseDownListeners(); - this.refresh(true); - }; - SelectionManager.prototype._addMouseDownListeners = function () { - var _this = this; - this._terminal.element.ownerDocument.addEventListener('mousemove', this._mouseMoveListener); - this._terminal.element.ownerDocument.addEventListener('mouseup', this._mouseUpListener); - this._dragScrollIntervalTimer = setInterval(function () { return _this._dragScroll(); }, DRAG_SCROLL_INTERVAL); - }; - SelectionManager.prototype._removeMouseDownListeners = function () { - this._terminal.element.ownerDocument.removeEventListener('mousemove', this._mouseMoveListener); - this._terminal.element.ownerDocument.removeEventListener('mouseup', this._mouseUpListener); - clearInterval(this._dragScrollIntervalTimer); - this._dragScrollIntervalTimer = null; - }; - SelectionManager.prototype._onIncrementalClick = function (event) { - if (this._model.selectionStart) { - this._model.selectionEnd = this._getMouseBufferCoords(event); - } - }; - SelectionManager.prototype._onSingleClick = function (event) { - this._model.selectionStartLength = 0; - this._model.isSelectAllActive = false; - this._activeSelectionMode = SelectionMode.NORMAL; - this._model.selectionStart = this._getMouseBufferCoords(event); - if (!this._model.selectionStart) { - return; - } - this._model.selectionEnd = null; - var line = this._buffer.lines.get(this._model.selectionStart[1]); - if (!line) { - return; - } - if (line.length >= this._model.selectionStart[0]) { - return; - } - var char = line[this._model.selectionStart[0]]; - if (char[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 0) { - this._model.selectionStart[0]++; - } - }; - SelectionManager.prototype._onDoubleClick = function (event) { - var coords = this._getMouseBufferCoords(event); - if (coords) { - this._activeSelectionMode = SelectionMode.WORD; - this._selectWordAt(coords); - } - }; - SelectionManager.prototype._onTripleClick = function (event) { - var coords = this._getMouseBufferCoords(event); - if (coords) { - this._activeSelectionMode = SelectionMode.LINE; - this._selectLineAt(coords[1]); - } - }; - SelectionManager.prototype._onMouseMove = function (event) { - event.stopImmediatePropagation(); - var previousSelectionEnd = this._model.selectionEnd ? [this._model.selectionEnd[0], this._model.selectionEnd[1]] : null; - this._model.selectionEnd = this._getMouseBufferCoords(event); - if (!this._model.selectionEnd) { - this.refresh(true); - return; - } - if (this._activeSelectionMode === SelectionMode.LINE) { - if (this._model.selectionEnd[1] < this._model.selectionStart[1]) { - this._model.selectionEnd[0] = 0; - } - else { - this._model.selectionEnd[0] = this._terminal.cols; - } - } - else if (this._activeSelectionMode === SelectionMode.WORD) { - this._selectToWordAt(this._model.selectionEnd); - } - this._dragScrollAmount = this._getMouseEventScrollAmount(event); - if (this._dragScrollAmount > 0) { - this._model.selectionEnd[0] = this._terminal.cols; - } - else if (this._dragScrollAmount < 0) { - this._model.selectionEnd[0] = 0; - } - if (this._model.selectionEnd[1] < this._buffer.lines.length) { - var char = this._buffer.lines.get(this._model.selectionEnd[1])[this._model.selectionEnd[0]]; - if (char && char[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 0) { - this._model.selectionEnd[0]++; - } - } - if (!previousSelectionEnd || - previousSelectionEnd[0] !== this._model.selectionEnd[0] || - previousSelectionEnd[1] !== this._model.selectionEnd[1]) { - this.refresh(true); - } - }; - SelectionManager.prototype._dragScroll = function () { - if (this._dragScrollAmount) { - this._terminal.scrollLines(this._dragScrollAmount, false); - if (this._dragScrollAmount > 0) { - this._model.selectionEnd = [this._terminal.cols - 1, this._terminal.buffer.ydisp + this._terminal.rows]; - } - else { - this._model.selectionEnd = [0, this._terminal.buffer.ydisp]; - } - this.refresh(); - } - }; - SelectionManager.prototype._onMouseUp = function (event) { - this._removeMouseDownListeners(); - if (this.hasSelection) - this.emit('selection'); - }; - SelectionManager.prototype._convertViewportColToCharacterIndex = function (bufferLine, coords) { - var charIndex = coords[0]; - for (var i = 0; coords[0] >= i; i++) { - var char = bufferLine[i]; - if (char[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 0) { - charIndex--; - } - else if (char[Buffer_1.CHAR_DATA_CHAR_INDEX].length > 1 && coords[0] !== i) { - charIndex += char[Buffer_1.CHAR_DATA_CHAR_INDEX].length - 1; - } - } - return charIndex; - }; - SelectionManager.prototype.setSelection = function (col, row, length) { - this._model.clearSelection(); - this._removeMouseDownListeners(); - this._model.selectionStart = [col, row]; - this._model.selectionStartLength = length; - this.refresh(); - }; - SelectionManager.prototype._getWordAt = function (coords) { - var bufferLine = this._buffer.lines.get(coords[1]); - if (!bufferLine) { - return null; - } - var line = this._buffer.translateBufferLineToString(coords[1], false); - var startIndex = this._convertViewportColToCharacterIndex(bufferLine, coords); - var endIndex = startIndex; - var charOffset = coords[0] - startIndex; - var leftWideCharCount = 0; - var rightWideCharCount = 0; - var leftLongCharOffset = 0; - var rightLongCharOffset = 0; - if (line.charAt(startIndex) === ' ') { - while (startIndex > 0 && line.charAt(startIndex - 1) === ' ') { - startIndex--; - } - while (endIndex < line.length && line.charAt(endIndex + 1) === ' ') { - endIndex++; - } - } - else { - var startCol = coords[0]; - var endCol = coords[0]; - if (bufferLine[startCol][Buffer_1.CHAR_DATA_WIDTH_INDEX] === 0) { - leftWideCharCount++; - startCol--; - } - if (bufferLine[endCol][Buffer_1.CHAR_DATA_WIDTH_INDEX] === 2) { - rightWideCharCount++; - endCol++; - } - if (bufferLine[endCol][Buffer_1.CHAR_DATA_CHAR_INDEX].length > 1) { - rightLongCharOffset += bufferLine[endCol][Buffer_1.CHAR_DATA_CHAR_INDEX].length - 1; - endIndex += bufferLine[endCol][Buffer_1.CHAR_DATA_CHAR_INDEX].length - 1; - } - while (startCol > 0 && startIndex > 0 && !this._isCharWordSeparator(bufferLine[startCol - 1])) { - var char = bufferLine[startCol - 1]; - if (char[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 0) { - leftWideCharCount++; - startCol--; - } - else if (char[Buffer_1.CHAR_DATA_CHAR_INDEX].length > 1) { - leftLongCharOffset += char[Buffer_1.CHAR_DATA_CHAR_INDEX].length - 1; - startIndex -= char[Buffer_1.CHAR_DATA_CHAR_INDEX].length - 1; - } - startIndex--; - startCol--; - } - while (endCol < bufferLine.length && endIndex + 1 < line.length && !this._isCharWordSeparator(bufferLine[endCol + 1])) { - var char = bufferLine[endCol + 1]; - if (char[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 2) { - rightWideCharCount++; - endCol++; - } - else if (char[Buffer_1.CHAR_DATA_CHAR_INDEX].length > 1) { - rightLongCharOffset += char[Buffer_1.CHAR_DATA_CHAR_INDEX].length - 1; - endIndex += char[Buffer_1.CHAR_DATA_CHAR_INDEX].length - 1; - } - endIndex++; - endCol++; - } - } - endIndex++; - var start = startIndex - + charOffset - - leftWideCharCount - + leftLongCharOffset; - var length = Math.min(this._terminal.cols, endIndex - - startIndex - + leftWideCharCount - + rightWideCharCount - - leftLongCharOffset - - rightLongCharOffset); - return { start: start, length: length }; - }; - SelectionManager.prototype._selectWordAt = function (coords) { - var wordPosition = this._getWordAt(coords); - if (wordPosition) { - this._model.selectionStart = [wordPosition.start, coords[1]]; - this._model.selectionStartLength = wordPosition.length; - } - }; - SelectionManager.prototype._selectToWordAt = function (coords) { - var wordPosition = this._getWordAt(coords); - if (wordPosition) { - this._model.selectionEnd = [this._model.areSelectionValuesReversed() ? wordPosition.start : (wordPosition.start + wordPosition.length), coords[1]]; - } - }; - SelectionManager.prototype._isCharWordSeparator = function (charData) { - if (charData[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 0) { - return false; - } - return WORD_SEPARATORS.indexOf(charData[Buffer_1.CHAR_DATA_CHAR_INDEX]) >= 0; - }; - SelectionManager.prototype._selectLineAt = function (line) { - this._model.selectionStart = [0, line]; - this._model.selectionStartLength = this._terminal.cols; - }; - return SelectionManager; -}(EventEmitter_1.EventEmitter)); -exports.SelectionManager = SelectionManager; - - - -},{"./Buffer":1,"./EventEmitter":7,"./SelectionModel":12,"./utils/Browser":28,"./utils/MouseHelper":32}],12:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var SelectionModel = (function () { - function SelectionModel(_terminal) { - this._terminal = _terminal; - this.clearSelection(); - } - SelectionModel.prototype.clearSelection = function () { - this.selectionStart = null; - this.selectionEnd = null; - this.isSelectAllActive = false; - this.selectionStartLength = 0; - }; - Object.defineProperty(SelectionModel.prototype, "finalSelectionStart", { - get: function () { - if (this.isSelectAllActive) { - return [0, 0]; - } - if (!this.selectionEnd || !this.selectionStart) { - return this.selectionStart; - } - return this.areSelectionValuesReversed() ? this.selectionEnd : this.selectionStart; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(SelectionModel.prototype, "finalSelectionEnd", { - get: function () { - if (this.isSelectAllActive) { - return [this._terminal.cols, this._terminal.buffer.ybase + this._terminal.rows - 1]; - } - if (!this.selectionStart) { - return null; - } - if (!this.selectionEnd || this.areSelectionValuesReversed()) { - return [this.selectionStart[0] + this.selectionStartLength, this.selectionStart[1]]; - } - if (this.selectionStartLength) { - if (this.selectionEnd[1] === this.selectionStart[1]) { - return [Math.max(this.selectionStart[0] + this.selectionStartLength, this.selectionEnd[0]), this.selectionEnd[1]]; - } - } - return this.selectionEnd; - }, - enumerable: true, - configurable: true - }); - SelectionModel.prototype.areSelectionValuesReversed = function () { - var start = this.selectionStart; - var end = this.selectionEnd; - if (!start || !end) { - return false; - } - return start[1] > end[1] || (start[1] === end[1] && start[0] > end[0]); - }; - SelectionModel.prototype.onTrim = function (amount) { - if (this.selectionStart) { - this.selectionStart[1] -= amount; - } - if (this.selectionEnd) { - this.selectionEnd[1] -= amount; - } - if (this.selectionEnd && this.selectionEnd[1] < 0) { - this.clearSelection(); - return true; - } - if (this.selectionStart && this.selectionStart[1] < 0) { - this.selectionStart[1] = 0; - } - return false; - }; - return SelectionModel; -}()); -exports.SelectionModel = SelectionModel; - - - -},{}],13:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var BufferSet_1 = require("./BufferSet"); -var Buffer_1 = require("./Buffer"); -var CompositionHelper_1 = require("./CompositionHelper"); -var EventEmitter_1 = require("./EventEmitter"); -var Viewport_1 = require("./Viewport"); -var Clipboard_1 = require("./handlers/Clipboard"); -var EscapeSequences_1 = require("./EscapeSequences"); -var InputHandler_1 = require("./InputHandler"); -var Parser_1 = require("./Parser"); -var Renderer_1 = require("./renderer/Renderer"); -var Linkifier_1 = require("./Linkifier"); -var SelectionManager_1 = require("./SelectionManager"); -var CharMeasure_1 = require("./utils/CharMeasure"); -var Browser = require("./utils/Browser"); -var MouseHelper_1 = require("./utils/MouseHelper"); -var Sounds_1 = require("./utils/Sounds"); -var ColorManager_1 = require("./renderer/ColorManager"); -var MouseZoneManager_1 = require("./input/MouseZoneManager"); -var CharAtlas_1 = require("./renderer/CharAtlas"); -var document = (typeof window !== 'undefined') ? window.document : null; -var WRITE_BUFFER_PAUSE_THRESHOLD = 5; -var WRITE_BATCH_SIZE = 300; -var DEFAULT_OPTIONS = { - convertEol: false, - termName: 'xterm', - geometry: [80, 24], - cursorBlink: false, - cursorStyle: 'block', - bellSound: Sounds_1.BellSound, - bellStyle: 'none', - enableBold: true, - fontFamily: 'courier-new, courier, monospace', - fontSize: 15, - lineHeight: 1.0, - letterSpacing: 0, - scrollback: 1000, - screenKeys: false, - debug: false, - cancelEvents: false, - disableStdin: false, - useFlowControl: false, - tabStopWidth: 8, - theme: null -}; -var Terminal = (function (_super) { - __extends(Terminal, _super); - function Terminal(options) { - if (options === void 0) { options = {}; } - var _this = _super.call(this) || this; - _this.browser = Browser; - _this.options = options; - _this.setup(); - return _this; - } - Terminal.prototype.setup = function () { - var _this = this; - Object.keys(DEFAULT_OPTIONS).forEach(function (key) { - if (_this.options[key] == null) { - _this.options[key] = DEFAULT_OPTIONS[key]; - } - _this[key] = _this.options[key]; - }); - this.parent = document ? document.body : null; - this.cols = this.options.cols || this.options.geometry[0]; - this.rows = this.options.rows || this.options.geometry[1]; - this.geometry = [this.cols, this.rows]; - if (this.options.handler) { - this.on('data', this.options.handler); - } - this.cursorState = 0; - this.cursorHidden = false; - this.sendDataQueue = ''; - this.customKeyEventHandler = null; - this.applicationKeypad = false; - this.applicationCursor = false; - this.originMode = false; - this.insertMode = false; - this.wraparoundMode = true; - this.bracketedPasteMode = false; - this.charset = null; - this.gcharset = null; - this.glevel = 0; - this.charsets = [null]; - this.readable = true; - this.writable = true; - this.defAttr = (0 << 18) | (257 << 9) | (256 << 0); - this.curAttr = (0 << 18) | (257 << 9) | (256 << 0); - this.params = []; - this.currentParam = 0; - this.prefix = ''; - this.postfix = ''; - this.writeBuffer = []; - this.writeInProgress = false; - this.xoffSentToCatchUp = false; - this.writeStopped = false; - this.surrogate_high = ''; - this.userScrolling = false; - this.inputHandler = new InputHandler_1.InputHandler(this); - this.parser = new Parser_1.Parser(this.inputHandler, this); - this.renderer = this.renderer || null; - this.selectionManager = this.selectionManager || null; - this.linkifier = this.linkifier || new Linkifier_1.Linkifier(this); - this._mouseZoneManager = this._mouseZoneManager || null; - this.buffers = new BufferSet_1.BufferSet(this); - this.buffer = this.buffers.active; - this.buffers.on('activate', function (buffer) { - _this.buffer = buffer; - }); - if (this.selectionManager) { - this.selectionManager.setBuffer(this.buffer); - } - }; - Terminal.prototype.eraseAttr = function () { - return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff); - }; - Terminal.prototype.focus = function () { - this.textarea.focus(); - }; - Object.defineProperty(Terminal.prototype, "isFocused", { - get: function () { - return document.activeElement === this.textarea; - }, - enumerable: true, - configurable: true - }); - Terminal.prototype.getOption = function (key) { - if (!(key in DEFAULT_OPTIONS)) { - throw new Error('No option with key "' + key + '"'); - } - if (typeof this.options[key] !== 'undefined') { - return this.options[key]; - } - return this[key]; - }; - Terminal.prototype.setOption = function (key, value) { - if (!(key in DEFAULT_OPTIONS)) { - throw new Error('No option with key "' + key + '"'); - } - switch (key) { - case 'bellStyle': - if (!value) { - value = 'none'; - } - break; - case 'cursorStyle': - if (!value) { - value = 'block'; - } - break; - case 'lineHeight': - if (value < 1) { - console.warn(key + " cannot be less than 1, value: " + value); - return; - } - case 'tabStopWidth': - if (value < 1) { - console.warn(key + " cannot be less than 1, value: " + value); - return; - } - break; - case 'theme': - if (this.renderer) { - this._setTheme(value); - return; - } - break; - case 'scrollback': - value = Math.min(value, Buffer_1.MAX_BUFFER_SIZE); - if (value < 0) { - console.warn(key + " cannot be less than 0, value: " + value); - return; - } - if (this.options[key] !== value) { - var newBufferLength = this.rows + value; - if (this.buffer.lines.length > newBufferLength) { - var amountToTrim = this.buffer.lines.length - newBufferLength; - var needsRefresh = (this.buffer.ydisp - amountToTrim < 0); - this.buffer.lines.trimStart(amountToTrim); - this.buffer.ybase = Math.max(this.buffer.ybase - amountToTrim, 0); - this.buffer.ydisp = Math.max(this.buffer.ydisp - amountToTrim, 0); - if (needsRefresh) { - this.refresh(0, this.rows - 1); - } - } - } - break; - } - this[key] = value; - this.options[key] = value; - switch (key) { - case 'fontFamily': - case 'fontSize': - this.renderer.clear(); - this.charMeasure.measure(this.options); - break; - case 'enableBold': - case 'letterSpacing': - case 'lineHeight': - this.renderer.clear(); - this.renderer.onResize(this.cols, this.rows, false); - this.refresh(0, this.rows - 1); - case 'scrollback': - this.buffers.resize(this.cols, this.rows); - this.viewport.syncScrollArea(); - break; - case 'tabStopWidth': - this.buffers.setupTabStops(); - break; - case 'bellSound': - case 'bellStyle': - this.syncBellSound(); - break; - } - if (this.renderer) { - this.renderer.onOptionsChanged(); - } - }; - Terminal.prototype._onTextAreaFocus = function () { - if (this.sendFocus) { - this.send(EscapeSequences_1.C0.ESC + '[I'); - } - this.element.classList.add('focus'); - this.showCursor(); - this.emit('focus'); - }; - ; - Terminal.prototype.blur = function () { - return this.textarea.blur(); - }; - Terminal.prototype._onTextAreaBlur = function () { - this.refresh(this.buffer.y, this.buffer.y); - if (this.sendFocus) { - this.send(EscapeSequences_1.C0.ESC + '[O'); - } - this.element.classList.remove('focus'); - this.emit('blur'); - }; - Terminal.prototype.initGlobal = function () { - var _this = this; - this.bindKeys(); - on(this.element, 'copy', function (event) { - if (!_this.hasSelection()) { - return; - } - Clipboard_1.copyHandler(event, _this, _this.selectionManager); - }); - var pasteHandlerWrapper = function (event) { return Clipboard_1.pasteHandler(event, _this); }; - on(this.textarea, 'paste', pasteHandlerWrapper); - on(this.element, 'paste', pasteHandlerWrapper); - if (Browser.isFirefox) { - on(this.element, 'mousedown', function (event) { - if (event.button === 2) { - Clipboard_1.rightClickHandler(event, _this.textarea, _this.selectionManager); - } - }); - } - else { - on(this.element, 'contextmenu', function (event) { - Clipboard_1.rightClickHandler(event, _this.textarea, _this.selectionManager); - }); - } - if (Browser.isLinux) { - on(this.element, 'auxclick', function (event) { - if (event.button === 1) { - Clipboard_1.moveTextAreaUnderMouseCursor(event, _this.textarea); - } - }); - } - }; - Terminal.prototype.bindKeys = function () { - var _this = this; - var self = this; - on(this.element, 'keydown', function (ev) { - if (document.activeElement !== this) { - return; - } - self._keyDown(ev); - }, true); - on(this.element, 'keypress', function (ev) { - if (document.activeElement !== this) { - return; - } - self._keyPress(ev); - }, true); - on(this.element, 'keyup', function (ev) { - if (!wasMondifierKeyOnlyEvent(ev)) { - _this.focus(); - } - }, true); - on(this.textarea, 'keydown', function (ev) { - _this._keyDown(ev); - }, true); - on(this.textarea, 'keypress', function (ev) { - _this._keyPress(ev); - _this.textarea.value = ''; - }, true); - on(this.textarea, 'compositionstart', function () { return _this.compositionHelper.compositionstart(); }); - on(this.textarea, 'compositionupdate', function (e) { return _this.compositionHelper.compositionupdate(e); }); - on(this.textarea, 'compositionend', function () { return _this.compositionHelper.compositionend(); }); - this.on('refresh', function () { return _this.compositionHelper.updateCompositionElements(); }); - this.on('refresh', function (data) { return _this.queueLinkification(data.start, data.end); }); - }; - Terminal.prototype.open = function (parent) { - var _this = this; - var i = 0; - var div; - this.parent = parent || this.parent; - if (!this.parent) { - throw new Error('Terminal requires a parent element.'); - } - this.context = this.parent.ownerDocument.defaultView; - this.document = this.parent.ownerDocument; - this.body = this.document.body; - CharAtlas_1.initialize(this.document); - this.element = this.document.createElement('div'); - this.element.classList.add('terminal'); - this.element.classList.add('xterm'); - this.element.setAttribute('tabindex', '0'); - this.viewportElement = document.createElement('div'); - this.viewportElement.classList.add('xterm-viewport'); - this.element.appendChild(this.viewportElement); - this.viewportScrollArea = document.createElement('div'); - this.viewportScrollArea.classList.add('xterm-scroll-area'); - this.viewportElement.appendChild(this.viewportScrollArea); - this.syncBellSound(); - this._mouseZoneManager = new MouseZoneManager_1.MouseZoneManager(this); - this.on('scroll', function () { return _this._mouseZoneManager.clearAll(); }); - this.linkifier.attachToDom(this._mouseZoneManager); - this.helperContainer = document.createElement('div'); - this.helperContainer.classList.add('xterm-helpers'); - this.element.appendChild(this.helperContainer); - this.textarea = document.createElement('textarea'); - this.textarea.classList.add('xterm-helper-textarea'); - this.textarea.setAttribute('autocorrect', 'off'); - this.textarea.setAttribute('autocapitalize', 'off'); - this.textarea.setAttribute('spellcheck', 'false'); - this.textarea.tabIndex = 0; - this.textarea.addEventListener('focus', function () { return _this._onTextAreaFocus(); }); - this.textarea.addEventListener('blur', function () { return _this._onTextAreaBlur(); }); - this.helperContainer.appendChild(this.textarea); - this.compositionView = document.createElement('div'); - this.compositionView.classList.add('composition-view'); - this.compositionHelper = new CompositionHelper_1.CompositionHelper(this.textarea, this.compositionView, this); - this.helperContainer.appendChild(this.compositionView); - this.charSizeStyleElement = document.createElement('style'); - this.helperContainer.appendChild(this.charSizeStyleElement); - this.parent.appendChild(this.element); - this.charMeasure = new CharMeasure_1.CharMeasure(document, this.helperContainer); - this.renderer = new Renderer_1.Renderer(this, this.options.theme); - this.options.theme = null; - this.viewport = new Viewport_1.Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure); - this.viewport.onThemeChanged(this.renderer.colorManager.colors); - this.on('cursormove', function () { return _this.renderer.onCursorMove(); }); - this.on('resize', function () { return _this.renderer.onResize(_this.cols, _this.rows, false); }); - this.on('blur', function () { return _this.renderer.onBlur(); }); - this.on('focus', function () { return _this.renderer.onFocus(); }); - window.addEventListener('resize', function () { return _this.renderer.onWindowResize(window.devicePixelRatio); }); - this.charMeasure.on('charsizechanged', function () { return _this.renderer.onResize(_this.cols, _this.rows, true); }); - this.renderer.on('resize', function (dimensions) { return _this.viewport.syncScrollArea(); }); - this.selectionManager = new SelectionManager_1.SelectionManager(this, this.buffer, this.charMeasure); - this.element.addEventListener('mousedown', function (e) { return _this.selectionManager.onMouseDown(e); }); - this.selectionManager.on('refresh', function (data) { return _this.renderer.onSelectionChanged(data.start, data.end); }); - this.selectionManager.on('newselection', function (text) { - _this.textarea.value = text; - _this.textarea.focus(); - _this.textarea.select(); - }); - this.on('scroll', function () { - _this.viewport.syncScrollArea(); - _this.selectionManager.refresh(); - }); - this.viewportElement.addEventListener('scroll', function () { return _this.selectionManager.refresh(); }); - this.mouseHelper = new MouseHelper_1.MouseHelper(this.renderer); - this.charMeasure.measure(this.options); - this.refresh(0, this.rows - 1); - this.initGlobal(); - this.bindMouse(); - }; - Terminal.prototype._setTheme = function (theme) { - var colors = this.renderer.setTheme(theme); - if (this.viewport) { - this.viewport.onThemeChanged(colors); - } - }; - Terminal.loadAddon = function (addon, callback) { - if (typeof exports === 'object' && typeof module === 'object') { - return require('./addons/' + addon + '/' + addon); - } - else if (typeof define === 'function') { - return require(['./addons/' + addon + '/' + addon], callback); - } - else { - console.error('Cannot load a module without a CommonJS or RequireJS environment.'); - return false; - } - }; - Terminal.prototype.bindMouse = function () { - var _this = this; - var el = this.element; - var self = this; - var pressed = 32; - function sendButton(ev) { - var button; - var pos; - button = getButton(ev); - pos = self.mouseHelper.getRawByteCoords(ev, self.element, self.charMeasure, self.options.lineHeight, self.cols, self.rows); - if (!pos) - return; - sendEvent(button, pos); - switch (ev.overrideType || ev.type) { - case 'mousedown': - pressed = button; - break; - case 'mouseup': - pressed = 32; - break; - case 'wheel': - break; - } - } - function sendMove(ev) { - var button = pressed; - var pos = self.mouseHelper.getRawByteCoords(ev, self.element, self.charMeasure, self.options.lineHeight, self.cols, self.rows); - if (!pos) - return; - button += 32; - sendEvent(button, pos); - } - function encode(data, ch) { - if (!self.utfMouse) { - if (ch === 255) { - data.push(0); - return; - } - if (ch > 127) - ch = 127; - data.push(ch); - } - else { - if (ch === 2047) { - data.push(0); - return; - } - if (ch < 127) { - data.push(ch); - } - else { - if (ch > 2047) - ch = 2047; - data.push(0xC0 | (ch >> 6)); - data.push(0x80 | (ch & 0x3F)); - } - } - } - function sendEvent(button, pos) { - if (self.vt300Mouse) { - button &= 3; - pos.x -= 32; - pos.y -= 32; - var data_1 = EscapeSequences_1.C0.ESC + '[24'; - if (button === 0) - data_1 += '1'; - else if (button === 1) - data_1 += '3'; - else if (button === 2) - data_1 += '5'; - else if (button === 3) - return; - else - data_1 += '0'; - data_1 += '~[' + pos.x + ',' + pos.y + ']\r'; - self.send(data_1); - return; - } - if (self.decLocator) { - button &= 3; - pos.x -= 32; - pos.y -= 32; - if (button === 0) - button = 2; - else if (button === 1) - button = 4; - else if (button === 2) - button = 6; - else if (button === 3) - button = 3; - self.send(EscapeSequences_1.C0.ESC + '[' - + button - + ';' - + (button === 3 ? 4 : 0) - + ';' - + pos.y - + ';' - + pos.x - + ';' - + pos.page || 0 - + '&w'); - return; - } - if (self.urxvtMouse) { - pos.x -= 32; - pos.y -= 32; - pos.x++; - pos.y++; - self.send(EscapeSequences_1.C0.ESC + '[' + button + ';' + pos.x + ';' + pos.y + 'M'); - return; - } - if (self.sgrMouse) { - pos.x -= 32; - pos.y -= 32; - self.send(EscapeSequences_1.C0.ESC + '[<' - + (((button & 3) === 3 ? button & ~3 : button) - 32) - + ';' - + pos.x - + ';' - + pos.y - + ((button & 3) === 3 ? 'm' : 'M')); - return; - } - var data = []; - encode(data, button); - encode(data, pos.x); - encode(data, pos.y); - self.send(EscapeSequences_1.C0.ESC + '[M' + String.fromCharCode.apply(String, data)); - } - function getButton(ev) { - var button; - var shift; - var meta; - var ctrl; - var mod; - switch (ev.overrideType || ev.type) { - case 'mousedown': - button = ev.button != null - ? +ev.button - : ev.which != null - ? ev.which - 1 - : null; - if (Browser.isMSIE) { - button = button === 1 ? 0 : button === 4 ? 1 : button; - } - break; - case 'mouseup': - button = 3; - break; - case 'DOMMouseScroll': - button = ev.detail < 0 - ? 64 - : 65; - break; - case 'wheel': - button = ev.wheelDeltaY > 0 - ? 64 - : 65; - break; - } - shift = ev.shiftKey ? 4 : 0; - meta = ev.metaKey ? 8 : 0; - ctrl = ev.ctrlKey ? 16 : 0; - mod = shift | meta | ctrl; - if (self.vt200Mouse) { - mod &= ctrl; - } - else if (!self.normalMouse) { - mod = 0; - } - button = (32 + (mod << 2)) + button; - return button; - } - on(el, 'mousedown', function (ev) { - ev.preventDefault(); - _this.focus(); - if (!_this.mouseEvents || _this.selectionManager.shouldForceSelection(ev)) { - return; - } - sendButton(ev); - if (_this.vt200Mouse) { - ev.overrideType = 'mouseup'; - sendButton(ev); - return _this.cancel(ev); - } - if (_this.normalMouse) - on(_this.document, 'mousemove', sendMove); - if (!_this.x10Mouse) { - var handler_1 = function (ev) { - sendButton(ev); - if (_this.normalMouse) - off(_this.document, 'mousemove', sendMove); - off(_this.document, 'mouseup', handler_1); - return _this.cancel(ev); - }; - on(_this.document, 'mouseup', handler_1); - } - return _this.cancel(ev); - }); - on(el, 'wheel', function (ev) { - if (!_this.mouseEvents) - return; - if (_this.x10Mouse || _this.vt300Mouse || _this.decLocator) - return; - sendButton(ev); - ev.preventDefault(); - }); - on(el, 'wheel', function (ev) { - if (_this.mouseEvents) - return; - _this.viewport.onWheel(ev); - return _this.cancel(ev); - }); - on(el, 'touchstart', function (ev) { - if (_this.mouseEvents) - return; - _this.viewport.onTouchStart(ev); - return _this.cancel(ev); - }); - on(el, 'touchmove', function (ev) { - if (_this.mouseEvents) - return; - _this.viewport.onTouchMove(ev); - return _this.cancel(ev); - }); - }; - Terminal.prototype.destroy = function () { - _super.prototype.destroy.call(this); - this.readable = false; - this.writable = false; - this.handler = function () { }; - this.write = function () { }; - if (this.element && this.element.parentNode) { - this.element.parentNode.removeChild(this.element); - } - }; - Terminal.prototype.refresh = function (start, end) { - if (this.renderer) { - this.renderer.queueRefresh(start, end); - } - }; - Terminal.prototype.queueLinkification = function (start, end) { - if (this.linkifier) { - this.linkifier.linkifyRows(start, end); - } - }; - Terminal.prototype.showCursor = function () { - if (!this.cursorState) { - this.cursorState = 1; - this.refresh(this.buffer.y, this.buffer.y); - } - }; - Terminal.prototype.scroll = function (isWrapped) { - var newLine = this.blankLine(undefined, isWrapped); - var topRow = this.buffer.ybase + this.buffer.scrollTop; - var bottomRow = this.buffer.ybase + this.buffer.scrollBottom; - if (this.buffer.scrollTop === 0) { - var willBufferBeTrimmed = this.buffer.lines.length === this.buffer.lines.maxLength; - if (bottomRow === this.buffer.lines.length - 1) { - this.buffer.lines.push(newLine); - } - else { - this.buffer.lines.splice(bottomRow + 1, 0, newLine); - } - if (!willBufferBeTrimmed) { - this.buffer.ybase++; - if (!this.userScrolling) { - this.buffer.ydisp++; - } - } - else { - if (this.userScrolling) { - this.buffer.ydisp = Math.max(this.buffer.ydisp - 1, 0); - } - } - } - else { - var scrollRegionHeight = bottomRow - topRow + 1; - this.buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1); - this.buffer.lines.set(bottomRow, newLine); - } - if (!this.userScrolling) { - this.buffer.ydisp = this.buffer.ybase; - } - this.updateRange(this.buffer.scrollTop); - this.updateRange(this.buffer.scrollBottom); - this.emit('scroll', this.buffer.ydisp); - }; - Terminal.prototype.scrollLines = function (disp, suppressScrollEvent) { - if (disp < 0) { - if (this.buffer.ydisp === 0) { - return; - } - this.userScrolling = true; - } - else if (disp + this.buffer.ydisp >= this.buffer.ybase) { - this.userScrolling = false; - } - var oldYdisp = this.buffer.ydisp; - this.buffer.ydisp = Math.max(Math.min(this.buffer.ydisp + disp, this.buffer.ybase), 0); - if (oldYdisp === this.buffer.ydisp) { - return; - } - if (!suppressScrollEvent) { - this.emit('scroll', this.buffer.ydisp); - } - this.refresh(0, this.rows - 1); - }; - Terminal.prototype.scrollPages = function (pageCount) { - this.scrollLines(pageCount * (this.rows - 1)); - }; - Terminal.prototype.scrollToTop = function () { - this.scrollLines(-this.buffer.ydisp); - }; - Terminal.prototype.scrollToBottom = function () { - this.scrollLines(this.buffer.ybase - this.buffer.ydisp); - }; - Terminal.prototype.write = function (data) { - var _this = this; - this.writeBuffer.push(data); - if (this.options.useFlowControl && !this.xoffSentToCatchUp && this.writeBuffer.length >= WRITE_BUFFER_PAUSE_THRESHOLD) { - this.send(EscapeSequences_1.C0.DC3); - this.xoffSentToCatchUp = true; - } - if (!this.writeInProgress && this.writeBuffer.length > 0) { - this.writeInProgress = true; - setTimeout(function () { - _this.innerWrite(); - }); - } - }; - Terminal.prototype.innerWrite = function () { - var _this = this; - var writeBatch = this.writeBuffer.splice(0, WRITE_BATCH_SIZE); - while (writeBatch.length > 0) { - var data = writeBatch.shift(); - if (this.xoffSentToCatchUp && writeBatch.length === 0 && this.writeBuffer.length === 0) { - this.send(EscapeSequences_1.C0.DC1); - this.xoffSentToCatchUp = false; - } - this.refreshStart = this.buffer.y; - this.refreshEnd = this.buffer.y; - var state = this.parser.parse(data); - this.parser.setState(state); - this.updateRange(this.buffer.y); - this.refresh(this.refreshStart, this.refreshEnd); - } - if (this.writeBuffer.length > 0) { - setTimeout(function () { return _this.innerWrite(); }, 0); - } - else { - this.writeInProgress = false; - } - }; - Terminal.prototype.writeln = function (data) { - this.write(data + '\r\n'); - }; - Terminal.prototype.attachCustomKeyEventHandler = function (customKeyEventHandler) { - this.customKeyEventHandler = customKeyEventHandler; - }; - Terminal.prototype.setHypertextLinkHandler = function (handler) { - if (!this.linkifier) { - throw new Error('Cannot attach a hypertext link handler before Terminal.open is called'); - } - this.linkifier.setHypertextLinkHandler(handler); - this.refresh(0, this.rows - 1); - }; - Terminal.prototype.setHypertextValidationCallback = function (callback) { - if (!this.linkifier) { - throw new Error('Cannot attach a hypertext validation callback before Terminal.open is called'); - } - this.linkifier.setHypertextValidationCallback(callback); - this.refresh(0, this.rows - 1); - }; - Terminal.prototype.registerLinkMatcher = function (regex, handler, options) { - if (this.linkifier) { - var matcherId = this.linkifier.registerLinkMatcher(regex, handler, options); - this.refresh(0, this.rows - 1); - return matcherId; - } - return 0; - }; - Terminal.prototype.deregisterLinkMatcher = function (matcherId) { - if (this.linkifier) { - if (this.linkifier.deregisterLinkMatcher(matcherId)) { - this.refresh(0, this.rows - 1); - } - } - }; - Terminal.prototype.hasSelection = function () { - return this.selectionManager ? this.selectionManager.hasSelection : false; - }; - Terminal.prototype.getSelection = function () { - return this.selectionManager ? this.selectionManager.selectionText : ''; - }; - Terminal.prototype.clearSelection = function () { - if (this.selectionManager) { - this.selectionManager.clearSelection(); - } - }; - Terminal.prototype.selectAll = function () { - if (this.selectionManager) { - this.selectionManager.selectAll(); - } - }; - Terminal.prototype._keyDown = function (ev) { - if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) { - return false; - } - if (!this.compositionHelper.keydown(ev)) { - if (this.buffer.ybase !== this.buffer.ydisp) { - this.scrollToBottom(); - } - return false; - } - var result = this._evaluateKeyEscapeSequence(ev); - if (result.key === EscapeSequences_1.C0.DC3) { - this.writeStopped = true; - } - else if (result.key === EscapeSequences_1.C0.DC1) { - this.writeStopped = false; - } - if (result.scrollLines) { - this.scrollLines(result.scrollLines); - return this.cancel(ev, true); - } - if (isThirdLevelShift(this.browser, ev)) { - return true; - } - if (result.cancel) { - this.cancel(ev, true); - } - if (!result.key) { - return true; - } - this.emit('keydown', ev); - this.emit('key', result.key, ev); - this.showCursor(); - this.handler(result.key); - return this.cancel(ev, true); - }; - Terminal.prototype._evaluateKeyEscapeSequence = function (ev) { - var result = { - cancel: false, - key: undefined, - scrollLines: undefined - }; - var modifiers = (ev.shiftKey ? 1 : 0) | (ev.altKey ? 2 : 0) | (ev.ctrlKey ? 4 : 0) | (ev.metaKey ? 8 : 0); - switch (ev.keyCode) { - case 0: - if (ev.key === 'UIKeyInputUpArrow') { - if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OA'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[A'; - } - } - else if (ev.key === 'UIKeyInputLeftArrow') { - if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OD'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[D'; - } - } - else if (ev.key === 'UIKeyInputRightArrow') { - if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OC'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[C'; - } - } - else if (ev.key === 'UIKeyInputDownArrow') { - if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OB'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[B'; - } - } - break; - case 8: - if (ev.shiftKey) { - result.key = EscapeSequences_1.C0.BS; - break; - } - result.key = EscapeSequences_1.C0.DEL; - break; - case 9: - if (ev.shiftKey) { - result.key = EscapeSequences_1.C0.ESC + '[Z'; - break; - } - result.key = EscapeSequences_1.C0.HT; - result.cancel = true; - break; - case 13: - result.key = EscapeSequences_1.C0.CR; - result.cancel = true; - break; - case 27: - result.key = EscapeSequences_1.C0.ESC; - result.cancel = true; - break; - case 37: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'D'; - if (result.key === EscapeSequences_1.C0.ESC + '[1;3D') { - result.key = (this.browser.isMac) ? EscapeSequences_1.C0.ESC + 'b' : EscapeSequences_1.C0.ESC + '[1;5D'; - } - } - else if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OD'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[D'; - } - break; - case 39: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'C'; - if (result.key === EscapeSequences_1.C0.ESC + '[1;3C') { - result.key = (this.browser.isMac) ? EscapeSequences_1.C0.ESC + 'f' : EscapeSequences_1.C0.ESC + '[1;5C'; - } - } - else if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OC'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[C'; - } - break; - case 38: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'A'; - if (result.key === EscapeSequences_1.C0.ESC + '[1;3A') { - result.key = EscapeSequences_1.C0.ESC + '[1;5A'; - } - } - else if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OA'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[A'; - } - break; - case 40: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'B'; - if (result.key === EscapeSequences_1.C0.ESC + '[1;3B') { - result.key = EscapeSequences_1.C0.ESC + '[1;5B'; - } - } - else if (this.applicationCursor) { - result.key = EscapeSequences_1.C0.ESC + 'OB'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[B'; - } - break; - case 45: - if (!ev.shiftKey && !ev.ctrlKey) { - result.key = EscapeSequences_1.C0.ESC + '[2~'; - } - break; - case 46: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[3;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[3~'; - } - break; - case 36: - if (modifiers) - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'H'; - else if (this.applicationCursor) - result.key = EscapeSequences_1.C0.ESC + 'OH'; - else - result.key = EscapeSequences_1.C0.ESC + '[H'; - break; - case 35: - if (modifiers) - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'F'; - else if (this.applicationCursor) - result.key = EscapeSequences_1.C0.ESC + 'OF'; - else - result.key = EscapeSequences_1.C0.ESC + '[F'; - break; - case 33: - if (ev.shiftKey) { - result.scrollLines = -(this.rows - 1); - } - else { - result.key = EscapeSequences_1.C0.ESC + '[5~'; - } - break; - case 34: - if (ev.shiftKey) { - result.scrollLines = this.rows - 1; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[6~'; - } - break; - case 112: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'P'; - } - else { - result.key = EscapeSequences_1.C0.ESC + 'OP'; - } - break; - case 113: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'Q'; - } - else { - result.key = EscapeSequences_1.C0.ESC + 'OQ'; - } - break; - case 114: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'R'; - } - else { - result.key = EscapeSequences_1.C0.ESC + 'OR'; - } - break; - case 115: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[1;' + (modifiers + 1) + 'S'; - } - else { - result.key = EscapeSequences_1.C0.ESC + 'OS'; - } - break; - case 116: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[15;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[15~'; - } - break; - case 117: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[17;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[17~'; - } - break; - case 118: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[18;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[18~'; - } - break; - case 119: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[19;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[19~'; - } - break; - case 120: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[20;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[20~'; - } - break; - case 121: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[21;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[21~'; - } - break; - case 122: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[23;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[23~'; - } - break; - case 123: - if (modifiers) { - result.key = EscapeSequences_1.C0.ESC + '[24;' + (modifiers + 1) + '~'; - } - else { - result.key = EscapeSequences_1.C0.ESC + '[24~'; - } - break; - default: - if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) { - if (ev.keyCode >= 65 && ev.keyCode <= 90) { - result.key = String.fromCharCode(ev.keyCode - 64); - } - else if (ev.keyCode === 32) { - result.key = String.fromCharCode(0); - } - else if (ev.keyCode >= 51 && ev.keyCode <= 55) { - result.key = String.fromCharCode(ev.keyCode - 51 + 27); - } - else if (ev.keyCode === 56) { - result.key = String.fromCharCode(127); - } - else if (ev.keyCode === 219) { - result.key = String.fromCharCode(27); - } - else if (ev.keyCode === 220) { - result.key = String.fromCharCode(28); - } - else if (ev.keyCode === 221) { - result.key = String.fromCharCode(29); - } - } - else if (!this.browser.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) { - if (ev.keyCode >= 65 && ev.keyCode <= 90) { - result.key = EscapeSequences_1.C0.ESC + String.fromCharCode(ev.keyCode + 32); - } - else if (ev.keyCode === 192) { - result.key = EscapeSequences_1.C0.ESC + '`'; - } - else if (ev.keyCode >= 48 && ev.keyCode <= 57) { - result.key = EscapeSequences_1.C0.ESC + (ev.keyCode - 48); - } - } - else if (this.browser.isMac && !ev.altKey && !ev.ctrlKey && ev.metaKey) { - if (ev.keyCode === 65) { - this.selectAll(); - } - } - break; - } - return result; - }; - Terminal.prototype.setgLevel = function (g) { - this.glevel = g; - this.charset = this.charsets[g]; - }; - Terminal.prototype.setgCharset = function (g, charset) { - this.charsets[g] = charset; - if (this.glevel === g) { - this.charset = charset; - } - }; - Terminal.prototype._keyPress = function (ev) { - var key; - if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) { - return false; - } - this.cancel(ev); - if (ev.charCode) { - key = ev.charCode; - } - else if (ev.which == null) { - key = ev.keyCode; - } - else if (ev.which !== 0 && ev.charCode !== 0) { - key = ev.which; - } - else { - return false; - } - if (!key || ((ev.altKey || ev.ctrlKey || ev.metaKey) && !isThirdLevelShift(this.browser, ev))) { - return false; - } - key = String.fromCharCode(key); - this.emit('keypress', key, ev); - this.emit('key', key, ev); - this.showCursor(); - this.handler(key); - return true; - }; - Terminal.prototype.send = function (data) { - var _this = this; - if (!this.sendDataQueue) { - setTimeout(function () { - _this.handler(_this.sendDataQueue); - _this.sendDataQueue = ''; - }, 1); - } - this.sendDataQueue += data; - }; - Terminal.prototype.bell = function () { - var _this = this; - this.emit('bell'); - if (this.soundBell()) - this.bellAudioElement.play(); - if (this.visualBell()) { - this.element.classList.add('visual-bell-active'); - clearTimeout(this.visualBellTimer); - this.visualBellTimer = window.setTimeout(function () { - _this.element.classList.remove('visual-bell-active'); - }, 200); - } - }; - Terminal.prototype.log = function (text, data) { - if (!this.options.debug) - return; - if (!this.context.console || !this.context.console.log) - return; - this.context.console.log(text, data); - }; - Terminal.prototype.error = function (text, data) { - if (!this.options.debug) - return; - if (!this.context.console || !this.context.console.error) - return; - this.context.console.error(text, data); - }; - Terminal.prototype.resize = function (x, y) { - if (isNaN(x) || isNaN(y)) { - return; - } - if (x === this.cols && y === this.rows) { - if (!this.charMeasure.width || !this.charMeasure.height) { - this.charMeasure.measure(this.options); - } - return; - } - if (x < 1) - x = 1; - if (y < 1) - y = 1; - this.buffers.resize(x, y); - this.cols = x; - this.rows = y; - this.buffers.setupTabStops(this.cols); - this.charMeasure.measure(this.options); - this.refresh(0, this.rows - 1); - this.geometry = [this.cols, this.rows]; - this.emit('resize', { cols: x, rows: y }); - }; - Terminal.prototype.updateRange = function (y) { - if (y < this.refreshStart) - this.refreshStart = y; - if (y > this.refreshEnd) - this.refreshEnd = y; - }; - Terminal.prototype.maxRange = function () { - this.refreshStart = 0; - this.refreshEnd = this.rows - 1; - }; - Terminal.prototype.eraseRight = function (x, y) { - var line = this.buffer.lines.get(this.buffer.ybase + y); - if (!line) { - return; - } - var ch = [this.eraseAttr(), ' ', 1, 32]; - for (; x < this.cols; x++) { - line[x] = ch; - } - this.updateRange(y); - }; - Terminal.prototype.eraseLeft = function (x, y) { - var line = this.buffer.lines.get(this.buffer.ybase + y); - if (!line) { - return; - } - var ch = [this.eraseAttr(), ' ', 1, 32]; - x++; - while (x--) { - line[x] = ch; - } - this.updateRange(y); - }; - Terminal.prototype.clear = function () { - if (this.buffer.ybase === 0 && this.buffer.y === 0) { - return; - } - this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)); - this.buffer.lines.length = 1; - this.buffer.ydisp = 0; - this.buffer.ybase = 0; - this.buffer.y = 0; - for (var i = 1; i < this.rows; i++) { - this.buffer.lines.push(this.blankLine()); - } - this.refresh(0, this.rows - 1); - this.emit('scroll', this.buffer.ydisp); - }; - Terminal.prototype.eraseLine = function (y) { - this.eraseRight(0, y); - }; - Terminal.prototype.blankLine = function (cur, isWrapped, cols) { - var attr = cur ? this.eraseAttr() : this.defAttr; - var ch = [attr, ' ', 1, 32]; - var line = []; - if (isWrapped) { - line.isWrapped = isWrapped; - } - cols = cols || this.cols; - for (var i = 0; i < cols; i++) { - line[i] = ch; - } - return line; - }; - Terminal.prototype.ch = function (cur) { - if (cur) { - return [this.eraseAttr(), ' ', 1, 32]; - } - return [this.defAttr, ' ', 1, 32]; - }; - Terminal.prototype.is = function (term) { - return (this.options.termName + '').indexOf(term) === 0; - }; - Terminal.prototype.handler = function (data) { - if (this.options.disableStdin) { - return; - } - if (this.selectionManager && this.selectionManager.hasSelection) { - this.selectionManager.clearSelection(); - } - if (this.buffer.ybase !== this.buffer.ydisp) { - this.scrollToBottom(); - } - this.emit('data', data); - }; - Terminal.prototype.handleTitle = function (title) { - this.emit('title', title); - }; - Terminal.prototype.index = function () { - this.buffer.y++; - if (this.buffer.y > this.buffer.scrollBottom) { - this.buffer.y--; - this.scroll(); - } - if (this.buffer.x >= this.cols) { - this.buffer.x--; - } - }; - Terminal.prototype.reverseIndex = function () { - if (this.buffer.y === this.buffer.scrollTop) { - var scrollRegionHeight = this.buffer.scrollBottom - this.buffer.scrollTop; - this.buffer.lines.shiftElements(this.buffer.y + this.buffer.ybase, scrollRegionHeight, 1); - this.buffer.lines.set(this.buffer.y + this.buffer.ybase, this.blankLine(true)); - this.updateRange(this.buffer.scrollTop); - this.updateRange(this.buffer.scrollBottom); - } - else { - this.buffer.y--; - } - }; - Terminal.prototype.reset = function () { - this.options.rows = this.rows; - this.options.cols = this.cols; - var customKeyEventHandler = this.customKeyEventHandler; - var inputHandler = this.inputHandler; - var buffers = this.buffers; - this.setup(); - this.customKeyEventHandler = customKeyEventHandler; - this.inputHandler = inputHandler; - this.buffers = buffers; - this.refresh(0, this.rows - 1); - this.viewport.syncScrollArea(); - }; - Terminal.prototype.tabSet = function () { - this.buffer.tabs[this.buffer.x] = true; - }; - Terminal.prototype.cancel = function (ev, force) { - if (!this.options.cancelEvents && !force) { - return; - } - ev.preventDefault(); - ev.stopPropagation(); - return false; - }; - Terminal.prototype.matchColor = function (r1, g1, b1) { - return matchColor_(r1, g1, b1); - }; - Terminal.prototype.visualBell = function () { - return this.options.bellStyle === 'visual' || - this.options.bellStyle === 'both'; - }; - Terminal.prototype.soundBell = function () { - return this.options.bellStyle === 'sound' || - this.options.bellStyle === 'both'; - }; - Terminal.prototype.syncBellSound = function () { - if (this.soundBell() && this.bellAudioElement) { - this.bellAudioElement.setAttribute('src', this.options.bellSound); - } - else if (this.soundBell()) { - this.bellAudioElement = document.createElement('audio'); - this.bellAudioElement.setAttribute('preload', 'auto'); - this.bellAudioElement.setAttribute('src', this.options.bellSound); - this.helperContainer.appendChild(this.bellAudioElement); - } - else if (this.bellAudioElement) { - this.helperContainer.removeChild(this.bellAudioElement); - } - }; - return Terminal; -}(EventEmitter_1.EventEmitter)); -exports.Terminal = Terminal; -function globalOn(el, type, handler, capture) { - if (!Array.isArray(el)) { - el = [el]; - } - el.forEach(function (element) { - element.addEventListener(type, handler, capture || false); - }); -} -var on = globalOn; -function off(el, type, handler, capture) { - if (capture === void 0) { capture = false; } - el.removeEventListener(type, handler, capture); -} -function isThirdLevelShift(browser, ev) { - var thirdLevelKey = (browser.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) || - (browser.isMSWindows && ev.altKey && ev.ctrlKey && !ev.metaKey); - if (ev.type === 'keypress') { - return thirdLevelKey; - } - return thirdLevelKey && (!ev.keyCode || ev.keyCode > 47); -} -function wasMondifierKeyOnlyEvent(ev) { - return ev.keyCode === 16 || - ev.keyCode === 17 || - ev.keyCode === 18; -} -var vcolors = (function () { - var result = ColorManager_1.DEFAULT_ANSI_COLORS.map(function (c) { - c = c.substring(1); - return [ - parseInt(c.substring(0, 2), 16), - parseInt(c.substring(2, 4), 16), - parseInt(c.substring(4, 6), 16) - ]; - }); - var r = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]; - for (var i = 0; i < 216; i++) { - result.push([ - r[(i / 36) % 6 | 0], - r[(i / 6) % 6 | 0], - r[i % 6] - ]); - } - var c; - for (var i = 0; i < 24; i++) { - c = 8 + i * 10; - result.push([c, c, c]); - } - return result; -})(); -var matchColorCache = {}; -function matchColorDistance(r1, g1, b1, r2, g2, b2) { - return Math.pow(30 * (r1 - r2), 2) - + Math.pow(59 * (g1 - g2), 2) - + Math.pow(11 * (b1 - b2), 2); -} -; -function matchColor_(r1, g1, b1) { - var hash = (r1 << 16) | (g1 << 8) | b1; - if (matchColorCache[hash] != null) { - return matchColorCache[hash]; - } - var ldiff = Infinity; - var li = -1; - var i = 0; - var c; - var r2; - var g2; - var b2; - var diff; - for (; i < vcolors.length; i++) { - c = vcolors[i]; - r2 = c[0]; - g2 = c[1]; - b2 = c[2]; - diff = matchColorDistance(r1, g1, b1, r2, g2, b2); - if (diff === 0) { - li = i; - break; - } - if (diff < ldiff) { - ldiff = diff; - li = i; - } - } - return matchColorCache[hash] = li; -} - - - -},{"./Buffer":1,"./BufferSet":2,"./CompositionHelper":5,"./EscapeSequences":6,"./EventEmitter":7,"./InputHandler":8,"./Linkifier":9,"./Parser":10,"./SelectionManager":11,"./Viewport":15,"./handlers/Clipboard":16,"./input/MouseZoneManager":17,"./renderer/CharAtlas":19,"./renderer/ColorManager":20,"./renderer/Renderer":24,"./utils/Browser":28,"./utils/CharMeasure":29,"./utils/MouseHelper":32,"./utils/Sounds":33}],14:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var LinkHoverEventTypes; -(function (LinkHoverEventTypes) { - LinkHoverEventTypes["HOVER"] = "linkhover"; - LinkHoverEventTypes["TOOLTIP"] = "linktooltip"; - LinkHoverEventTypes["LEAVE"] = "linkleave"; -})(LinkHoverEventTypes = exports.LinkHoverEventTypes || (exports.LinkHoverEventTypes = {})); -; - - - -},{}],15:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var Viewport = (function () { - function Viewport(terminal, viewportElement, scrollArea, charMeasure) { - var _this = this; - this.terminal = terminal; - this.viewportElement = viewportElement; - this.scrollArea = scrollArea; - this.charMeasure = charMeasure; - this.currentRowHeight = 0; - this.lastRecordedBufferLength = 0; - this.lastRecordedViewportHeight = 0; - this.lastRecordedBufferHeight = 0; - this.viewportElement.addEventListener('scroll', this.onScroll.bind(this)); - setTimeout(function () { return _this.syncScrollArea(); }, 0); - } - Viewport.prototype.onThemeChanged = function (colors) { - this.viewportElement.style.backgroundColor = colors.background; - }; - Viewport.prototype.refresh = function () { - if (this.charMeasure.height > 0) { - this.currentRowHeight = this.terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio; - if (this.lastRecordedViewportHeight !== this.terminal.renderer.dimensions.canvasHeight) { - this.lastRecordedViewportHeight = this.terminal.renderer.dimensions.canvasHeight; - this.viewportElement.style.height = this.lastRecordedViewportHeight + 'px'; - } - var newBufferHeight = Math.round(this.currentRowHeight * this.lastRecordedBufferLength); - if (this.lastRecordedBufferHeight !== newBufferHeight) { - this.lastRecordedBufferHeight = newBufferHeight; - this.scrollArea.style.height = this.lastRecordedBufferHeight + 'px'; - } - } - }; - Viewport.prototype.syncScrollArea = function () { - if (this.lastRecordedBufferLength !== this.terminal.buffer.lines.length) { - this.lastRecordedBufferLength = this.terminal.buffer.lines.length; - this.refresh(); - } - else if (this.lastRecordedViewportHeight !== this.terminal.renderer.dimensions.canvasHeight) { - this.refresh(); - } - else { - if (this.terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio !== this.currentRowHeight) { - this.refresh(); - } - } - var scrollTop = this.terminal.buffer.ydisp * this.currentRowHeight; - if (this.viewportElement.scrollTop !== scrollTop) { - this.viewportElement.scrollTop = scrollTop; - } - }; - Viewport.prototype.onScroll = function (ev) { - var newRow = Math.round(this.viewportElement.scrollTop / this.currentRowHeight); - var diff = newRow - this.terminal.buffer.ydisp; - this.terminal.scrollLines(diff, true); - }; - Viewport.prototype.onWheel = function (ev) { - if (ev.deltaY === 0) { - return; - } - var multiplier = 1; - if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) { - multiplier = this.currentRowHeight; - } - else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) { - multiplier = this.currentRowHeight * this.terminal.rows; - } - this.viewportElement.scrollTop += ev.deltaY * multiplier; - ev.preventDefault(); - }; - ; - Viewport.prototype.onTouchStart = function (ev) { - this.lastTouchY = ev.touches[0].pageY; - }; - ; - Viewport.prototype.onTouchMove = function (ev) { - var deltaY = this.lastTouchY - ev.touches[0].pageY; - this.lastTouchY = ev.touches[0].pageY; - if (deltaY === 0) { - return; - } - this.viewportElement.scrollTop += deltaY; - ev.preventDefault(); - }; - ; - return Viewport; -}()); -exports.Viewport = Viewport; - - - -},{}],16:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -function prepareTextForTerminal(text, isMSWindows) { - if (isMSWindows) { - return text.replace(/\r?\n/g, '\r'); - } - return text; -} -exports.prepareTextForTerminal = prepareTextForTerminal; -function bracketTextForPaste(text, bracketedPasteMode) { - if (bracketedPasteMode) { - return '\x1b[200~' + text + '\x1b[201~'; - } - return text; -} -exports.bracketTextForPaste = bracketTextForPaste; -function copyHandler(ev, term, selectionManager) { - if (term.browser.isMSIE) { - window.clipboardData.setData('Text', selectionManager.selectionText); - } - else { - ev.clipboardData.setData('text/plain', selectionManager.selectionText); - } - ev.preventDefault(); -} -exports.copyHandler = copyHandler; -function pasteHandler(ev, term) { - ev.stopPropagation(); - var text; - var dispatchPaste = function (text) { - text = prepareTextForTerminal(text, term.browser.isMSWindows); - text = bracketTextForPaste(text, term.bracketedPasteMode); - term.handler(text); - term.textarea.value = ''; - term.emit('paste', text); - term.cancel(ev); - }; - if (term.browser.isMSIE) { - if (window.clipboardData) { - text = window.clipboardData.getData('Text'); - dispatchPaste(text); - } - } - else { - if (ev.clipboardData) { - text = ev.clipboardData.getData('text/plain'); - dispatchPaste(text); - } - } -} -exports.pasteHandler = pasteHandler; -function moveTextAreaUnderMouseCursor(ev, textarea) { - textarea.style.position = 'fixed'; - textarea.style.width = '20px'; - textarea.style.height = '20px'; - textarea.style.left = (ev.clientX - 10) + 'px'; - textarea.style.top = (ev.clientY - 10) + 'px'; - textarea.style.zIndex = '1000'; - textarea.focus(); - setTimeout(function () { - textarea.style.position = null; - textarea.style.width = null; - textarea.style.height = null; - textarea.style.left = null; - textarea.style.top = null; - textarea.style.zIndex = null; - }, 4); -} -exports.moveTextAreaUnderMouseCursor = moveTextAreaUnderMouseCursor; -function rightClickHandler(ev, textarea, selectionManager) { - moveTextAreaUnderMouseCursor(ev, textarea); - textarea.value = selectionManager.selectionText; - textarea.select(); -} -exports.rightClickHandler = rightClickHandler; - - - -},{}],17:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var HOVER_DURATION = 500; -var MouseZoneManager = (function () { - function MouseZoneManager(_terminal) { - var _this = this; - this._terminal = _terminal; - this._zones = []; - this._areZonesActive = false; - this._tooltipTimeout = null; - this._currentZone = null; - this._lastHoverCoords = [null, null]; - this._terminal.element.addEventListener('mousedown', function (e) { return _this._onMouseDown(e); }); - this._mouseMoveListener = function (e) { return _this._onMouseMove(e); }; - this._clickListener = function (e) { return _this._onClick(e); }; - } - MouseZoneManager.prototype.add = function (zone) { - this._zones.push(zone); - if (this._zones.length === 1) { - this._activate(); - } - }; - MouseZoneManager.prototype.clearAll = function (start, end) { - if (this._zones.length === 0) { - return; - } - if (!end) { - start = 0; - end = this._terminal.rows - 1; - } - for (var i = 0; i < this._zones.length; i++) { - var zone = this._zones[i]; - if (zone.y > start && zone.y <= end + 1) { - if (this._currentZone && this._currentZone === zone) { - this._currentZone.leaveCallback(); - this._currentZone = null; - } - this._zones.splice(i--, 1); - } - } - if (this._zones.length === 0) { - this._deactivate(); - } - }; - MouseZoneManager.prototype._activate = function () { - if (!this._areZonesActive) { - this._areZonesActive = true; - this._terminal.element.addEventListener('mousemove', this._mouseMoveListener); - this._terminal.element.addEventListener('click', this._clickListener); - } - }; - MouseZoneManager.prototype._deactivate = function () { - if (this._areZonesActive) { - this._areZonesActive = false; - this._terminal.element.removeEventListener('mousemove', this._mouseMoveListener); - this._terminal.element.removeEventListener('click', this._clickListener); - } - }; - MouseZoneManager.prototype._onMouseMove = function (e) { - if (this._lastHoverCoords[0] !== e.pageX || this._lastHoverCoords[1] !== e.pageY) { - this._onHover(e); - this._lastHoverCoords = [e.pageX, e.pageY]; - } - }; - MouseZoneManager.prototype._onHover = function (e) { - var _this = this; - var zone = this._findZoneEventAt(e); - if (zone === this._currentZone) { - return; - } - if (this._currentZone) { - this._currentZone.leaveCallback(); - this._currentZone = null; - if (this._tooltipTimeout) { - clearTimeout(this._tooltipTimeout); - } - } - if (!zone) { - return; - } - this._currentZone = zone; - if (zone.hoverCallback) { - zone.hoverCallback(e); - } - this._tooltipTimeout = setTimeout(function () { return _this._onTooltip(e); }, HOVER_DURATION); - }; - MouseZoneManager.prototype._onTooltip = function (e) { - this._tooltipTimeout = null; - var zone = this._findZoneEventAt(e); - if (zone && zone.tooltipCallback) { - zone.tooltipCallback(e); - } - }; - MouseZoneManager.prototype._onMouseDown = function (e) { - if (!this._areZonesActive) { - return; - } - var zone = this._findZoneEventAt(e); - if (zone) { - e.preventDefault(); - e.stopImmediatePropagation(); - } - }; - MouseZoneManager.prototype._onClick = function (e) { - var zone = this._findZoneEventAt(e); - if (zone) { - zone.clickCallback(e); - e.preventDefault(); - e.stopImmediatePropagation(); - } - }; - MouseZoneManager.prototype._findZoneEventAt = function (e) { - var coords = this._terminal.mouseHelper.getCoords(e, this._terminal.element, this._terminal.charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows); - if (!coords) { - return null; - } - for (var i = 0; i < this._zones.length; i++) { - var zone = this._zones[i]; - if (zone.y === coords[1] && zone.x1 <= coords[0] && zone.x2 > coords[0]) { - return zone; - } - } - ; - return null; - }; - return MouseZoneManager; -}()); -exports.MouseZoneManager = MouseZoneManager; -var MouseZone = (function () { - function MouseZone(x1, x2, y, clickCallback, hoverCallback, tooltipCallback, leaveCallback) { - this.x1 = x1; - this.x2 = x2; - this.y = y; - this.clickCallback = clickCallback; - this.hoverCallback = hoverCallback; - this.tooltipCallback = tooltipCallback; - this.leaveCallback = leaveCallback; - } - return MouseZone; -}()); -exports.MouseZone = MouseZone; - - - -},{}],18:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var CharAtlas_1 = require("./CharAtlas"); -var Buffer_1 = require("../Buffer"); -exports.INVERTED_DEFAULT_COLOR = -1; -var DIM_OPACITY = 0.5; -var BaseRenderLayer = (function () { - function BaseRenderLayer(container, id, zIndex, _alpha, _colors) { - this._alpha = _alpha; - this._colors = _colors; - this._scaledCharWidth = 0; - this._scaledCharHeight = 0; - this._scaledCellWidth = 0; - this._scaledCellHeight = 0; - this._scaledCharLeft = 0; - this._scaledCharTop = 0; - this._canvas = document.createElement('canvas'); - this._canvas.id = "xterm-" + id + "-layer"; - this._canvas.style.zIndex = zIndex.toString(); - this._ctx = this._canvas.getContext('2d', { alpha: _alpha }); - this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio); - if (!_alpha) { - this.clearAll(); - } - container.appendChild(this._canvas); - } - BaseRenderLayer.prototype.onOptionsChanged = function (terminal) { }; - BaseRenderLayer.prototype.onBlur = function (terminal) { }; - BaseRenderLayer.prototype.onFocus = function (terminal) { }; - BaseRenderLayer.prototype.onCursorMove = function (terminal) { }; - BaseRenderLayer.prototype.onGridChanged = function (terminal, startRow, endRow) { }; - BaseRenderLayer.prototype.onSelectionChanged = function (terminal, start, end) { }; - BaseRenderLayer.prototype.onThemeChanged = function (terminal, colorSet) { - this._refreshCharAtlas(terminal, colorSet); - }; - BaseRenderLayer.prototype._refreshCharAtlas = function (terminal, colorSet) { - var _this = this; - if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) { - return; - } - this._charAtlas = null; - var result = CharAtlas_1.acquireCharAtlas(terminal, this._colors, this._scaledCharWidth, this._scaledCharHeight); - if (result instanceof HTMLCanvasElement) { - this._charAtlas = result; - } - else { - result.then(function (bitmap) { return _this._charAtlas = bitmap; }); - } - }; - BaseRenderLayer.prototype.resize = function (terminal, dim, charSizeChanged) { - this._scaledCellWidth = dim.scaledCellWidth; - this._scaledCellHeight = dim.scaledCellHeight; - this._scaledCharWidth = dim.scaledCharWidth; - this._scaledCharHeight = dim.scaledCharHeight; - this._scaledCharLeft = dim.scaledCharLeft; - this._scaledCharTop = dim.scaledCharTop; - this._canvas.width = dim.scaledCanvasWidth; - this._canvas.height = dim.scaledCanvasHeight; - this._canvas.style.width = dim.canvasWidth + "px"; - this._canvas.style.height = dim.canvasHeight + "px"; - if (!this._alpha) { - this.clearAll(); - } - if (charSizeChanged) { - this._refreshCharAtlas(terminal, this._colors); - } - }; - BaseRenderLayer.prototype.fillCells = function (x, y, width, height) { - this._ctx.fillRect(x * this._scaledCellWidth, y * this._scaledCellHeight, width * this._scaledCellWidth, height * this._scaledCellHeight); - }; - BaseRenderLayer.prototype.fillBottomLineAtCells = function (x, y, width) { - if (width === void 0) { width = 1; } - this._ctx.fillRect(x * this._scaledCellWidth, (y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1, width * this._scaledCellWidth, window.devicePixelRatio); - }; - BaseRenderLayer.prototype.fillLeftLineAtCell = function (x, y) { - this._ctx.fillRect(x * this._scaledCellWidth, y * this._scaledCellHeight, window.devicePixelRatio, this._scaledCellHeight); - }; - BaseRenderLayer.prototype.strokeRectAtCell = function (x, y, width, height) { - this._ctx.lineWidth = window.devicePixelRatio; - this._ctx.strokeRect(x * this._scaledCellWidth + window.devicePixelRatio / 2, y * this._scaledCellHeight + (window.devicePixelRatio / 2), width * this._scaledCellWidth - window.devicePixelRatio, (height * this._scaledCellHeight) - window.devicePixelRatio); - }; - BaseRenderLayer.prototype.clearAll = function () { - if (this._alpha) { - this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); - } - else { - this._ctx.fillStyle = this._colors.background; - this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height); - } - }; - BaseRenderLayer.prototype.clearCells = function (x, y, width, height) { - if (this._alpha) { - this._ctx.clearRect(x * this._scaledCellWidth, y * this._scaledCellHeight, width * this._scaledCellWidth, height * this._scaledCellHeight); - } - else { - this._ctx.fillStyle = this._colors.background; - this._ctx.fillRect(x * this._scaledCellWidth, y * this._scaledCellHeight, width * this._scaledCellWidth, height * this._scaledCellHeight); - } - }; - BaseRenderLayer.prototype.fillCharTrueColor = function (terminal, charData, x, y) { - this._ctx.font = terminal.options.fontSize * window.devicePixelRatio + "px " + terminal.options.fontFamily; - this._ctx.textBaseline = 'top'; - this._clipRow(terminal, y); - this._ctx.fillText(charData[Buffer_1.CHAR_DATA_CHAR_INDEX], x * this._scaledCellWidth + this._scaledCharLeft, y * this._scaledCellHeight + this._scaledCharTop); - }; - BaseRenderLayer.prototype.drawChar = function (terminal, char, code, width, x, y, fg, bg, bold, dim) { - var colorIndex = 0; - if (fg < 256) { - colorIndex = fg + 2; - } - else { - if (bold && terminal.options.enableBold) { - colorIndex = 1; - } - } - var isAscii = code < 256; - var isBasicColor = (colorIndex > 1 && fg < 16) && (fg < 8 || bold); - var isDefaultColor = fg >= 256; - var isDefaultBackground = bg >= 256; - if (this._charAtlas && isAscii && (isBasicColor || isDefaultColor) && isDefaultBackground) { - var charAtlasCellWidth = this._scaledCharWidth + CharAtlas_1.CHAR_ATLAS_CELL_SPACING; - var charAtlasCellHeight = this._scaledCharHeight + CharAtlas_1.CHAR_ATLAS_CELL_SPACING; - if (dim) { - this._ctx.globalAlpha = DIM_OPACITY; - } - if (bold && !terminal.options.enableBold) { - if (colorIndex > 1) { - colorIndex -= 8; - } - } - this._ctx.drawImage(this._charAtlas, code * charAtlasCellWidth, colorIndex * charAtlasCellHeight, charAtlasCellWidth, this._scaledCharHeight, x * this._scaledCellWidth + this._scaledCharLeft, y * this._scaledCellHeight + this._scaledCharTop, charAtlasCellWidth, this._scaledCharHeight); - } - else { - this._drawUncachedChar(terminal, char, width, fg, x, y, bold, dim); - } - }; - BaseRenderLayer.prototype._drawUncachedChar = function (terminal, char, width, fg, x, y, bold, dim) { - this._ctx.save(); - this._ctx.font = terminal.options.fontSize * window.devicePixelRatio + "px " + terminal.options.fontFamily; - if (bold && terminal.options.enableBold) { - this._ctx.font = "bold " + this._ctx.font; - } - this._ctx.textBaseline = 'top'; - if (fg === exports.INVERTED_DEFAULT_COLOR) { - this._ctx.fillStyle = this._colors.background; - } - else if (fg < 256) { - this._ctx.fillStyle = this._colors.ansi[fg]; - } - else { - this._ctx.fillStyle = this._colors.foreground; - } - this._clipRow(terminal, y); - if (dim) { - this._ctx.globalAlpha = DIM_OPACITY; - } - this._ctx.fillText(char, x * this._scaledCellWidth + this._scaledCharLeft, y * this._scaledCellHeight + this._scaledCharTop); - this._ctx.restore(); - }; - BaseRenderLayer.prototype._clipRow = function (terminal, y) { - this._ctx.beginPath(); - this._ctx.rect(0, y * this._scaledCellHeight, terminal.cols * this._scaledCellWidth, this._scaledCellHeight); - this._ctx.clip(); - }; - return BaseRenderLayer; -}()); -exports.BaseRenderLayer = BaseRenderLayer; - - - -},{"../Buffer":1,"./CharAtlas":19}],19:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var Browser_1 = require("../utils/Browser"); -exports.CHAR_ATLAS_CELL_SPACING = 1; -var charAtlasCache = []; -function acquireCharAtlas(terminal, colors, scaledCharWidth, scaledCharHeight) { - var newConfig = generateConfig(scaledCharWidth, scaledCharHeight, terminal, colors); - for (var i = 0; i < charAtlasCache.length; i++) { - var entry = charAtlasCache[i]; - var ownedByIndex = entry.ownedBy.indexOf(terminal); - if (ownedByIndex >= 0) { - if (configEquals(entry.config, newConfig)) { - return entry.bitmap; - } - else { - if (entry.ownedBy.length === 1) { - charAtlasCache.splice(i, 1); - } - else { - entry.ownedBy.splice(ownedByIndex, 1); - } - break; - } - } - } - for (var i = 0; i < charAtlasCache.length; i++) { - var entry = charAtlasCache[i]; - if (configEquals(entry.config, newConfig)) { - entry.ownedBy.push(terminal); - return entry.bitmap; - } - } - var newEntry = { - bitmap: generator.generate(scaledCharWidth, scaledCharHeight, terminal.options.fontSize, terminal.options.fontFamily, colors.background, colors.foreground, colors.ansi), - config: newConfig, - ownedBy: [terminal] - }; - charAtlasCache.push(newEntry); - return newEntry.bitmap; -} -exports.acquireCharAtlas = acquireCharAtlas; -function generateConfig(scaledCharWidth, scaledCharHeight, terminal, colors) { - var clonedColors = { - foreground: colors.foreground, - background: colors.background, - cursor: null, - cursorAccent: null, - selection: null, - ansi: colors.ansi.slice(0, 16) - }; - return { - scaledCharWidth: scaledCharWidth, - scaledCharHeight: scaledCharHeight, - fontFamily: terminal.options.fontFamily, - fontSize: terminal.options.fontSize, - colors: clonedColors - }; -} -function configEquals(a, b) { - for (var i = 0; i < a.colors.ansi.length; i++) { - if (a.colors.ansi[i] !== b.colors.ansi[i]) { - return false; - } - } - return a.fontFamily === b.fontFamily && - a.fontSize === b.fontSize && - a.scaledCharWidth === b.scaledCharWidth && - a.scaledCharHeight === b.scaledCharHeight && - a.colors.foreground === b.colors.foreground && - a.colors.background === b.colors.background; -} -var generator; -function initialize(document) { - if (!generator) { - generator = new CharAtlasGenerator(document); - } -} -exports.initialize = initialize; -var CharAtlasGenerator = (function () { - function CharAtlasGenerator(_document) { - this._document = _document; - this._canvas = this._document.createElement('canvas'); - this._ctx = this._canvas.getContext('2d', { alpha: false }); - this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio); - } - CharAtlasGenerator.prototype.generate = function (scaledCharWidth, scaledCharHeight, fontSize, fontFamily, background, foreground, ansiColors) { - var cellWidth = scaledCharWidth + exports.CHAR_ATLAS_CELL_SPACING; - var cellHeight = scaledCharHeight + exports.CHAR_ATLAS_CELL_SPACING; - this._canvas.width = 255 * cellWidth; - this._canvas.height = (2 + 16) * cellHeight; - this._ctx.fillStyle = background; - this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height); - this._ctx.save(); - this._ctx.fillStyle = foreground; - this._ctx.font = fontSize * window.devicePixelRatio + "px " + fontFamily; - this._ctx.textBaseline = 'top'; - for (var i = 0; i < 256; i++) { - this._ctx.save(); - this._ctx.beginPath(); - this._ctx.rect(i * cellWidth, 0, cellWidth, cellHeight); - this._ctx.clip(); - this._ctx.fillText(String.fromCharCode(i), i * cellWidth, 0); - this._ctx.restore(); - } - this._ctx.save(); - this._ctx.font = "bold " + this._ctx.font; - for (var i = 0; i < 256; i++) { - this._ctx.save(); - this._ctx.beginPath(); - this._ctx.rect(i * cellWidth, cellHeight, cellWidth, cellHeight); - this._ctx.clip(); - this._ctx.fillText(String.fromCharCode(i), i * cellWidth, cellHeight); - this._ctx.restore(); - } - this._ctx.restore(); - this._ctx.font = fontSize * window.devicePixelRatio + "px " + fontFamily; - for (var colorIndex = 0; colorIndex < 16; colorIndex++) { - if (colorIndex === 8) { - this._ctx.font = "bold " + this._ctx.font; - } - var y = (colorIndex + 2) * cellHeight; - for (var i = 0; i < 256; i++) { - this._ctx.save(); - this._ctx.beginPath(); - this._ctx.rect(i * cellWidth, y, cellWidth, cellHeight); - this._ctx.clip(); - this._ctx.fillStyle = ansiColors[colorIndex]; - this._ctx.fillText(String.fromCharCode(i), i * cellWidth, y); - this._ctx.restore(); - } - } - this._ctx.restore(); - if (!('createImageBitmap' in window) || Browser_1.isFirefox) { - var result = this._canvas; - this._canvas = this._document.createElement('canvas'); - this._ctx = this._canvas.getContext('2d'); - this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio); - return result; - } - var charAtlasImageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height); - var r = parseInt(background.substr(1, 2), 16); - var g = parseInt(background.substr(3, 2), 16); - var b = parseInt(background.substr(5, 2), 16); - this._clearColor(charAtlasImageData, r, g, b); - var promise = window.createImageBitmap(charAtlasImageData); - this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); - return promise; - }; - CharAtlasGenerator.prototype._clearColor = function (imageData, r, g, b) { - for (var offset = 0; offset < imageData.data.length; offset += 4) { - if (imageData.data[offset] === r && - imageData.data[offset + 1] === g && - imageData.data[offset + 2] === b) { - imageData.data[offset + 3] = 0; - } - } - }; - return CharAtlasGenerator; -}()); - - - -},{"../utils/Browser":28}],20:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var DEFAULT_FOREGROUND = '#ffffff'; -var DEFAULT_BACKGROUND = '#333333'; -var DEFAULT_CURSOR = '#ffffff'; -var DEFAULT_CURSOR_ACCENT = '#333333'; -var DEFAULT_SELECTION = 'rgba(255, 255, 255, 0.3)'; -exports.DEFAULT_ANSI_COLORS = [ - '#2e3436', - '#cc0000', - '#4e9a06', - '#c4a000', - '#3465a4', - '#75507b', - '#06989a', - '#d3d7cf', - '#555753', - '#ef2929', - '#8ae234', - '#fce94f', - '#729fcf', - '#ad7fa8', - '#34e2e2', - '#eeeeec' -]; -function generate256Colors(first16Colors) { - var colors = first16Colors.slice(); - var v = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]; - for (var i = 0; i < 216; i++) { - var r = toPaddedHex(v[(i / 36) % 6 | 0]); - var g = toPaddedHex(v[(i / 6) % 6 | 0]); - var b = toPaddedHex(v[i % 6]); - colors.push("#" + r + g + b); - } - for (var i = 0; i < 24; i++) { - var c = toPaddedHex(8 + i * 10); - colors.push("#" + c + c + c); - } - return colors; -} -function toPaddedHex(c) { - var s = c.toString(16); - return s.length < 2 ? '0' + s : s; -} -var ColorManager = (function () { - function ColorManager() { - this.colors = { - foreground: DEFAULT_FOREGROUND, - background: DEFAULT_BACKGROUND, - cursor: DEFAULT_CURSOR, - cursorAccent: DEFAULT_CURSOR_ACCENT, - selection: DEFAULT_SELECTION, - ansi: generate256Colors(exports.DEFAULT_ANSI_COLORS) - }; - } - ColorManager.prototype.setTheme = function (theme) { - this.colors.foreground = theme.foreground || DEFAULT_FOREGROUND; - this.colors.background = this._validateColor(theme.background, DEFAULT_BACKGROUND); - this.colors.cursor = theme.cursor || DEFAULT_CURSOR; - this.colors.cursorAccent = theme.cursorAccent || DEFAULT_CURSOR_ACCENT; - this.colors.selection = theme.selection || DEFAULT_SELECTION; - this.colors.ansi[0] = theme.black || exports.DEFAULT_ANSI_COLORS[0]; - this.colors.ansi[1] = theme.red || exports.DEFAULT_ANSI_COLORS[1]; - this.colors.ansi[2] = theme.green || exports.DEFAULT_ANSI_COLORS[2]; - this.colors.ansi[3] = theme.yellow || exports.DEFAULT_ANSI_COLORS[3]; - this.colors.ansi[4] = theme.blue || exports.DEFAULT_ANSI_COLORS[4]; - this.colors.ansi[5] = theme.magenta || exports.DEFAULT_ANSI_COLORS[5]; - this.colors.ansi[6] = theme.cyan || exports.DEFAULT_ANSI_COLORS[6]; - this.colors.ansi[7] = theme.white || exports.DEFAULT_ANSI_COLORS[7]; - this.colors.ansi[8] = theme.brightBlack || exports.DEFAULT_ANSI_COLORS[8]; - this.colors.ansi[9] = theme.brightRed || exports.DEFAULT_ANSI_COLORS[9]; - this.colors.ansi[10] = theme.brightGreen || exports.DEFAULT_ANSI_COLORS[10]; - this.colors.ansi[11] = theme.brightYellow || exports.DEFAULT_ANSI_COLORS[11]; - this.colors.ansi[12] = theme.brightBlue || exports.DEFAULT_ANSI_COLORS[12]; - this.colors.ansi[13] = theme.brightMagenta || exports.DEFAULT_ANSI_COLORS[13]; - this.colors.ansi[14] = theme.brightCyan || exports.DEFAULT_ANSI_COLORS[14]; - this.colors.ansi[15] = theme.brightWhite || exports.DEFAULT_ANSI_COLORS[15]; - }; - ColorManager.prototype._validateColor = function (color, fallback) { - if (!color) { - return fallback; - } - if (color.length === 7 && color.charAt(0) === '#') { - return color; - } - if (color.length === 4 && color.charAt(0) === '#') { - var r = color.charAt(1); - var g = color.charAt(2); - var b = color.charAt(3); - return "#" + r + r + g + g + b + b; - } - return fallback; - }; - return ColorManager; -}()); -exports.ColorManager = ColorManager; - - - -},{}],21:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var Buffer_1 = require("../Buffer"); -var BaseRenderLayer_1 = require("./BaseRenderLayer"); -var BLINK_INTERVAL = 600; -var CursorRenderLayer = (function (_super) { - __extends(CursorRenderLayer, _super); - function CursorRenderLayer(container, zIndex, colors) { - var _this = _super.call(this, container, 'cursor', zIndex, true, colors) || this; - _this._state = { - x: null, - y: null, - isFocused: null, - style: null, - width: null, - }; - _this._cursorRenderers = { - 'bar': _this._renderBarCursor.bind(_this), - 'block': _this._renderBlockCursor.bind(_this), - 'underline': _this._renderUnderlineCursor.bind(_this) - }; - return _this; - } - CursorRenderLayer.prototype.resize = function (terminal, dim, charSizeChanged) { - _super.prototype.resize.call(this, terminal, dim, charSizeChanged); - this._state = { - x: null, - y: null, - isFocused: null, - style: null, - width: null, - }; - }; - CursorRenderLayer.prototype.reset = function (terminal) { - this._clearCursor(); - if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.dispose(); - this._cursorBlinkStateManager = null; - this.onOptionsChanged(terminal); - } - }; - CursorRenderLayer.prototype.onBlur = function (terminal) { - if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.pause(); - } - terminal.refresh(terminal.buffer.y, terminal.buffer.y); - }; - CursorRenderLayer.prototype.onFocus = function (terminal) { - if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.resume(terminal); - } - else { - terminal.refresh(terminal.buffer.y, terminal.buffer.y); - } - }; - CursorRenderLayer.prototype.onOptionsChanged = function (terminal) { - var _this = this; - if (terminal.options.cursorBlink) { - if (!this._cursorBlinkStateManager) { - this._cursorBlinkStateManager = new CursorBlinkStateManager(terminal, function () { - _this._render(terminal, true); - }); - } - } - else { - if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.dispose(); - this._cursorBlinkStateManager = null; - } - terminal.refresh(terminal.buffer.y, terminal.buffer.y); - } - }; - CursorRenderLayer.prototype.onCursorMove = function (terminal) { - if (this._cursorBlinkStateManager) { - this._cursorBlinkStateManager.restartBlinkAnimation(terminal); - } - }; - CursorRenderLayer.prototype.onGridChanged = function (terminal, startRow, endRow) { - if (!this._cursorBlinkStateManager || this._cursorBlinkStateManager.isPaused) { - this._render(terminal, false); - } - }; - CursorRenderLayer.prototype._render = function (terminal, triggeredByAnimationFrame) { - if (!terminal.cursorState || terminal.cursorHidden) { - this._clearCursor(); - return; - } - var cursorY = terminal.buffer.ybase + terminal.buffer.y; - var viewportRelativeCursorY = cursorY - terminal.buffer.ydisp; - if (viewportRelativeCursorY < 0 || viewportRelativeCursorY >= terminal.rows) { - this._clearCursor(); - return; - } - var charData = terminal.buffer.lines.get(cursorY)[terminal.buffer.x]; - if (!charData) { - return; - } - if (!terminal.isFocused) { - this._clearCursor(); - this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor; - this._renderBlurCursor(terminal, terminal.buffer.x, viewportRelativeCursorY, charData); - this._ctx.restore(); - this._state.x = terminal.buffer.x; - this._state.y = viewportRelativeCursorY; - this._state.isFocused = false; - this._state.style = terminal.options.cursorStyle; - this._state.width = charData[Buffer_1.CHAR_DATA_WIDTH_INDEX]; - return; - } - if (this._cursorBlinkStateManager && !this._cursorBlinkStateManager.isCursorVisible) { - this._clearCursor(); - return; - } - if (this._state) { - if (this._state.x === terminal.buffer.x && - this._state.y === viewportRelativeCursorY && - this._state.isFocused === terminal.isFocused && - this._state.style === terminal.options.cursorStyle && - this._state.width === charData[Buffer_1.CHAR_DATA_WIDTH_INDEX]) { - return; - } - this._clearCursor(); - } - this._ctx.save(); - this._cursorRenderers[terminal.options.cursorStyle || 'block'](terminal, terminal.buffer.x, viewportRelativeCursorY, charData); - this._ctx.restore(); - this._state.x = terminal.buffer.x; - this._state.y = viewportRelativeCursorY; - this._state.isFocused = false; - this._state.style = terminal.options.cursorStyle; - this._state.width = charData[Buffer_1.CHAR_DATA_WIDTH_INDEX]; - }; - CursorRenderLayer.prototype._clearCursor = function () { - if (this._state) { - this.clearCells(this._state.x, this._state.y, this._state.width, 1); - this._state = { - x: null, - y: null, - isFocused: null, - style: null, - width: null, - }; - } - }; - CursorRenderLayer.prototype._renderBarCursor = function (terminal, x, y, charData) { - this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor; - this.fillLeftLineAtCell(x, y); - this._ctx.restore(); - }; - CursorRenderLayer.prototype._renderBlockCursor = function (terminal, x, y, charData) { - this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor; - this.fillCells(x, y, charData[Buffer_1.CHAR_DATA_WIDTH_INDEX], 1); - this._ctx.fillStyle = this._colors.cursorAccent; - this.fillCharTrueColor(terminal, charData, x, y); - this._ctx.restore(); - }; - CursorRenderLayer.prototype._renderUnderlineCursor = function (terminal, x, y, charData) { - this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor; - this.fillBottomLineAtCells(x, y); - this._ctx.restore(); - }; - CursorRenderLayer.prototype._renderBlurCursor = function (terminal, x, y, charData) { - this._ctx.save(); - this._ctx.strokeStyle = this._colors.cursor; - this.strokeRectAtCell(x, y, charData[Buffer_1.CHAR_DATA_WIDTH_INDEX], 1); - this._ctx.restore(); - }; - return CursorRenderLayer; -}(BaseRenderLayer_1.BaseRenderLayer)); -exports.CursorRenderLayer = CursorRenderLayer; -var CursorBlinkStateManager = (function () { - function CursorBlinkStateManager(terminal, renderCallback) { - this.renderCallback = renderCallback; - this.isCursorVisible = true; - if (terminal.isFocused) { - this._restartInterval(); - } - } - Object.defineProperty(CursorBlinkStateManager.prototype, "isPaused", { - get: function () { return !(this._blinkStartTimeout || this._blinkInterval); }, - enumerable: true, - configurable: true - }); - CursorBlinkStateManager.prototype.dispose = function () { - if (this._blinkInterval) { - window.clearInterval(this._blinkInterval); - this._blinkInterval = null; - } - if (this._blinkStartTimeout) { - window.clearTimeout(this._blinkStartTimeout); - this._blinkStartTimeout = null; - } - if (this._animationFrame) { - window.cancelAnimationFrame(this._animationFrame); - this._animationFrame = null; - } - }; - CursorBlinkStateManager.prototype.restartBlinkAnimation = function (terminal) { - var _this = this; - if (this.isPaused) { - return; - } - this._animationTimeRestarted = Date.now(); - this.isCursorVisible = true; - if (!this._animationFrame) { - this._animationFrame = window.requestAnimationFrame(function () { - _this.renderCallback(); - _this._animationFrame = null; - }); - } - }; - CursorBlinkStateManager.prototype._restartInterval = function (timeToStart) { - var _this = this; - if (timeToStart === void 0) { timeToStart = BLINK_INTERVAL; } - if (this._blinkInterval) { - window.clearInterval(this._blinkInterval); - } - this._blinkStartTimeout = setTimeout(function () { - if (_this._animationTimeRestarted) { - var time = BLINK_INTERVAL - (Date.now() - _this._animationTimeRestarted); - _this._animationTimeRestarted = null; - if (time > 0) { - _this._restartInterval(time); - return; - } - } - _this.isCursorVisible = false; - _this._animationFrame = window.requestAnimationFrame(function () { - _this.renderCallback(); - _this._animationFrame = null; - }); - _this._blinkInterval = setInterval(function () { - if (_this._animationTimeRestarted) { - var time = BLINK_INTERVAL - (Date.now() - _this._animationTimeRestarted); - _this._animationTimeRestarted = null; - _this._restartInterval(time); - return; - } - _this.isCursorVisible = !_this.isCursorVisible; - _this._animationFrame = window.requestAnimationFrame(function () { - _this.renderCallback(); - _this._animationFrame = null; - }); - }, BLINK_INTERVAL); - }, timeToStart); - }; - CursorBlinkStateManager.prototype.pause = function () { - this.isCursorVisible = true; - if (this._blinkInterval) { - window.clearInterval(this._blinkInterval); - this._blinkInterval = null; - } - if (this._blinkStartTimeout) { - window.clearTimeout(this._blinkStartTimeout); - this._blinkStartTimeout = null; - } - if (this._animationFrame) { - window.cancelAnimationFrame(this._animationFrame); - this._animationFrame = null; - } - }; - CursorBlinkStateManager.prototype.resume = function (terminal) { - this._animationTimeRestarted = null; - this._restartInterval(); - this.restartBlinkAnimation(terminal); - }; - return CursorBlinkStateManager; -}()); - - - -},{"../Buffer":1,"./BaseRenderLayer":18}],22:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var GridCache = (function () { - function GridCache() { - this.cache = []; - } - GridCache.prototype.resize = function (width, height) { - for (var x = 0; x < width; x++) { - if (this.cache.length <= x) { - this.cache.push([]); - } - for (var y = this.cache[x].length; y < height; y++) { - this.cache[x].push(null); - } - this.cache[x].length = height; - } - this.cache.length = width; - }; - GridCache.prototype.clear = function () { - for (var x = 0; x < this.cache.length; x++) { - for (var y = 0; y < this.cache[x].length; y++) { - this.cache[x][y] = null; - } - } - }; - return GridCache; -}()); -exports.GridCache = GridCache; - - - -},{}],23:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var BaseRenderLayer_1 = require("./BaseRenderLayer"); -var Types_1 = require("../Types"); -var LinkRenderLayer = (function (_super) { - __extends(LinkRenderLayer, _super); - function LinkRenderLayer(container, zIndex, colors, terminal) { - var _this = _super.call(this, container, 'link', zIndex, true, colors) || this; - _this._state = null; - terminal.linkifier.on(Types_1.LinkHoverEventTypes.HOVER, function (e) { return _this._onLinkHover(e); }); - terminal.linkifier.on(Types_1.LinkHoverEventTypes.LEAVE, function (e) { return _this._onLinkLeave(e); }); - return _this; - } - LinkRenderLayer.prototype.resize = function (terminal, dim, charSizeChanged) { - _super.prototype.resize.call(this, terminal, dim, charSizeChanged); - this._state = null; - }; - LinkRenderLayer.prototype.reset = function (terminal) { - this._clearCurrentLink(); - }; - LinkRenderLayer.prototype._clearCurrentLink = function () { - if (this._state) { - this.clearCells(this._state.x, this._state.y, this._state.length, 1); - this._state = null; - } - }; - LinkRenderLayer.prototype._onLinkHover = function (e) { - this._ctx.fillStyle = this._colors.foreground; - this.fillBottomLineAtCells(e.x, e.y, e.length); - this._state = e; - }; - LinkRenderLayer.prototype._onLinkLeave = function (e) { - this._clearCurrentLink(); - }; - return LinkRenderLayer; -}(BaseRenderLayer_1.BaseRenderLayer)); -exports.LinkRenderLayer = LinkRenderLayer; - - - -},{"../Types":14,"./BaseRenderLayer":18}],24:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var TextRenderLayer_1 = require("./TextRenderLayer"); -var SelectionRenderLayer_1 = require("./SelectionRenderLayer"); -var CursorRenderLayer_1 = require("./CursorRenderLayer"); -var ColorManager_1 = require("./ColorManager"); -var LinkRenderLayer_1 = require("./LinkRenderLayer"); -var EventEmitter_1 = require("../EventEmitter"); -var Renderer = (function (_super) { - __extends(Renderer, _super); - function Renderer(_terminal, theme) { - var _this = _super.call(this) || this; - _this._terminal = _terminal; - _this._refreshRowsQueue = []; - _this._refreshAnimationFrame = null; - _this.colorManager = new ColorManager_1.ColorManager(); - if (theme) { - _this.colorManager.setTheme(theme); - } - _this._renderLayers = [ - new TextRenderLayer_1.TextRenderLayer(_this._terminal.element, 0, _this.colorManager.colors), - new SelectionRenderLayer_1.SelectionRenderLayer(_this._terminal.element, 1, _this.colorManager.colors), - new LinkRenderLayer_1.LinkRenderLayer(_this._terminal.element, 2, _this.colorManager.colors, _this._terminal), - new CursorRenderLayer_1.CursorRenderLayer(_this._terminal.element, 3, _this.colorManager.colors) - ]; - _this.dimensions = { - scaledCharWidth: null, - scaledCharHeight: null, - scaledCellWidth: null, - scaledCellHeight: null, - scaledCharLeft: null, - scaledCharTop: null, - scaledCanvasWidth: null, - scaledCanvasHeight: null, - canvasWidth: null, - canvasHeight: null, - actualCellWidth: null, - actualCellHeight: null - }; - _this._devicePixelRatio = window.devicePixelRatio; - return _this; - } - Renderer.prototype.onWindowResize = function (devicePixelRatio) { - if (this._devicePixelRatio !== devicePixelRatio) { - this._devicePixelRatio = devicePixelRatio; - this.onResize(this._terminal.cols, this._terminal.rows, true); - } - }; - Renderer.prototype.setTheme = function (theme) { - var _this = this; - this.colorManager.setTheme(theme); - this._renderLayers.forEach(function (l) { - l.onThemeChanged(_this._terminal, _this.colorManager.colors); - l.reset(_this._terminal); - }); - this._terminal.refresh(0, this._terminal.rows - 1); - return this.colorManager.colors; - }; - Renderer.prototype.onResize = function (cols, rows, didCharSizeChange) { - var _this = this; - if (!this._terminal.charMeasure.width || !this._terminal.charMeasure.height) { - return; - } - this.dimensions.scaledCharWidth = Math.floor(this._terminal.charMeasure.width * window.devicePixelRatio); - this.dimensions.scaledCharHeight = Math.ceil(this._terminal.charMeasure.height * window.devicePixelRatio); - this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._terminal.options.lineHeight); - this.dimensions.scaledCharTop = this._terminal.options.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2); - this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._terminal.options.letterSpacing); - this.dimensions.scaledCharLeft = Math.floor(this._terminal.options.letterSpacing / 2); - this.dimensions.scaledCanvasHeight = this._terminal.rows * this.dimensions.scaledCellHeight; - this.dimensions.scaledCanvasWidth = this._terminal.cols * this.dimensions.scaledCellWidth; - this.dimensions.canvasHeight = Math.round(this.dimensions.scaledCanvasHeight / window.devicePixelRatio); - this.dimensions.canvasWidth = Math.round(this.dimensions.scaledCanvasWidth / window.devicePixelRatio); - this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._terminal.rows; - this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._terminal.cols; - this._renderLayers.forEach(function (l) { return l.resize(_this._terminal, _this.dimensions, didCharSizeChange); }); - this._terminal.refresh(0, this._terminal.rows - 1); - this.emit('resize', { - width: this.dimensions.canvasWidth, - height: this.dimensions.canvasHeight - }); - }; - Renderer.prototype.onCharSizeChanged = function () { - this.onResize(this._terminal.cols, this._terminal.rows, true); - }; - Renderer.prototype.onBlur = function () { - var _this = this; - this._renderLayers.forEach(function (l) { return l.onBlur(_this._terminal); }); - }; - Renderer.prototype.onFocus = function () { - var _this = this; - this._renderLayers.forEach(function (l) { return l.onFocus(_this._terminal); }); - }; - Renderer.prototype.onSelectionChanged = function (start, end) { - var _this = this; - this._renderLayers.forEach(function (l) { return l.onSelectionChanged(_this._terminal, start, end); }); - }; - Renderer.prototype.onCursorMove = function () { - var _this = this; - this._renderLayers.forEach(function (l) { return l.onCursorMove(_this._terminal); }); - }; - Renderer.prototype.onOptionsChanged = function () { - var _this = this; - this._renderLayers.forEach(function (l) { return l.onOptionsChanged(_this._terminal); }); - }; - Renderer.prototype.clear = function () { - var _this = this; - this._renderLayers.forEach(function (l) { return l.reset(_this._terminal); }); - }; - Renderer.prototype.queueRefresh = function (start, end) { - this._refreshRowsQueue.push({ start: start, end: end }); - if (!this._refreshAnimationFrame) { - this._refreshAnimationFrame = window.requestAnimationFrame(this._refreshLoop.bind(this)); - } - }; - Renderer.prototype._refreshLoop = function () { - var _this = this; - var start; - var end; - if (this._refreshRowsQueue.length > 4) { - start = 0; - end = this._terminal.rows - 1; - } - else { - start = this._refreshRowsQueue[0].start; - end = this._refreshRowsQueue[0].end; - for (var i = 1; i < this._refreshRowsQueue.length; i++) { - if (this._refreshRowsQueue[i].start < start) { - start = this._refreshRowsQueue[i].start; - } - if (this._refreshRowsQueue[i].end > end) { - end = this._refreshRowsQueue[i].end; - } - } - } - this._refreshRowsQueue = []; - this._refreshAnimationFrame = null; - start = Math.max(start, 0); - end = Math.min(end, this._terminal.rows - 1); - this._renderLayers.forEach(function (l) { return l.onGridChanged(_this._terminal, start, end); }); - this._terminal.emit('refresh', { start: start, end: end }); - }; - return Renderer; -}(EventEmitter_1.EventEmitter)); -exports.Renderer = Renderer; - - - -},{"../EventEmitter":7,"./ColorManager":20,"./CursorRenderLayer":21,"./LinkRenderLayer":23,"./SelectionRenderLayer":25,"./TextRenderLayer":26}],25:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var BaseRenderLayer_1 = require("./BaseRenderLayer"); -var SelectionRenderLayer = (function (_super) { - __extends(SelectionRenderLayer, _super); - function SelectionRenderLayer(container, zIndex, colors) { - var _this = _super.call(this, container, 'selection', zIndex, true, colors) || this; - _this._state = { - start: null, - end: null - }; - return _this; - } - SelectionRenderLayer.prototype.resize = function (terminal, dim, charSizeChanged) { - _super.prototype.resize.call(this, terminal, dim, charSizeChanged); - this._state = { - start: null, - end: null - }; - }; - SelectionRenderLayer.prototype.reset = function (terminal) { - if (this._state.start && this._state.end) { - this._state = { - start: null, - end: null - }; - this.clearAll(); - } - }; - SelectionRenderLayer.prototype.onSelectionChanged = function (terminal, start, end) { - if (this._state.start === start || this._state.end === end) { - return; - } - this.clearAll(); - if (!start || !end) { - return; - } - var viewportStartRow = start[1] - terminal.buffer.ydisp; - var viewportEndRow = end[1] - terminal.buffer.ydisp; - var viewportCappedStartRow = Math.max(viewportStartRow, 0); - var viewportCappedEndRow = Math.min(viewportEndRow, terminal.rows - 1); - if (viewportCappedStartRow >= terminal.rows || viewportCappedEndRow < 0) { - return; - } - var startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0; - var startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : terminal.cols; - this._ctx.fillStyle = this._colors.selection; - this.fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1); - var middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0); - this.fillCells(0, viewportCappedStartRow + 1, terminal.cols, middleRowsCount); - if (viewportCappedStartRow !== viewportCappedEndRow) { - var endCol = viewportEndRow === viewportCappedEndRow ? end[0] : terminal.cols; - this.fillCells(0, viewportCappedEndRow, endCol, 1); - } - this._state.start = [start[0], start[1]]; - this._state.end = [end[0], end[1]]; - }; - return SelectionRenderLayer; -}(BaseRenderLayer_1.BaseRenderLayer)); -exports.SelectionRenderLayer = SelectionRenderLayer; - - - -},{"./BaseRenderLayer":18}],26:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var Buffer_1 = require("../Buffer"); -var Types_1 = require("./Types"); -var GridCache_1 = require("./GridCache"); -var BaseRenderLayer_1 = require("./BaseRenderLayer"); -var OVERLAP_OWNED_CHAR_DATA = [null, '', 0, -1]; -var TextRenderLayer = (function (_super) { - __extends(TextRenderLayer, _super); - function TextRenderLayer(container, zIndex, colors) { - var _this = _super.call(this, container, 'text', zIndex, false, colors) || this; - _this._characterOverlapCache = {}; - _this._state = new GridCache_1.GridCache(); - return _this; - } - TextRenderLayer.prototype.resize = function (terminal, dim, charSizeChanged) { - _super.prototype.resize.call(this, terminal, dim, charSizeChanged); - var terminalFont = terminal.options.fontSize * window.devicePixelRatio + "px " + terminal.options.fontFamily; - if (this._characterWidth !== dim.scaledCharWidth || this._characterFont !== terminalFont) { - this._characterWidth = dim.scaledCharWidth; - this._characterFont = terminalFont; - this._characterOverlapCache = {}; - } - this._state.clear(); - this._state.resize(terminal.cols, terminal.rows); - }; - TextRenderLayer.prototype.reset = function (terminal) { - this._state.clear(); - this.clearAll(); - }; - TextRenderLayer.prototype.onGridChanged = function (terminal, startRow, endRow) { - if (this._state.cache.length === 0) { - return; - } - for (var y = startRow; y <= endRow; y++) { - var row = y + terminal.buffer.ydisp; - var line = terminal.buffer.lines.get(row); - this.clearCells(0, y, terminal.cols, 1); - for (var x = 0; x < terminal.cols; x++) { - var charData = line[x]; - var code = charData[Buffer_1.CHAR_DATA_CODE_INDEX]; - var char = charData[Buffer_1.CHAR_DATA_CHAR_INDEX]; - var attr = charData[Buffer_1.CHAR_DATA_ATTR_INDEX]; - var width = charData[Buffer_1.CHAR_DATA_WIDTH_INDEX]; - if (width === 0) { - continue; - } - if (code === 32) { - if (x > 0) { - var previousChar = line[x - 1]; - if (this._isOverlapping(previousChar)) { - continue; - } - } - } - var flags = attr >> 18; - var bg = attr & 0x1ff; - var isDefaultBackground = bg >= 256; - var isInvisible = flags & Types_1.FLAGS.INVISIBLE; - var isInverted = flags & Types_1.FLAGS.INVERSE; - if (!code || (code === 32 && isDefaultBackground && !isInverted) || isInvisible) { - continue; - } - if (width !== 0 && this._isOverlapping(charData)) { - if (x < line.length - 1 && line[x + 1][Buffer_1.CHAR_DATA_CODE_INDEX] === 32) { - width = 2; - } - } - var fg = (attr >> 9) & 0x1ff; - if (isInverted) { - var temp = bg; - bg = fg; - fg = temp; - if (fg === 256) { - fg = BaseRenderLayer_1.INVERTED_DEFAULT_COLOR; - } - if (bg === 257) { - bg = BaseRenderLayer_1.INVERTED_DEFAULT_COLOR; - } - } - if (width === 2) { - } - if (bg < 256) { - this._ctx.save(); - this._ctx.fillStyle = (bg === BaseRenderLayer_1.INVERTED_DEFAULT_COLOR ? this._colors.foreground : this._colors.ansi[bg]); - this.fillCells(x, y, width, 1); - this._ctx.restore(); - } - this._ctx.save(); - if (flags & Types_1.FLAGS.BOLD) { - this._ctx.font = "bold " + this._ctx.font; - if (fg < 8) { - fg += 8; - } - } - if (flags & Types_1.FLAGS.UNDERLINE) { - if (fg === BaseRenderLayer_1.INVERTED_DEFAULT_COLOR) { - this._ctx.fillStyle = this._colors.background; - } - else if (fg < 256) { - this._ctx.fillStyle = this._colors.ansi[fg]; - } - else { - this._ctx.fillStyle = this._colors.foreground; - } - this.fillBottomLineAtCells(x, y); - } - this.drawChar(terminal, char, code, width, x, y, fg, bg, !!(flags & Types_1.FLAGS.BOLD), !!(flags & Types_1.FLAGS.DIM)); - this._ctx.restore(); - } - } - }; - TextRenderLayer.prototype._isOverlapping = function (charData) { - if (charData[Buffer_1.CHAR_DATA_WIDTH_INDEX] !== 1) { - return false; - } - var code = charData[Buffer_1.CHAR_DATA_CODE_INDEX]; - if (code < 256) { - return false; - } - var char = charData[Buffer_1.CHAR_DATA_CHAR_INDEX]; - if (this._characterOverlapCache.hasOwnProperty(char)) { - return this._characterOverlapCache[char]; - } - this._ctx.save(); - this._ctx.font = this._characterFont; - var overlaps = Math.floor(this._ctx.measureText(char).width) > this._characterWidth; - this._ctx.restore(); - this._characterOverlapCache[char] = overlaps; - return overlaps; - }; - TextRenderLayer.prototype._clearChar = function (x, y) { - var colsToClear = 1; - var state = this._state.cache[x][y]; - if (state && state[Buffer_1.CHAR_DATA_WIDTH_INDEX] === 2) { - colsToClear = 2; - } - this.clearCells(x, y, colsToClear, 1); - }; - return TextRenderLayer; -}(BaseRenderLayer_1.BaseRenderLayer)); -exports.TextRenderLayer = TextRenderLayer; - - - -},{"../Buffer":1,"./BaseRenderLayer":18,"./GridCache":22,"./Types":27}],27:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var FLAGS; -(function (FLAGS) { - FLAGS[FLAGS["BOLD"] = 1] = "BOLD"; - FLAGS[FLAGS["UNDERLINE"] = 2] = "UNDERLINE"; - FLAGS[FLAGS["BLINK"] = 4] = "BLINK"; - FLAGS[FLAGS["INVERSE"] = 8] = "INVERSE"; - FLAGS[FLAGS["INVISIBLE"] = 16] = "INVISIBLE"; - FLAGS[FLAGS["DIM"] = 32] = "DIM"; -})(FLAGS = exports.FLAGS || (exports.FLAGS = {})); -; - - - -},{}],28:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var Generic_1 = require("./Generic"); -var isNode = (typeof navigator === 'undefined') ? true : false; -var userAgent = (isNode) ? 'node' : navigator.userAgent; -var platform = (isNode) ? 'node' : navigator.platform; -exports.isFirefox = !!~userAgent.indexOf('Firefox'); -exports.isMSIE = !!~userAgent.indexOf('MSIE') || !!~userAgent.indexOf('Trident'); -exports.isMac = Generic_1.contains(['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'], platform); -exports.isIpad = platform === 'iPad'; -exports.isIphone = platform === 'iPhone'; -exports.isMSWindows = Generic_1.contains(['Windows', 'Win16', 'Win32', 'WinCE'], platform); -exports.isLinux = platform.indexOf('Linux') >= 0; - - - -},{"./Generic":31}],29:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var EventEmitter_1 = require("../EventEmitter"); -var CharMeasure = (function (_super) { - __extends(CharMeasure, _super); - function CharMeasure(document, parentElement) { - var _this = _super.call(this) || this; - _this._document = document; - _this._parentElement = parentElement; - return _this; - } - Object.defineProperty(CharMeasure.prototype, "width", { - get: function () { - return this._width; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(CharMeasure.prototype, "height", { - get: function () { - return this._height; - }, - enumerable: true, - configurable: true - }); - CharMeasure.prototype.measure = function (options) { - var _this = this; - if (!this._measureElement) { - this._measureElement = this._document.createElement('span'); - this._measureElement.style.position = 'absolute'; - this._measureElement.style.top = '0'; - this._measureElement.style.left = '-9999em'; - this._measureElement.style.lineHeight = 'normal'; - this._measureElement.textContent = 'W'; - this._measureElement.setAttribute('aria-hidden', 'true'); - this._parentElement.appendChild(this._measureElement); - setTimeout(function () { return _this._doMeasure(options); }, 0); - } - else { - this._doMeasure(options); - } - }; - CharMeasure.prototype._doMeasure = function (options) { - this._measureElement.style.fontFamily = options.fontFamily; - this._measureElement.style.fontSize = options.fontSize + "px"; - var geometry = this._measureElement.getBoundingClientRect(); - if (geometry.width === 0 || geometry.height === 0) { - return; - } - if (this._width !== geometry.width || this._height !== geometry.height) { - this._width = geometry.width; - this._height = Math.ceil(geometry.height); - this.emit('charsizechanged'); - } - }; - return CharMeasure; -}(EventEmitter_1.EventEmitter)); -exports.CharMeasure = CharMeasure; - - - -},{"../EventEmitter":7}],30:[function(require,module,exports){ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var EventEmitter_1 = require("../EventEmitter"); -var CircularList = (function (_super) { - __extends(CircularList, _super); - function CircularList(_maxLength) { - var _this = _super.call(this) || this; - _this._maxLength = _maxLength; - _this._array = new Array(_this._maxLength); - _this._startIndex = 0; - _this._length = 0; - return _this; - } - Object.defineProperty(CircularList.prototype, "maxLength", { - get: function () { - return this._maxLength; - }, - set: function (newMaxLength) { - if (this._maxLength === newMaxLength) { - return; - } - var newArray = new Array(newMaxLength); - for (var i = 0; i < Math.min(newMaxLength, this.length); i++) { - newArray[i] = this._array[this._getCyclicIndex(i)]; - } - this._array = newArray; - this._maxLength = newMaxLength; - this._startIndex = 0; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(CircularList.prototype, "length", { - get: function () { - return this._length; - }, - set: function (newLength) { - if (newLength > this._length) { - for (var i = this._length; i < newLength; i++) { - this._array[i] = undefined; - } - } - this._length = newLength; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(CircularList.prototype, "forEach", { - get: function () { - var _this = this; - return function (callbackfn) { - var i = 0; - var length = _this.length; - for (var i_1 = 0; i_1 < length; i_1++) { - callbackfn(_this.get(i_1), i_1); - } - }; - }, - enumerable: true, - configurable: true - }); - CircularList.prototype.get = function (index) { - return this._array[this._getCyclicIndex(index)]; - }; - CircularList.prototype.set = function (index, value) { - this._array[this._getCyclicIndex(index)] = value; - }; - CircularList.prototype.push = function (value) { - this._array[this._getCyclicIndex(this._length)] = value; - if (this._length === this._maxLength) { - this._startIndex++; - if (this._startIndex === this._maxLength) { - this._startIndex = 0; - } - this.emit('trim', 1); - } - else { - this._length++; - } - }; - CircularList.prototype.pop = function () { - return this._array[this._getCyclicIndex(this._length-- - 1)]; - }; - CircularList.prototype.splice = function (start, deleteCount) { - var items = []; - for (var _i = 2; _i < arguments.length; _i++) { - items[_i - 2] = arguments[_i]; - } - if (deleteCount) { - for (var i = start; i < this._length - deleteCount; i++) { - this._array[this._getCyclicIndex(i)] = this._array[this._getCyclicIndex(i + deleteCount)]; - } - this._length -= deleteCount; - } - if (items && items.length) { - for (var i = this._length - 1; i >= start; i--) { - this._array[this._getCyclicIndex(i + items.length)] = this._array[this._getCyclicIndex(i)]; - } - for (var i = 0; i < items.length; i++) { - this._array[this._getCyclicIndex(start + i)] = items[i]; - } - if (this._length + items.length > this.maxLength) { - var countToTrim = (this._length + items.length) - this.maxLength; - this._startIndex += countToTrim; - this._length = this.maxLength; - this.emit('trim', countToTrim); - } - else { - this._length += items.length; - } - } - }; - CircularList.prototype.trimStart = function (count) { - if (count > this._length) { - count = this._length; - } - this._startIndex += count; - this._length -= count; - this.emit('trim', count); - }; - CircularList.prototype.shiftElements = function (start, count, offset) { - if (count <= 0) { - return; - } - if (start < 0 || start >= this._length) { - throw new Error('start argument out of range'); - } - if (start + offset < 0) { - throw new Error('Cannot shift elements in list beyond index 0'); - } - if (offset > 0) { - for (var i = count - 1; i >= 0; i--) { - this.set(start + i + offset, this.get(start + i)); - } - var expandListBy = (start + count + offset) - this._length; - if (expandListBy > 0) { - this._length += expandListBy; - while (this._length > this.maxLength) { - this._length--; - this._startIndex++; - this.emit('trim', 1); - } - } - } - else { - for (var i = 0; i < count; i++) { - this.set(start + i + offset, this.get(start + i)); - } - } - }; - CircularList.prototype._getCyclicIndex = function (index) { - return (this._startIndex + index) % this.maxLength; - }; - return CircularList; -}(EventEmitter_1.EventEmitter)); -exports.CircularList = CircularList; - - - -},{"../EventEmitter":7}],31:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -function contains(arr, el) { - return arr.indexOf(el) >= 0; -} -exports.contains = contains; -; - - - -},{}],32:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var MouseHelper = (function () { - function MouseHelper(_renderer) { - this._renderer = _renderer; - } - MouseHelper.getCoordsRelativeToElement = function (event, element) { - if (event.pageX == null) { - return null; - } - var originalElement = element; - var x = event.pageX; - var y = event.pageY; - while (element) { - x -= element.offsetLeft; - y -= element.offsetTop; - element = 'offsetParent' in element ? element.offsetParent : element.parentElement; - } - element = originalElement; - while (element && element !== element.ownerDocument.body) { - x += element.scrollLeft; - y += element.scrollTop; - element = element.parentElement; - } - return [x, y]; - }; - MouseHelper.prototype.getCoords = function (event, element, charMeasure, lineHeight, colCount, rowCount, isSelection) { - if (!charMeasure.width || !charMeasure.height) { - return null; - } - var coords = MouseHelper.getCoordsRelativeToElement(event, element); - if (!coords) { - return null; - } - coords[0] = Math.ceil((coords[0] + (isSelection ? this._renderer.dimensions.actualCellWidth / 2 : 0)) / this._renderer.dimensions.actualCellWidth); - coords[1] = Math.ceil(coords[1] / this._renderer.dimensions.actualCellHeight); - coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0)); - coords[1] = Math.min(Math.max(coords[1], 1), rowCount); - return coords; - }; - MouseHelper.prototype.getRawByteCoords = function (event, element, charMeasure, lineHeight, colCount, rowCount) { - var coords = this.getCoords(event, element, charMeasure, lineHeight, colCount, rowCount); - var x = coords[0]; - var y = coords[1]; - x += 32; - y += 32; - return { x: x, y: y }; - }; - return MouseHelper; -}()); -exports.MouseHelper = MouseHelper; - - - -},{}],33:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.BellSound = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQBAADpAFgCwAMlBZoG/wdmCcoKRAypDQ8PbRDBEQQTOxRtFYcWlBePGIUZXhoiG88bcBz7HHIdzh0WHlMeZx51HmkeUx4WHs8dah0AHXwc3hs9G4saxRnyGBIYGBcQFv8U4RPAEoYRQBACD70NWwwHC6gJOwjWBloF7gOBAhABkf8b/qv8R/ve+Xf4Ife79W/0JfPZ8Z/wde9N7ijtE+wU6xvqM+lb6H7nw+YX5mrlxuQz5Mzje+Ma49fioeKD4nXiYeJy4pHitOL04j/jn+MN5IPkFOWs5U3mDefM55/ogOl36m7rdOyE7abuyu8D8Unyj/Pg9D/2qfcb+Yn6/vuK/Qj/lAAlAg=='; - - - -},{}],34:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var Terminal_1 = require("./Terminal"); -module.exports = Terminal_1.Terminal; - - - -},{"./Terminal":13}]},{},[34])(34) -}); -//# sourceMappingURL=xterm.js.map diff --git a/route/static/build/xterm.js.map b/route/static/build/xterm.js.map deleted file mode 100755 index 9716c5e22..000000000 --- a/route/static/build/xterm.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"xterm.js","sources":["../src/xterm.ts","../src/utils/Sounds.ts","../src/utils/MouseHelper.ts","../src/utils/Generic.ts","../src/utils/CircularList.ts","../src/utils/CharMeasure.ts","../src/utils/Browser.ts","../src/renderer/Types.ts","../src/renderer/TextRenderLayer.ts","../src/renderer/SelectionRenderLayer.ts","../src/renderer/Renderer.ts","../src/renderer/LinkRenderLayer.ts","../src/renderer/GridCache.ts","../src/renderer/CursorRenderLayer.ts","../src/renderer/ColorManager.ts","../src/renderer/CharAtlas.ts","../src/renderer/BaseRenderLayer.ts","../src/input/MouseZoneManager.ts","../src/handlers/Clipboard.ts","../src/Viewport.ts","../src/Types.ts","../src/Terminal.ts","../src/SelectionModel.ts","../src/SelectionManager.ts","../src/Parser.ts","../src/Linkifier.ts","../src/InputHandler.ts","../src/EventEmitter.ts","../src/EscapeSequences.ts","../src/CompositionHelper.ts","../src/Charsets.ts","../src/CharWidth.ts","../src/BufferSet.ts","../src/Buffer.ts","../node_modules/browser-pack/_prelude.js"],"sourcesContent":["/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n *\n * This file is the entry point for browserify.\n */\n\nimport { Terminal } from './Terminal';\n\nmodule.exports = Terminal;\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\n// Source: https://freesound.org/people/altemark/sounds/45759/\n// This sound is released under the Creative Commons Attribution 3.0 Unported\n// (CC BY 3.0) license. It was created by 'altemark'. No modifications have been\n// made, apart from the conversion to base64.\nexport const BellSound = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQBAADpAFgCwAMlBZoG/wdmCcoKRAypDQ8PbRDBEQQTOxRtFYcWlBePGIUZXhoiG88bcBz7HHIdzh0WHlMeZx51HmkeUx4WHs8dah0AHXwc3hs9G4saxRnyGBIYGBcQFv8U4RPAEoYRQBACD70NWwwHC6gJOwjWBloF7gOBAhABkf8b/qv8R/ve+Xf4Ife79W/0JfPZ8Z/wde9N7ijtE+wU6xvqM+lb6H7nw+YX5mrlxuQz5Mzje+Ma49fioeKD4nXiYeJy4pHitOL04j/jn+MN5IPkFOWs5U3mDefM55/ogOl36m7rdOyE7abuyu8D8Unyj/Pg9D/2qfcb+Yn6/vuK/Qj/lAAlAg==';\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ICharMeasure } from '../Interfaces';\nimport { IRenderer } from '../renderer/Interfaces';\n\nexport class MouseHelper {\n constructor(private _renderer: IRenderer) {}\n\n public static getCoordsRelativeToElement(event: {pageX: number, pageY: number}, element: HTMLElement): [number, number] {\n // Ignore browsers that don't support MouseEvent.pageX\n if (event.pageX == null) {\n return null;\n }\n\n const originalElement = element;\n let x = event.pageX;\n let y = event.pageY;\n\n // Converts the coordinates from being relative to the document to being\n // relative to the terminal.\n while (element) {\n x -= element.offsetLeft;\n y -= element.offsetTop;\n element = 'offsetParent' in element ? element.offsetParent : element.parentElement;\n }\n element = originalElement;\n while (element && element !== element.ownerDocument.body) {\n x += element.scrollLeft;\n y += element.scrollTop;\n element = element.parentElement;\n }\n return [x, y];\n }\n\n /**\n * Gets coordinates within the terminal for a particular mouse event. The result\n * is returned as an array in the form [x, y] instead of an object as it's a\n * little faster and this function is used in some low level code.\n * @param event The mouse event.\n * @param element The terminal's container element.\n * @param charMeasure The char measure object used to determine character sizes.\n * @param colCount The number of columns in the terminal.\n * @param rowCount The number of rows n the terminal.\n * @param isSelection Whether the request is for the selection or not. This will\n * apply an offset to the x value such that the left half of the cell will\n * select that cell and the right half will select the next cell.\n */\n public getCoords(event: {pageX: number, pageY: number}, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number, isSelection?: boolean): [number, number] {\n // Coordinates cannot be measured if charMeasure has not been initialized\n if (!charMeasure.width || !charMeasure.height) {\n return null;\n }\n\n const coords = MouseHelper.getCoordsRelativeToElement(event, element);\n if (!coords) {\n return null;\n }\n\n coords[0] = Math.ceil((coords[0] + (isSelection ? this._renderer.dimensions.actualCellWidth / 2 : 0)) / this._renderer.dimensions.actualCellWidth);\n coords[1] = Math.ceil(coords[1] / this._renderer.dimensions.actualCellHeight);\n\n // Ensure coordinates are within the terminal viewport. Note that selections\n // need an addition point of precision to cover the end point (as characters\n // cover half of one char and half of the next).\n coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0));\n coords[1] = Math.min(Math.max(coords[1], 1), rowCount);\n\n return coords;\n }\n\n /**\n * Gets coordinates within the terminal for a particular mouse event, wrapping\n * them to the bounds of the terminal and adding 32 to both the x and y values\n * as expected by xterm.\n * @param event The mouse event.\n * @param element The terminal's container element.\n * @param charMeasure The char measure object used to determine character sizes.\n * @param colCount The number of columns in the terminal.\n * @param rowCount The number of rows in the terminal.\n */\n public getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number): { x: number, y: number } {\n const coords = this.getCoords(event, element, charMeasure, lineHeight, colCount, rowCount);\n let x = coords[0];\n let y = coords[1];\n\n // xterm sends raw bytes and starts at 32 (SP) for each.\n x += 32;\n y += 32;\n\n return { x, y };\n }\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\n/**\n * Return if the given array contains the given element\n * @param {Array} array The array to search for the given element.\n * @param {Object} el The element to look for into the array\n */\nexport function contains(arr: any[], el: any): boolean {\n return arr.indexOf(el) >= 0;\n};\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { EventEmitter } from '../EventEmitter';\nimport { ICircularList } from '../Interfaces';\n\n/**\n * Represents a circular list; a list with a maximum size that wraps around when push is called,\n * overriding values at the start of the list.\n */\nexport class CircularList extends EventEmitter implements ICircularList {\n protected _array: T[];\n private _startIndex: number;\n private _length: number;\n\n constructor(\n private _maxLength: number\n ) {\n super();\n this._array = new Array(this._maxLength);\n this._startIndex = 0;\n this._length = 0;\n }\n\n public get maxLength(): number {\n return this._maxLength;\n }\n\n public set maxLength(newMaxLength: number) {\n // There was no change in maxLength, return early.\n if (this._maxLength === newMaxLength) {\n return;\n }\n\n // Reconstruct array, starting at index 0. Only transfer values from the\n // indexes 0 to length.\n let newArray = new Array(newMaxLength);\n for (let i = 0; i < Math.min(newMaxLength, this.length); i++) {\n newArray[i] = this._array[this._getCyclicIndex(i)];\n }\n this._array = newArray;\n this._maxLength = newMaxLength;\n this._startIndex = 0;\n }\n\n public get length(): number {\n return this._length;\n }\n\n public set length(newLength: number) {\n if (newLength > this._length) {\n for (let i = this._length; i < newLength; i++) {\n this._array[i] = undefined;\n }\n }\n this._length = newLength;\n }\n\n public get forEach(): (callbackfn: (value: T, index: number) => void) => void {\n return (callbackfn: (value: T, index: number) => void) => {\n let i = 0;\n let length = this.length;\n for (let i = 0; i < length; i++) {\n callbackfn(this.get(i), i);\n }\n };\n }\n\n /**\n * Gets the value at an index.\n *\n * Note that for performance reasons there is no bounds checking here, the index reference is\n * circular so this should always return a value and never throw.\n * @param index The index of the value to get.\n * @return The value corresponding to the index.\n */\n public get(index: number): T {\n return this._array[this._getCyclicIndex(index)];\n }\n\n /**\n * Sets the value at an index.\n *\n * Note that for performance reasons there is no bounds checking here, the index reference is\n * circular so this should always return a value and never throw.\n * @param index The index to set.\n * @param value The value to set.\n */\n public set(index: number, value: T): void {\n this._array[this._getCyclicIndex(index)] = value;\n }\n\n /**\n * Pushes a new value onto the list, wrapping around to the start of the array, overriding index 0\n * if the maximum length is reached.\n * @param value The value to push onto the list.\n */\n public push(value: T): void {\n this._array[this._getCyclicIndex(this._length)] = value;\n if (this._length === this._maxLength) {\n this._startIndex++;\n if (this._startIndex === this._maxLength) {\n this._startIndex = 0;\n }\n this.emit('trim', 1);\n } else {\n this._length++;\n }\n }\n\n /**\n * Removes and returns the last value on the list.\n * @return The popped value.\n */\n public pop(): T {\n return this._array[this._getCyclicIndex(this._length-- - 1)];\n }\n\n /**\n * Deletes and/or inserts items at a particular index (in that order). Unlike\n * Array.prototype.splice, this operation does not return the deleted items as a new array in\n * order to save creating a new array. Note that this operation may shift all values in the list\n * in the worst case.\n * @param start The index to delete and/or insert.\n * @param deleteCount The number of elements to delete.\n * @param items The items to insert.\n */\n public splice(start: number, deleteCount: number, ...items: T[]): void {\n // Delete items\n if (deleteCount) {\n for (let i = start; i < this._length - deleteCount; i++) {\n this._array[this._getCyclicIndex(i)] = this._array[this._getCyclicIndex(i + deleteCount)];\n }\n this._length -= deleteCount;\n }\n\n if (items && items.length) {\n // Add items\n for (let i = this._length - 1; i >= start; i--) {\n this._array[this._getCyclicIndex(i + items.length)] = this._array[this._getCyclicIndex(i)];\n }\n for (let i = 0; i < items.length; i++) {\n this._array[this._getCyclicIndex(start + i)] = items[i];\n }\n\n // Adjust length as needed\n if (this._length + items.length > this.maxLength) {\n const countToTrim = (this._length + items.length) - this.maxLength;\n this._startIndex += countToTrim;\n this._length = this.maxLength;\n this.emit('trim', countToTrim);\n } else {\n this._length += items.length;\n }\n }\n }\n\n /**\n * Trims a number of items from the start of the list.\n * @param count The number of items to remove.\n */\n public trimStart(count: number): void {\n if (count > this._length) {\n count = this._length;\n }\n this._startIndex += count;\n this._length -= count;\n this.emit('trim', count);\n }\n\n public shiftElements(start: number, count: number, offset: number): void {\n if (count <= 0) {\n return;\n }\n if (start < 0 || start >= this._length) {\n throw new Error('start argument out of range');\n }\n if (start + offset < 0) {\n throw new Error('Cannot shift elements in list beyond index 0');\n }\n\n if (offset > 0) {\n for (let i = count - 1; i >= 0; i--) {\n this.set(start + i + offset, this.get(start + i));\n }\n const expandListBy = (start + count + offset) - this._length;\n if (expandListBy > 0) {\n this._length += expandListBy;\n while (this._length > this.maxLength) {\n this._length--;\n this._startIndex++;\n this.emit('trim', 1);\n }\n }\n } else {\n for (let i = 0; i < count; i++) {\n this.set(start + i + offset, this.get(start + i));\n }\n }\n }\n\n /**\n * Gets the cyclic index for the specified regular index. The cyclic index can then be used on the\n * backing array to get the element associated with the regular index.\n * @param index The regular index.\n * @returns The cyclic index.\n */\n private _getCyclicIndex(index: number): number {\n return (this._startIndex + index) % this.maxLength;\n }\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { EventEmitter } from '../EventEmitter';\nimport { ICharMeasure, ITerminal, ITerminalOptions } from '../Interfaces';\n\n/**\n * Utility class that measures the size of a character. Measurements are done in\n * the DOM rather than with a canvas context because support for extracting the\n * height of characters is patchy across browsers.\n */\nexport class CharMeasure extends EventEmitter implements ICharMeasure {\n private _document: Document;\n private _parentElement: HTMLElement;\n private _measureElement: HTMLElement;\n private _width: number;\n private _height: number;\n\n constructor(document: Document, parentElement: HTMLElement) {\n super();\n this._document = document;\n this._parentElement = parentElement;\n }\n\n public get width(): number {\n return this._width;\n }\n\n public get height(): number {\n return this._height;\n }\n\n public measure(options: ITerminalOptions): void {\n if (!this._measureElement) {\n this._measureElement = this._document.createElement('span');\n this._measureElement.style.position = 'absolute';\n this._measureElement.style.top = '0';\n this._measureElement.style.left = '-9999em';\n this._measureElement.style.lineHeight = 'normal';\n this._measureElement.textContent = 'W';\n this._measureElement.setAttribute('aria-hidden', 'true');\n this._parentElement.appendChild(this._measureElement);\n // Perform _doMeasure async if the element was just attached as sometimes\n // getBoundingClientRect does not return accurate values without this.\n setTimeout(() => this._doMeasure(options), 0);\n } else {\n this._doMeasure(options);\n }\n }\n\n private _doMeasure(options: ITerminalOptions): void {\n this._measureElement.style.fontFamily = options.fontFamily;\n this._measureElement.style.fontSize = `${options.fontSize}px`;\n const geometry = this._measureElement.getBoundingClientRect();\n // The element is likely currently display:none, we should retain the\n // previous value.\n if (geometry.width === 0 || geometry.height === 0) {\n return;\n }\n if (this._width !== geometry.width || this._height !== geometry.height) {\n this._width = geometry.width;\n this._height = Math.ceil(geometry.height);\n this.emit('charsizechanged');\n }\n }\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { contains } from './Generic';\n\nconst isNode = (typeof navigator === 'undefined') ? true : false;\nconst userAgent = (isNode) ? 'node' : navigator.userAgent;\nconst platform = (isNode) ? 'node' : navigator.platform;\n\nexport const isFirefox = !!~userAgent.indexOf('Firefox');\nexport const isMSIE = !!~userAgent.indexOf('MSIE') || !!~userAgent.indexOf('Trident');\n\n// Find the users platform. We use this to interpret the meta key\n// and ISO third level shifts.\n// http://stackoverflow.com/q/19877924/577598\nexport const isMac = contains(['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'], platform);\nexport const isIpad = platform === 'iPad';\nexport const isIphone = platform === 'iPhone';\nexport const isMSWindows = contains(['Windows', 'Win16', 'Win32', 'WinCE'], platform);\nexport const isLinux = platform.indexOf('Linux') >= 0;\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\n /**\n * Flags used to render terminal text properly.\n */\nexport enum FLAGS {\n BOLD = 1,\n UNDERLINE = 2,\n BLINK = 4,\n INVERSE = 8,\n INVISIBLE = 16,\n DIM = 32\n};\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IColorSet, IRenderDimensions } from './Interfaces';\nimport { IBuffer, ICharMeasure, ITerminal } from '../Interfaces';\nimport { CHAR_DATA_ATTR_INDEX, CHAR_DATA_CODE_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX } from '../Buffer';\nimport { FLAGS } from './Types';\nimport { GridCache } from './GridCache';\nimport { CharData } from '../Types';\nimport { BaseRenderLayer, INVERTED_DEFAULT_COLOR } from './BaseRenderLayer';\n\n/**\n * This CharData looks like a null character, which will forc a clear and render\n * when the character changes (a regular space ' ' character may not as it's\n * drawn state is a cleared cell).\n */\nconst OVERLAP_OWNED_CHAR_DATA: CharData = [null, '', 0, -1];\n\nexport class TextRenderLayer extends BaseRenderLayer {\n private _state: GridCache;\n private _characterWidth: number;\n private _characterFont: string;\n private _characterOverlapCache: { [key: string]: boolean } = {};\n\n constructor(container: HTMLElement, zIndex: number, colors: IColorSet) {\n super(container, 'text', zIndex, false, colors);\n this._state = new GridCache();\n }\n\n public resize(terminal: ITerminal, dim: IRenderDimensions, charSizeChanged: boolean): void {\n super.resize(terminal, dim, charSizeChanged);\n\n // Clear the character width cache if the font or width has changed\n const terminalFont = `${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`;\n if (this._characterWidth !== dim.scaledCharWidth || this._characterFont !== terminalFont) {\n this._characterWidth = dim.scaledCharWidth;\n this._characterFont = terminalFont;\n this._characterOverlapCache = {};\n }\n // Resizing the canvas discards the contents of the canvas so clear state\n this._state.clear();\n this._state.resize(terminal.cols, terminal.rows);\n }\n\n public reset(terminal: ITerminal): void {\n this._state.clear();\n this.clearAll();\n }\n\n public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void {\n // Resize has not been called yet\n if (this._state.cache.length === 0) {\n return;\n }\n\n for (let y = startRow; y <= endRow; y++) {\n const row = y + terminal.buffer.ydisp;\n const line = terminal.buffer.lines.get(row);\n\n this.clearCells(0, y, terminal.cols, 1);\n // for (let x = 0; x < terminal.cols; x++) {\n // this._state.cache[x][y] = null;\n // }\n\n for (let x = 0; x < terminal.cols; x++) {\n const charData = line[x];\n const code: number = charData[CHAR_DATA_CODE_INDEX];\n const char: string = charData[CHAR_DATA_CHAR_INDEX];\n const attr: number = charData[CHAR_DATA_ATTR_INDEX];\n let width: number = charData[CHAR_DATA_WIDTH_INDEX];\n\n // The character to the left is a wide character, drawing is owned by\n // the char at x-1\n if (width === 0) {\n // this._state.cache[x][y] = null;\n continue;\n }\n\n // If the character is a space and the character to the left is an\n // overlapping character, skip the character and allow the overlapping\n // char to take full control over this character's cell.\n if (code === 32 /*' '*/) {\n if (x > 0) {\n const previousChar: CharData = line[x - 1];\n if (this._isOverlapping(previousChar)) {\n continue;\n }\n }\n }\n\n // Skip rendering if the character is identical\n // const state = this._state.cache[x][y];\n // if (state && state[CHAR_DATA_CHAR_INDEX] === char && state[CHAR_DATA_ATTR_INDEX] === attr) {\n // // Skip render, contents are identical\n // this._state.cache[x][y] = charData;\n // continue;\n // }\n\n // Clear the old character was not a space with the default background\n // const wasInverted = !!(state && state[CHAR_DATA_ATTR_INDEX] && state[CHAR_DATA_ATTR_INDEX] >> 18 & FLAGS.INVERSE);\n // if (state && !(state[CHAR_DATA_CODE_INDEX] === 32 /*' '*/ && (state[CHAR_DATA_ATTR_INDEX] & 0x1ff) >= 256 && !wasInverted)) {\n // this._clearChar(x, y);\n // }\n // this._state.cache[x][y] = charData;\n\n const flags = attr >> 18;\n let bg = attr & 0x1ff;\n\n // Skip rendering if the character is invisible\n const isDefaultBackground = bg >= 256;\n const isInvisible = flags & FLAGS.INVISIBLE;\n const isInverted = flags & FLAGS.INVERSE;\n if (!code || (code === 32 /*' '*/ && isDefaultBackground && !isInverted) || isInvisible) {\n continue;\n }\n\n // If the character is an overlapping char and the character to the right is a\n // space, take ownership of the cell to the right.\n if (width !== 0 && this._isOverlapping(charData)) {\n // If the character is overlapping, we want to force a re-render on every\n // frame. This is specifically to work around the case where two\n // overlaping chars `a` and `b` are adjacent, the cursor is moved to b and a\n // space is added. Without this, the first half of `b` would never\n // get removed, and `a` would not re-render because it thinks it's\n // already in the correct state.\n // this._state.cache[x][y] = OVERLAP_OWNED_CHAR_DATA;\n if (x < line.length - 1 && line[x + 1][CHAR_DATA_CODE_INDEX] === 32 /*' '*/) {\n width = 2;\n // this._clearChar(x + 1, y);\n // The overlapping char's char data will force a clear and render when the\n // overlapping char is no longer to the left of the character and also when\n // the space changes to another character.\n // this._state.cache[x + 1][y] = OVERLAP_OWNED_CHAR_DATA;\n }\n }\n\n let fg = (attr >> 9) & 0x1ff;\n\n // If inverse flag is on, the foreground should become the background.\n if (isInverted) {\n const temp = bg;\n bg = fg;\n fg = temp;\n if (fg === 256) {\n fg = INVERTED_DEFAULT_COLOR;\n }\n if (bg === 257) {\n bg = INVERTED_DEFAULT_COLOR;\n }\n }\n\n // Clear the cell next to this character if it's wide\n if (width === 2) {\n // this.clearCells(x + 1, y, 1, 1);\n }\n\n // Draw background\n if (bg < 256) {\n this._ctx.save();\n this._ctx.fillStyle = (bg === INVERTED_DEFAULT_COLOR ? this._colors.foreground : this._colors.ansi[bg]);\n this.fillCells(x, y, width, 1);\n this._ctx.restore();\n }\n\n this._ctx.save();\n if (flags & FLAGS.BOLD) {\n this._ctx.font = `bold ${this._ctx.font}`;\n // Convert the FG color to the bold variant\n if (fg < 8) {\n fg += 8;\n }\n }\n\n if (flags & FLAGS.UNDERLINE) {\n if (fg === INVERTED_DEFAULT_COLOR) {\n this._ctx.fillStyle = this._colors.background;\n } else if (fg < 256) {\n // 256 color support\n this._ctx.fillStyle = this._colors.ansi[fg];\n } else {\n this._ctx.fillStyle = this._colors.foreground;\n }\n this.fillBottomLineAtCells(x, y);\n }\n\n this.drawChar(terminal, char, code, width, x, y, fg, bg, !!(flags & FLAGS.BOLD), !!(flags & FLAGS.DIM));\n\n this._ctx.restore();\n }\n }\n }\n\n\t/**\n\t * Whether a character is overlapping to the next cell.\n\t */\n private _isOverlapping(charData: CharData): boolean {\n // Only single cell characters can be overlapping, rendering issues can\n // occur without this check\n if (charData[CHAR_DATA_WIDTH_INDEX] !== 1) {\n return false;\n }\n\n // We assume that any ascii character will not overlap\n const code = charData[CHAR_DATA_CODE_INDEX];\n if (code < 256) {\n return false;\n }\n\n // Deliver from cache if available\n const char = charData[CHAR_DATA_CHAR_INDEX];\n if (this._characterOverlapCache.hasOwnProperty(char)) {\n return this._characterOverlapCache[char];\n }\n\n // Setup the font\n this._ctx.save();\n this._ctx.font = this._characterFont;\n\n // Measure the width of the character, but Math.floor it\n // because that is what the renderer does when it calculates\n // the character dimensions we are comparing against\n const overlaps = Math.floor(this._ctx.measureText(char).width) > this._characterWidth;\n\n // Restore the original context\n this._ctx.restore();\n\n // Cache and return\n this._characterOverlapCache[char] = overlaps;\n return overlaps;\n }\n\n /**\n * Clear the charcater at the cell specified.\n * @param x The column of the char.\n * @param y The row of the char.\n */\n private _clearChar(x: number, y: number): void {\n let colsToClear = 1;\n // Clear the adjacent character if it was wide\n const state = this._state.cache[x][y];\n if (state && state[CHAR_DATA_WIDTH_INDEX] === 2) {\n colsToClear = 2;\n }\n this.clearCells(x, y, colsToClear, 1);\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IColorSet, IRenderDimensions } from './Interfaces';\nimport { IBuffer, ICharMeasure, ITerminal } from '../Interfaces';\nimport { CHAR_DATA_ATTR_INDEX } from '../Buffer';\nimport { GridCache } from './GridCache';\nimport { FLAGS } from './Types';\nimport { BaseRenderLayer } from './BaseRenderLayer';\n\nexport class SelectionRenderLayer extends BaseRenderLayer {\n private _state: {start: [number, number], end: [number, number]};\n\n constructor(container: HTMLElement, zIndex: number, colors: IColorSet) {\n super(container, 'selection', zIndex, true, colors);\n this._state = {\n start: null,\n end: null\n };\n }\n\n public resize(terminal: ITerminal, dim: IRenderDimensions, charSizeChanged: boolean): void {\n super.resize(terminal, dim, charSizeChanged);\n // Resizing the canvas discards the contents of the canvas so clear state\n this._state = {\n start: null,\n end: null\n };\n }\n\n public reset(terminal: ITerminal): void {\n if (this._state.start && this._state.end) {\n this._state = {\n start: null,\n end: null\n };\n this.clearAll();\n }\n }\n\n public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number]): void {\n // Selection has not changed\n if (this._state.start === start || this._state.end === end) {\n return;\n }\n\n // Remove all selections\n this.clearAll();\n\n // Selection does not exist\n if (!start || !end) {\n return;\n }\n\n // Translate from buffer position to viewport position\n const viewportStartRow = start[1] - terminal.buffer.ydisp;\n const viewportEndRow = end[1] - terminal.buffer.ydisp;\n const viewportCappedStartRow = Math.max(viewportStartRow, 0);\n const viewportCappedEndRow = Math.min(viewportEndRow, terminal.rows - 1);\n\n // No need to draw the selection\n if (viewportCappedStartRow >= terminal.rows || viewportCappedEndRow < 0) {\n return;\n }\n\n // Draw first row\n const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;\n const startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : terminal.cols;\n this._ctx.fillStyle = this._colors.selection;\n this.fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1);\n\n // Draw middle rows\n const middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0);\n this.fillCells(0, viewportCappedStartRow + 1, terminal.cols, middleRowsCount);\n\n // Draw final row\n if (viewportCappedStartRow !== viewportCappedEndRow) {\n // Only draw viewportEndRow if it's not the same as viewportStartRow\n const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : terminal.cols;\n this.fillCells(0, viewportCappedEndRow, endCol, 1);\n }\n\n // Save state for next render\n this._state.start = [start[0], start[1]];\n this._state.end = [end[0], end[1]];\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal, ITheme } from '../Interfaces';\nimport { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from '../Buffer';\nimport { TextRenderLayer } from './TextRenderLayer';\nimport { SelectionRenderLayer } from './SelectionRenderLayer';\nimport { CursorRenderLayer } from './CursorRenderLayer';\nimport { ColorManager } from './ColorManager';\nimport { BaseRenderLayer } from './BaseRenderLayer';\nimport { IRenderLayer, IColorSet, IRenderer, IRenderDimensions } from './Interfaces';\nimport { LinkRenderLayer } from './LinkRenderLayer';\nimport { EventEmitter } from '../EventEmitter';\n\nexport class Renderer extends EventEmitter implements IRenderer {\n /** A queue of the rows to be refreshed */\n private _refreshRowsQueue: {start: number, end: number}[] = [];\n private _refreshAnimationFrame = null;\n\n private _renderLayers: IRenderLayer[];\n private _devicePixelRatio: number;\n\n public colorManager: ColorManager;\n public dimensions: IRenderDimensions;\n\n constructor(private _terminal: ITerminal, theme: ITheme) {\n super();\n this.colorManager = new ColorManager();\n if (theme) {\n this.colorManager.setTheme(theme);\n }\n this._renderLayers = [\n new TextRenderLayer(this._terminal.element, 0, this.colorManager.colors),\n new SelectionRenderLayer(this._terminal.element, 1, this.colorManager.colors),\n new LinkRenderLayer(this._terminal.element, 2, this.colorManager.colors, this._terminal),\n new CursorRenderLayer(this._terminal.element, 3, this.colorManager.colors)\n ];\n this.dimensions = {\n scaledCharWidth: null,\n scaledCharHeight: null,\n scaledCellWidth: null,\n scaledCellHeight: null,\n scaledCharLeft: null,\n scaledCharTop: null,\n scaledCanvasWidth: null,\n scaledCanvasHeight: null,\n canvasWidth: null,\n canvasHeight: null,\n actualCellWidth: null,\n actualCellHeight: null\n };\n this._devicePixelRatio = window.devicePixelRatio;\n }\n\n public onWindowResize(devicePixelRatio: number): void {\n // If the device pixel ratio changed, the char atlas needs to be regenerated\n // and the terminal needs to refreshed\n if (this._devicePixelRatio !== devicePixelRatio) {\n this._devicePixelRatio = devicePixelRatio;\n this.onResize(this._terminal.cols, this._terminal.rows, true);\n }\n }\n\n public setTheme(theme: ITheme): IColorSet {\n this.colorManager.setTheme(theme);\n\n // Clear layers and force a full render\n this._renderLayers.forEach(l => {\n l.onThemeChanged(this._terminal, this.colorManager.colors);\n l.reset(this._terminal);\n });\n\n this._terminal.refresh(0, this._terminal.rows - 1);\n\n return this.colorManager.colors;\n }\n\n public onResize(cols: number, rows: number, didCharSizeChange: boolean): void {\n if (!this._terminal.charMeasure.width || !this._terminal.charMeasure.height) {\n return;\n }\n\n // Calculate the scaled character width. Width is floored as it must be\n // drawn to an integer grid in order for the CharAtlas \"stamps\" to not be\n // blurry. When text is drawn to the grid not using the CharAtlas, it is\n // clipped to ensure there is no overlap with the next cell.\n this.dimensions.scaledCharWidth = Math.floor(this._terminal.charMeasure.width * window.devicePixelRatio);\n\n // Calculate the scaled character height. Height is ceiled in case\n // devicePixelRatio is a floating point number in order to ensure there is\n // enough space to draw the character to the cell.\n this.dimensions.scaledCharHeight = Math.ceil(this._terminal.charMeasure.height * window.devicePixelRatio);\n\n // Calculate the scaled cell height, if lineHeight is not 1 then the value\n // will be floored because since lineHeight can never be lower then 1, there\n // is a guarentee that the scaled line height will always be larger than\n // scaled char height.\n this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._terminal.options.lineHeight);\n\n // Calculate the y coordinate within a cell that text should draw from in\n // order to draw in the center of a cell.\n this.dimensions.scaledCharTop = this._terminal.options.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2);\n\n // Calculate the scaled cell width, taking the letterSpacing into account.\n this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._terminal.options.letterSpacing);\n\n // Calculate the x coordinate with a cell that text should draw from in\n // order to draw in the center of a cell.\n this.dimensions.scaledCharLeft = Math.floor(this._terminal.options.letterSpacing / 2);\n\n // Recalculate the canvas dimensions; scaled* define the actual number of\n // pixel in the canvas\n this.dimensions.scaledCanvasHeight = this._terminal.rows * this.dimensions.scaledCellHeight;\n this.dimensions.scaledCanvasWidth = this._terminal.cols * this.dimensions.scaledCellWidth;\n\n // The the size of the canvas on the page. It's very important that this\n // rounds to nearest integer and not ceils as browsers often set\n // window.devicePixelRatio as something like 1.100000023841858, when it's\n // actually 1.1. Ceiling causes blurriness as the backing canvas image is 1\n // pixel too large for the canvas element size.\n this.dimensions.canvasHeight = Math.round(this.dimensions.scaledCanvasHeight / window.devicePixelRatio);\n this.dimensions.canvasWidth = Math.round(this.dimensions.scaledCanvasWidth / window.devicePixelRatio);\n\n // Get the _actual_ dimensions of an individual cell. This needs to be\n // derived from the canvasWidth/Height calculated above which takes into\n // account window.devicePixelRatio. CharMeasure.width/height by itself is\n // insufficient when the page is not at 100% zoom level as CharMeasure is\n // measured in CSS pixels, but the actual char size on the canvas can\n // differ.\n this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._terminal.rows;\n this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._terminal.cols;\n\n // Resize all render layers\n this._renderLayers.forEach(l => l.resize(this._terminal, this.dimensions, didCharSizeChange));\n\n // Force a refresh\n this._terminal.refresh(0, this._terminal.rows - 1);\n\n this.emit('resize', {\n width: this.dimensions.canvasWidth,\n height: this.dimensions.canvasHeight\n });\n }\n\n public onCharSizeChanged(): void {\n this.onResize(this._terminal.cols, this._terminal.rows, true);\n }\n\n public onBlur(): void {\n this._renderLayers.forEach(l => l.onBlur(this._terminal));\n }\n\n public onFocus(): void {\n this._renderLayers.forEach(l => l.onFocus(this._terminal));\n }\n\n public onSelectionChanged(start: [number, number], end: [number, number]): void {\n this._renderLayers.forEach(l => l.onSelectionChanged(this._terminal, start, end));\n }\n\n public onCursorMove(): void {\n this._renderLayers.forEach(l => l.onCursorMove(this._terminal));\n }\n\n public onOptionsChanged(): void {\n this._renderLayers.forEach(l => l.onOptionsChanged(this._terminal));\n }\n\n public clear(): void {\n this._renderLayers.forEach(l => l.reset(this._terminal));\n }\n\n /**\n * Queues a refresh between two rows (inclusive), to be done on next animation\n * frame.\n * @param {number} start The start row.\n * @param {number} end The end row.\n */\n public queueRefresh(start: number, end: number): void {\n this._refreshRowsQueue.push({ start: start, end: end });\n if (!this._refreshAnimationFrame) {\n this._refreshAnimationFrame = window.requestAnimationFrame(this._refreshLoop.bind(this));\n }\n }\n\n /**\n * Performs the refresh loop callback, calling refresh only if a refresh is\n * necessary before queueing up the next one.\n */\n private _refreshLoop(): void {\n let start;\n let end;\n if (this._refreshRowsQueue.length > 4) {\n // Just do a full refresh when 5+ refreshes are queued\n start = 0;\n end = this._terminal.rows - 1;\n } else {\n // Get start and end rows that need refreshing\n start = this._refreshRowsQueue[0].start;\n end = this._refreshRowsQueue[0].end;\n for (let i = 1; i < this._refreshRowsQueue.length; i++) {\n if (this._refreshRowsQueue[i].start < start) {\n start = this._refreshRowsQueue[i].start;\n }\n if (this._refreshRowsQueue[i].end > end) {\n end = this._refreshRowsQueue[i].end;\n }\n }\n }\n this._refreshRowsQueue = [];\n this._refreshAnimationFrame = null;\n\n // Render\n start = Math.max(start, 0);\n end = Math.min(end, this._terminal.rows - 1);\n this._renderLayers.forEach(l => l.onGridChanged(this._terminal, start, end));\n this._terminal.emit('refresh', {start, end});\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IColorSet, IRenderDimensions } from './Interfaces';\nimport { IBuffer, ICharMeasure, ITerminal, ILinkifierAccessor } from '../Interfaces';\nimport { CHAR_DATA_ATTR_INDEX } from '../Buffer';\nimport { GridCache } from './GridCache';\nimport { FLAGS } from './Types';\nimport { BaseRenderLayer, INVERTED_DEFAULT_COLOR } from './BaseRenderLayer';\nimport { LinkHoverEvent, LinkHoverEventTypes } from '../Types';\n\nexport class LinkRenderLayer extends BaseRenderLayer {\n private _state: LinkHoverEvent = null;\n\n constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ILinkifierAccessor) {\n super(container, 'link', zIndex, true, colors);\n terminal.linkifier.on(LinkHoverEventTypes.HOVER, (e: LinkHoverEvent) => this._onLinkHover(e));\n terminal.linkifier.on(LinkHoverEventTypes.LEAVE, (e: LinkHoverEvent) => this._onLinkLeave(e));\n }\n\n public resize(terminal: ITerminal, dim: IRenderDimensions, charSizeChanged: boolean): void {\n super.resize(terminal, dim, charSizeChanged);\n // Resizing the canvas discards the contents of the canvas so clear state\n this._state = null;\n }\n\n public reset(terminal: ITerminal): void {\n this._clearCurrentLink();\n }\n\n private _clearCurrentLink(): void {\n if (this._state) {\n this.clearCells(this._state.x, this._state.y, this._state.length, 1);\n this._state = null;\n }\n }\n\n private _onLinkHover(e: LinkHoverEvent): void {\n this._ctx.fillStyle = this._colors.foreground;\n this.fillBottomLineAtCells(e.x, e.y, e.length);\n this._state = e;\n }\n\n private _onLinkLeave(e: LinkHoverEvent): void {\n this._clearCurrentLink();\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nexport class GridCache {\n public cache: T[][];\n\n public constructor() {\n this.cache = [];\n }\n\n public resize(width: number, height: number): void {\n for (let x = 0; x < width; x++) {\n if (this.cache.length <= x) {\n this.cache.push([]);\n }\n for (let y = this.cache[x].length; y < height; y++) {\n this.cache[x].push(null);\n }\n this.cache[x].length = height;\n }\n this.cache.length = width;\n }\n\n public clear(): void {\n for (let x = 0; x < this.cache.length; x++) {\n for (let y = 0; y < this.cache[x].length; y++) {\n this.cache[x][y] = null;\n }\n }\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IColorSet, IRenderDimensions } from './Interfaces';\nimport { IBuffer, ICharMeasure, ITerminal, ITerminalOptions } from '../Interfaces';\nimport { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CODE_INDEX, CHAR_DATA_CHAR_INDEX } from '../Buffer';\nimport { GridCache } from './GridCache';\nimport { FLAGS } from './Types';\nimport { BaseRenderLayer } from './BaseRenderLayer';\nimport { CharData } from '../Types';\n\ninterface CursorState {\n x: number;\n y: number;\n isFocused: boolean;\n style: string;\n width: number;\n}\n\n/**\n * The time between cursor blinks.\n */\nconst BLINK_INTERVAL = 600;\n\nexport class CursorRenderLayer extends BaseRenderLayer {\n private _state: CursorState;\n private _cursorRenderers: {[key: string]: (terminal: ITerminal, x: number, y: number, charData: CharData) => void};\n private _cursorBlinkStateManager: CursorBlinkStateManager;\n private _isFocused: boolean;\n\n constructor(container: HTMLElement, zIndex: number, colors: IColorSet) {\n super(container, 'cursor', zIndex, true, colors);\n this._state = {\n x: null,\n y: null,\n isFocused: null,\n style: null,\n width: null,\n };\n this._cursorRenderers = {\n 'bar': this._renderBarCursor.bind(this),\n 'block': this._renderBlockCursor.bind(this),\n 'underline': this._renderUnderlineCursor.bind(this)\n };\n // TODO: Consider initial options? Maybe onOptionsChanged should be called at the end of open?\n }\n\n public resize(terminal: ITerminal, dim: IRenderDimensions, charSizeChanged: boolean): void {\n super.resize(terminal, dim, charSizeChanged);\n // Resizing the canvas discards the contents of the canvas so clear state\n this._state = {\n x: null,\n y: null,\n isFocused: null,\n style: null,\n width: null,\n };\n }\n\n public reset(terminal: ITerminal): void {\n this._clearCursor();\n if (this._cursorBlinkStateManager) {\n this._cursorBlinkStateManager.dispose();\n this._cursorBlinkStateManager = null;\n this.onOptionsChanged(terminal);\n }\n }\n\n public onBlur(terminal: ITerminal): void {\n if (this._cursorBlinkStateManager) {\n this._cursorBlinkStateManager.pause();\n }\n terminal.refresh(terminal.buffer.y, terminal.buffer.y);\n }\n\n public onFocus(terminal: ITerminal): void {\n if (this._cursorBlinkStateManager) {\n this._cursorBlinkStateManager.resume(terminal);\n } else {\n terminal.refresh(terminal.buffer.y, terminal.buffer.y);\n }\n }\n\n public onOptionsChanged(terminal: ITerminal): void {\n if (terminal.options.cursorBlink) {\n if (!this._cursorBlinkStateManager) {\n this._cursorBlinkStateManager = new CursorBlinkStateManager(terminal, () => {\n this._render(terminal, true);\n });\n }\n } else {\n if (this._cursorBlinkStateManager) {\n this._cursorBlinkStateManager.dispose();\n this._cursorBlinkStateManager = null;\n }\n // Request a refresh from the terminal as management of rendering is being\n // moved back to the terminal\n terminal.refresh(terminal.buffer.y, terminal.buffer.y);\n }\n }\n\n public onCursorMove(terminal: ITerminal): void {\n if (this._cursorBlinkStateManager) {\n this._cursorBlinkStateManager.restartBlinkAnimation(terminal);\n }\n }\n\n public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void {\n // Only render if the animation frame is not active\n if (!this._cursorBlinkStateManager || this._cursorBlinkStateManager.isPaused) {\n this._render(terminal, false);\n }\n }\n\n private _render(terminal: ITerminal, triggeredByAnimationFrame: boolean): void {\n // Don't draw the cursor if it's hidden\n if (!terminal.cursorState || terminal.cursorHidden) {\n this._clearCursor();\n return;\n }\n\n const cursorY = terminal.buffer.ybase + terminal.buffer.y;\n const viewportRelativeCursorY = cursorY - terminal.buffer.ydisp;\n\n // Don't draw the cursor if it's off-screen\n if (viewportRelativeCursorY < 0 || viewportRelativeCursorY >= terminal.rows) {\n this._clearCursor();\n return;\n }\n\n const charData = terminal.buffer.lines.get(cursorY)[terminal.buffer.x];\n if (!charData) {\n return;\n }\n\n if (!terminal.isFocused) {\n this._clearCursor();\n this._ctx.save();\n this._ctx.fillStyle = this._colors.cursor;\n this._renderBlurCursor(terminal, terminal.buffer.x, viewportRelativeCursorY, charData);\n this._ctx.restore();\n this._state.x = terminal.buffer.x;\n this._state.y = viewportRelativeCursorY;\n this._state.isFocused = false;\n this._state.style = terminal.options.cursorStyle;\n this._state.width = charData[CHAR_DATA_WIDTH_INDEX];\n return;\n }\n\n // Don't draw the cursor if it's blinking\n if (this._cursorBlinkStateManager && !this._cursorBlinkStateManager.isCursorVisible) {\n this._clearCursor();\n return;\n }\n\n if (this._state) {\n // The cursor is already in the correct spot, don't redraw\n if (this._state.x === terminal.buffer.x &&\n this._state.y === viewportRelativeCursorY &&\n this._state.isFocused === terminal.isFocused &&\n this._state.style === terminal.options.cursorStyle &&\n this._state.width === charData[CHAR_DATA_WIDTH_INDEX]) {\n return;\n }\n this._clearCursor();\n }\n\n this._ctx.save();\n this._cursorRenderers[terminal.options.cursorStyle || 'block'](terminal, terminal.buffer.x, viewportRelativeCursorY, charData);\n this._ctx.restore();\n\n this._state.x = terminal.buffer.x;\n this._state.y = viewportRelativeCursorY;\n this._state.isFocused = false;\n this._state.style = terminal.options.cursorStyle;\n this._state.width = charData[CHAR_DATA_WIDTH_INDEX];\n }\n\n private _clearCursor(): void {\n if (this._state) {\n this.clearCells(this._state.x, this._state.y, this._state.width, 1);\n this._state = {\n x: null,\n y: null,\n isFocused: null,\n style: null,\n width: null,\n };\n }\n }\n\n private _renderBarCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {\n this._ctx.save();\n this._ctx.fillStyle = this._colors.cursor;\n this.fillLeftLineAtCell(x, y);\n this._ctx.restore();\n }\n\n private _renderBlockCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {\n this._ctx.save();\n this._ctx.fillStyle = this._colors.cursor;\n this.fillCells(x, y, charData[CHAR_DATA_WIDTH_INDEX], 1);\n this._ctx.fillStyle = this._colors.cursorAccent;\n this.fillCharTrueColor(terminal, charData, x, y);\n this._ctx.restore();\n }\n\n private _renderUnderlineCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {\n this._ctx.save();\n this._ctx.fillStyle = this._colors.cursor;\n this.fillBottomLineAtCells(x, y);\n this._ctx.restore();\n }\n\n private _renderBlurCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {\n this._ctx.save();\n this._ctx.strokeStyle = this._colors.cursor;\n this.strokeRectAtCell(x, y, charData[CHAR_DATA_WIDTH_INDEX], 1);\n this._ctx.restore();\n }\n}\n\nclass CursorBlinkStateManager {\n public isCursorVisible: boolean;\n\n private _animationFrame: number;\n private _blinkStartTimeout: number;\n private _blinkInterval: number;\n\n /**\n * The time at which the animation frame was restarted, this is used on the\n * next render to restart the timers so they don't need to restart the timers\n * multiple times over a short period.\n */\n private _animationTimeRestarted: number;\n\n constructor(\n terminal: ITerminal,\n private renderCallback: () => void\n ) {\n this.isCursorVisible = true;\n if (terminal.isFocused) {\n this._restartInterval();\n }\n }\n\n public get isPaused(): boolean { return !(this._blinkStartTimeout || this._blinkInterval); }\n\n public dispose(): void {\n if (this._blinkInterval) {\n window.clearInterval(this._blinkInterval);\n this._blinkInterval = null;\n }\n if (this._blinkStartTimeout) {\n window.clearTimeout(this._blinkStartTimeout);\n this._blinkStartTimeout = null;\n }\n if (this._animationFrame) {\n window.cancelAnimationFrame(this._animationFrame);\n this._animationFrame = null;\n }\n }\n\n public restartBlinkAnimation(terminal: ITerminal): void {\n if (this.isPaused) {\n return;\n }\n // Save a timestamp so that the restart can be done on the next interval\n this._animationTimeRestarted = Date.now();\n // Force a cursor render to ensure it's visible and in the correct position\n this.isCursorVisible = true;\n if (!this._animationFrame) {\n this._animationFrame = window.requestAnimationFrame(() => {\n this.renderCallback();\n this._animationFrame = null;\n });\n }\n }\n\n private _restartInterval(timeToStart: number = BLINK_INTERVAL): void {\n // Clear any existing interval\n if (this._blinkInterval) {\n window.clearInterval(this._blinkInterval);\n }\n\n // Setup the initial timeout which will hide the cursor, this is done before\n // the regular interval is setup in order to support restarting the blink\n // animation in a lightweight way (without thrashing clearInterval and\n // setInterval).\n this._blinkStartTimeout = setTimeout(() => {\n // Check if another animation restart was requested while this was being\n // started\n if (this._animationTimeRestarted) {\n const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted);\n this._animationTimeRestarted = null;\n if (time > 0) {\n this._restartInterval(time);\n return;\n }\n }\n\n // Hide the cursor\n this.isCursorVisible = false;\n this._animationFrame = window.requestAnimationFrame(() => {\n this.renderCallback();\n this._animationFrame = null;\n });\n\n // Setup the blink interval\n this._blinkInterval = setInterval(() => {\n // Adjust the animation time if it was restarted\n if (this._animationTimeRestarted) {\n // calc time diff\n // Make restart interval do a setTimeout initially?\n const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted);\n this._animationTimeRestarted = null;\n this._restartInterval(time);\n return;\n }\n\n // Invert visibility and render\n this.isCursorVisible = !this.isCursorVisible;\n this._animationFrame = window.requestAnimationFrame(() => {\n this.renderCallback();\n this._animationFrame = null;\n });\n }, BLINK_INTERVAL);\n }, timeToStart);\n }\n\n public pause(): void {\n this.isCursorVisible = true;\n if (this._blinkInterval) {\n window.clearInterval(this._blinkInterval);\n this._blinkInterval = null;\n }\n if (this._blinkStartTimeout) {\n window.clearTimeout(this._blinkStartTimeout);\n this._blinkStartTimeout = null;\n }\n if (this._animationFrame) {\n window.cancelAnimationFrame(this._animationFrame);\n this._animationFrame = null;\n }\n }\n\n public resume(terminal: ITerminal): void {\n this._animationTimeRestarted = null;\n this._restartInterval();\n this.restartBlinkAnimation(terminal);\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IColorSet, IColorManager } from './Interfaces';\nimport { ITheme } from '../Interfaces';\n\nconst DEFAULT_FOREGROUND = '#ffffff';\nconst DEFAULT_BACKGROUND = '#000000';\nconst DEFAULT_CURSOR = '#ffffff';\nconst DEFAULT_CURSOR_ACCENT = '#000000';\nconst DEFAULT_SELECTION = 'rgba(255, 255, 255, 0.3)';\nexport const DEFAULT_ANSI_COLORS = [\n // dark:\n '#2e3436',\n '#cc0000',\n '#4e9a06',\n '#c4a000',\n '#3465a4',\n '#75507b',\n '#06989a',\n '#d3d7cf',\n // bright:\n '#555753',\n '#ef2929',\n '#8ae234',\n '#fce94f',\n '#729fcf',\n '#ad7fa8',\n '#34e2e2',\n '#eeeeec'\n];\n\n/**\n * Fills an existing 16 length string with the remaining 240 ANSI colors.\n * @param first16Colors The first 16 ANSI colors.\n */\nfunction generate256Colors(first16Colors: string[]): string[] {\n let colors = first16Colors.slice();\n\n // Generate colors (16-231)\n let v = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff];\n for (let i = 0; i < 216; i++) {\n const r = toPaddedHex(v[(i / 36) % 6 | 0]);\n const g = toPaddedHex(v[(i / 6) % 6 | 0]);\n const b = toPaddedHex(v[i % 6]);\n colors.push(`#${r}${g}${b}`);\n }\n\n // Generate greys (232-255)\n for (let i = 0; i < 24; i++) {\n const c = toPaddedHex(8 + i * 10);\n colors.push(`#${c}${c}${c}`);\n }\n\n return colors;\n}\n\nfunction toPaddedHex(c: number): string {\n let s = c.toString(16);\n return s.length < 2 ? '0' + s : s;\n}\n\n/**\n * Manages the source of truth for a terminal's colors.\n */\nexport class ColorManager implements IColorManager {\n public colors: IColorSet;\n\n constructor() {\n this.colors = {\n foreground: DEFAULT_FOREGROUND,\n background: DEFAULT_BACKGROUND,\n cursor: DEFAULT_CURSOR,\n cursorAccent: DEFAULT_CURSOR_ACCENT,\n selection: DEFAULT_SELECTION,\n ansi: generate256Colors(DEFAULT_ANSI_COLORS)\n };\n }\n\n /**\n * Sets the terminal's theme.\n * @param theme The theme to use. If a partial theme is provided then default\n * colors will be used where colors are not defined.\n */\n public setTheme(theme: ITheme): void {\n this.colors.foreground = theme.foreground || DEFAULT_FOREGROUND;\n this.colors.background = this._validateColor(theme.background, DEFAULT_BACKGROUND);\n this.colors.cursor = theme.cursor || DEFAULT_CURSOR;\n this.colors.cursorAccent = theme.cursorAccent || DEFAULT_CURSOR_ACCENT;\n this.colors.selection = theme.selection || DEFAULT_SELECTION;\n this.colors.ansi[0] = theme.black || DEFAULT_ANSI_COLORS[0];\n this.colors.ansi[1] = theme.red || DEFAULT_ANSI_COLORS[1];\n this.colors.ansi[2] = theme.green || DEFAULT_ANSI_COLORS[2];\n this.colors.ansi[3] = theme.yellow || DEFAULT_ANSI_COLORS[3];\n this.colors.ansi[4] = theme.blue || DEFAULT_ANSI_COLORS[4];\n this.colors.ansi[5] = theme.magenta || DEFAULT_ANSI_COLORS[5];\n this.colors.ansi[6] = theme.cyan || DEFAULT_ANSI_COLORS[6];\n this.colors.ansi[7] = theme.white || DEFAULT_ANSI_COLORS[7];\n this.colors.ansi[8] = theme.brightBlack || DEFAULT_ANSI_COLORS[8];\n this.colors.ansi[9] = theme.brightRed || DEFAULT_ANSI_COLORS[9];\n this.colors.ansi[10] = theme.brightGreen || DEFAULT_ANSI_COLORS[10];\n this.colors.ansi[11] = theme.brightYellow || DEFAULT_ANSI_COLORS[11];\n this.colors.ansi[12] = theme.brightBlue || DEFAULT_ANSI_COLORS[12];\n this.colors.ansi[13] = theme.brightMagenta || DEFAULT_ANSI_COLORS[13];\n this.colors.ansi[14] = theme.brightCyan || DEFAULT_ANSI_COLORS[14];\n this.colors.ansi[15] = theme.brightWhite || DEFAULT_ANSI_COLORS[15];\n }\n\n private _validateColor(color: string, fallback: string): string {\n if (!color) {\n return fallback;\n }\n if (color.length === 7 && color.charAt(0) === '#') {\n return color;\n }\n if (color.length === 4 && color.charAt(0) === '#') {\n const r = color.charAt(1);\n const g = color.charAt(2);\n const b = color.charAt(3);\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return fallback;\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal, ITheme } from '../Interfaces';\nimport { IColorSet } from '../renderer/Interfaces';\nimport { isFirefox } from '../utils/Browser';\n\nexport const CHAR_ATLAS_CELL_SPACING = 1;\n\ninterface ICharAtlasConfig {\n fontSize: number;\n fontFamily: string;\n scaledCharWidth: number;\n scaledCharHeight: number;\n colors: IColorSet;\n}\n\ninterface ICharAtlasCacheEntry {\n bitmap: HTMLCanvasElement | Promise;\n config: ICharAtlasConfig;\n ownedBy: ITerminal[];\n}\n\nlet charAtlasCache: ICharAtlasCacheEntry[] = [];\n\n/**\n * Acquires a char atlas, either generating a new one or returning an existing\n * one that is in use by another terminal.\n * @param terminal The terminal.\n * @param colors The colors to use.\n */\nexport function acquireCharAtlas(terminal: ITerminal, colors: IColorSet, scaledCharWidth: number, scaledCharHeight: number): HTMLCanvasElement | Promise {\n const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, terminal, colors);\n\n // Check to see if the terminal already owns this config\n for (let i = 0; i < charAtlasCache.length; i++) {\n const entry = charAtlasCache[i];\n const ownedByIndex = entry.ownedBy.indexOf(terminal);\n if (ownedByIndex >= 0) {\n if (configEquals(entry.config, newConfig)) {\n return entry.bitmap;\n } else {\n // The configs differ, release the terminal from the entry\n if (entry.ownedBy.length === 1) {\n charAtlasCache.splice(i, 1);\n } else {\n entry.ownedBy.splice(ownedByIndex, 1);\n }\n break;\n }\n }\n }\n\n // Try match a char atlas from the cache\n for (let i = 0; i < charAtlasCache.length; i++) {\n const entry = charAtlasCache[i];\n if (configEquals(entry.config, newConfig)) {\n // Add the terminal to the cache entry and return\n entry.ownedBy.push(terminal);\n return entry.bitmap;\n }\n }\n\n const newEntry: ICharAtlasCacheEntry = {\n bitmap: generator.generate(scaledCharWidth, scaledCharHeight, terminal.options.fontSize, terminal.options.fontFamily, colors.background, colors.foreground, colors.ansi),\n config: newConfig,\n ownedBy: [terminal]\n };\n charAtlasCache.push(newEntry);\n return newEntry.bitmap;\n}\n\nfunction generateConfig(scaledCharWidth: number, scaledCharHeight: number, terminal: ITerminal, colors: IColorSet): ICharAtlasConfig {\n const clonedColors = {\n foreground: colors.foreground,\n background: colors.background,\n cursor: null,\n cursorAccent: null,\n selection: null,\n ansi: colors.ansi.slice(0, 16)\n };\n return {\n scaledCharWidth,\n scaledCharHeight,\n fontFamily: terminal.options.fontFamily,\n fontSize: terminal.options.fontSize,\n colors: clonedColors\n };\n}\n\nfunction configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean {\n for (let i = 0; i < a.colors.ansi.length; i++) {\n if (a.colors.ansi[i] !== b.colors.ansi[i]) {\n return false;\n }\n }\n return a.fontFamily === b.fontFamily &&\n a.fontSize === b.fontSize &&\n a.scaledCharWidth === b.scaledCharWidth &&\n a.scaledCharHeight === b.scaledCharHeight &&\n a.colors.foreground === b.colors.foreground &&\n a.colors.background === b.colors.background;\n}\n\nlet generator: CharAtlasGenerator;\n\n/**\n * Initializes the char atlas generator.\n * @param document The document.\n */\nexport function initialize(document: Document): void {\n if (!generator) {\n generator = new CharAtlasGenerator(document);\n }\n}\n\nclass CharAtlasGenerator {\n private _canvas: HTMLCanvasElement;\n private _ctx: CanvasRenderingContext2D;\n\n constructor(private _document: Document) {\n this._canvas = this._document.createElement('canvas');\n this._ctx = this._canvas.getContext('2d', {alpha: false});\n this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio);\n }\n\n public generate(scaledCharWidth: number, scaledCharHeight: number, fontSize: number, fontFamily: string, background: string, foreground: string, ansiColors: string[]): HTMLCanvasElement | Promise {\n const cellWidth = scaledCharWidth + CHAR_ATLAS_CELL_SPACING;\n const cellHeight = scaledCharHeight + CHAR_ATLAS_CELL_SPACING;\n this._canvas.width = 255 * cellWidth;\n this._canvas.height = (/*default+default bold*/2 + /*0-15*/16) * cellHeight;\n\n this._ctx.fillStyle = background;\n this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height);\n\n this._ctx.save();\n this._ctx.fillStyle = foreground;\n this._ctx.font = `${fontSize * window.devicePixelRatio}px ${fontFamily}`;\n this._ctx.textBaseline = 'top';\n\n // Default color\n for (let i = 0; i < 256; i++) {\n this._ctx.save();\n this._ctx.beginPath();\n this._ctx.rect(i * cellWidth, 0, cellWidth, cellHeight);\n this._ctx.clip();\n this._ctx.fillText(String.fromCharCode(i), i * cellWidth, 0);\n this._ctx.restore();\n }\n // Default color bold\n this._ctx.save();\n this._ctx.font = `bold ${this._ctx.font}`;\n for (let i = 0; i < 256; i++) {\n this._ctx.save();\n this._ctx.beginPath();\n this._ctx.rect(i * cellWidth, cellHeight, cellWidth, cellHeight);\n this._ctx.clip();\n this._ctx.fillText(String.fromCharCode(i), i * cellWidth, cellHeight);\n this._ctx.restore();\n }\n this._ctx.restore();\n\n // Colors 0-15\n this._ctx.font = `${fontSize * window.devicePixelRatio}px ${fontFamily}`;\n for (let colorIndex = 0; colorIndex < 16; colorIndex++) {\n // colors 8-15 are bold\n if (colorIndex === 8) {\n this._ctx.font = `bold ${this._ctx.font}`;\n }\n const y = (colorIndex + 2) * cellHeight;\n // Draw ascii characters\n for (let i = 0; i < 256; i++) {\n this._ctx.save();\n this._ctx.beginPath();\n this._ctx.rect(i * cellWidth, y, cellWidth, cellHeight);\n this._ctx.clip();\n this._ctx.fillStyle = ansiColors[colorIndex];\n this._ctx.fillText(String.fromCharCode(i), i * cellWidth, y);\n this._ctx.restore();\n }\n }\n this._ctx.restore();\n\n // Support is patchy for createImageBitmap at the moment, pass a canvas back\n // if support is lacking as drawImage works there too. Firefox is also\n // included here as ImageBitmap appears both buggy and has horrible\n // performance (tested on v55).\n if (!('createImageBitmap' in window) || isFirefox) {\n // Regenerate canvas and context as they are now owned by the char atlas\n const result = this._canvas;\n this._canvas = this._document.createElement('canvas');\n this._ctx = this._canvas.getContext('2d');\n this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio);\n return result;\n }\n\n const charAtlasImageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height);\n\n // Remove the background color from the image so characters may overlap\n const r = parseInt(background.substr(1, 2), 16);\n const g = parseInt(background.substr(3, 2), 16);\n const b = parseInt(background.substr(5, 2), 16);\n this._clearColor(charAtlasImageData, r, g, b);\n\n const promise = window.createImageBitmap(charAtlasImageData);\n // Clear the rect while the promise is in progress\n this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);\n return promise;\n }\n\n private _clearColor(imageData: ImageData, r: number, g: number, b: number): void {\n for (let offset = 0; offset < imageData.data.length; offset += 4) {\n if (imageData.data[offset] === r &&\n imageData.data[offset + 1] === g &&\n imageData.data[offset + 2] === b) {\n imageData.data[offset + 3] = 0;\n }\n }\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IRenderLayer, IColorSet, IRenderDimensions } from './Interfaces';\nimport { ITerminal, ITerminalOptions } from '../Interfaces';\nimport { acquireCharAtlas, CHAR_ATLAS_CELL_SPACING } from './CharAtlas';\nimport { CharData } from '../Types';\nimport { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from '../Buffer';\n\nexport const INVERTED_DEFAULT_COLOR = -1;\nconst DIM_OPACITY = 0.5;\n\nexport abstract class BaseRenderLayer implements IRenderLayer {\n private _canvas: HTMLCanvasElement;\n protected _ctx: CanvasRenderingContext2D;\n private _scaledCharWidth: number = 0;\n private _scaledCharHeight: number = 0;\n private _scaledCellWidth: number = 0;\n private _scaledCellHeight: number = 0;\n private _scaledCharLeft: number = 0;\n private _scaledCharTop: number = 0;\n\n private _charAtlas: HTMLCanvasElement | ImageBitmap;\n\n constructor(\n container: HTMLElement,\n id: string,\n zIndex: number,\n private _alpha: boolean,\n protected _colors: IColorSet\n ) {\n this._canvas = document.createElement('canvas');\n this._canvas.id = `xterm-${id}-layer`;\n this._canvas.style.zIndex = zIndex.toString();\n this._ctx = this._canvas.getContext('2d', {alpha: _alpha});\n this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio);\n // Draw the background if this is an opaque layer\n if (!_alpha) {\n this.clearAll();\n }\n container.appendChild(this._canvas);\n }\n\n public onOptionsChanged(terminal: ITerminal): void {}\n public onBlur(terminal: ITerminal): void {}\n public onFocus(terminal: ITerminal): void {}\n public onCursorMove(terminal: ITerminal): void {}\n public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void {}\n public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number]): void {}\n\n public onThemeChanged(terminal: ITerminal, colorSet: IColorSet): void {\n this._refreshCharAtlas(terminal, colorSet);\n }\n\n /**\n * Refreshes the char atlas, aquiring a new one if necessary.\n * @param terminal The terminal.\n * @param colorSet The color set to use for the char atlas.\n */\n private _refreshCharAtlas(terminal: ITerminal, colorSet: IColorSet): void {\n if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) {\n return;\n }\n this._charAtlas = null;\n const result = acquireCharAtlas(terminal, this._colors, this._scaledCharWidth, this._scaledCharHeight);\n if (result instanceof HTMLCanvasElement) {\n this._charAtlas = result;\n } else {\n result.then(bitmap => this._charAtlas = bitmap);\n }\n }\n\n public resize(terminal: ITerminal, dim: IRenderDimensions, charSizeChanged: boolean): void {\n this._scaledCellWidth = dim.scaledCellWidth;\n this._scaledCellHeight = dim.scaledCellHeight;\n this._scaledCharWidth = dim.scaledCharWidth;\n this._scaledCharHeight = dim.scaledCharHeight;\n this._scaledCharLeft = dim.scaledCharLeft;\n this._scaledCharTop = dim.scaledCharTop;\n this._canvas.width = dim.scaledCanvasWidth;\n this._canvas.height = dim.scaledCanvasHeight;\n this._canvas.style.width = `${dim.canvasWidth}px`;\n this._canvas.style.height = `${dim.canvasHeight}px`;\n\n // Draw the background if this is an opaque layer\n if (!this._alpha) {\n this.clearAll();\n }\n\n if (charSizeChanged) {\n this._refreshCharAtlas(terminal, this._colors);\n }\n }\n\n public abstract reset(terminal: ITerminal): void;\n\n /**\n * Fills 1+ cells completely. This uses the existing fillStyle on the context.\n * @param x The column to start at.\n * @param y The row to start at\n * @param width The number of columns to fill.\n * @param height The number of rows to fill.\n */\n protected fillCells(x: number, y: number, width: number, height: number): void {\n this._ctx.fillRect(\n x * this._scaledCellWidth,\n y * this._scaledCellHeight,\n width * this._scaledCellWidth,\n height * this._scaledCellHeight);\n }\n\n /**\n * Fills a 1px line (2px on HDPI) at the bottom of the cell. This uses the\n * existing fillStyle on the context.\n * @param x The column to fill.\n * @param y The row to fill.\n */\n protected fillBottomLineAtCells(x: number, y: number, width: number = 1): void {\n this._ctx.fillRect(\n x * this._scaledCellWidth,\n (y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1 /* Ensure it's drawn within the cell */,\n width * this._scaledCellWidth,\n window.devicePixelRatio);\n }\n\n /**\n * Fills a 1px line (2px on HDPI) at the left of the cell. This uses the\n * existing fillStyle on the context.\n * @param x The column to fill.\n * @param y The row to fill.\n */\n protected fillLeftLineAtCell(x: number, y: number): void {\n this._ctx.fillRect(\n x * this._scaledCellWidth,\n y * this._scaledCellHeight,\n window.devicePixelRatio,\n this._scaledCellHeight);\n }\n\n /**\n * Strokes a 1px rectangle (2px on HDPI) around a cell. This uses the existing\n * strokeStyle on the context.\n * @param x The column to fill.\n * @param y The row to fill.\n */\n protected strokeRectAtCell(x: number, y: number, width: number, height: number): void {\n this._ctx.lineWidth = window.devicePixelRatio;\n this._ctx.strokeRect(\n x * this._scaledCellWidth + window.devicePixelRatio / 2,\n y * this._scaledCellHeight + (window.devicePixelRatio / 2),\n width * this._scaledCellWidth - window.devicePixelRatio,\n (height * this._scaledCellHeight) - window.devicePixelRatio);\n }\n\n /**\n * Clears the entire canvas.\n */\n protected clearAll(): void {\n if (this._alpha) {\n this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);\n } else {\n this._ctx.fillStyle = this._colors.background;\n this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height);\n }\n }\n\n /**\n * Clears 1+ cells completely.\n * @param x The column to start at.\n * @param y The row to start at.\n * @param width The number of columns to clear.\n * @param height The number of rows to clear.\n */\n protected clearCells(x: number, y: number, width: number, height: number): void {\n if (this._alpha) {\n this._ctx.clearRect(\n x * this._scaledCellWidth,\n y * this._scaledCellHeight,\n width * this._scaledCellWidth,\n height * this._scaledCellHeight);\n } else {\n this._ctx.fillStyle = this._colors.background;\n this._ctx.fillRect(\n x * this._scaledCellWidth,\n y * this._scaledCellHeight,\n width * this._scaledCellWidth,\n height * this._scaledCellHeight);\n }\n }\n\n /**\n * Draws a truecolor character at the cell. The character will be clipped to\n * ensure that it fits with the cell, including the cell to the right if it's\n * a wide character. This uses the existing fillStyle on the context.\n * @param terminal The terminal.\n * @param charData The char data for the character to draw.\n * @param x The column to draw at.\n * @param y The row to draw at.\n * @param color The color of the character.\n */\n protected fillCharTrueColor(terminal: ITerminal, charData: CharData, x: number, y: number): void {\n this._ctx.font = `${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`;\n this._ctx.textBaseline = 'top';\n this._clipRow(terminal, y);\n this._ctx.fillText(\n charData[CHAR_DATA_CHAR_INDEX],\n x * this._scaledCellWidth + this._scaledCharLeft,\n y * this._scaledCellHeight + this._scaledCharTop);\n }\n\n /**\n * Draws a character at a cell. If possible this will draw using the character\n * atlas to reduce draw time.\n * @param terminal The terminal.\n * @param char The character.\n * @param code The character code.\n * @param width The width of the character.\n * @param x The column to draw at.\n * @param y The row to draw at.\n * @param fg The foreground color, in the format stored within the attributes.\n * @param bg The background color, in the format stored within the attributes.\n * This is used to validate whether a cached image can be used.\n * @param bold Whether the text is bold.\n */\n protected drawChar(terminal: ITerminal, char: string, code: number, width: number, x: number, y: number, fg: number, bg: number, bold: boolean, dim: boolean): void {\n let colorIndex = 0;\n if (fg < 256) {\n colorIndex = fg + 2;\n } else {\n // If default color and bold\n if (bold && terminal.options.enableBold) {\n colorIndex = 1;\n }\n }\n const isAscii = code < 256;\n // A color is basic if it is one of the standard normal or bold weight\n // colors of the characters held in the char atlas. Note that this excludes\n // the normal weight _light_ color characters.\n const isBasicColor = (colorIndex > 1 && fg < 16) && (fg < 8 || bold);\n const isDefaultColor = fg >= 256;\n const isDefaultBackground = bg >= 256;\n if (this._charAtlas && isAscii && (isBasicColor || isDefaultColor) && isDefaultBackground) {\n // ImageBitmap's draw about twice as fast as from a canvas\n const charAtlasCellWidth = this._scaledCharWidth + CHAR_ATLAS_CELL_SPACING;\n const charAtlasCellHeight = this._scaledCharHeight + CHAR_ATLAS_CELL_SPACING;\n\n // Apply alpha to dim the character\n if (dim) {\n this._ctx.globalAlpha = DIM_OPACITY;\n }\n\n // Draw the non-bold version of the same color if bold is not enabled\n if (bold && !terminal.options.enableBold) {\n // Ignore default color as it's not touched above\n if (colorIndex > 1) {\n colorIndex -= 8;\n }\n }\n\n this._ctx.drawImage(this._charAtlas,\n code * charAtlasCellWidth,\n colorIndex * charAtlasCellHeight,\n charAtlasCellWidth,\n this._scaledCharHeight,\n x * this._scaledCellWidth + this._scaledCharLeft,\n y * this._scaledCellHeight + this._scaledCharTop,\n charAtlasCellWidth,\n this._scaledCharHeight);\n } else {\n this._drawUncachedChar(terminal, char, width, fg, x, y, bold, dim);\n }\n // This draws the atlas (for debugging purposes)\n // this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);\n // this._ctx.drawImage(this._charAtlas, 0, 0);\n }\n\n /**\n * Draws a character at a cell. The character will be clipped to\n * ensure that it fits with the cell, including the cell to the right if it's\n * a wide character.\n * @param terminal The terminal.\n * @param char The character.\n * @param width The width of the character.\n * @param fg The foreground color, in the format stored within the attributes.\n * @param x The column to draw at.\n * @param y The row to draw at.\n */\n private _drawUncachedChar(terminal: ITerminal, char: string, width: number, fg: number, x: number, y: number, bold: boolean, dim: boolean): void {\n this._ctx.save();\n this._ctx.font = `${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`;\n if (bold && terminal.options.enableBold) {\n this._ctx.font = `bold ${this._ctx.font}`;\n }\n this._ctx.textBaseline = 'top';\n\n if (fg === INVERTED_DEFAULT_COLOR) {\n this._ctx.fillStyle = this._colors.background;\n } else if (fg < 256) {\n // 256 color support\n this._ctx.fillStyle = this._colors.ansi[fg];\n } else {\n this._ctx.fillStyle = this._colors.foreground;\n }\n\n this._clipRow(terminal, y);\n\n // Apply alpha to dim the character\n if (dim) {\n this._ctx.globalAlpha = DIM_OPACITY;\n }\n // Draw the character\n this._ctx.fillText(\n char,\n x * this._scaledCellWidth + this._scaledCharLeft,\n y * this._scaledCellHeight + this._scaledCharTop);\n this._ctx.restore();\n }\n\n /**\n * Clips a row to ensure no pixels will be drawn outside the cells in the row.\n * @param terminal The terminal.\n * @param y The row to clip.\n */\n private _clipRow(terminal: ITerminal, y: number): void {\n this._ctx.beginPath();\n this._ctx.rect(\n 0,\n y * this._scaledCellHeight,\n terminal.cols * this._scaledCellWidth,\n this._scaledCellHeight);\n this._ctx.clip();\n }\n}\n\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IMouseZoneManager, IMouseZone } from './Interfaces';\nimport { ITerminal } from '../Interfaces';\n\nconst HOVER_DURATION = 500;\n\n/**\n * The MouseZoneManager allows components to register zones within the terminal\n * that trigger hover and click callbacks.\n *\n * This class was intentionally made not so robust initially as the only case it\n * needed to support was single-line links which never overlap. Improvements can\n * be made in the future.\n */\nexport class MouseZoneManager implements IMouseZoneManager {\n private _zones: IMouseZone[] = [];\n\n private _areZonesActive: boolean = false;\n private _mouseMoveListener: (e: MouseEvent) => any;\n private _clickListener: (e: MouseEvent) => any;\n\n private _tooltipTimeout: number = null;\n private _currentZone: IMouseZone = null;\n private _lastHoverCoords: [number, number] = [null, null];\n\n constructor(\n private _terminal: ITerminal\n ) {\n this._terminal.element.addEventListener('mousedown', e => this._onMouseDown(e));\n\n // These events are expensive, only listen to it when mouse zones are active\n this._mouseMoveListener = e => this._onMouseMove(e);\n this._clickListener = e => this._onClick(e);\n }\n\n public add(zone: IMouseZone): void {\n this._zones.push(zone);\n if (this._zones.length === 1) {\n this._activate();\n }\n }\n\n public clearAll(start?: number, end?: number): void {\n // Exit if there's nothing to clear\n if (this._zones.length === 0) {\n return;\n }\n\n // Clear all if start/end weren't set\n if (!end) {\n start = 0;\n end = this._terminal.rows - 1;\n }\n\n // Iterate through zones and clear them out if they're within the range\n for (let i = 0; i < this._zones.length; i++) {\n const zone = this._zones[i];\n if (zone.y > start && zone.y <= end + 1) {\n if (this._currentZone && this._currentZone === zone) {\n this._currentZone.leaveCallback();\n this._currentZone = null;\n }\n this._zones.splice(i--, 1);\n }\n }\n\n // Deactivate the mouse zone manager if all the zones have been removed\n if (this._zones.length === 0) {\n this._deactivate();\n }\n }\n\n private _activate(): void {\n if (!this._areZonesActive) {\n this._areZonesActive = true;\n this._terminal.element.addEventListener('mousemove', this._mouseMoveListener);\n this._terminal.element.addEventListener('click', this._clickListener);\n }\n }\n\n private _deactivate(): void {\n if (this._areZonesActive) {\n this._areZonesActive = false;\n this._terminal.element.removeEventListener('mousemove', this._mouseMoveListener);\n this._terminal.element.removeEventListener('click', this._clickListener);\n }\n }\n\n private _onMouseMove(e: MouseEvent): void {\n // TODO: Ideally this would only clear the hover state when the mouse moves\n // outside of the mouse zone\n if (this._lastHoverCoords[0] !== e.pageX || this._lastHoverCoords[1] !== e.pageY) {\n this._onHover(e);\n // Record the current coordinates\n this._lastHoverCoords = [e.pageX, e.pageY];\n }\n }\n\n private _onHover(e: MouseEvent): void {\n const zone = this._findZoneEventAt(e);\n\n // Do nothing if the zone is the same\n if (zone === this._currentZone) {\n return;\n }\n\n // Fire the hover end callback and cancel any existing timer if a new zone\n // is being hovered\n if (this._currentZone) {\n this._currentZone.leaveCallback();\n this._currentZone = null;\n if (this._tooltipTimeout) {\n clearTimeout(this._tooltipTimeout);\n }\n }\n\n // Exit if there is not zone\n if (!zone) {\n return;\n }\n this._currentZone = zone;\n\n // Trigger the hover callback\n if (zone.hoverCallback) {\n zone.hoverCallback(e);\n }\n\n // Restart the tooltip timeout\n this._tooltipTimeout = setTimeout(() => this._onTooltip(e), HOVER_DURATION);\n }\n\n private _onTooltip(e: MouseEvent): void {\n this._tooltipTimeout = null;\n const zone = this._findZoneEventAt(e);\n if (zone && zone.tooltipCallback) {\n zone.tooltipCallback(e);\n }\n }\n\n private _onMouseDown(e: MouseEvent): void {\n // Ignore the event if there are no zones active\n if (!this._areZonesActive) {\n return;\n }\n\n // Find the active zone, prevent event propagation if found to prevent other\n // components from handling the mouse event.\n const zone = this._findZoneEventAt(e);\n if (zone) {\n // TODO: When link modifier support is added, the event should only be\n // cancelled when the modifier is held (see #1021)\n e.preventDefault();\n e.stopImmediatePropagation();\n }\n }\n\n private _onClick(e: MouseEvent): void {\n // Find the active zone and click it if found\n const zone = this._findZoneEventAt(e);\n if (zone) {\n zone.clickCallback(e);\n e.preventDefault();\n e.stopImmediatePropagation();\n }\n }\n\n private _findZoneEventAt(e: MouseEvent): IMouseZone {\n const coords = this._terminal.mouseHelper.getCoords(e, this._terminal.element, this._terminal.charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows);\n if (!coords) {\n return null;\n }\n for (let i = 0; i < this._zones.length; i++) {\n const zone = this._zones[i];\n if (zone.y === coords[1] && zone.x1 <= coords[0] && zone.x2 > coords[0]) {\n return zone;\n }\n };\n return null;\n }\n}\n\nexport class MouseZone implements IMouseZone {\n constructor(\n public x1: number,\n public x2: number,\n public y: number,\n public clickCallback: (e: MouseEvent) => any,\n public hoverCallback?: (e: MouseEvent) => any,\n public tooltipCallback?: (e: MouseEvent) => any,\n public leaveCallback?: () => void\n ) {\n }\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal, ISelectionManager } from '../Interfaces';\n\ninterface IWindow extends Window {\n clipboardData?: {\n getData(format: string): string;\n setData(format: string, data: string): void;\n };\n}\n\ndeclare var window: IWindow;\n\n/**\n * Prepares text to be pasted into the terminal by normalizing the line endings\n * @param text The pasted text that needs processing before inserting into the terminal\n */\nexport function prepareTextForTerminal(text: string, isMSWindows: boolean): string {\n if (isMSWindows) {\n return text.replace(/\\r?\\n/g, '\\r');\n }\n return text;\n}\n\n/**\n * Bracket text for paste, if necessary, as per https://cirw.in/blog/bracketed-paste\n * @param text The pasted text to bracket\n */\nexport function bracketTextForPaste(text: string, bracketedPasteMode: boolean): string {\n if (bracketedPasteMode) {\n return '\\x1b[200~' + text + '\\x1b[201~';\n }\n return text;\n}\n\n/**\n * Binds copy functionality to the given terminal.\n * @param {ClipboardEvent} ev The original copy event to be handled\n */\nexport function copyHandler(ev: ClipboardEvent, term: ITerminal, selectionManager: ISelectionManager): void {\n if (term.browser.isMSIE) {\n window.clipboardData.setData('Text', selectionManager.selectionText);\n } else {\n ev.clipboardData.setData('text/plain', selectionManager.selectionText);\n }\n\n // Prevent or the original text will be copied.\n ev.preventDefault();\n}\n\n/**\n * Redirect the clipboard's data to the terminal's input handler.\n * @param {ClipboardEvent} ev The original paste event to be handled\n * @param {Terminal} term The terminal on which to apply the handled paste event\n */\nexport function pasteHandler(ev: ClipboardEvent, term: ITerminal): void {\n ev.stopPropagation();\n\n let text: string;\n\n let dispatchPaste = function(text: string): void {\n text = prepareTextForTerminal(text, term.browser.isMSWindows);\n text = bracketTextForPaste(text, term.bracketedPasteMode);\n term.handler(text);\n term.textarea.value = '';\n term.emit('paste', text);\n term.cancel(ev);\n };\n\n if (term.browser.isMSIE) {\n if (window.clipboardData) {\n text = window.clipboardData.getData('Text');\n dispatchPaste(text);\n }\n } else {\n if (ev.clipboardData) {\n text = ev.clipboardData.getData('text/plain');\n dispatchPaste(text);\n }\n }\n}\n\n/**\n * Moves the textarea under the mouse cursor and focuses it.\n * @param ev The original right click event to be handled.\n * @param textarea The terminal's textarea.\n */\nexport function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextAreaElement): void {\n // Bring textarea at the cursor position\n textarea.style.position = 'fixed';\n textarea.style.width = '20px';\n textarea.style.height = '20px';\n textarea.style.left = (ev.clientX - 10) + 'px';\n textarea.style.top = (ev.clientY - 10) + 'px';\n textarea.style.zIndex = '1000';\n\n textarea.focus();\n\n // Reset the terminal textarea's styling\n setTimeout(() => {\n textarea.style.position = null;\n textarea.style.width = null;\n textarea.style.height = null;\n textarea.style.left = null;\n textarea.style.top = null;\n textarea.style.zIndex = null;\n }, 4);\n}\n\n/**\n * Bind to right-click event and allow right-click copy and paste.\n * @param ev The original right click event to be handled.\n * @param textarea The terminal's textarea.\n * @param selectionManager The terminal's selection manager.\n */\nexport function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, selectionManager: ISelectionManager): void {\n moveTextAreaUnderMouseCursor(ev, textarea);\n\n // Get textarea ready to copy from the context menu\n textarea.value = selectionManager.selectionText;\n textarea.select();\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal, IViewport } from './Interfaces';\nimport { CharMeasure } from './utils/CharMeasure';\nimport { IColorSet } from './renderer/Interfaces';\n\n/**\n * Represents the viewport of a terminal, the visible area within the larger buffer of output.\n * Logic for the virtual scroll bar is included in this object.\n */\nexport class Viewport implements IViewport {\n private currentRowHeight: number = 0;\n private lastRecordedBufferLength: number = 0;\n private lastRecordedViewportHeight: number = 0;\n private lastRecordedBufferHeight: number = 0;\n private lastTouchY: number;\n\n /**\n * Creates a new Viewport.\n * @param terminal The terminal this viewport belongs to.\n * @param viewportElement The DOM element acting as the viewport.\n * @param scrollArea The DOM element acting as the scroll area.\n * @param charMeasure A DOM element used to measure the character size of. the terminal.\n */\n constructor(\n private terminal: ITerminal,\n private viewportElement: HTMLElement,\n private scrollArea: HTMLElement,\n private charMeasure: CharMeasure\n ) {\n this.viewportElement.addEventListener('scroll', this.onScroll.bind(this));\n\n // Perform this async to ensure the CharMeasure is ready.\n setTimeout(() => this.syncScrollArea(), 0);\n }\n\n public onThemeChanged(colors: IColorSet): void {\n this.viewportElement.style.backgroundColor = colors.background;\n }\n\n /**\n * Refreshes row height, setting line-height, viewport height and scroll area height if\n * necessary.\n */\n private refresh(): void {\n if (this.charMeasure.height > 0) {\n this.currentRowHeight = this.terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio;\n\n if (this.lastRecordedViewportHeight !== this.terminal.renderer.dimensions.canvasHeight) {\n this.lastRecordedViewportHeight = this.terminal.renderer.dimensions.canvasHeight;\n this.viewportElement.style.height = this.lastRecordedViewportHeight + 'px';\n }\n\n const newBufferHeight = Math.round(this.currentRowHeight * this.lastRecordedBufferLength);\n if (this.lastRecordedBufferHeight !== newBufferHeight) {\n this.lastRecordedBufferHeight = newBufferHeight;\n this.scrollArea.style.height = this.lastRecordedBufferHeight + 'px';\n }\n }\n }\n\n /**\n * Updates dimensions and synchronizes the scroll area if necessary.\n */\n public syncScrollArea(): void {\n if (this.lastRecordedBufferLength !== this.terminal.buffer.lines.length) {\n // If buffer height changed\n this.lastRecordedBufferLength = this.terminal.buffer.lines.length;\n this.refresh();\n } else if (this.lastRecordedViewportHeight !== (this.terminal).renderer.dimensions.canvasHeight) {\n // If viewport height changed\n this.refresh();\n } else {\n // If size has changed, refresh viewport\n if (this.terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio !== this.currentRowHeight) {\n this.refresh();\n }\n }\n\n // Sync scrollTop\n const scrollTop = this.terminal.buffer.ydisp * this.currentRowHeight;\n if (this.viewportElement.scrollTop !== scrollTop) {\n this.viewportElement.scrollTop = scrollTop;\n }\n }\n\n /**\n * Handles scroll events on the viewport, calculating the new viewport and requesting the\n * terminal to scroll to it.\n * @param ev The scroll event.\n */\n private onScroll(ev: Event): void {\n const newRow = Math.round(this.viewportElement.scrollTop / this.currentRowHeight);\n const diff = newRow - this.terminal.buffer.ydisp;\n this.terminal.scrollLines(diff, true);\n }\n\n /**\n * Handles mouse wheel events by adjusting the viewport's scrollTop and delegating the actual\n * scrolling to `onScroll`, this event needs to be attached manually by the consumer of\n * `Viewport`.\n * @param ev The mouse wheel event.\n */\n public onWheel(ev: WheelEvent): void {\n if (ev.deltaY === 0) {\n // Do nothing if it's not a vertical scroll event\n return;\n }\n // Fallback to WheelEvent.DOM_DELTA_PIXEL\n let multiplier = 1;\n if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {\n multiplier = this.currentRowHeight;\n } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {\n multiplier = this.currentRowHeight * this.terminal.rows;\n }\n this.viewportElement.scrollTop += ev.deltaY * multiplier;\n // Prevent the page from scrolling when the terminal scrolls\n ev.preventDefault();\n };\n\n /**\n * Handles the touchstart event, recording the touch occurred.\n * @param ev The touch event.\n */\n public onTouchStart(ev: TouchEvent): void {\n this.lastTouchY = ev.touches[0].pageY;\n };\n\n /**\n * Handles the touchmove event, scrolling the viewport if the position shifted.\n * @param ev The touch event.\n */\n public onTouchMove(ev: TouchEvent): void {\n let deltaY = this.lastTouchY - ev.touches[0].pageY;\n this.lastTouchY = ev.touches[0].pageY;\n if (deltaY === 0) {\n return;\n }\n this.viewportElement.scrollTop += deltaY;\n ev.preventDefault();\n };\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nexport type LinkMatcher = {\n id: number,\n regex: RegExp,\n handler: LinkMatcherHandler,\n hoverTooltipCallback?: LinkMatcherHandler,\n hoverLeaveCallback?: () => void,\n matchIndex?: number,\n validationCallback?: LinkMatcherValidationCallback,\n priority?: number\n};\nexport type LinkMatcherHandler = (event: MouseEvent, uri: string) => boolean | void;\nexport type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void;\n\nexport type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;\nexport type Charset = {[key: string]: string};\n\nexport type CharData = [number, string, number, number];\nexport type LineData = CharData[];\n\nexport type LinkHoverEvent = {\n x: number,\n y: number,\n length: number\n};\n\nexport enum LinkHoverEventTypes {\n HOVER = 'linkhover',\n TOOLTIP = 'linktooltip',\n LEAVE = 'linkleave'\n};\n","/**\n * Copyright (c) 2014 The xterm.js authors. All rights reserved.\n * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)\n * @license MIT\n *\n * Originally forked from (with the author's permission):\n * Fabrice Bellard's javascript vt100 for jslinux:\n * http://bellard.org/jslinux/\n * Copyright (c) 2011 Fabrice Bellard\n * The original design remains. The terminal itself\n * has been extended to include xterm CSI codes, among\n * other features.\n *\n * Terminal Emulation References:\n * http://vt100.net/\n * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt\n * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html\n * http://invisible-island.net/vttest/\n * http://www.inwap.com/pdp10/ansicode.txt\n * http://linux.die.net/man/4/console_codes\n * http://linux.die.net/man/7/urxvt\n */\n\nimport { BufferSet } from './BufferSet';\nimport { Buffer, MAX_BUFFER_SIZE } from './Buffer';\nimport { CompositionHelper } from './CompositionHelper';\nimport { EventEmitter } from './EventEmitter';\nimport { Viewport } from './Viewport';\nimport { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './handlers/Clipboard';\nimport { CircularList } from './utils/CircularList';\nimport { C0 } from './EscapeSequences';\nimport { InputHandler } from './InputHandler';\nimport { Parser } from './Parser';\nimport { Renderer } from './renderer/Renderer';\nimport { Linkifier } from './Linkifier';\nimport { SelectionManager } from './SelectionManager';\nimport { CharMeasure } from './utils/CharMeasure';\nimport * as Browser from './utils/Browser';\nimport { MouseHelper } from './utils/MouseHelper';\nimport { CHARSETS } from './Charsets';\nimport { CustomKeyEventHandler, Charset, LinkMatcherHandler, LinkMatcherValidationCallback, CharData, LineData } from './Types';\nimport { ITerminal, IBrowser, ITerminalOptions, IInputHandlingTerminal, ILinkMatcherOptions, IViewport, ICompositionHelper, ITheme, ILinkifier } from './Interfaces';\nimport { BellSound } from './utils/Sounds';\nimport { DEFAULT_ANSI_COLORS } from './renderer/ColorManager';\nimport { IMouseZoneManager } from './input/Interfaces';\nimport { MouseZoneManager } from './input/MouseZoneManager';\nimport { initialize as initializeCharAtlas } from './renderer/CharAtlas';\nimport { IRenderer } from './renderer/Interfaces';\n\n// Declares required for loadAddon\ndeclare var exports: any;\ndeclare var module: any;\ndeclare var define: any;\ndeclare var require: any;\n\n// Let it work inside Node.js for automated testing purposes.\nconst document = (typeof window !== 'undefined') ? window.document : null;\n\n/**\n * The amount of write requests to queue before sending an XOFF signal to the\n * pty process. This number must be small in order for ^C and similar sequences\n * to be responsive.\n */\nconst WRITE_BUFFER_PAUSE_THRESHOLD = 5;\n\n/**\n * The number of writes to perform in a single batch before allowing the\n * renderer to catch up with a 0ms setTimeout.\n */\nconst WRITE_BATCH_SIZE = 300;\n\nconst DEFAULT_OPTIONS: ITerminalOptions = {\n convertEol: false,\n termName: 'xterm',\n geometry: [80, 24],\n cursorBlink: false,\n cursorStyle: 'block',\n bellSound: BellSound,\n bellStyle: 'none',\n enableBold: true,\n fontFamily: 'courier-new, courier, monospace',\n fontSize: 15,\n lineHeight: 1.0,\n letterSpacing: 0,\n scrollback: 1000,\n screenKeys: false,\n debug: false,\n cancelEvents: false,\n disableStdin: false,\n useFlowControl: false,\n tabStopWidth: 8,\n theme: null\n // programFeatures: false,\n // focusKeys: false,\n};\n\nexport class Terminal extends EventEmitter implements ITerminal, IInputHandlingTerminal {\n public textarea: HTMLTextAreaElement;\n public element: HTMLElement;\n\n /**\n * The HTMLElement that the terminal is created in, set by Terminal.open.\n */\n private parent: HTMLElement;\n private context: Window;\n private document: Document;\n private body: HTMLBodyElement;\n private viewportScrollArea: HTMLElement;\n private viewportElement: HTMLElement;\n private helperContainer: HTMLElement;\n private compositionView: HTMLElement;\n private charSizeStyleElement: HTMLStyleElement;\n private bellAudioElement: HTMLAudioElement;\n private visualBellTimer: number;\n\n public browser: IBrowser = Browser;\n\n public options: ITerminalOptions;\n private colors: any;\n\n // TODO: This can be changed to an enum or boolean, 0 and 1 seem to be the only options\n public cursorState: number;\n public cursorHidden: boolean;\n public convertEol: boolean;\n\n private sendDataQueue: string;\n private customKeyEventHandler: CustomKeyEventHandler;\n\n // modes\n public applicationKeypad: boolean;\n public applicationCursor: boolean;\n public originMode: boolean;\n public insertMode: boolean;\n public wraparoundMode: boolean; // defaults: xterm - true, vt100 - false\n public bracketedPasteMode: boolean;\n\n // charset\n // The current charset\n public charset: Charset;\n public gcharset: number;\n public glevel: number;\n public charsets: Charset[];\n\n // mouse properties\n private decLocator: boolean; // This is unstable and never set\n public x10Mouse: boolean;\n public vt200Mouse: boolean;\n private vt300Mouse: boolean; // This is unstable and never set\n public normalMouse: boolean;\n public mouseEvents: boolean;\n public sendFocus: boolean;\n public utfMouse: boolean;\n public sgrMouse: boolean;\n public urxvtMouse: boolean;\n\n // misc\n private refreshStart: number;\n private refreshEnd: number;\n public savedCols: number;\n\n // stream\n private readable: boolean;\n private writable: boolean;\n\n public defAttr: number;\n public curAttr: number;\n\n public params: (string | number)[];\n public currentParam: string | number;\n public prefix: string;\n public postfix: string;\n\n // user input states\n public writeBuffer: string[];\n private writeInProgress: boolean;\n\n /**\n * Whether _xterm.js_ sent XOFF in order to catch up with the pty process.\n * This is a distinct state from writeStopped so that if the user requested\n * XOFF via ^S that it will not automatically resume when the writeBuffer goes\n * below threshold.\n */\n private xoffSentToCatchUp: boolean;\n\n /** Whether writing has been stopped as a result of XOFF */\n private writeStopped: boolean;\n\n // leftover surrogate high from previous write invocation\n private surrogate_high: string;\n\n // Store if user went browsing history in scrollback\n private userScrolling: boolean;\n\n private inputHandler: InputHandler;\n private parser: Parser;\n public renderer: IRenderer;\n public selectionManager: SelectionManager;\n public linkifier: ILinkifier;\n public buffers: BufferSet;\n public buffer: Buffer;\n public viewport: IViewport;\n private compositionHelper: ICompositionHelper;\n public charMeasure: CharMeasure;\n private _mouseZoneManager: IMouseZoneManager;\n public mouseHelper: MouseHelper;\n\n public cols: number;\n public rows: number;\n public geometry: [/*cols*/number, /*rows*/number];\n\n /**\n * Creates a new `Terminal` object.\n *\n * @param {object} options An object containing a set of options, the available options are:\n * - `cursorBlink` (boolean): Whether the terminal cursor blinks\n * - `cols` (number): The number of columns of the terminal (horizontal size)\n * - `rows` (number): The number of rows of the terminal (vertical size)\n *\n * @public\n * @class Xterm Xterm\n * @alias module:xterm/src/xterm\n */\n constructor(\n options: ITerminalOptions = {}\n ) {\n super();\n this.options = options;\n this.setup();\n }\n\n private setup(): void {\n Object.keys(DEFAULT_OPTIONS).forEach((key) => {\n if (this.options[key] == null) {\n this.options[key] = DEFAULT_OPTIONS[key];\n }\n // TODO: We should move away from duplicate options on the Terminal object\n this[key] = this.options[key];\n });\n\n // this.context = options.context || window;\n // this.document = options.document || document;\n // TODO: WHy not document.body?\n this.parent = document ? document.body : null;\n\n this.cols = this.options.cols || this.options.geometry[0];\n this.rows = this.options.rows || this.options.geometry[1];\n this.geometry = [this.cols, this.rows];\n\n if (this.options.handler) {\n this.on('data', this.options.handler);\n }\n\n this.cursorState = 0;\n this.cursorHidden = false;\n this.sendDataQueue = '';\n this.customKeyEventHandler = null;\n\n // modes\n this.applicationKeypad = false;\n this.applicationCursor = false;\n this.originMode = false;\n this.insertMode = false;\n this.wraparoundMode = true; // defaults: xterm - true, vt100 - false\n this.bracketedPasteMode = false;\n\n // charset\n this.charset = null;\n this.gcharset = null;\n this.glevel = 0;\n // TODO: Can this be just []?\n this.charsets = [null];\n\n this.readable = true;\n this.writable = true;\n\n this.defAttr = (0 << 18) | (257 << 9) | (256 << 0);\n this.curAttr = (0 << 18) | (257 << 9) | (256 << 0);\n\n this.params = [];\n this.currentParam = 0;\n this.prefix = '';\n this.postfix = '';\n\n // user input states\n this.writeBuffer = [];\n this.writeInProgress = false;\n\n this.xoffSentToCatchUp = false;\n this.writeStopped = false;\n this.surrogate_high = '';\n this.userScrolling = false;\n\n this.inputHandler = new InputHandler(this);\n this.parser = new Parser(this.inputHandler, this);\n // Reuse renderer if the Terminal is being recreated via a reset call.\n this.renderer = this.renderer || null;\n this.selectionManager = this.selectionManager || null;\n this.linkifier = this.linkifier || new Linkifier(this);\n this._mouseZoneManager = this._mouseZoneManager || null;\n\n // Create the terminal's buffers and set the current buffer\n this.buffers = new BufferSet(this);\n this.buffer = this.buffers.active; // Convenience shortcut;\n this.buffers.on('activate', (buffer: Buffer) => {\n this.buffer = buffer;\n });\n\n // Ensure the selection manager has the correct buffer\n if (this.selectionManager) {\n this.selectionManager.setBuffer(this.buffer);\n }\n }\n\n /**\n * back_color_erase feature for xterm.\n */\n public eraseAttr(): number {\n // if (this.is('screen')) return this.defAttr;\n return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff);\n }\n\n /**\n * Focus the terminal. Delegates focus handling to the terminal's DOM element.\n */\n public focus(): void {\n this.textarea.focus();\n }\n\n public get isFocused(): boolean {\n return document.activeElement === this.textarea;\n }\n\n /**\n * Retrieves an option's value from the terminal.\n * @param {string} key The option key.\n */\n public getOption(key: string): any {\n if (!(key in DEFAULT_OPTIONS)) {\n throw new Error('No option with key \"' + key + '\"');\n }\n\n if (typeof this.options[key] !== 'undefined') {\n return this.options[key];\n }\n\n return this[key];\n }\n\n /**\n * Sets an option on the terminal.\n * @param {string} key The option key.\n * @param {any} value The option value.\n */\n public setOption(key: string, value: any): void {\n if (!(key in DEFAULT_OPTIONS)) {\n throw new Error('No option with key \"' + key + '\"');\n }\n switch (key) {\n case 'bellStyle':\n if (!value) {\n value = 'none';\n }\n break;\n case 'cursorStyle':\n if (!value) {\n value = 'block';\n }\n break;\n case 'lineHeight':\n if (value < 1) {\n console.warn(`${key} cannot be less than 1, value: ${value}`);\n return;\n }\n case 'tabStopWidth':\n if (value < 1) {\n console.warn(`${key} cannot be less than 1, value: ${value}`);\n return;\n }\n break;\n case 'theme':\n // If open has been called we do not want to set options.theme as the\n // source of truth is owned by the renderer.\n if (this.renderer) {\n this._setTheme(value);\n return;\n }\n break;\n case 'scrollback':\n value = Math.min(value, MAX_BUFFER_SIZE);\n\n if (value < 0) {\n console.warn(`${key} cannot be less than 0, value: ${value}`);\n return;\n }\n if (this.options[key] !== value) {\n const newBufferLength = this.rows + value;\n if (this.buffer.lines.length > newBufferLength) {\n const amountToTrim = this.buffer.lines.length - newBufferLength;\n const needsRefresh = (this.buffer.ydisp - amountToTrim < 0);\n this.buffer.lines.trimStart(amountToTrim);\n this.buffer.ybase = Math.max(this.buffer.ybase - amountToTrim, 0);\n this.buffer.ydisp = Math.max(this.buffer.ydisp - amountToTrim, 0);\n if (needsRefresh) {\n this.refresh(0, this.rows - 1);\n }\n }\n }\n break;\n }\n this[key] = value;\n this.options[key] = value;\n switch (key) {\n case 'fontFamily':\n case 'fontSize':\n // When the font changes the size of the cells may change which requires a renderer clear\n this.renderer.clear();\n this.charMeasure.measure(this.options);\n break;\n case 'enableBold':\n case 'letterSpacing':\n case 'lineHeight':\n // When the font changes the size of the cells may change which requires a renderer clear\n this.renderer.clear();\n this.renderer.onResize(this.cols, this.rows, false);\n this.refresh(0, this.rows - 1);\n // this.charMeasure.measure(this.options);\n case 'scrollback':\n this.buffers.resize(this.cols, this.rows);\n this.viewport.syncScrollArea();\n break;\n case 'tabStopWidth': this.buffers.setupTabStops(); break;\n case 'bellSound':\n case 'bellStyle': this.syncBellSound(); break;\n }\n // Inform renderer of changes\n if (this.renderer) {\n this.renderer.onOptionsChanged();\n }\n }\n\n /**\n * Binds the desired focus behavior on a given terminal object.\n */\n private _onTextAreaFocus(): void {\n if (this.sendFocus) {\n this.send(C0.ESC + '[I');\n }\n this.element.classList.add('focus');\n this.showCursor();\n this.emit('focus');\n };\n\n /**\n * Blur the terminal, calling the blur function on the terminal's underlying\n * textarea.\n */\n public blur(): void {\n return this.textarea.blur();\n }\n\n /**\n * Binds the desired blur behavior on a given terminal object.\n */\n private _onTextAreaBlur(): void {\n this.refresh(this.buffer.y, this.buffer.y);\n if (this.sendFocus) {\n this.send(C0.ESC + '[O');\n }\n this.element.classList.remove('focus');\n this.emit('blur');\n }\n\n /**\n * Initialize default behavior\n */\n private initGlobal(): void {\n this.bindKeys();\n\n // Bind clipboard functionality\n on(this.element, 'copy', (event: ClipboardEvent) => {\n // If mouse events are active it means the selection manager is disabled and\n // copy should be handled by the host program.\n if (!this.hasSelection()) {\n return;\n }\n copyHandler(event, this, this.selectionManager);\n });\n const pasteHandlerWrapper = event => pasteHandler(event, this);\n on(this.textarea, 'paste', pasteHandlerWrapper);\n on(this.element, 'paste', pasteHandlerWrapper);\n\n // Handle right click context menus\n if (Browser.isFirefox) {\n // Firefox doesn't appear to fire the contextmenu event on right click\n on(this.element, 'mousedown', (event: MouseEvent) => {\n if (event.button === 2) {\n rightClickHandler(event, this.textarea, this.selectionManager);\n }\n });\n } else {\n on(this.element, 'contextmenu', (event: MouseEvent) => {\n rightClickHandler(event, this.textarea, this.selectionManager);\n });\n }\n\n // Move the textarea under the cursor when middle clicking on Linux to ensure\n // middle click to paste selection works. This only appears to work in Chrome\n // at the time is writing.\n if (Browser.isLinux) {\n // Use auxclick event over mousedown the latter doesn't seem to work. Note\n // that the regular click event doesn't fire for the middle mouse button.\n on(this.element, 'auxclick', (event: MouseEvent) => {\n if (event.button === 1) {\n moveTextAreaUnderMouseCursor(event, this.textarea);\n }\n });\n }\n }\n\n /**\n * Apply key handling to the terminal\n */\n private bindKeys(): void {\n const self = this;\n on(this.element, 'keydown', function (ev: KeyboardEvent): void {\n if (document.activeElement !== this) {\n return;\n }\n self._keyDown(ev);\n }, true);\n\n on(this.element, 'keypress', function (ev: KeyboardEvent): void {\n if (document.activeElement !== this) {\n return;\n }\n self._keyPress(ev);\n }, true);\n\n on(this.element, 'keyup', (ev: KeyboardEvent) => {\n if (!wasMondifierKeyOnlyEvent(ev)) {\n this.focus();\n }\n }, true);\n\n on(this.textarea, 'keydown', (ev: KeyboardEvent) => {\n this._keyDown(ev);\n }, true);\n\n on(this.textarea, 'keypress', (ev: KeyboardEvent) => {\n this._keyPress(ev);\n // Truncate the textarea's value, since it is not needed\n this.textarea.value = '';\n }, true);\n\n on(this.textarea, 'compositionstart', () => this.compositionHelper.compositionstart());\n on(this.textarea, 'compositionupdate', (e: CompositionEvent) => this.compositionHelper.compositionupdate(e));\n on(this.textarea, 'compositionend', () => this.compositionHelper.compositionend());\n this.on('refresh', () => this.compositionHelper.updateCompositionElements());\n this.on('refresh', (data) => this.queueLinkification(data.start, data.end));\n }\n\n /**\n * Opens the terminal within an element.\n *\n * @param {HTMLElement} parent The element to create the terminal within.\n */\n public open(parent: HTMLElement): void {\n let i = 0;\n let div;\n\n this.parent = parent || this.parent;\n\n if (!this.parent) {\n throw new Error('Terminal requires a parent element.');\n }\n\n // Grab global elements\n this.context = this.parent.ownerDocument.defaultView;\n this.document = this.parent.ownerDocument;\n this.body = this.document.body;\n\n initializeCharAtlas(this.document);\n\n // Create main element container\n this.element = this.document.createElement('div');\n this.element.classList.add('terminal');\n this.element.classList.add('xterm');\n\n this.element.setAttribute('tabindex', '0');\n\n this.viewportElement = document.createElement('div');\n this.viewportElement.classList.add('xterm-viewport');\n this.element.appendChild(this.viewportElement);\n this.viewportScrollArea = document.createElement('div');\n this.viewportScrollArea.classList.add('xterm-scroll-area');\n this.viewportElement.appendChild(this.viewportScrollArea);\n\n // preload audio\n this.syncBellSound();\n\n this._mouseZoneManager = new MouseZoneManager(this);\n this.on('scroll', () => this._mouseZoneManager.clearAll());\n this.linkifier.attachToDom(this._mouseZoneManager);\n\n // Create the container that will hold helpers like the textarea for\n // capturing DOM Events. Then produce the helpers.\n this.helperContainer = document.createElement('div');\n this.helperContainer.classList.add('xterm-helpers');\n // TODO: This should probably be inserted once it's filled to prevent an additional layout\n this.element.appendChild(this.helperContainer);\n this.textarea = document.createElement('textarea');\n this.textarea.classList.add('xterm-helper-textarea');\n this.textarea.setAttribute('autocorrect', 'off');\n this.textarea.setAttribute('autocapitalize', 'off');\n this.textarea.setAttribute('spellcheck', 'false');\n this.textarea.tabIndex = 0;\n this.textarea.addEventListener('focus', () => this._onTextAreaFocus());\n this.textarea.addEventListener('blur', () => this._onTextAreaBlur());\n this.helperContainer.appendChild(this.textarea);\n\n this.compositionView = document.createElement('div');\n this.compositionView.classList.add('composition-view');\n this.compositionHelper = new CompositionHelper(this.textarea, this.compositionView, this);\n this.helperContainer.appendChild(this.compositionView);\n\n this.charSizeStyleElement = document.createElement('style');\n this.helperContainer.appendChild(this.charSizeStyleElement);\n\n this.parent.appendChild(this.element);\n\n this.charMeasure = new CharMeasure(document, this.helperContainer);\n\n this.renderer = new Renderer(this, this.options.theme);\n this.options.theme = null;\n this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure);\n this.viewport.onThemeChanged(this.renderer.colorManager.colors);\n\n this.on('cursormove', () => this.renderer.onCursorMove());\n this.on('resize', () => this.renderer.onResize(this.cols, this.rows, false));\n this.on('blur', () => this.renderer.onBlur());\n this.on('focus', () => this.renderer.onFocus());\n window.addEventListener('resize', () => this.renderer.onWindowResize(window.devicePixelRatio));\n this.charMeasure.on('charsizechanged', () => this.renderer.onResize(this.cols, this.rows, true));\n this.renderer.on('resize', (dimensions) => this.viewport.syncScrollArea());\n\n this.selectionManager = new SelectionManager(this, this.buffer, this.charMeasure);\n this.element.addEventListener('mousedown', (e: MouseEvent) => this.selectionManager.onMouseDown(e));\n this.selectionManager.on('refresh', data => this.renderer.onSelectionChanged(data.start, data.end));\n this.selectionManager.on('newselection', text => {\n // If there's a new selection, put it into the textarea, focus and select it\n // in order to register it as a selection on the OS. This event is fired\n // only on Linux to enable middle click to paste selection.\n this.textarea.value = text;\n this.textarea.focus();\n this.textarea.select();\n });\n this.on('scroll', () => {\n this.viewport.syncScrollArea();\n this.selectionManager.refresh();\n });\n this.viewportElement.addEventListener('scroll', () => this.selectionManager.refresh());\n\n this.mouseHelper = new MouseHelper(this.renderer);\n\n // Measure the character size\n this.charMeasure.measure(this.options);\n\n // Setup loop that draws to screen\n this.refresh(0, this.rows - 1);\n\n // Initialize global actions that need to be taken on the document.\n this.initGlobal();\n\n // Listen for mouse events and translate\n // them into terminal mouse protocols.\n this.bindMouse();\n }\n\n /**\n * Sets the theme on the renderer. The renderer must have been initialized.\n * @param theme The theme to ste.\n */\n private _setTheme(theme: ITheme): void {\n const colors = this.renderer.setTheme(theme);\n if (this.viewport) {\n this.viewport.onThemeChanged(colors);\n }\n }\n\n /**\n * Attempts to load an add-on using CommonJS or RequireJS (whichever is available).\n * @param {string} addon The name of the addon to load\n * @static\n */\n public static loadAddon(addon: string, callback?: Function): boolean | any {\n // TODO: Improve return type and documentation\n if (typeof exports === 'object' && typeof module === 'object') {\n // CommonJS\n return require('./addons/' + addon + '/' + addon);\n } else if (typeof define === 'function') {\n // RequireJS\n return (require)(['./addons/' + addon + '/' + addon], callback);\n } else {\n console.error('Cannot load a module without a CommonJS or RequireJS environment.');\n return false;\n }\n }\n\n /**\n * XTerm mouse events\n * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking\n * To better understand these\n * the xterm code is very helpful:\n * Relevant files:\n * button.c, charproc.c, misc.c\n * Relevant functions in xterm/button.c:\n * BtnCode, EmitButtonCode, EditorButton, SendMousePosition\n */\n public bindMouse(): void {\n const el = this.element;\n const self = this;\n let pressed = 32;\n\n // mouseup, mousedown, wheel\n // left click: ^[[M 3<^[[M#3<\n // wheel up: ^[[M`3>\n function sendButton(ev: MouseEvent | WheelEvent): void {\n let button;\n let pos;\n\n // get the xterm-style button\n button = getButton(ev);\n\n // get mouse coordinates\n pos = self.mouseHelper.getRawByteCoords(ev, self.element, self.charMeasure, self.options.lineHeight, self.cols, self.rows);\n if (!pos) return;\n\n sendEvent(button, pos);\n\n switch ((ev).overrideType || ev.type) {\n case 'mousedown':\n pressed = button;\n break;\n case 'mouseup':\n // keep it at the left\n // button, just in case.\n pressed = 32;\n break;\n case 'wheel':\n // nothing. don't\n // interfere with\n // `pressed`.\n break;\n }\n }\n\n // motion example of a left click:\n // ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7<\n function sendMove(ev: MouseEvent): void {\n let button = pressed;\n let pos = self.mouseHelper.getRawByteCoords(ev, self.element, self.charMeasure, self.options.lineHeight, self.cols, self.rows);\n if (!pos) return;\n\n // buttons marked as motions\n // are incremented by 32\n button += 32;\n\n sendEvent(button, pos);\n }\n\n // encode button and\n // position to characters\n function encode(data: number[], ch: number): void {\n if (!self.utfMouse) {\n if (ch === 255) {\n data.push(0);\n return;\n }\n if (ch > 127) ch = 127;\n data.push(ch);\n } else {\n if (ch === 2047) {\n data.push(0);\n return;\n }\n if (ch < 127) {\n data.push(ch);\n } else {\n if (ch > 2047) ch = 2047;\n data.push(0xC0 | (ch >> 6));\n data.push(0x80 | (ch & 0x3F));\n }\n }\n }\n\n // send a mouse event:\n // regular/utf8: ^[[M Cb Cx Cy\n // urxvt: ^[[ Cb ; Cx ; Cy M\n // sgr: ^[[ Cb ; Cx ; Cy M/m\n // vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \\r\n // locator: CSI P e ; P b ; P r ; P c ; P p & w\n function sendEvent(button: number, pos: {x: number, y: number}): void {\n // self.emit('mouse', {\n // x: pos.x - 32,\n // y: pos.x - 32,\n // button: button\n // });\n\n if (self.vt300Mouse) {\n // NOTE: Unstable.\n // http://www.vt100.net/docs/vt3xx-gp/chapter15.html\n button &= 3;\n pos.x -= 32;\n pos.y -= 32;\n let data = C0.ESC + '[24';\n if (button === 0) data += '1';\n else if (button === 1) data += '3';\n else if (button === 2) data += '5';\n else if (button === 3) return;\n else data += '0';\n data += '~[' + pos.x + ',' + pos.y + ']\\r';\n self.send(data);\n return;\n }\n\n if (self.decLocator) {\n // NOTE: Unstable.\n button &= 3;\n pos.x -= 32;\n pos.y -= 32;\n if (button === 0) button = 2;\n else if (button === 1) button = 4;\n else if (button === 2) button = 6;\n else if (button === 3) button = 3;\n self.send(C0.ESC + '['\n + button\n + ';'\n + (button === 3 ? 4 : 0)\n + ';'\n + pos.y\n + ';'\n + pos.x\n + ';'\n // Not sure what page is meant to be\n + (pos).page || 0\n + '&w');\n return;\n }\n\n if (self.urxvtMouse) {\n pos.x -= 32;\n pos.y -= 32;\n pos.x++;\n pos.y++;\n self.send(C0.ESC + '[' + button + ';' + pos.x + ';' + pos.y + 'M');\n return;\n }\n\n if (self.sgrMouse) {\n pos.x -= 32;\n pos.y -= 32;\n self.send(C0.ESC + '[<'\n + (((button & 3) === 3 ? button & ~3 : button) - 32)\n + ';'\n + pos.x\n + ';'\n + pos.y\n + ((button & 3) === 3 ? 'm' : 'M'));\n return;\n }\n\n let data: number[] = [];\n\n encode(data, button);\n encode(data, pos.x);\n encode(data, pos.y);\n\n self.send(C0.ESC + '[M' + String.fromCharCode.apply(String, data));\n }\n\n function getButton(ev: MouseEvent): number {\n let button;\n let shift;\n let meta;\n let ctrl;\n let mod;\n\n // two low bits:\n // 0 = left\n // 1 = middle\n // 2 = right\n // 3 = release\n // wheel up/down:\n // 1, and 2 - with 64 added\n switch ((ev).overrideType || ev.type) {\n case 'mousedown':\n button = ev.button != null\n ? +ev.button\n : ev.which != null\n ? ev.which - 1\n : null;\n\n if (Browser.isMSIE) {\n button = button === 1 ? 0 : button === 4 ? 1 : button;\n }\n break;\n case 'mouseup':\n button = 3;\n break;\n case 'DOMMouseScroll':\n button = ev.detail < 0\n ? 64\n : 65;\n break;\n case 'wheel':\n button = (ev).wheelDeltaY > 0\n ? 64\n : 65;\n break;\n }\n\n // next three bits are the modifiers:\n // 4 = shift, 8 = meta, 16 = control\n shift = ev.shiftKey ? 4 : 0;\n meta = ev.metaKey ? 8 : 0;\n ctrl = ev.ctrlKey ? 16 : 0;\n mod = shift | meta | ctrl;\n\n // no mods\n if (self.vt200Mouse) {\n // ctrl only\n mod &= ctrl;\n } else if (!self.normalMouse) {\n mod = 0;\n }\n\n // increment to SP\n button = (32 + (mod << 2)) + button;\n\n return button;\n }\n\n on(el, 'mousedown', (ev: MouseEvent) => {\n\n // Prevent the focus on the textarea from getting lost\n // and make sure we get focused on mousedown\n ev.preventDefault();\n this.focus();\n\n // Don't send the mouse button to the pty if mouse events are disabled or\n // if the selection manager is having selection forced (ie. a modifier is\n // held).\n if (!this.mouseEvents || this.selectionManager.shouldForceSelection(ev)) {\n return;\n }\n\n // send the button\n sendButton(ev);\n\n // fix for odd bug\n // if (this.vt200Mouse && !this.normalMouse) {\n if (this.vt200Mouse) {\n (ev).overrideType = 'mouseup';\n sendButton(ev);\n return this.cancel(ev);\n }\n\n // bind events\n if (this.normalMouse) on(this.document, 'mousemove', sendMove);\n\n // x10 compatibility mode can't send button releases\n if (!this.x10Mouse) {\n const handler = (ev: MouseEvent) => {\n sendButton(ev);\n // TODO: Seems dangerous calling this on document?\n if (this.normalMouse) off(this.document, 'mousemove', sendMove);\n off(this.document, 'mouseup', handler);\n return this.cancel(ev);\n };\n // TODO: Seems dangerous calling this on document?\n on(this.document, 'mouseup', handler);\n }\n\n return this.cancel(ev);\n });\n\n // if (this.normalMouse) {\n // on(this.document, 'mousemove', sendMove);\n // }\n\n on(el, 'wheel', (ev: WheelEvent) => {\n if (!this.mouseEvents) return;\n if (this.x10Mouse || this.vt300Mouse || this.decLocator) return;\n sendButton(ev);\n ev.preventDefault();\n });\n\n // allow wheel scrolling in\n // the shell for example\n on(el, 'wheel', (ev: WheelEvent) => {\n if (this.mouseEvents) return;\n this.viewport.onWheel(ev);\n return this.cancel(ev);\n });\n\n on(el, 'touchstart', (ev: TouchEvent) => {\n if (this.mouseEvents) return;\n this.viewport.onTouchStart(ev);\n return this.cancel(ev);\n });\n\n on(el, 'touchmove', (ev: TouchEvent) => {\n if (this.mouseEvents) return;\n this.viewport.onTouchMove(ev);\n return this.cancel(ev);\n });\n }\n\n /**\n * Destroys the terminal.\n */\n public destroy(): void {\n super.destroy();\n this.readable = false;\n this.writable = false;\n this.handler = () => {};\n this.write = () => {};\n if (this.element && this.element.parentNode) {\n this.element.parentNode.removeChild(this.element);\n }\n // this.emit('close');\n }\n\n /**\n * Tells the renderer to refresh terminal content between two rows (inclusive) at the next\n * opportunity.\n * @param {number} start The row to start from (between 0 and this.rows - 1).\n * @param {number} end The row to end at (between start and this.rows - 1).\n */\n public refresh(start: number, end: number): void {\n if (this.renderer) {\n this.renderer.queueRefresh(start, end);\n }\n }\n\n /**\n * Queues linkification for the specified rows.\n * @param {number} start The row to start from (between 0 and this.rows - 1).\n * @param {number} end The row to end at (between start and this.rows - 1).\n */\n private queueLinkification(start: number, end: number): void {\n if (this.linkifier) {\n this.linkifier.linkifyRows(start, end);\n }\n }\n\n /**\n * Display the cursor element\n */\n public showCursor(): void {\n if (!this.cursorState) {\n this.cursorState = 1;\n this.refresh(this.buffer.y, this.buffer.y);\n }\n }\n\n /**\n * Scroll the terminal down 1 row, creating a blank line.\n * @param isWrapped Whether the new line is wrapped from the previous line.\n */\n public scroll(isWrapped?: boolean): void {\n const newLine = this.blankLine(undefined, isWrapped);\n const topRow = this.buffer.ybase + this.buffer.scrollTop;\n let bottomRow = this.buffer.ybase + this.buffer.scrollBottom;\n\n if (this.buffer.scrollTop === 0) {\n // Determine whether the buffer is going to be trimmed after insertion.\n const willBufferBeTrimmed = this.buffer.lines.length === this.buffer.lines.maxLength;\n\n // Insert the line using the fastest method\n if (bottomRow === this.buffer.lines.length - 1) {\n this.buffer.lines.push(newLine);\n } else {\n this.buffer.lines.splice(bottomRow + 1, 0, newLine);\n }\n\n // Only adjust ybase and ydisp when the buffer is not trimmed\n if (!willBufferBeTrimmed) {\n this.buffer.ybase++;\n // Only scroll the ydisp with ybase if the user has not scrolled up\n if (!this.userScrolling) {\n this.buffer.ydisp++;\n }\n } else {\n // When the buffer is full and the user has scrolled up, keep the text\n // stable unless ydisp is right at the top\n if (this.userScrolling) {\n this.buffer.ydisp = Math.max(this.buffer.ydisp - 1, 0);\n }\n }\n } else {\n // scrollTop is non-zero which means no line will be going to the\n // scrollback, instead we can just shift them in-place.\n const scrollRegionHeight = bottomRow - topRow + 1/*as it's zero-based*/;\n this.buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1);\n this.buffer.lines.set(bottomRow, newLine);\n }\n\n // Move the viewport to the bottom of the buffer unless the user is\n // scrolling.\n if (!this.userScrolling) {\n this.buffer.ydisp = this.buffer.ybase;\n }\n\n // Flag rows that need updating\n this.updateRange(this.buffer.scrollTop);\n this.updateRange(this.buffer.scrollBottom);\n\n /**\n * This event is emitted whenever the terminal is scrolled.\n * The one parameter passed is the new y display position.\n *\n * @event scroll\n */\n this.emit('scroll', this.buffer.ydisp);\n }\n\n /**\n * Scroll the display of the terminal\n * @param {number} disp The number of lines to scroll down (negative scroll up).\n * @param {boolean} suppressScrollEvent Don't emit the scroll event as scrollLines. This is used\n * to avoid unwanted events being handled by the viewport when the event was triggered from the\n * viewport originally.\n */\n public scrollLines(disp: number, suppressScrollEvent?: boolean): void {\n if (disp < 0) {\n if (this.buffer.ydisp === 0) {\n return;\n }\n this.userScrolling = true;\n } else if (disp + this.buffer.ydisp >= this.buffer.ybase) {\n this.userScrolling = false;\n }\n\n const oldYdisp = this.buffer.ydisp;\n this.buffer.ydisp = Math.max(Math.min(this.buffer.ydisp + disp, this.buffer.ybase), 0);\n\n // No change occurred, don't trigger scroll/refresh\n if (oldYdisp === this.buffer.ydisp) {\n return;\n }\n\n if (!suppressScrollEvent) {\n this.emit('scroll', this.buffer.ydisp);\n }\n\n this.refresh(0, this.rows - 1);\n }\n\n /**\n * Scroll the display of the terminal by a number of pages.\n * @param {number} pageCount The number of pages to scroll (negative scrolls up).\n */\n public scrollPages(pageCount: number): void {\n this.scrollLines(pageCount * (this.rows - 1));\n }\n\n /**\n * Scrolls the display of the terminal to the top.\n */\n public scrollToTop(): void {\n this.scrollLines(-this.buffer.ydisp);\n }\n\n /**\n * Scrolls the display of the terminal to the bottom.\n */\n public scrollToBottom(): void {\n this.scrollLines(this.buffer.ybase - this.buffer.ydisp);\n }\n\n /**\n * Writes text to the terminal.\n * @param {string} data The text to write to the terminal.\n */\n public write(data: string): void {\n this.writeBuffer.push(data);\n\n // Send XOFF to pause the pty process if the write buffer becomes too large so\n // xterm.js can catch up before more data is sent. This is necessary in order\n // to keep signals such as ^C responsive.\n if (this.options.useFlowControl && !this.xoffSentToCatchUp && this.writeBuffer.length >= WRITE_BUFFER_PAUSE_THRESHOLD) {\n // XOFF - stop pty pipe\n // XON will be triggered by emulator before processing data chunk\n this.send(C0.DC3);\n this.xoffSentToCatchUp = true;\n }\n\n if (!this.writeInProgress && this.writeBuffer.length > 0) {\n // Kick off a write which will write all data in sequence recursively\n this.writeInProgress = true;\n // Kick off an async innerWrite so more writes can come in while processing data\n setTimeout(() => {\n this.innerWrite();\n });\n }\n }\n\n private innerWrite(): void {\n const writeBatch = this.writeBuffer.splice(0, WRITE_BATCH_SIZE);\n while (writeBatch.length > 0) {\n const data = writeBatch.shift();\n\n // If XOFF was sent in order to catch up with the pty process, resume it if\n // the writeBuffer is empty to allow more data to come in.\n if (this.xoffSentToCatchUp && writeBatch.length === 0 && this.writeBuffer.length === 0) {\n this.send(C0.DC1);\n this.xoffSentToCatchUp = false;\n }\n\n this.refreshStart = this.buffer.y;\n this.refreshEnd = this.buffer.y;\n\n // HACK: Set the parser state based on it's state at the time of return.\n // This works around the bug #662 which saw the parser state reset in the\n // middle of parsing escape sequence in two chunks. For some reason the\n // state of the parser resets to 0 after exiting parser.parse. This change\n // just sets the state back based on the correct return statement.\n const state = this.parser.parse(data);\n this.parser.setState(state);\n\n this.updateRange(this.buffer.y);\n this.refresh(this.refreshStart, this.refreshEnd);\n }\n if (this.writeBuffer.length > 0) {\n // Allow renderer to catch up before processing the next batch\n setTimeout(() => this.innerWrite(), 0);\n } else {\n this.writeInProgress = false;\n }\n }\n\n /**\n * Writes text to the terminal, followed by a break line character (\\n).\n * @param {string} data The text to write to the terminal.\n */\n public writeln(data: string): void {\n this.write(data + '\\r\\n');\n }\n\n /**\n * Attaches a custom key event handler which is run before keys are processed,\n * giving consumers of xterm.js ultimate control as to what keys should be\n * processed by the terminal and what keys should not.\n * @param customKeyEventHandler The custom KeyboardEvent handler to attach.\n * This is a function that takes a KeyboardEvent, allowing consumers to stop\n * propogation and/or prevent the default action. The function returns whether\n * the event should be processed by xterm.js.\n */\n public attachCustomKeyEventHandler(customKeyEventHandler: CustomKeyEventHandler): void {\n this.customKeyEventHandler = customKeyEventHandler;\n }\n\n /**\n * Attaches a http(s) link handler, forcing web links to behave differently to\n * regular tags. This will trigger a refresh as links potentially need to be\n * reconstructed. Calling this with null will remove the handler.\n * @param handler The handler callback function.\n */\n public setHypertextLinkHandler(handler: LinkMatcherHandler): void {\n if (!this.linkifier) {\n throw new Error('Cannot attach a hypertext link handler before Terminal.open is called');\n }\n this.linkifier.setHypertextLinkHandler(handler);\n // Refresh to force links to refresh\n this.refresh(0, this.rows - 1);\n }\n\n /**\n * Attaches a validation callback for hypertext links. This is useful to use\n * validation logic or to do something with the link's element and url.\n * @param callback The callback to use, this can\n * be cleared with null.\n */\n public setHypertextValidationCallback(callback: LinkMatcherValidationCallback): void {\n if (!this.linkifier) {\n throw new Error('Cannot attach a hypertext validation callback before Terminal.open is called');\n }\n this.linkifier.setHypertextValidationCallback(callback);\n // // Refresh to force links to refresh\n this.refresh(0, this.rows - 1);\n }\n\n /**\n * Registers a link matcher, allowing custom link patterns to be matched and\n * handled.\n * @param regex The regular expression to search for, specifically\n * this searches the textContent of the rows. You will want to use \\s to match\n * a space ' ' character for example.\n * @param handler The callback when the link is called.\n * @param options Options for the link matcher.\n * @return The ID of the new matcher, this can be used to deregister.\n */\n public registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: ILinkMatcherOptions): number {\n if (this.linkifier) {\n const matcherId = this.linkifier.registerLinkMatcher(regex, handler, options);\n this.refresh(0, this.rows - 1);\n return matcherId;\n }\n return 0;\n }\n\n /**\n * Deregisters a link matcher if it has been registered.\n * @param matcherId The link matcher's ID (returned after register)\n */\n public deregisterLinkMatcher(matcherId: number): void {\n if (this.linkifier) {\n if (this.linkifier.deregisterLinkMatcher(matcherId)) {\n this.refresh(0, this.rows - 1);\n }\n }\n }\n\n /**\n * Gets whether the terminal has an active selection.\n */\n public hasSelection(): boolean {\n return this.selectionManager ? this.selectionManager.hasSelection : false;\n }\n\n /**\n * Gets the terminal's current selection, this is useful for implementing copy\n * behavior outside of xterm.js.\n */\n public getSelection(): string {\n return this.selectionManager ? this.selectionManager.selectionText : '';\n }\n\n /**\n * Clears the current terminal selection.\n */\n public clearSelection(): void {\n if (this.selectionManager) {\n this.selectionManager.clearSelection();\n }\n }\n\n /**\n * Selects all text within the terminal.\n */\n public selectAll(): void {\n if (this.selectionManager) {\n this.selectionManager.selectAll();\n }\n }\n\n /**\n * Handle a keydown event\n * Key Resources:\n * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent\n * @param {KeyboardEvent} ev The keydown event to be handled.\n */\n protected _keyDown(ev: KeyboardEvent): boolean {\n if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) {\n return false;\n }\n\n if (!this.compositionHelper.keydown(ev)) {\n if (this.buffer.ybase !== this.buffer.ydisp) {\n this.scrollToBottom();\n }\n return false;\n }\n\n const result = this._evaluateKeyEscapeSequence(ev);\n\n if (result.key === C0.DC3) { // XOFF\n this.writeStopped = true;\n } else if (result.key === C0.DC1) { // XON\n this.writeStopped = false;\n }\n\n if (result.scrollLines) {\n this.scrollLines(result.scrollLines);\n return this.cancel(ev, true);\n }\n\n if (isThirdLevelShift(this.browser, ev)) {\n return true;\n }\n\n if (result.cancel) {\n // The event is canceled at the end already, is this necessary?\n this.cancel(ev, true);\n }\n\n if (!result.key) {\n return true;\n }\n\n this.emit('keydown', ev);\n this.emit('key', result.key, ev);\n this.showCursor();\n this.handler(result.key);\n\n return this.cancel(ev, true);\n }\n\n /**\n * Returns an object that determines how a KeyboardEvent should be handled. The key of the\n * returned value is the new key code to pass to the PTY.\n *\n * Reference: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html\n * @param ev The keyboard event to be translated to key escape sequence.\n */\n protected _evaluateKeyEscapeSequence(ev: KeyboardEvent): {cancel: boolean, key: string, scrollLines: number} {\n const result: {cancel: boolean, key: string, scrollLines: number} = {\n // Whether to cancel event propogation (NOTE: this may not be needed since the event is\n // canceled at the end of keyDown\n cancel: false,\n // The new key even to emit\n key: undefined,\n // The number of characters to scroll, if this is defined it will cancel the event\n scrollLines: undefined\n };\n const modifiers = (ev.shiftKey ? 1 : 0) | (ev.altKey ? 2 : 0) | (ev.ctrlKey ? 4 : 0) | (ev.metaKey ? 8 : 0);\n switch (ev.keyCode) {\n case 0:\n if (ev.key === 'UIKeyInputUpArrow') {\n if (this.applicationCursor) {\n result.key = C0.ESC + 'OA';\n } else {\n result.key = C0.ESC + '[A';\n }\n }\n else if (ev.key === 'UIKeyInputLeftArrow') {\n if (this.applicationCursor) {\n result.key = C0.ESC + 'OD';\n } else {\n result.key = C0.ESC + '[D';\n }\n }\n else if (ev.key === 'UIKeyInputRightArrow') {\n if (this.applicationCursor) {\n result.key = C0.ESC + 'OC';\n } else {\n result.key = C0.ESC + '[C';\n }\n }\n else if (ev.key === 'UIKeyInputDownArrow') {\n if (this.applicationCursor) {\n result.key = C0.ESC + 'OB';\n } else {\n result.key = C0.ESC + '[B';\n }\n }\n break;\n case 8:\n // backspace\n if (ev.shiftKey) {\n result.key = C0.BS; // ^H\n break;\n }\n result.key = C0.DEL; // ^?\n break;\n case 9:\n // tab\n if (ev.shiftKey) {\n result.key = C0.ESC + '[Z';\n break;\n }\n result.key = C0.HT;\n result.cancel = true;\n break;\n case 13:\n // return/enter\n result.key = C0.CR;\n result.cancel = true;\n break;\n case 27:\n // escape\n result.key = C0.ESC;\n result.cancel = true;\n break;\n case 37:\n // left-arrow\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'D';\n // HACK: Make Alt + left-arrow behave like Ctrl + left-arrow: move one word backwards\n // http://unix.stackexchange.com/a/108106\n // macOS uses different escape sequences than linux\n if (result.key === C0.ESC + '[1;3D') {\n result.key = (this.browser.isMac) ? C0.ESC + 'b' : C0.ESC + '[1;5D';\n }\n } else if (this.applicationCursor) {\n result.key = C0.ESC + 'OD';\n } else {\n result.key = C0.ESC + '[D';\n }\n break;\n case 39:\n // right-arrow\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'C';\n // HACK: Make Alt + right-arrow behave like Ctrl + right-arrow: move one word forward\n // http://unix.stackexchange.com/a/108106\n // macOS uses different escape sequences than linux\n if (result.key === C0.ESC + '[1;3C') {\n result.key = (this.browser.isMac) ? C0.ESC + 'f' : C0.ESC + '[1;5C';\n }\n } else if (this.applicationCursor) {\n result.key = C0.ESC + 'OC';\n } else {\n result.key = C0.ESC + '[C';\n }\n break;\n case 38:\n // up-arrow\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'A';\n // HACK: Make Alt + up-arrow behave like Ctrl + up-arrow\n // http://unix.stackexchange.com/a/108106\n if (result.key === C0.ESC + '[1;3A') {\n result.key = C0.ESC + '[1;5A';\n }\n } else if (this.applicationCursor) {\n result.key = C0.ESC + 'OA';\n } else {\n result.key = C0.ESC + '[A';\n }\n break;\n case 40:\n // down-arrow\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'B';\n // HACK: Make Alt + down-arrow behave like Ctrl + down-arrow\n // http://unix.stackexchange.com/a/108106\n if (result.key === C0.ESC + '[1;3B') {\n result.key = C0.ESC + '[1;5B';\n }\n } else if (this.applicationCursor) {\n result.key = C0.ESC + 'OB';\n } else {\n result.key = C0.ESC + '[B';\n }\n break;\n case 45:\n // insert\n if (!ev.shiftKey && !ev.ctrlKey) {\n // or + are used to\n // copy-paste on some systems.\n result.key = C0.ESC + '[2~';\n }\n break;\n case 46:\n // delete\n if (modifiers) {\n result.key = C0.ESC + '[3;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[3~';\n }\n break;\n case 36:\n // home\n if (modifiers)\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'H';\n else if (this.applicationCursor)\n result.key = C0.ESC + 'OH';\n else\n result.key = C0.ESC + '[H';\n break;\n case 35:\n // end\n if (modifiers)\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'F';\n else if (this.applicationCursor)\n result.key = C0.ESC + 'OF';\n else\n result.key = C0.ESC + '[F';\n break;\n case 33:\n // page up\n if (ev.shiftKey) {\n result.scrollLines = -(this.rows - 1);\n } else {\n result.key = C0.ESC + '[5~';\n }\n break;\n case 34:\n // page down\n if (ev.shiftKey) {\n result.scrollLines = this.rows - 1;\n } else {\n result.key = C0.ESC + '[6~';\n }\n break;\n case 112:\n // F1-F12\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'P';\n } else {\n result.key = C0.ESC + 'OP';\n }\n break;\n case 113:\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'Q';\n } else {\n result.key = C0.ESC + 'OQ';\n }\n break;\n case 114:\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'R';\n } else {\n result.key = C0.ESC + 'OR';\n }\n break;\n case 115:\n if (modifiers) {\n result.key = C0.ESC + '[1;' + (modifiers + 1) + 'S';\n } else {\n result.key = C0.ESC + 'OS';\n }\n break;\n case 116:\n if (modifiers) {\n result.key = C0.ESC + '[15;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[15~';\n }\n break;\n case 117:\n if (modifiers) {\n result.key = C0.ESC + '[17;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[17~';\n }\n break;\n case 118:\n if (modifiers) {\n result.key = C0.ESC + '[18;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[18~';\n }\n break;\n case 119:\n if (modifiers) {\n result.key = C0.ESC + '[19;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[19~';\n }\n break;\n case 120:\n if (modifiers) {\n result.key = C0.ESC + '[20;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[20~';\n }\n break;\n case 121:\n if (modifiers) {\n result.key = C0.ESC + '[21;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[21~';\n }\n break;\n case 122:\n if (modifiers) {\n result.key = C0.ESC + '[23;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[23~';\n }\n break;\n case 123:\n if (modifiers) {\n result.key = C0.ESC + '[24;' + (modifiers + 1) + '~';\n } else {\n result.key = C0.ESC + '[24~';\n }\n break;\n default:\n // a-z and space\n if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {\n if (ev.keyCode >= 65 && ev.keyCode <= 90) {\n result.key = String.fromCharCode(ev.keyCode - 64);\n } else if (ev.keyCode === 32) {\n // NUL\n result.key = String.fromCharCode(0);\n } else if (ev.keyCode >= 51 && ev.keyCode <= 55) {\n // escape, file sep, group sep, record sep, unit sep\n result.key = String.fromCharCode(ev.keyCode - 51 + 27);\n } else if (ev.keyCode === 56) {\n // delete\n result.key = String.fromCharCode(127);\n } else if (ev.keyCode === 219) {\n // ^[ - Control Sequence Introducer (CSI)\n result.key = String.fromCharCode(27);\n } else if (ev.keyCode === 220) {\n // ^\\ - String Terminator (ST)\n result.key = String.fromCharCode(28);\n } else if (ev.keyCode === 221) {\n // ^] - Operating System Command (OSC)\n result.key = String.fromCharCode(29);\n }\n } else if (!this.browser.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) {\n // On Mac this is a third level shift. Use instead.\n if (ev.keyCode >= 65 && ev.keyCode <= 90) {\n result.key = C0.ESC + String.fromCharCode(ev.keyCode + 32);\n } else if (ev.keyCode === 192) {\n result.key = C0.ESC + '`';\n } else if (ev.keyCode >= 48 && ev.keyCode <= 57) {\n result.key = C0.ESC + (ev.keyCode - 48);\n }\n } else if (this.browser.isMac && !ev.altKey && !ev.ctrlKey && ev.metaKey) {\n if (ev.keyCode === 65) { // cmd + a\n this.selectAll();\n }\n }\n break;\n }\n\n return result;\n }\n\n /**\n * Set the G level of the terminal\n * @param g\n */\n public setgLevel(g: number): void {\n this.glevel = g;\n this.charset = this.charsets[g];\n }\n\n /**\n * Set the charset for the given G level of the terminal\n * @param g\n * @param charset\n */\n public setgCharset(g: number, charset: Charset): void {\n this.charsets[g] = charset;\n if (this.glevel === g) {\n this.charset = charset;\n }\n }\n\n /**\n * Handle a keypress event.\n * Key Resources:\n * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent\n * @param {KeyboardEvent} ev The keypress event to be handled.\n */\n protected _keyPress(ev: KeyboardEvent): boolean {\n let key;\n\n if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) {\n return false;\n }\n\n this.cancel(ev);\n\n if (ev.charCode) {\n key = ev.charCode;\n } else if (ev.which == null) {\n key = ev.keyCode;\n } else if (ev.which !== 0 && ev.charCode !== 0) {\n key = ev.which;\n } else {\n return false;\n }\n\n if (!key || (\n (ev.altKey || ev.ctrlKey || ev.metaKey) && !isThirdLevelShift(this.browser, ev)\n )) {\n return false;\n }\n\n key = String.fromCharCode(key);\n\n this.emit('keypress', key, ev);\n this.emit('key', key, ev);\n this.showCursor();\n this.handler(key);\n\n return true;\n }\n\n /**\n * Send data for handling to the terminal\n * @param {string} data\n */\n public send(data: string): void {\n if (!this.sendDataQueue) {\n setTimeout(() => {\n this.handler(this.sendDataQueue);\n this.sendDataQueue = '';\n }, 1);\n }\n\n this.sendDataQueue += data;\n }\n\n /**\n * Ring the bell.\n * Note: We could do sweet things with webaudio here\n */\n public bell(): void {\n this.emit('bell');\n if (this.soundBell()) this.bellAudioElement.play();\n\n if (this.visualBell()) {\n this.element.classList.add('visual-bell-active');\n clearTimeout(this.visualBellTimer);\n this.visualBellTimer = window.setTimeout(() => {\n this.element.classList.remove('visual-bell-active');\n }, 200);\n }\n }\n\n /**\n * Log the current state to the console.\n */\n public log(text: string, data?: any): void {\n if (!this.options.debug) return;\n if (!this.context.console || !this.context.console.log) return;\n this.context.console.log(text, data);\n }\n\n /**\n * Log the current state as error to the console.\n */\n public error(text: string, data?: any): void {\n if (!this.options.debug) return;\n if (!this.context.console || !this.context.console.error) return;\n this.context.console.error(text, data);\n }\n\n /**\n * Resizes the terminal.\n *\n * @param {number} x The number of columns to resize to.\n * @param {number} y The number of rows to resize to.\n */\n public resize(x: number, y: number): void {\n if (isNaN(x) || isNaN(y)) {\n return;\n }\n\n if (x === this.cols && y === this.rows) {\n // Check if we still need to measure the char size (fixes #785).\n if (!this.charMeasure.width || !this.charMeasure.height) {\n this.charMeasure.measure(this.options);\n }\n return;\n }\n\n if (x < 1) x = 1;\n if (y < 1) y = 1;\n\n this.buffers.resize(x, y);\n\n this.cols = x;\n this.rows = y;\n this.buffers.setupTabStops(this.cols);\n\n this.charMeasure.measure(this.options);\n\n this.refresh(0, this.rows - 1);\n\n this.geometry = [this.cols, this.rows];\n this.emit('resize', {cols: x, rows: y});\n }\n\n /**\n * Updates the range of rows to refresh\n * @param {number} y The number of rows to refresh next.\n */\n public updateRange(y: number): void {\n if (y < this.refreshStart) this.refreshStart = y;\n if (y > this.refreshEnd) this.refreshEnd = y;\n // if (y > this.refreshEnd) {\n // this.refreshEnd = y;\n // if (y > this.rows - 1) {\n // this.refreshEnd = this.rows - 1;\n // }\n // }\n }\n\n /**\n * Set the range of refreshing to the maximum value\n */\n public maxRange(): void {\n this.refreshStart = 0;\n this.refreshEnd = this.rows - 1;\n }\n\n /**\n * Erase in the identified line everything from \"x\" to the end of the line (right).\n * @param {number} x The column from which to start erasing to the end of the line.\n * @param {number} y The line in which to operate.\n */\n public eraseRight(x: number, y: number): void {\n const line = this.buffer.lines.get(this.buffer.ybase + y);\n if (!line) {\n return;\n }\n const ch: CharData = [this.eraseAttr(), ' ', 1, 32 /* ' '.charCodeAt(0) */]; // xterm\n for (; x < this.cols; x++) {\n line[x] = ch;\n }\n this.updateRange(y);\n }\n\n /**\n * Erase in the identified line everything from \"x\" to the start of the line (left).\n * @param {number} x The column from which to start erasing to the start of the line.\n * @param {number} y The line in which to operate.\n */\n public eraseLeft(x: number, y: number): void {\n const line = this.buffer.lines.get(this.buffer.ybase + y);\n if (!line) {\n return;\n }\n const ch: CharData = [this.eraseAttr(), ' ', 1, 32 /* ' '.charCodeAt(0) */]; // xterm\n x++;\n while (x--) {\n line[x] = ch;\n }\n this.updateRange(y);\n }\n\n /**\n * Clear the entire buffer, making the prompt line the new first line.\n */\n public clear(): void {\n if (this.buffer.ybase === 0 && this.buffer.y === 0) {\n // Don't clear if it's already clear\n return;\n }\n this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y));\n this.buffer.lines.length = 1;\n this.buffer.ydisp = 0;\n this.buffer.ybase = 0;\n this.buffer.y = 0;\n for (let i = 1; i < this.rows; i++) {\n this.buffer.lines.push(this.blankLine());\n }\n this.refresh(0, this.rows - 1);\n this.emit('scroll', this.buffer.ydisp);\n }\n\n /**\n * Erase all content in the given line\n * @param {number} y The line to erase all of its contents.\n */\n public eraseLine(y: number): void {\n this.eraseRight(0, y);\n }\n\n /**\n * Return the data array of a blank line\n * @param {boolean} cur First bunch of data for each \"blank\" character.\n * @param {boolean} isWrapped Whether the new line is wrapped from the previous line.\n * @param {boolean} cols The number of columns in the terminal, if this is not\n * set, the terminal's current column count would be used.\n */\n public blankLine(cur?: boolean, isWrapped?: boolean, cols?: number): LineData {\n const attr = cur ? this.eraseAttr() : this.defAttr;\n\n const ch: CharData = [attr, ' ', 1, 32 /* ' '.charCodeAt(0) */]; // width defaults to 1 halfwidth character\n const line: LineData = [];\n\n // TODO: It is not ideal that this is a property on an array, a buffer line\n // class should be added that will hold this data and other useful functions.\n if (isWrapped) {\n (line).isWrapped = isWrapped;\n }\n\n cols = cols || this.cols;\n for (let i = 0; i < cols; i++) {\n line[i] = ch;\n }\n\n return line;\n }\n\n /**\n * If cur return the back color xterm feature attribute. Else return defAttr.\n * @param cur\n */\n public ch(cur?: boolean): CharData {\n if (cur) {\n return [this.eraseAttr(), ' ', 1, 32 /* ' '.charCodeAt(0) */];\n }\n return [this.defAttr, ' ', 1, 32 /* ' '.charCodeAt(0) */];\n }\n\n /**\n * Evaluate if the current terminal is the given argument.\n * @param term The terminal name to evaluate\n */\n public is(term: string): boolean {\n return (this.options.termName + '').indexOf(term) === 0;\n }\n\n /**\n * Emit the 'data' event and populate the given data.\n * @param {string} data The data to populate in the event.\n */\n public handler(data: string): void {\n // Prevents all events to pty process if stdin is disabled\n if (this.options.disableStdin) {\n return;\n }\n\n // Clear the selection if the selection manager is available and has an active selection\n if (this.selectionManager && this.selectionManager.hasSelection) {\n this.selectionManager.clearSelection();\n }\n\n // Input is being sent to the terminal, the terminal should focus the prompt.\n if (this.buffer.ybase !== this.buffer.ydisp) {\n this.scrollToBottom();\n }\n this.emit('data', data);\n }\n\n /**\n * Emit the 'title' event and populate the given title.\n * @param {string} title The title to populate in the event.\n */\n private handleTitle(title: string): void {\n /**\n * This event is emitted when the title of the terminal is changed\n * from inside the terminal. The parameter is the new title.\n *\n * @event title\n */\n this.emit('title', title);\n }\n\n /**\n * ESC\n */\n\n /**\n * ESC D Index (IND is 0x84).\n */\n public index(): void {\n this.buffer.y++;\n if (this.buffer.y > this.buffer.scrollBottom) {\n this.buffer.y--;\n this.scroll();\n }\n // If the end of the line is hit, prevent this action from wrapping around to the next line.\n if (this.buffer.x >= this.cols) {\n this.buffer.x--;\n }\n }\n\n /**\n * ESC M Reverse Index (RI is 0x8d).\n *\n * Move the cursor up one row, inserting a new blank line if necessary.\n */\n public reverseIndex(): void {\n if (this.buffer.y === this.buffer.scrollTop) {\n // possibly move the code below to term.reverseScroll();\n // test: echo -ne '\\e[1;1H\\e[44m\\eM\\e[0m'\n // blankLine(true) is xterm/linux behavior\n const scrollRegionHeight = this.buffer.scrollBottom - this.buffer.scrollTop;\n this.buffer.lines.shiftElements(this.buffer.y + this.buffer.ybase, scrollRegionHeight, 1);\n this.buffer.lines.set(this.buffer.y + this.buffer.ybase, this.blankLine(true));\n this.updateRange(this.buffer.scrollTop);\n this.updateRange(this.buffer.scrollBottom);\n } else {\n this.buffer.y--;\n }\n }\n\n /**\n * ESC c Full Reset (RIS).\n */\n public reset(): void {\n this.options.rows = this.rows;\n this.options.cols = this.cols;\n const customKeyEventHandler = this.customKeyEventHandler;\n const inputHandler = this.inputHandler;\n const buffers = this.buffers;\n this.setup();\n this.customKeyEventHandler = customKeyEventHandler;\n this.inputHandler = inputHandler;\n this.buffers = buffers;\n this.refresh(0, this.rows - 1);\n this.viewport.syncScrollArea();\n }\n\n\n /**\n * ESC H Tab Set (HTS is 0x88).\n */\n private tabSet(): void {\n this.buffer.tabs[this.buffer.x] = true;\n }\n\n // TODO: Remove cancel function and cancelEvents option\n public cancel(ev: Event, force?: boolean): boolean {\n if (!this.options.cancelEvents && !force) {\n return;\n }\n ev.preventDefault();\n ev.stopPropagation();\n return false;\n }\n\n // TODO: Remove when true color is implemented\n public matchColor(r1: number, g1: number, b1: number): number {\n return matchColor_(r1, g1, b1);\n }\n\n private visualBell(): boolean {\n return this.options.bellStyle === 'visual' ||\n this.options.bellStyle === 'both';\n }\n\n private soundBell(): boolean {\n return this.options.bellStyle === 'sound' ||\n this.options.bellStyle === 'both';\n }\n\n private syncBellSound(): void {\n if (this.soundBell() && this.bellAudioElement) {\n this.bellAudioElement.setAttribute('src', this.options.bellSound);\n } else if (this.soundBell()) {\n this.bellAudioElement = document.createElement('audio');\n this.bellAudioElement.setAttribute('preload', 'auto');\n this.bellAudioElement.setAttribute('src', this.options.bellSound);\n this.helperContainer.appendChild(this.bellAudioElement);\n } else if (this.bellAudioElement) {\n this.helperContainer.removeChild(this.bellAudioElement);\n }\n }\n}\n\n/**\n * Helpers\n */\n\nfunction globalOn(el: any, type: string, handler: (event: Event) => any, capture?: boolean): void {\n if (!Array.isArray(el)) {\n el = [el];\n }\n el.forEach((element: HTMLElement) => {\n element.addEventListener(type, handler, capture || false);\n });\n}\n// TODO: Remove once everything is typed\nconst on = globalOn;\n\nfunction off(el: any, type: string, handler: (event: Event) => any, capture: boolean = false): void {\n el.removeEventListener(type, handler, capture);\n}\n\nfunction isThirdLevelShift(browser: IBrowser, ev: KeyboardEvent): boolean {\n const thirdLevelKey =\n (browser.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) ||\n (browser.isMSWindows && ev.altKey && ev.ctrlKey && !ev.metaKey);\n\n if (ev.type === 'keypress') {\n return thirdLevelKey;\n }\n\n // Don't invoke for arrows, pageDown, home, backspace, etc. (on non-keypress events)\n return thirdLevelKey && (!ev.keyCode || ev.keyCode > 47);\n}\n\nfunction wasMondifierKeyOnlyEvent(ev: KeyboardEvent): boolean {\n return ev.keyCode === 16 || // Shift\n ev.keyCode === 17 || // Ctrl\n ev.keyCode === 18; // Alt\n}\n\n/**\n * TODO:\n * The below color-related code can be removed when true color is implemented.\n * It's only purpose is to match true color requests with the closest matching\n * ANSI color code.\n */\n\n// Colors 0-15 + 16-255\n// Much thanks to TooTallNate for writing this.\nconst vcolors: number[][] = (function(): number[][] {\n const result = DEFAULT_ANSI_COLORS.map(c => {\n c = c.substring(1);\n return [\n parseInt(c.substring(0, 2), 16),\n parseInt(c.substring(2, 4), 16),\n parseInt(c.substring(4, 6), 16)\n ];\n });\n const r = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff];\n\n // 16-231\n for (let i = 0; i < 216; i++) {\n result.push([\n r[(i / 36) % 6 | 0],\n r[(i / 6) % 6 | 0],\n r[i % 6]\n ]);\n }\n\n // 232-255 (grey)\n let c: number;\n for (let i = 0; i < 24; i++) {\n c = 8 + i * 10;\n result.push([c, c, c]);\n }\n\n return result;\n})();\n\nconst matchColorCache: {[colorRGBHash: number]: number} = {};\n\n// http://stackoverflow.com/questions/1633828\nfunction matchColorDistance(r1: number, g1: number, b1: number, r2: number, g2: number, b2: number): number {\n return Math.pow(30 * (r1 - r2), 2)\n + Math.pow(59 * (g1 - g2), 2)\n + Math.pow(11 * (b1 - b2), 2);\n};\n\n\nfunction matchColor_(r1: number, g1: number, b1: number): number {\n const hash = (r1 << 16) | (g1 << 8) | b1;\n\n if (matchColorCache[hash] != null) {\n return matchColorCache[hash];\n }\n\n let ldiff = Infinity;\n let li = -1;\n let i = 0;\n let c: number[];\n let r2: number;\n let g2: number;\n let b2: number;\n let diff: number;\n\n for (; i < vcolors.length; i++) {\n c = vcolors[i];\n r2 = c[0];\n g2 = c[1];\n b2 = c[2];\n\n diff = matchColorDistance(r1, g1, b1, r2, g2, b2);\n\n if (diff === 0) {\n li = i;\n break;\n }\n\n if (diff < ldiff) {\n ldiff = diff;\n li = i;\n }\n }\n\n return matchColorCache[hash] = li;\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal } from './Interfaces';\n\n/**\n * Represents a selection within the buffer. This model only cares about column\n * and row coordinates, not wide characters.\n */\nexport class SelectionModel {\n /**\n * Whether select all is currently active.\n */\n public isSelectAllActive: boolean;\n\n /**\n * The [x, y] position the selection starts at.\n */\n public selectionStart: [number, number];\n\n /**\n * The minimal length of the selection from the start position. When double\n * clicking on a word, the word will be selected which makes the selection\n * start at the start of the word and makes this variable the length.\n */\n public selectionStartLength: number;\n\n /**\n * The [x, y] position the selection ends at.\n */\n public selectionEnd: [number, number];\n\n constructor(\n private _terminal: ITerminal\n ) {\n this.clearSelection();\n }\n\n /**\n * Clears the current selection.\n */\n public clearSelection(): void {\n this.selectionStart = null;\n this.selectionEnd = null;\n this.isSelectAllActive = false;\n this.selectionStartLength = 0;\n }\n\n /**\n * The final selection start, taking into consideration select all.\n */\n public get finalSelectionStart(): [number, number] {\n if (this.isSelectAllActive) {\n return [0, 0];\n }\n\n if (!this.selectionEnd || !this.selectionStart) {\n return this.selectionStart;\n }\n\n return this.areSelectionValuesReversed() ? this.selectionEnd : this.selectionStart;\n }\n\n /**\n * The final selection end, taking into consideration select all, double click\n * word selection and triple click line selection.\n */\n public get finalSelectionEnd(): [number, number] {\n if (this.isSelectAllActive) {\n return [this._terminal.cols, this._terminal.buffer.ybase + this._terminal.rows - 1];\n }\n\n if (!this.selectionStart) {\n return null;\n }\n\n // Use the selection start if the end doesn't exist or they're reversed\n if (!this.selectionEnd || this.areSelectionValuesReversed()) {\n return [this.selectionStart[0] + this.selectionStartLength, this.selectionStart[1]];\n }\n\n // Ensure the the word/line is selected after a double/triple click\n if (this.selectionStartLength) {\n // Select the larger of the two when start and end are on the same line\n if (this.selectionEnd[1] === this.selectionStart[1]) {\n return [Math.max(this.selectionStart[0] + this.selectionStartLength, this.selectionEnd[0]), this.selectionEnd[1]];\n }\n }\n return this.selectionEnd;\n }\n\n /**\n * Returns whether the selection start and end are reversed.\n */\n public areSelectionValuesReversed(): boolean {\n const start = this.selectionStart;\n const end = this.selectionEnd;\n if (!start || !end) {\n return false;\n }\n return start[1] > end[1] || (start[1] === end[1] && start[0] > end[0]);\n }\n\n /**\n * Handle the buffer being trimmed, adjust the selection position.\n * @param amount The amount the buffer is being trimmed.\n * @return Whether a refresh is necessary.\n */\n public onTrim(amount: number): boolean {\n // Adjust the selection position based on the trimmed amount.\n if (this.selectionStart) {\n this.selectionStart[1] -= amount;\n }\n if (this.selectionEnd) {\n this.selectionEnd[1] -= amount;\n }\n\n // The selection has moved off the buffer, clear it.\n if (this.selectionEnd && this.selectionEnd[1] < 0) {\n this.clearSelection();\n return true;\n }\n\n // If the selection start is trimmed, ensure the start column is 0.\n if (this.selectionStart && this.selectionStart[1] < 0) {\n this.selectionStart[1] = 0;\n }\n return false;\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { MouseHelper } from './utils/MouseHelper';\nimport * as Browser from './utils/Browser';\nimport { CharMeasure } from './utils/CharMeasure';\nimport { CircularList } from './utils/CircularList';\nimport { EventEmitter } from './EventEmitter';\nimport { ITerminal, ICircularList, ISelectionManager, IBuffer } from './Interfaces';\nimport { SelectionModel } from './SelectionModel';\nimport { LineData, CharData } from './Types';\nimport { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from './Buffer';\n\n/**\n * The number of pixels the mouse needs to be above or below the viewport in\n * order to scroll at the maximum speed.\n */\nconst DRAG_SCROLL_MAX_THRESHOLD = 50;\n\n/**\n * The maximum scrolling speed\n */\nconst DRAG_SCROLL_MAX_SPEED = 15;\n\n/**\n * The number of milliseconds between drag scroll updates.\n */\nconst DRAG_SCROLL_INTERVAL = 50;\n\n/**\n * A string containing all characters that are considered word separated by the\n * double click to select work logic.\n */\nconst WORD_SEPARATORS = ' ()[]{}\\'\"';\n\nconst NON_BREAKING_SPACE_CHAR = String.fromCharCode(160);\nconst ALL_NON_BREAKING_SPACE_REGEX = new RegExp(NON_BREAKING_SPACE_CHAR, 'g');\n\n/**\n * Represents a position of a word on a line.\n */\ninterface IWordPosition {\n start: number;\n length: number;\n}\n\n/**\n * A selection mode, this drives how the selection behaves on mouse move.\n */\nenum SelectionMode {\n NORMAL,\n WORD,\n LINE\n}\n\n/**\n * A class that manages the selection of the terminal. With help from\n * SelectionModel, SelectionManager handles with all logic associated with\n * dealing with the selection, including handling mouse interaction, wide\n * characters and fetching the actual text within the selection. Rendering is\n * not handled by the SelectionManager but a 'refresh' event is fired when the\n * selection is ready to be redrawn.\n */\nexport class SelectionManager extends EventEmitter implements ISelectionManager {\n protected _model: SelectionModel;\n\n /**\n * The amount to scroll every drag scroll update (depends on how far the mouse\n * drag is above or below the terminal).\n */\n private _dragScrollAmount: number;\n\n /**\n * The current selection mode.\n */\n private _activeSelectionMode: SelectionMode;\n\n /**\n * A setInterval timer that is active while the mouse is down whose callback\n * scrolls the viewport when necessary.\n */\n private _dragScrollIntervalTimer: NodeJS.Timer;\n\n /**\n * The animation frame ID used for refreshing the selection.\n */\n private _refreshAnimationFrame: number;\n\n /**\n * Whether selection is enabled.\n */\n private _enabled = true;\n\n private _mouseMoveListener: EventListener;\n private _mouseUpListener: EventListener;\n\n constructor(\n private _terminal: ITerminal,\n private _buffer: IBuffer,\n private _charMeasure: CharMeasure\n ) {\n super();\n this._initListeners();\n this.enable();\n\n this._model = new SelectionModel(_terminal);\n this._activeSelectionMode = SelectionMode.NORMAL;\n }\n\n /**\n * Initializes listener variables.\n */\n private _initListeners(): void {\n this._mouseMoveListener = event => this._onMouseMove(event);\n this._mouseUpListener = event => this._onMouseUp(event);\n\n // Only adjust the selection on trim, shiftElements is rarely used (only in\n // reverseIndex) and delete in a splice is only ever used when the same\n // number of elements was just added. Given this is could actually be\n // beneficial to leave the selection as is for these cases.\n this._buffer.lines.on('trim', (amount: number) => this._onTrim(amount));\n }\n\n /**\n * Disables the selection manager. This is useful for when terminal mouse\n * are enabled.\n */\n public disable(): void {\n this.clearSelection();\n this._enabled = false;\n }\n\n /**\n * Enable the selection manager.\n */\n public enable(): void {\n this._enabled = true;\n }\n\n /**\n * Sets the active buffer, this should be called when the alt buffer is\n * switched in or out.\n * @param buffer The active buffer.\n */\n public setBuffer(buffer: IBuffer): void {\n this._buffer = buffer;\n this.clearSelection();\n }\n\n public get selectionStart(): [number, number] { return this._model.finalSelectionStart; }\n public get selectionEnd(): [number, number] { return this._model.finalSelectionEnd; }\n\n /**\n * Gets whether there is an active text selection.\n */\n public get hasSelection(): boolean {\n const start = this._model.finalSelectionStart;\n const end = this._model.finalSelectionEnd;\n if (!start || !end) {\n return false;\n }\n return start[0] !== end[0] || start[1] !== end[1];\n }\n\n /**\n * Gets the text currently selected.\n */\n public get selectionText(): string {\n const start = this._model.finalSelectionStart;\n const end = this._model.finalSelectionEnd;\n if (!start || !end) {\n return '';\n }\n\n // Get first row\n const startRowEndCol = start[1] === end[1] ? end[0] : null;\n let result: string[] = [];\n result.push(this._buffer.translateBufferLineToString(start[1], true, start[0], startRowEndCol));\n\n // Get middle rows\n for (let i = start[1] + 1; i <= end[1] - 1; i++) {\n const bufferLine = this._buffer.lines.get(i);\n const lineText = this._buffer.translateBufferLineToString(i, true);\n if ((bufferLine).isWrapped) {\n result[result.length - 1] += lineText;\n } else {\n result.push(lineText);\n }\n }\n\n // Get final row\n if (start[1] !== end[1]) {\n const bufferLine = this._buffer.lines.get(end[1]);\n const lineText = this._buffer.translateBufferLineToString(end[1], true, 0, end[0]);\n if ((bufferLine).isWrapped) {\n result[result.length - 1] += lineText;\n } else {\n result.push(lineText);\n }\n }\n\n // Format string by replacing non-breaking space chars with regular spaces\n // and joining the array into a multi-line string.\n const formattedResult = result.map(line => {\n return line.replace(ALL_NON_BREAKING_SPACE_REGEX, ' ');\n }).join(Browser.isMSWindows ? '\\r\\n' : '\\n');\n\n return formattedResult;\n }\n\n /**\n * Clears the current terminal selection.\n */\n public clearSelection(): void {\n this._model.clearSelection();\n this._removeMouseDownListeners();\n this.refresh();\n }\n\n /**\n * Queues a refresh, redrawing the selection on the next opportunity.\n * @param isNewSelection Whether the selection should be registered as a new\n * selection on Linux.\n */\n public refresh(isNewSelection?: boolean): void {\n // Queue the refresh for the renderer\n if (!this._refreshAnimationFrame) {\n this._refreshAnimationFrame = window.requestAnimationFrame(() => this._refresh());\n }\n\n // If the platform is Linux and the refresh call comes from a mouse event,\n // we need to update the selection for middle click to paste selection.\n if (Browser.isLinux && isNewSelection) {\n const selectionText = this.selectionText;\n if (selectionText.length) {\n this.emit('newselection', this.selectionText);\n }\n }\n }\n\n /**\n * Fires the refresh event, causing consumers to pick it up and redraw the\n * selection state.\n */\n private _refresh(): void {\n this._refreshAnimationFrame = null;\n this.emit('refresh', { start: this._model.finalSelectionStart, end: this._model.finalSelectionEnd });\n }\n\n /**\n * Selects all text within the terminal.\n */\n public selectAll(): void {\n this._model.isSelectAllActive = true;\n this.refresh();\n this.emit('selection');\n }\n\n /**\n * Handle the buffer being trimmed, adjust the selection position.\n * @param amount The amount the buffer is being trimmed.\n */\n private _onTrim(amount: number): void {\n const needsRefresh = this._model.onTrim(amount);\n if (needsRefresh) {\n this.refresh();\n }\n }\n\n /**\n * Gets the 0-based [x, y] buffer coordinates of the current mouse event.\n * @param event The mouse event.\n */\n private _getMouseBufferCoords(event: MouseEvent): [number, number] {\n const coords = this._terminal.mouseHelper.getCoords(event, this._terminal.element, this._charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows, true);\n if (!coords) {\n return null;\n }\n\n // Convert to 0-based\n coords[0]--;\n coords[1]--;\n\n // Convert viewport coords to buffer coords\n coords[1] += this._terminal.buffer.ydisp;\n return coords;\n }\n\n /**\n * Gets the amount the viewport should be scrolled based on how far out of the\n * terminal the mouse is.\n * @param event The mouse event.\n */\n private _getMouseEventScrollAmount(event: MouseEvent): number {\n let offset = MouseHelper.getCoordsRelativeToElement(event, this._terminal.element)[1];\n const terminalHeight = this._terminal.rows * Math.ceil(this._charMeasure.height * this._terminal.options.lineHeight);\n if (offset >= 0 && offset <= terminalHeight) {\n return 0;\n }\n if (offset > terminalHeight) {\n offset -= terminalHeight;\n }\n\n offset = Math.min(Math.max(offset, -DRAG_SCROLL_MAX_THRESHOLD), DRAG_SCROLL_MAX_THRESHOLD);\n offset /= DRAG_SCROLL_MAX_THRESHOLD;\n return (offset / Math.abs(offset)) + Math.round(offset * (DRAG_SCROLL_MAX_SPEED - 1));\n }\n\n /**\n * Returns whether the selection manager should force selection, regardless of\n * whether the terminal is in mouse events mode.\n * @param event The mouse event.\n */\n public shouldForceSelection(event: MouseEvent): boolean {\n return Browser.isMac ? event.altKey : event.shiftKey;\n }\n\n /**\n * Handles te mousedown event, setting up for a new selection.\n * @param event The mousedown event.\n */\n public onMouseDown(event: MouseEvent): void {\n // If we have selection, we want the context menu on right click even if the\n // terminal is in mouse mode.\n if (event.button === 2 && this.hasSelection) {\n return;\n }\n\n // Only action the primary button\n if (event.button !== 0) {\n return;\n }\n\n // Allow selection when using a specific modifier key, even when disabled\n if (!this._enabled) {\n if (!this.shouldForceSelection(event)) {\n return;\n }\n\n // Don't send the mouse down event to the current process, we want to select\n event.stopPropagation();\n }\n\n // Tell the browser not to start a regular selection\n event.preventDefault();\n\n // Reset drag scroll state\n this._dragScrollAmount = 0;\n\n if (this._enabled && event.shiftKey) {\n this._onIncrementalClick(event);\n } else {\n if (event.detail === 1) {\n this._onSingleClick(event);\n } else if (event.detail === 2) {\n this._onDoubleClick(event);\n } else if (event.detail === 3) {\n this._onTripleClick(event);\n }\n }\n\n this._addMouseDownListeners();\n this.refresh(true);\n }\n\n /**\n * Adds listeners when mousedown is triggered.\n */\n private _addMouseDownListeners(): void {\n // Listen on the document so that dragging outside of viewport works\n this._terminal.element.ownerDocument.addEventListener('mousemove', this._mouseMoveListener);\n this._terminal.element.ownerDocument.addEventListener('mouseup', this._mouseUpListener);\n this._dragScrollIntervalTimer = setInterval(() => this._dragScroll(), DRAG_SCROLL_INTERVAL);\n }\n\n /**\n * Removes the listeners that are registered when mousedown is triggered.\n */\n private _removeMouseDownListeners(): void {\n this._terminal.element.ownerDocument.removeEventListener('mousemove', this._mouseMoveListener);\n this._terminal.element.ownerDocument.removeEventListener('mouseup', this._mouseUpListener);\n clearInterval(this._dragScrollIntervalTimer);\n this._dragScrollIntervalTimer = null;\n }\n\n /**\n * Performs an incremental click, setting the selection end position to the mouse\n * position.\n * @param event The mouse event.\n */\n private _onIncrementalClick(event: MouseEvent): void {\n if (this._model.selectionStart) {\n this._model.selectionEnd = this._getMouseBufferCoords(event);\n }\n }\n\n /**\n * Performs a single click, resetting relevant state and setting the selection\n * start position.\n * @param event The mouse event.\n */\n private _onSingleClick(event: MouseEvent): void {\n this._model.selectionStartLength = 0;\n this._model.isSelectAllActive = false;\n this._activeSelectionMode = SelectionMode.NORMAL;\n\n // Initialize the new selection\n this._model.selectionStart = this._getMouseBufferCoords(event);\n if (!this._model.selectionStart) {\n return;\n }\n this._model.selectionEnd = null;\n\n // Ensure the line exists\n const line = this._buffer.lines.get(this._model.selectionStart[1]);\n if (!line) {\n return;\n }\n\n // Return early if the click event is not in the buffer (eg. in scroll bar)\n if (line.length >= this._model.selectionStart[0]) {\n return;\n }\n\n // If the mouse is over the second half of a wide character, adjust the\n // selection to cover the whole character\n const char = line[this._model.selectionStart[0]];\n if (char[CHAR_DATA_WIDTH_INDEX] === 0) {\n this._model.selectionStart[0]++;\n }\n }\n\n /**\n * Performs a double click, selecting the current work.\n * @param event The mouse event.\n */\n private _onDoubleClick(event: MouseEvent): void {\n const coords = this._getMouseBufferCoords(event);\n if (coords) {\n this._activeSelectionMode = SelectionMode.WORD;\n this._selectWordAt(coords);\n }\n }\n\n /**\n * Performs a triple click, selecting the current line and activating line\n * select mode.\n * @param event The mouse event.\n */\n private _onTripleClick(event: MouseEvent): void {\n const coords = this._getMouseBufferCoords(event);\n if (coords) {\n this._activeSelectionMode = SelectionMode.LINE;\n this._selectLineAt(coords[1]);\n }\n }\n\n /**\n * Handles the mousemove event when the mouse button is down, recording the\n * end of the selection and refreshing the selection.\n * @param event The mousemove event.\n */\n private _onMouseMove(event: MouseEvent): void {\n // If the mousemove listener is active it means that a selection is\n // currently being made, we should stop propogation to prevent mouse events\n // to be sent to the pty.\n event.stopImmediatePropagation();\n\n // Record the previous position so we know whether to redraw the selection\n // at the end.\n const previousSelectionEnd = this._model.selectionEnd ? [this._model.selectionEnd[0], this._model.selectionEnd[1]] : null;\n\n // Set the initial selection end based on the mouse coordinates\n this._model.selectionEnd = this._getMouseBufferCoords(event);\n if (!this._model.selectionEnd) {\n this.refresh(true);\n return;\n }\n\n // Select the entire line if line select mode is active.\n if (this._activeSelectionMode === SelectionMode.LINE) {\n if (this._model.selectionEnd[1] < this._model.selectionStart[1]) {\n this._model.selectionEnd[0] = 0;\n } else {\n this._model.selectionEnd[0] = this._terminal.cols;\n }\n } else if (this._activeSelectionMode === SelectionMode.WORD) {\n this._selectToWordAt(this._model.selectionEnd);\n }\n\n // Determine the amount of scrolling that will happen.\n this._dragScrollAmount = this._getMouseEventScrollAmount(event);\n\n // If the cursor was above or below the viewport, make sure it's at the\n // start or end of the viewport respectively.\n if (this._dragScrollAmount > 0) {\n this._model.selectionEnd[0] = this._terminal.cols;\n } else if (this._dragScrollAmount < 0) {\n this._model.selectionEnd[0] = 0;\n }\n\n // If the character is a wide character include the cell to the right in the\n // selection. Note that selections at the very end of the line will never\n // have a character.\n if (this._model.selectionEnd[1] < this._buffer.lines.length) {\n const char = this._buffer.lines.get(this._model.selectionEnd[1])[this._model.selectionEnd[0]];\n if (char && char[CHAR_DATA_WIDTH_INDEX] === 0) {\n this._model.selectionEnd[0]++;\n }\n }\n\n // Only draw here if the selection changes.\n if (!previousSelectionEnd ||\n previousSelectionEnd[0] !== this._model.selectionEnd[0] ||\n previousSelectionEnd[1] !== this._model.selectionEnd[1]) {\n this.refresh(true);\n }\n }\n\n /**\n * The callback that occurs every DRAG_SCROLL_INTERVAL ms that does the\n * scrolling of the viewport.\n */\n private _dragScroll(): void {\n if (this._dragScrollAmount) {\n this._terminal.scrollLines(this._dragScrollAmount, false);\n // Re-evaluate selection\n if (this._dragScrollAmount > 0) {\n this._model.selectionEnd = [this._terminal.cols - 1, this._terminal.buffer.ydisp + this._terminal.rows];\n } else {\n this._model.selectionEnd = [0, this._terminal.buffer.ydisp];\n }\n this.refresh();\n }\n }\n\n /**\n * Handles the mouseup event, removing the mousedown listeners.\n * @param event The mouseup event.\n */\n private _onMouseUp(event: MouseEvent): void {\n this._removeMouseDownListeners();\n\n if (this.hasSelection)\n this.emit('selection');\n }\n\n /**\n * Converts a viewport column to the character index on the buffer line, the\n * latter takes into account wide characters.\n * @param coords The coordinates to find the 2 index for.\n */\n private _convertViewportColToCharacterIndex(bufferLine: any, coords: [number, number]): number {\n let charIndex = coords[0];\n for (let i = 0; coords[0] >= i; i++) {\n const char = bufferLine[i];\n if (char[CHAR_DATA_WIDTH_INDEX] === 0) {\n // Wide characters aren't included in the line string so decrement the\n // index so the index is back on the wide character.\n charIndex--;\n } else if (char[CHAR_DATA_CHAR_INDEX].length > 1 && coords[0] !== i) {\n // Emojis take up multiple characters, so adjust accordingly. For these\n // we don't want ot include the character at the column as we're\n // returning the start index in the string, not the end index.\n charIndex += char[CHAR_DATA_CHAR_INDEX].length - 1;\n }\n }\n return charIndex;\n }\n\n public setSelection(col: number, row: number, length: number): void {\n this._model.clearSelection();\n this._removeMouseDownListeners();\n this._model.selectionStart = [col, row];\n this._model.selectionStartLength = length;\n this.refresh();\n }\n\n /**\n * Gets positional information for the word at the coordinated specified.\n * @param coords The coordinates to get the word at.\n */\n private _getWordAt(coords: [number, number]): IWordPosition {\n const bufferLine = this._buffer.lines.get(coords[1]);\n if (!bufferLine) {\n return null;\n }\n\n const line = this._buffer.translateBufferLineToString(coords[1], false);\n\n // Get actual index, taking into consideration wide characters\n let startIndex = this._convertViewportColToCharacterIndex(bufferLine, coords);\n let endIndex = startIndex;\n\n // Record offset to be used later\n const charOffset = coords[0] - startIndex;\n let leftWideCharCount = 0;\n let rightWideCharCount = 0;\n let leftLongCharOffset = 0;\n let rightLongCharOffset = 0;\n\n if (line.charAt(startIndex) === ' ') {\n // Expand until non-whitespace is hit\n while (startIndex > 0 && line.charAt(startIndex - 1) === ' ') {\n startIndex--;\n }\n while (endIndex < line.length && line.charAt(endIndex + 1) === ' ') {\n endIndex++;\n }\n } else {\n // Expand until whitespace is hit. This algorithm works by scanning left\n // and right from the starting position, keeping both the index format\n // (line) and the column format (bufferLine) in sync. When a wide\n // character is hit, it is recorded and the column index is adjusted.\n let startCol = coords[0];\n let endCol = coords[0];\n\n // Consider the initial position, skip it and increment the wide char\n // variable\n if (bufferLine[startCol][CHAR_DATA_WIDTH_INDEX] === 0) {\n leftWideCharCount++;\n startCol--;\n }\n if (bufferLine[endCol][CHAR_DATA_WIDTH_INDEX] === 2) {\n rightWideCharCount++;\n endCol++;\n }\n\n // Adjust the end index for characters whose length are > 1 (emojis)\n if (bufferLine[endCol][CHAR_DATA_CHAR_INDEX].length > 1) {\n rightLongCharOffset += bufferLine[endCol][CHAR_DATA_CHAR_INDEX].length - 1;\n endIndex += bufferLine[endCol][CHAR_DATA_CHAR_INDEX].length - 1;\n }\n\n // Expand the string in both directions until a space is hit\n while (startCol > 0 && startIndex > 0 && !this._isCharWordSeparator(bufferLine[startCol - 1])) {\n const char = bufferLine[startCol - 1];\n if (char[CHAR_DATA_WIDTH_INDEX] === 0) {\n // If the next character is a wide char, record it and skip the column\n leftWideCharCount++;\n startCol--;\n } else if (char[CHAR_DATA_CHAR_INDEX].length > 1) {\n // If the next character's string is longer than 1 char (eg. emoji),\n // adjust the index\n leftLongCharOffset += char[CHAR_DATA_CHAR_INDEX].length - 1;\n startIndex -= char[CHAR_DATA_CHAR_INDEX].length - 1;\n }\n startIndex--;\n startCol--;\n }\n while (endCol < bufferLine.length && endIndex + 1 < line.length && !this._isCharWordSeparator(bufferLine[endCol + 1])) {\n const char = bufferLine[endCol + 1];\n if (char[CHAR_DATA_WIDTH_INDEX] === 2) {\n // If the next character is a wide char, record it and skip the column\n rightWideCharCount++;\n endCol++;\n } else if (char[CHAR_DATA_CHAR_INDEX].length > 1) {\n // If the next character's string is longer than 1 char (eg. emoji),\n // adjust the index\n rightLongCharOffset += char[CHAR_DATA_CHAR_INDEX].length - 1;\n endIndex += char[CHAR_DATA_CHAR_INDEX].length - 1;\n }\n endIndex++;\n endCol++;\n }\n }\n\n // Incremenet the end index so it is at the start of the next character\n endIndex++;\n\n // Calculate the start _column_, converting the the string indexes back to\n // column coordinates.\n const start =\n startIndex // The index of the selection's start char in the line string\n + charOffset // The difference between the initial char's column and index\n - leftWideCharCount // The number of wide chars left of the initial char\n + leftLongCharOffset; // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis)\n\n // Calculate the length in _columns_, converting the the string indexes back\n // to column coordinates.\n const length = Math.min(this._terminal.cols, // Disallow lengths larger than the terminal cols\n endIndex // The index of the selection's end char in the line string\n - startIndex // The index of the selection's start char in the line string\n + leftWideCharCount // The number of wide chars left of the initial char\n + rightWideCharCount // The number of wide chars right of the initial char (inclusive)\n - leftLongCharOffset // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis)\n - rightLongCharOffset); // The number of additional chars right of the initial char (inclusive) added by columns with strings longer than 1 (emojis)\n\n return { start, length };\n }\n\n /**\n * Selects the word at the coordinates specified.\n * @param coords The coordinates to get the word at.\n */\n protected _selectWordAt(coords: [number, number]): void {\n const wordPosition = this._getWordAt(coords);\n if (wordPosition) {\n this._model.selectionStart = [wordPosition.start, coords[1]];\n this._model.selectionStartLength = wordPosition.length;\n }\n }\n\n /**\n * Sets the selection end to the word at the coordinated specified.\n * @param coords The coordinates to get the word at.\n */\n private _selectToWordAt(coords: [number, number]): void {\n const wordPosition = this._getWordAt(coords);\n if (wordPosition) {\n this._model.selectionEnd = [this._model.areSelectionValuesReversed() ? wordPosition.start : (wordPosition.start + wordPosition.length), coords[1]];\n }\n }\n\n /**\n * Gets whether the character is considered a word separator by the select\n * word logic.\n * @param char The character to check.\n */\n private _isCharWordSeparator(charData: CharData): boolean {\n // Zero width characters are never separators as they are always to the\n // right of wide characters\n if (charData[CHAR_DATA_WIDTH_INDEX] === 0) {\n return false;\n }\n return WORD_SEPARATORS.indexOf(charData[CHAR_DATA_CHAR_INDEX]) >= 0;\n }\n\n /**\n * Selects the line specified.\n * @param line The line index.\n */\n protected _selectLineAt(line: number): void {\n this._model.selectionStart = [0, line];\n this._model.selectionStartLength = this._terminal.cols;\n }\n}\n","/**\n * Copyright (c) 2014 The xterm.js authors. All rights reserved.\n * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)\n * @license MIT\n */\n\nimport { C0 } from './EscapeSequences';\nimport { IInputHandler } from './Interfaces';\nimport { CHARSETS, DEFAULT_CHARSET } from './Charsets';\n\nconst normalStateHandler: {[key: string]: (parser: Parser, handler: IInputHandler) => void} = {};\nnormalStateHandler[C0.BEL] = (parser, handler) => handler.bell();\nnormalStateHandler[C0.LF] = (parser, handler) => handler.lineFeed();\nnormalStateHandler[C0.VT] = normalStateHandler[C0.LF];\nnormalStateHandler[C0.FF] = normalStateHandler[C0.LF];\nnormalStateHandler[C0.CR] = (parser, handler) => handler.carriageReturn();\nnormalStateHandler[C0.BS] = (parser, handler) => handler.backspace();\nnormalStateHandler[C0.HT] = (parser, handler) => handler.tab();\nnormalStateHandler[C0.SO] = (parser, handler) => handler.shiftOut();\nnormalStateHandler[C0.SI] = (parser, handler) => handler.shiftIn();\nnormalStateHandler[C0.ESC] = (parser, handler) => parser.setState(ParserState.ESCAPED);\n\n// TODO: Remove terminal when parser owns params and currentParam\nconst escapedStateHandler: {[key: string]: (parser: Parser, terminal: any) => void} = {};\nescapedStateHandler['['] = (parser, terminal) => {\n // ESC [ Control Sequence Introducer (CSI is 0x9b)\n terminal.params = [];\n terminal.currentParam = 0;\n parser.setState(ParserState.CSI_PARAM);\n};\nescapedStateHandler[']'] = (parser, terminal) => {\n // ESC ] Operating System Command (OSC is 0x9d)\n terminal.params = [];\n terminal.currentParam = 0;\n parser.setState(ParserState.OSC);\n};\nescapedStateHandler['P'] = (parser, terminal) => {\n // ESC P Device Control String (DCS is 0x90)\n terminal.params = [];\n terminal.currentParam = 0;\n parser.setState(ParserState.DCS);\n};\nescapedStateHandler['_'] = (parser, terminal) => {\n // ESC _ Application Program Command ( APC is 0x9f).\n parser.setState(ParserState.IGNORE);\n};\nescapedStateHandler['^'] = (parser, terminal) => {\n // ESC ^ Privacy Message ( PM is 0x9e).\n parser.setState(ParserState.IGNORE);\n};\nescapedStateHandler['c'] = (parser, terminal) => {\n // ESC c Full Reset (RIS).\n terminal.reset();\n};\nescapedStateHandler['E'] = (parser, terminal) => {\n // ESC E Next Line ( NEL is 0x85).\n terminal.buffer.x = 0;\n terminal.index();\n parser.setState(ParserState.NORMAL);\n};\nescapedStateHandler['D'] = (parser, terminal) => {\n // ESC D Index ( IND is 0x84).\n terminal.index();\n parser.setState(ParserState.NORMAL);\n};\nescapedStateHandler['M'] = (parser, terminal) => {\n // ESC M Reverse Index ( RI is 0x8d).\n terminal.reverseIndex();\n parser.setState(ParserState.NORMAL);\n};\nescapedStateHandler['%'] = (parser, terminal) => {\n // ESC % Select default/utf-8 character set.\n // @ = default, G = utf-8\n terminal.setgLevel(0);\n terminal.setgCharset(0, DEFAULT_CHARSET); // US (default)\n parser.setState(ParserState.NORMAL);\n parser.skipNextChar();\n};\nescapedStateHandler[C0.CAN] = (parser) => parser.setState(ParserState.NORMAL);\n\nconst csiParamStateHandler: {[key: string]: (parser: Parser) => void} = {};\ncsiParamStateHandler['?'] = (parser) => parser.setPrefix('?');\ncsiParamStateHandler['>'] = (parser) => parser.setPrefix('>');\ncsiParamStateHandler['!'] = (parser) => parser.setPrefix('!');\ncsiParamStateHandler['0'] = (parser) => parser.setParam(parser.getParam() * 10);\ncsiParamStateHandler['1'] = (parser) => parser.setParam(parser.getParam() * 10 + 1);\ncsiParamStateHandler['2'] = (parser) => parser.setParam(parser.getParam() * 10 + 2);\ncsiParamStateHandler['3'] = (parser) => parser.setParam(parser.getParam() * 10 + 3);\ncsiParamStateHandler['4'] = (parser) => parser.setParam(parser.getParam() * 10 + 4);\ncsiParamStateHandler['5'] = (parser) => parser.setParam(parser.getParam() * 10 + 5);\ncsiParamStateHandler['6'] = (parser) => parser.setParam(parser.getParam() * 10 + 6);\ncsiParamStateHandler['7'] = (parser) => parser.setParam(parser.getParam() * 10 + 7);\ncsiParamStateHandler['8'] = (parser) => parser.setParam(parser.getParam() * 10 + 8);\ncsiParamStateHandler['9'] = (parser) => parser.setParam(parser.getParam() * 10 + 9);\ncsiParamStateHandler['$'] = (parser) => parser.setPostfix('$');\ncsiParamStateHandler['\"'] = (parser) => parser.setPostfix('\"');\ncsiParamStateHandler[' '] = (parser) => parser.setPostfix(' ');\ncsiParamStateHandler['\\''] = (parser) => parser.setPostfix('\\'');\ncsiParamStateHandler[';'] = (parser) => parser.finalizeParam();\ncsiParamStateHandler[C0.CAN] = (parser) => parser.setState(ParserState.NORMAL);\n\nconst csiStateHandler: {[key: string]: (handler: IInputHandler, params: number[], prefix: string, postfix: string, parser: Parser) => void} = {};\ncsiStateHandler['@'] = (handler, params, prefix) => handler.insertChars(params);\ncsiStateHandler['A'] = (handler, params, prefix) => handler.cursorUp(params);\ncsiStateHandler['B'] = (handler, params, prefix) => handler.cursorDown(params);\ncsiStateHandler['C'] = (handler, params, prefix) => handler.cursorForward(params);\ncsiStateHandler['D'] = (handler, params, prefix) => handler.cursorBackward(params);\ncsiStateHandler['E'] = (handler, params, prefix) => handler.cursorNextLine(params);\ncsiStateHandler['F'] = (handler, params, prefix) => handler.cursorPrecedingLine(params);\ncsiStateHandler['G'] = (handler, params, prefix) => handler.cursorCharAbsolute(params);\ncsiStateHandler['H'] = (handler, params, prefix) => handler.cursorPosition(params);\ncsiStateHandler['I'] = (handler, params, prefix) => handler.cursorForwardTab(params);\ncsiStateHandler['J'] = (handler, params, prefix) => handler.eraseInDisplay(params);\ncsiStateHandler['K'] = (handler, params, prefix) => handler.eraseInLine(params);\ncsiStateHandler['L'] = (handler, params, prefix) => handler.insertLines(params);\ncsiStateHandler['M'] = (handler, params, prefix) => handler.deleteLines(params);\ncsiStateHandler['P'] = (handler, params, prefix) => handler.deleteChars(params);\ncsiStateHandler['S'] = (handler, params, prefix) => handler.scrollUp(params);\ncsiStateHandler['T'] = (handler, params, prefix) => {\n if (params.length < 2 && !prefix) {\n handler.scrollDown(params);\n }\n};\ncsiStateHandler['X'] = (handler, params, prefix) => handler.eraseChars(params);\ncsiStateHandler['Z'] = (handler, params, prefix) => handler.cursorBackwardTab(params);\ncsiStateHandler['`'] = (handler, params, prefix) => handler.charPosAbsolute(params);\ncsiStateHandler['a'] = (handler, params, prefix) => handler.HPositionRelative(params);\ncsiStateHandler['b'] = (handler, params, prefix) => handler.repeatPrecedingCharacter(params);\ncsiStateHandler['c'] = (handler, params, prefix) => handler.sendDeviceAttributes(params);\ncsiStateHandler['d'] = (handler, params, prefix) => handler.linePosAbsolute(params);\ncsiStateHandler['e'] = (handler, params, prefix) => handler.VPositionRelative(params);\ncsiStateHandler['f'] = (handler, params, prefix) => handler.HVPosition(params);\ncsiStateHandler['g'] = (handler, params, prefix) => handler.tabClear(params);\ncsiStateHandler['h'] = (handler, params, prefix) => handler.setMode(params);\ncsiStateHandler['l'] = (handler, params, prefix) => handler.resetMode(params);\ncsiStateHandler['m'] = (handler, params, prefix) => handler.charAttributes(params);\ncsiStateHandler['n'] = (handler, params, prefix) => handler.deviceStatus(params);\ncsiStateHandler['p'] = (handler, params, prefix) => {\n switch (prefix) {\n case '!': handler.softReset(params); break;\n }\n};\ncsiStateHandler['q'] = (handler, params, prefix, postfix) => {\n if (postfix === ' ') {\n handler.setCursorStyle(params);\n }\n};\ncsiStateHandler['r'] = (handler, params) => handler.setScrollRegion(params);\ncsiStateHandler['s'] = (handler, params) => handler.saveCursor(params);\ncsiStateHandler['u'] = (handler, params) => handler.restoreCursor(params);\ncsiStateHandler[C0.CAN] = (handler, params, prefix, postfix, parser) => parser.setState(ParserState.NORMAL);\n\nexport enum ParserState {\n NORMAL = 0,\n ESCAPED = 1,\n CSI_PARAM = 2,\n CSI = 3,\n OSC = 4,\n CHARSET = 5,\n DCS = 6,\n IGNORE = 7\n}\n\n/**\n * The terminal's parser, all input into the terminal goes through the parser\n * which parses and defers the actual input handling the the IInputHandler\n * specified in the constructor.\n */\nexport class Parser {\n private _state: ParserState;\n private _position: number;\n\n // TODO: Remove terminal when handler can do everything\n constructor(\n private _inputHandler: IInputHandler,\n private _terminal: any\n ) {\n this._state = ParserState.NORMAL;\n }\n\n /**\n * Parse and handle data.\n *\n * @param data The data to parse.\n */\n public parse(data: string): ParserState {\n const l = data.length;\n let j;\n let cs;\n let ch;\n let code;\n let low;\n\n const cursorStartX = this._terminal.buffer.x;\n const cursorStartY = this._terminal.buffer.y;\n\n if (this._terminal.debug) {\n this._terminal.log('data: ' + data);\n }\n\n this._position = 0;\n // apply leftover surrogate high from last write\n if (this._terminal.surrogate_high) {\n data = this._terminal.surrogate_high + data;\n this._terminal.surrogate_high = '';\n }\n\n for (; this._position < l; this._position++) {\n ch = data[this._position];\n\n // FIXME: higher chars than 0xa0 are not allowed in escape sequences\n // --> maybe move to default\n code = data.charCodeAt(this._position);\n if (0xD800 <= code && code <= 0xDBFF) {\n // we got a surrogate high\n // get surrogate low (next 2 bytes)\n low = data.charCodeAt(this._position + 1);\n if (isNaN(low)) {\n // end of data stream, save surrogate high\n this._terminal.surrogate_high = ch;\n continue;\n }\n code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;\n ch += data.charAt(this._position + 1);\n }\n // surrogate low - already handled above\n if (0xDC00 <= code && code <= 0xDFFF)\n continue;\n\n switch (this._state) {\n case ParserState.NORMAL:\n if (ch in normalStateHandler) {\n normalStateHandler[ch](this, this._inputHandler);\n } else {\n this._inputHandler.addChar(ch, code);\n }\n break;\n case ParserState.ESCAPED:\n if (ch in escapedStateHandler) {\n escapedStateHandler[ch](this, this._terminal);\n // Skip switch as it was just handled\n break;\n }\n switch (ch) {\n\n // ESC (,),*,+,-,. Designate G0-G2 Character Set.\n case '(': // <-- this seems to get all the attention\n case ')':\n case '*':\n case '+':\n case '-':\n case '.':\n switch (ch) {\n case '(':\n this._terminal.gcharset = 0;\n break;\n case ')':\n this._terminal.gcharset = 1;\n break;\n case '*':\n this._terminal.gcharset = 2;\n break;\n case '+':\n this._terminal.gcharset = 3;\n break;\n case '-':\n this._terminal.gcharset = 1;\n break;\n case '.':\n this._terminal.gcharset = 2;\n break;\n }\n this._state = ParserState.CHARSET;\n break;\n\n // Designate G3 Character Set (VT300).\n // A = ISO Latin-1 Supplemental.\n // Not implemented.\n case '/':\n this._terminal.gcharset = 3;\n this._state = ParserState.CHARSET;\n this._position--;\n break;\n\n // ESC N\n // Single Shift Select of G2 Character Set\n // ( SS2 is 0x8e). This affects next character only.\n case 'N':\n break;\n // ESC O\n // Single Shift Select of G3 Character Set\n // ( SS3 is 0x8f). This affects next character only.\n case 'O':\n break;\n // ESC n\n // Invoke the G2 Character Set as GL (LS2).\n case 'n':\n this._terminal.setgLevel(2);\n break;\n // ESC o\n // Invoke the G3 Character Set as GL (LS3).\n case 'o':\n this._terminal.setgLevel(3);\n break;\n // ESC |\n // Invoke the G3 Character Set as GR (LS3R).\n case '|':\n this._terminal.setgLevel(3);\n break;\n // ESC }\n // Invoke the G2 Character Set as GR (LS2R).\n case '}':\n this._terminal.setgLevel(2);\n break;\n // ESC ~\n // Invoke the G1 Character Set as GR (LS1R).\n case '~':\n this._terminal.setgLevel(1);\n break;\n\n // ESC 7 Save Cursor (DECSC).\n case '7':\n this._inputHandler.saveCursor();\n this._state = ParserState.NORMAL;\n break;\n\n // ESC 8 Restore Cursor (DECRC).\n case '8':\n this._inputHandler.restoreCursor();\n this._state = ParserState.NORMAL;\n break;\n\n // ESC # 3 DEC line height/width\n case '#':\n this._state = ParserState.NORMAL;\n this._position++;\n break;\n\n // ESC H Tab Set (HTS is 0x88).\n case 'H':\n this._terminal.tabSet();\n this._state = ParserState.NORMAL;\n break;\n\n // ESC = Application Keypad (DECKPAM).\n case '=':\n this._terminal.log('Serial port requested application keypad.');\n this._terminal.applicationKeypad = true;\n if (this._terminal.viewport) {\n this._terminal.viewport.syncScrollArea();\n }\n this._state = ParserState.NORMAL;\n break;\n\n // ESC > Normal Keypad (DECKPNM).\n case '>':\n this._terminal.log('Switching back to normal keypad.');\n this._terminal.applicationKeypad = false;\n if (this._terminal.viewport) {\n this._terminal.viewport.syncScrollArea();\n }\n this._state = ParserState.NORMAL;\n break;\n\n default:\n this._state = ParserState.NORMAL;\n this._terminal.error('Unknown ESC control: %s.', ch);\n break;\n }\n break;\n\n case ParserState.CHARSET:\n if (ch in CHARSETS) {\n cs = CHARSETS[ch];\n if (ch === '/') { // ISOLatin is actually /A\n this.skipNextChar();\n }\n } else {\n cs = DEFAULT_CHARSET;\n }\n this._terminal.setgCharset(this._terminal.gcharset, cs);\n this._terminal.gcharset = null;\n this._state = ParserState.NORMAL;\n break;\n\n case ParserState.OSC:\n // OSC Ps ; Pt ST\n // OSC Ps ; Pt BEL\n // Set Text Parameters.\n if (ch === C0.ESC || ch === C0.BEL) {\n if (ch === C0.ESC) this._position++;\n\n this._terminal.params.push(this._terminal.currentParam);\n\n switch (this._terminal.params[0]) {\n case 0:\n case 1:\n case 2:\n if (this._terminal.params[1]) {\n this._terminal.title = this._terminal.params[1];\n this._terminal.handleTitle(this._terminal.title);\n }\n break;\n case 3:\n // set X property\n break;\n case 4:\n case 5:\n // change dynamic colors\n break;\n case 10:\n case 11:\n case 12:\n case 13:\n case 14:\n case 15:\n case 16:\n case 17:\n case 18:\n case 19:\n // change dynamic ui colors\n break;\n case 46:\n // change log file\n break;\n case 50:\n // dynamic font\n break;\n case 51:\n // emacs shell\n break;\n case 52:\n // manipulate selection data\n break;\n case 104:\n case 105:\n case 110:\n case 111:\n case 112:\n case 113:\n case 114:\n case 115:\n case 116:\n case 117:\n case 118:\n // reset colors\n break;\n }\n\n this._terminal.params = [];\n this._terminal.currentParam = 0;\n this._state = ParserState.NORMAL;\n } else {\n if (!this._terminal.params.length) {\n if (ch >= '0' && ch <= '9') {\n this._terminal.currentParam =\n this._terminal.currentParam * 10 + ch.charCodeAt(0) - 48;\n } else if (ch === ';') {\n this._terminal.params.push(this._terminal.currentParam);\n this._terminal.currentParam = '';\n }\n } else {\n this._terminal.currentParam += ch;\n }\n }\n break;\n\n case ParserState.CSI_PARAM:\n if (ch in csiParamStateHandler) {\n csiParamStateHandler[ch](this);\n break;\n }\n this.finalizeParam();\n // Fall through the CSI as this character should be the CSI code.\n this._state = ParserState.CSI;\n\n case ParserState.CSI:\n if (ch in csiStateHandler) {\n if (this._terminal.debug) {\n this._terminal.log(`CSI ${this._terminal.prefix ? this._terminal.prefix : ''} ${this._terminal.params ? this._terminal.params.join(';') : ''} ${this._terminal.postfix ? this._terminal.postfix : ''} ${ch}`);\n }\n csiStateHandler[ch](this._inputHandler, this._terminal.params, this._terminal.prefix, this._terminal.postfix, this);\n } else {\n this._terminal.error('Unknown CSI code: %s.', ch);\n }\n\n this._state = ParserState.NORMAL;\n this._terminal.prefix = '';\n this._terminal.postfix = '';\n break;\n\n case ParserState.DCS:\n if (ch === C0.ESC || ch === C0.BEL) {\n if (ch === C0.ESC) this._position++;\n let pt;\n let valid: boolean;\n\n switch (this._terminal.prefix) {\n // User-Defined Keys (DECUDK).\n case '':\n break;\n\n // Request Status String (DECRQSS).\n // test: echo -e '\\eP$q\"p\\e\\\\'\n case '$q':\n pt = this._terminal.currentParam;\n valid = false;\n\n switch (pt) {\n // DECSCA\n case '\"q':\n pt = '0\"q';\n break;\n\n // DECSCL\n case '\"p':\n pt = '61\"p';\n break;\n\n // DECSTBM\n case 'r':\n pt = ''\n + (this._terminal.buffer.scrollTop + 1)\n + ';'\n + (this._terminal.buffer.scrollBottom + 1)\n + 'r';\n break;\n\n // SGR\n case 'm':\n pt = '0m';\n break;\n\n default:\n this._terminal.error('Unknown DCS Pt: %s.', pt);\n pt = '';\n break;\n }\n\n this._terminal.send(C0.ESC + 'P' + +valid + '$r' + pt + C0.ESC + '\\\\');\n break;\n\n // Set Termcap/Terminfo Data (xterm, experimental).\n case '+p':\n break;\n\n // Request Termcap/Terminfo String (xterm, experimental)\n // Regular xterm does not even respond to this sequence.\n // This can cause a small glitch in vim.\n // test: echo -ne '\\eP+q6b64\\e\\\\'\n case '+q':\n pt = this._terminal.currentParam;\n valid = false;\n\n this._terminal.send(C0.ESC + 'P' + +valid + '+r' + pt + C0.ESC + '\\\\');\n break;\n\n default:\n this._terminal.error('Unknown DCS prefix: %s.', this._terminal.prefix);\n break;\n }\n\n this._terminal.currentParam = 0;\n this._terminal.prefix = '';\n this._state = ParserState.NORMAL;\n } else if (!this._terminal.currentParam) {\n if (!this._terminal.prefix && ch !== '$' && ch !== '+') {\n this._terminal.currentParam = ch;\n } else if (this._terminal.prefix.length === 2) {\n this._terminal.currentParam = ch;\n } else {\n this._terminal.prefix += ch;\n }\n } else {\n this._terminal.currentParam += ch;\n }\n break;\n\n case ParserState.IGNORE:\n // For PM and APC.\n if (ch === C0.ESC || ch === C0.BEL) {\n if (ch === C0.ESC) this._position++;\n this._state = ParserState.NORMAL;\n }\n break;\n }\n }\n\n // Fire the cursormove event if it's moved. This is done inside the parser\n // as a render cannot happen in the middle of a parsing round.\n if (this._terminal.buffer.x !== cursorStartX || this._terminal.buffer.y !== cursorStartY) {\n this._terminal.emit('cursormove');\n }\n\n return this._state;\n }\n\n /**\n * Set the parser's current parsing state.\n *\n * @param state The new state.\n */\n public setState(state: ParserState): void {\n this._state = state;\n }\n\n /**\n * Sets the parsier's current prefix. CSI codes can have prefixes of '?', '>'\n * or '!'.\n *\n * @param prefix The prefix.\n */\n public setPrefix(prefix: string): void {\n this._terminal.prefix = prefix;\n }\n\n /**\n * Sets the parsier's current prefix. CSI codes can have postfixes of '$',\n * '\"', ' ', '\\''.\n *\n * @param postfix The postfix.\n */\n public setPostfix(postfix: string): void {\n this._terminal.postfix = postfix;\n }\n\n /**\n * Sets the parser's current parameter.\n *\n * @param param the parameter.\n */\n public setParam(param: number): void {\n this._terminal.currentParam = param;\n }\n\n /**\n * Gets the parser's current parameter.\n */\n public getParam(): number {\n return this._terminal.currentParam;\n }\n\n /**\n * Finalizes the parser's current parameter, adding it to the list of\n * parameters and setting the new current parameter to 0.\n */\n public finalizeParam(): void {\n this._terminal.params.push(this._terminal.currentParam);\n this._terminal.currentParam = 0;\n }\n\n /**\n * Tell the parser to skip the next character.\n */\n public skipNextChar(): void {\n this._position++;\n }\n\n /**\n * Tell the parser to repeat parsing the current character (for example if it\n * needs parsing using a different state.\n */\n // public repeatChar(): void {\n // this._position--;\n // }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ILinkMatcherOptions, ITerminal, IBufferAccessor, ILinkifier, IElementAccessor } from './Interfaces';\nimport { LinkMatcher, LinkMatcherHandler, LinkMatcherValidationCallback, LineData, LinkHoverEvent, LinkHoverEventTypes } from './Types';\nimport { IMouseZoneManager } from './input/Interfaces';\nimport { MouseZone } from './input/MouseZoneManager';\nimport { EventEmitter } from './EventEmitter';\n\nconst protocolClause = '(https?:\\\\/\\\\/)';\nconst domainCharacterSet = '[\\\\da-z\\\\.-]+';\nconst negatedDomainCharacterSet = '[^\\\\da-z\\\\.-]+';\nconst domainBodyClause = '(' + domainCharacterSet + ')';\nconst tldClause = '([a-z\\\\.]{2,6})';\nconst ipClause = '((\\\\d{1,3}\\\\.){3}\\\\d{1,3})';\nconst localHostClause = '(localhost)';\nconst portClause = '(:\\\\d{1,5})';\nconst hostClause = '((' + domainBodyClause + '\\\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?';\nconst pathClause = '(\\\\/[\\\\/\\\\w\\\\.\\\\-%~]*)*';\nconst queryStringHashFragmentCharacterSet = '[0-9\\\\w\\\\[\\\\]\\\\(\\\\)\\\\/\\\\?\\\\!#@$%&\\'*+,:;~\\\\=\\\\.\\\\-]*';\nconst queryStringClause = '(\\\\?' + queryStringHashFragmentCharacterSet + ')?';\nconst hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?';\nconst negatedPathCharacterSet = '[^\\\\/\\\\w\\\\.\\\\-%]+';\nconst bodyClause = hostClause + pathClause + queryStringClause + hashFragmentClause;\nconst start = '(?:^|' + negatedDomainCharacterSet + ')(';\nconst end = ')($|' + negatedPathCharacterSet + ')';\nconst strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end);\n\n/**\n * The ID of the built in http(s) link matcher.\n */\nconst HYPERTEXT_LINK_MATCHER_ID = 0;\n\n/**\n * The Linkifier applies links to rows shortly after they have been refreshed.\n */\nexport class Linkifier extends EventEmitter implements ILinkifier {\n /**\n * The time to wait after a row is changed before it is linkified. This prevents\n * the costly operation of searching every row multiple times, potentially a\n * huge amount of times.\n */\n protected static TIME_BEFORE_LINKIFY = 200;\n\n protected _linkMatchers: LinkMatcher[] = [];\n\n private _mouseZoneManager: IMouseZoneManager;\n private _rowsTimeoutId: number;\n private _nextLinkMatcherId = HYPERTEXT_LINK_MATCHER_ID;\n private _rowsToLinkify: {start: number, end: number};\n\n constructor(\n protected _terminal: IBufferAccessor & IElementAccessor\n ) {\n super();\n this._rowsToLinkify = {\n start: null,\n end: null\n };\n this.registerLinkMatcher(strictUrlRegex, null, { matchIndex: 1 });\n }\n\n /**\n * Attaches the linkifier to the DOM, enabling linkification.\n * @param mouseZoneManager The mouse zone manager to register link zones with.\n */\n public attachToDom(mouseZoneManager: IMouseZoneManager): void {\n this._mouseZoneManager = mouseZoneManager;\n }\n\n /**\n * Queue linkification on a set of rows.\n * @param start The row to linkify from (inclusive).\n * @param end The row to linkify to (inclusive).\n */\n public linkifyRows(start: number, end: number): void {\n // Don't attempt linkify if not yet attached to DOM\n if (!this._mouseZoneManager) {\n return;\n }\n\n // Increase range to linkify\n if (!this._rowsToLinkify.start) {\n this._rowsToLinkify.start = start;\n this._rowsToLinkify.end = end;\n } else {\n this._rowsToLinkify.start = this._rowsToLinkify.start < start ? this._rowsToLinkify.start : start;\n this._rowsToLinkify.end = this._rowsToLinkify.end > end ? this._rowsToLinkify.end : end;\n }\n\n // Clear out any existing links on this row range\n this._mouseZoneManager.clearAll(start, end);\n\n // Restart timer\n if (this._rowsTimeoutId) {\n clearTimeout(this._rowsTimeoutId);\n }\n this._rowsTimeoutId = setTimeout(() => this._linkifyRows(), Linkifier.TIME_BEFORE_LINKIFY);\n }\n\n /**\n * Linkifies the rows requested.\n */\n private _linkifyRows(): void {\n this._rowsTimeoutId = null;\n for (let i = this._rowsToLinkify.start; i <= this._rowsToLinkify.end; i++) {\n this._linkifyRow(i);\n }\n this._rowsToLinkify.start = null;\n this._rowsToLinkify.end = null;\n }\n\n /**\n * Attaches a handler for hypertext links, overriding default behavior for\n * tandard http(s) links.\n * @param handler The handler to use, this can be cleared with null.\n */\n public setHypertextLinkHandler(handler: LinkMatcherHandler): void {\n this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].handler = handler;\n }\n\n /**\n * Attaches a validation callback for hypertext links.\n * @param callback The callback to use, this can be cleared with null.\n */\n public setHypertextValidationCallback(callback: LinkMatcherValidationCallback): void {\n this._linkMatchers[HYPERTEXT_LINK_MATCHER_ID].validationCallback = callback;\n }\n\n /**\n * Registers a link matcher, allowing custom link patterns to be matched and\n * handled.\n * @param regex The regular expression to search for. Specifically, this\n * searches the textContent of the rows. You will want to use \\s to match a\n * space ' ' character for example.\n * @param handler The callback when the link is called.\n * @param options Options for the link matcher.\n * @return The ID of the new matcher, this can be used to deregister.\n */\n public registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options: ILinkMatcherOptions = {}): number {\n if (this._nextLinkMatcherId !== HYPERTEXT_LINK_MATCHER_ID && !handler) {\n throw new Error('handler must be defined');\n }\n const matcher: LinkMatcher = {\n id: this._nextLinkMatcherId++,\n regex,\n handler,\n matchIndex: options.matchIndex,\n validationCallback: options.validationCallback,\n hoverTooltipCallback: options.tooltipCallback,\n hoverLeaveCallback: options.leaveCallback,\n priority: options.priority || 0\n };\n this._addLinkMatcherToList(matcher);\n return matcher.id;\n }\n\n /**\n * Inserts a link matcher to the list in the correct position based on the\n * priority of each link matcher. New link matchers of equal priority are\n * considered after older link matchers.\n * @param matcher The link matcher to be added.\n */\n private _addLinkMatcherToList(matcher: LinkMatcher): void {\n if (this._linkMatchers.length === 0) {\n this._linkMatchers.push(matcher);\n return;\n }\n\n for (let i = this._linkMatchers.length - 1; i >= 0; i--) {\n if (matcher.priority <= this._linkMatchers[i].priority) {\n this._linkMatchers.splice(i + 1, 0, matcher);\n return;\n }\n }\n\n this._linkMatchers.splice(0, 0, matcher);\n }\n\n /**\n * Deregisters a link matcher if it has been registered.\n * @param matcherId The link matcher's ID (returned after register)\n * @return Whether a link matcher was found and deregistered.\n */\n public deregisterLinkMatcher(matcherId: number): boolean {\n // ID 0 is the hypertext link matcher which cannot be deregistered\n for (let i = 1; i < this._linkMatchers.length; i++) {\n if (this._linkMatchers[i].id === matcherId) {\n this._linkMatchers.splice(i, 1);\n return true;\n }\n }\n return false;\n }\n\n /**\n * Linkifies a row.\n * @param rowIndex The index of the row to linkify.\n */\n private _linkifyRow(rowIndex: number): void {\n const absoluteRowIndex = this._terminal.buffer.ydisp + rowIndex;\n if (absoluteRowIndex >= this._terminal.buffer.lines.length) {\n return;\n }\n const text = this._terminal.buffer.translateBufferLineToString(absoluteRowIndex, false);\n for (let i = 0; i < this._linkMatchers.length; i++) {\n this._doLinkifyRow(rowIndex, text, this._linkMatchers[i]);\n }\n }\n\n /**\n * Linkifies a row given a specific handler.\n * @param rowIndex The row index to linkify.\n * @param text The text of the row (excludes text in the row that's already\n * linkified).\n * @param matcher The link matcher for this line.\n * @param offset The how much of the row has already been linkified.\n * @return The link element(s) that were added.\n */\n private _doLinkifyRow(rowIndex: number, text: string, matcher: LinkMatcher, offset: number = 0): void {\n // Iterate over nodes as we want to consider text nodes\n let result = [];\n const isHttpLinkMatcher = matcher.id === HYPERTEXT_LINK_MATCHER_ID;\n\n // Find the first match\n let match = text.match(matcher.regex);\n if (!match || match.length === 0) {\n return;\n }\n let uri = match[typeof matcher.matchIndex !== 'number' ? 0 : matcher.matchIndex];\n\n // Get index, match.index is for the outer match which includes negated chars\n const index = text.indexOf(uri);\n\n // Ensure the link is valid before registering\n if (matcher.validationCallback) {\n matcher.validationCallback(uri, isValid => {\n // Discard link if the line has already changed\n if (this._rowsTimeoutId) {\n return;\n }\n if (isValid) {\n this._addLink(offset + index, rowIndex, uri, matcher);\n }\n });\n } else {\n this._addLink(offset + index, rowIndex, uri, matcher);\n }\n\n // Recursively check for links in the rest of the text\n const remainingStartIndex = index + uri.length;\n const remainingText = text.substr(remainingStartIndex);\n if (remainingText.length > 0) {\n this._doLinkifyRow(rowIndex, remainingText, matcher, offset + remainingStartIndex);\n }\n }\n\n /**\n * Registers a link to the mouse zone manager.\n * @param x The column the link starts.\n * @param y The row the link is on.\n * @param uri The URI of the link.\n * @param matcher The link matcher for the link.\n */\n private _addLink(x: number, y: number, uri: string, matcher: LinkMatcher): void {\n this._mouseZoneManager.add(new MouseZone(\n x + 1,\n x + 1 + uri.length,\n y + 1,\n e => {\n if (matcher.handler) {\n return matcher.handler(e, uri);\n }\n window.open(uri, '_blank');\n },\n e => {\n this.emit(LinkHoverEventTypes.HOVER, { x, y, length: uri.length});\n this._terminal.element.style.cursor = 'pointer';\n },\n e => {\n this.emit(LinkHoverEventTypes.TOOLTIP, { x, y, length: uri.length});\n if (matcher.hoverTooltipCallback) {\n matcher.hoverTooltipCallback(e, uri);\n }\n },\n () => {\n this.emit(LinkHoverEventTypes.LEAVE, { x, y, length: uri.length});\n this._terminal.element.style.cursor = '';\n if (matcher.hoverLeaveCallback) {\n matcher.hoverLeaveCallback();\n }\n }\n ));\n }\n}\n","/**\n * Copyright (c) 2014 The xterm.js authors. All rights reserved.\n * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)\n * @license MIT\n */\n\nimport { IInputHandler, ITerminal, IInputHandlingTerminal } from './Interfaces';\nimport { C0 } from './EscapeSequences';\nimport { DEFAULT_CHARSET } from './Charsets';\nimport { CharData } from './Types';\nimport { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX } from './Buffer';\nimport { FLAGS } from './renderer/Types';\nimport { wcwidth } from './CharWidth';\n\n/**\n * The terminal's standard implementation of IInputHandler, this handles all\n * input from the Parser.\n *\n * Refer to http://invisible-island.net/xterm/ctlseqs/ctlseqs.html to understand\n * each function's header comment.\n */\nexport class InputHandler implements IInputHandler {\n constructor(private _terminal: IInputHandlingTerminal) { }\n\n public addChar(char: string, code: number): void {\n if (char >= ' ') {\n // calculate print space\n // expensive call, therefore we save width in line buffer\n const ch_width = wcwidth(code);\n\n if (this._terminal.charset && this._terminal.charset[char]) {\n char = this._terminal.charset[char];\n }\n\n let row = this._terminal.buffer.y + this._terminal.buffer.ybase;\n\n // insert combining char in last cell\n // FIXME: needs handling after cursor jumps\n if (!ch_width && this._terminal.buffer.x) {\n // dont overflow left\n if (this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1]) {\n if (!this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][CHAR_DATA_WIDTH_INDEX]) {\n // found empty cell after fullwidth, need to go 2 cells back\n if (this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2]) {\n this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2][CHAR_DATA_CHAR_INDEX] += char;\n this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2][3] = char.charCodeAt(0);\n }\n } else {\n this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][CHAR_DATA_CHAR_INDEX] += char;\n this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][3] = char.charCodeAt(0);\n }\n this._terminal.updateRange(this._terminal.buffer.y);\n }\n return;\n }\n\n // goto next line if ch would overflow\n // TODO: needs a global min terminal width of 2\n if (this._terminal.buffer.x + ch_width - 1 >= this._terminal.cols) {\n // autowrap - DECAWM\n if (this._terminal.wraparoundMode) {\n this._terminal.buffer.x = 0;\n this._terminal.buffer.y++;\n if (this._terminal.buffer.y > this._terminal.buffer.scrollBottom) {\n this._terminal.buffer.y--;\n this._terminal.scroll(true);\n } else {\n // The line already exists (eg. the initial viewport), mark it as a\n // wrapped line\n (this._terminal.buffer.lines.get(this._terminal.buffer.y)).isWrapped = true;\n }\n } else {\n if (ch_width === 2) // FIXME: check for xterm behavior\n return;\n }\n }\n row = this._terminal.buffer.y + this._terminal.buffer.ybase;\n\n // insert mode: move characters to right\n if (this._terminal.insertMode) {\n // do this twice for a fullwidth char\n for (let moves = 0; moves < ch_width; ++moves) {\n // remove last cell, if it's width is 0\n // we have to adjust the second last cell as well\n const removed = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).pop();\n if (removed[CHAR_DATA_WIDTH_INDEX] === 0\n && this._terminal.buffer.lines.get(row)[this._terminal.cols - 2]\n && this._terminal.buffer.lines.get(row)[this._terminal.cols - 2][CHAR_DATA_WIDTH_INDEX] === 2) {\n this._terminal.buffer.lines.get(row)[this._terminal.cols - 2] = [this._terminal.curAttr, ' ', 1, ' '.charCodeAt(0)];\n }\n\n // insert empty cell at cursor\n this._terminal.buffer.lines.get(row).splice(this._terminal.buffer.x, 0, [this._terminal.curAttr, ' ', 1, ' '.charCodeAt(0)]);\n }\n }\n\n this._terminal.buffer.lines.get(row)[this._terminal.buffer.x] = [this._terminal.curAttr, char, ch_width, char.charCodeAt(0)];\n this._terminal.buffer.x++;\n this._terminal.updateRange(this._terminal.buffer.y);\n\n // fullwidth char - set next cell width to zero and advance cursor\n if (ch_width === 2) {\n this._terminal.buffer.lines.get(row)[this._terminal.buffer.x] = [this._terminal.curAttr, '', 0, undefined];\n this._terminal.buffer.x++;\n }\n }\n }\n\n /**\n * BEL\n * Bell (Ctrl-G).\n */\n public bell(): void {\n this._terminal.bell();\n }\n\n /**\n * LF\n * Line Feed or New Line (NL). (LF is Ctrl-J).\n */\n public lineFeed(): void {\n if (this._terminal.convertEol) {\n this._terminal.buffer.x = 0;\n }\n this._terminal.buffer.y++;\n if (this._terminal.buffer.y > this._terminal.buffer.scrollBottom) {\n this._terminal.buffer.y--;\n this._terminal.scroll();\n }\n // If the end of the line is hit, prevent this action from wrapping around to the next line.\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x--;\n }\n /**\n * This event is emitted whenever the terminal outputs a LF or NL.\n *\n * @event lineFeed\n */\n this._terminal.emit('lineFeed');\n }\n\n /**\n * CR\n * Carriage Return (Ctrl-M).\n */\n public carriageReturn(): void {\n this._terminal.buffer.x = 0;\n }\n\n /**\n * BS\n * Backspace (Ctrl-H).\n */\n public backspace(): void {\n if (this._terminal.buffer.x > 0) {\n this._terminal.buffer.x--;\n }\n }\n\n /**\n * TAB\n * Horizontal Tab (HT) (Ctrl-I).\n */\n public tab(): void {\n this._terminal.buffer.x = this._terminal.buffer.nextStop();\n }\n\n /**\n * SO\n * Shift Out (Ctrl-N) -> Switch to Alternate Character Set. This invokes the\n * G1 character set.\n */\n public shiftOut(): void {\n this._terminal.setgLevel(1);\n }\n\n /**\n * SI\n * Shift In (Ctrl-O) -> Switch to Standard Character Set. This invokes the G0\n * character set (the default).\n */\n public shiftIn(): void {\n this._terminal.setgLevel(0);\n }\n\n /**\n * CSI Ps @\n * Insert Ps (Blank) Character(s) (default = 1) (ICH).\n */\n public insertChars(params: number[]): void {\n let param = params[0];\n if (param < 1) param = 1;\n\n const row = this._terminal.buffer.y + this._terminal.buffer.ybase;\n let j = this._terminal.buffer.x;\n const ch: CharData = [this._terminal.eraseAttr(), ' ', 1, 32]; // xterm\n\n while (param-- && j < this._terminal.cols) {\n this._terminal.buffer.lines.get(row).splice(j++, 0, ch);\n this._terminal.buffer.lines.get(row).pop();\n }\n }\n\n /**\n * CSI Ps A\n * Cursor Up Ps Times (default = 1) (CUU).\n */\n public cursorUp(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.y -= param;\n if (this._terminal.buffer.y < 0) {\n this._terminal.buffer.y = 0;\n }\n }\n\n /**\n * CSI Ps B\n * Cursor Down Ps Times (default = 1) (CUD).\n */\n public cursorDown(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.y += param;\n if (this._terminal.buffer.y >= this._terminal.rows) {\n this._terminal.buffer.y = this._terminal.rows - 1;\n }\n // If the end of the line is hit, prevent this action from wrapping around to the next line.\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x--;\n }\n }\n\n /**\n * CSI Ps C\n * Cursor Forward Ps Times (default = 1) (CUF).\n */\n public cursorForward(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.x += param;\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x = this._terminal.cols - 1;\n }\n }\n\n /**\n * CSI Ps D\n * Cursor Backward Ps Times (default = 1) (CUB).\n */\n public cursorBackward(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n // If the end of the line is hit, prevent this action from wrapping around to the next line.\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x--;\n }\n this._terminal.buffer.x -= param;\n if (this._terminal.buffer.x < 0) {\n this._terminal.buffer.x = 0;\n }\n }\n\n /**\n * CSI Ps E\n * Cursor Next Line Ps Times (default = 1) (CNL).\n * same as CSI Ps B ?\n */\n public cursorNextLine(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.y += param;\n if (this._terminal.buffer.y >= this._terminal.rows) {\n this._terminal.buffer.y = this._terminal.rows - 1;\n }\n this._terminal.buffer.x = 0;\n }\n\n\n /**\n * CSI Ps F\n * Cursor Preceding Line Ps Times (default = 1) (CNL).\n * reuse CSI Ps A ?\n */\n public cursorPrecedingLine(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.y -= param;\n if (this._terminal.buffer.y < 0) {\n this._terminal.buffer.y = 0;\n }\n this._terminal.buffer.x = 0;\n }\n\n\n /**\n * CSI Ps G\n * Cursor Character Absolute [column] (default = [row,1]) (CHA).\n */\n public cursorCharAbsolute(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.x = param - 1;\n }\n\n /**\n * CSI Ps ; Ps H\n * Cursor Position [row;column] (default = [1,1]) (CUP).\n */\n public cursorPosition(params: number[]): void {\n let col: number;\n let row: number = params[0] - 1;\n\n if (params.length >= 2) {\n col = params[1] - 1;\n } else {\n col = 0;\n }\n\n if (row < 0) {\n row = 0;\n } else if (row >= this._terminal.rows) {\n row = this._terminal.rows - 1;\n }\n\n if (col < 0) {\n col = 0;\n } else if (col >= this._terminal.cols) {\n col = this._terminal.cols - 1;\n }\n\n this._terminal.buffer.x = col;\n this._terminal.buffer.y = row;\n }\n\n /**\n * CSI Ps I\n * Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).\n */\n public cursorForwardTab(params: number[]): void {\n let param = params[0] || 1;\n while (param--) {\n this._terminal.buffer.x = this._terminal.buffer.nextStop();\n }\n }\n\n /**\n * CSI Ps J Erase in Display (ED).\n * Ps = 0 -> Erase Below (default).\n * Ps = 1 -> Erase Above.\n * Ps = 2 -> Erase All.\n * Ps = 3 -> Erase Saved Lines (xterm).\n * CSI ? Ps J\n * Erase in Display (DECSED).\n * Ps = 0 -> Selective Erase Below (default).\n * Ps = 1 -> Selective Erase Above.\n * Ps = 2 -> Selective Erase All.\n */\n public eraseInDisplay(params: number[]): void {\n let j;\n switch (params[0]) {\n case 0:\n this._terminal.eraseRight(this._terminal.buffer.x, this._terminal.buffer.y);\n j = this._terminal.buffer.y + 1;\n for (; j < this._terminal.rows; j++) {\n this._terminal.eraseLine(j);\n }\n break;\n case 1:\n this._terminal.eraseLeft(this._terminal.buffer.x, this._terminal.buffer.y);\n j = this._terminal.buffer.y;\n while (j--) {\n this._terminal.eraseLine(j);\n }\n break;\n case 2:\n j = this._terminal.rows;\n while (j--) this._terminal.eraseLine(j);\n break;\n case 3:\n // Clear scrollback (everything not in viewport)\n const scrollBackSize = this._terminal.buffer.lines.length - this._terminal.rows;\n if (scrollBackSize > 0) {\n this._terminal.buffer.lines.trimStart(scrollBackSize);\n this._terminal.buffer.ybase = Math.max(this._terminal.buffer.ybase - scrollBackSize, 0);\n this._terminal.buffer.ydisp = Math.max(this._terminal.buffer.ydisp - scrollBackSize, 0);\n // Force a scroll event to refresh viewport\n this._terminal.emit('scroll', 0);\n }\n break;\n }\n }\n\n /**\n * CSI Ps K Erase in Line (EL).\n * Ps = 0 -> Erase to Right (default).\n * Ps = 1 -> Erase to Left.\n * Ps = 2 -> Erase All.\n * CSI ? Ps K\n * Erase in Line (DECSEL).\n * Ps = 0 -> Selective Erase to Right (default).\n * Ps = 1 -> Selective Erase to Left.\n * Ps = 2 -> Selective Erase All.\n */\n public eraseInLine(params: number[]): void {\n switch (params[0]) {\n case 0:\n this._terminal.eraseRight(this._terminal.buffer.x, this._terminal.buffer.y);\n break;\n case 1:\n this._terminal.eraseLeft(this._terminal.buffer.x, this._terminal.buffer.y);\n break;\n case 2:\n this._terminal.eraseLine(this._terminal.buffer.y);\n break;\n }\n }\n\n /**\n * CSI Ps L\n * Insert Ps Line(s) (default = 1) (IL).\n */\n public insertLines(params: number[]): void {\n let param: number = params[0];\n if (param < 1) {\n param = 1;\n }\n let row: number = this._terminal.buffer.y + this._terminal.buffer.ybase;\n\n let scrollBottomRowsOffset = this._terminal.rows - 1 - this._terminal.buffer.scrollBottom;\n let scrollBottomAbsolute = this._terminal.rows - 1 + this._terminal.buffer.ybase - scrollBottomRowsOffset + 1;\n while (param--) {\n // test: echo -e '\\e[44m\\e[1L\\e[0m'\n // blankLine(true) - xterm/linux behavior\n this._terminal.buffer.lines.splice(scrollBottomAbsolute - 1, 1);\n this._terminal.buffer.lines.splice(row, 0, this._terminal.blankLine(true));\n }\n\n // this.maxRange();\n this._terminal.updateRange(this._terminal.buffer.y);\n this._terminal.updateRange(this._terminal.buffer.scrollBottom);\n }\n\n /**\n * CSI Ps M\n * Delete Ps Line(s) (default = 1) (DL).\n */\n public deleteLines(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n const row: number = this._terminal.buffer.y + this._terminal.buffer.ybase;\n\n let j: number;\n j = this._terminal.rows - 1 - this._terminal.buffer.scrollBottom;\n j = this._terminal.rows - 1 + this._terminal.buffer.ybase - j;\n while (param--) {\n // test: echo -e '\\e[44m\\e[1M\\e[0m'\n // blankLine(true) - xterm/linux behavior\n this._terminal.buffer.lines.splice(row, 1);\n this._terminal.buffer.lines.splice(j, 0, this._terminal.blankLine(true));\n }\n\n // this.maxRange();\n this._terminal.updateRange(this._terminal.buffer.y);\n this._terminal.updateRange(this._terminal.buffer.scrollBottom);\n }\n\n /**\n * CSI Ps P\n * Delete Ps Character(s) (default = 1) (DCH).\n */\n public deleteChars(params: number[]): void {\n let param: number = params[0];\n if (param < 1) {\n param = 1;\n }\n\n const row = this._terminal.buffer.y + this._terminal.buffer.ybase;\n const ch: CharData = [this._terminal.eraseAttr(), ' ', 1, 32]; // xterm\n\n while (param--) {\n this._terminal.buffer.lines.get(row).splice(this._terminal.buffer.x, 1);\n this._terminal.buffer.lines.get(row).push(ch);\n }\n this._terminal.updateRange(this._terminal.buffer.y);\n }\n\n /**\n * CSI Ps S Scroll up Ps lines (default = 1) (SU).\n */\n public scrollUp(params: number[]): void {\n let param = params[0] || 1;\n while (param--) {\n this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollTop, 1);\n this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollBottom, 0, this._terminal.blankLine());\n }\n // this.maxRange();\n this._terminal.updateRange(this._terminal.buffer.scrollTop);\n this._terminal.updateRange(this._terminal.buffer.scrollBottom);\n }\n\n /**\n * CSI Ps T Scroll down Ps lines (default = 1) (SD).\n */\n public scrollDown(params: number[]): void {\n let param = params[0] || 1;\n while (param--) {\n this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollBottom, 1);\n this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollTop, 0, this._terminal.blankLine());\n }\n // this.maxRange();\n this._terminal.updateRange(this._terminal.buffer.scrollTop);\n this._terminal.updateRange(this._terminal.buffer.scrollBottom);\n }\n\n /**\n * CSI Ps X\n * Erase Ps Character(s) (default = 1) (ECH).\n */\n public eraseChars(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n\n const row = this._terminal.buffer.y + this._terminal.buffer.ybase;\n let j = this._terminal.buffer.x;\n const ch: CharData = [this._terminal.eraseAttr(), ' ', 1, 32]; // xterm\n\n while (param-- && j < this._terminal.cols) {\n this._terminal.buffer.lines.get(row)[j++] = ch;\n }\n }\n\n /**\n * CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).\n */\n public cursorBackwardTab(params: number[]): void {\n let param = params[0] || 1;\n while (param--) {\n this._terminal.buffer.x = this._terminal.buffer.prevStop();\n }\n }\n\n /**\n * CSI Pm ` Character Position Absolute\n * [column] (default = [row,1]) (HPA).\n */\n public charPosAbsolute(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.x = param - 1;\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x = this._terminal.cols - 1;\n }\n }\n\n /**\n * CSI Pm a Character Position Relative\n * [columns] (default = [row,col+1]) (HPR)\n * reuse CSI Ps C ?\n */\n public HPositionRelative(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.x += param;\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x = this._terminal.cols - 1;\n }\n }\n\n /**\n * CSI Ps b Repeat the preceding graphic character Ps times (REP).\n */\n public repeatPrecedingCharacter(params: number[]): void {\n let param = params[0] || 1;\n const line = this._terminal.buffer.lines.get(this._terminal.buffer.ybase + this._terminal.buffer.y);\n const ch = line[this._terminal.buffer.x - 1] || [this._terminal.defAttr, ' ', 1, 32];\n\n while (param--) {\n line[this._terminal.buffer.x++] = ch;\n }\n }\n\n /**\n * CSI Ps c Send Device Attributes (Primary DA).\n * Ps = 0 or omitted -> request attributes from terminal. The\n * response depends on the decTerminalID resource setting.\n * -> CSI ? 1 ; 2 c (``VT100 with Advanced Video Option'')\n * -> CSI ? 1 ; 0 c (``VT101 with No Options'')\n * -> CSI ? 6 c (``VT102'')\n * -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c (``VT220'')\n * The VT100-style response parameters do not mean anything by\n * themselves. VT220 parameters do, telling the host what fea-\n * tures the terminal supports:\n * Ps = 1 -> 132-columns.\n * Ps = 2 -> Printer.\n * Ps = 6 -> Selective erase.\n * Ps = 8 -> User-defined keys.\n * Ps = 9 -> National replacement character sets.\n * Ps = 1 5 -> Technical characters.\n * Ps = 2 2 -> ANSI color, e.g., VT525.\n * Ps = 2 9 -> ANSI text locator (i.e., DEC Locator mode).\n * CSI > Ps c\n * Send Device Attributes (Secondary DA).\n * Ps = 0 or omitted -> request the terminal's identification\n * code. The response depends on the decTerminalID resource set-\n * ting. It should apply only to VT220 and up, but xterm extends\n * this to VT100.\n * -> CSI > Pp ; Pv ; Pc c\n * where Pp denotes the terminal type\n * Pp = 0 -> ``VT100''.\n * Pp = 1 -> ``VT220''.\n * and Pv is the firmware version (for xterm, this was originally\n * the XFree86 patch number, starting with 95). In a DEC termi-\n * nal, Pc indicates the ROM cartridge registration number and is\n * always zero.\n * More information:\n * xterm/charproc.c - line 2012, for more information.\n * vim responds with ^[[?0c or ^[[?1c after the terminal's response (?)\n */\n public sendDeviceAttributes(params: number[]): void {\n if (params[0] > 0) {\n return;\n }\n\n if (!this._terminal.prefix) {\n if (this._terminal.is('xterm') || this._terminal.is('rxvt-unicode') || this._terminal.is('screen')) {\n this._terminal.send(C0.ESC + '[?1;2c');\n } else if (this._terminal.is('linux')) {\n this._terminal.send(C0.ESC + '[?6c');\n }\n } else if (this._terminal.prefix === '>') {\n // xterm and urxvt\n // seem to spit this\n // out around ~370 times (?).\n if (this._terminal.is('xterm')) {\n this._terminal.send(C0.ESC + '[>0;276;0c');\n } else if (this._terminal.is('rxvt-unicode')) {\n this._terminal.send(C0.ESC + '[>85;95;0c');\n } else if (this._terminal.is('linux')) {\n // not supported by linux console.\n // linux console echoes parameters.\n this._terminal.send(params[0] + 'c');\n } else if (this._terminal.is('screen')) {\n this._terminal.send(C0.ESC + '[>83;40003;0c');\n }\n }\n }\n\n /**\n * CSI Pm d Vertical Position Absolute (VPA)\n * [row] (default = [1,column])\n */\n public linePosAbsolute(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.y = param - 1;\n if (this._terminal.buffer.y >= this._terminal.rows) {\n this._terminal.buffer.y = this._terminal.rows - 1;\n }\n }\n\n /**\n * CSI Pm e Vertical Position Relative (VPR)\n * [rows] (default = [row+1,column])\n * reuse CSI Ps B ?\n */\n public VPositionRelative(params: number[]): void {\n let param = params[0];\n if (param < 1) {\n param = 1;\n }\n this._terminal.buffer.y += param;\n if (this._terminal.buffer.y >= this._terminal.rows) {\n this._terminal.buffer.y = this._terminal.rows - 1;\n }\n // If the end of the line is hit, prevent this action from wrapping around to the next line.\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x--;\n }\n }\n\n /**\n * CSI Ps ; Ps f\n * Horizontal and Vertical Position [row;column] (default =\n * [1,1]) (HVP).\n */\n public HVPosition(params: number[]): void {\n if (params[0] < 1) params[0] = 1;\n if (params[1] < 1) params[1] = 1;\n\n this._terminal.buffer.y = params[0] - 1;\n if (this._terminal.buffer.y >= this._terminal.rows) {\n this._terminal.buffer.y = this._terminal.rows - 1;\n }\n\n this._terminal.buffer.x = params[1] - 1;\n if (this._terminal.buffer.x >= this._terminal.cols) {\n this._terminal.buffer.x = this._terminal.cols - 1;\n }\n }\n\n /**\n * CSI Ps g Tab Clear (TBC).\n * Ps = 0 -> Clear Current Column (default).\n * Ps = 3 -> Clear All.\n * Potentially:\n * Ps = 2 -> Clear Stops on Line.\n * http://vt100.net/annarbor/aaa-ug/section6.html\n */\n public tabClear(params: number[]): void {\n let param = params[0];\n if (param <= 0) {\n delete this._terminal.buffer.tabs[this._terminal.buffer.x];\n } else if (param === 3) {\n this._terminal.buffer.tabs = {};\n }\n }\n\n /**\n * CSI Pm h Set Mode (SM).\n * Ps = 2 -> Keyboard Action Mode (AM).\n * Ps = 4 -> Insert Mode (IRM).\n * Ps = 1 2 -> Send/receive (SRM).\n * Ps = 2 0 -> Automatic Newline (LNM).\n * CSI ? Pm h\n * DEC Private Mode Set (DECSET).\n * Ps = 1 -> Application Cursor Keys (DECCKM).\n * Ps = 2 -> Designate USASCII for character sets G0-G3\n * (DECANM), and set VT100 mode.\n * Ps = 3 -> 132 Column Mode (DECCOLM).\n * Ps = 4 -> Smooth (Slow) Scroll (DECSCLM).\n * Ps = 5 -> Reverse Video (DECSCNM).\n * Ps = 6 -> Origin Mode (DECOM).\n * Ps = 7 -> Wraparound Mode (DECAWM).\n * Ps = 8 -> Auto-repeat Keys (DECARM).\n * Ps = 9 -> Send Mouse X & Y on button press. See the sec-\n * tion Mouse Tracking.\n * Ps = 1 0 -> Show toolbar (rxvt).\n * Ps = 1 2 -> Start Blinking Cursor (att610).\n * Ps = 1 8 -> Print form feed (DECPFF).\n * Ps = 1 9 -> Set print extent to full screen (DECPEX).\n * Ps = 2 5 -> Show Cursor (DECTCEM).\n * Ps = 3 0 -> Show scrollbar (rxvt).\n * Ps = 3 5 -> Enable font-shifting functions (rxvt).\n * Ps = 3 8 -> Enter Tektronix Mode (DECTEK).\n * Ps = 4 0 -> Allow 80 -> 132 Mode.\n * Ps = 4 1 -> more(1) fix (see curses resource).\n * Ps = 4 2 -> Enable Nation Replacement Character sets (DECN-\n * RCM).\n * Ps = 4 4 -> Turn On Margin Bell.\n * Ps = 4 5 -> Reverse-wraparound Mode.\n * Ps = 4 6 -> Start Logging. This is normally disabled by a\n * compile-time option.\n * Ps = 4 7 -> Use Alternate Screen Buffer. (This may be dis-\n * abled by the titeInhibit resource).\n * Ps = 6 6 -> Application keypad (DECNKM).\n * Ps = 6 7 -> Backarrow key sends backspace (DECBKM).\n * Ps = 1 0 0 0 -> Send Mouse X & Y on button press and\n * release. See the section Mouse Tracking.\n * Ps = 1 0 0 1 -> Use Hilite Mouse Tracking.\n * Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking.\n * Ps = 1 0 0 3 -> Use All Motion Mouse Tracking.\n * Ps = 1 0 0 4 -> Send FocusIn/FocusOut events.\n * Ps = 1 0 0 5 -> Enable Extended Mouse Mode.\n * Ps = 1 0 1 0 -> Scroll to bottom on tty output (rxvt).\n * Ps = 1 0 1 1 -> Scroll to bottom on key press (rxvt).\n * Ps = 1 0 3 4 -> Interpret \"meta\" key, sets eighth bit.\n * (enables the eightBitInput resource).\n * Ps = 1 0 3 5 -> Enable special modifiers for Alt and Num-\n * Lock keys. (This enables the numLock resource).\n * Ps = 1 0 3 6 -> Send ESC when Meta modifies a key. (This\n * enables the metaSendsEscape resource).\n * Ps = 1 0 3 7 -> Send DEL from the editing-keypad Delete\n * key.\n * Ps = 1 0 3 9 -> Send ESC when Alt modifies a key. (This\n * enables the altSendsEscape resource).\n * Ps = 1 0 4 0 -> Keep selection even if not highlighted.\n * (This enables the keepSelection resource).\n * Ps = 1 0 4 1 -> Use the CLIPBOARD selection. (This enables\n * the selectToClipboard resource).\n * Ps = 1 0 4 2 -> Enable Urgency window manager hint when\n * Control-G is received. (This enables the bellIsUrgent\n * resource).\n * Ps = 1 0 4 3 -> Enable raising of the window when Control-G\n * is received. (enables the popOnBell resource).\n * Ps = 1 0 4 7 -> Use Alternate Screen Buffer. (This may be\n * disabled by the titeInhibit resource).\n * Ps = 1 0 4 8 -> Save cursor as in DECSC. (This may be dis-\n * abled by the titeInhibit resource).\n * Ps = 1 0 4 9 -> Save cursor as in DECSC and use Alternate\n * Screen Buffer, clearing it first. (This may be disabled by\n * the titeInhibit resource). This combines the effects of the 1\n * 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based\n * applications rather than the 4 7 mode.\n * Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode.\n * Ps = 1 0 5 1 -> Set Sun function-key mode.\n * Ps = 1 0 5 2 -> Set HP function-key mode.\n * Ps = 1 0 5 3 -> Set SCO function-key mode.\n * Ps = 1 0 6 0 -> Set legacy keyboard emulation (X11R6).\n * Ps = 1 0 6 1 -> Set VT220 keyboard emulation.\n * Ps = 2 0 0 4 -> Set bracketed paste mode.\n * Modes:\n * http: *vt100.net/docs/vt220-rm/chapter4.html\n */\n public setMode(params: number[]): void {\n if (params.length > 1) {\n for (let i = 0; i < params.length; i++) {\n this.setMode([params[i]]);\n }\n\n return;\n }\n\n if (!this._terminal.prefix) {\n switch (params[0]) {\n case 4:\n this._terminal.insertMode = true;\n break;\n case 20:\n // this._t.convertEol = true;\n break;\n }\n } else if (this._terminal.prefix === '?') {\n switch (params[0]) {\n case 1:\n this._terminal.applicationCursor = true;\n break;\n case 2:\n this._terminal.setgCharset(0, DEFAULT_CHARSET);\n this._terminal.setgCharset(1, DEFAULT_CHARSET);\n this._terminal.setgCharset(2, DEFAULT_CHARSET);\n this._terminal.setgCharset(3, DEFAULT_CHARSET);\n // set VT100 mode here\n break;\n case 3: // 132 col mode\n this._terminal.savedCols = this._terminal.cols;\n this._terminal.resize(132, this._terminal.rows);\n break;\n case 6:\n this._terminal.originMode = true;\n break;\n case 7:\n this._terminal.wraparoundMode = true;\n break;\n case 12:\n // this.cursorBlink = true;\n break;\n case 66:\n this._terminal.log('Serial port requested application keypad.');\n this._terminal.applicationKeypad = true;\n this._terminal.viewport.syncScrollArea();\n break;\n case 9: // X10 Mouse\n // no release, no motion, no wheel, no modifiers.\n case 1000: // vt200 mouse\n // no motion.\n // no modifiers, except control on the wheel.\n case 1002: // button event mouse\n case 1003: // any event mouse\n // any event - sends motion events,\n // even if there is no button held down.\n\n // TODO: Why are params[0] compares nested within a switch for params[0]?\n\n this._terminal.x10Mouse = params[0] === 9;\n this._terminal.vt200Mouse = params[0] === 1000;\n this._terminal.normalMouse = params[0] > 1000;\n this._terminal.mouseEvents = true;\n this._terminal.element.classList.add('enable-mouse-events');\n this._terminal.selectionManager.disable();\n this._terminal.log('Binding to mouse events.');\n break;\n case 1004: // send focusin/focusout events\n // focusin: ^[[I\n // focusout: ^[[O\n this._terminal.sendFocus = true;\n break;\n case 1005: // utf8 ext mode mouse\n this._terminal.utfMouse = true;\n // for wide terminals\n // simply encodes large values as utf8 characters\n break;\n case 1006: // sgr ext mode mouse\n this._terminal.sgrMouse = true;\n // for wide terminals\n // does not add 32 to fields\n // press: ^[[ Keyboard Action Mode (AM).\n * Ps = 4 -> Replace Mode (IRM).\n * Ps = 1 2 -> Send/receive (SRM).\n * Ps = 2 0 -> Normal Linefeed (LNM).\n * CSI ? Pm l\n * DEC Private Mode Reset (DECRST).\n * Ps = 1 -> Normal Cursor Keys (DECCKM).\n * Ps = 2 -> Designate VT52 mode (DECANM).\n * Ps = 3 -> 80 Column Mode (DECCOLM).\n * Ps = 4 -> Jump (Fast) Scroll (DECSCLM).\n * Ps = 5 -> Normal Video (DECSCNM).\n * Ps = 6 -> Normal Cursor Mode (DECOM).\n * Ps = 7 -> No Wraparound Mode (DECAWM).\n * Ps = 8 -> No Auto-repeat Keys (DECARM).\n * Ps = 9 -> Don't send Mouse X & Y on button press.\n * Ps = 1 0 -> Hide toolbar (rxvt).\n * Ps = 1 2 -> Stop Blinking Cursor (att610).\n * Ps = 1 8 -> Don't print form feed (DECPFF).\n * Ps = 1 9 -> Limit print to scrolling region (DECPEX).\n * Ps = 2 5 -> Hide Cursor (DECTCEM).\n * Ps = 3 0 -> Don't show scrollbar (rxvt).\n * Ps = 3 5 -> Disable font-shifting functions (rxvt).\n * Ps = 4 0 -> Disallow 80 -> 132 Mode.\n * Ps = 4 1 -> No more(1) fix (see curses resource).\n * Ps = 4 2 -> Disable Nation Replacement Character sets (DEC-\n * NRCM).\n * Ps = 4 4 -> Turn Off Margin Bell.\n * Ps = 4 5 -> No Reverse-wraparound Mode.\n * Ps = 4 6 -> Stop Logging. (This is normally disabled by a\n * compile-time option).\n * Ps = 4 7 -> Use Normal Screen Buffer.\n * Ps = 6 6 -> Numeric keypad (DECNKM).\n * Ps = 6 7 -> Backarrow key sends delete (DECBKM).\n * Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and\n * release. See the section Mouse Tracking.\n * Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking.\n * Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking.\n * Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking.\n * Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events.\n * Ps = 1 0 0 5 -> Disable Extended Mouse Mode.\n * Ps = 1 0 1 0 -> Don't scroll to bottom on tty output\n * (rxvt).\n * Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt).\n * Ps = 1 0 3 4 -> Don't interpret \"meta\" key. (This disables\n * the eightBitInput resource).\n * Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num-\n * Lock keys. (This disables the numLock resource).\n * Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key.\n * (This disables the metaSendsEscape resource).\n * Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad\n * Delete key.\n * Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key.\n * (This disables the altSendsEscape resource).\n * Ps = 1 0 4 0 -> Do not keep selection when not highlighted.\n * (This disables the keepSelection resource).\n * Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables\n * the selectToClipboard resource).\n * Ps = 1 0 4 2 -> Disable Urgency window manager hint when\n * Control-G is received. (This disables the bellIsUrgent\n * resource).\n * Ps = 1 0 4 3 -> Disable raising of the window when Control-\n * G is received. (This disables the popOnBell resource).\n * Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen\n * first if in the Alternate Screen. (This may be disabled by\n * the titeInhibit resource).\n * Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be\n * disabled by the titeInhibit resource).\n * Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor\n * as in DECRC. (This may be disabled by the titeInhibit\n * resource). This combines the effects of the 1 0 4 7 and 1 0\n * 4 8 modes. Use this with terminfo-based applications rather\n * than the 4 7 mode.\n * Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode.\n * Ps = 1 0 5 1 -> Reset Sun function-key mode.\n * Ps = 1 0 5 2 -> Reset HP function-key mode.\n * Ps = 1 0 5 3 -> Reset SCO function-key mode.\n * Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6).\n * Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style.\n * Ps = 2 0 0 4 -> Reset bracketed paste mode.\n */\n public resetMode(params: number[]): void {\n if (params.length > 1) {\n for (let i = 0; i < params.length; i++) {\n this.resetMode([params[i]]);\n }\n\n return;\n }\n\n if (!this._terminal.prefix) {\n switch (params[0]) {\n case 4:\n this._terminal.insertMode = false;\n break;\n case 20:\n // this._t.convertEol = false;\n break;\n }\n } else if (this._terminal.prefix === '?') {\n switch (params[0]) {\n case 1:\n this._terminal.applicationCursor = false;\n break;\n case 3:\n if (this._terminal.cols === 132 && this._terminal.savedCols) {\n this._terminal.resize(this._terminal.savedCols, this._terminal.rows);\n }\n delete this._terminal.savedCols;\n break;\n case 6:\n this._terminal.originMode = false;\n break;\n case 7:\n this._terminal.wraparoundMode = false;\n break;\n case 12:\n // this.cursorBlink = false;\n break;\n case 66:\n this._terminal.log('Switching back to normal keypad.');\n this._terminal.applicationKeypad = false;\n this._terminal.viewport.syncScrollArea();\n break;\n case 9: // X10 Mouse\n case 1000: // vt200 mouse\n case 1002: // button event mouse\n case 1003: // any event mouse\n this._terminal.x10Mouse = false;\n this._terminal.vt200Mouse = false;\n this._terminal.normalMouse = false;\n this._terminal.mouseEvents = false;\n this._terminal.element.classList.remove('enable-mouse-events');\n this._terminal.selectionManager.enable();\n break;\n case 1004: // send focusin/focusout events\n this._terminal.sendFocus = false;\n break;\n case 1005: // utf8 ext mode mouse\n this._terminal.utfMouse = false;\n break;\n case 1006: // sgr ext mode mouse\n this._terminal.sgrMouse = false;\n break;\n case 1015: // urxvt ext mode mouse\n this._terminal.urxvtMouse = false;\n break;\n case 25: // hide cursor\n this._terminal.cursorHidden = true;\n break;\n case 1049: // alt screen buffer cursor\n // FALL-THROUGH\n case 47: // normal screen buffer\n case 1047: // normal screen buffer - clearing it first\n // Ensure the selection manager has the correct buffer\n this._terminal.buffers.activateNormalBuffer();\n // TODO: Not sure if we need to save/restore after switching the buffer\n // if (params[0] === 1049) {\n // this.restoreCursor(params);\n // }\n this._terminal.selectionManager.setBuffer(this._terminal.buffer);\n this._terminal.refresh(0, this._terminal.rows - 1);\n this._terminal.viewport.syncScrollArea();\n this._terminal.showCursor();\n break;\n case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)\n this._terminal.bracketedPasteMode = false;\n break;\n }\n }\n }\n\n /**\n * CSI Pm m Character Attributes (SGR).\n * Ps = 0 -> Normal (default).\n * Ps = 1 -> Bold.\n * Ps = 2 -> Faint, decreased intensity (ISO 6429).\n * Ps = 4 -> Underlined.\n * Ps = 5 -> Blink (appears as Bold).\n * Ps = 7 -> Inverse.\n * Ps = 8 -> Invisible, i.e., hidden (VT300).\n * Ps = 2 2 -> Normal (neither bold nor faint).\n * Ps = 2 4 -> Not underlined.\n * Ps = 2 5 -> Steady (not blinking).\n * Ps = 2 7 -> Positive (not inverse).\n * Ps = 2 8 -> Visible, i.e., not hidden (VT300).\n * Ps = 3 0 -> Set foreground color to Black.\n * Ps = 3 1 -> Set foreground color to Red.\n * Ps = 3 2 -> Set foreground color to Green.\n * Ps = 3 3 -> Set foreground color to Yellow.\n * Ps = 3 4 -> Set foreground color to Blue.\n * Ps = 3 5 -> Set foreground color to Magenta.\n * Ps = 3 6 -> Set foreground color to Cyan.\n * Ps = 3 7 -> Set foreground color to White.\n * Ps = 3 9 -> Set foreground color to default (original).\n * Ps = 4 0 -> Set background color to Black.\n * Ps = 4 1 -> Set background color to Red.\n * Ps = 4 2 -> Set background color to Green.\n * Ps = 4 3 -> Set background color to Yellow.\n * Ps = 4 4 -> Set background color to Blue.\n * Ps = 4 5 -> Set background color to Magenta.\n * Ps = 4 6 -> Set background color to Cyan.\n * Ps = 4 7 -> Set background color to White.\n * Ps = 4 9 -> Set background color to default (original).\n *\n * If 16-color support is compiled, the following apply. Assume\n * that xterm's resources are set so that the ISO color codes are\n * the first 8 of a set of 16. Then the aixterm colors are the\n * bright versions of the ISO colors:\n * Ps = 9 0 -> Set foreground color to Black.\n * Ps = 9 1 -> Set foreground color to Red.\n * Ps = 9 2 -> Set foreground color to Green.\n * Ps = 9 3 -> Set foreground color to Yellow.\n * Ps = 9 4 -> Set foreground color to Blue.\n * Ps = 9 5 -> Set foreground color to Magenta.\n * Ps = 9 6 -> Set foreground color to Cyan.\n * Ps = 9 7 -> Set foreground color to White.\n * Ps = 1 0 0 -> Set background color to Black.\n * Ps = 1 0 1 -> Set background color to Red.\n * Ps = 1 0 2 -> Set background color to Green.\n * Ps = 1 0 3 -> Set background color to Yellow.\n * Ps = 1 0 4 -> Set background color to Blue.\n * Ps = 1 0 5 -> Set background color to Magenta.\n * Ps = 1 0 6 -> Set background color to Cyan.\n * Ps = 1 0 7 -> Set background color to White.\n *\n * If xterm is compiled with the 16-color support disabled, it\n * supports the following, from rxvt:\n * Ps = 1 0 0 -> Set foreground and background color to\n * default.\n *\n * If 88- or 256-color support is compiled, the following apply.\n * Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second\n * Ps.\n * Ps = 4 8 ; 5 ; Ps -> Set background color to the second\n * Ps.\n */\n public charAttributes(params: number[]): void {\n // Optimize a single SGR0.\n if (params.length === 1 && params[0] === 0) {\n this._terminal.curAttr = this._terminal.defAttr;\n return;\n }\n\n const l = params.length;\n let flags = this._terminal.curAttr >> 18;\n let fg = (this._terminal.curAttr >> 9) & 0x1ff;\n let bg = this._terminal.curAttr & 0x1ff;\n let p;\n\n for (let i = 0; i < l; i++) {\n p = params[i];\n if (p >= 30 && p <= 37) {\n // fg color 8\n fg = p - 30;\n } else if (p >= 40 && p <= 47) {\n // bg color 8\n bg = p - 40;\n } else if (p >= 90 && p <= 97) {\n // fg color 16\n p += 8;\n fg = p - 90;\n } else if (p >= 100 && p <= 107) {\n // bg color 16\n p += 8;\n bg = p - 100;\n } else if (p === 0) {\n // default\n flags = this._terminal.defAttr >> 18;\n fg = (this._terminal.defAttr >> 9) & 0x1ff;\n bg = this._terminal.defAttr & 0x1ff;\n // flags = 0;\n // fg = 0x1ff;\n // bg = 0x1ff;\n } else if (p === 1) {\n // bold text\n flags |= FLAGS.BOLD;\n } else if (p === 4) {\n // underlined text\n flags |= FLAGS.UNDERLINE;\n } else if (p === 5) {\n // blink\n flags |= FLAGS.BLINK;\n } else if (p === 7) {\n // inverse and positive\n // test with: echo -e '\\e[31m\\e[42mhello\\e[7mworld\\e[27mhi\\e[m'\n flags |= FLAGS.INVERSE;\n } else if (p === 8) {\n // invisible\n flags |= FLAGS.INVISIBLE;\n } else if (p === 2) {\n // dimmed text\n flags |= FLAGS.DIM;\n } else if (p === 22) {\n // not bold nor faint\n flags &= ~FLAGS.BOLD;\n flags &= ~FLAGS.DIM;\n } else if (p === 24) {\n // not underlined\n flags &= ~FLAGS.UNDERLINE;\n } else if (p === 25) {\n // not blink\n flags &= ~FLAGS.BLINK;\n } else if (p === 27) {\n // not inverse\n flags &= ~FLAGS.INVERSE;\n } else if (p === 28) {\n // not invisible\n flags &= ~FLAGS.INVISIBLE;\n } else if (p === 39) {\n // reset fg\n fg = (this._terminal.defAttr >> 9) & 0x1ff;\n } else if (p === 49) {\n // reset bg\n bg = this._terminal.defAttr & 0x1ff;\n } else if (p === 38) {\n // fg color 256\n if (params[i + 1] === 2) {\n i += 2;\n fg = this._terminal.matchColor(\n params[i] & 0xff,\n params[i + 1] & 0xff,\n params[i + 2] & 0xff);\n if (fg === -1) fg = 0x1ff;\n i += 2;\n } else if (params[i + 1] === 5) {\n i += 2;\n p = params[i] & 0xff;\n fg = p;\n }\n } else if (p === 48) {\n // bg color 256\n if (params[i + 1] === 2) {\n i += 2;\n bg = this._terminal.matchColor(\n params[i] & 0xff,\n params[i + 1] & 0xff,\n params[i + 2] & 0xff);\n if (bg === -1) bg = 0x1ff;\n i += 2;\n } else if (params[i + 1] === 5) {\n i += 2;\n p = params[i] & 0xff;\n bg = p;\n }\n } else if (p === 100) {\n // reset fg/bg\n fg = (this._terminal.defAttr >> 9) & 0x1ff;\n bg = this._terminal.defAttr & 0x1ff;\n } else {\n this._terminal.error('Unknown SGR attribute: %d.', p);\n }\n }\n\n this._terminal.curAttr = (flags << 18) | (fg << 9) | bg;\n }\n\n /**\n * CSI Ps n Device Status Report (DSR).\n * Ps = 5 -> Status Report. Result (``OK'') is\n * CSI 0 n\n * Ps = 6 -> Report Cursor Position (CPR) [row;column].\n * Result is\n * CSI r ; c R\n * CSI ? Ps n\n * Device Status Report (DSR, DEC-specific).\n * Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI\n * ? r ; c R (assumes page is zero).\n * Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready).\n * or CSI ? 1 1 n (not ready).\n * Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked)\n * or CSI ? 2 1 n (locked).\n * Ps = 2 6 -> Report Keyboard status as\n * CSI ? 2 7 ; 1 ; 0 ; 0 n (North American).\n * The last two parameters apply to VT400 & up, and denote key-\n * board ready and LK01 respectively.\n * Ps = 5 3 -> Report Locator status as\n * CSI ? 5 3 n Locator available, if compiled-in, or\n * CSI ? 5 0 n No Locator, if not.\n */\n public deviceStatus(params: number[]): void {\n if (!this._terminal.prefix) {\n switch (params[0]) {\n case 5:\n // status report\n this._terminal.send(C0.ESC + '[0n');\n break;\n case 6:\n // cursor position\n this._terminal.send(C0.ESC + '['\n + (this._terminal.buffer.y + 1)\n + ';'\n + (this._terminal.buffer.x + 1)\n + 'R');\n break;\n }\n } else if (this._terminal.prefix === '?') {\n // modern xterm doesnt seem to\n // respond to any of these except ?6, 6, and 5\n switch (params[0]) {\n case 6:\n // cursor position\n this._terminal.send(C0.ESC + '[?'\n + (this._terminal.buffer.y + 1)\n + ';'\n + (this._terminal.buffer.x + 1)\n + 'R');\n break;\n case 15:\n // no printer\n // this.send(C0.ESC + '[?11n');\n break;\n case 25:\n // dont support user defined keys\n // this.send(C0.ESC + '[?21n');\n break;\n case 26:\n // north american keyboard\n // this.send(C0.ESC + '[?27;1;0;0n');\n break;\n case 53:\n // no dec locator/mouse\n // this.send(C0.ESC + '[?50n');\n break;\n }\n }\n }\n\n /**\n * CSI ! p Soft terminal reset (DECSTR).\n * http://vt100.net/docs/vt220-rm/table4-10.html\n */\n public softReset(params: number[]): void {\n this._terminal.cursorHidden = false;\n this._terminal.insertMode = false;\n this._terminal.originMode = false;\n this._terminal.wraparoundMode = true; // defaults: xterm - true, vt100 - false\n this._terminal.applicationKeypad = false; // ?\n this._terminal.viewport.syncScrollArea();\n this._terminal.applicationCursor = false;\n this._terminal.buffer.scrollTop = 0;\n this._terminal.buffer.scrollBottom = this._terminal.rows - 1;\n this._terminal.curAttr = this._terminal.defAttr;\n this._terminal.buffer.x = this._terminal.buffer.y = 0; // ?\n this._terminal.charset = null;\n this._terminal.glevel = 0; // ??\n this._terminal.charsets = [null]; // ??\n }\n\n /**\n * CSI Ps SP q Set cursor style (DECSCUSR, VT520).\n * Ps = 0 -> blinking block.\n * Ps = 1 -> blinking block (default).\n * Ps = 2 -> steady block.\n * Ps = 3 -> blinking underline.\n * Ps = 4 -> steady underline.\n * Ps = 5 -> blinking bar (xterm).\n * Ps = 6 -> steady bar (xterm).\n */\n public setCursorStyle(params?: number[]): void {\n const param = params[0] < 1 ? 1 : params[0];\n switch (param) {\n case 1:\n case 2:\n this._terminal.setOption('cursorStyle', 'block');\n break;\n case 3:\n case 4:\n this._terminal.setOption('cursorStyle', 'underline');\n break;\n case 5:\n case 6:\n this._terminal.setOption('cursorStyle', 'bar');\n break;\n }\n const isBlinking = param % 2 === 1;\n this._terminal.setOption('cursorBlink', isBlinking);\n }\n\n /**\n * CSI Ps ; Ps r\n * Set Scrolling Region [top;bottom] (default = full size of win-\n * dow) (DECSTBM).\n * CSI ? Pm r\n */\n public setScrollRegion(params: number[]): void {\n if (this._terminal.prefix) return;\n this._terminal.buffer.scrollTop = (params[0] || 1) - 1;\n this._terminal.buffer.scrollBottom = (params[1] && params[1] <= this._terminal.rows ? params[1] : this._terminal.rows) - 1;\n this._terminal.buffer.x = 0;\n this._terminal.buffer.y = 0;\n }\n\n\n /**\n * CSI s\n * Save cursor (ANSI.SYS).\n */\n public saveCursor(params: number[]): void {\n this._terminal.buffer.savedX = this._terminal.buffer.x;\n this._terminal.buffer.savedY = this._terminal.buffer.y;\n }\n\n\n /**\n * CSI u\n * Restore cursor (ANSI.SYS).\n */\n public restoreCursor(params: number[]): void {\n this._terminal.buffer.x = this._terminal.buffer.savedX || 0;\n this._terminal.buffer.y = this._terminal.buffer.savedY || 0;\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IEventEmitter, IListenerType } from './Interfaces';\n\nexport class EventEmitter implements IEventEmitter {\n private _events: {[type: string]: IListenerType[]};\n\n constructor() {\n // Restore the previous events if available, this will happen if the\n // constructor is called multiple times on the same object (terminal reset).\n this._events = this._events || {};\n }\n\n public on(type: string, listener: IListenerType): void {\n this._events[type] = this._events[type] || [];\n this._events[type].push(listener);\n }\n\n public off(type: string, listener: IListenerType): void {\n if (!this._events[type]) {\n return;\n }\n\n let obj = this._events[type];\n let i = obj.length;\n\n while (i--) {\n if (obj[i] === listener || obj[i].listener === listener) {\n obj.splice(i, 1);\n return;\n }\n }\n }\n\n public removeAllListeners(type: string): void {\n if (this._events[type]) {\n delete this._events[type];\n }\n }\n\n public once(type: string, listener: IListenerType): void {\n function on(): void {\n let args = Array.prototype.slice.call(arguments);\n this.off(type, on);\n listener.apply(this, args);\n }\n (on).listener = listener;\n this.on(type, on);\n }\n\n public emit(type: string, ...args: any[]): void {\n if (!this._events[type]) {\n return;\n }\n let obj = this._events[type];\n for (let i = 0; i < obj.length; i++) {\n obj[i].apply(this, args);\n }\n }\n\n public listeners(type: string): IListenerType[] {\n return this._events[type] || [];\n }\n\n protected destroy(): void {\n this._events = {};\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\n/**\n * C0 control codes\n * See = https://en.wikipedia.org/wiki/C0_and_C1_control_codes\n */\nexport namespace C0 {\n /** Null (Caret = ^@, C = \\0) */\n export const NUL = '\\x00';\n /** Start of Heading (Caret = ^A) */\n export const SOH = '\\x01';\n /** Start of Text (Caret = ^B) */\n export const STX = '\\x02';\n /** End of Text (Caret = ^C) */\n export const ETX = '\\x03';\n /** End of Transmission (Caret = ^D) */\n export const EOT = '\\x04';\n /** Enquiry (Caret = ^E) */\n export const ENQ = '\\x05';\n /** Acknowledge (Caret = ^F) */\n export const ACK = '\\x06';\n /** Bell (Caret = ^G, C = \\a) */\n export const BEL = '\\x07';\n /** Backspace (Caret = ^H, C = \\b) */\n export const BS = '\\x08';\n /** Character Tabulation, Horizontal Tabulation (Caret = ^I, C = \\t) */\n export const HT = '\\x09';\n /** Line Feed (Caret = ^J, C = \\n) */\n export const LF = '\\x0a';\n /** Line Tabulation, Vertical Tabulation (Caret = ^K, C = \\v) */\n export const VT = '\\x0b';\n /** Form Feed (Caret = ^L, C = \\f) */\n export const FF = '\\x0c';\n /** Carriage Return (Caret = ^M, C = \\r) */\n export const CR = '\\x0d';\n /** Shift Out (Caret = ^N) */\n export const SO = '\\x0e';\n /** Shift In (Caret = ^O) */\n export const SI = '\\x0f';\n /** Data Link Escape (Caret = ^P) */\n export const DLE = '\\x10';\n /** Device Control One (XON) (Caret = ^Q) */\n export const DC1 = '\\x11';\n /** Device Control Two (Caret = ^R) */\n export const DC2 = '\\x12';\n /** Device Control Three (XOFF) (Caret = ^S) */\n export const DC3 = '\\x13';\n /** Device Control Four (Caret = ^T) */\n export const DC4 = '\\x14';\n /** Negative Acknowledge (Caret = ^U) */\n export const NAK = '\\x15';\n /** Synchronous Idle (Caret = ^V) */\n export const SYN = '\\x16';\n /** End of Transmission Block (Caret = ^W) */\n export const ETB = '\\x17';\n /** Cancel (Caret = ^X) */\n export const CAN = '\\x18';\n /** End of Medium (Caret = ^Y) */\n export const EM = '\\x19';\n /** Substitute (Caret = ^Z) */\n export const SUB = '\\x1a';\n /** Escape (Caret = ^[, C = \\e) */\n export const ESC = '\\x1b';\n /** File Separator (Caret = ^\\) */\n export const FS = '\\x1c';\n /** Group Separator (Caret = ^]) */\n export const GS = '\\x1d';\n /** Record Separator (Caret = ^^) */\n export const RS = '\\x1e';\n /** Unit Separator (Caret = ^_) */\n export const US = '\\x1f';\n /** Space */\n export const SP = '\\x20';\n /** Delete (Caret = ^?) */\n export const DEL = '\\x7f';\n};\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal } from './Interfaces';\n\ninterface IPosition {\n start: number;\n end: number;\n}\n\n/**\n * Encapsulates the logic for handling compositionstart, compositionupdate and compositionend\n * events, displaying the in-progress composition to the UI and forwarding the final composition\n * to the handler.\n */\nexport class CompositionHelper {\n /**\n * Whether input composition is currently happening, eg. via a mobile keyboard, speech input or\n * IME. This variable determines whether the compositionText should be displayed on the UI.\n */\n private isComposing: boolean;\n\n /**\n * The position within the input textarea's value of the current composition.\n */\n private compositionPosition: IPosition;\n\n /**\n * Whether a composition is in the process of being sent, setting this to false will cancel any\n * in-progress composition.\n */\n private isSendingComposition: boolean;\n\n /**\n * Creates a new CompositionHelper.\n * @param textarea The textarea that xterm uses for input.\n * @param compositionView The element to display the in-progress composition in.\n * @param terminal The Terminal to forward the finished composition to.\n */\n constructor(\n private textarea: HTMLTextAreaElement,\n private compositionView: HTMLElement,\n private terminal: ITerminal\n ) {\n this.isComposing = false;\n this.isSendingComposition = false;\n this.compositionPosition = { start: null, end: null };\n }\n\n /**\n * Handles the compositionstart event, activating the composition view.\n */\n public compositionstart(): void {\n this.isComposing = true;\n this.compositionPosition.start = this.textarea.value.length;\n this.compositionView.textContent = '';\n this.compositionView.classList.add('active');\n }\n\n /**\n * Handles the compositionupdate event, updating the composition view.\n * @param {CompositionEvent} ev The event.\n */\n public compositionupdate(ev: CompositionEvent): void {\n this.compositionView.textContent = ev.data;\n this.updateCompositionElements();\n setTimeout(() => {\n this.compositionPosition.end = this.textarea.value.length;\n }, 0);\n }\n\n /**\n * Handles the compositionend event, hiding the composition view and sending the composition to\n * the handler.\n */\n public compositionend(): void {\n this.finalizeComposition(true);\n }\n\n /**\n * Handles the keydown event, routing any necessary events to the CompositionHelper functions.\n * @param ev The keydown event.\n * @return Whether the Terminal should continue processing the keydown event.\n */\n public keydown(ev: KeyboardEvent): boolean {\n if (this.isComposing || this.isSendingComposition) {\n if (ev.keyCode === 229) {\n // Continue composing if the keyCode is the \"composition character\"\n return false;\n } else if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) {\n // Continue composing if the keyCode is a modifier key\n return false;\n } else {\n // Finish composition immediately. This is mainly here for the case where enter is\n // pressed and the handler needs to be triggered before the command is executed.\n this.finalizeComposition(false);\n }\n }\n\n if (ev.keyCode === 229) {\n // If the \"composition character\" is used but gets to this point it means a non-composition\n // character (eg. numbers and punctuation) was pressed when the IME was active.\n this.handleAnyTextareaChanges();\n return false;\n }\n\n return true;\n }\n\n /**\n * Finalizes the composition, resuming regular input actions. This is called when a composition\n * is ending.\n * @param waitForPropogation Whether to wait for events to propogate before sending\n * the input. This should be false if a non-composition keystroke is entered before the\n * compositionend event is triggered, such as enter, so that the composition is send before\n * the command is executed.\n */\n private finalizeComposition(waitForPropogation: boolean): void {\n this.compositionView.classList.remove('active');\n this.isComposing = false;\n this.clearTextareaPosition();\n\n if (!waitForPropogation) {\n // Cancel any delayed composition send requests and send the input immediately.\n this.isSendingComposition = false;\n const input = this.textarea.value.substring(this.compositionPosition.start, this.compositionPosition.end);\n this.terminal.handler(input);\n } else {\n // Make a deep copy of the composition position here as a new compositionstart event may\n // fire before the setTimeout executes.\n const currentCompositionPosition = {\n start: this.compositionPosition.start,\n end: this.compositionPosition.end,\n };\n\n // Since composition* events happen before the changes take place in the textarea on most\n // browsers, use a setTimeout with 0ms time to allow the native compositionend event to\n // complete. This ensures the correct character is retrieved, this solution was used\n // because:\n // - The compositionend event's data property is unreliable, at least on Chromium\n // - The last compositionupdate event's data property does not always accurately describe\n // the character, a counter example being Korean where an ending consonsant can move to\n // the following character if the following input is a vowel.\n this.isSendingComposition = true;\n setTimeout(() => {\n // Ensure that the input has not already been sent\n if (this.isSendingComposition) {\n this.isSendingComposition = false;\n let input;\n if (this.isComposing) {\n // Use the end position to get the string if a new composition has started.\n input = this.textarea.value.substring(currentCompositionPosition.start, currentCompositionPosition.end);\n } else {\n // Don't use the end position here in order to pick up any characters after the\n // composition has finished, for example when typing a non-composition character\n // (eg. 2) after a composition character.\n input = this.textarea.value.substring(currentCompositionPosition.start);\n }\n this.terminal.handler(input);\n }\n }, 0);\n }\n }\n\n /**\n * Apply any changes made to the textarea after the current event chain is allowed to complete.\n * This should be called when not currently composing but a keydown event with the \"composition\n * character\" (229) is triggered, in order to allow non-composition text to be entered when an\n * IME is active.\n */\n private handleAnyTextareaChanges(): void {\n const oldValue = this.textarea.value;\n setTimeout(() => {\n // Ignore if a composition has started since the timeout\n if (!this.isComposing) {\n const newValue = this.textarea.value;\n const diff = newValue.replace(oldValue, '');\n if (diff.length > 0) {\n this.terminal.handler(diff);\n }\n }\n }, 0);\n }\n\n /**\n * Positions the composition view on top of the cursor and the textarea just below it (so the\n * IME helper dialog is positioned correctly).\n * @param dontRecurse Whether to use setTimeout to recursively trigger another update, this is\n * necessary as the IME events across browsers are not consistently triggered.\n */\n public updateCompositionElements(dontRecurse?: boolean): void {\n if (!this.isComposing) {\n return;\n }\n\n if (this.terminal.buffer.isCursorInViewport) {\n const cellHeight = Math.ceil(this.terminal.charMeasure.height * this.terminal.options.lineHeight);\n const cursorTop = this.terminal.buffer.y * cellHeight;\n const cursorLeft = this.terminal.buffer.x * this.terminal.charMeasure.width;\n\n this.compositionView.style.left = cursorLeft + 'px';\n this.compositionView.style.top = cursorTop + 'px';\n this.compositionView.style.height = cellHeight + 'px';\n this.compositionView.style.lineHeight = cellHeight + 'px';\n // Sync the textarea to the exact position of the composition view so the IME knows where the\n // text is.\n const compositionViewBounds = this.compositionView.getBoundingClientRect();\n this.textarea.style.left = cursorLeft + 'px';\n this.textarea.style.top = cursorTop + 'px';\n this.textarea.style.width = compositionViewBounds.width + 'px';\n this.textarea.style.height = compositionViewBounds.height + 'px';\n this.textarea.style.lineHeight = compositionViewBounds.height + 'px';\n }\n\n if (!dontRecurse) {\n setTimeout(() => this.updateCompositionElements(true), 0);\n }\n };\n\n /**\n * Clears the textarea's position so that the cursor does not blink on IE.\n * @private\n */\n private clearTextareaPosition(): void {\n this.textarea.style.left = '';\n this.textarea.style.top = '';\n };\n}\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { Charset } from './Types';\n\n/**\n * The character sets supported by the terminal. These enable several languages\n * to be represented within the terminal with only 8-bit encoding. See ISO 2022\n * for a discussion on character sets. Only VT100 character sets are supported.\n */\nexport const CHARSETS: { [key: string]: Charset } = {};\n\n/**\n * The default character set, US.\n */\nexport const DEFAULT_CHARSET: Charset = CHARSETS['B'];\n\n/**\n * DEC Special Character and Line Drawing Set.\n * Reference: http://vt100.net/docs/vt102-ug/table5-13.html\n * A lot of curses apps use this if they see TERM=xterm.\n * testing: echo -e '\\e(0a\\e(B'\n * The xterm output sometimes seems to conflict with the\n * reference above. xterm seems in line with the reference\n * when running vttest however.\n * The table below now uses xterm's output from vttest.\n */\nCHARSETS['0'] = {\n '`': '\\u25c6', // '◆'\n 'a': '\\u2592', // '▒'\n 'b': '\\u0009', // '\\t'\n 'c': '\\u000c', // '\\f'\n 'd': '\\u000d', // '\\r'\n 'e': '\\u000a', // '\\n'\n 'f': '\\u00b0', // '°'\n 'g': '\\u00b1', // '±'\n 'h': '\\u2424', // '\\u2424' (NL)\n 'i': '\\u000b', // '\\v'\n 'j': '\\u2518', // '┘'\n 'k': '\\u2510', // '┐'\n 'l': '\\u250c', // '┌'\n 'm': '\\u2514', // '└'\n 'n': '\\u253c', // '┼'\n 'o': '\\u23ba', // '⎺'\n 'p': '\\u23bb', // '⎻'\n 'q': '\\u2500', // '─'\n 'r': '\\u23bc', // '⎼'\n 's': '\\u23bd', // '⎽'\n 't': '\\u251c', // '├'\n 'u': '\\u2524', // '┤'\n 'v': '\\u2534', // '┴'\n 'w': '\\u252c', // '┬'\n 'x': '\\u2502', // '│'\n 'y': '\\u2264', // '≤'\n 'z': '\\u2265', // '≥'\n '{': '\\u03c0', // 'π'\n '|': '\\u2260', // '≠'\n '}': '\\u00a3', // '£'\n '~': '\\u00b7' // '·'\n};\n\n/**\n * British character set\n * ESC (A\n * Reference: http://vt100.net/docs/vt220-rm/table2-5.html\n */\nCHARSETS['A'] = {\n '#': '£'\n};\n\n/**\n * United States character set\n * ESC (B\n */\nCHARSETS['B'] = null;\n\n/**\n * Dutch character set\n * ESC (4\n * Reference: http://vt100.net/docs/vt220-rm/table2-6.html\n */\nCHARSETS['4'] = {\n '#': '£',\n '@': '¾',\n '[': 'ij',\n '\\\\': '½',\n ']': '|',\n '{': '¨',\n '|': 'f',\n '}': '¼',\n '~': '´'\n};\n\n/**\n * Finnish character set\n * ESC (C or ESC (5\n * Reference: http://vt100.net/docs/vt220-rm/table2-7.html\n */\nCHARSETS['C'] =\nCHARSETS['5'] = {\n '[': 'Ä',\n '\\\\': 'Ö',\n ']': 'Å',\n '^': 'Ü',\n '`': 'é',\n '{': 'ä',\n '|': 'ö',\n '}': 'å',\n '~': 'ü'\n};\n\n/**\n * French character set\n * ESC (R\n * Reference: http://vt100.net/docs/vt220-rm/table2-8.html\n */\nCHARSETS['R'] = {\n '#': '£',\n '@': 'à',\n '[': '°',\n '\\\\': 'ç',\n ']': '§',\n '{': 'é',\n '|': 'ù',\n '}': 'è',\n '~': '¨'\n};\n\n/**\n * French Canadian character set\n * ESC (Q\n * Reference: http://vt100.net/docs/vt220-rm/table2-9.html\n */\nCHARSETS['Q'] = {\n '@': 'à',\n '[': 'â',\n '\\\\': 'ç',\n ']': 'ê',\n '^': 'î',\n '`': 'ô',\n '{': 'é',\n '|': 'ù',\n '}': 'è',\n '~': 'û'\n};\n\n/**\n * German character set\n * ESC (K\n * Reference: http://vt100.net/docs/vt220-rm/table2-10.html\n */\nCHARSETS['K'] = {\n '@': '§',\n '[': 'Ä',\n '\\\\': 'Ö',\n ']': 'Ü',\n '{': 'ä',\n '|': 'ö',\n '}': 'ü',\n '~': 'ß'\n};\n\n/**\n * Italian character set\n * ESC (Y\n * Reference: http://vt100.net/docs/vt220-rm/table2-11.html\n */\nCHARSETS['Y'] = {\n '#': '£',\n '@': '§',\n '[': '°',\n '\\\\': 'ç',\n ']': 'é',\n '`': 'ù',\n '{': 'à',\n '|': 'ò',\n '}': 'è',\n '~': 'ì'\n};\n\n/**\n * Norwegian/Danish character set\n * ESC (E or ESC (6\n * Reference: http://vt100.net/docs/vt220-rm/table2-12.html\n */\nCHARSETS['E'] =\nCHARSETS['6'] = {\n '@': 'Ä',\n '[': 'Æ',\n '\\\\': 'Ø',\n ']': 'Å',\n '^': 'Ü',\n '`': 'ä',\n '{': 'æ',\n '|': 'ø',\n '}': 'å',\n '~': 'ü'\n};\n\n/**\n * Spanish character set\n * ESC (Z\n * Reference: http://vt100.net/docs/vt220-rm/table2-13.html\n */\nCHARSETS['Z'] = {\n '#': '£',\n '@': '§',\n '[': '¡',\n '\\\\': 'Ñ',\n ']': '¿',\n '{': '°',\n '|': 'ñ',\n '}': 'ç'\n};\n\n/**\n * Swedish character set\n * ESC (H or ESC (7\n * Reference: http://vt100.net/docs/vt220-rm/table2-14.html\n */\nCHARSETS['H'] =\nCHARSETS['7'] = {\n '@': 'É',\n '[': 'Ä',\n '\\\\': 'Ö',\n ']': 'Å',\n '^': 'Ü',\n '`': 'é',\n '{': 'ä',\n '|': 'ö',\n '}': 'å',\n '~': 'ü'\n};\n\n/**\n * Swiss character set\n * ESC (=\n * Reference: http://vt100.net/docs/vt220-rm/table2-15.html\n */\nCHARSETS['='] = {\n '#': 'ù',\n '@': 'à',\n '[': 'é',\n '\\\\': 'ç',\n ']': 'ê',\n '^': 'î',\n '_': 'è',\n '`': 'ô',\n '{': 'ä',\n '|': 'ö',\n '}': 'ü',\n '~': 'û'\n};\n","/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nexport const wcwidth = (function(opts: {nul: number, control: number}): (ucs: number) => number {\n // extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c\n // combining characters\n const COMBINING_BMP = [\n [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489],\n [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2],\n [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603],\n [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670],\n [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED],\n [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A],\n [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902],\n [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D],\n [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981],\n [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD],\n [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C],\n [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D],\n [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC],\n [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD],\n [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C],\n [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D],\n [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0],\n [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48],\n [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC],\n [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD],\n [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D],\n [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6],\n [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E],\n [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC],\n [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35],\n [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E],\n [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97],\n [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030],\n [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039],\n [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F],\n [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753],\n [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD],\n [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD],\n [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922],\n [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B],\n [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34],\n [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42],\n [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF],\n [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063],\n [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F],\n [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B],\n [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F],\n [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB],\n ];\n const COMBINING_HIGH = [\n [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F],\n [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169],\n [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD],\n [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F],\n [0xE0100, 0xE01EF]\n ];\n // binary search\n function bisearch(ucs: number, data: number[][]): boolean {\n let min = 0;\n let max = data.length - 1;\n let mid;\n if (ucs < data[0][0] || ucs > data[max][1])\n return false;\n while (max >= min) {\n mid = (min + max) >> 1;\n if (ucs > data[mid][1])\n min = mid + 1;\n else if (ucs < data[mid][0])\n max = mid - 1;\n else\n return true;\n }\n return false;\n }\n function wcwidthBMP(ucs: number): number {\n // test for 8-bit control characters\n if (ucs === 0)\n return opts.nul;\n if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))\n return opts.control;\n // binary search in table of non-spacing characters\n if (bisearch(ucs, COMBINING_BMP))\n return 0;\n // if we arrive here, ucs is not a combining or C0/C1 control character\n if (isWideBMP(ucs)) {\n return 2;\n }\n return 1;\n }\n function isWideBMP(ucs: number): boolean {\n return (\n ucs >= 0x1100 && (\n ucs <= 0x115f || // Hangul Jamo init. consonants\n ucs === 0x2329 ||\n ucs === 0x232a ||\n (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs !== 0x303f) || // CJK..Yi\n (ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables\n (ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compat Ideographs\n (ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms\n (ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compat Forms\n (ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms\n (ucs >= 0xffe0 && ucs <= 0xffe6)));\n }\n function wcwidthHigh(ucs: number): 0 | 1 | 2 {\n if (bisearch(ucs, COMBINING_HIGH))\n return 0;\n if ((ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd)) {\n return 2;\n }\n return 1;\n }\n const control = opts.control | 0;\n let table: number[] | Uint32Array = null;\n function init_table(): number[] | Uint32Array {\n // lookup table for BMP\n const CODEPOINTS = 65536; // BMP holds 65536 codepoints\n const BITWIDTH = 2; // a codepoint can have a width of 0, 1 or 2\n const ITEMSIZE = 32; // using uint32_t\n const CONTAINERSIZE = CODEPOINTS * BITWIDTH / ITEMSIZE;\n const CODEPOINTS_PER_ITEM = ITEMSIZE / BITWIDTH;\n table = (typeof Uint32Array === 'undefined')\n ? new Array(CONTAINERSIZE)\n : new Uint32Array(CONTAINERSIZE);\n for (let i = 0; i < CONTAINERSIZE; ++i) {\n let num = 0;\n let pos = CODEPOINTS_PER_ITEM;\n while (pos--)\n num = (num << 2) | wcwidthBMP(CODEPOINTS_PER_ITEM * i + pos);\n table[i] = num;\n }\n return table;\n }\n // get width from lookup table\n // position in container : num / CODEPOINTS_PER_ITEM\n // ==> n = table[Math.floor(num / 16)]\n // ==> n = table[num >> 4]\n // 16 codepoints per number: FFEEDDCCBBAA99887766554433221100\n // position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH\n // ==> m = (n % 16) * 2\n // ==> m = (num & 15) << 1\n // right shift to position m\n // ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766\n // we are only interested in 2 LSBs, cut off higher bits\n // ==> n = n & 3 e.g. 000000000000000000000000000000XX\n return function (num: number): number {\n num = num | 0; // get asm.js like optimization under V8\n if (num < 32)\n return control | 0;\n if (num < 127)\n return 1;\n let t = table || init_table();\n if (num < 65536)\n return t[num >> 4] >> ((num & 15) << 1) & 3;\n // do a full search for high codepoints\n return wcwidthHigh(num);\n };\n})({nul: 0, control: 0}); // configurable options\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal, IBufferSet } from './Interfaces';\nimport { Buffer } from './Buffer';\nimport { EventEmitter } from './EventEmitter';\n\n/**\n * The BufferSet represents the set of two buffers used by xterm terminals (normal and alt) and\n * provides also utilities for working with them.\n */\nexport class BufferSet extends EventEmitter implements IBufferSet {\n private _normal: Buffer;\n private _alt: Buffer;\n private _activeBuffer: Buffer;\n\n /**\n * Create a new BufferSet for the given terminal.\n * @param {Terminal} terminal - The terminal the BufferSet will belong to\n */\n constructor(private _terminal: ITerminal) {\n super();\n this._normal = new Buffer(this._terminal, true);\n this._normal.fillViewportRows();\n\n // The alt buffer should never have scrollback.\n // See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer\n this._alt = new Buffer(this._terminal, false);\n this._activeBuffer = this._normal;\n\n this.setupTabStops();\n }\n\n /**\n * Returns the alt Buffer of the BufferSet\n * @returns {Buffer}\n */\n public get alt(): Buffer {\n return this._alt;\n }\n\n /**\n * Returns the normal Buffer of the BufferSet\n * @returns {Buffer}\n */\n public get active(): Buffer {\n return this._activeBuffer;\n }\n\n /**\n * Returns the currently active Buffer of the BufferSet\n * @returns {Buffer}\n */\n public get normal(): Buffer {\n return this._normal;\n }\n\n /**\n * Sets the normal Buffer of the BufferSet as its currently active Buffer\n */\n public activateNormalBuffer(): void {\n // The alt buffer should always be cleared when we switch to the normal\n // buffer. This frees up memory since the alt buffer should always be new\n // when activated.\n this._alt.clear();\n\n this._activeBuffer = this._normal;\n this.emit('activate', this._normal);\n }\n\n /**\n * Sets the alt Buffer of the BufferSet as its currently active Buffer\n */\n public activateAltBuffer(): void {\n // Since the alt buffer is always cleared when the normal buffer is\n // activated, we want to fill it when switching to it.\n this._alt.fillViewportRows();\n this._activeBuffer = this._alt;\n this.emit('activate', this._alt);\n }\n\n /**\n * Resizes both normal and alt buffers, adjusting their data accordingly.\n * @param newCols The new number of columns.\n * @param newRows The new number of rows.\n */\n public resize(newCols: number, newRows: number): void {\n this._normal.resize(newCols, newRows);\n this._alt.resize(newCols, newRows);\n }\n\n /**\n * Setup the tab stops.\n * @param i The index to start setting up tab stops from.\n */\n public setupTabStops(i?: number): void {\n this._normal.setupTabStops(i);\n this._alt.setupTabStops(i);\n }\n}\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { ITerminal, IBuffer } from './Interfaces';\nimport { CircularList } from './utils/CircularList';\nimport { LineData, CharData } from './Types';\n\nexport const CHAR_DATA_ATTR_INDEX = 0;\nexport const CHAR_DATA_CHAR_INDEX = 1;\nexport const CHAR_DATA_WIDTH_INDEX = 2;\nexport const CHAR_DATA_CODE_INDEX = 3;\nexport const MAX_BUFFER_SIZE = 4294967295; // 2^32 - 1\n\n/**\n * This class represents a terminal buffer (an internal state of the terminal), where the\n * following information is stored (in high-level):\n * - text content of this particular buffer\n * - cursor position\n * - scroll position\n */\nexport class Buffer implements IBuffer {\n private _lines: CircularList;\n\n public ydisp: number;\n public ybase: number;\n public y: number;\n public x: number;\n public scrollBottom: number;\n public scrollTop: number;\n public tabs: any;\n public savedY: number;\n public savedX: number;\n\n /**\n * Create a new Buffer.\n * @param _terminal The terminal the Buffer will belong to.\n * @param _hasScrollback Whether the buffer should respect the scrollback of\n * the terminal.\n */\n constructor(\n private _terminal: ITerminal,\n private _hasScrollback: boolean\n ) {\n this.clear();\n }\n\n public get lines(): CircularList {\n return this._lines;\n }\n\n public get hasScrollback(): boolean {\n return this._hasScrollback && this.lines.maxLength > this._terminal.rows;\n }\n\n public get isCursorInViewport(): boolean {\n const absoluteY = this.ybase + this.y;\n const relativeY = absoluteY - this.ydisp;\n return (relativeY >= 0 && relativeY < this._terminal.rows);\n }\n\n /**\n * Gets the correct buffer length based on the rows provided, the terminal's\n * scrollback and whether this buffer is flagged to have scrollback or not.\n * @param rows The terminal rows to use in the calculation.\n */\n private _getCorrectBufferLength(rows: number): number {\n if (!this._hasScrollback) {\n return rows;\n }\n\n const correctBufferLength = rows + this._terminal.options.scrollback;\n\n return correctBufferLength > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : correctBufferLength;\n }\n\n /**\n * Fills the buffer's viewport with blank lines.\n */\n public fillViewportRows(): void {\n if (this._lines.length === 0) {\n let i = this._terminal.rows;\n while (i--) {\n this.lines.push(this._terminal.blankLine());\n }\n }\n }\n\n /**\n * Clears the buffer to it's initial state, discarding all previous data.\n */\n public clear(): void {\n this.ydisp = 0;\n this.ybase = 0;\n this.y = 0;\n this.x = 0;\n this._lines = new CircularList(this._getCorrectBufferLength(this._terminal.rows));\n this.scrollTop = 0;\n this.scrollBottom = this._terminal.rows - 1;\n this.setupTabStops();\n }\n\n /**\n * Resizes the buffer, adjusting its data accordingly.\n * @param newCols The new number of columns.\n * @param newRows The new number of rows.\n */\n public resize(newCols: number, newRows: number): void {\n // Increase max length if needed before adjustments to allow space to fill\n // as required.\n const newMaxLength = this._getCorrectBufferLength(newRows);\n if (newMaxLength > this._lines.maxLength) {\n this._lines.maxLength = newMaxLength;\n }\n\n // The following adjustments should only happen if the buffer has been\n // initialized/filled.\n if (this._lines.length > 0) {\n // Deal with columns increasing (we don't do anything when columns reduce)\n if (this._terminal.cols < newCols) {\n const ch: CharData = [this._terminal.defAttr, ' ', 1, 32]; // does xterm use the default attr?\n for (let i = 0; i < this._lines.length; i++) {\n // TODO: This should be removed, with tests setup for the case that was\n // causing the underlying bug, see https://github.com/sourcelair/xterm.js/issues/824\n if (this._lines.get(i) === undefined) {\n this._lines.set(i, this._terminal.blankLine(undefined, undefined, newCols));\n }\n while (this._lines.get(i).length < newCols) {\n this._lines.get(i).push(ch);\n }\n }\n }\n\n // Resize rows in both directions as needed\n let addToY = 0;\n if (this._terminal.rows < newRows) {\n for (let y = this._terminal.rows; y < newRows; y++) {\n if (this._lines.length < newRows + this.ybase) {\n if (this.ybase > 0 && this._lines.length <= this.ybase + this.y + addToY + 1) {\n // There is room above the buffer and there are no empty elements below the line,\n // scroll up\n this.ybase--;\n addToY++;\n if (this.ydisp > 0) {\n // Viewport is at the top of the buffer, must increase downwards\n this.ydisp--;\n }\n } else {\n // Add a blank line if there is no buffer left at the top to scroll to, or if there\n // are blank lines after the cursor\n this._lines.push(this._terminal.blankLine(undefined, undefined, newCols));\n }\n }\n }\n } else { // (this._terminal.rows >= newRows)\n for (let y = this._terminal.rows; y > newRows; y--) {\n if (this._lines.length > newRows + this.ybase) {\n if (this._lines.length > this.ybase + this.y + 1) {\n // The line is a blank line below the cursor, remove it\n this._lines.pop();\n } else {\n // The line is the cursor, scroll down\n this.ybase++;\n this.ydisp++;\n }\n }\n }\n }\n\n // Reduce max length if needed after adjustments, this is done after as it\n // would otherwise cut data from the bottom of the buffer.\n if (newMaxLength < this._lines.maxLength) {\n // Trim from the top of the buffer and adjust ybase and ydisp.\n const amountToTrim = this._lines.length - newMaxLength;\n if (amountToTrim > 0) {\n this._lines.trimStart(amountToTrim);\n this.ybase = Math.max(this.ybase - amountToTrim, 0);\n this.ydisp = Math.max(this.ydisp - amountToTrim, 0);\n }\n this._lines.maxLength = newMaxLength;\n }\n\n // Make sure that the cursor stays on screen\n if (this.y >= newRows) {\n this.y = newRows - 1;\n }\n if (addToY) {\n this.y += addToY;\n }\n\n if (this.x >= newCols) {\n this.x = newCols - 1;\n }\n\n this.scrollTop = 0;\n }\n\n this.scrollBottom = newRows - 1;\n }\n\n /**\n * Translates a buffer line to a string, with optional start and end columns.\n * Wide characters will count as two columns in the resulting string. This\n * function is useful for getting the actual text underneath the raw selection\n * position.\n * @param line The line being translated.\n * @param trimRight Whether to trim whitespace to the right.\n * @param startCol The column to start at.\n * @param endCol The column to end at.\n */\n public translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol: number = 0, endCol: number = null): string {\n // Get full line\n let lineString = '';\n const line = this.lines.get(lineIndex);\n if (!line) {\n return '';\n }\n\n // Initialize column and index values. Column values represent the actual\n // cell column, indexes represent the index in the string. Indexes are\n // needed here because some chars are 0 characters long (eg. after wide\n // chars) and some chars are longer than 1 characters long (eg. emojis).\n let startIndex = startCol;\n endCol = endCol || line.length;\n let endIndex = endCol;\n\n for (let i = 0; i < line.length; i++) {\n const char = line[i];\n lineString += char[CHAR_DATA_CHAR_INDEX];\n // Adjust start and end cols for wide characters if they affect their\n // column indexes\n if (char[CHAR_DATA_WIDTH_INDEX] === 0) {\n if (startCol >= i) {\n startIndex--;\n }\n if (endCol >= i) {\n endIndex--;\n }\n } else {\n // Adjust the columns to take glyphs that are represented by multiple\n // code points into account.\n if (char[CHAR_DATA_CHAR_INDEX].length > 1) {\n if (startCol > i) {\n startIndex += char[CHAR_DATA_CHAR_INDEX].length - 1;\n }\n if (endCol > i) {\n endIndex += char[CHAR_DATA_CHAR_INDEX].length - 1;\n }\n }\n }\n }\n\n // Calculate the final end col by trimming whitespace on the right of the\n // line if needed.\n if (trimRight) {\n const rightWhitespaceIndex = lineString.search(/\\s+$/);\n if (rightWhitespaceIndex !== -1) {\n endIndex = Math.min(endIndex, rightWhitespaceIndex);\n }\n // Return the empty string if only trimmed whitespace is selected\n if (endIndex <= startIndex) {\n return '';\n }\n }\n\n return lineString.substring(startIndex, endIndex);\n }\n\n /**\n * Setup the tab stops.\n * @param i The index to start setting up tab stops from.\n */\n public setupTabStops(i?: number): void {\n if (i != null) {\n if (!this.tabs[i]) {\n i = this.prevStop(i);\n }\n } else {\n this.tabs = {};\n i = 0;\n }\n\n for (; i < this._terminal.cols; i += this._terminal.options.tabStopWidth) {\n this.tabs[i] = true;\n }\n }\n\n /**\n * Move the cursor to the previous tab stop from the given position (default is current).\n * @param x The position to move the cursor to the previous tab stop.\n */\n public prevStop(x?: number): number {\n if (x == null) {\n x = this.x;\n }\n while (!this.tabs[--x] && x > 0);\n return x >= this._terminal.cols ? this._terminal.cols - 1 : x < 0 ? 0 : x;\n }\n\n /**\n * Move the cursor one tab stop forward from the given position (default is current).\n * @param x The position to move the cursor one tab stop forward.\n */\n public nextStop(x?: number): number {\n if (x == null) {\n x = this.x;\n }\n while (!this.tabs[++x] && x < this._terminal.cols);\n return x >= this._terminal.cols ? this._terminal.cols - 1 : x < 0 ? 0 : x;\n }\n}\n",null],"names":[],"mappings":"AkCAA;;;ADMA;AAGa;AACA;AACA;AACA;AACA;AASb;AAmBA;AACA;AACA;AAEA;AACA;AAEA;AAAA;AACA;AACA;;;AAAA;AAEA;AAAA;AACA;AACA;;;AAAA;AAEA;AAAA;AACA;AACA;AACA;AACA;;;AAAA;AAOA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AAGA;AACA;AACA;AACA;AAIA;AAEA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAIA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAYA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAEA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AAjSa;;;;;;;;;;;;;;;;;ADhBb;AACA;AAMA;AAAA;AASA;AAAA;AAAA;AAEA;AACA;AAIA;AACA;AAEA;;AACA;AAMA;AAAA;AACA;AACA;;;AAAA;AAMA;AAAA;AACA;AACA;;;AAAA;AAMA;AAAA;AACA;AACA;;;AAAA;AAKA;AAIA;AAEA;AACA;AACA;AAKA;AAGA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAAA;AAxFa;;;;;;;ADRA;AAGb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAaA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;;;;;;ADpJa;AAKA;AAYb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AAMA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AD7OA;AAwBA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAMA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAOA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAGA;AACA;AACA;AAEA;AAGA;AACA;AACA;AAEA;AACA;AAUA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AAUA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AAQA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAQA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAMA;AACA;AACA;AACA;AAAA;AACA;AAAA;AApNa;;;;;;;ADRb;AAAA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAAC;;;;;;;ADvED;AAGA;AAGA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AA/Da;;;;;;;ADAb;AACA;AAEA;AACA;AACA;AASA;AACA;AAAA;AAAA;AAEA;AACA;AAGA;AAEA;AACA;AACA;AAEA;AAIA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAGA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAMA;AACA;AAMA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAOA;AACA;AACA;AAOA;AACA;AACA;AAMA;AACA;AACA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAcA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAaA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAuCA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AAIA;AACA;AACA;AAAA;AACA;AACA;AAAA;AAGA;AACA;AAAA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAOA;AACA;AAAA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAUA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAwFA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AAKA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAoFA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAmEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AAAA;AAEA;AACA;AACA;AAAA;AAEA;AACA;AACA;AAIA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAGA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AAIA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AAIA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAyBA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAGA;AACA;AAGA;AACA;AAGA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAYA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AAAA;AAz5Ca;;;;;;;;;;;;;;;;;ADfb;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AAKA;AAAA;AAeA;AAAA;AACA;AARA;AAIA;AAOA;AACA;AACA;AACA;AACA;;AACA;AAMA;AACA;AACA;AAOA;AAAA;AAEA;AACA;AACA;AAGA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAGA;AAGA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AAMA;AACA;AACA;AAYA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAOA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAWA;AAAA;AAAA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AAGA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AASA;AAAA;AACA;AAKA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AA3PA;AA4PA;AAlQA;AAAa;;;;;;;ADhCb;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AAKA;AACA;AACA;AAEA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAIA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AAKA;AACA;AAIA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAIA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AAIA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAGA;AACA;AAMA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AAEA;AACA;AAOA;AACA;AACA;AAQA;AACA;AACA;AAQA;AACA;AACA;AAOA;AACA;AACA;AAKA;AACA;AACA;AAMA;AACA;AACA;AACA;AAKA;AACA;AACA;AASA;AAAA;AAjfa;;;;;;;;;;;;;;;;;ADnKb;AACA;AAGA;AAEA;AAEA;AAMA;AAKA;AAKA;AAMA;AAEA;AACA;AAaA;AAAA;AACA;AACA;AACA;AACA;AAUA;AAAA;AAiCA;AAAA;AACA;AACA;AACA;AARA;AAWA;AACA;AAEA;AACA;;AACA;AAKA;AAAA;AACA;AACA;AAMA;AACA;AAMA;AACA;AACA;AACA;AAKA;AACA;AACA;AAOA;AACA;AACA;AACA;AAEA;AAAA;;;AAAA;AACA;AAAA;;;AAAA;AAKA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAAA;AAKA;AAAA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAIA;AACA;AACA;AAEA;AACA;;;AAAA;AAKA;AACA;AACA;AACA;AACA;AAOA;AAAA;AAEA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAGA;AACA;AAGA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAOA;AACA;AACA;AAMA;AAGA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AAGA;AAGA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAKA;AAAA;AAEA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AAIA;AAIA;AAGA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AAGA;AAIA;AACA;AACA;AAAA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AAEA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AAGA;AACA;AAAA;AAIA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AAGA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAKA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAIA;AAEA;AACA;AACA;AAIA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAOA;AAGA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AAAA;AAjqBa;;;;;;;ADtDb;AAuBA;AACA;AAEA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAKA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;;AAAA;AAMA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAGA;AACA;AACA;AAGA;AAEA;AACA;AACA;AACA;AACA;AACA;;;AAAA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AAEA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AAxHa;;;;;;;;;;;;;;;;;ADYb;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AAEA;AACA;AAUA;AAOA;AAMA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AAAA;AA8HA;AACA;AADA;AA3GA;AA+GA;AACA;;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAKA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAKA;AAEA;AACA;AAKA;AACA;AACA;AAEA;AAAA;AACA;AACA;;;AAAA;AAMA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAMA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AAAA;AACA;AAGA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAKA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAOA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAGA;AACA;AACA;AAEA;AAGA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AACA;AACA;AAIA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AAGA;AAGA;AAIA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAOA;AAEA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AAYA;AAAA;AACA;AACA;AACA;AAKA;AACA;AACA;AAGA;AAGA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAIA;AACA;AACA;AAIA;AACA;AACA;AACA;AAAA;AAIA;AAEA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAQA;AAOA;AAGA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AASA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AAGA;AAEA;AACA;AAAA;AACA;AACA;AAGA;AAEA;AACA;AAEA;AAIA;AACA;AAKA;AACA;AACA;AAGA;AAIA;AACA;AACA;AACA;AACA;AAGA;AAAA;AAGA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAMA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AAIA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAQA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AAEA;AAEA;AAGA;AACA;AACA;AAAA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AAIA;AACA;AACA;AAGA;AACA;AAQA;AACA;AASA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAMA;AACA;AACA;AAKA;AACA;AACA;AAKA;AACA;AACA;AAMA;AAAA;AACA;AAKA;AAGA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AAEA;AACA;AAOA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AACA;AAMA;AACA;AACA;AAWA;AACA;AACA;AAQA;AACA;AACA;AACA;AACA;AAEA;AACA;AAQA;AACA;AACA;AACA;AACA;AAEA;AACA;AAYA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AAMA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AAQA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AASA;AACA;AAGA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAIA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAIA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AAAA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAMA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AAEA;AAGA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAMA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAMA;AAAA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AAAA;AACA;AAAA;AACA;AACA;AAKA;AACA;AAAA;AACA;AAAA;AACA;AACA;AAQA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAMA;AACA;AAAA;AACA;AAAA;AAOA;AAKA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AASA;AACA;AAEA;AACA;AAIA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAMA;AAEA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAMA;AAOA;AACA;AASA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAOA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AA//Da;AAqgEb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AAWA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AAAC;AAGD;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;;;;;;;AD9rEA;AAAA;AACA;AACA;AACA;AACA;AAAC;;;;;;;ADrBD;AAcA;AAAA;AACA;AACA;AACA;AACA;AAjBA;AACA;AACA;AACA;AAgBA;AAGA;AACA;AAEA;AACA;AACA;AAMA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AAEA;AACA;AACA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AAQA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAMA;AACA;AACA;AAAA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AAnIa;;;;;;;ADOb;AACA;AACA;AACA;AACA;AACA;AALA;AAWA;AACA;AACA;AACA;AACA;AACA;AALA;AAWA;AACA;AACA;AACA;AAAA;AACA;AACA;AAGA;AACA;AATA;AAgBA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAzBA;AAgCA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AApBA;AA4BA;AACA;AAGA;AACA;AACA;AANA;;;;;;;AD9GA;AAUA;AAWA;AAAA;AACA;AAXA;AAEA;AAIA;AACA;AACA;AAKA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AACA;AAGA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAIA;AACA;AAGA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AArKa;AAuKb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAXa;;;;;;;ADlLb;AAEA;AAEa;AACb;AAEA;AAYA;AAIA;AACA;AAdA;AACA;AACA;AACA;AACA;AACA;AAWA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAOA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AAWA;AACA;AAKA;AAQA;AAAA;AACA;AAKA;AAQA;AACA;AAKA;AAQA;AACA;AACA;AAKA;AAKA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AASA;AACA;AACA;AAKA;AAAA;AACA;AACA;AAKA;AACA;AAYA;AACA;AACA;AACA;AACA;AAIA;AAgBA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AAEA;AACA;AAGA;AACA;AACA;AAGA;AAEA;AACA;AACA;AACA;AAEA;AASA;AAAA;AACA;AACA;AAIA;AAaA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAEA;AACA;AAAA;AACA;AACA;AAEA;AAGA;AACA;AACA;AAEA;AAIA;AACA;AAOA;AACA;AACA;AAKA;AACA;AACA;AAAA;AAhUsB;;;;;;;ADPtB;AAEa;AAgBb;AAQA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAvCA;AAyCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAMA;AACA;AACA;AACA;AACA;AAJA;AAMA;AAIA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;;;;;;;ADrNA;AACA;AACA;AACA;AACA;AACa;AAEb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAKA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AA1Da;;;;;;;;;;;;;;;;;AD5Db;AAGA;AAcA;AAEA;AAAA;AAMA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AApMa;AAsMb;AAcA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;;;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AAMA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAGA;AAEA;AAGA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;;;;;;;AD5VA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AA3Ba;;;;;;;;;;;;;;;;;ADKb;AACA;AAEA;AAAA;AAGA;AAAA;AAFA;AAIA;AACA;;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAnCa;;;;;;;;;;;;;;;;;ADNb;AACA;AACA;AACA;AAGA;AACA;AAEA;AAAA;AAWA;AAAA;AAAA;AATA;AACA;AAUA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AAGA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAAA;AACA;AACA;AACA;AAMA;AAKA;AAMA;AAIA;AAGA;AAIA;AAIA;AACA;AAOA;AACA;AAQA;AACA;AAGA;AAGA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAAA;AACA;AACA;AAQA;AACA;AACA;AACA;AACA;AACA;AAMA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AA5Ma;;;;;;;;;;;;;;;;;ADNb;AAEA;AAAA;AAGA;AAAA;AAEA;AACA;AACA;AACA;;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAGA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AAGA;AAEA;AACA;AACA;AAGA;AACA;AACA;AACA;AAAA;AA5Ea;;;;;;;;;;;;;;;;;ADLb;AACA;AACA;AAEA;AAOA;AAEA;AAAA;AAMA;AAAA;AAFA;AAIA;;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAKA;AACA;AACA;AACA;AACA;AACA;AAIA;AAEA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAiBA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAIA;AAQA;AACA;AAMA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAKA;AAGA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AAGA;AACA;AAKA;AAGA;AAGA;AACA;AACA;AAOA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAnOa;;;;;;;ADZb;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAC;;;;;;;ADVD;AAEA;AACA;AACA;AAEa;AACA;AAKA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;ADhBb;AAQA;AAAA;AAOA;AAAA;AAEA;AACA;;AACA;AAEA;AAAA;AACA;AACA;;;AAAA;AAEA;AAAA;AACA;AACA;;;AAAA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAtDa;;;;;;;;;;;;;;;;;ADRb;AAOA;AAAA;AAKA;AAAA;AACA;AAGA;AACA;AACA;;AACA;AAEA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAjBA;AAmBA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AATA;AAWA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAAA;AAUA;AACA;AACA;AAUA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAMA;AACA;AACA;AAWA;AAAA;AAAA;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AACA;AACA;AAAA;AAxMa;;;;;;;ADFb;AACA;AACA;AAFA;AAEC;;;;;;;ADJD;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAeA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAKA;AACA;AAEA;AACA;AAYA;AACA;AACA;AACA;AAGA;AACA;AAEA;AACA;AACA;AAAA;AAtFa;;;;;;;ADCA;;;;;;;ADFb;AAEA;;;"} \ No newline at end of file diff --git a/route/static/codemirror/addon/dialog/dialog.css b/route/static/codemirror/addon/dialog/dialog.css deleted file mode 100755 index 494690b40..000000000 --- a/route/static/codemirror/addon/dialog/dialog.css +++ /dev/null @@ -1 +0,0 @@ -.CodeMirror-dialog{position:absolute;left:0;right:0;background:inherit;z-index:15;padding:.4em .8em;overflow:hidden;color:inherit}.CodeMirror-dialog-top{border-bottom:1px solid #eee;top:0}.CodeMirror-dialog-bottom{border-top:1px solid #eee;bottom:0}.CodeMirror-dialog input{border:0;outline:0;background:transparent;width:20em;color:inherit;font-family:monospace}.CodeMirror-dialog button{font-size:70%}.Dialog-close{color:#111;float:right;font-family:Arial;font-size:16px;height:30px;line-height:30px;text-align:center;width:30px;cursor:pointer} \ No newline at end of file diff --git a/route/static/codemirror/addon/dialog/dialog.js b/route/static/codemirror/addon/dialog/dialog.js deleted file mode 100755 index ef64aa000..000000000 --- a/route/static/codemirror/addon/dialog/dialog.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(b){function c(d,h,e){var g=d.getWrapperElement();var f;f=g.appendChild(document.createElement("div"));if(e){f.className="CodeMirror-dialog CodeMirror-dialog-bottom"}else{f.className="CodeMirror-dialog CodeMirror-dialog-top"}if(typeof h=="string"){f.innerHTML=h}else{f.appendChild(h)}return f}function a(d,e){if(d.state.currentNotificationClose){d.state.currentNotificationClose()}d.state.currentNotificationClose=e}b.defineExtension("openDialog",function(i,j,l){if(!l){l={}}a(this,null);var f=c(this,i,l.bottom);var e=false,g=this;function k(m){if(typeof m=="string"){h.value=m}else{if(e){return}e=true;f.parentNode.removeChild(f);g.focus();if(l.onClose){l.onClose(f)}}}var h=f.getElementsByTagName("input")[0],d;if(h){h.focus();if(l.value){h.value=l.value;if(l.selectValueOnOpen!==false){h.select()}}if(l.onInput){b.on(h,"input",function(m){l.onInput(m,h.value,k)})}if(l.onKeyUp){b.on(h,"keyup",function(m){l.onKeyUp(m,h.value,k)})}b.on(h,"keydown",function(m){if(l&&l.onKeyDown&&l.onKeyDown(m,h.value,k)){return}if(m.keyCode==27||(l.closeOnEnter!==false&&m.keyCode==13)){h.blur();b.e_stop(m);k()}if(m.keyCode==13){j(h.value,m)}});if(l.closeOnBlur!==false){b.on(h,"blur",k)}}else{if(d=f.getElementsByTagName("button")[0]){b.on(d,"click",function(){k();g.focus()});if(l.closeOnBlur!==false){b.on(d,"blur",k)}d.focus()}}return k});b.defineExtension("openConfirm",function(m,g,o){a(this,null);var h=c(this,m,o&&o.bottom);var j=h.getElementsByTagName("button");var f=false,k=this,d=1;function n(){if(f){return}f=true;h.parentNode.removeChild(h);k.focus()}j[0].focus();for(var e=0;e -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                - \ No newline at end of file diff --git a/route/static/codemirror/addon/display/autorefresh.js b/route/static/codemirror/addon/display/autorefresh.js deleted file mode 100755 index 5a7a32968..000000000 --- a/route/static/codemirror/addon/display/autorefresh.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(b){b.defineOption("autoRefresh",false,function(d,e){if(d.state.autoRefresh){a(d,d.state.autoRefresh);d.state.autoRefresh=null}if(e&&d.display.wrapper.offsetHeight==0){c(d,d.state.autoRefresh={delay:e.delay||250})}});function c(d,f){function e(){if(d.display.wrapper.offsetHeight){a(d,f);if(d.display.lastWrapHeight!=d.display.wrapper.clientHeight){d.refresh()}}else{f.timeout=setTimeout(e,f.delay)}}f.timeout=setTimeout(e,f.delay);f.hurry=function(){clearTimeout(f.timeout);f.timeout=setTimeout(e,50)};b.on(window,"mouseup",f.hurry);b.on(window,"keyup",f.hurry)}function a(d,e){clearTimeout(e.timeout);b.off(window,"mouseup",e.hurry);b.off(window,"keyup",e.hurry)}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/display/fullscreen.css b/route/static/codemirror/addon/display/fullscreen.css deleted file mode 100755 index a414b0220..000000000 --- a/route/static/codemirror/addon/display/fullscreen.css +++ /dev/null @@ -1 +0,0 @@ -.CodeMirror-fullscreen{position:fixed;top:0;left:0;right:0;bottom:0;height:auto;z-index:9} \ No newline at end of file diff --git a/route/static/codemirror/addon/display/fullscreen.js b/route/static/codemirror/addon/display/fullscreen.js deleted file mode 100755 index a2329a4dd..000000000 --- a/route/static/codemirror/addon/display/fullscreen.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(a){a.defineOption("fullScreen",false,function(d,f,e){if(e==a.Init){e=false}if(!e==!f){return}if(f){b(d)}else{c(d)}});function b(d){var e=d.getWrapperElement();d.state.fullScreenRestore={scrollTop:window.pageYOffset,scrollLeft:window.pageXOffset,width:e.style.width,height:e.style.height};e.style.width="";e.style.height="auto";e.className+=" CodeMirror-fullscreen";document.documentElement.style.overflow="hidden";d.refresh()}function c(d){var e=d.getWrapperElement();e.className=e.className.replace(/\s*CodeMirror-fullscreen\b/,"");document.documentElement.style.overflow="";var f=d.state.fullScreenRestore;e.style.width=f.width;e.style.height=f.height;window.scrollTo(f.scrollLeft,f.scrollTop);d.refresh()}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/display/index.html b/route/static/codemirror/addon/display/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/codemirror/addon/display/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/codemirror/addon/display/panel.js b/route/static/codemirror/addon/display/panel.js deleted file mode 100755 index d3b0e63f1..000000000 --- a/route/static/codemirror/addon/display/panel.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(a){a.defineExtension("addPanel",function(g,f){f=f||{};if(!this.state.panels){b(this)}var h=this.state.panels;var j=h.wrapper;var i=this.getWrapperElement();if(f.after instanceof d&&!f.after.cleared){j.insertBefore(g,f.before.node.nextSibling)}else{if(f.before instanceof d&&!f.before.cleared){j.insertBefore(g,f.before.node)}else{if(f.replace instanceof d&&!f.replace.cleared){j.insertBefore(g,f.replace.node);f.replace.clear()}else{if(f.position=="bottom"){j.appendChild(g)}else{if(f.position=="before-bottom"){j.insertBefore(g,i.nextSibling)}else{if(f.position=="after-top"){j.insertBefore(g,i)}else{j.insertBefore(g,j.firstChild)}}}}}}var e=(f&&f.height)||g.offsetHeight;this._setSize(null,h.heightLeft-=e);h.panels++;return new d(this,g,f,e)});function d(f,h,g,e){this.cm=f;this.node=h;this.options=g;this.height=e;this.cleared=false}d.prototype.clear=function(){if(this.cleared){return}this.cleared=true;var e=this.cm.state.panels;this.cm._setSize(null,e.heightLeft+=this.height);e.wrapper.removeChild(this.node);if(--e.panels==0){c(this.cm)}};d.prototype.changed=function(e){var f=e==null?this.node.offsetHeight:e;var g=this.cm.state.panels;this.cm._setSize(null,g.height+=(f-this.height));this.height=f};function b(f){var h=f.getWrapperElement();var g=window.getComputedStyle?window.getComputedStyle(h):h.currentStyle;var e=parseInt(g.height);var i=f.state.panels={setHeight:h.style.height,heightLeft:e,panels:0,wrapper:document.createElement("div")};h.parentNode.insertBefore(i.wrapper,h);var j=f.hasFocus();i.wrapper.appendChild(h);if(j){f.focus()}f._setSize=f.setSize;if(e!=null){f.setSize=function(m,k){if(k==null){return this._setSize(m,k)}i.setHeight=k;if(typeof k!="number"){var l=/^(\d+\.?\d*)px$/.exec(k);if(l){k=Number(l[1])}else{i.wrapper.style.height=k;k=i.wrapper.offsetHeight;i.wrapper.style.height=""}}f._setSize(m,i.heightLeft+=(k-e));e=k}}}function c(e){var g=e.state.panels;e.state.panels=null;var f=e.getWrapperElement();g.wrapper.parentNode.replaceChild(f,g.wrapper);f.style.height=g.setHeight;e.setSize=e._setSize;e.setSize()}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/display/placeholder.js b/route/static/codemirror/addon/display/placeholder.js deleted file mode 100755 index acb9db7ec..000000000 --- a/route/static/codemirror/addon/display/placeholder.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(b){b.defineOption("placeholder","",function(g,j,h){var i=h&&h!=b.Init;if(j&&!i){g.on("blur",e);g.on("change",a);g.on("swapDoc",a);a(g)}else{if(!j&&i){g.off("blur",e);g.off("change",a);g.off("swapDoc",a);c(g);var k=g.getWrapperElement();k.className=k.className.replace(" CodeMirror-empty","")}}if(j&&!g.hasFocus()){e(g)}});function c(g){if(g.state.placeholder){g.state.placeholder.parentNode.removeChild(g.state.placeholder);g.state.placeholder=null}}function d(g){c(g);var h=g.state.placeholder=document.createElement("pre");h.style.cssText="height: 0; overflow: visible";h.className="CodeMirror-placeholder";var i=g.getOption("placeholder");if(typeof i=="string"){i=document.createTextNode(i)}h.appendChild(i);g.display.lineSpace.insertBefore(h,g.display.lineSpace.firstChild)}function e(g){if(f(g)){d(g)}}function a(g){var i=g.getWrapperElement(),h=f(g);i.className=i.className.replace(" CodeMirror-empty","")+(h?" CodeMirror-empty":"");if(h){d(g)}else{c(g)}}function f(g){return(g.lineCount()===1)&&(g.getLine(0)==="")}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/display/rulers.js b/route/static/codemirror/addon/display/rulers.js deleted file mode 100755 index cfbd155da..000000000 --- a/route/static/codemirror/addon/display/rulers.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(b){b.defineOption("rulers",false,function(c,d){if(c.state.rulerDiv){c.state.rulerDiv.parentElement.removeChild(c.state.rulerDiv);c.state.rulerDiv=null;c.off("refresh",a)}if(d&&d.length){c.state.rulerDiv=c.display.lineSpace.parentElement.insertBefore(document.createElement("div"),c.display.lineSpace);c.state.rulerDiv.className="CodeMirror-rulers";a(c);c.on("refresh",a)}});function a(c){c.state.rulerDiv.textContent="";var k=c.getOption("rulers");var d=c.defaultCharWidth();var j=c.charCoords(b.Pos(c.firstLine(),0),"div").left;c.state.rulerDiv.style.minHeight=(c.display.scroller.offsetHeight+30)+"px";for(var h=0;h=0;v--){var y=t[v].head;s.replaceRange("",p(y.line,y.ch-1),p(y.line,y.ch+1),"+delete")}}function b(s){var u=o(s);var w=u&&a(u,"explode");if(!w||s.getOption("disableInput")){return f.Pass}var t=s.listSelections();for(var v=0;v0;return{anchor:new p(s.anchor.line,s.anchor.ch+(i?-1:1)),head:new p(s.head.line,s.head.ch+(i?1:-1))}}function q(F,s){var A=o(F);if(!A||F.getOption("disableInput")){return f.Pass}var u=a(A,"pairs");var D=u.indexOf(s);if(D==-1){return f.Pass}var I=a(A,"triples");var E=u.charAt(D+1)==s;var t=F.listSelections();var v=D%2==0;var B;for(var x=0;x=0&&F.getRange(H,p(H.line,H.ch+3))==s+s+s){C="skipThree"}else{C="skip"}}}else{if(E&&H.ch>1&&I.indexOf(s)>=0&&F.getRange(p(H.line,H.ch-2),H)==s+s&&(H.ch<=2||F.getRange(p(H.line,H.ch-3),p(H.line,H.ch-2))!=s)){C="addFour"}else{if(E){if(!f.isWordChar(y)&&n(F,H,s)){C="both"}else{return f.Pass}}else{if(v&&(F.getLine(H.line).length==H.ch||c(y,u)||/\s/.test(y))){C="both"}else{return f.Pass}}}}}if(!B){B=C}else{if(B!=C){return f.Pass}}}var w=D%2?u.charAt(D-1):s;var G=D%2?s:u.charAt(D+1);F.operation(function(){if(B=="skip"){F.execCommand("goCharRight")}else{if(B=="skipThree"){for(var K=0;K<3;K++){F.execCommand("goCharRight")}}else{if(B=="surround"){var J=F.getSelections();for(var K=0;K-1&&t%2==1}function h(i,t){var s=i.getRange(p(t.line,t.ch-1),p(t.line,t.ch+1));return s.length==2?s:null}function n(i,x,v){var s=i.getLine(x.line);var u=i.getTokenAt(x);if(/\bstring2?\b/.test(u.type)){return false}var w=new f.StringStream(s.slice(0,x.ch)+v+s.slice(x.ch),4);w.pos=w.start=u.start;for(;;){var t=i.getMode().token(w,u.state);if(w.pos>=x.ch+1){return/\bstring2?\b/.test(t)}w.start=w.pos}}function k(i,t){var s=i.getTokenAt(p(t.line,t.ch+1));return/\bstring/.test(s.type)&&s.start==t.ch}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/edit/closetag.js b/route/static/codemirror/addon/edit/closetag.js deleted file mode 100755 index 3fda5544b..000000000 --- a/route/static/codemirror/addon/edit/closetag.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("../fold/xml-fold"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","../fold/xml-fold"],a)}else{a(CodeMirror)}}})(function(b){b.defineOption("autoCloseTags",false,function(i,l,j){if(j!=b.Init&&j){i.removeKeyMap("autoCloseTags")}if(!l){return}var k={name:"autoCloseTags"};if(typeof l!="object"||l.whenClosing){k["'/'"]=function(m){return e(m)}}if(typeof l!="object"||l.whenOpening){k["'>'"]=function(m){return a(m)}}i.addKeyMap(k)});var d=["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"];var c=["applet","blockquote","body","button","div","dl","fieldset","form","frameset","h1","h2","h3","h4","h5","h6","head","html","iframe","layer","legend","object","ol","p","select","table","ul"];function a(x){if(x.getOption("disableInput")){return b.Pass}var k=x.listSelections(),r=[];for(var s=0;sw.ch){q=q.slice(0,q.length-y.end+w.ch)}var u=q.toLowerCase();if(!q||y.type=="string"&&(y.end!=w.ch||!/[\"\']/.test(y.string.charAt(y.string.length-1))||y.string.length==1)||y.type=="tag"&&j.type=="closeTag"||y.string.indexOf("/")==(y.string.length-1)||n&&g(n,u)>-1||f(x,q,w,j,true)){return b.Pass}var p=v&&g(v,u)>-1;r[s]={indent:p,text:">"+(p?"\n\n":"")+"",newPos:p?b.Pos(w.line+1,0):b.Pos(w.line,w.ch+1)}}for(var s=k.length-1;s>=0;s--){var o=r[s];x.replaceRange(o.text,k[s].head,k[s].anchor,"+insert");var m=x.listSelections().slice(0);m[s]={head:o.newPos,anchor:o.newPos};x.setSelections(m);if(o.indent){x.indentLine(o.newPos.line,null,true);x.indentLine(o.newPos.line+1,null,true)}}}function h(r,n){var k=r.listSelections(),m=[];var q=n?"/":""){l+=">"}m[o]=l}r.replaceSelections(m);k=r.listSelections();for(var o=0;o[> ]*|- \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/,c=/^(\s*)(>[> ]*|- \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/,a=/[*+-]\s/;b.commands.newlineAndIndentContinueMarkdownList=function(q){if(q.getOption("disableInput")){return b.Pass}var g=q.listSelections(),k=[];for(var m=0;m")>=0?n[2].replace("x"," "):(parseInt(n[3],10)+1)+n[4];k[m]="\n"+j+e+f}}q.replaceSelections(k)}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/edit/editAll.js b/route/static/codemirror/addon/edit/editAll.js deleted file mode 100755 index 0426f0e93..000000000 --- a/route/static/codemirror/addon/edit/editAll.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("../fold/xml-fold"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","../fold/xml-fold"],a)}else{a(CodeMirror)}}})(function(c){c.defineOption("matchTags",false,function(e,g,f){if(f&&f!=c.Init){e.off("cursorActivity",b);e.off("viewportChange",d);a(e)}if(g){e.state.matchBothTags=typeof g=="object"&&g.bothTags;e.on("cursorActivity",b);e.on("viewportChange",d);b(e)}});function a(e){if(e.state.tagHit){e.state.tagHit.clear()}if(e.state.tagOther){e.state.tagOther.clear()}e.state.tagHit=e.state.tagOther=null}function b(e){e.state.failedTagMatch=false;e.operation(function(){a(e);if(e.somethingSelected()){return}var j=e.getCursor(),g=e.getViewport();g.from=Math.min(g.from,j.line);g.to=Math.max(j.line+1,g.to);var h=c.findMatchingTag(e,j,g);if(!h){return}if(e.state.matchBothTags){var i=h.at=="open"?h.open:h.close;if(i){e.state.tagHit=e.markText(i.from,i.to,{className:"CodeMirror-matchingtag"})}}var f=h.at=="close"?h.open:h.close;if(f){e.state.tagOther=e.markText(f.from,f.to,{className:"CodeMirror-matchingtag"})}else{e.state.failedTagMatch=true}})}function d(e){if(e.state.failedTagMatch){b(e)}}c.commands.toMatchingTag=function(f){var g=c.findMatchingTag(f,f.getCursor());if(g){var e=g.at=="close"?g.open:g.close;if(e){f.extendSelection(e.to,e.from)}}}});(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(e){var d=/MSIE \d/.test(navigator.userAgent)&&(document.documentMode==null||document.documentMode<8);var i=e.Pos;var a={"(":")>",")":"(<","[":"]>","]":"[<","{":"}>","}":"{<"};function f(q,m,p,k){var s=q.getLineHandle(m.line),o=m.ch-1;var n=(o>=0&&a[s.text.charAt(o)])||a[s.text.charAt(++o)];if(!n){return null}var l=n.charAt(1)==">"?1:-1;if(p&&(l>0)!=(o==m.ch)){return null}var j=q.getTokenTypeAt(i(m.line,o+1));var r=g(q,i(m.line,o+(l>0?1:0)),l,j||null,k);if(r==null){return null}return{from:i(m.line,o),to:r&&r.pos,match:r&&r.ch==n.charAt(0),forward:l>0}}function g(w,r,n,k,m){var l=(m&&m.maxScanLineLength)||10000;var t=(m&&m.maxScanLines)||1000;var v=[];var x=m&&m.bracketRegex?m.bracketRegex:/[(){}[\]]/;var q=n>0?Math.min(r.line+t,w.lastLine()+1):Math.max(w.firstLine()-1,r.line-t);for(var o=r.line;o!=q;o+=n){var y=w.getLine(o);if(!y){continue}var u=n>0?0:y.length-1,p=n>0?y.length:-1;if(y.length>l){continue}if(o==r.line){u=r.ch-(n<0?1:0)}for(;u!=p;u+=n){var j=y.charAt(u);if(x.test(j)&&(k===undefined||w.getTokenTypeAt(i(o,u+1))==k)){var s=a[j];if((s.charAt(1)==">")==(n>0)){v.push(j)}else{if(!v.length){return{pos:i(o,u),ch:j}}else{v.pop()}}}}}return o-n==(n>0?w.lastLine():w.firstLine())?false:null}function b(s,n,m){var k=s.state.matchBrackets.maxHighlightLineLength||1000;var r=[],l=s.listSelections();for(var o=0;o -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                - \ No newline at end of file diff --git a/route/static/codemirror/addon/edit/matchbrackets.js b/route/static/codemirror/addon/edit/matchbrackets.js deleted file mode 100755 index 7e6a31eb7..000000000 --- a/route/static/codemirror/addon/edit/matchbrackets.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(e){var d=/MSIE \d/.test(navigator.userAgent)&&(document.documentMode==null||document.documentMode<8);var i=e.Pos;var a={"(":")>",")":"(<","[":"]>","]":"[<","{":"}>","}":"{<"};function f(q,m,p,k){var s=q.getLineHandle(m.line),o=m.ch-1;var n=(o>=0&&a[s.text.charAt(o)])||a[s.text.charAt(++o)];if(!n){return null}var l=n.charAt(1)==">"?1:-1;if(p&&(l>0)!=(o==m.ch)){return null}var j=q.getTokenTypeAt(i(m.line,o+1));var r=g(q,i(m.line,o+(l>0?1:0)),l,j||null,k);if(r==null){return null}return{from:i(m.line,o),to:r&&r.pos,match:r&&r.ch==n.charAt(0),forward:l>0}}function g(w,r,n,k,m){var l=(m&&m.maxScanLineLength)||10000;var t=(m&&m.maxScanLines)||1000;var v=[];var x=m&&m.bracketRegex?m.bracketRegex:/[(){}[\]]/;var q=n>0?Math.min(r.line+t,w.lastLine()+1):Math.max(w.firstLine()-1,r.line-t);for(var o=r.line;o!=q;o+=n){var y=w.getLine(o);if(!y){continue}var u=n>0?0:y.length-1,p=n>0?y.length:-1;if(y.length>l){continue}if(o==r.line){u=r.ch-(n<0?1:0)}for(;u!=p;u+=n){var j=y.charAt(u);if(x.test(j)&&(k===undefined||w.getTokenTypeAt(i(o,u+1))==k)){var s=a[j];if((s.charAt(1)==">")==(n>0)){v.push(j)}else{if(!v.length){return{pos:i(o,u),ch:j}}else{v.pop()}}}}}return o-n==(n>0?w.lastLine():w.firstLine())?false:null}function b(s,n,m){var k=s.state.matchBrackets.maxHighlightLineLength||1000;var r=[],l=s.listSelections();for(var o=0;og.pos){g.pos=f;return null}g.pos=e;return"trailingspace"},name:"trailingspace"})}}})}); \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/anyword-hint.js b/route/static/codemirror/addon/hint/anyword-hint.js deleted file mode 100755 index 2b27ad172..000000000 --- a/route/static/codemirror/addon/hint/anyword-hint.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(c){var b=/[\w$]+/,a=500;c.registerHelper("hint","anyword",function(o,u){var f=u&&u.word||b;var n=u&&u.range||a;var q=o.getCursor(),e=o.getLine(q.line);var l=q.ch,h=l;while(h&&f.test(e.charAt(h-1))){--h}var g=h!=l&&e.slice(h,l);var p=u&&u.list||[],d={};var s=new RegExp(f.source,"g");for(var k=-1;k<=1;k+=2){var t=q.line,j=Math.min(Math.max(t+k*n,o.firstLine()),o.lastLine())+k;for(;t!=j;t+=k){var r=o.getLine(t),i;while(i=s.exec(r)){if(t==q.line&&i[0]===g){continue}if((!g||i[0].lastIndexOf(g,0)==0)&&!Object.prototype.hasOwnProperty.call(d,i[0])){d[i[0]]=true;p.push(i[0])}}}}return{list:p,from:c.Pos(q.line,h),to:c.Pos(q.line,l)}})}); \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/css-hint.js b/route/static/codemirror/addon/hint/css-hint.js deleted file mode 100755 index 1086c2a59..000000000 --- a/route/static/codemirror/addon/hint/css-hint.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("../../mode/css/css"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","../../mode/css/css"],a)}else{a(CodeMirror)}}})(function(a){var b={link:1,visited:1,active:1,hover:1,focus:1,"first-letter":1,"first-line":1,"first-child":1,before:1,after:1,lang:1};a.registerHelper("hint","css",function(g){var h=g.getCursor(),e=g.getTokenAt(h);var m=a.innerMode(g.getMode(),e.state);if(m.mode.name!="css"){return}if(e.type=="keyword"&&"!important".indexOf(e.string)==0){return{list:["!important"],from:a.Pos(h.line,e.start),to:a.Pos(h.line,e.end)}}var d=e.start,f=h.ch,c=e.string.slice(0,f-d);if(/[^\w$_-]/.test(c)){c="";d=f=h.ch}var i=a.resolveMode("text/css");var l=[];function k(o){for(var n in o){if(!c||n.lastIndexOf(c,0)==0){l.push(n)}}}var j=m.state.state;if(j=="pseudo"||e.type=="variable-3"){k(b)}else{if(j=="block"||j=="maybeprop"){k(i.propertyKeywords)}else{if(j=="prop"||j=="parens"||j=="at"||j=="params"){k(i.valueKeywords);k(i.colorKeywords)}else{if(j=="media"||j=="media_parens"){k(i.mediaTypes);k(i.mediaFeatures)}}}}if(l.length){return{list:l,from:a.Pos(h.line,d),to:a.Pos(h.line,f)}}})}); \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/html-hint.js b/route/static/codemirror/addon/hint/html-hint.js deleted file mode 100755 index 10e0ef6ea..000000000 --- a/route/static/codemirror/addon/hint/html-hint.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("./xml-hint"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","./xml-hint"],a)}else{a(CodeMirror)}}})(function(f){var g="ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" ");var j=["_blank","_self","_top","_parent"];var a=["ascii","utf-8","utf-16","latin1","latin1"];var e=["get","post","put","delete"];var b=["application/x-www-form-urlencoded","multipart/form-data","text/plain"];var c=["all","screen","print","embossed","braille","handheld","print","projection","screen","tty","tv","speech","3d-glasses","resolution [>][<][=] [X]","device-aspect-ratio: X/Y","orientation:portrait","orientation:landscape","device-height: [X]","device-width: [X]"];var m={attrs:{}};var h={a:{attrs:{href:null,ping:null,type:null,media:c,target:j,hreflang:g}},abbr:m,acronym:m,address:m,applet:m,area:{attrs:{alt:null,coords:null,href:null,target:null,ping:null,media:c,hreflang:g,type:null,shape:["default","rect","circle","poly"]}},article:m,aside:m,audio:{attrs:{src:null,mediagroup:null,crossorigin:["anonymous","use-credentials"],preload:["none","metadata","auto"],autoplay:["","autoplay"],loop:["","loop"],controls:["","controls"]}},b:m,base:{attrs:{href:null,target:j}},basefont:m,bdi:m,bdo:m,big:m,blockquote:{attrs:{cite:null}},body:m,br:m,button:{attrs:{form:null,formaction:null,name:null,value:null,autofocus:["","autofocus"],disabled:["","autofocus"],formenctype:b,formmethod:e,formnovalidate:["","novalidate"],formtarget:j,type:["submit","reset","button"]}},canvas:{attrs:{width:null,height:null}},caption:m,center:m,cite:m,code:m,col:{attrs:{span:null}},colgroup:{attrs:{span:null}},command:{attrs:{type:["command","checkbox","radio"],label:null,icon:null,radiogroup:null,command:null,title:null,disabled:["","disabled"],checked:["","checked"]}},data:{attrs:{value:null}},datagrid:{attrs:{disabled:["","disabled"],multiple:["","multiple"]}},datalist:{attrs:{data:null}},dd:m,del:{attrs:{cite:null,datetime:null}},details:{attrs:{open:["","open"]}},dfn:m,dir:m,div:m,dl:m,dt:m,em:m,embed:{attrs:{src:null,type:null,width:null,height:null}},eventsource:{attrs:{src:null}},fieldset:{attrs:{disabled:["","disabled"],form:null,name:null}},figcaption:m,figure:m,font:m,footer:m,form:{attrs:{action:null,name:null,"accept-charset":a,autocomplete:["on","off"],enctype:b,method:e,novalidate:["","novalidate"],target:j}},frame:m,frameset:m,h1:m,h2:m,h3:m,h4:m,h5:m,h6:m,head:{attrs:{},children:["title","base","link","style","meta","script","noscript","command"]},header:m,hgroup:m,hr:m,html:{attrs:{manifest:null},children:["head","body"]},i:m,iframe:{attrs:{src:null,srcdoc:null,name:null,width:null,height:null,sandbox:["allow-top-navigation","allow-same-origin","allow-forms","allow-scripts"],seamless:["","seamless"]}},img:{attrs:{alt:null,src:null,ismap:null,usemap:null,width:null,height:null,crossorigin:["anonymous","use-credentials"]}},input:{attrs:{alt:null,dirname:null,form:null,formaction:null,height:null,list:null,max:null,maxlength:null,min:null,name:null,pattern:null,placeholder:null,size:null,src:null,step:null,value:null,width:null,accept:["audio/*","video/*","image/*"],autocomplete:["on","off"],autofocus:["","autofocus"],checked:["","checked"],disabled:["","disabled"],formenctype:b,formmethod:e,formnovalidate:["","novalidate"],formtarget:j,multiple:["","multiple"],readonly:["","readonly"],required:["","required"],type:["hidden","text","search","tel","url","email","password","datetime","date","month","week","time","datetime-local","number","range","color","checkbox","radio","file","submit","image","reset","button"]}},ins:{attrs:{cite:null,datetime:null}},kbd:m,keygen:{attrs:{challenge:null,form:null,name:null,autofocus:["","autofocus"],disabled:["","disabled"],keytype:["RSA"]}},label:{attrs:{"for":null,form:null}},legend:m,li:{attrs:{value:null}},link:{attrs:{href:null,type:null,hreflang:g,media:c,sizes:["all","16x16","16x16 32x32","16x16 32x32 64x64"]}},map:{attrs:{name:null}},mark:m,menu:{attrs:{label:null,type:["list","context","toolbar"]}},meta:{attrs:{content:null,charset:a,name:["viewport","application-name","author","description","generator","keywords"],"http-equiv":["content-language","content-type","default-style","refresh"]}},meter:{attrs:{value:null,min:null,low:null,high:null,max:null,optimum:null}},nav:m,noframes:m,noscript:m,object:{attrs:{data:null,type:null,name:null,usemap:null,form:null,width:null,height:null,typemustmatch:["","typemustmatch"]}},ol:{attrs:{reversed:["","reversed"],start:null,type:["1","a","A","i","I"]}},optgroup:{attrs:{disabled:["","disabled"],label:null}},option:{attrs:{disabled:["","disabled"],label:null,selected:["","selected"],value:null}},output:{attrs:{"for":null,form:null,name:null}},p:m,param:{attrs:{name:null,value:null}},pre:m,progress:{attrs:{value:null,max:null}},q:{attrs:{cite:null}},rp:m,rt:m,ruby:m,s:m,samp:m,script:{attrs:{type:["text/javascript"],src:null,async:["","async"],defer:["","defer"],charset:a}},section:m,select:{attrs:{form:null,name:null,size:null,autofocus:["","autofocus"],disabled:["","disabled"],multiple:["","multiple"]}},small:m,source:{attrs:{src:null,type:null,media:null}},span:m,strike:m,strong:m,style:{attrs:{type:["text/css"],media:c,scoped:null}},sub:m,summary:m,sup:m,table:m,tbody:m,td:{attrs:{colspan:null,rowspan:null,headers:null}},textarea:{attrs:{dirname:null,form:null,maxlength:null,name:null,placeholder:null,rows:null,cols:null,autofocus:["","autofocus"],disabled:["","disabled"],readonly:["","readonly"],required:["","required"],wrap:["soft","hard"]}},tfoot:m,th:{attrs:{colspan:null,rowspan:null,headers:null,scope:["row","col","rowgroup","colgroup"]}},thead:m,time:{attrs:{datetime:null}},title:m,tr:m,track:{attrs:{src:null,label:null,"default":null,kind:["subtitles","captions","descriptions","chapters","metadata"],srclang:g}},tt:m,u:m,ul:m,"var":m,video:{attrs:{src:null,poster:null,width:null,height:null,crossorigin:["anonymous","use-credentials"],preload:["auto","metadata","none"],autoplay:["","autoplay"],mediagroup:["movie"],muted:["","muted"],controls:["","controls"]}},wbr:m};var d={accesskey:["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"],"class":null,contenteditable:["true","false"],contextmenu:null,dir:["ltr","rtl","auto"],draggable:["true","false","auto"],dropzone:["copy","move","link","string:","file:"],hidden:["hidden"],id:null,inert:["inert"],itemid:null,itemprop:null,itemref:null,itemscope:["itemscope"],itemtype:null,lang:["en","es"],spellcheck:["true","false"],style:null,tabindex:["1","2","3","4","5","6","7","8","9"],title:null,translate:["yes","no"],onclick:null,rel:["stylesheet","alternate","author","bookmark","help","license","next","nofollow","noreferrer","prefetch","prev","search","tag"]};function i(o){for(var n in d){if(d.hasOwnProperty(n)){o.attrs[n]=d[n]}}}i(m);for(var l in h){if(h.hasOwnProperty(l)&&h[l]!=m){i(h[l])}}f.htmlSchema=h;function k(n,o){var q={schemaInfo:h};if(o){for(var p in o){q[p]=o[p]}}return f.hint.xml(n,q)}f.registerHelper("hint","html",k)}); \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/index.html b/route/static/codemirror/addon/hint/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/codemirror/addon/hint/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/javascript-hint.js b/route/static/codemirror/addon/hint/javascript-hint.js deleted file mode 100755 index f9cfa09eb..000000000 --- a/route/static/codemirror/addon/hint/javascript-hint.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(d){var l=d.Pos;function g(p,r){for(var q=0,s=p.length;qv.ch){s.end=v.ch;s.string=s.string.slice(0,v.ch-s.start)}}var p=s;while(p.type=="property"){p=w(u,l(v.line,p.start));if(p.string!="."){return}p=w(u,l(v.line,p.start));if(!r){var r=[]}r.push(p)}return{list:i(s,r,t,q),from:l(v.line,s.start),to:l(v.line,s.end)}}function a(q,p){return h(q,c,function(r,s){return r.getTokenAt(s)},p)}d.registerHelper("hint","javascript",a);function b(q,r){var p=q.getTokenAt(r);if(r.ch==p.start+1&&p.string.charAt(0)=="."){p.end=p.start;p.string=".";p.type="property"}else{if(/^\.[\w$_]*$/.test(p.string)){p.type="property";p.start++;p.string=p.string.replace(/\./,"")}}return p}function j(q,p){return h(q,n,b,p)}d.registerHelper("hint","coffeescript",j);var m=("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight toUpperCase toLowerCase split concat match replace search").split(" ");var o=("length concat join splice push pop shift unshift slice reverse sort indexOf lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");var f="prototype apply call bind".split(" ");var c=("break case catch continue debugger default delete do else false finally for function if in instanceof new null return switch throw true try typeof var void while with").split(" ");var n=("and break catch class continue delete do else extends false finally for if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");function k(q,s){if(!Object.getOwnPropertyNames||!Object.getPrototypeOf){for(var p in q){s(p)}}else{for(var r=q;r;r=Object.getPrototypeOf(r)){Object.getOwnPropertyNames(r).forEach(s)}}}function i(u,t,y,B){var A=[],r=u.string,s=B&&B.globalScope||window;function w(v){if(v.lastIndexOf(r,0)==0&&!e(A,v)){A.push(v)}}function q(v){if(typeof v=="string"){g(m,w)}else{if(v instanceof Array){g(o,w)}else{if(v instanceof Function){g(f,w)}}}k(v,w)}if(t&&t.length){var x=t.pop(),p;if(x.type&&x.type.indexOf("variable")===0){if(B&&B.additionalContext){p=B.additionalContext[x.string]}if(!B||B.useGlobalScope!==false){p=p||s[x.string]}}else{if(x.type=="string"){p=""}else{if(x.type=="atom"){p=1}else{if(x.type=="function"){if(s.jQuery!=null&&(x.string=="$"||x.string=="jQuery")&&(typeof s.jQuery=="function")){p=s.jQuery()}else{if(s._!=null&&(x.string=="_")&&(typeof s._=="function")){p=s._()}}}}}}while(p!=null&&t.length){p=p[t.pop().string]}if(p!=null){q(p)}}else{for(var z=u.state.localVars;z;z=z.next){w(z.name)}for(var z=u.state.globalVars;z;z=z.next){w(z.name)}if(!B||B.useGlobalScope!==false){q(s)}g(y,w)}return A}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/show-hint.css b/route/static/codemirror/addon/hint/show-hint.css deleted file mode 100755 index 4027ed60e..000000000 --- a/route/static/codemirror/addon/hint/show-hint.css +++ /dev/null @@ -1 +0,0 @@ -.CodeMirror-hints{position:absolute;z-index:10;overflow:hidden;list-style:none;margin:0;padding:2px;-webkit-box-shadow:2px 3px 5px rgba(0,0,0,.2);-moz-box-shadow:2px 3px 5px rgba(0,0,0,.2);box-shadow:2px 3px 5px rgba(0,0,0,.2);border-radius:3px;border:1px solid silver;background:white;font-size:90%;font-family:monospace;max-height:20em;overflow-y:auto}.CodeMirror-hint{margin:0;padding:0 4px;border-radius:2px;white-space:pre;color:black;cursor:pointer}li.CodeMirror-hint-active{background:#08f;color:white} \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/show-hint.js b/route/static/codemirror/addon/hint/show-hint.js deleted file mode 100755 index 24ca4b4e7..000000000 --- a/route/static/codemirror/addon/hint/show-hint.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(e){var i="CodeMirror-hint";var h="CodeMirror-hint-active";e.showHint=function(q,r,s){if(!r){return q.showHint(s)}if(s&&s.async){r.async=true}var t={hint:r};if(s){for(var u in s){t[u]=s[u]}}return q.showHint(t)};e.defineExtension("showHint",function(r){r=o(this,this.getCursor("start"),r);var t=this.listSelections();if(t.length>1){return}if(this.somethingSelected()){if(!r.hint.supportsSelection){return}for(var s=0;s0&&r.to.ch-r.from.ch!=q.to.ch-q.from.ch}function o(q,v,s){var t=q.options.hintOptions;var r={};for(var u in c){r[u]=c[u]}if(t){for(var u in t){if(t[u]!==undefined){r[u]=t[u]}}}if(s){for(var u in s){if(s[u]!==undefined){r[u]=s[u]}}}if(r.hint.resolve){r.hint=r.hint.resolve(q,v)}return r}function p(q){if(typeof q=="string"){return q}else{return q.text}}function d(u,x){var r={Up:function(){x.moveFocus(-1)},Down:function(){x.moveFocus(1)},PageUp:function(){x.moveFocus(-x.menuSize()+1,true)},PageDown:function(){x.moveFocus(x.menuSize()-1,true)},Home:function(){x.setFocus(0)},End:function(){x.setFocus(x.length-1)},Enter:x.pick,Tab:x.pick,Esc:x.close};var w=u.options.customKeys;var t=w?{}:r;function s(y,A){var z;if(typeof A!="string"){z=function(B){return A(B,x)}}else{if(r.hasOwnProperty(A)){z=r[A]}else{z=A}}t[y]=z}if(w){for(var v in w){if(w.hasOwnProperty(v)){s(v,w[v])}}}var q=u.options.extraKeys;if(q){for(var v in q){if(q.hasOwnProperty(v)){s(v,q[v])}}}return t}function m(r,q){while(q&&q!=r){if(q.nodeName.toUpperCase()==="LI"&&q.parentNode==r){return q}q=q.parentNode}}function n(E,P){this.completion=E;this.data=P;this.picked=false;var v=this,A=E.cm;var M=this.hints=document.createElement("ul");M.className="CodeMirror-hints";this.selectedHint=P.selectedHint||0;var z=P.list;for(var O=0;OM.clientHeight+1;var H=A.getScrollInfo();if(D>0){var J=C.bottom-C.top,q=y.top-(y.bottom-C.top);if(q-J>0){M.style.top=(I=y.top-J)+"px";G=false}else{if(J>N){M.style.height=(N-5)+"px";M.style.top=(I=y.bottom-C.top)+"px";var x=A.getCursor();if(P.from.ch!=x.ch){y=A.cursorCoords(x);M.style.left=(w=y.left)+"px";C=M.getBoundingClientRect()}}}}var F=C.right-B;if(F>0){if(C.right-C.left>B){M.style.width=(B-5)+"px";F-=(C.right-C.left)-B}M.style.left=(w=y.left-F)+"px"}if(r){for(var L=M.firstChild;L;L=L.nextSibling){L.style.paddingRight=A.display.nativeBarWidth+"px"}}A.addKeyMap(this.keyMap=d(E,{moveFocus:function(R,Q){v.changeActive(v.selectedHint+R,Q)},setFocus:function(Q){v.changeActive(Q)},menuSize:function(){return v.screenAmount()},length:z.length,close:function(){E.close()},pick:function(){v.pick()},data:P}));if(E.options.closeOnUnfocus){var K;A.on("blur",this.onBlur=function(){K=setTimeout(function(){E.close()},100)});A.on("focus",this.onFocus=function(){clearTimeout(K)})}A.on("scroll",this.onScroll=function(){var T=A.getScrollInfo(),S=A.getWrapperElement().getBoundingClientRect();var R=I+H.top-T.top;var Q=R-(window.pageYOffset||(document.documentElement||document.body).scrollTop);if(!G){Q+=M.offsetHeight}if(Q<=S.top||Q>=S.bottom){return E.close()}M.style.top=R+"px";M.style.left=(w+H.left-T.left)+"px"});e.on(M,"dblclick",function(R){var Q=m(M,R.target||R.srcElement);if(Q&&Q.hintId!=null){v.changeActive(Q.hintId);v.pick()}});e.on(M,"click",function(R){var Q=m(M,R.target||R.srcElement);if(Q&&Q.hintId!=null){v.changeActive(Q.hintId);if(E.options.completeOnSingleClick){v.pick()}}});e.on(M,"mousedown",function(){setTimeout(function(){A.focus()},20)});e.signal(P,"select",z[0],M.firstChild);return true}n.prototype={close:function(){if(this.completion.widget!=this){return}this.completion.widget=null;this.hints.parentNode.removeChild(this.hints);this.completion.cm.removeKeyMap(this.keyMap);var q=this.completion.cm;if(this.completion.options.closeOnUnfocus){q.off("blur",this.onBlur);q.off("focus",this.onFocus)}q.off("scroll",this.onScroll)},disable:function(){this.completion.cm.removeKeyMap(this.keyMap);var q=this;this.keyMap={Enter:function(){q.picked=true}};this.completion.cm.addKeyMap(this.keyMap)},pick:function(){this.completion.pick(this.data,this.selectedHint)},changeActive:function(q,s){if(q>=this.data.list.length){q=s?this.data.list.length-1:0}else{if(q<0){q=s?0:this.data.list.length-1}}if(this.selectedHint==q){return}var r=this.hints.childNodes[this.selectedHint];r.className=r.className.replace(" "+h,"");r=this.hints.childNodes[this.selectedHint=q];r.className+=" "+h;if(r.offsetTopthis.hints.scrollTop+this.hints.clientHeight){this.hints.scrollTop=r.offsetTop+r.offsetHeight-this.hints.clientHeight+3}}e.signal(this.data,"select",this.data.list[this.selectedHint],r)},screenAmount:function(){return Math.floor(this.hints.clientHeight/this.hints.firstChild.offsetHeight)||1}};function k(r,t){if(!r.somethingSelected()){return t}var q=[];for(var s=0;s0){z(B)}else{x(A+1)}})}x(0)};r.async=true;r.supportsSelection=true;return r}else{if(t=q.getHelper(q.getCursor(),"hintWords")){return function(v){return e.hint.fromList(v,{words:t})}}else{if(e.hint.anyword){return function(v,w){return e.hint.anyword(v,w)}}else{return function(){}}}}}e.registerHelper("hint","auto",{resolve:a});e.registerHelper("hint","fromList",function(w,z){var x=w.getCursor(),r=w.getTokenAt(x);var u=e.Pos(x.line,r.end);if(r.string&&/\w/.test(r.string[r.string.length-1])){var s=r.string,v=e.Pos(x.line,r.start)}else{var s="",v=u}var y=[];for(var t=0;t,]/,closeOnUnfocus:true,completeOnSingleClick:true,container:null,customKeys:null,extraKeys:null};e.defineOption("hintOptions",null)}); \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/sql-hint.js b/route/static/codemirror/addon/hint/sql-hint.js deleted file mode 100755 index cb9fd7e57..000000000 --- a/route/static/codemirror/addon/hint/sql-hint.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("../../mode/sql/sql"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","../../mode/sql/sql"],a)}else{a(CodeMirror)}}})(function(p){var h;var s;var i;var j={QUERY_DIV:";",ALIAS_KEYWORD:"AS"};var n=p.Pos,o=p.cmpPos;function l(v){return Object.prototype.toString.call(v)=="[object Array]"}function d(v){var w=v.doc.modeOption;if(w==="sql"){w="text/x-sql"}return p.resolveMode(w).keywords}function a(v){return typeof v=="string"?v:v.text}function b(v,w){if(l(w)){w={columns:w}}if(!w.text){w.text=v}return w}function q(w){var v={};if(l(w)){for(var y=w.length-1;y>=0;y--){var z=w[y];v[a(z).toUpperCase()]=b(a(z),z)}}else{if(w){for(var x in w){v[x.toUpperCase()]=b(x,w[x])}}}return v}function c(v){return h[v.toUpperCase()]}function g(w){var v={};for(var x in w){if(w.hasOwnProperty(x)){v[x]=w[x]}}return v}function e(w,y){var v=w.length;var x=a(y).substr(0,v);return w.toUpperCase()===x.toUpperCase()}function f(v,y,z,x){if(l(z)){for(var w=0;w0)&&o(D,z[x])<=0){H={start:G,end:z[x]};break}G=z[x]}var E=I.getRange(H.start,H.end,false);for(var x=0;xB.ch){x.end=B.ch;x.string=x.string.slice(0,B.ch-x.start)}if(x.string.match(/^[.`\w@]\w*$/)){C=x.string;v=x.start;y=x.end}else{v=y=B.ch;C=""}if(C.charAt(0)=="."||C.charAt(0)=="`"){v=t(B,x,E,z)}else{f(E,C,h,function(F){return F});f(E,C,s,function(F){return F});if(!A){f(E,C,i,function(F){return F.toUpperCase()})}}return{list:E,from:n(B.line,v),to:n(B.line,y)}})}); \ No newline at end of file diff --git a/route/static/codemirror/addon/hint/xml-hint.js b/route/static/codemirror/addon/hint/xml-hint.js deleted file mode 100755 index f2321c98c..000000000 --- a/route/static/codemirror/addon/hint/xml-hint.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(b){var c=b.Pos;function a(o,h){var t=h&&h.schemaInfo;var E=(h&&h.quoteChar)||'"';if(!t){return}var g=o.getCursor(),m=o.getTokenAt(g);if(m.end>g.ch){m.end=g.ch;m.string=m.string.slice(0,g.ch-m.start)}var F=b.innerMode(o.getMode(),m.state);if(F.mode.name!="xml"){return}var p=[],z=false,A;var H=/\btag\b/.test(m.type)&&!/>$/.test(m.string);var e=H&&/^\w/.test(m.string),C;if(e){var s=o.getLine(g.line).slice(Math.max(0,m.start-2),m.start);var l=/<\/$/.test(s)?"close":/<$/.test(s)?"open":null;if(l){C=m.start-(l=="close"?2:1)}}else{if(H&&m.string=="<"){l="open"}else{if(H&&m.string=="")}}else{var u=t[F.state.tagName],x=u&&u.attrs;var j=t["!attrs"];if(!x&&!j){return}if(!x){x=j}else{if(j){var r={};for(var d in j){if(j.hasOwnProperty(d)){r[d]=j[d]}}for(var d in x){if(x.hasOwnProperty(d)){r[d]=x[d]}}x=r}}if(m.type=="string"||m.string=="="){var s=o.getRange(c(g.line,Math.max(0,g.ch-60)),c(g.line,m.type=="string"?m.start:m.end));var v=s.match(/([^\s\u00a0=<>\"\']+)=$/),q;if(!v||!x.hasOwnProperty(v[1])||!(q=x[v[1]])){return}if(typeof q=="function"){q=q.call(this,o)}if(m.type=="string"){A=m.string;var w=0;if(/['"]/.test(m.string.charAt(0))){E=m.string.charAt(0);A=m.string.slice(1);w++}var D=m.string.length;if(/['"]/.test(m.string.charAt(D-1))){E=m.string.charAt(D-1);A=m.string.substr(w,D-2)}z=true}for(var B=0;B -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                - \ No newline at end of file diff --git a/route/static/codemirror/addon/scroll/annotatescrollbar.js b/route/static/codemirror/addon/scroll/annotatescrollbar.js deleted file mode 100755 index 88f7f83b4..000000000 --- a/route/static/codemirror/addon/scroll/annotatescrollbar.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(a){a.defineExtension("annotateScrollbar",function(c){if(typeof c=="string"){c={className:c}}return new b(this,c)});a.defineOption("scrollButtonHeight",0);function b(c,e){this.cm=c;this.options=e;this.buttonHeight=e.scrollButtonHeight||c.getOption("scrollButtonHeight");this.annotations=[];this.doRedraw=this.doUpdate=null;this.div=c.getWrapperElement().appendChild(document.createElement("div"));this.div.style.cssText="position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none";this.computeScale();function f(g){clearTimeout(d.doRedraw);d.doRedraw=setTimeout(function(){d.redraw()},g)}var d=this;c.on("refresh",this.resizeHandler=function(){clearTimeout(d.doUpdate);d.doUpdate=setTimeout(function(){if(d.computeScale()){f(20)}},100)});c.on("markerAdded",this.resizeHandler);c.on("markerCleared",this.resizeHandler);if(e.listenForChanges!==false){c.on("change",this.changeHandler=function(){f(250)})}}b.prototype.computeScale=function(){var c=this.cm;var d=(c.getWrapperElement().clientHeight-c.display.barHeight-this.buttonHeight*2)/c.getScrollerElement().scrollHeight;if(d!=this.hScale){this.hScale=d;return true}};b.prototype.update=function(c){this.annotations=c;this.redraw()};b.prototype.redraw=function(o){if(o!==false){this.computeScale()}var p=this.cm,t=this.hScale;var s=document.createDocumentFragment(),g=this.annotations;var k=p.getOption("lineWrapping");var j=k&&p.defaultTextHeight()*1.5;var e=null,n=null;function q(v,u){if(e!=v.line){e=v.line;n=p.getLineHandle(e)}if(k&&n.height>j){return p.charCoords(v,"local")[u?"top":"bottom"]}var i=p.heightAtLine(n,"local");return i+(u?0:n.height)}if(p.display.barWidth){for(var h=0,l;hc+0.9){break}d=g[++h];c=q(d.to,false)*t}if(c==m){continue}var r=Math.max(c-m,3);var f=s.appendChild(document.createElement("div"));f.style.cssText="position: absolute; right: 0px; width: "+Math.max(p.display.barWidth-1,2)+"px; top: "+(m+this.buttonHeight)+"px; height: "+r+"px";f.className=this.options.className;if(d.id){f.setAttribute("annotation-id",d.id)}}}this.div.textContent="";this.div.appendChild(s)};b.prototype.clear=function(){this.cm.off("refresh",this.resizeHandler);this.cm.off("markerAdded",this.resizeHandler);this.cm.off("markerCleared",this.resizeHandler);if(this.changeHandler){this.cm.off("change",this.changeHandler)}this.div.parentNode.removeChild(this.div)}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/scroll/index.html b/route/static/codemirror/addon/scroll/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/codemirror/addon/scroll/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/codemirror/addon/scroll/scrollpastend.js b/route/static/codemirror/addon/scroll/scrollpastend.js deleted file mode 100755 index 9e2877c83..000000000 --- a/route/static/codemirror/addon/scroll/scrollpastend.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(b){b.defineOption("scrollPastEnd",false,function(d,f,e){if(e&&e!=b.Init){d.off("change",a);d.off("refresh",c);d.display.lineSpace.parentNode.style.paddingBottom="";d.state.scrollPastEndPadding=null}if(f){d.on("change",a);d.on("refresh",c);c(d)}});function a(d,e){if(b.changeEnd(e).line==d.lastLine()){c(d)}}function c(d){var g="";if(d.lineCount()>1){var e=d.display.scroller.clientHeight-30,f=d.getLineHandle(d.lastLine()).height;g=(e-f)+"px"}if(d.state.scrollPastEndPadding!=g){d.state.scrollPastEndPadding=g;d.display.lineSpace.parentNode.style.paddingBottom=g;d.off("refresh",c);d.setSize();d.on("refresh",c)}}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/scroll/simplescrollbars.css b/route/static/codemirror/addon/scroll/simplescrollbars.css deleted file mode 100755 index b4b5245bc..000000000 --- a/route/static/codemirror/addon/scroll/simplescrollbars.css +++ /dev/null @@ -1 +0,0 @@ -.CodeMirror-simplescroll-horizontal div,.CodeMirror-simplescroll-vertical div{position:absolute;background:#ccc;-moz-box-sizing:border-box;box-sizing:border-box;border:1px solid #bbb;border-radius:2px}.CodeMirror-simplescroll-horizontal,.CodeMirror-simplescroll-vertical{position:absolute;z-index:6;background:#eee}.CodeMirror-simplescroll-horizontal{bottom:0;left:0;height:8px}.CodeMirror-simplescroll-horizontal div{bottom:0;height:100%}.CodeMirror-simplescroll-vertical{right:0;top:0;width:8px}.CodeMirror-simplescroll-vertical div{right:0;width:100%}.CodeMirror-overlayscroll .CodeMirror-scrollbar-filler,.CodeMirror-overlayscroll .CodeMirror-gutter-filler{display:none}.CodeMirror-overlayscroll-horizontal div,.CodeMirror-overlayscroll-vertical div{position:absolute;background:#bcd;border-radius:3px}.CodeMirror-overlayscroll-horizontal,.CodeMirror-overlayscroll-vertical{position:absolute;z-index:6}.CodeMirror-overlayscroll-horizontal{bottom:0;left:0;height:6px}.CodeMirror-overlayscroll-horizontal div{bottom:0;height:100%}.CodeMirror-overlayscroll-vertical{right:0;top:0;width:6px}.CodeMirror-overlayscroll-vertical div{right:0;width:100%} \ No newline at end of file diff --git a/route/static/codemirror/addon/scroll/simplescrollbars.js b/route/static/codemirror/addon/scroll/simplescrollbars.js deleted file mode 100755 index 303b69a98..000000000 --- a/route/static/codemirror/addon/scroll/simplescrollbars.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(b){function a(f,h,e){this.orientation=h;this.scroll=e;this.screen=this.total=this.size=1;this.pos=0;this.node=document.createElement("div");this.node.className=f+"-"+h;this.inner=this.node.appendChild(document.createElement("div"));var g=this;b.on(this.inner,"mousedown",function(n){if(n.which!=1){return}b.e_preventDefault(n);var l=g.orientation=="horizontal"?"pageX":"pageY";var o=n[l],m=g.pos;function k(){b.off(document,"mousemove",j);b.off(document,"mouseup",k)}function j(p){if(p.which!=1){return k()}g.moveTo(m+(p[l]-o)*(g.total/g.size))}b.on(document,"mousemove",j);b.on(document,"mouseup",k)});b.on(this.node,"click",function(l){b.e_preventDefault(l);var j=g.inner.getBoundingClientRect(),k;if(g.orientation=="horizontal"){k=l.clientXj.right?1:0}else{k=l.clientYj.bottom?1:0}g.moveTo(g.pos+k*g.screen)});function i(l){var k=b.wheelEventPixels(l)[g.orientation=="horizontal"?"x":"y"];var j=g.pos;g.moveTo(g.pos+k);if(g.pos!=j){b.e_preventDefault(l)}}b.on(this.node,"mousewheel",i);b.on(this.node,"DOMMouseScroll",i)}a.prototype.setPos=function(f,e){if(f<0){f=0}if(f>this.total-this.screen){f=this.total-this.screen}if(!e&&f==this.pos){return false}this.pos=f;this.inner.style[this.orientation=="horizontal"?"left":"top"]=(f*(this.size/this.total))+"px";return true};a.prototype.moveTo=function(e){if(this.setPos(e)){this.scroll(e,this.orientation)}};var d=10;a.prototype.update=function(g,h,i){var f=this.screen!=h||this.total!=g||this.size!=i;if(f){this.screen=h;this.total=g;this.size=i}var e=this.screen*(this.size/this.total);if(eh.clientWidth+1;var e=h.scrollHeight>h.clientHeight+1;this.vert.node.style.display=e?"block":"none";this.horiz.node.style.display=i?"block":"none";if(e){this.vert.update(h.scrollHeight,h.clientHeight,h.viewHeight-(i?f:0));this.vert.node.style.bottom=i?f+"px":"0"}if(i){this.horiz.update(h.scrollWidth,h.clientWidth,h.viewWidth-(e?f:0)-h.barLeft);this.horiz.node.style.right=e?f+"px":"0";this.horiz.node.style.left=h.barLeft+"px"}return{right:e?f:0,bottom:i?f:0}};c.prototype.setScrollTop=function(e){this.vert.setPos(e)};c.prototype.setScrollLeft=function(e){this.horiz.setPos(e)};c.prototype.clear=function(){var e=this.horiz.node.parentNode;e.removeChild(this.horiz.node);e.removeChild(this.vert.node)};b.scrollbarModel.simple=function(f,e){return new c("CodeMirror-simplescroll",f,e)};b.scrollbarModel.overlay=function(f,e){return new c("CodeMirror-overlayscroll",f,e)}}); \ No newline at end of file diff --git a/route/static/codemirror/addon/search/index.html b/route/static/codemirror/addon/search/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/codemirror/addon/search/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/codemirror/addon/search/jump-to-line.js b/route/static/codemirror/addon/search/jump-to-line.js deleted file mode 100755 index 0d0a498dd..000000000 --- a/route/static/codemirror/addon/search/jump-to-line.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("../dialog/dialog"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","../dialog/dialog"],a)}else{a(CodeMirror)}}})(function(a){function b(e,i,g,j,h){if(e.openDialog){e.openDialog(i,h,{value:j,selectValueOnOpen:true})}else{h(prompt(g,j))}}var d='Jump to line: (Use line:column or scroll% syntax)';function c(e,g){var f=Number(g);if(/^[-+]/.test(g)){return e.getCursor().line+f}else{return f-1}}a.commands.jumpToLine=function(e){var f=e.getCursor();b(e,d,"Jump to line:",(f.line+1)+":"+f.ch,function(i){if(!i){return}var h;if(h=/^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(i)){e.setCursor(c(e,h[1]),Number(h[2]))}else{if(h=/^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(i)){var g=Math.round(e.lineCount()*Number(h[1])/100);if(/^[-+]/.test(h[1])){g=f.line+g+1}e.setCursor(g-1,f.ch)}else{if(h=/^\s*\:?\s*([\+\-]?\d+)\s*/.exec(i)){e.setCursor(c(e,h[1]),f.ch)}}}})};a.keyMap["default"]["Alt-G"]="jumpToLine"}); \ No newline at end of file diff --git a/route/static/codemirror/addon/search/match-highlighter.js b/route/static/codemirror/addon/search/match-highlighter.js deleted file mode 100755 index 5bc46f2ed..000000000 --- a/route/static/codemirror/addon/search/match-highlighter.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("./matchesonscrollbar"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","./matchesonscrollbar"],a)}else{a(CodeMirror)}}})(function(d){var c={style:"matchhighlight",minChars:2,delay:100,wordsOnly:false,annotateScrollbar:false,showToken:false,trim:true};function j(n){this.options={};for(var m in c){this.options[m]=(n&&n.hasOwnProperty(m)?n:c)[m]}this.overlay=this.timeout=null;this.matchesonscroll=null;this.active=false}d.defineOption("highlightSelectionMatches",false,function(m,p,n){if(n&&n!=d.Init){l(m);clearTimeout(m.state.matchHighlighter.timeout);m.state.matchHighlighter=null;m.off("cursorActivity",i);m.off("focus",h)}if(p){var o=m.state.matchHighlighter=new j(p);if(m.hasFocus()){o.active=true;f(m)}else{m.on("focus",h)}m.on("cursorActivity",i)}});function i(m){var n=m.state.matchHighlighter;if(n.active||m.hasFocus()){a(m,n)}}function h(m){var n=m.state.matchHighlighter;if(!n.active){n.active=true;a(m,n)}}function a(m,n){clearTimeout(n.timeout);n.timeout=setTimeout(function(){f(m)},n.options.delay)}function b(n,r,p,o){var q=n.state.matchHighlighter;n.addOverlay(q.overlay=e(r,p,o));if(q.options.annotateScrollbar&&n.showMatchesOnScrollbar){var m=p?new RegExp("\\b"+r+"\\b"):r;q.matchesonscroll=n.showMatchesOnScrollbar(m,false,{className:"CodeMirror-selection-highlight-scrollbar"})}}function l(m){var n=m.state.matchHighlighter;if(n.overlay){m.removeOverlay(n.overlay);n.overlay=null;if(n.matchesonscroll){n.matchesonscroll.clear();n.matchesonscroll=null}}}function f(m){m.operation(function(){var n=m.state.matchHighlighter;l(m);if(!m.somethingSelected()&&n.options.showToken){var u=n.options.showToken===true?/[\w$]/:n.options.showToken;var t=m.getCursor(),v=m.getLine(t.line),o=t.ch,p=o;while(o&&u.test(v.charAt(o-1))){--o}while(p=n.options.minChars){b(m,q,false,n.options.style)}})}function g(m,r,q){var o=m.getRange(r,q);if(o.match(/^\w+$/)!==null){if(r.ch>0){var p={line:r.line,ch:r.ch-1};var n=m.getRange(p,r);if(n.match(/\W/)===null){return false}}if(q.ch=this.gap.to){break}if(f.to.line>=this.gap.from){this.matches.splice(g--,1)}}var h=this.cm.getSearchCursor(this.query,b.Pos(this.gap.from,0),this.caseFold);var e=this.options&&this.options.maxMatches||d;while(h.findNext()){var f={from:h.from(),to:h.to()};if(f.from.line>=this.gap.to){break}this.matches.splice(g++,0,f);if(this.matches.length>e){break}}this.gap=null};function a(e,g,f){if(e<=g){return e}return Math.max(g,e+f)}c.prototype.onChange=function(k){var l=k.from.line;var e=b.changeEnd(k).line;var f=e-k.to.line;if(this.gap){this.gap.from=Math.min(a(this.gap.from,l,f),k.from.line);this.gap.to=Math.max(a(this.gap.to,l,f),k.from.line)}else{this.gap={from:k.from.line,to:e+1}}if(f){for(var h=0;hv.cursorCoords(H,"window").top){(w=G).style.opacity=0.4}})};d(v,u,B,A,function(E,G){var D=n.keyName(E);var F=n.keyMap[v.getOption("keyMap")][D];if(!F){F=v.getOption("extraKeys")[D]}if(F=="findNext"||F=="findPrev"||F=="findPersistentNext"||F=="findPersistentPrev"){n.e_stop(E);t(v,s(v),G);v.execCommand(F)}else{if(F=="find"||F=="findPersistent"){n.e_stop(E);A(G,E)}}});if(z&&B){t(v,C,B);f(v,y)}}else{p(v,u,"Search for:",B,function(D){if(D&&!C.query){v.operation(function(){t(v,C,D);C.posFrom=C.posTo=v.getCursor();f(v,y)})}})}}function f(v,w,x){v.operation(function(){var y=s(v);var z=b(v,y.query,w?y.posFrom:y.posTo);if(!z.find(w)){z=b(v,y.query,w?n.Pos(v.lastLine()):n.Pos(v.firstLine(),0));if(!z.find(w)){return}}v.setSelection(z.from(),z.to());v.scrollIntoView({from:z.from(),to:z.to()},20);y.posFrom=z.from();y.posTo=z.to();if(x){x(z.from(),z.to())}})}function q(v){v.operation(function(){var w=s(v);w.lastQuery=w.query;if(!w.query){return}w.query=w.queryText=null;v.removeOverlay(w.overlay);if(w.annotate){w.annotate.clear();w.annotate=null}})}var a=' (Use /re/ syntax for regexp search)';var g='到: ';var o="替换? ";function c(v,w,x){v.operation(function(){for(var z=b(v,w);z.findNext();){if(typeof w!="string"){var y=v.getRange(z.from(),z.to()).match(w);z.replace(x.replace(/\$(\d)/g,function(A,B){return y[B]}))}else{z.replace(x)}}})}function r(v,x){if(v.getOption("readOnly")){return}var y=v.getSelection()||s(v).lastQuery;var w=x?"全部替换:":"替换:";p(v,w+a,w,y,function(z){if(!z){return}z=h(z);p(v,g,"更换:","",function(D){D=i(D);if(x){c(v,z,D)}else{q(v);var C=b(v,z,v.getCursor("from"));var B=function(){var F=C.from(),E;if(!(E=C.findNext())){C=b(v,z);if(!(E=C.findNext())||(F&&C.from().line==F.line&&C.from().ch==F.ch)){return}}v.setSelection(C.from(),C.to());v.scrollIntoView({from:C.from(),to:C.to()});l(v,o,"替换?",[function(){A(E)},B,function(){c(v,z,D)}])};var A=function(E){C.replace(typeof z=="string"?D:D.replace(/\$(\d)/g,function(F,G){return E[G]}));B()};B()}})})}n.commands.find=function(v){q(v);m(v)};n.commands.findPersistent=function(v){q(v);m(v,false,true)};n.commands.findPersistentNext=function(v){m(v,false,true,true)};n.commands.findPersistentPrev=function(v){m(v,true,true,true)};n.commands.findNext=m;n.commands.findPrev=function(v){m(v,true)};n.commands.clearSearch=q;n.commands.replace=r;n.commands.replaceAll=function(v){r(v,true)}});(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],a)}else{a(CodeMirror)}}})(function(a){var b=a.Pos;function c(k,i,l,g){this.atOccurrence=false;this.doc=k;if(g==null&&typeof i=="string"){g=false}l=l?k.clipPos(l):b(0,0);this.pos={from:l,to:l};if(typeof i!="string"){if(!i.global){i=new RegExp(i.source,i.ignoreCase?"ig":"g")}this.matches=function(p,t){if(p){i.lastIndex=0;var m=k.getLine(t.line).slice(0,t.ch),r=0,o,s;for(;;){i.lastIndex=r;var q=i.exec(m);if(!q){break}o=q;s=o.index;r=o.index+(o[0].length||1);if(r==m.length){break}}var n=(o&&o[0].length)||0;if(!n){if(s==0&&m.length==0){o=undefined}else{if(s!=k.getLine(t.line).length){n++}}}}else{i.lastIndex=t.ch;var m=k.getLine(t.line),o=i.exec(m);var n=(o&&o[0].length)||0;var s=o&&o.index;if(s+n!=m.length&&!n){n=1}}if(o&&n){return{from:b(t.line,s),to:b(t.line,s+n),match:o}}}}else{var e=i;if(g){i=i.toLowerCase()}var f=g?function(m){return m.toLowerCase()}:function(m){return m};var j=i.split("\n");if(j.length==1){if(!i.length){this.matches=function(){}}else{this.matches=function(o,q){if(o){var p=k.getLine(q.line).slice(0,q.ch),m=f(p);var n=m.lastIndexOf(i);if(n>-1){n=d(p,m,n);return{from:b(q.line,n),to:b(q.line,n+e.length)}}}else{var p=k.getLine(q.line).slice(q.ch),m=f(p);var n=m.indexOf(i);if(n>-1){n=d(p,m,n)+q.ch;return{from:b(q.line,n),to:b(q.line,n+e.length)}}}}}}else{var h=e.split("\n");this.matches=function(n,p){var t=j.length-1;if(n){if(p.line-(j.length-1)=1;--m,--o){if(j[m]!=f(k.getLine(o))){return}}var u=k.getLine(o),q=u.length-h[0].length;if(f(u.slice(q))!=j[0]){return}return{from:b(o,q),to:s}}else{if(p.line+(j.length-1)>k.lastLine()){return}var u=k.getLine(p.line),q=u.length-h[0].length;if(f(u.slice(q))!=j[0]){return}var r=b(p.line,q);for(var o=p.line+1,m=1;mh){--f}else{return f}}}}a.defineExtension("getSearchCursor",function(f,g,e){return new c(this.doc,f,g,e)});a.defineDocExtension("getSearchCursor",function(f,g,e){return new c(this,f,g,e)});a.defineExtension("selectMatches",function(g,f){var e=[];var h=this.getSearchCursor(g,this.getCursor("from"),f);while(h.findNext()){if(a.cmpPos(h.to(),this.getCursor("to"))>0){break}e.push({anchor:h.from(),head:h.to()})}if(e.length){this.setSelections(e,0)}})});(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("../dialog/dialog"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","../dialog/dialog"],a)}else{a(CodeMirror)}}})(function(a){function b(e,i,g,j,h){if(e.openDialog){e.openDialog(i,h,{value:j,selectValueOnOpen:true})}else{h(prompt(g,j))}}var d='Jump to line: (Use line:column or scroll% syntax)';function c(e,g){var f=Number(g);if(/^[-+]/.test(g)){return e.getCursor().line+f}else{return f-1}}a.commands.jumpToLine=function(e){var f=e.getCursor();b(e,d,"Jump to line:",(f.line+1)+":"+f.ch,function(i){if(!i){return}var h;if(h=/^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(i)){e.setCursor(c(e,h[1]),Number(h[2]))}else{if(h=/^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(i)){var g=Math.round(e.lineCount()*Number(h[1])/100);if(/^[-+]/.test(h[1])){g=f.line+g+1}e.setCursor(g-1,f.ch)}else{if(h=/^\s*\:?\s*([\+\-]?\d+)\s*/.exec(i)){e.setCursor(c(e,h[1]),f.ch)}}}})};a.keyMap["default"]["Alt-G"]="jumpToLine"});(function(a){if(typeof exports=="object"&&typeof module=="object"){a(require("../../lib/codemirror"),require("./searchcursor"),require("../scroll/annotatescrollbar"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","./searchcursor","../scroll/annotatescrollbar"],a)}else{a(CodeMirror)}}})(function(b){b.defineExtension("showMatchesOnScrollbar",function(g,f,e){if(typeof e=="string"){e={className:e}}if(!e){e={}}return new c(this,g,f,e)});function c(e,j,i,h){this.cm=e;this.options=h;var f={listenForChanges:false};for(var k in h){f[k]=h[k]}if(!f.className){f.className="CodeMirror-search-match"}this.annotation=e.annotateScrollbar(f);this.query=j;this.caseFold=i;this.gap={from:e.firstLine(),to:e.lastLine()+1};this.matches=[];this.update=null;this.findMatches();this.annotation.update(this.matches);var g=this;e.on("change",this.changeHandler=function(l,m){g.onChange(m)})}var d=1000;c.prototype.findMatches=function(){if(!this.gap){return}for(var g=0;g=this.gap.to){break}if(f.to.line>=this.gap.from){this.matches.splice(g--,1)}}var h=this.cm.getSearchCursor(this.query,b.Pos(this.gap.from,0),this.caseFold);var e=this.options&&this.options.maxMatches||d;while(h.findNext()){var f={from:h.from(),to:h.to()};if(f.from.line>=this.gap.to){break}this.matches.splice(g++,0,f);if(this.matches.length>e){break}}this.gap=null};function a(e,g,f){if(e<=g){return e}return Math.max(g,e+f)}c.prototype.onChange=function(k){var l=k.from.line;var e=b.changeEnd(k).line;var f=e-k.to.line;if(this.gap){this.gap.from=Math.min(a(this.gap.from,l,f),k.from.line);this.gap.to=Math.max(a(this.gap.to,l,f),k.from.line)}else{this.gap={from:k.from.line,to:e+1}}if(f){for(var h=0;h=n.options.minChars){b(m,q,false,n.options.style)}})}function g(m,r,q){var o=m.getRange(r,q);if(o.match(/^\w+$/)!==null){if(r.ch>0){var p={line:r.line,ch:r.ch-1};var n=m.getRange(p,r);if(n.match(/\W/)===null){return false}}if(q.ch-1){n=d(p,m,n);return{from:b(q.line,n),to:b(q.line,n+e.length)}}}else{var p=k.getLine(q.line).slice(q.ch),m=f(p);var n=m.indexOf(i);if(n>-1){n=d(p,m,n)+q.ch;return{from:b(q.line,n),to:b(q.line,n+e.length)}}}}}}else{var h=e.split("\n");this.matches=function(n,p){var t=j.length-1;if(n){if(p.line-(j.length-1)=1;--m,--o){if(j[m]!=f(k.getLine(o))){return}}var u=k.getLine(o),q=u.length-h[0].length;if(f(u.slice(q))!=j[0]){return}return{from:b(o,q),to:s}}else{if(p.line+(j.length-1)>k.lastLine()){return}var u=k.getLine(p.line),q=u.length-h[0].length;if(f(u.slice(q))!=j[0]){return}var r=b(p.line,q);for(var o=p.line+1,m=1;mh){--f}else{return f}}}}a.defineExtension("getSearchCursor",function(f,g,e){return new c(this.doc,f,g,e)});a.defineDocExtension("getSearchCursor",function(f,g,e){return new c(this,f,g,e)});a.defineExtension("selectMatches",function(g,f){var e=[];var h=this.getSearchCursor(g,this.getCursor("from"),f);while(h.findNext()){if(a.cmpPos(h.to(),this.getCursor("to"))>0){break}e.push({anchor:h.from(),head:h.to()})}if(e.length){this.setSelections(e,0)}})}); \ No newline at end of file diff --git a/route/static/codemirror/index.html b/route/static/codemirror/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/codemirror/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/codemirror/lib/codemirror.css b/route/static/codemirror/lib/codemirror.css deleted file mode 100755 index 48e2ac5a8..000000000 --- a/route/static/codemirror/lib/codemirror.css +++ /dev/null @@ -1,4 +0,0 @@ -.CodeMirror{font-family:monospace;height:auto;color:black;border:#ccc 1px solid}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{background-color:white}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:black}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid black;border-right:0;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-animate-fat-cursor{width:auto;border:0;-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite;background-color:#7e7}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:-20px;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:blue}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:bold}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta{color:#555}.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-s-default .cm-error{color:#f00}.cm-invalidchar{color:#f00}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0f0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#f22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:white}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;height:100%;outline:0;position:relative}.CodeMirror-sizer{position:relative;border-right:30px solid transparent}.CodeMirror-vscrollbar,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{position:absolute;z-index:6;display:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-30px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:none!important;border:none!important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper{-webkit-user-select:none;-moz-user-select:none;user-select:none}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:transparent;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:none;font-variant-ligatures:none}.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;overflow:auto}.CodeMirror-code{outline:0}.CodeMirror-scroll,.CodeMirror-sizer,.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background:#ffa;background:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0} -.CodeMirror-hints{position:absolute;z-index:10;overflow:hidden;list-style:none;margin:0;padding:2px;-webkit-box-shadow:2px 3px 5px rgba(0,0,0,.2);-moz-box-shadow:2px 3px 5px rgba(0,0,0,.2);box-shadow:2px 3px 5px rgba(0,0,0,.2);border-radius:3px;border:1px solid silver;background:white;font-size:90%;font-family:monospace;max-height:20em;overflow-y:auto}.CodeMirror-hint{margin:0;padding:0 4px;border-radius:2px;white-space:pre;color:black;cursor:pointer}li.CodeMirror-hint-active{background:#08f;color:white} -.CodeMirror-dialog{position:absolute;left:0;right:0;background:inherit;z-index:15;padding:.4em .8em;overflow:hidden;color:inherit}.CodeMirror-dialog-top{border-bottom:1px solid #eee;top:0}.CodeMirror-dialog-bottom{border-top:1px solid #eee;bottom:0}.CodeMirror-dialog input{border:0;outline:0;background:transparent;width:20em;color:inherit;font-family:monospace}.CodeMirror-dialog button{font-size:70%}.Dialog-close{color:#111;float:right;font-family:Arial;font-size:16px;height:30px;line-height:30px;text-align:center;width:30px;cursor:pointer} -.CodeMirror-search-match{background:gold;border-top:1px solid orange;border-bottom:1px solid orange;-moz-box-sizing:border-box;box-sizing:border-box;opacity:.5} \ No newline at end of file diff --git a/route/static/codemirror/lib/codemirror.js b/route/static/codemirror/lib/codemirror.js deleted file mode 100755 index b9cb291c8..000000000 --- a/route/static/codemirror/lib/codemirror.js +++ /dev/null @@ -1 +0,0 @@ -(function(c,d){typeof exports==="object"&&typeof module!=="undefined"?module.exports=d():typeof define==="function"&&define.amd?define(d):(c.CodeMirror=d())}(this,(function(){var iP=navigator.userAgent;var ir=navigator.platform;var kc=/gecko\/\d/i.test(iP);var kv=/MSIE \d/.test(iP);var nf=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(iP);var kj=kv||nf;var kH=kj&&(kv?document.documentMode||6:nf[1]);var mU=/WebKit\//.test(iP);var j6=mU&&/Qt\/\d+\.\d+/.test(iP);var ju=/Chrome\//.test(iP);var iL=/Opera\//.test(iP);var ob=/Apple Computer/.test(navigator.vendor);var jI=/Mac OS X 1\d\D([8-9]|\d\d)\D/.test(iP);var iq=/PhantomJS/.test(iP);var hE=/AppleWebKit/.test(iP)&&/Mobile\/\w+/.test(iP);var ie=hE||/Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(iP);var k9=hE||/Mac/.test(ir);var i6=/\bCrOS\b/.test(iP);var nW=/win/i.test(ir);var nE=iL&&iP.match(/Version\/(\d*\.\d*)/);if(nE){nE=Number(nE[1])}if(nE&&nE>=15){iL=false;mU=true}var mW=k9&&(j6||iL&&(nE==null||nE<12.11));var hT=kc||(kj&&kH>=9);function lO(a){return new RegExp("(^|\\s)"+a+"(?:$|\\s)\\s*")}var kS=function(d,c){var b=d.className;var e=lO(c).exec(b);if(e){var a=b.slice(e.index+e[0].length);d.className=b.slice(0,e.index)+(a?e[1]+a:"")}};function iN(b){for(var a=b.childNodes.length;a>0;--a){b.removeChild(b.firstChild)}return b}function mR(a,b){return iN(a).appendChild(b)}function gZ(f,b,c,d){var a=document.createElement(f);if(c){a.className=c}if(d){a.style.cssText=d}if(typeof b=="string"){a.appendChild(document.createTextNode(b))}else{if(b){for(var e=0;e=f){return h+(f-c)}h+=g-c;h+=b-(h%b);c=g+1}}function hA(){this.id=null}hA.prototype.set=function(a,b){clearTimeout(this.id);this.id=setTimeout(b,a)};function jb(a,c){for(var b=0;b=e){return a+Math.min(d,e-f)}f+=g-a;f+=b-(f%b);a=g+1;if(f>=e){return a}}}var gU=[""];function j7(a){while(gU.length<=a){gU.push(ji(gU)+" ")}return gU[a]}function ji(a){return a[a.length-1]}function mO(a,b){var d=[];for(var c=0;c"\x80"&&(a.toUpperCase()!=a.toLowerCase()||mu.test(a))}function mo(a,b){if(!b){return h0(a)}if(b.source.indexOf("\\w")>-1&&h0(a)){return true}return b.test(a)}function hS(a){for(var b in a){if(a.hasOwnProperty(b)&&a[b]){return false}}return true}var ky=/[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;function iB(a){return a.charCodeAt(0)>=768&&ky.test(a)}function iU(b,c,d){var a=this;this.input=d;a.scrollbarFiller=gZ("div",null,"CodeMirror-scrollbar-filler");a.scrollbarFiller.setAttribute("cm-not-content","true");a.gutterFiller=gZ("div",null,"CodeMirror-gutter-filler");a.gutterFiller.setAttribute("cm-not-content","true");a.lineDiv=gZ("div",null,"CodeMirror-code");a.selectionDiv=gZ("div",null,null,"position: relative; z-index: 1");a.cursorDiv=gZ("div",null,"CodeMirror-cursors");a.measure=gZ("div",null,"CodeMirror-measure");a.lineMeasure=gZ("div",null,"CodeMirror-measure");a.lineSpace=gZ("div",[a.measure,a.lineMeasure,a.selectionDiv,a.cursorDiv,a.lineDiv],null,"position: relative; outline: none");a.mover=gZ("div",[gZ("div",[a.lineSpace],"CodeMirror-lines")],null,"position: relative");a.sizer=gZ("div",[a.mover],"CodeMirror-sizer");a.sizerWidth=null;a.heightForcer=gZ("div",null,null,"position: absolute; height: "+ko+"px; width: 1px;");a.gutters=gZ("div",null,"CodeMirror-gutters");a.lineGutter=null;a.scroller=gZ("div",[a.sizer,a.heightForcer,a.gutters],"CodeMirror-scroll");a.scroller.setAttribute("tabIndex","-1");a.wrapper=gZ("div",[a.scrollbarFiller,a.gutterFiller,a.scroller],"CodeMirror");if(kj&&kH<8){a.gutters.style.zIndex=-1;a.scroller.style.paddingRight=0}if(!mU&&!(kc&&ie)){a.scroller.draggable=true}if(b){if(b.appendChild){b.appendChild(a.wrapper)}else{b(a.wrapper)}}a.viewFrom=a.viewTo=c.first;a.reportedViewFrom=a.reportedViewTo=c.first;a.view=[];a.renderedView=null;a.externalMeasured=null;a.viewOffset=0;a.lastWrapHeight=a.lastWrapWidth=0;a.updateLineNumbers=null;a.nativeBarWidth=a.barHeight=a.barWidth=0;a.scrollbarsClipped=false;a.lineNumWidth=a.lineNumInnerWidth=a.lineNumChars=null;a.alignWidgets=false;a.cachedCharWidth=a.cachedTextHeight=a.cachedPaddingH=null;a.maxLine=null;a.maxLineLength=0;a.maxLineChanged=false;a.wheelDX=a.wheelDY=a.wheelStartX=a.wheelStartY=null;a.shift=false;a.selForContextMenu=null;a.activeTouch=null;d.init(a)}function hm(c,a){a-=c.first;if(a<0||a>=c.size){throw new Error("There is no line "+(a+c.first)+" in the document.")}var f=c;while(!f.lines){for(var e=0;;++e){var b=f.children[e],d=b.chunkSize();if(a=b.first&&aa){return lA(a,hm(c,a).text.length)}return iu(b,hm(c,b.line).text.length)}function iu(b,c){var a=b.ch;if(a==null||a>c){return lA(b.line,c)}else{if(a<0){return lA(b.line,0)}else{return b}}}function lo(b,a){var d=[];for(var c=0;c=g:a.to>g);(b||(b=[])).push(new h7(d,a.from,f?null:a.to))}}}return b}function oc(h,f,c){var b;if(h){for(var e=0;e=f:a.to>f);if(g||a.from==f&&d.type=="bookmark"&&(!c||a.marker.insertLeft)){var j=a.from==null||(d.inclusiveLeft?a.from<=f:a.from0&&h){for(var l=0;l0){continue}var d=[k,1],n=kI(m.from,l.from),e=kI(m.to,l.to);if(n<0||!f.inclusiveLeft&&!n){d.push({from:m.from,to:l.from})}if(e>0||!f.inclusiveRight&&!e){d.push({from:l.to,to:m.to})}h.splice.apply(h,d);k+=d.length-1}}return h}function hV(c){var a=c.markedSpans;if(!a){return}for(var b=0;b=0&&g<=0||c<=0&&g>=0){continue}if(c<=0&&(k.marker.inclusiveRight&&h.inclusiveLeft?kI(b.to,f)>=0:kI(b.to,f)>0)||c>=0&&(k.marker.inclusiveRight&&h.inclusiveLeft?kI(b.from,e)<=0:kI(b.from,e)<0)){return true}}}}function jV(b){var a;while(a=kb(b)){b=a.find(-1,true).line}return b}function kW(b){var a,c;while(a=jy(b)){b=a.find(1,true).line;(c||(c=[])).push(b)}return c}function nJ(b,d){var a=hm(b,d),c=jV(a);if(a==c){return d}return m5(c)}function iJ(b,c){if(c>b.lastLine()){return c}var d=hm(b,c),a;if(!il(b,d)){return c}while(a=jy(d)){d=a.find(1,true).line}return m5(d)+1}function il(a,d){var e=gN&&d.markedSpans;if(e){for(var b=void 0,c=0;cb.maxLineLength){b.maxLineLength=e;b.maxLine=d}})}function iG(g,a,b,c){if(!g){return c(a,b,"ltr")}var d=false;for(var e=0;ea||a==b&&f.to==a){c(Math.max(f.from,a),Math.min(f.to,b),f.level==1?"rtl":"ltr");d=true}}if(!d){c(a,b,"ltr")}}function kT(a){return a.level%2?a.to:a.from}function hJ(a){return a.level%2?a.from:a.to}function md(b){var a=ld(b);return a?kT(a[0]):0}function lj(b){var a=ld(b);if(!a){return b.text.length}return hJ(ji(a))}function ne(d,c,b){var a=d[0].level;if(c==a){return true}if(b==a){return false}return ca){return d}if((b.from==a||b.to==a)){if(c==null){c=d}else{if(ne(e,b.level,e[c].level)){if(b.from!=b.to){hz=c}return d}else{if(b.from!=b.to){hz=d}return c}}}}return c}function hl(b,a,d,c){if(!c){return a+d}do{a+=d}while(a>0&&iB(b.text.charAt(a)));return a}function ka(f,a,g,e){var d=ld(f);if(!d){return nn(f,a,g,e)}var b=n6(d,a),h=d[b];var c=hl(f,a,h.level%2?-g:g,e);for(;;){if(c>h.from&&c0)==h.level%2?h.to:h.from}else{h=d[b+=g];if(!h){return null}if((g>0)==h.level%2){c=hl(f,h.to,-1,e)}else{c=hl(f,h.from,1,e)}}}}function nn(c,a,e,d){var b=a+e;if(d){while(b>0&&iB(c.text.charAt(b))){b+=e}}return b<0||b>c.text.length?null:b}var mc=(function(){var e="bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";var g="rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm";function h(l){if(l<=247){return e.charAt(l)}else{if(1424<=l&&l<=1524){return"R"}else{if(1536<=l&&l<=1773){return g.charAt(l-1536)}else{if(1774<=l&&l<=2220){return"r"}else{if(8192<=l&&l<=8203){return"w"}else{if(l==8204){return"b"}else{return"L"}}}}}}}var b=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;var a=/[stwN]/,j=/[LRr]/,k=/[Lb1n]/,f=/[1n]/;var c="L";function d(l,m,n){this.level=l;this.from=m;this.to=n}return function(s){if(!b.test(s)){return false}var R=s.length,F=[];for(var l=0;l-1){d[e]=c.slice(0,f).concat(c.slice(f+1))}}}}}function n9(a,b){var e=h4(a,b);if(!e.length){return}var d=Array.prototype.slice.call(arguments,2);for(var c=0;c0}function nA(a){a.prototype.on=function(c,b){nY(this,c,b)};a.prototype.off=function(c,b){ih(this,c,b)}}function l7(a){if(a.preventDefault){a.preventDefault()}else{a.returnValue=false}}function iT(a){if(a.stopPropagation){a.stopPropagation()}else{a.cancelBubble=true}}function na(a){return a.defaultPrevented!=null?a.defaultPrevented:a.returnValue==false}function jK(a){l7(a);iT(a)}function mb(a){return a.target||a.srcElement}function hi(b){var a=b.which;if(a==null){if(b.button&1){a=1}else{if(b.button&2){a=3}else{if(b.button&4){a=2}}}}if(k9&&b.ctrlKey&&a==1){a=3}return a}var kr=function(){if(kj&&kH<9){return false}var a=gZ("div");return"draggable" in a||"dragDrop" in a}();var hk;function lP(a){if(hk==null){var b=gZ("span","\u200b");mR(a,gZ("span",[b,document.createTextNode("x")]));if(a.firstChild.offsetHeight!=0){hk=b.offsetWidth<=1&&b.offsetHeight>2&&!(kj&&kH<8)}}var c=hk?gZ("span","\u200b"):gZ("span","\u00a0",null,"display: inline-block; width: 1px; margin-right: -1px");c.setAttribute("cm-text","");return c}var hj;function m2(b){if(hj!=null){return hj}var a=mR(b,document.createTextNode("A\u062eA"));var c=ki(a,0,1).getBoundingClientRect();var d=ki(a,1,2).getBoundingClientRect();iN(b);if(!c||c.left==c.right){return false}return hj=(d.right-c.right<3)}var hd="\n\nb".split(/\n/).length!=3?function(b){var a=0,e=[],c=b.length;while(a<=c){var d=b.indexOf("\n",a);if(d==-1){d=b.length}var f=b.slice(a,b.charAt(d-1)=="\r"?d-1:d);var g=f.indexOf("\r");if(g!=-1){e.push(f.slice(0,g));a+=g+1}else{e.push(f);a=d+1}}return e}:function(a){return a.split(/\r\n?|\n/)};var lv=window.getSelection?function(b){try{return b.selectionStart!=b.selectionEnd}catch(a){return false}}:function(b){var c;try{c=b.ownerDocument.selection.createRange()}catch(a){}if(!c||c.parentElement()!=b){return false}return c.compareEndPoints("StartToEnd",c)!=0};var jz=(function(){var a=gZ("div");if("oncopy" in a){return true}a.setAttribute("oncopy","return;");return typeof a.oncopy=="function"})();var hu=null;function n2(d){if(hu!=null){return hu}var c=mR(d,gZ("span","x"));var b=c.getBoundingClientRect();var a=ki(c,0,1).getBoundingClientRect();return hu=Math.abs(b.left-a.left)>1}var le={};var nQ={};function jE(a,b){if(arguments.length>2){b.dependencies=Array.prototype.slice.call(arguments,2)}le[a]=b}function lT(b,a){nQ[b]=a}function hP(a){if(typeof a=="string"&&nQ.hasOwnProperty(a)){a=nQ[a]}else{if(a&&typeof a.name=="string"&&nQ.hasOwnProperty(a.name)){var b=nQ[a.name];if(typeof b=="string"){b={name:b}}a=ks(b,a);a.name=b.name}else{if(typeof a=="string"&&/^[\w\-]+\/[\w\-]+\+xml$/.test(a)){return hP("application/xml")}else{if(typeof a=="string"&&/^[\w\-]+\/[\w\-]+\+json$/.test(a)){return hP("application/json")}}}}if(typeof a=="string"){return{name:a}}else{return a||{name:"null"}}}function h8(f,g){g=hP(g);var c=le[g.name];if(!c){return h8(f,"text/plain")}var b=c(f,g);if(iV.hasOwnProperty(g.name)){var e=iV[g.name];for(var a in e){if(!e.hasOwnProperty(a)){continue}if(b.hasOwnProperty(a)){b["_"+a]=b[a]}b[a]=e[a]}}b.name=g.name;if(g.helperType){b.helperType=g.helperType}if(g.modeProps){for(var d in g.modeProps){b[d]=g.modeProps[d]}}return b}var iV={};function jc(b,c){var a=iV.hasOwnProperty(b)?iV[b]:(iV[b]={});n0(c,a)}function nK(c,b){if(b===true){return b}if(c.copyState){return c.copyState(b)}var d={};for(var a in b){var e=b[a];if(e instanceof Array){e=e.concat([])}d[a]=e}return d}function hX(b,a){var c;while(b.innerMode){c=b.innerMode(a);if(!c||c.mode==b){break}a=c.state;b=c.mode}return c||{mode:b,state:a}}function nR(b,c,a){return b.startState?b.startState(c,a):true}var hU=function(a,b){this.pos=this.start=0;this.string=a;this.tabSize=b||8;this.lastColumnPos=this.lastColumnValue=0;this.lineStart=0};hU.prototype={eol:function(){return this.pos>=this.string.length},sol:function(){return this.pos==this.lineStart},peek:function(){return this.string.charAt(this.pos)||undefined},next:function(){if(this.posb},eatSpace:function(){var b=this;var a=this.pos;while(/[\s\u00a0]/.test(this.string.charAt(this.pos))){++b.pos}return this.pos>a},skipToEnd:function(){this.pos=this.string.length},skipTo:function(a){var b=this.string.indexOf(a,this.pos);if(b>-1){this.pos=b;return true}},backUp:function(a){this.pos-=a},column:function(){if(this.lastColumnPos0){return null}if(e&&f!==false){this.pos+=e[0].length}return e}},current:function(){return this.string.slice(this.start,this.pos)},hideFirstChars:function(b,a){this.lineStart+=b;try{return a()}finally{this.lineStart-=b}}};function ib(f,g,b,c){var d=[f.state.modeGen],e={};j2(f,g.text,f.doc.mode,b,function(k,j){return d.push(k,j)},e,c);var h=function(j){var l=f.state.overlays[j],k=1,m=0;j2(f,g.text,l.mode,true,function(r,p){var n=k;while(mr){d.splice(k,1,r,d[k+1],q)}k+=2;m=Math.min(r,q)}if(!p){return}if(l.opaque){d.splice(n,k-n,r,"overlay "+p);k=n+2}else{for(;ne.options.maxHighlightLength?nK(e.doc.mode,c):c);d.stateAfter=c;d.styles=b.styles;if(b.classes){d.styleClasses=b.classes}else{if(d.styleClasses){d.styleClasses=null}}if(a===e.doc.frontier){e.doc.frontier++}}return d.styles}function kM(e,a,g){var c=e.doc,d=e.display;if(!c.mode.startState){return true}var b=mv(e,a,g),f=b>c.first&&hm(c,b-1).stateAfter;if(!f){f=nR(c.mode)}else{f=nK(c.mode,f)}c.iter(b,a,function(h){k0(e,h.text,f);var j=b==a-1||b%5==0||b>=d.viewFrom&&bb.start){return d}}throw new Error("Mode "+a.name+" failed to advance stream.")}function j4(c,f,j,k){var d=function(n){return({start:b.start,end:b.pos,string:b.current(),type:l||null,state:n?nK(e.mode,m):m})};var e=c.doc,h=e.mode,l;f=i3(e,f);var a=hm(e,f.line),m=kM(c,f.line,j);var b=new hU(a.text,c.options.tabSize),g;if(k){g=[]}while((k||b.pose.options.maxHighlightLength){o=false;if(m){k0(e,c,p,d.pos)}d.pos=c.length;g=null}else{g=i8(jj(l,d,p,a),n)}if(a){var b=a[0].name;if(b){g="m-"+(g?b+" "+g:b)}}if(!o||j!=g){while(hj;--a){if(a<=f.first){return f.first}var c=hm(f,a-1);if(c.stateAfter&&(!h||a<=f.frontier)){return a}var d=mV(c.text,null,e.options.tabSize);if(g==null||k>d){g=a-1;k=d}}return g}function gW(b,c,a){this.text=b;mM(this,c);this.height=a?a(this):1}nA(gW);gW.prototype.lineNo=function(){return m5(this)};function hY(e,a,d,c){e.text=a;if(e.stateAfter){e.stateAfter=null}if(e.styles){e.styles=null}if(e.order!=null){e.order=null}hV(e);mM(e,d);var b=c?c(e):1;if(b!=e.height){gV(e,b)}}function nv(a){a.parent=null;hV(a)}var lE={};var nM={};function hO(b,c){if(!b||/^\s*$/.test(b)){return null}var a=c.addModeClass?nM:lE;return a[b]||(a[b]=b.replace(/\S+/g,"cm-$&"))}function j0(c,d){var e=gZ("span",null,null,mU?"padding-right: .1px":null);var f={pre:gZ("pre",[e],"CodeMirror-line"),content:e,col:0,pos:0,cm:c,trailingSpace:false,splitSpaces:(kj||mU)&&c.getOption("lineWrapping")};d.measure={};for(var g=0;g<=(d.rest?d.rest.length:0);g++){var a=g?d.rest[g-1]:d.line,h=void 0;f.pos=0;f.addToken=ke;if(m2(c.display.measure)&&(h=ld(a))){f.addToken=lF(f.addToken,h)}f.map=[];var j=d!=c.display.externalMeasured&&m5(a);lK(a,f,jG(c,a,j));if(a.styleClasses){if(a.styleClasses.bgClass){f.bgClass=g9(a.styleClasses.bgClass,f.bgClass||"")}if(a.styleClasses.textClass){f.textClass=g9(a.styleClasses.textClass,f.textClass||"")}}if(f.map.length==0){f.map.push(0,0,f.content.appendChild(lP(c.display.measure)))}if(g==0){d.measure.map=f.map;d.measure.cache={}}else{(d.measure.maps||(d.measure.maps=[])).push(f.map);(d.measure.caches||(d.measure.caches=[])).push({})}}if(mU){var b=f.content.lastChild;if(/\bcm-tab\b/.test(b.className)||(b.querySelector&&b.querySelector(".cm-tab"))){f.content.className="cm-tab-wrap-hack"}}n9(c,"renderLine",c,d.line,f.pre);if(f.pre.className){f.textClass=g9(f.pre.className,f.textClass||"")}return f}function hn(b){var a=gZ("span","\u2022","cm-invalidchar");a.title="\\u"+b.charCodeAt(0).toString(16);a.setAttribute("aria-label",a.title);return a}function ke(e,k,u,c,g,r,l){if(!k){return}var a=e.splitSpaces?ma(k,e.trailingSpace):k;var q=e.cm.state.specialChars,p=false;var b;if(!q.test(k)){e.col+=k.length;b=document.createTextNode(a);e.map.push(e.pos,e.pos+k.length,b);if(kj&&kH<9){p=true}e.pos+=k.length}else{b=document.createDocumentFragment();var n=0;while(true){q.lastIndex=n;var d=q.exec(k);var s=d?d.index-n:k.length-n;if(s){var h=document.createTextNode(a.slice(n,n+s));if(kj&&kH<9){b.appendChild(gZ("span",[h]))}else{b.appendChild(h)}e.map.push(e.pos,e.pos+s,h);e.col+=s;e.pos+=s}if(!d){break}n+=s+1;var t=void 0;if(d[0]=="\t"){var f=e.cm.options.tabSize,j=f-e.col%f;t=b.appendChild(gZ("span",j7(j),"cm-tab"));t.setAttribute("role","presentation");t.setAttribute("cm-text","\t");e.col+=j}else{if(d[0]=="\r"||d[0]=="\n"){t=b.appendChild(gZ("span",d[0]=="\r"?"\u240d":"\u2424","cm-invalidchar"));t.setAttribute("cm-text",d[0]);e.col+=1}else{t=e.cm.options.specialCharPlaceholder(d[0]);t.setAttribute("cm-text",d[0]);if(kj&&kH<9){b.appendChild(gZ("span",[t]))}else{b.appendChild(t)}e.col+=1}}e.map.push(e.pos,e.pos+1,t);e.pos++}}e.trailingSpace=a.charCodeAt(k.length-1)==32;if(u||c||g||p||l){var o=u||"";if(c){o+=c}if(g){o+=g}var m=gZ("span",[b],o,l);if(r){m.title=r}return e.content.appendChild(m)}e.content.appendChild(b)}function ma(a,b){if(a.length>1&&!/ /.test(a)){return a}var e=b,f="";for(var d=0;dm&&l.from<=m){break}}if(l.to>=k){return b(f,d,n,j,c,e,g)}b(f,d.slice(0,l.to-m),n,j,null,e,g);j=null;d=d.slice(l.to-m);m=l.to}}}function ny(e,c,b,d){var a=!d&&b.widgetNode;if(a){e.map.push(e.pos,e.pos+c,a)}if(!d&&e.cm.display.input.needsContentAttribute){if(!a){a=e.content.appendChild(document.createElement("span"))}a.setAttribute("cm-marker",b.id)}if(a){e.cm.display.input.setUneditable(a);e.content.appendChild(a)}e.pos+=c;e.trailingSpace=false}function lK(d,A,e){var o=d.markedSpans,h=d.text,C=0;if(!o){for(var l=1;lq||B.collapsed&&y.to==q&&y.from==q)){if(y.to!=null&&y.to!=q&&k>y.to){k=y.to;m=""}if(B.className){v+=" "+B.className}if(B.css){c=(c?c+";":"")+B.css}if(B.startStyle&&y.from==q){D+=" "+B.startStyle}if(B.endStyle&&y.to==k){(f||(f=[])).push(B.endStyle,y.to)}if(B.title&&!g){g=B.title}if(B.collapsed&&(!t||lW(t.marker,B)<0)){t=y}}else{if(y.from>q&&k>y.from){k=y.from}}}}if(f){for(var n=0;n=w){break}var b=Math.min(w,k);while(true){if(a){var u=q+a.length;if(!t){var r=u>b?a.slice(0,b-q):a;A.addToken(A,r,s?s+v:v,D,q+r.length==k?m:"",g,c)}if(u>=b){a=a.slice(b-q);q=b;break}q=u;D=""}a=h.slice(C,C=e[x++]);s=hO(e[x++],A.cm.options)}}}function lk(b,a,c){this.line=a;this.rest=kW(a);this.size=this.rest?m5(ji(this.rest))-c+1:1;this.node=this.text=null;this.hidden=il(b,a)}function hQ(e,a,b){var c=[],f;for(var d=a;d2){e.push((b.bottom+j.top)/2-d.top)}}}e.push(d.bottom-d.top)}}function jT(c,e,b){if(c.line==e){return{map:c.measure.map,cache:c.measure.cache}}for(var d=0;db){return{map:c.measure.maps[a],cache:c.measure.caches[a],before:true}}}}function mP(c,d){d=jV(d);var a=m5(d);var e=c.display.externalMeasured=new lk(c.doc,d,a);e.lineN=a;var b=e.built=j0(c,e);e.text=b.pre;mR(c.display.lineMeasure,b.pre);return e}function ic(b,d,a,c){return mB(b,gP(b,d),a,c)}function hp(a,b){if(b>=a.display.viewFrom&&b=c.lineN&&bk){f=b-g;j=f-1;if(k>=b){c="right"}}}}if(j!=null){h=a[e+2];if(g==b&&d==(h.insertLeft?"left":"right")){c=d}if(d=="left"&&j==0){while(e&&a[e-2]==a[e-3]&&a[e-1].insertLeft){h=a[(e-=3)+2];c="left"}}if(d=="right"&&j==b-g){while(e=0;a--){if((b=d[a]).left!=b.right){break}}}return b}function kL(g,s,d,k){var f=n3(s.map,d,k);var u=f.node,l=f.start,m=f.end,p=f.collapse;var o;if(u.nodeType==3){for(var j=0;j<4;j++){while(l&&iB(s.line.text.charAt(f.coverStart+l))){--l}while(f.coverStart+m0){p=k="right"}var n;if(g.options.lineWrapping&&(n=u.getClientRects()).length>1){o=n[k=="right"?n.length-1:0]}else{o=u.getBoundingClientRect()}}if(kj&&kH<9&&!l&&(!o||!o.left&&!o.right)){var h=u.parentNode.getClientRects()[0];if(h){o={left:h.left,right:h.left+kJ(g.display),top:h.top,bottom:h.bottom}}else{o=jk}}var a=o.top-s.rect.top,c=o.bottom-s.rect.top;var q=(a+c)/2;var r=s.view.measure.heights;var t=0;for(;tp.from){return j(n-1)}return j(n,o)}var h=ld(g),c=e.ch;if(!h){return j(c)}var m=n6(h,c);var k=b(c,m);if(hz!=null){k.other=b(c,hz)}return k}function kw(c,a){var b=0;a=i3(c.doc,a);if(!c.options.lineWrapping){b=kJ(c.display)*a.ch}var e=hm(c.doc,a.line);var d=m7(e)+hs(c.display);return{left:b,right:b,top:d,bottom:d+e.height}}function g1(c,e,d,a){var b=lA(c,e);b.xRel=a;if(d){b.outside=true}return b}function hg(d,g,h){var e=d.doc;h+=d.display.viewOffset;if(h<0){return g1(e.first,0,true,-1)}var j=nj(e,h),c=e.first+e.size-1;if(j>c){return g1(e.first+e.size-1,hm(e,c).text.length,true,1)}if(g<0){g=0}var k=hm(e,j);for(;;){var b=mX(d,k,j,g,h);var f=jy(k);var a=f&&f.find(0,true);if(f&&(b.ch>a.from.ch||b.ch==a.from.ch&&b.xRel>0)){j=m5(k=a.to.line)}else{return b}}}function mX(e,r,m,c,d){var f=d-m7(r);var k=false,y=2*e.display.wrapper.clientWidth;var B=gP(e,r);function q(C){var D=lL(e,lA(m,C),"line",r,B);k=true;if(f>D.bottom){return D.left-y}else{if(fs){return g1(m,p,l,1)}for(;;){if(a?p==x||p==ka(r,x,1):p-x<=1){var b=c0&&b1){var w=mB(e,B,b,"right");if(f<=w.bottom&&f>=w.top&&Math.abs(c-w.right)1?1:0);return g}var h=Math.ceil(z/2),j=x+h;if(a){j=x;for(var v=0;vc){p=j;s=n;if(l=k){s+=1000}z=h}else{x=j;A=n;t=k;z-=h}}}var n7;function nF(a){if(a.cachedTextHeight!=null){return a.cachedTextHeight}if(n7==null){n7=gZ("pre");for(var b=0;b<49;++b){n7.appendChild(document.createTextNode("x"));n7.appendChild(gZ("br"))}n7.appendChild(document.createTextNode("x"))}mR(a.measure,n7);var c=n7.offsetHeight/50;if(c>3){a.cachedTextHeight=c}iN(a.measure);return c||1}function kJ(a){if(a.cachedCharWidth!=null){return a.cachedCharWidth}var c=gZ("span","xxxxxxxxxx");var b=gZ("pre",[c]);mR(a.measure,b);var d=c.getBoundingClientRect(),e=(d.right-d.left)/10;if(e>2){a.cachedCharWidth=e}return e||10}function ho(g){var b=g.display,d={},e={};var c=b.gutters.clientLeft;for(var a=b.gutters.firstChild,f=0;a;a=a.nextSibling,++f){d[g.options.gutters[f]]=a.offsetLeft+a.clientLeft+c;e[g.options.gutters[f]]=a.clientWidth}return{fixedPos:lw(b),gutterTotalWidth:b.gutters.offsetWidth,gutterLeft:d,gutterWidth:e,wrapperWidth:b.wrapper.clientWidth}}function lw(a){return a.scroller.getBoundingClientRect().left-a.sizer.getBoundingClientRect().left}function mp(b){var c=nF(b.display),d=b.options.lineWrapping;var a=d&&Math.max(5,b.display.scroller.clientWidth/kJ(b.display)-3);return function(f){if(il(b.doc,f)){return 0}var g=0;if(f.widgets){for(var e=0;e=d.display.viewTo){return null}a-=d.display.viewFrom;if(a<0){return null}var c=d.display.view;for(var b=0;b=c.display.viewTo||b.to().line3){e(D,F.top,null,F.bottom);D=n;if(F.bottomz.bottom||E.bottom==z.bottom&&E.right>z.right){z=E}if(D0){b.blinker=setInterval(function(){return b.cursorDiv.style.visibility=(c=!c)?"":"hidden"},a.options.cursorBlinkRate)}else{if(a.options.cursorBlinkRate<0){b.cursorDiv.style.visibility="hidden"}}}function kh(a){if(!a.state.focused){a.display.input.focus();mm(a)}}function nk(a){a.state.delayingBlurEvent=true;setTimeout(function(){if(a.state.delayingBlurEvent){a.state.delayingBlurEvent=false;nL(a)}},100)}function mm(a,b){if(a.state.delayingBlurEvent){a.state.delayingBlurEvent=false}if(a.options.readOnly=="nocursor"){return}if(!a.state.focused){n9(a,"focus",a,b);a.state.focused=true;h6(a.display.wrapper,"CodeMirror-focused");if(!a.curOp&&a.display.selForContextMenu!=a.doc.sel){a.display.input.reset();if(mU){setTimeout(function(){return a.display.input.reset(true)},20)}}a.display.input.receivedFocus()}ku(a)}function nL(a,b){if(a.state.delayingBlurEvent){return}if(a.state.focused){n9(a,"blur",a,b);a.state.focused=false;kS(a.display.wrapper,"CodeMirror-focused")}clearInterval(a.display.blinker);setTimeout(function(){if(!a.state.focused){a.display.shift=false}},150)}function ja(a){var c=a.display,b=c.view;if(!c.alignWidgets&&(!c.gutters.firstChild||!a.options.fixedGutter)){return}var e=lw(c)-c.scroller.scrollLeft+a.doc.scrollLeft;var j=c.gutters.offsetWidth,h=e+"px";for(var f=0;f0.001||d<-0.001){gV(b.line,a);kX(b.line);if(b.rest){for(var k=0;k=d){e=nj(c,m7(hm(c,b))-g.wrapper.clientHeight);d=b}}}return{from:e,to:Math.max(d,e+1)}}function l4(a,b){if(Math.abs(a.doc.scrollTop-b)<2){return}a.doc.scrollTop=b;if(!kc){lN(a,{top:b})}if(a.display.scroller.scrollTop!=b){a.display.scroller.scrollTop=b}a.display.scrollbars.setScrollTop(b);if(kc){lN(a)}ij(a,100)}function nq(a,b,c){if(c?b==a.doc.scrollLeft:Math.abs(a.doc.scrollLeft-b)<2){return}b=Math.min(b,a.display.scroller.scrollWidth-a.display.scroller.clientWidth);a.doc.scrollLeft=b;ja(a);if(a.display.scroller.scrollLeft!=b){a.display.scroller.scrollLeft=b}a.display.scrollbars.setScrollLeft(b)}var iF=0;var kG=null;if(kj){kG=-0.53}else{if(kc){kG=15}else{if(ju){kG=-0.7}else{if(ob){kG=-1/3}}}}function ls(b){var c=b.wheelDeltaX,a=b.wheelDeltaY;if(c==null&&b.detail&&b.axis==b.HORIZONTAL_AXIS){c=b.detail}if(a==null&&b.detail&&b.axis==b.VERTICAL_AXIS){a=b.detail}else{if(a==null){a=b.wheelDelta}}return{x:c,y:a}}function mF(a){var b=ls(a);b.x*=kG;b.y*=kG;return b}function k8(e,l){var c=ls(l),a=c.x,b=c.y;var j=e.display,f=j.scroller;var m=f.scrollWidth>f.clientWidth;var n=f.scrollHeight>f.clientHeight;if(!(a&&m||b&&n)){return}if(b&&k9&&mU){outer:for(var d=l.target,g=j.view;d!=f;d=d.parentNode){for(var o=0;od.clientWidth+1;var e=d.scrollHeight>d.clientHeight+1;var a=d.nativeBarWidth;if(e){this.vert.style.display="block";this.vert.style.bottom=b?a+"px":"0";var f=d.viewHeight-(b?a:0);this.vert.firstChild.style.height=Math.max(0,d.scrollHeight-d.clientHeight+f)+"px"}else{this.vert.style.display="";this.vert.firstChild.style.height="0"}if(b){this.horiz.style.display="block";this.horiz.style.right=e?a+"px":"0";this.horiz.style.left=d.barLeft+"px";var c=d.viewWidth-d.barLeft-(e?a:0);this.horiz.firstChild.style.width=(d.scrollWidth-d.clientWidth+c)+"px"}else{this.horiz.style.display="";this.horiz.firstChild.style.width="0"}if(!this.checkedZeroWidth&&d.clientHeight>0){if(a==0){this.zeroWidthHack()}this.checkedZeroWidth=true}return{right:e?a:0,bottom:b?a:0}},setScrollLeft:function(a){if(this.horiz.scrollLeft!=a){this.horiz.scrollLeft=a}if(this.disableHoriz){this.enableZeroWidthBar(this.horiz,this.disableHoriz)}},setScrollTop:function(a){if(this.vert.scrollTop!=a){this.vert.scrollTop=a}if(this.disableVert){this.enableZeroWidthBar(this.vert,this.disableVert)}},zeroWidthHack:function(){var a=k9&&!jI?"12px":"18px";this.horiz.style.height=this.vert.style.width=a;this.horiz.style.pointerEvents=this.vert.style.pointerEvents="none";this.disableHoriz=new hA;this.disableVert=new hA},enableZeroWidthBar:function(c,a){c.style.pointerEvents="auto";function b(){var d=c.getBoundingClientRect();var e=document.elementFromPoint(d.left+1,d.bottom-1);if(e!=c){c.style.pointerEvents="none"}else{a.set(1000,b)}}a.set(1000,b)},clear:function(){var a=this.horiz.parentNode;a.removeChild(this.horiz);a.removeChild(this.vert)}},i7.prototype);function hy(){}hy.prototype=n0({update:function(){return{bottom:0,right:0}},setScrollLeft:function(){},setScrollTop:function(){},clear:function(){}},hy.prototype);function hK(c,a){if(!a){a=kR(c)}var d=c.display.barWidth,e=c.display.barHeight;nN(c,a);for(var b=0;b<4&&d!=c.display.barWidth||e!=c.display.barHeight;b++){if(d!=c.display.barWidth&&c.options.lineWrapping){i(c)}nN(c,kR(c));d=c.display.barWidth;e=c.display.barHeight}}function nN(b,d){var a=b.display;var c=a.scrollbars.update(d);a.sizer.style.paddingRight=(a.barWidth=c.right)+"px";a.sizer.style.paddingBottom=(a.barHeight=c.bottom)+"px";a.heightForcer.style.borderBottom=c.bottom+"px solid transparent";if(c.right&&c.bottom){a.scrollbarFiller.style.display="block";a.scrollbarFiller.style.height=c.bottom+"px";a.scrollbarFiller.style.width=c.right+"px"}else{a.scrollbarFiller.style.display=""}if(c.bottom&&b.options.coverGutterNextToScrollbar&&b.options.fixedGutter){a.gutterFiller.style.display="block";a.gutterFiller.style.height=c.bottom+"px";a.gutterFiller.style.width=d.gutterWidth+"px"}else{a.gutterFiller.style.display=""}}var lX={"native":i7,"null":hy};function oa(a){if(a.display.scrollbars){a.display.scrollbars.clear();if(a.display.scrollbars.addClass){kS(a.display.wrapper,a.display.scrollbars.addClass)}}a.display.scrollbars=new lX[a.options.scrollbarStyle](function(b){a.display.wrapper.insertBefore(b,a.display.scrollbarFiller);nY(b,"mousedown",function(){if(a.state.focused){setTimeout(function(){return a.display.input.focus()},0)}});b.setAttribute("cm-not-content","true")},function(b,c){if(c=="horizontal"){nq(a,b)}else{l4(a,b)}},a);if(a.display.scrollbars.addClass){h6(a.display.wrapper,a.display.scrollbars.addClass)}}function iD(f,b){if(nT(f,"scrollCursorIntoView")){return}var a=f.display,e=a.sizer.getBoundingClientRect(),d=null;if(b.top+e.top<0){d=true}else{if(b.bottom+e.top>(window.innerHeight||document.documentElement.clientHeight)){d=false}}if(d!=null&&!iq){var c=gZ("div","\u200b",null,("position: absolute;\n top: "+(b.top-a.viewOffset-hs(f.display))+"px;\n height: "+(b.bottom-b.top+lh(f)+a.barHeight)+"px;\n left: "+(b.left)+"px; width: 2px;"));f.display.lineSpace.appendChild(c);c.scrollIntoView(d);f.display.lineSpace.removeChild(c)}}function mx(a,d,h,j){if(j==null){j=0}var b;for(var g=0;g<5;g++){var f=false;b=lL(a,d);var c=!h||h==d?b:lL(a,h);var k=mn(a,Math.min(b.left,c.left),Math.min(b.top,c.top)-j,Math.max(b.left,c.left),Math.max(b.bottom,c.bottom)+j);var e=a.doc.scrollTop,l=a.doc.scrollLeft;if(k.scrollTop!=null){l4(a,k.scrollTop);if(Math.abs(a.doc.scrollTop-e)>1){f=true}}if(k.scrollLeft!=null){nq(a,k.scrollLeft);if(Math.abs(a.doc.scrollLeft-l)>1){f=true}}if(!f){break}}return b}function mt(d,e,b,f,c){var a=mn(d,e,b,f,c);if(a.scrollTop!=null){l4(d,a.scrollTop)}if(a.scrollLeft!=null){nq(d,a.scrollLeft)}}function mn(d,o,e,q,f){var j=d.display,l=nF(d.display);if(e<0){e=0}var n=d.curOp&&d.curOp.scrollTop!=null?d.curOp.scrollTop:j.scroller.scrollTop;var b=m8(d),r={};if(f-e>b){f=e+b}var p=d.doc.height+ni(j);var h=ep-l;if(en+b){var g=Math.min(e,(k?p:f)-b);if(g!=n){r.scrollTop=g}}}var a=d.curOp&&d.curOp.scrollLeft!=null?d.curOp.scrollLeft:j.scroller.scrollLeft;var c=i4(d)-(d.options.fixedGutter?j.gutters.offsetWidth:0);var m=q-o>c;if(m){q=o+c}if(o<10){r.scrollLeft=0}else{if(oc+a-3){r.scrollLeft=q+(m?0:10)-c}}}return r}function lM(a,b,c){if(b!=null||c!=null){h2(a)}if(b!=null){a.curOp.scrollLeft=(a.curOp.scrollLeft==null?a.doc.scrollLeft:a.curOp.scrollLeft)+b}if(c!=null){a.curOp.scrollTop=(a.curOp.scrollTop==null?a.doc.scrollTop:a.curOp.scrollTop)+c}}function jl(b){h2(b);var d=b.getCursor(),a=d,c=d;if(!b.options.lineWrapping){a=d.ch?lA(d.line,d.ch-1):d;c=lA(d.line,d.ch+1)}b.curOp.scrollToPos={from:a,to:c,margin:b.options.cursorScrollMargin,isCursor:true}}function h2(c){var d=c.curOp.scrollToPos;if(d){c.curOp.scrollToPos=null;var a=kw(c,d.from),b=kw(c,d.to);var e=mn(c,Math.min(a.left,b.left),Math.min(a.top,b.top)-d.margin,Math.max(a.right,b.right),Math.max(a.bottom,b.bottom)+d.margin);c.scrollTo(e.scrollLeft,e.scrollTop)}}var iv=0;function lY(a){a.curOp={cm:a,viewChanged:false,startHeight:a.doc.height,forceUpdate:false,updateInput:null,typing:false,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:false,updateMaxLine:false,scrollLeft:null,scrollTop:null,scrollToPos:null,focus:false,id:++iv};js(a.curOp)}function ng(a){var b=a.curOp;nX(b,function(c){for(var d=0;d=c.viewTo)||c.maxLineChanged&&a.options.lineWrapping;b.update=b.mustUpdate&&new n4(a,b.mustUpdate&&{top:b.scrollTop,ensure:b.scrollToPos},b.forceUpdate)}function m6(a){a.updatedDisplay=a.mustUpdate&&mC(a.cm,a.update)}function nP(b){var a=b.cm,c=a.display;if(b.updatedDisplay){i(a)}b.barMeasure=kR(a);if(c.maxLineChanged&&!a.options.lineWrapping){b.adjustWidthTo=ic(a,c.maxLine,c.maxLine.text.length).left+3;a.display.sizerWidth=b.adjustWidthTo;b.barMeasure.scrollWidth=Math.max(c.scroller.clientWidth,c.sizer.offsetLeft+b.adjustWidthTo+lh(a)+a.display.barWidth);b.maxScrollLeft=Math.max(0,c.sizer.offsetLeft+b.adjustWidthTo-i4(a))}if(b.updatedDisplay||b.selectionChanged){b.preparedSelection=c.input.prepareSelection(b.focus)}}function m9(b){var a=b.cm;if(b.adjustWidthTo!=null){a.display.sizer.style.minWidth=b.adjustWidthTo+"px";if(b.maxScrollLefte)){h.updateLineNumbers=e}c.curOp.viewChanged=true;if(e>=h.viewTo){if(gN&&nJ(c.doc,e)h.viewFrom){jw(c)}else{h.viewFrom+=b;h.viewTo+=b}}else{if(e<=h.viewFrom&&d>=h.viewTo){jw(c)}else{if(e<=h.viewFrom){var f=jq(c,d,d+b,1);if(f){h.view=h.view.slice(f.index);h.viewFrom=f.lineN;h.viewTo+=b}else{jw(c)}}else{if(d>=h.viewTo){var k=jq(c,e,e,-1);if(k){h.view=h.view.slice(0,k.index);h.viewTo=k.lineN}else{jw(c)}}else{var g=jq(c,e,e,-1);var j=jq(c,d,d+b,1);if(g&&j){h.view=h.view.slice(0,g.index).concat(hQ(c,g.lineN,j.lineN)).concat(h.view.slice(j.index));h.viewTo+=b}else{jw(c)}}}}}}var a=h.externalMeasured;if(a){if(d=c.lineN&&f=a.viewTo){return}var e=a.view[iR(g,f)];if(e.node==null){return}var d=e.changes||(e.changes=[]);if(jb(d,b)==-1){d.push(b)}}function jw(a){a.display.viewFrom=a.display.viewTo=a.doc.first;a.display.view=[];a.display.viewOffset=0}function jq(a,g,e,h){var d=iR(a,g),b,c=a.display.view;if(!gN||e==a.doc.first+a.doc.size){return{index:d,lineN:e}}var j=a.display.viewFrom;for(var f=0;f0){if(d==c.length-1){return null}b=(j+c[d].size)-g;d++}else{b=j-g}g+=b;e+=b}while(nJ(a.doc,e)!=e){if(d==(h<0?0:c.length-1)){return null}e+=h*c[d-(h<0?1:0)].size;d+=h}return{index:d,lineN:e}}function lm(c,a,b){var d=c.display,e=d.view;if(e.length==0||a>=d.viewTo||b<=d.viewFrom){d.view=hQ(c,a,b);d.viewFrom=a}else{if(d.viewFrom>a){d.view=hQ(c,a,d.viewFrom).concat(d.view)}else{if(d.viewFromb){d.view=d.view.slice(0,iR(c,b))}}}d.viewTo=b}function jv(e){var d=e.display.view,a=0;for(var b=0;b=c.display.viewTo){return}var d=+new Date+c.options.workTime;var b=nK(a.mode,kM(c,a.frontier));var e=[];a.iter(a.frontier,Math.min(a.first+a.size,c.display.viewTo+500),function(h){if(a.frontier>=c.display.viewFrom){var n=h.styles,j=h.text.length>c.options.maxHighlightLength;var l=ib(c,h,j?nK(a.mode,b):b,true);h.styles=l.styles;var f=h.styleClasses,m=l.classes;if(m){h.styleClasses=m}else{if(f){h.styleClasses=null}}var k=!n||n.length!=h.styles.length||f!=m&&(!f||!m||f.bgClass!=m.bgClass||f.textClass!=m.textClass);for(var g=0;!k&&gd){ij(c,c.options.workDelay);return true}});if(e.length){lI(c,function(){for(var f=0;f=g.viewFrom&&h.visible.to<=g.viewTo&&(g.updateLineNumbers==null||g.updateLineNumbers>=g.viewTo)&&g.renderedView==g.view&&jv(b)==0){return false}if(iE(b)){jw(b);h.dims=ho(b)}var j=c.first+c.size;var e=Math.max(h.visible.from-b.options.viewportMargin,c.first);var d=Math.min(j,h.visible.to+b.options.viewportMargin);if(g.viewFromd&&g.viewTo-d<20){d=Math.min(j,g.viewTo)}if(gN){e=nJ(b.doc,e);d=iJ(b.doc,d)}var k=e!=g.viewFrom||d!=g.viewTo||g.lastWrapHeight!=h.wrapperHeight||g.lastWrapWidth!=h.wrapperWidth;lm(b,e,d);g.viewOffset=m7(hm(b.doc,g.viewFrom));b.display.mover.style.top=g.viewOffset+"px";var a=jv(b);if(!k&&a==0&&!h.force&&g.renderedView==g.view&&(g.updateLineNumbers==null||g.updateLineNumbers>=g.viewTo)){return false}var f=j3();if(a>4){g.lineDiv.style.display="none"}km(b,g.updateLineNumbers,h.dims);if(a>4){g.lineDiv.style.display=""}g.renderedView=g.view;if(f&&j3()!=f&&f.offsetHeight){f.focus()}iN(g.cursorDiv);iN(g.selectionDiv);g.gutters.style.height=g.sizer.style.minHeight=0;if(k){g.lastWrapHeight=h.wrapperHeight;g.lastWrapWidth=h.wrapperWidth;ij(b,400)}g.updateLineNumbers=null;return true}function kt(e,a){var c=a.viewport;for(var b=true;;b=false){if(!b||!e.options.lineWrapping||a.oldDisplayWidth==i4(e)){if(c&&c.top!=null){c={top:Math.min(e.doc.height+ni(e.display)-m8(e),c.top)}}a.visible=lc(e.display,e.doc,c);if(a.visible.from>=e.display.viewFrom&&a.visible.to<=e.display.viewTo){break}}if(!mC(e,a)){break}i(e);var d=kR(e);nu(e);hK(e,d);kV(e,d)}a.signal(e,"update",e);if(e.display.viewFrom!=e.display.reportedViewFrom||e.display.viewTo!=e.display.reportedViewTo){a.signal(e,"viewportChange",e,e.display.viewFrom,e.display.viewTo);e.display.reportedViewFrom=e.display.viewFrom;e.display.reportedViewTo=e.display.viewTo}}function lN(d,b){var a=new n4(d,b);if(mC(d,a)){i(d);kt(d,a);var c=kR(d);nu(d);hK(d,c);kV(d,c);a.finish()}}function km(c,m,d){var g=c.display,a=c.options.lineNumbers;var o=g.lineDiv,b=o.firstChild;function h(p){var q=p.nextSibling;if(mU&&k9&&c.display.currentWheelTarget==p){p.style.display="none"}else{p.parentNode.removeChild(p)}return q}var f=g.view,j=g.viewFrom;for(var l=0;l-1){e=false}nC(c,k,j,d)}if(e){iN(k.lineNumber);k.lineNumber.appendChild(document.createTextNode(jF(c.options,j)))}b=k.node.nextSibling}}j+=k.size}while(b){b=h(b)}}function jO(a){var b=a.display.gutters.offsetWidth;a.display.sizer.style.marginLeft=b+"px"}function kV(a,b){a.display.sizer.style.minHeight=b.docHeight+"px";a.display.heightForcer.style.top=b.docHeight+"px";a.display.gutters.style.height=(b.docHeight+a.display.barHeight+lh(a))+"px"}function io(f){var e=f.display.gutters,a=f.options.gutters;iN(e);var d=0;for(;d-1&&!a.lineNumbers){a.gutters=a.gutters.slice(0);a.gutters.splice(b,1)}}}function g0(a,b){this.ranges=a;this.primIndex=b}g0.prototype={primary:function(){return this.ranges[this.primIndex]},equals:function(e){var a=this;if(e==this){return true}if(e.primIndex!=this.primIndex||e.ranges.length!=this.ranges.length){return false}for(var c=0;c=0&&kI(a,d.to())<=0){return c}}return -1}};function lz(a,b){this.anchor=a;this.head=b}lz.prototype={from:function(){return m3(this.anchor,this.head)},to:function(){return nD(this.anchor,this.head)},empty:function(){return this.head.line==this.anchor.line&&this.head.ch==this.anchor.ch}};function mA(j,b){var g=j[b];j.sort(function(l,k){return kI(l.from(),k.from())});b=jb(j,g);for(var e=1;e=0){var d=m3(h.from(),a.from()),c=nD(h.to(),a.to());var f=h.empty()?a.from()==a.head:h.from()==h.head;if(e<=b){--b}j.splice(--e,2,new lz(f?c:d,f?d:c))}}return new g0(j,b)}function j1(a,b){return new g0([new lz(a,b||a)],0)}function m1(a){if(!a.text){return a.to}return lA(a.from.line+a.text.length-1,ji(a.text).length+(a.text.length==1?a.from.ch:0))}function nS(b,c){if(kI(b,c.from)<0){return b}if(kI(b,c.to)<=0){return m1(c)}var a=b.line+c.text.length-(c.to.line-c.from.line)-1,d=b.ch;if(b.line==c.to.line){d+=m1(c).ch-c.to.ch}return lA(a,d)}function iK(b,a){var d=[];for(var c=0;c1){r.remove(a.line+1,c-1)}r.insert(a.line+1,l)}}}}nw(r,"change",r,f)}function iA(b,c,d){function a(j,e,g){if(j.linked){for(var f=0;f1&&!b.done[b.done.length-2].ranges){b.done.pop();return ji(b.done)}}}}function iX(d,f,a,g){var h=d.history;h.undone.length=0;var j=+new Date,c;var b;if((h.lastOp==g||h.lastOrigin==f.origin&&f.origin&&((f.origin.charAt(0)=="+"&&d.cm&&h.lastModTime>j-d.cm.options.historyEventDelay)||f.origin.charAt(0)=="*"))&&(c=kl(h,h.lastOp==g))){b=ji(c.changes);if(kI(f.from,f.to)==0&&kI(f.from,b.to)==0){b.to=m1(f)}else{c.changes.push(k6(d,f))}}else{var e=ji(h.done);if(!e||!e.ranges){lx(d.sel,h.done)}c={changes:[k6(d,f)],generation:h.generation};h.done.push(c);while(h.done.length>h.undoDepth){h.done.shift();if(!h.done[0].ranges){h.done.shift()}}}h.done.push(a);h.generation=++h.maxGeneration;h.lastModTime=h.lastSelTime=j;h.lastOp=h.lastSelOp=g;h.lastOrigin=h.lastSelOrigin=f.origin;if(!b){n9(d,"historyAdded")}}function nx(a,c,d,b){var e=c.charAt(0);return e=="*"||e=="+"&&d.ranges.length==b.ranges.length&&d.somethingSelected()==b.somethingSelected()&&new Date-a.history.lastSelTime<=(a.cm?a.cm.options.historyEventDelay:500)}function hN(a,d,c,e){var b=a.history,f=e&&e.origin;if(c==b.lastSelOp||(f&&b.lastSelOrigin==f&&(b.lastModTime==b.lastSelTime&&b.lastOrigin==f||nx(a,f,ji(b.done),d)))){b.done[b.done.length-1]=d}else{lx(d,b.done)}b.lastSelTime=+new Date;b.lastSelOrigin=f;b.lastSelOp=c;if(e&&e.clearRedo!==false){h3(b.undone)}}function lx(c,a){var b=ji(a);if(!(b&&b.ranges&&b.equals(c))){a.push(c)}}function nU(f,a,b,d){var c=a["spans_"+f.id],e=0;f.iter(Math.max(f.first,b),Math.min(f.first+f.size,d),function(g){if(g.markedSpans){(c||(c=a["spans_"+f.id]={}))[e]=g.markedSpans}++e})}function l3(a){if(!a){return null}var c;for(var b=0;b-1){ji(c)[m]=e[m];delete e[m]}}}}}}return l}function im(a,f,b,d){if(a.cm&&a.cm.display.shift||a.extend){var c=f.anchor;if(d){var e=kI(b,c)<0;if(e!=(kI(d,c)<0)){c=b;b=d}else{if(e!=(kI(b,d)<0)){b=d}}}return new lz(c,b)}else{return new lz(d||b,b)}}function g5(b,c,a,d){mL(b,new g0([im(b,b.sel.primary(),c,a)],0),d)}function mN(a,b,d){var e=[];for(var c=0;c=d.ch:m.to>d.ch))){if(f){n9(l,"beforeCursorEnter");if(l.explicitlyCleared){if(!a.markedSpans){break}else{--h;continue}}}if(!l.atomic){continue}if(j){var e=l.find(k<0?1:-1),c=void 0;if(k<0?l.inclusiveRight:l.inclusiveLeft){e=hF(b,e,-k,e&&e.line==d.line?a:null)}if(e&&e.line==d.line&&(c=kI(e,j))&&(k<0?c<0:c>0)){return iS(b,e,d,k,f)}}var g=l.find(k<0?-1:1);if(k<0?l.inclusiveLeft:l.inclusiveRight){g=hF(b,g,k,g.line==d.line?a:null)}return g?iS(b,g,d,k,f):null}}}return d}function mJ(b,a,e,g,d){var f=g||1;var c=iS(b,a,e,f,d)||(!d&&iS(b,a,e,f,true))||iS(b,a,e,-f,d)||(!d&&iS(b,a,e,-f,true));if(!c){b.cantEdit=true;return lA(b.first,0)}return c}function hF(c,b,d,a){if(d<0&&b.ch==0){if(b.line>c.first){return i3(c,lA(b.line-1))}else{return null}}else{if(d>0&&b.ch==(a||hm(c,b.line)).text.length){if(b.line=0;--e){mf(b,{from:d[e].from,to:d[e].to,text:e?[""]:a.text})}}else{mf(b,a)}}function mf(c,b){if(b.text.length==1&&b.text[0]==""&&kI(b.from,b.to)==0){return}var d=iK(c,b);iX(c,b,d,c.cm?c.cm.curOp.id:NaN);ik(c,b,d,h5(c,b));var a=[];iA(c,function(e,f){if(!f&&jb(a,e.history)==-1){kF(e.history,b);a.push(e.history)}ik(e,b,null,h5(e,b))})}function k5(c,d,a){if(c.cm&&c.cm.state.suppressEdits&&!a){return}var e=c.history,n,l=c.sel;var o=d=="undo"?e.done:e.undone,b=d=="undo"?e.undone:e.done;var h=0;for(;h=0;--k){var j=g(k);if(j){return j.v}}}function iC(d,b){if(b==0){return}d.first+=b;d.sel=new g0(mO(d.sel.ranges,function(e){return new lz(lA(e.anchor.line+b,e.anchor.ch),lA(e.head.line+b,e.head.ch))}),d.sel.primIndex);if(d.cm){np(d.cm,d.first,d.first-b,b);for(var c=d.cm.display,a=c.viewFrom;ab.lastLine()){return}if(a.from.linee){a={from:a.from,to:lA(e,hm(b,e).text.length),text:[a.text[0]],origin:a.origin}}a.removed=gY(b,a.from,a.to);if(!d){d=iK(b,a)}if(b.cm){n5(b.cm,a,f)}else{id(b,a,f)}jN(b,d,ln)}function n5(b,g,j){var c=b.doc,h=b.display,f=g.from,d=g.to;var e=false,k=f.line;if(!b.options.lineWrapping){k=m5(jV(hm(c,f.line)));c.iter(k,d.line+1,function(o){if(o==h.maxLine){e=true;return true}})}if(c.sel.contains(g.from,g.to)>-1){lD(b)}id(c,g,j,mp(b));if(!b.options.lineWrapping){c.iter(k,f.line+g.text.length,function(o){var p=hZ(o);if(p>h.maxLineLength){h.maxLine=o;h.maxLineLength=p;h.maxLineChanged=true;e=false}});if(e){b.curOp.updateMaxLine=true}}c.frontier=Math.min(c.frontier,f.line);ij(b,400);var a=g.text.length-(d.line-f.line)-1;if(g.full){np(b)}else{if(f.line==d.line&&g.text.length==1&&!lR(b.doc,g)){lS(b,f.line,"text")}else{np(b,f.line,d.line+1,a)}}var m=iI(b,"changes"),l=iI(b,"change");if(l||m){var n={from:f,to:d,text:g.text,removed:g.removed,origin:g.origin};if(l){nw(b,"change",b,n)}if(m){(b.curOp.changeObjs||(b.curOp.changeObjs=[])).push(n)}}b.display.selForContextMenu=null}function gT(d,e,a,b,c){if(!b){b=a}if(kI(b,a)<0){var f=b;b=a;a=f}if(typeof e=="string"){e=d.splitLines(e)}mi(d,{from:a,to:b,text:e,origin:c})}function ml(b,c,d,a){if(d1||!(this.children[0] instanceof hH))){var a=[];this.collapse(a);this.children=[new hH(a)];this.children[0].parent=this}},collapse:function(c){var a=this;for(var b=0;b50){var h=k.lines.length%25+25;for(var c=h;c10);c.parent.maybeSpill()},iterN:function(g,h,a){var b=this;for(var f=0;fc.display.maxLineLength){c.display.maxLine=m;c.display.maxLineLength=f;c.display.maxLineChanged=true}}}if(j!=null&&c&&this.collapsed){np(c,j,e+1)}this.lines.length=0;this.explicitlyCleared=true;if(this.atomic&&this.doc.cantEdit){this.doc.cantEdit=false;if(c){jt(c.doc)}}if(c){nw(c,"markerCleared",c,this)}if(k){ng(c)}if(this.parent){this.parent.clear()}};lV.prototype.find=function(d,f){var h=this;if(d==null&&this.type=="bookmark"){d=1}var a,b;for(var e=0;e0||b==0&&h.clearWhenEmpty!==false){return h}if(h.replacedWith){h.collapsed=true;h.widgetNode=gZ("span",[h.replacedWith],"CodeMirror-widget");if(!a.handleMouseEvents){h.widgetNode.setAttribute("cm-ignore-events","true")}if(a.insertLeft){h.widgetNode.insertLeft=true}}if(h.collapsed){if(jS(c,e.line,e,d,h)||e.line!=d.line&&jS(c,d.line,e,d,h)){throw new Error("Inserting collapsed marker partially overlapping an existing one")}lf()}if(h.addToHistory){iX(c,{from:e,to:d,origin:"markText"},c.sel,NaN)}var k=e.line,f=c.cm,l;c.iter(k,d.line+1,function(m){if(f&&h.collapsed&&!f.options.lineWrapping&&jV(m)==f.display.maxLine){l=true}if(h.collapsed&&k!=e.line){gV(m,0)}kP(m,new h7(h,k==e.line?e.ch:null,k==d.line?d.ch:null));++k});if(h.collapsed){c.iter(e.line,d.line+1,function(m){if(il(c,m)){gV(m,0)}})}if(h.clearOnEnter){nY(h,"beforeCursorEnter",function(){return h.clear()})}if(h.readOnly){ia();if(c.history.done.length||c.history.undone.length){c.clearHistory()}}if(h.collapsed){h.id=++gQ;h.atomic=true}if(f){if(l){f.curOp.updateMaxLine=true}if(h.collapsed){np(f,e.line,d.line+1)}else{if(h.className||h.title||h.startStyle||h.endStyle||h.css){for(var j=e.line;j<=d.line;j++){lS(f,j,"text")}}}if(h.atomic){jt(f.doc)}nw(f,"markerAdded",f,h)}return h}function jZ(b,c){var a=this;this.markers=b;this.primary=c;for(var d=0;d=0;h--){mi(e,c[h])}if(k){ht(this,k)}else{if(this.cm){jl(this.cm)}}}),undo:mg(function(){k5(this,"undo")}),redo:mg(function(){k5(this,"redo")}),undoSelection:mg(function(){k5(this,"undo",true)}),redoSelection:mg(function(){k5(this,"redo",true)}),setExtending:function(a){this.extend=a},getExtending:function(){return this.extend},historySize:function(){var b=this.history,e=0,c=0;for(var d=0;d=a.ch)){b.push(c.marker.parent||c.marker)}}}return b},findMarks:function(a,c,e){a=i3(this,a);c=i3(this,c);var d=[],b=a.line;this.iter(a.line,c.line+1,function(h){var f=h.markedSpans;if(f){for(var g=0;g=j.to||j.from==null&&b!=a.line||j.from!=null&&b==c.line&&j.from>=c.ch)&&(!e||e(j.marker))){d.push(j.marker.parent||j.marker)}}}++b});return d},getAllMarks:function(){var a=[];this.iter(function(c){var d=c.markedSpans;if(d){for(var b=0;bb){d=b;return true}b-=e;++a});return i3(this,lA(a,d))},indexFromPos:function(b){b=i3(this,b);var a=b.ch;if(b.linea){a=b.from}if(b.to!=null&&b.to-1){b.state.draggingText(d);setTimeout(function(){return b.display.input.focus()},20);return}try{var m=d.dataTransfer.getData("Text");if(m){var g;if(b.state.draggingText&&!b.state.draggingText.copy){g=b.listSelections()}jN(b.doc,j1(c,c));if(g){for(var j=0;j=0;h--){gT(g.doc,"",c[h].from,c[h].to,"+delete")}jl(g)})}var jg={selectAll:nh,singleSelection:function(a){return a.setSelection(a.getCursor("anchor"),a.getCursor("head"),ln)},killLine:function(a){return hL(a,function(b){if(b.empty()){var c=hm(a.doc,b.head.line).text.length;if(b.head.ch==c&&b.head.line0){b=new lA(b.line,b.ch+1);a.replaceRange(g.charAt(b.ch-1)+g.charAt(b.ch-2),lA(b.line,b.ch-2),b,"+transpose")}else{if(b.line>a.doc.first){var c=hm(a.doc,b.line-1).text;if(c){b=new lA(b.line,1);a.replaceRange(g.charAt(0)+a.doc.lineSeparator()+c.charAt(c.length-1),lA(b.line-1,c.length-1),b,"+transpose")}}}}f.push(new lz(b,b))}a.setSelections(f)})},newlineAndIndent:function(a){return lI(a,function(){var d=a.listSelections();for(var c=d.length-1;c>=0;c--){a.replaceRange(a.doc.lineSeparator(),d[c].anchor,d[c].head,"+input")}d=a.listSelections();for(var b=0;bg-400&&kI(jr.pos,a)==0){d="triple"}else{if(i1&&i1.time>g-400&&kI(i1.pos,a)==0){d="double";jr={time:g,pos:a}}else{d="single";i1={time:g,pos:a}}}var c=h.doc.sel,f=k9?b.metaKey:b.ctrlKey,e;if(h.options.dragDrop&&kr&&!h.isReadOnly()&&d=="single"&&(e=c.contains(a))>-1&&(kI((e=c.ranges[e]).from(),a)<0||a.xRel>0)&&(kI(e.to(),a)>0||a.xRel<0)){gR(h,b,a,f)}else{kB(h,b,a,d,f)}}function gR(f,b,a,g){var c=f.display,e=+new Date;var d=mQ(f,function(h){if(mU){c.scroller.draggable=false}f.state.draggingText=false;ih(document,"mouseup",d);ih(c.scroller,"drop",d);if(Math.abs(b.clientX-h.clientX)+Math.abs(b.clientY-h.clientY)<10){l7(h);if(!g&&+new Date-200-1){o=q[s]}else{o=new lz(n,n)}}else{o=r.sel.primary();s=r.sel.primIndex}if(i6?t.shiftKey&&t.metaKey:t.altKey){p="rect";if(!k){o=new lz(n,n)}n=kg(m,t,true,true);s=-1}else{if(p=="double"){var v=m.findWordAt(n);if(m.display.shift||r.extend){o=im(r,o,v.anchor,v.head)}else{o=v}}else{if(p=="triple"){var g=new lz(lA(n.line,0),i3(r,lA(n.line+1,0)));if(m.display.shift||r.extend){o=im(r,o,g.anchor,g.head)}else{o=g}}else{o=im(r,o,n)}}}if(!k){s=0;mL(r,new g0([o],0),l9);l=r.sel}else{if(s==-1){s=q.length;mL(r,mA(q.concat([o]),s),{scroll:false,origin:"*mouse"})}else{if(q.length>1&&q[s].empty()&&p=="single"&&!t.shiftKey){mL(r,mA(q.slice(0,s).concat(q.slice(s+1)),0),{scroll:false,origin:"*mouse"});l=r.sel}else{k2(r,s,o,l9)}}}var b=n;function c(B){if(kI(b,B)==0){return}b=B;if(p=="rect"){var J=[],E=m.options.tabSize;var K=mV(hm(r,n.line).text,n.ch,E);var x=mV(hm(r,B.line).text,B.ch,E);var I=Math.min(K,x),z=Math.max(K,x);for(var w=Math.min(n.line,B.line),G=Math.min(m.lastLine(),Math.max(n.line,B.line));w<=G;w++){var y=hm(r,w).text,H=jM(y,I,E);if(I==z){J.push(new lz(lA(w,H),lA(w,H)))}else{if(y.length>H){J.push(new lz(lA(w,H),lA(w,jM(y,z,E))))}}}if(!J.length){J.push(new lz(n,n))}mL(r,mA(l.ranges.slice(0,s).concat(J),s),{origin:"*mouse",scroll:false});m.scrollIntoView(B)}else{var D=o;var F=D.anchor,C=B;if(p!="single"){var A;if(p=="double"){A=m.findWordAt(B)}else{A=new lz(lA(B.line,0),i3(r,lA(B.line+1,0)))}if(kI(A.anchor,F)>0){C=A.head;F=m3(D.from(),A.anchor)}else{C=A.anchor;F=nD(D.to(),A.head)}}var L=l.ranges.slice(0);L[s]=new lz(i3(r,F),C);mL(r,mA(L,s),l9)}}var e=a.wrapper.getBoundingClientRect();var j=0;function u(x){var z=++j;var A=kg(m,x,true,p=="rect");if(!A){return}if(kI(A,b)!=0){m.curOp.focus=j3();c(A);var w=lc(a,r);if(A.line>=w.to||A.linee.bottom?20:0;if(y){setTimeout(mQ(m,function(){if(j!=z){return}a.scroller.scrollTop+=y;u(x)}),50)}}}function f(w){m.state.selectingText=false;j=Infinity;l7(w);a.input.focus();ih(document,"mousemove",d);ih(document,"mouseup",h);r.history.lastSelOrigin=null}var d=mQ(m,function(w){if(!hi(w)){f(w)}else{u(w)}});var h=mQ(m,f);m.state.selectingText=h;nY(document,"mousemove",d);nY(document,"mouseup",h)}function hC(b,f,d,c){var l,m;try{l=f.clientX;m=f.clientY}catch(f){return false}if(l>=Math.floor(b.display.gutters.getBoundingClientRect().right)){return false}if(c){l7(f)}var e=b.display;var g=e.lineDiv.getBoundingClientRect();if(m>g.bottom||!iI(b,d)){return na(f)}m-=g.top-e.viewOffset;for(var j=0;j=l){var a=nj(b.doc,m);var k=b.options.gutters[j];n9(b,d,b,a,k,f);return na(f)}}}function kE(a,b){return hC(a,b,"gutterClick",true)}function mI(a,b){if(my(a.display,b)||jh(a,b)){return}if(nT(a,b,"contextmenu")){return}a.display.input.onContextMenu(b)}function jh(a,b){if(!iI(a,"gutterContextMenu")){return false}return hC(a,b,"gutterContextMenu",false)}function lC(a){a.display.wrapper.className=a.display.wrapper.className.replace(/\s*cm-s-\S+/g,"")+a.options.theme.replace(/(^|\s)\s*/g," cm-s-");nm(a)}var kQ={toString:function(){return"CodeMirror.Init"}};var hB={};var mk={};function i0(a){var b=a.optionHandlers;function c(g,d,e,f){a.defaults[g]=d;if(e){b[g]=f?function(h,j,k){if(k!=kQ){e(h,j,k)}}:e}}a.defineOption=c;a.Init=kQ;c("value","",function(e,d){return e.setValue(d)},true);c("mode",null,function(e,d){e.doc.modeOption=d;ly(e)},true);c("indentUnit",2,ly,true);c("indentWithTabs",false);c("smartIndent",true);c("tabSize",4,function(d){h1(d);nm(d);np(d)},true);c("lineSeparator",null,function(h,e){h.doc.lineSep=e;if(!e){return}var f=[],d=h.doc.first;h.doc.iter(function(l){for(var j=0;;){var k=l.text.indexOf(e,j);if(k==-1){break}j=k+e.length;f.push(lA(d,k))}d++});for(var g=f.length-1;g>=0;g--){gT(h.doc,e,f[g],lA(f[g].line,f[g].ch+e.length))}});c("specialChars",/[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g,function(f,d,e){f.state.specialChars=new RegExp(d.source+(d.test("\t")?"":"|\t"),"g");if(e!=kQ){f.refresh()}});c("specialCharPlaceholder",hn,function(d){return d.refresh()},true);c("electricChars",true);c("inputStyle",ie?"contenteditable":"textarea",function(){throw new Error("inputStyle can not (yet) be changed in a running editor")},true);c("spellcheck",false,function(e,d){return e.getInputField().spellcheck=d},true);c("rtlMoveVisually",!nW);c("wholeLineUpdateBefore",true);c("theme","default",function(d){lC(d);k1(d)},true);c("keyMap","default",function(h,d,g){var f=g6(d);var e=g!=kQ&&g6(g);if(e&&e.detach){e.detach(h,f)}if(f.attach){f.attach(h,e||null)}});c("extraKeys",null);c("lineWrapping",false,i2,true);c("gutters",[],function(d){kK(d.options);k1(d)},true);c("fixedGutter",true,function(e,d){e.display.gutters.style.left=d?lw(e.display)+"px":"0";e.refresh()},true);c("coverGutterNextToScrollbar",false,function(d){return hK(d)},true);c("scrollbarStyle","native",function(d){oa(d);hK(d);d.display.scrollbars.setScrollTop(d.doc.scrollTop);d.display.scrollbars.setScrollLeft(d.doc.scrollLeft)},true);c("lineNumbers",false,function(d){kK(d.options);k1(d)},true);c("firstLineNumber",1,k1,true);c("lineNumberFormatter",function(d){return d},k1,true);c("showCursorWhenSelecting",false,nu,true);c("resetSelectionOnContextMenu",true);c("lineWiseCopyCut",true);c("readOnly",false,function(e,d){if(d=="nocursor"){nL(e);e.display.input.blur();e.display.disabled=true}else{e.display.disabled=false}e.display.input.readOnlyChanged(d)});c("disableInput",false,function(e,d){if(!d){e.display.input.reset()}},true);c("dragDrop",true,g2);c("allowDropFileTypes",null);c("cursorBlinkRate",530);c("cursorScrollMargin",0);c("cursorHeight",1,nu,true);c("singleCursorHeightPerLine",true,nu,true);c("workTime",100);c("workDelay",100);c("flattenSpans",true,h1,true);c("addModeClass",false,h1,true);c("pollInterval",100);c("undoDepth",200,function(e,d){return e.doc.history.undoDepth=d});c("historyEventDelay",1250);c("viewportMargin",10,function(d){return d.refresh()},true);c("maxHighlightLength",10000,h1,true);c("moveInputWithCursor",true,function(e,d){if(!d){e.display.input.resetPosition()}});c("tabindex",null,function(e,d){return e.display.input.getField().tabIndex=d||""});c("autofocus",null)}function k1(a){io(a);np(a);setTimeout(function(){return ja(a)},20)}function g2(f,b,e){var a=e&&e!=kQ;if(!b!=!a){var d=f.display.dragFunctions;var c=b?nY:ih;c(f.display.scroller,"dragstart",d.start);c(f.display.scroller,"dragenter",d.enter);c(f.display.scroller,"dragover",d.over);c(f.display.scroller,"dragleave",d.leave);c(f.display.scroller,"drop",d.drop)}}function i2(a){if(a.options.lineWrapping){h6(a.display.wrapper,"CodeMirror-wrap");a.display.sizer.style.minWidth="";a.display.sizerWidth=null}else{kS(a.display.wrapper,"CodeMirror-wrap");kY(a)}lr(a);np(a);nm(a);setTimeout(function(){return hK(a)},100)}function hx(g,e){var h=this;if(!(this instanceof hx)){return new hx(g,e)}this.options=e=e?n0(e):{};n0(hB,e,false);kK(e);var a=e.value;if(typeof a=="string"){a=new mT(a,e.mode,null,e.lineSeparator)}this.doc=a;var f=new hx.inputStyles[e.inputStyle](this);var b=this.display=new iU(g,a,f);b.wrapper.CodeMirror=this;io(this);lC(this);if(e.lineWrapping){this.display.wrapper.className+=" CodeMirror-wrap"}if(e.autofocus&&!ie){b.input.focus()}oa(this);this.state={keyMaps:[],overlays:[],modeGen:0,overwrite:false,delayingBlurEvent:false,focused:false,suppressEdits:false,pasteIncoming:false,cutIncoming:false,selectingText:false,draggingText:false,highlight:new hA(),keySeq:null,specialChars:null};if(kj&&kH<11){setTimeout(function(){return h.display.input.reset(true)},20)}hf(this);l8();lY(this);this.curOp.forceUpdate=true;ip(this,a);if((e.autofocus&&!ie)||this.hasFocus()){setTimeout(mD(mm,this),20)}else{nL(this)}for(var c in mk){if(mk.hasOwnProperty(c)){mk[c](h,e[c],kQ)}}iE(this);if(e.finishInit){e.finishInit(this)}for(var d=0;d20*20}nY(c.scroller,"touchstart",function(j){if(!nT(h,j)&&!e(j)){c.input.ensurePolled();clearTimeout(a);var k=+new Date;c.activeTouch={start:k,moved:false,prev:k-f.end<=300?f:null};if(j.touches.length==1){c.activeTouch.left=j.touches[0].pageX;c.activeTouch.top=j.touches[0].pageY}}});nY(c.scroller,"touchmove",function(){if(c.activeTouch){c.activeTouch.moved=true}});nY(c.scroller,"touchend",function(l){var j=c.activeTouch;if(j&&!my(c,l)&&j.left!=null&&!j.moved&&new Date-j.start<300){var k=h.coordsChar(c.activeTouch,"page"),m;if(!j.prev||g(j,j.prev)){m=new lz(k,k)}else{if(!j.prev.prev||g(j,j.prev.prev)){m=h.findWordAt(k)}else{m=new lz(lA(k.line,0),i3(h.doc,lA(k.line+1,0)))}}h.setSelection(m.anchor,m.head);h.focus();l7(l)}b()});nY(c.scroller,"touchcancel",b);nY(c.scroller,"scroll",function(){if(c.scroller.clientHeight){l4(h,c.scroller.scrollTop);nq(h,c.scroller.scrollLeft,true);n9(h,"scroll",h)}});nY(c.scroller,"mousewheel",function(j){return k8(h,j)});nY(c.scroller,"DOMMouseScroll",function(j){return k8(h,j)});nY(c.wrapper,"scroll",function(){return c.wrapper.scrollTop=c.wrapper.scrollLeft=0});c.dragFunctions={enter:function(j){if(!nT(h,j)){jK(j)}},over:function(j){if(!nT(h,j)){hD(h,j);jK(j)}},start:function(j){return l0(h,j)},drop:mQ(h,l5),leave:function(j){if(!nT(h,j)){jx(h)}}};var d=c.input.getField();nY(d,"keyup",function(j){return me.call(h,j)});nY(d,"keydown",mQ(h,kp));nY(d,"keypress",mQ(h,mw));nY(d,"focus",function(j){return mm(h,j)});nY(d,"blur",function(j){return nL(h,j)})}var a9=[];hx.defineInitHook=function(a){return a9.push(a)};function nz(r,l,a,n){var b=r.doc,o;if(a==null){a="add"}if(a=="smart"){if(!b.mode.indent){a="prev"}else{o=kM(r,l)}}var g=r.options.tabSize;var q=hm(b,l),h=mV(q.text,null,g);if(q.stateAfter){q.stateAfter=null}var p=q.text.match(/^\s*/)[0],d;if(!n&&!/\S/.test(q.text)){d=0;a="not"}else{if(a=="smart"){d=b.mode.indent(o,q.text.slice(p.length),q.text);if(d==kU||d>150){if(!n){return}a="prev"}}}if(a=="prev"){if(l>b.first){d=mV(hm(b,l-1).text,null,g)}else{d=0}}else{if(a=="add"){d=h+r.options.indentUnit}else{if(a=="subtract"){d=h-r.options.indentUnit}else{if(typeof a=="number"){d=h+a}}}}d=Math.max(0,d);var c="",e=0;if(r.options.indentWithTabs){for(var k=Math.floor(d/g);k;--k){e+=g;c+="\t"}}if(e1){if(lZ&&lZ.text.join("\n")==k){if(n.ranges.length%lZ.text.length==0){p=[];for(var j=0;j=0;o--){var q=n.ranges[o];var d=q.from(),e=q.to();if(q.empty()){if(m&&m>0){d=lA(d.line,d.ch-m)}else{if(a.state.overwrite&&!l){e=lA(e.line,Math.min(hm(c,e.line).text.length,e.ch+ji(h).length))}else{if(lZ&&lZ.lineWise&&lZ.text.join("\n")==k){d=e=lA(d.line,0)}}}}g=a.curOp.updateInput;var f={from:d,to:e,text:p?p[o%p.length]:h,origin:b||(l?"paste":a.state.cutIncoming?"cut":"+input")};mi(a.doc,f);nw(a,"inputRead",a,f)}if(k&&!l){g7(a,k)}jl(a);a.curOp.updateInput=g;a.curOp.typing=true;a.state.pasteIncoming=a.state.cutIncoming=false}function mz(b,a){var c=b.clipboardData&&b.clipboardData.getData("Text");if(c){b.preventDefault();if(!a.isReadOnly()&&!a.options.disableInput){lI(a,function(){return g4(a,c,0,null,"paste")})}return true}}function g7(g,d){if(!g.options.electricChars||!g.options.smartIndent){return}var c=g.doc.sel;for(var e=c.ranges.length-1;e>=0;e--){var a=c.ranges[e];if(a.head.ch>100||(e&&c.ranges[e-1].head.line==a.head.line)){continue}var b=g.getModeAt(a.head);var h=false;if(b.electricChars){for(var f=0;f-1){h=nz(g,a.head.line,"smart");break}}}else{if(b.electricInput){if(b.electricInput.test(hm(g.doc,a.head.line).text.slice(0,a.head.ch))){h=nz(g,a.head.line,"smart")}}}if(h){nw(g,"electricInput",g,a.head.line)}}}function jf(f){var a=[],d=[];for(var c=0;c0){k2(h.doc,j,new lz(g,m[j].to()),ln)}}else{if(d.head.line>l){nz(h,d.head.line,e,true);l=d.head.line;if(j==h.doc.sel.primIndex){jl(h)}}}}}),getTokenAt:function(d,e){return j4(this,d,e)},getLineTokens:function(d,e){return j4(this,lA(d),e,true)},getTokenTypeAt:function(j){j=i3(this.doc,j);var e=jG(this,hm(this.doc,j.line));var l=0,k=(e.length-1)/2,f=j.ch;var g;if(f==0){g=e[2]}else{for(;;){var h=(l+k)>>1;if((h?e[h*2-1]:0)>=f){k=h}else{if(e[h*2+1]e){g=e;h=true}}f=hm(this.doc,g)}else{f=g}return j8(this,f,{top:0,left:0},j||"page",d).top+(h?this.doc.height-m7(f):0)},defaultTextHeight:function(){return nF(this.display)},defaultCharWidth:function(){return kJ(this.display)},getViewport:function(){return{from:this.display.viewFrom,to:this.display.viewTo}},addWidget:function(h,l,f,k,d){var j=this.display;h=lL(this,i3(this.doc,h));var g=h.bottom,m=h.left;l.style.position="absolute";l.setAttribute("cm-ignore-events","true");this.display.input.setUneditable(l);j.sizer.appendChild(l);if(k=="over"){g=h.top}else{if(k=="above"||k=="near"){var n=Math.max(j.wrapper.clientHeight,this.doc.height),e=Math.max(j.sizer.clientWidth,j.lineSpace.clientWidth);if((k=="above"||h.bottom+l.offsetHeight>n)&&h.top>l.offsetHeight){g=h.top-l.offsetHeight}else{if(h.bottom+l.offsetHeight<=n){g=h.bottom}}if(m+l.offsetWidth>e){m=e-l.offsetWidth}}}l.style.top=g+"px";l.style.left=l.style.right="";if(d=="right"){m=j.sizer.clientWidth-l.offsetWidth;l.style.right="0px"}else{if(d=="left"){m=0}else{if(d=="middle"){m=(j.sizer.clientWidth-l.offsetWidth)/2}}l.style.left=m+"px"}if(f){mt(this,m,g,m+l.offsetWidth,g+l.offsetHeight)}},triggerOnKeyDown:jC(kp),triggerOnKeyPress:jC(mw),triggerOnKeyUp:me,execCommand:function(d){if(jg.hasOwnProperty(d)){return jg[d].call(null,this)}},triggerElectric:jC(function(d){g7(this,d)}),findPosH:function(j,e,d,g){var k=this;var h=1;if(e<0){h=-1;e=-e}var l=i3(this.doc,j);for(var f=0;f0&&h(e.charAt(k-1))){--k}while(f0.5){lr(this)}n9(this,"refresh",this)}),swapDoc:jC(function(d){var e=this.doc;e.cm=null;ip(this,d);nm(this);this.display.input.reset();this.scrollTo(d.scrollLeft,d.scrollTop);this.curOp.forceScroll=true;nw(this,"swapDoc",this,e);return e}),getInputField:function(){return this.display.input.getField()},getWrapperElement:function(){return this.display.wrapper},getScrollerElement:function(){return this.display.scroller},getGutterElement:function(){return this.display.gutters}};nA(a);a.registerHelper=function(e,f,d){if(!b.hasOwnProperty(e)){b[e]=a[e]={_global:[]}}b[e][f]=d};a.registerGlobalHelper=function(e,f,g,d){a.registerHelper(e,f,d);b[e]._global.push({pred:g,val:d})}};function lg(r,m,d,e,k){var g=m.line,f=m.ch,s=d;var p=hm(r,g);function a(){var t=g+d;if(t=r.first+r.size){return false}g=t;return p=hm(r,t)}function b(t){var u=(k?ka:nn)(p,f,d,true);if(u==null){if(!t&&a()){if(k){f=(d<0?lj:md)(p)}else{f=d<0?p.text.length:0}}else{return false}}else{f=u}return true}if(e=="char"){b()}else{if(e=="column"){b(true)}else{if(e=="word"||e=="group"){var c=null,j=e=="group";var q=r.cm&&r.cm.getHelper(m,"wordChars");for(var l=true;;l=false){if(d<0&&!b(!l)){break}var o=p.text.charAt(f)||"\n";var n=mo(o,q)?"w":j&&o=="\n"?"n":!j||/\s/.test(o)?null:"p";if(j&&!l&&!n){n="s"}if(c&&c!=n){if(d<0){d=1;b()}break}if(n){c=n}if(d>0&&!b(!l)){break}}}}}var h=mJ(r,lA(g,f),m,s,true);if(!kI(m,h)){h.hitSide=true}return h}function lB(b,g,k,c){var d=b.doc,e=g.left,f;if(c=="page"){var h=Math.min(b.display.wrapper.clientHeight,window.innerHeight||document.documentElement.clientHeight);var a=Math.max(h-0.5*nF(b.display),3);f=(k>0?g.bottom:g.top)+k*a}else{if(c=="line"){f=k>0?g.bottom+3:g.top-3}}var j;for(;;){j=hg(b,e,f);if(!j.outside){break}if(k<0?f<=0:f>=d.height){j.hitSide=true;break}f+=k*5}return j}function k7(a){this.cm=a;this.lastAnchorNode=this.lastAnchorOffset=this.lastFocusNode=this.lastFocusOffset=null;this.polling=new hA();this.composing=null;this.gracePeriod=false;this.readDOMTimeout=null}k7.prototype=n0({init:function(d){var a=this;var e=this,c=e.cm;var b=e.div=d.lineDiv;hh(b,c.options.spellcheck);nY(b,"paste",function(g){if(nT(c,g)||mz(g,c)){return}if(kH<=11){setTimeout(mQ(c,function(){if(!e.pollContent()){np(c)}}),20)}});nY(b,"compositionstart",function(g){a.composing={data:g.data}});nY(b,"compositionupdate",function(g){if(!a.composing){a.composing={data:g.data}}});nY(b,"compositionend",function(g){if(a.composing){if(g.data!=a.composing.data){a.readFromDOMSoon()}a.composing=null}});nY(b,"touchstart",function(){return e.forceCompositionEnd()});nY(b,"input",function(){if(!a.composing){a.readFromDOMSoon()}});function f(k){if(nT(c,k)){return}if(c.somethingSelected()){jJ({lineWise:false,text:c.getSelections()});if(k.type=="cut"){c.replaceSelection("",null,"cut")}}else{if(!c.options.lineWiseCopyCut){return}else{var g=jf(c);jJ({lineWise:true,text:g.text});if(k.type=="cut"){c.operation(function(){c.setSelections(g.ranges,0,ln);c.replaceSelection("",null,"cut")})}}}if(k.clipboardData){k.clipboardData.clearData();var l=lZ.text.join("\n");k.clipboardData.setData("Text",l);if(k.clipboardData.getData("Text")==l){k.preventDefault();return}}var m=nG(),j=m.firstChild;c.display.lineSpace.insertBefore(m,c.display.lineSpace.firstChild);j.value=lZ.text.join("\n");var h=document.activeElement;kf(j);setTimeout(function(){c.display.lineSpace.removeChild(m);h.focus();if(h==b){e.showPrimarySelection()}},50)}nY(b,"copy",f);nY(b,"cut",f)},prepareSelection:function(){var a=i9(this.cm,false);a.focus=this.cm.state.focused;return a},showSelection:function(b,a){if(!b||!this.cm.display.view.length){return}if(b.focus||a){this.showPrimarySelection()}this.showMultipleSelections(b)},showPrimarySelection:function(){var k=window.getSelection(),g=this.cm.doc.sel.primary();var j=mG(this.cm,k.anchorNode,k.anchorOffset);var e=mG(this.cm,k.focusNode,k.focusOffset);if(j&&!j.bad&&e&&!e.bad&&kI(m3(j,e),g.from())==0&&kI(nD(j,e),g.to())==0){return}var l=mq(this.cm,g.from());var f=mq(this.cm,g.to());if(!l&&!f){return}var b=this.cm.display.view;var h=k.rangeCount&&k.getRangeAt(0);if(!l){l={node:b[0].measure.map[2],offset:0}}else{if(!f){var m=b[b.length-1].measure;var a=m.maps?m.maps[m.maps.length-1]:m.map;f={node:a[a.length-1],offset:a[a.length-2]-a[a.length-3]}}}var d;try{d=ki(l.node,l.offset,f.offset,f.node)}catch(c){}if(d){if(!kc&&this.cm.state.focused){k.collapse(l.node,l.offset);if(!d.collapsed){k.removeAllRanges();k.addRange(d)}}else{k.removeAllRanges();k.addRange(d)}if(h&&k.anchorNode==null){k.addRange(h)}else{if(kc){this.startGracePeriod()}}}this.rememberSelection()},startGracePeriod:function(){var a=this;clearTimeout(this.gracePeriod);this.gracePeriod=setTimeout(function(){a.gracePeriod=false;if(a.selectionChanged()){a.cm.operation(function(){return a.cm.curOp.selectionChanged=true})}},20)},showMultipleSelections:function(a){mR(this.cm.display.cursorDiv,a.cursors);mR(this.cm.display.selectionDiv,a.selection)},rememberSelection:function(){var a=window.getSelection();this.lastAnchorNode=a.anchorNode;this.lastAnchorOffset=a.anchorOffset;this.lastFocusNode=a.focusNode;this.lastFocusOffset=a.focusOffset},selectionInEditor:function(){var b=window.getSelection();if(!b.rangeCount){return false}var a=b.getRangeAt(0).commonAncestorContainer;return hR(this.div,a)},focus:function(){if(this.cm.options.readOnly!="nocursor"){if(!this.selectionInEditor()){this.showSelection(this.prepareSelection(),true)}this.div.focus()}},blur:function(){this.div.blur()},getField:function(){return this.div},supportsTouch:function(){return true},receivedFocus:function(){var a=this;if(this.selectionInEditor()){this.pollSelection()}else{lI(this.cm,function(){return a.cm.curOp.selectionChanged=true})}function b(){if(a.cm.state.focused){a.pollSelection();a.polling.set(a.cm.options.pollInterval,b)}}this.polling.set(this.cm.options.pollInterval,b)},selectionChanged:function(){var a=window.getSelection();return a.anchorNode!=this.lastAnchorNode||a.anchorOffset!=this.lastAnchorOffset||a.focusNode!=this.lastFocusNode||a.focusOffset!=this.lastFocusOffset},pollSelection:function(){if(!this.composing&&this.readDOMTimeout==null&&!this.gracePeriod&&this.selectionChanged()){var b=window.getSelection(),a=this.cm;this.rememberSelection();var d=mG(a,b.anchorNode,b.anchorOffset);var c=mG(a,b.focusNode,b.focusOffset);if(d&&c){lI(a,function(){mL(a.doc,j1(d,c),ln);if(d.bad||c.bad){a.curOp.selectionChanged=true}})}}},pollContent:function(){if(this.readDOMTimeout!=null){clearTimeout(this.readDOMTimeout);this.readDOMTimeout=null}var f=this.cm,t=f.display,v=f.doc.sel.primary();var u=v.from(),m=v.to();if(u.ch==0&&u.line>f.firstLine()){u=lA(u.line-1,hm(f.doc,u.line-1).length)}if(m.ch==hm(f.doc,m.line).text.length&&m.linet.viewTo-1){return false}var j,l,g;if(u.line==t.viewFrom||(j=iR(f,u.line))==0){l=m5(t.view[0].line);g=t.view[0].node}else{l=m5(t.view[j].line);g=t.view[j-1].node.nextSibling}var w=iR(f,m.line);var d,a;if(w==t.view.length-1){d=t.viewTo-1;a=t.lineDiv.lastChild}else{d=m5(t.view[w+1].line)-1;a=t.view[w+1].node.previousSibling}if(!g){return false}var s=f.doc.splitLines(g3(f,g,a,l,d));var b=gY(f.doc,lA(l,0),lA(d,hm(f.doc,d).text.length));while(s.length>1&&b.length>1){if(ji(s)==ji(b)){s.pop();b.pop();d--}else{if(s[0]==b[0]){s.shift();b.shift();l++}else{break}}}var x=0,o=0;var e=s[0],p=b[0],q=Math.min(e.length,p.length);while(x1||s[0]||kI(k,c)){gT(f.doc,s,k,c,"+input");return true}},ensurePolled:function(){this.forceCompositionEnd()},reset:function(){this.forceCompositionEnd()},forceCompositionEnd:function(){if(!this.composing){return}this.composing=null;if(!this.pollContent()){np(this.cm)}this.div.blur();this.div.focus()},readFromDOMSoon:function(){var a=this;if(this.readDOMTimeout!=null){return}this.readDOMTimeout=setTimeout(function(){a.readDOMTimeout=null;if(a.composing){return}if(a.cm.isReadOnly()||!a.pollContent()){lI(a.cm,function(){return np(a.cm)})}},80)},setUneditable:function(a){a.contentEditable="false"},onKeyPress:function(a){a.preventDefault();if(!this.cm.isReadOnly()){mQ(this.cm,g4)(this.cm,String.fromCharCode(a.charCode==null?a.keyCode:a.charCode),0)}},readOnlyChanged:function(a){this.div.contentEditable=String(a!="nocursor")},onContextMenu:g8,resetPosition:g8,needsContentAttribute:true},k7.prototype);function mq(d,f){var e=hp(d,f.line);if(!e||e.hidden){return null}var b=hm(d.doc,f.line);var j=jT(e,b,f.line);var h=ld(b),g="left";if(h){var a=n6(h,f.ch);g=a%2?"right":"left"}var c=n3(j.map,f.ch,g);c.offset=c.collapse=="right"?c.end:c.start;return c}function jH(b,a){if(a){b.bad=true}return b}function g3(c,e,d,h,k){var a="",j=false,b=c.doc.lineSeparator();function g(l){return function(m){return m.id==l}}function f(o){if(o.nodeType==1){var r=o.getAttribute("cm-text");if(r!=null){if(r==""){a+=o.textContent.replace(/\u200b/g,"")}else{a+=r}return}var p=o.getAttribute("cm-marker"),l;if(p){var n=c.findMarks(lA(h,0),lA(k+1,0),g(+p));if(n.length&&(l=n[0].find())){a+=gY(c.doc,l.from,l.to).join(b)}return}if(o.getAttribute("contenteditable")=="false"){return}for(var q=0;q=9&&a.hasSelection){a.hasSelection=null}g.poll()});nY(d,"paste",function(h){if(nT(e,h)||mz(h,e)){return}e.state.pasteIncoming=true;g.fastPoll()});function c(j){if(nT(e,j)){return}if(e.somethingSelected()){jJ({lineWise:false,text:e.getSelections()});if(g.inaccurateSelection){g.prevInput="";g.inaccurateSelection=false;d.value=lZ.text.join("\n");kf(d)}}else{if(!e.options.lineWiseCopyCut){return}else{var h=jf(e);jJ({lineWise:true,text:h.text});if(j.type=="cut"){e.setSelections(h.ranges,null,ln)}else{g.prevInput="";d.value=h.text.join("\n");kf(d)}}}if(j.type=="cut"){e.state.cutIncoming=true}}nY(d,"cut",c);nY(d,"copy",c);nY(f.scroller,"paste",function(h){if(my(f,h)||nT(e,h)){return}e.state.pasteIncoming=true;g.focus()});nY(f.lineSpace,"selectstart",function(h){if(!my(f,h)){l7(h)}});nY(d,"compositionstart",function(){var h=e.getCursor("from");if(g.composing){g.composing.range.clear()}g.composing={start:h,range:e.markText(h,e.getCursor("to"),{className:"CodeMirror-composing"})}});nY(d,"compositionend",function(){if(g.composing){g.poll();g.composing.range.clear();g.composing=null}})},prepareSelection:function(){var g=this.cm,b=g.display,c=g.doc;var e=i9(g);if(g.options.moveInputWithCursor){var a=lL(g,c.sel.primary().head,"div");var f=b.wrapper.getBoundingClientRect(),d=b.lineDiv.getBoundingClientRect();e.teTop=Math.max(0,Math.min(b.wrapper.clientHeight-10,a.top+d.top-f.top));e.teLeft=Math.max(0,Math.min(b.wrapper.clientWidth-10,a.left+d.left-f.left))}return e},showSelection:function(b){var a=this.cm,c=a.display;mR(c.cursorDiv,b.cursors);mR(c.selectionDiv,b.selection);if(b.teTop!=null){this.wrapper.style.top=b.teTop+"px";this.wrapper.style.left=b.teLeft+"px"}},reset:function(e){if(this.contextMenuPending){return}var g,f,d=this.cm,b=d.doc;if(d.somethingSelected()){this.prevInput="";var a=b.sel.primary();g=jz&&(a.to().line-a.from().line>100||(f=d.getSelection()).length>1000);var c=g?"-":f||d.getSelection();this.textarea.value=c;if(d.state.focused){kf(this.textarea)}if(kj&&kH>=9){this.hasSelection=c}}else{if(!e){this.prevInput=this.textarea.value="";if(kj&&kH>=9){this.hasSelection=null}}}this.inaccurateSelection=g},getField:function(){return this.textarea},supportsTouch:function(){return false},focus:function(){if(this.cm.options.readOnly!="nocursor"&&(!ie||j3()!=this.textarea)){try{this.textarea.focus()}catch(a){}}},blur:function(){this.textarea.blur()},resetPosition:function(){this.wrapper.style.top=this.wrapper.style.left=0},receivedFocus:function(){this.slowPoll()},slowPoll:function(){var a=this;if(this.pollingFast){return}this.polling.set(this.cm.options.pollInterval,function(){a.poll();if(a.cm.state.focused){a.slowPoll()}})},fastPoll:function(){var c=false,a=this;a.pollingFast=true;function b(){var d=a.poll();if(!d&&!c){c=true;a.polling.set(60,b)}else{a.pollingFast=false;a.slowPoll()}}a.polling.set(20,b)},poll:function(){var a=this;var f=this.cm,g=this.textarea,e=this.prevInput;if(this.contextMenuPending||!f.state.focused||(lv(g)&&!e&&!this.composing)||f.isReadOnly()||f.options.disableInput||f.state.keySeq){return false}var c=g.value;if(c==e&&!f.somethingSelected()){return false}if(kj&&kH>=9&&this.hasSelection===c||k9&&/[\uf700-\uf7ff]/.test(c)){f.display.input.reset();return false}if(f.doc.sel==f.display.selForContextMenu){var d=c.charCodeAt(0);if(d==8203&&!e){e="\u200b"}if(d==8666){this.reset();return this.cm.execCommand("undo")}}var b=0,h=Math.min(e.length,c.length);while(b1000||c.indexOf("\n")>-1){g.value=a.prevInput=""}else{a.prevInput=c}if(a.composing){a.composing.range.clear();a.composing.range=f.markText(a.composing.start,f.getCursor("to"),{className:"CodeMirror-composing"})}});return true},ensurePolled:function(){if(this.pollingFast&&this.poll()){this.pollingFast=false}},onKeyPress:function(){if(kj&&kH>=9){this.hasSelection=null}this.fastPoll()},onContextMenu:function(l){var e=this,d=e.cm,j=d.display,p=e.textarea;var f=kg(d,l),g=j.scroller.scrollTop;if(!f||iL){return}var m=d.options.resetSelectionOnContextMenu;if(m&&d.doc.sel.contains(f)==-1){mQ(d,mL)(d.doc,j1(f),ln)}var k=p.style.cssText,a=e.wrapper.style.cssText;e.wrapper.style.cssText="position: absolute";var b=e.wrapper.getBoundingClientRect();p.style.cssText="position: absolute; width: 30px; height: 30px;\n top: "+(l.clientY-b.top-5)+"px; left: "+(l.clientX-b.left-5)+"px;\n z-index: 1000; background: "+(kj?"rgba(255, 255, 255, .05)":"transparent")+";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";var c;if(mU){c=window.scrollY}j.input.focus();if(mU){window.scrollTo(null,c)}j.input.reset();if(!d.somethingSelected()){p.value=e.prevInput=" "}e.contextMenuPending=true;j.selForContextMenu=d.doc.sel;clearTimeout(j.detectingSelectAll);function n(){if(p.selectionStart!=null){var r=d.somethingSelected();var q="\u200b"+(r?p.value:"");p.value="\u21da";p.value=q;e.prevInput=r?"":"\u200b";p.selectionStart=1;p.selectionEnd=q.length;j.selForContextMenu=d.doc.sel}}function h(){e.contextMenuPending=false;e.wrapper.style.cssText=a;p.style.cssText=k;if(kj&&kH<9){j.scrollbars.setScrollTop(j.scroller.scrollTop=g)}if(p.selectionStart!=null){if(!kj||(kj&&kH<9)){n()}var r=0,q=function(){if(j.selForContextMenu==d.doc.sel&&p.selectionStart==0&&p.selectionEnd>0&&e.prevInput=="\u200b"){mQ(d,nh)(d)}else{if(r++<10){j.detectingSelectAll=setTimeout(q,500)}else{j.input.reset()}}};j.detectingSelectAll=setTimeout(q,200)}}if(kj&&kH>=9){n()}if(hT){jK(l);var o=function(){ih(window,"mouseup",o);setTimeout(h,20)};nY(window,"mouseup",o)}else{setTimeout(h,50)}},readOnlyChanged:function(a){if(!a){this.reset()}},setUneditable:g8,needsContentAttribute:false},lt.prototype);function je(c,b){b=b?n0(b):{};b.value=c.value;if(!b.tabindex&&c.tabIndex){b.tabindex=c.tabIndex}if(!b.placeholder&&c.placeholder){b.placeholder=c.placeholder}if(b.autofocus==null){var a=j3();b.autofocus=a==c||c.getAttribute("autofocus")!=null&&a==document.body}function f(){c.value=d.getValue()}var e;if(c.form){nY(c.form,"submit",f);if(!b.leaveSubmitMethodAlone){var j=c.form;e=j.submit;try{var g=j.submit=function(){f();j.submit=e;j.submit();j.submit=g}}catch(h){}}}b.finishInit=function(k){k.save=f;k.getTextArea=function(){return c};k.toTextArea=function(){k.toTextArea=isNaN;f();c.parentNode.removeChild(k.getWrapperElement());c.style.display="";if(c.form){ih(c.form,"submit",f);if(typeof c.form.submit=="function"){c.form.submit=e}}}};c.style.display="none";var d=hx(function(k){return c.parentNode.insertBefore(k,c.nextSibling)},b);return d}function iZ(a){a.off=ih;a.on=nY;a.wheelEventPixels=mF;a.Doc=mT;a.splitLines=hd;a.countColumn=mV;a.findColumn=jM;a.isWordChar=h0;a.Pass=kU;a.signal=n9;a.Line=gW;a.changeEnd=m1;a.scrollbarModel=lX;a.Pos=lA;a.cmpPos=kI;a.modes=le;a.mimeModes=nQ;a.resolveMode=hP;a.getMode=h8;a.modeExtensions=iV;a.extendMode=jc;a.copyState=nK;a.startState=nR;a.innerMode=hX;a.commands=jg;a.keyMap=hq;a.keyName=iw;a.isModifierKey=jm;a.lookupKey=kN;a.normalizeKeyMap=l2;a.StringStream=hU;a.SharedTextMarker=jZ;a.TextMarker=lV;a.LineWidget=kO;a.e_preventDefault=l7;a.e_stopPropagation=iT;a.e_stop=jK;a.addClass=h6;a.contains=hR;a.rmClass=kS;a.keyNames=iQ}i0(hx);jX(hx);var k4="iter insert remove copy getEditor constructor".split(" ");for(var nd in mT.prototype){if(mT.prototype.hasOwnProperty(nd)&&jb(k4,nd)<0){hx.prototype[nd]=(function(a){return function(){return a.apply(this.doc,arguments)}})(mT.prototype[nd])}}nA(mT);hx.inputStyles={textarea:lt,contenteditable:k7};hx.defineMode=function(a){if(!hx.defaults.mode&&a!="null"){hx.defaults.mode=a}jE.apply(this,arguments)};hx.defineMIME=lT;hx.defineMode("null",function(){return({token:function(a){return a.skipToEnd()}})});hx.defineMIME("text/plain","null");hx.defineExtension=function(a,b){hx.prototype[a]=b};hx.defineDocExtension=function(a,b){mT.prototype[a]=b};hx.fromTextArea=je;iZ(hx);hx.version="5.21.0";return hx}))); \ No newline at end of file diff --git a/route/static/codemirror/lib/index.html b/route/static/codemirror/lib/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/codemirror/lib/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/codemirror/mode/index.html b/route/static/codemirror/mode/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/codemirror/mode/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/codemirror/mode/modeAll.js b/route/static/codemirror/mode/modeAll.js deleted file mode 100755 index 33a90a45b..000000000 --- a/route/static/codemirror/mode/modeAll.js +++ /dev/null @@ -1 +0,0 @@ -(function(b){if(typeof exports=="object"&&typeof module=="object"){b(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],b)}else{b(CodeMirror)}}})(function(b){b.defineMode("sql",function(C,z){var B=z.client||{},w=z.atoms||{"false":true,"true":true,"null":true},s=z.builtin||{},y=z.keywords||{},x=z.operatorChars||/^[*+\-%<>!=&|~^]/,r=z.support||{},a=z.hooks||{},D=z.dateSQL||{date:true,time:true,timestamp:true};function A(c,e){var f=c.next();if(a[f]){var g=a[f](c,e);if(g!==false){return g}}if(r.hexNumber&&((f=="0"&&c.match(/^[xX][0-9a-fA-F]+/))||(f=="x"||f=="X")&&c.match(/^'[0-9a-fA-F]+'/))){return"number"}else{if(r.binaryNumber&&(((f=="b"||f=="B")&&c.match(/^'[01]+'/))||(f=="0"&&c.match(/^b[01]+/)))){return"number"}else{if(f.charCodeAt(0)>47&&f.charCodeAt(0)<58){c.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/);r.decimallessFloat&&c.eat(".");return"number"}else{if(f=="?"&&(c.eatSpace()||c.eol()||c.eat(";"))){return"variable-3"}else{if(f=="'"||(f=='"'&&r.doubleQuote)){e.tokenize=q(f);return e.tokenize(c,e)}else{if((((r.nCharCast&&(f=="n"||f=="N"))||(r.charsetCast&&f=="_"&&c.match(/[a-z][a-z0-9]*/i)))&&(c.peek()=="'"||c.peek()=='"'))){return"keyword"}else{if(/^[\(\),\;\[\]]/.test(f)){return null}else{if(r.commentSlashSlash&&f=="/"&&c.eat("/")){c.skipToEnd();return"comment"}else{if((r.commentHash&&f=="#")||(f=="-"&&c.eat("-")&&(!r.commentSpaceRequired||c.eat(" ")))){c.skipToEnd();return"comment"}else{if(f=="/"&&c.eat("*")){e.tokenize=u;return e.tokenize(c,e)}else{if(f=="."){if(r.zerolessFloat&&c.match(/^(?:\d+(?:e[+-]?\d+)?)/i)){return"number"}if(r.ODBCdotTable&&c.match(/^[a-zA-Z_]+/)){return"variable-2"}}else{if(x.test(f)){c.eatWhile(x);return null}else{if(f=="{"&&(c.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/)||c.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))){return"number"}else{c.eatWhile(/^[_\w\d]/);var d=c.current().toLowerCase();if(D.hasOwnProperty(d)&&(c.match(/^( )+'[^']*'/)||c.match(/^( )+"[^"]*"/))){return"number"}if(w.hasOwnProperty(d)){return"atom"}if(s.hasOwnProperty(d)){return"builtin"}if(y.hasOwnProperty(d)){return"keyword"}if(B.hasOwnProperty(d)){return"string-2"}return null}}}}}}}}}}}}}}function q(c){return function(d,f){var e=false,g;while((g=d.next())!=null){if(g==c&&!e){f.tokenize=A;break}e=!e&&g=="\\"}return"string"}}function u(c,d){while(true){if(c.skipTo("*")){c.next();if(c.eat("/")){d.tokenize=A;break}}else{c.skipToEnd();break}}return"comment"}function v(c,d,e){d.context={prev:d.context,indent:c.indentation(),col:c.column(),type:e}}function t(c){c.indent=c.context.indent;c.context=c.context.prev}return{startState:function(){return{tokenize:A,context:null}},token:function(c,d){if(c.sol()){if(d.context&&d.context.align==null){d.context.align=false}}if(c.eatSpace()){return null}var e=d.tokenize(c,d);if(e=="comment"){return e}if(d.context&&d.context.align==null){d.context.align=true}var f=c.current();if(f=="("){v(c,d,")")}else{if(f=="["){v(c,d,"]")}else{if(d.context&&d.context.type==f){t(d)}}}return e},indent:function(c,e){var f=c.context;if(!f){return b.Pass}var d=e.charAt(0)==f.type;if(f.align){return f.col+(d?0:1)}else{return f.indent+(d?0:C.indentUnit)}},blockCommentStart:"/*",blockCommentEnd:"*/",lineComment:r.commentSlashSlash?"//":r.commentHash?"#":null}});(function(){function j(c){var d;while((d=c.next())!=null){if(d=="`"&&!c.eat("`")){return"variable-2"}}c.backUp(c.current().length-1);return c.eatWhile(/\w/)?"variable-2":null}function a(c){if(c.eat("@")){c.match(/^session\./);c.match(/^local\./);c.match(/^global\./)}if(c.eat("'")){c.match(/^.*'/);return"variable-2"}else{if(c.eat('"')){c.match(/^.*"/);return"variable-2"}else{if(c.eat("`")){c.match(/^.*`/);return"variable-2"}else{if(c.match(/^[0-9a-zA-Z$\.\_]+/)){return"variable-2"}}}}return null}function i(c){if(c.eat("N")){return"atom"}return c.match(/^[a-zA-Z.#!?]/)?"variable-2":null}var h="alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit ";function g(c){var e={},d=c.split(" ");for(var f=0;f!=]/,dateSQL:g("date time timestamp"),support:g("ODBCdotTable doubleQuote binaryNumber hexNumber")});b.defineMIME("text/x-mssql",{name:"sql",client:g("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),keywords:g(h+"begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare exec"),builtin:g("bigint numeric bit smallint decimal smallmoney int tinyint money float real char varchar text nchar nvarchar ntext binary varbinary image cursor timestamp hierarchyid uniqueidentifier sql_variant xml table "),atoms:g("false true null unknown"),operatorChars:/^[*+\-%<>!=]/,dateSQL:g("date datetimeoffset datetime2 smalldatetime datetime time"),hooks:{"@":a}});b.defineMIME("text/x-mysql",{name:"sql",client:g("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),keywords:g(h+"accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general get global grant grants group group_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),builtin:g("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),atoms:g("false true null unknown"),operatorChars:/^[*+\-%<>!=&|^]/,dateSQL:g("date time timestamp"),support:g("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),hooks:{"@":a,"`":j,"\\":i}});b.defineMIME("text/x-mariadb",{name:"sql",client:g("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),keywords:g(h+"accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated get global grant grants group groupby_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show shutdown signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),builtin:g("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),atoms:g("false true null unknown"),operatorChars:/^[*+\-%<>!=&|^]/,dateSQL:g("date time timestamp"),support:g("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),hooks:{"@":a,"`":j,"\\":i}});b.defineMIME("text/x-cassandra",{name:"sql",client:{},keywords:g("add all allow alter and any apply as asc authorize batch begin by clustering columnfamily compact consistency count create custom delete desc distinct drop each_quorum exists filtering from grant if in index insert into key keyspace keyspaces level limit local_one local_quorum modify nan norecursive nosuperuser not of on one order password permission permissions primary quorum rename revoke schema select set storage superuser table three to token truncate ttl two type unlogged update use user users using values where with writetime"),builtin:g("ascii bigint blob boolean counter decimal double float frozen inet int list map static text timestamp timeuuid tuple uuid varchar varint"),atoms:g("false true infinity NaN"),operatorChars:/^[<>=]/,dateSQL:{},support:g("commentSlashSlash decimallessFloat"),hooks:{}});b.defineMIME("text/x-plsql",{name:"sql",client:g("appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap"),keywords:g("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elseif elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning returns reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"),builtin:g("abs acos add_months ascii asin atan atan2 average bfile bfilename bigserial bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least length lengthb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim serial sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid unlogged upper user userenv varchar varchar2 variance varying vsize xml"),operatorChars:/^[*+\-%<>!=~]/,dateSQL:g("date time timestamp"),support:g("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber")});b.defineMIME("text/x-hive",{name:"sql",keywords:g("select alter $elem$ $key$ $value$ add after all analyze and archive as asc before between binary both bucket buckets by cascade case cast change cluster clustered clusterstatus collection column columns comment compute concatenate continue create cross cursor data database databases dbproperties deferred delete delimited desc describe directory disable distinct distribute drop else enable end escaped exclusive exists explain export extended external false fetch fields fileformat first format formatted from full function functions grant group having hold_ddltime idxproperties if import in index indexes inpath inputdriver inputformat insert intersect into is items join keys lateral left like limit lines load local location lock locks mapjoin materialized minus msck no_drop nocompress not of offline on option or order out outer outputdriver outputformat overwrite partition partitioned partitions percent plus preserve procedure purge range rcfile read readonly reads rebuild recordreader recordwriter recover reduce regexp rename repair replace restrict revoke right rlike row schema schemas semi sequencefile serde serdeproperties set shared show show_database sort sorted ssl statistics stored streamtable table tables tablesample tblproperties temporary terminated textfile then tmp to touch transform trigger true unarchive undo union uniquejoin unlock update use using utc utc_tmestamp view when where while with"),builtin:g("bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype"),atoms:g("false true null unknown"),operatorChars:/^[*+\-%<>!=]/,dateSQL:g("date timestamp"),support:g("ODBCdotTable doubleQuote binaryNumber hexNumber")});b.defineMIME("text/x-pgsql",{name:"sql",client:g("source"),keywords:g(h+"a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get global go goto grant granted greatest grouping groups handler header hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lead leading leakproof least left length level library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat"),builtin:g("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"),atoms:g("false true null unknown"),operatorChars:/^[*+\-%<>!=&|^\/#@?~]/,dateSQL:g("date time timestamp"),support:g("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast")});b.defineMIME("text/x-gql",{name:"sql",keywords:g("ancestor and asc by contains desc descendant distinct from group has in is limit offset on order select superset where"),atoms:g("false true"),builtin:g("blob datetime first key __key__ string integer double boolean null"),operatorChars:/^[*+\-%<>!=]/})}())});(function(b){if(typeof exports=="object"&&typeof module=="object"){b(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],b)}else{b(CodeMirror)}}})(function(e){var d={autoSelfClosers:{area:true,base:true,br:true,col:true,command:true,embed:true,frame:true,hr:true,img:true,input:true,keygen:true,link:true,meta:true,param:true,source:true,track:true,wbr:true,menuitem:true},implicitlyClosed:{dd:true,li:true,optgroup:true,option:true,p:true,rp:true,rt:true,tbody:true,td:true,tfoot:true,th:true,tr:true},contextGrabbers:{dd:{dd:true,dt:true},dt:{dd:true,dt:true},li:{li:true},option:{option:true,optgroup:true},optgroup:{optgroup:true},p:{address:true,article:true,aside:true,blockquote:true,dir:true,div:true,dl:true,fieldset:true,footer:true,form:true,h1:true,h2:true,h3:true,h4:true,h5:true,h6:true,header:true,hgroup:true,hr:true,menu:true,nav:true,ol:true,p:true,pre:true,section:true,table:true,ul:true},rp:{rp:true,rt:true},rt:{rp:true,rt:true},tbody:{tbody:true,tfoot:true},td:{td:true,th:true},tfoot:{tbody:true},th:{td:true,th:true},thead:{tbody:true,tfoot:true},tr:{tr:true}},doNotIndent:{pre:true},allowUnquoted:true,allowMissing:true,caseFold:true};var f={autoSelfClosers:{},implicitlyClosed:{},contextGrabbers:{},doNotIndent:{},allowUnquoted:false,allowMissing:false,caseFold:false};e.defineMode("xml",function(O,b){var H=O.indentUnit;var a={};var P=b.htmlMode?d:f;for(var V in P){a[V]=P[V]}for(var V in b){a[V]=b[V]}var U,T;function L(g,h){function j(l){h.tokenize=l;return l(g,h)}var i=g.next();if(i=="<"){if(g.eat("!")){if(g.eat("[")){if(g.match("CDATA[")){return j(c("atom","]]>"))}else{return null}}else{if(g.match("--")){return j(c("comment","-->"))}else{if(g.match("DOCTYPE",true,true)){g.eatWhile(/[\w\._\-]/);return j(K(1))}else{return null}}}}else{if(g.eat("?")){g.eatWhile(/[\w\._\-]/);h.tokenize=c("meta","?>");return"meta"}else{U=g.eat("/")?"closeTag":"openTag";h.tokenize=M;return"tag bracket"}}}else{if(i=="&"){var k;if(g.eat("#")){if(g.eat("x")){k=g.eatWhile(/[a-fA-F\d]/)&&g.eat(";")}else{k=g.eatWhile(/[\d]/)&&g.eat(";")}}else{k=g.eatWhile(/[\w\.\-:]/)&&g.eat(";")}return k?"atom":"error"}else{g.eatWhile(/[^&<]/);return null}}}L.isInText=true;function M(g,h){var i=g.next();if(i==">"||(i=="/"&&g.eat(">"))){h.tokenize=L;U=i==">"?"endTag":"selfcloseTag";return"tag bracket"}else{if(i=="="){U="equals";return null}else{if(i=="<"){h.tokenize=L;h.state=N;h.tagName=h.tagStart=null;var j=h.tokenize(g,h);return j?j+" tag error":"tag error"}else{if(/[\'\"]/.test(i)){h.tokenize=Q(i);h.stringStartCol=g.column();return h.tokenize(g,h)}else{g.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);return"word"}}}}}function Q(h){var g=function(i,j){while(!i.eol()){if(i.next()==h){j.tokenize=M;break}}return"string"};g.isInAttribute=true;return g}function c(g,h){return function(i,j){while(!i.eol()){if(i.match(h)){j.tokenize=L;break}i.next()}return g}}function K(g){return function(h,i){var j;while((j=h.next())!=null){if(j=="<"){i.tokenize=K(g+1);return i.tokenize(h,i)}else{if(j==">"){if(g==1){i.tokenize=L;break}else{i.tokenize=K(g-1);return i.tokenize(h,i)}}}}return"meta"}}function F(h,i,g){this.prev=h.context;this.tagName=i;this.indent=h.indented;this.startOfLine=g;if(a.doNotIndent.hasOwnProperty(i)||(h.context&&h.context.noIndent)){this.noIndent=true}}function C(g){if(g.context){g.context=g.context.prev}}function G(g,h){var i;while(true){if(!g.context){return}i=g.context.tagName;if(!a.contextGrabbers.hasOwnProperty(i)||!a.contextGrabbers[i].hasOwnProperty(h)){return}C(g)}}function N(i,g,h){if(i=="openTag"){h.tagStart=g.column();return X}else{if(i=="closeTag"){return D}else{return N}}}function X(i,g,h){if(i=="word"){h.tagName=g.current();T="tag";return W}else{T="error";return X}}function D(i,g,h){if(i=="word"){var j=g.current();if(h.context&&h.context.tagName!=j&&a.implicitlyClosed.hasOwnProperty(h.context.tagName)){C(h)}if((h.context&&h.context.tagName==j)||a.matchClosing===false){T="tag";return E}else{T="tag error";return J}}else{T="error";return J}}function E(h,i,g){if(h!="endTag"){T="error";return E}C(g);return N}function J(i,g,h){T="error";return E(i,g,h)}function W(h,j,g){if(h=="word"){T="attribute";return R}else{if(h=="endTag"||h=="selfcloseTag"){var i=g.tagName,k=g.tagStart;g.tagName=g.tagStart=null;if(h=="selfcloseTag"||a.autoSelfClosers.hasOwnProperty(i)){G(g,i)}else{G(g,i);g.context=new F(g,i,k==g.indented)}return N}}T="error";return W}function R(i,g,h){if(i=="equals"){return I}if(!a.allowMissing){T="error"}return W(i,g,h)}function I(i,g,h){if(i=="string"){return S}if(i=="word"&&a.allowUnquoted){T="string";return W}T="error";return W(i,g,h)}function S(i,g,h){if(i=="string"){return S}return W(i,g,h)}return{startState:function(h){var g={tokenize:L,state:N,indented:h||0,tagName:null,tagStart:null,context:null};if(h!=null){g.baseIndent=h}return g},token:function(g,h){if(!h.tagName&&g.sol()){h.indented=g.indentation()}if(g.eatSpace()){return null}U=null;var i=h.tokenize(g,h);if((i||U)&&i!="comment"){T=null;h.state=h.state(U||i,g,h);if(T){i=T=="error"?i+" error":T}}return i},indent:function(g,k,h){var i=g.context;if(g.tokenize.isInAttribute){if(g.tagStart==g.indented){return g.stringStartCol+1}else{return g.indented+H}}if(i&&i.noIndent){return e.Pass}if(g.tokenize!=M&&g.tokenize!=L){return h?h.match(/^(\s*)/)[0].length:0}if(g.tagName){if(a.multilineTagIndentPastTag!==false){return g.tagStart+g.tagName.length+2}else{return g.tagStart+H*(a.multilineTagIndentFactor||1)}}if(a.alignCDATA&&/$/,blockCommentStart:"",configuration:a.htmlMode?"html":"xml",helperType:a.htmlMode?"html":"xml",skipAttribute:function(g){if(g.state==I){g.state=W}}}});e.defineMIME("text/xml","xml");e.defineMIME("application/xml","xml");if(!e.mimeModes.hasOwnProperty("text/html")){e.defineMIME("text/html",{name:"xml",htmlMode:true})}});(function(b){if(typeof exports=="object"&&typeof module=="object"){b(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],b)}else{b(CodeMirror)}}})(function(A){function B(f,b,c,d,a,e){this.indented=f;this.column=b;this.type=c;this.info=d;this.align=a;this.prev=e}function L(d,b,e,a){var c=d.indented;if(d.context&&d.context.type=="statement"&&e!="statement"){c=d.context.indented}return d.context=new B(c,b,e,a,null,d.context)}function y(a){var b=a.context.type;if(b==")"||b=="]"||b=="}"){a.indented=a.context.indented}return a.context=a.context.prev}function R(a,b,c){if(b.prevToken=="variable"||b.prevToken=="variable-3"){return true}if(/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(a.string.slice(0,c))){return true}if(b.typeAtEndOfLine&&a.column()==a.indentation()){return true}}function C(a){for(;;){if(!a||a.type=="top"){return true}if(a.type=="}"&&a.prev.info!="namespace"){return false}a=a.prev}}A.defineMode("clike",function(d,W){var h=d.indentUnit,k=W.statementIndentUnit||h,g=W.dontAlignCalls,t=W.keywords||{},n=W.types||{},c=W.builtin||{},f=W.blockKeywords||{},i=W.defKeywords||{},o=W.atoms||{},q=W.hooks||{},m=W.multiLineStrings,p=W.indentStatements!==false,u=W.indentSwitch!==false,Z=W.namespaceSeparator,X=W.isPunctuationChar||/[\[\]{}\(\),;\:\.]/,l=W.numberStart||/[\d\.]/,s=W.number||/^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i,r=W.isOperatorChar||/[+\-*&%=<>!?|\/]/;var e,Y;function a(U,V){var aa=U.next();if(q[aa]){var S=q[aa](U,V);if(S!==false){return S}}if(aa=='"'||aa=="'"){V.tokenize=v(aa);return V.tokenize(U,V)}if(X.test(aa)){e=aa;return null}if(l.test(aa)){U.backUp(1);if(U.match(s)){return"number"}U.next()}if(aa=="/"){if(U.eat("*")){V.tokenize=j;return j(U,V)}if(U.eat("/")){U.skipToEnd();return"comment"}}if(r.test(aa)){while(!U.match(/^\/[\/*]/,false)&&U.eat(r)){}return"operator"}U.eatWhile(/[\w\$_\xa1-\uffff]/);if(Z){while(U.match(Z)){U.eatWhile(/[\w\$_\xa1-\uffff]/)}}var T=U.current();if(K(t,T)){if(K(f,T)){e="newstatement"}if(K(i,T)){Y=true}return"keyword"}if(K(n,T)){return"variable-3"}if(K(c,T)){if(K(f,T)){e="newstatement"}return"builtin"}if(K(o,T)){return"atom"}return"variable"}function v(S){return function(T,V){var U=false,aa,ab=false;while((aa=T.next())!=null){if(aa==S&&!U){ab=true;break}U=!U&&aa=="\\"}if(ab||!(U||m)){V.tokenize=null}return"string"}}function j(T,U){var S=false,V;while(V=T.next()){if(V=="/"&&S){U.tokenize=null;break}S=(V=="*")}return"comment"}function b(T,S){if(W.typeFirstDefinitions&&T.eol()&&C(S.context)){S.typeAtEndOfLine=R(T,S,T.pos)}}return{startState:function(S){return{tokenize:null,context:new B((S||0)-h,0,"top",null,false),indented:0,startOfLine:true,prevToken:null}},token:function(U,T){var aa=T.context;if(U.sol()){if(aa.align==null){aa.align=false}T.indented=U.indentation();T.startOfLine=true}if(U.eatSpace()){b(U,T);return null}e=Y=null;var V=(T.tokenize||a)(U,T);if(V=="comment"||V=="meta"){return V}if(aa.align==null){aa.align=true}if(e==";"||e==":"||(e==","&&U.match(/^\s*(?:\/\/.*)?$/,false))){while(T.context.type=="statement"){y(T)}}else{if(e=="{"){L(T,U.column(),"}")}else{if(e=="["){L(T,U.column(),"]")}else{if(e=="("){L(T,U.column(),")")}else{if(e=="}"){while(aa.type=="statement"){aa=y(T)}if(aa.type=="}"){aa=y(T)}while(aa.type=="statement"){aa=y(T)}}else{if(e==aa.type){y(T)}else{if(p&&(((aa.type=="}"||aa.type=="top")&&e!=";")||(aa.type=="statement"&&e=="newstatement"))){L(T,U.column(),"statement",U.current())}}}}}}}if(V=="variable"&&((T.prevToken=="def"||(W.typeFirstDefinitions&&R(U,T,U.start)&&C(T.context)&&U.match(/^\s*\(/,false))))){V="def"}if(q.token){var S=q.token(U,T,V);if(S!==undefined){V=S}}if(V=="def"&&W.styleDefs===false){V="variable"}T.startOfLine=false;T.prevToken=Y?"def":V||e;b(U,T);return V},indent:function(T,ad){if(T.tokenize!=a&&T.tokenize!=null||T.typeAtEndOfLine){return A.Pass}var S=T.context,ab=ad&&ad.charAt(0);if(S.type=="statement"&&ab=="}"){S=S.prev}if(W.dontIndentStatements){while(S.type=="statement"&&W.dontIndentStatements.test(S.info)){S=S.prev}}if(q.indent){var U=q.indent(T,S,ad);if(typeof U=="number"){return U}}var ac=ab==S.type;var V=S.prev&&S.prev.info=="switch";if(W.allmanIndentation&&/[{(]/.test(ab)){while(S.type!="top"&&S.type!="}"){S=S.prev}return S.indented}if(S.type=="statement"){return S.indented+(ab=="{"?0:k)}if(S.align&&(!g||S.type!=")")){return S.column+(ac?0:1)}if(S.type==")"&&!ac){return S.indented+k}return S.indented+(ac?0:h)+(!ac&&V&&!/^(?:case|default)\b/.test(ad)?h:0)},electricInput:u?/^\s*(?:case .*?:|default:|\{\}?|\})$/:/^\s*[{}]$/,blockCommentStart:"/*",blockCommentEnd:"*/",lineComment:"//",fold:"brace"}});function D(c){var a={},d=c.split(" ");for(var b=0;b <- <: <% >: # @ assert assume require print println printf readLine readBoolean readByte readShort readChar readInt readLong readFloat readDouble :: #:: "),types:D("AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable Compiler Double Exception Float Integer Long Math Number Object Package Pair Process Runtime Runnable SecurityManager Short StackTraceElement StrictMath String StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"),multiLineStrings:true,blockKeywords:D("catch class do else finally for forSome if match switch try while"),defKeywords:D("class def object package trait type val var"),atoms:D("true false null"),indentStatements:false,indentSwitch:false,hooks:{"@":function(a){a.eatWhile(/[\w\$_]/);return"meta"},'"':function(a,b){if(!a.match('""')){return false}b.tokenize=P;return b.tokenize(a,b)},"'":function(a){a.eatWhile(/[\w\$_\xa1-\uffff]/);return"atom"},"=":function(c,a){var b=a.context;if(b.type=="}"&&b.align&&c.eat(">")){a.context=new B(b.indented,b.column,b.type,b.info,null,b.prev);return"operator"}else{return false}}},modeProps:{closeBrackets:{triples:'"'}}});function Q(a){return function(f,d){var c=false,e,b=false;while(!f.eol()){if(!a&&!c&&f.match('"')){b=true;break}if(a&&f.match('"""')){b=true;break}e=f.next();if(!c&&e=="$"&&f.match("{")){f.skipTo("}")}c=!c&&e=="\\"&&!a}if(b||!a){d.tokenize=null}return"string"}}H("text/x-kotlin",{name:"clike",keywords:D("package as typealias class interface this super val var fun for is in This throw return break continue object if else while do try when !in !is as? file import where by get set abstract enum open inner override private public internal protected catch finally out final vararg reified dynamic companion constructor init sealed field property receiver param sparam lateinit data inline noinline tailrec external annotation crossinline const operator infix"),types:D("Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable Compiler Double Exception Float Integer Long Math Number Object Package Pair Process Runtime Runnable SecurityManager Short StackTraceElement StrictMath String StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"),intendSwitch:false,indentStatements:false,multiLineStrings:true,blockKeywords:D("catch class do else finally for if where try while enum"),defKeywords:D("class val var object package interface fun"),atoms:D("true false null this"),hooks:{'"':function(a,b){b.tokenize=Q(a.match('""'));return b.tokenize(a,b)}},modeProps:{closeBrackets:{triples:'"'}}});H(["x-shader/x-vertex","x-shader/x-fragment"],{name:"clike",keywords:D("sampler1D sampler2D sampler3D samplerCube sampler1DShadow sampler2DShadow const attribute uniform varying break continue discard return for while do if else struct in out inout"),types:D("float int bool void vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 mat2 mat3 mat4"),blockKeywords:D("for while do if else struct"),builtin:D("radians degrees sin cos tan asin acos atan pow exp log exp2 sqrt inversesqrt abs sign floor ceil fract mod min max clamp mix step smoothstep length distance dot cross normalize ftransform faceforward reflect refract matrixCompMult lessThan lessThanEqual greaterThan greaterThanEqual equal notEqual any all not texture1D texture1DProj texture1DLod texture1DProjLod texture2D texture2DProj texture2DLod texture2DProjLod texture3D texture3DProj texture3DLod texture3DProjLod textureCube textureCubeLod shadow1D shadow2D shadow1DProj shadow2DProj shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod dFdx dFdy fwidth noise1 noise2 noise3 noise4"),atoms:D("true false gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 gl_FogCoord gl_PointCoord gl_Position gl_PointSize gl_ClipVertex gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor gl_TexCoord gl_FogFragCoord gl_FragCoord gl_FrontFacing gl_FragData gl_FragDepth gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose gl_ProjectionMatrixInverseTranspose gl_ModelViewProjectionMatrixInverseTranspose gl_TextureMatrixInverseTranspose gl_NormalScale gl_DepthRange gl_ClipPlane gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel gl_FrontLightModelProduct gl_BackLightModelProduct gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ gl_FogParameters gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits gl_MaxDrawBuffers"),indentSwitch:false,hooks:{"#":F},modeProps:{fold:["brace","include"]}});H("text/x-nesc",{name:"clike",keywords:D(G+"as atomic async call command component components configuration event generic implementation includes interface module new norace nx_struct nx_union post provides signal task uses abstract extends"),types:D(z),blockKeywords:D("case do else for if switch while struct"),atoms:D("null true false"),hooks:{"#":F},modeProps:{fold:["brace","include"]}});H("text/x-objectivec",{name:"clike",keywords:D(G+"inline restrict _Bool _Complex _Imaginary BOOL Class bycopy byref id IMP in inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"),types:D(z),atoms:D("YES NO NULL NILL ON OFF true false"),hooks:{"@":function(a){a.eatWhile(/[\w\$]/);return"keyword"},"#":F,indent:function(c,b,a){if(b.type=="statement"&&/^@\w/.test(a)){return b.indented}}},modeProps:{fold:"brace"}});H("text/x-squirrel",{name:"clike",keywords:D("base break clone continue const default delete enum extends function in class foreach local resume return this throw typeof yield constructor instanceof static"),types:D(z),blockKeywords:D("case catch class else for foreach if switch try while"),defKeywords:D("function local class"),typeFirstDefinitions:true,atoms:D("true false null"),hooks:{"#":F},modeProps:{fold:["brace","include"]}});var N=null;function I(a){return function(f,d){var c=false,e,b=false;while(!f.eol()){if(!c&&f.match('"')&&(a=="single"||f.match('""'))){b=true;break}if(!c&&f.match("``")){N=I(a);b=true;break}e=f.next();c=a=="single"&&!c&&e=="\\"}if(b){d.tokenize=null}return"string"}}H("text/x-ceylon",{name:"clike",keywords:D("abstracts alias assembly assert assign break case catch class continue dynamic else exists extends finally for function given if import in interface is let module new nonempty object of out outer package return satisfies super switch then this throw try value void while"),types:function(b){var a=b.charAt(0);return(a===a.toUpperCase()&&a!==a.toLowerCase())},blockKeywords:D("case catch class dynamic else finally for function if interface module new object switch try while"),defKeywords:D("class dynamic function interface module object package value"),builtin:D("abstract actual aliased annotation by default deprecated doc final formal late license native optional sealed see serializable shared suppressWarnings tagged throws variable"),isPunctuationChar:/[\[\]{}\(\),;\:\.`]/,isOperatorChar:/[+\-*&%=<>!?|^~:\/]/,numberStart:/[\d#$]/,number:/^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i,multiLineStrings:true,typeFirstDefinitions:true,atoms:D("true false null larger smaller equal empty finished"),indentSwitch:false,styleDefs:false,hooks:{"@":function(a){a.eatWhile(/[\w\$_]/);return"meta"},'"':function(a,b){b.tokenize=I(a.match('""')?"triple":"single");return b.tokenize(a,b)},"`":function(a,b){if(!N||!a.match("`")){return false}b.tokenize=N;N=null;return b.tokenize(a,b)},"'":function(a){a.eatWhile(/[\w\$_\xa1-\uffff]/);return"atom"},token:function(b,c,a){if((a=="variable"||a=="variable-3")&&c.prevToken=="."){return"variable-2"}}},modeProps:{fold:["brace","import"],closeBrackets:{triples:'"'}}})});(function(b){if(typeof exports=="object"&&typeof module=="object"){b(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],b)}else{b(CodeMirror)}}})(function(E){E.defineMode("css",function(b,r){var ac=r.inline;if(!r.propertyKeywords){r=E.resolveMode("text/css")}var j=b.indentUnit,af=r.tokenHooks,s=r.documentTypes||{},c=r.mediaTypes||{},o=r.mediaFeatures||{},k=r.mediaValueKeywords||{},t=r.propertyKeywords||{},ae=r.nonStandardPropertyKeywords||{},x=r.fontProperties||{},d=r.counterDescriptors||{},l=r.colorKeywords||{},h=r.valueKeywords||{},n=r.allowNested,f=r.supportsAtComponent===true;var ad,m;function a(X,W){ad=W;return X}function ag(W,X){var Y=W.next();if(af[Y]){var Z=af[Y](W,X);if(Z!==false){return Z}}if(Y=="@"){W.eatWhile(/[\w\\\-]/);return a("def",W.current())}else{if(Y=="="||(Y=="~"||Y=="|")&&W.eat("=")){return a(null,"compare")}else{if(Y=='"'||Y=="'"){X.tokenize=q(Y);return X.tokenize(W,X)}else{if(Y=="#"){W.eatWhile(/[\w\\\-]/);return a("atom","hash")}else{if(Y=="!"){W.match(/^\s*\w*/);return a("keyword","important")}else{if(/\d/.test(Y)||Y=="."&&W.eat(/\d/)){W.eatWhile(/[\w.%]/);return a("number","unit")}else{if(Y==="-"){if(/[\d.]/.test(W.peek())){W.eatWhile(/[\w.%]/);return a("number","unit")}else{if(W.match(/^-[\w\\\-]+/)){W.eatWhile(/[\w\\\-]/);if(W.match(/^\s*:/,false)){return a("variable-2","variable-definition")}return a("variable-2","variable")}else{if(W.match(/^\w+-/)){return a("meta","meta")}}}}else{if(/[,+>*\/]/.test(Y)){return a(null,"select-op")}else{if(Y=="."&&W.match(/^-?[_a-z][_a-z0-9-]*/i)){return a("qualifier","qualifier")}else{if(/[:;{}\[\]\(\)]/.test(Y)){return a(null,Y)}else{if((Y=="u"&&W.match(/rl(-prefix)?\(/))||(Y=="d"&&W.match("omain("))||(Y=="r"&&W.match("egexp("))){W.backUp(1);X.tokenize=ah;return a("property","word")}else{if(/[\w\\\-]/.test(Y)){W.eatWhile(/[\w\\\-]/);return a("property","word")}else{return a(null,null)}}}}}}}}}}}}}function q(W){return function(X,Z){var Y=false,aa;while((aa=X.next())!=null){if(aa==W&&!Y){if(W==")"){X.backUp(1)}break}Y=!Y&&aa=="\\"}if(aa==W||!Y&&W!=")"){Z.tokenize=null}return a("string","string")}}function ah(W,X){W.next();if(!W.match(/\s*[\"\')]/,false)){X.tokenize=q(")")}else{X.tokenize=null}return a(null,"(")}function i(X,Y,W){this.type=X;this.indent=Y;this.prev=W}function v(X,W,Y,Z){X.context=new i(Y,W.indentation()+(Z===false?0:j),X.context);return Y}function g(W){if(W.context.prev){W.context=W.context.prev}return W.context.type}function p(Y,W,X){return w[X.context.type](Y,W,X)}function e(Z,X,Y,W){for(var aa=W||1;aa>0;aa--){Y.context=Y.context.prev}return p(Z,X,Y)}function u(W){var X=W.current().toLowerCase();if(h.hasOwnProperty(X)){m="atom"}else{if(l.hasOwnProperty(X)){m="keyword"}else{m="variable"}}}var w={};w.top=function(Y,W,X){if(Y=="{"){return v(X,W,"block")}else{if(Y=="}"&&X.context.prev){return g(X)}else{if(f&&/@component/.test(Y)){return v(X,W,"atComponentBlock")}else{if(/^@(-moz-)?document$/.test(Y)){return v(X,W,"documentTypes")}else{if(/^@(media|supports|(-moz-)?document|import)$/.test(Y)){return v(X,W,"atBlock")}else{if(/^@(font-face|counter-style)/.test(Y)){X.stateArg=Y;return"restricted_atBlock_before"}else{if(/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(Y)){return"keyframes"}else{if(Y&&Y.charAt(0)=="@"){return v(X,W,"at")}else{if(Y=="hash"){m="builtin"}else{if(Y=="word"){m="tag"}else{if(Y=="variable-definition"){return"maybeprop"}else{if(Y=="interpolation"){return v(X,W,"interpolation")}else{if(Y==":"){return"pseudo"}else{if(n&&Y=="("){return v(X,W,"parens")}}}}}}}}}}}}}}return X.context.type};w.block=function(Z,W,Y){if(Z=="word"){var X=W.current().toLowerCase();if(t.hasOwnProperty(X)){m="property";return"maybeprop"}else{if(ae.hasOwnProperty(X)){m="string-2";return"maybeprop"}else{if(n){m=W.match(/^\s*:(?:\s|$)/,false)?"property":"tag";return"block"}else{m+=" error";return"maybeprop"}}}}else{if(Z=="meta"){return"block"}else{if(!n&&(Z=="hash"||Z=="qualifier")){m="error";return"block"}else{return w.top(Z,W,Y)}}}};w.maybeprop=function(Y,W,X){if(Y==":"){return v(X,W,"prop")}return p(Y,W,X)};w.prop=function(Y,W,X){if(Y==";"){return g(X)}if(Y=="{"&&n){return v(X,W,"propBlock")}if(Y=="}"||Y=="{"){return e(Y,W,X)}if(Y=="("){return v(X,W,"parens")}if(Y=="hash"&&!/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(W.current())){m+=" error"}else{if(Y=="word"){u(W)}else{if(Y=="interpolation"){return v(X,W,"interpolation")}}}return"prop"};w.propBlock=function(X,Y,W){if(X=="}"){return g(W)}if(X=="word"){m="property";return"maybeprop"}return W.context.type};w.parens=function(Y,W,X){if(Y=="{"||Y=="}"){return e(Y,W,X)}if(Y==")"){return g(X)}if(Y=="("){return v(X,W,"parens")}if(Y=="interpolation"){return v(X,W,"interpolation")}if(Y=="word"){u(W)}return"parens"};w.pseudo=function(Y,W,X){if(Y=="word"){m="variable-3";return X.context.type}return p(Y,W,X)};w.documentTypes=function(Y,W,X){if(Y=="word"&&s.hasOwnProperty(W.current())){m="tag";return X.context.type}else{return w.atBlock(Y,W,X)}};w.atBlock=function(Z,W,Y){if(Z=="("){return v(Y,W,"atBlock_parens")}if(Z=="}"||Z==";"){return e(Z,W,Y)}if(Z=="{"){return g(Y)&&v(Y,W,n?"block":"top")}if(Z=="interpolation"){return v(Y,W,"interpolation")}if(Z=="word"){var X=W.current().toLowerCase();if(X=="only"||X=="not"||X=="and"||X=="or"){m="keyword"}else{if(c.hasOwnProperty(X)){m="attribute"}else{if(o.hasOwnProperty(X)){m="property"}else{if(k.hasOwnProperty(X)){m="keyword"}else{if(t.hasOwnProperty(X)){m="property"}else{if(ae.hasOwnProperty(X)){m="string-2"}else{if(h.hasOwnProperty(X)){m="atom"}else{if(l.hasOwnProperty(X)){m="keyword"}else{m="error"}}}}}}}}}return Y.context.type};w.atComponentBlock=function(Y,W,X){if(Y=="}"){return e(Y,W,X)}if(Y=="{"){return g(X)&&v(X,W,n?"block":"top",false)}if(Y=="word"){m="error"}return X.context.type};w.atBlock_parens=function(Y,W,X){if(Y==")"){return g(X)}if(Y=="{"||Y=="}"){return e(Y,W,X,2)}return w.atBlock(Y,W,X)};w.restricted_atBlock_before=function(Y,W,X){if(Y=="{"){return v(X,W,"restricted_atBlock")}if(Y=="word"&&X.stateArg=="@counter-style"){m="variable";return"restricted_atBlock_before"}return p(Y,W,X)};w.restricted_atBlock=function(Y,W,X){if(Y=="}"){X.stateArg=null;return g(X)}if(Y=="word"){if((X.stateArg=="@font-face"&&!x.hasOwnProperty(W.current().toLowerCase()))||(X.stateArg=="@counter-style"&&!d.hasOwnProperty(W.current().toLowerCase()))){m="error"}else{m="property"}return"maybeprop"}return"restricted_atBlock"};w.keyframes=function(Y,W,X){if(Y=="word"){m="variable";return"keyframes"}if(Y=="{"){return v(X,W,"top")}return p(Y,W,X)};w.at=function(Y,W,X){if(Y==";"){return g(X)}if(Y=="{"||Y=="}"){return e(Y,W,X)}if(Y=="word"){m="tag"}else{if(Y=="hash"){m="builtin"}}return"at"};w.interpolation=function(Y,W,X){if(Y=="}"){return g(X)}if(Y=="{"||Y==";"){return e(Y,W,X)}if(Y=="word"){m="variable"}else{if(Y!="variable"&&Y!="("&&Y!=")"){m="error"}}return"interpolation"};return{startState:function(W){return{tokenize:null,state:ac?"block":"top",stateArg:null,context:new i(ac?"block":"top",W||0,null)}},token:function(W,X){if(!X.tokenize&&W.eatSpace()){return null}var Y=(X.tokenize||ag)(W,X);if(Y&&typeof Y=="object"){ad=Y[1];Y=Y[0]}m=Y;X.state=w[X.state](ad,W,X);return m},indent:function(W,Y){var Z=W.context,X=Y&&Y.charAt(0);var aa=Z.indent;if(Z.type=="prop"&&(X=="}"||X==")")){Z=Z.prev}if(Z.prev){if(X=="}"&&(Z.type=="block"||Z.type=="top"||Z.type=="interpolation"||Z.type=="restricted_atBlock")){Z=Z.prev;aa=Z.indent}else{if(X==")"&&(Z.type=="parens"||Z.type=="atBlock_parens")||X=="{"&&(Z.type=="at"||Z.type=="atBlock")){aa=Math.max(0,Z.indent-j);Z=Z.prev}}}return aa},electricChars:"}",blockCommentStart:"/*",blockCommentEnd:"*/",fold:"brace"}});function P(a){var b={};for(var c=0;c-1){e.backUp(a.length-b)}else{if(a.match(/<\/?$/)){e.backUp(a.length);if(!e.match(d,false)){e.match(a)}}}return c}var n={};function p(b){var a=n[b];if(a){return a}return n[b]=new RegExp("\\s+"+b+"\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*")}function r(a,c){var b=a.match(p(c));return b?/^\s*(.*?)\s*$/.exec(b[2])[1]:""}function j(a,b){return new RegExp((b?"^":"")+"","i")}function k(e,f){for(var d in e){var c=f[d]||(f[d]=[]);var a=e[d];for(var b=a.length-1;b>=0;b--){c.unshift(a[b])}}}function q(b,a){for(var c=0;c=0;a--){d.script.unshift(["type",h[a].matches,h[a].mode])}}function b(C,i){var J=e.token(C,i.htmlState),B=/\btag\b/.test(J),H;if(B&&!/[<>\s\/]/.test(C.current())&&(H=i.htmlState.tagName&&i.htmlState.tagName.toLowerCase())&&d.hasOwnProperty(H)){i.inTag=H+" "}else{if(i.inTag&&B&&/>$/.test(C.current())){var I=/^([\S]+) (.*)/.exec(i.inTag);i.inTag=null;var D=C.current()==">"&&q(d[I[1]],I[2]);var E=o.getMode(c,D);var F=j(I[1],true),G=j(I[1],false);i.token=function(s,t){if(s.match(F,false)){t.token=b;t.localState=t.localMode=null;return null}return m(s,G,t.localMode.token(s,t.localState))};i.localMode=E;i.localState=o.startState(E,e.indent(i.htmlState,""))}else{if(i.inTag){i.inTag+=C.current();if(C.eol()){i.inTag+=" "}}}}return J}return{startState:function(){var i=o.startState(e);return{token:b,inTag:null,localMode:null,localState:null,htmlState:i}},copyState:function(i){var t;if(i.localState){t=o.copyState(i.localMode,i.localState)}return{token:i.token,inTag:i.inTag,localMode:i.localMode,localState:t,htmlState:o.copyState(e,i.htmlState)}},token:function(i,t){return t.token(i,t)},indent:function(i,t){if(!i.localMode||/^\s*<\//.test(t)){return e.indent(i.htmlState,t)}else{if(i.localMode.indent){return i.localMode.indent(i.localState,t)}else{return o.Pass}}},innerMode:function(i){return{state:i.localState||i.htmlState,mode:i.localMode||e}}}},"xml","javascript","css");o.defineMIME("text/html","htmlmixed")});(function(b){if(typeof exports=="object"&&typeof module=="object"){b(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],b)}else{b(CodeMirror)}}})(function(d){function c(a,b,f){return/^(?:operator|sof|keyword c|case|new|[\[{}\(,;:]|=>)$/.test(b.lastType)||(b.lastType=="quasi"&&/\{\s*$/.test(a.string.slice(0,a.pos-(f||0))))}d.defineMode("javascript",function(bU,bK){var a0=bU.indentUnit;var bE=bK.statementIndent;var b8=bK.jsonld;var bG=bK.json||b8;var a6=bK.typescript;var cd=bK.wordCharacters||/[\w$\xa1-\uffff]/;var bt=function(){function f(o){return{type:o,style:"keyword"}}var k=f("keyword a"),m=f("keyword b"),n=f("keyword c");var l=f("operator"),h={type:"atom",style:"atom"};var j={"if":f("if"),"while":k,"with":k,"else":m,"do":m,"try":m,"finally":m,"return":n,"break":n,"continue":n,"new":f("new"),"delete":n,"throw":n,"debugger":n,"var":f("var"),"const":f("var"),let:f("var"),"function":f("function"),"catch":f("catch"),"for":f("for"),"switch":f("switch"),"case":f("case"),"default":f("default"),"in":l,"typeof":l,"instanceof":l,"true":h,"false":h,"null":h,"undefined":h,"NaN":h,"Infinity":h,"this":f("this"),"class":f("class"),"super":f("atom"),yield:n,"export":f("export"),"import":f("import"),"extends":n,await:n,async:f("async")};if(a6){var e={type:"variable",style:"variable-3"};var i={"interface":f("class"),"implements":n,namespace:n,module:f("module"),"enum":f("module"),type:f("type"),"public":f("modifier"),"private":f("modifier"),"protected":f("modifier"),"abstract":f("modifier"),as:l,string:e,number:e,"boolean":e,any:e};for(var g in i){j[g]=i[g]}}return j}();var bh=/[+\-*&%=<>!?|~^]/;var bv=/^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;function bu(e){var g=false,h,f=false;while((h=e.next())!=null){if(!g){if(h=="/"&&!f){return}if(h=="["){f=true}else{if(f&&h=="]"){f=false}}}g=!g&&h=="\\"}}var bd,bs;function bl(e,f,g){bd=e;bs=g;return f}function bb(i,f){var h=i.next();if(h=='"'||h=="'"){f.tokenize=be(h);return f.tokenize(i,f)}else{if(h=="."&&i.match(/^\d+(?:[eE][+\-]?\d+)?/)){return bl("number","number")}else{if(h=="."&&i.match("..")){return bl("spread","meta")}else{if(/[\[\]{}\(\),;\:\.]/.test(h)){return bl(h)}else{if(h=="="&&i.eat(">")){return bl("=>","operator")}else{if(h=="0"&&i.eat(/x/i)){i.eatWhile(/[\da-f]/i);return bl("number","number")}else{if(h=="0"&&i.eat(/o/i)){i.eatWhile(/[0-7]/i);return bl("number","number")}else{if(h=="0"&&i.eat(/b/i)){i.eatWhile(/[01]/i);return bl("number","number")}else{if(/\d/.test(h)){i.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);return bl("number","number")}else{if(h=="/"){if(i.eat("*")){f.tokenize=b9;return b9(i,f)}else{if(i.eat("/")){i.skipToEnd();return bl("comment","comment")}else{if(c(i,f,1)){bu(i);i.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);return bl("regexp","string-2")}else{i.eatWhile(bh);return bl("operator","operator",i.current())}}}}else{if(h=="`"){f.tokenize=b7;return b7(i,f)}else{if(h=="#"){i.skipToEnd();return bl("error","error")}else{if(bh.test(h)){i.eatWhile(bh);return bl("operator","operator",i.current())}else{if(cd.test(h)){i.eatWhile(cd);var e=i.current(),g=bt.propertyIsEnumerable(e)&&bt[e];return(g&&f.lastType!=".")?bl(g.type,g.style,e):bl("variable","variable",e)}}}}}}}}}}}}}}}function be(e){return function(i,g){var f=false,h;if(b8&&i.peek()=="@"&&i.match(bv)){g.tokenize=bb;return bl("jsonld-keyword","meta")}while((h=i.next())!=null){if(h==e&&!f){break}f=!f&&h=="\\"}if(!f){g.tokenize=bb}return bl("string","string")}}function b9(e,f){var h=false,g;while(g=e.next()){if(g=="/"&&h){f.tokenize=bb;break}h=(g=="*")}return bl("comment","comment")}function b7(e,g){var f=false,h;while((h=e.next())!=null){if(!f&&(h=="`"||h=="$"&&e.eat("{"))){g.tokenize=bb;break}f=!f&&h=="\\"}return bl("quasi","string-2",e.current())}var aZ="([{}])";function cb(e,k){if(k.fatArrowAt){k.fatArrowAt=null}var f=e.string.indexOf("=>",e.start);if(f<0){return}if(a6){var i=/:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(e.string.slice(e.start,f));if(i){f=i.index}}var h=0,j=false;for(var g=f-1;g>=0;--g){var m=e.string.charAt(g);var l=aZ.indexOf(m);if(l>=0&&l<3){if(!h){++g;break}if(--h==0){if(m=="("){j=true}break}}else{if(l>=3&&l<6){++h}else{if(cd.test(m)){j=true}else{if(/["'\/]/.test(m)){return}else{if(j&&!h){++g;break}}}}}}if(j&&!h){k.fatArrowAt=g}}var ba={atom:true,number:true,variable:true,string:true,regexp:true,"this":true,"jsonld-keyword":true};function bn(i,g,h,j,f,e){this.indented=i;this.column=g;this.type=h;this.prev=f;this.info=e;if(j!=null){this.align=j}}function aS(e,f){for(var g=e.localVars;g;g=g.next){if(g.name==f){return true}}for(var h=e.context;h;h=h.prev){for(var g=h.vars;g;g=g.next){if(g.name==f){return true}}}}function a7(k,g,h,e,j){var i=k.cc;bx.state=k;bx.stream=j;bx.marked=null,bx.cc=i;bx.style=g;if(!k.lexical.hasOwnProperty("align")){k.lexical.align=true}while(true){var f=i.length?i.pop():bG?bD:b2;if(f(h,e)){while(i.length&&i[i.length-1].lex){i.pop()()}if(bx.marked){return bx.marked}if(h=="variable"&&aS(k,e)){return"variable-2"}return g}}}var bx={state:null,column:null,marked:null,cc:null};function bT(){for(var e=arguments.length-1;e>=0;e--){bx.cc.push(arguments[e])}}function bO(){bT.apply(null,arguments);return true}function cc(f){function g(i){for(var h=i;h;h=h.next){if(h.name==f){return true}}return false}var e=bx.state;bx.marked="def";if(e.context){if(g(e.localVars)){return}e.localVars={name:f,next:e.localVars}}else{if(g(e.globalVars)){return}if(bK.globalVars){e.globalVars={name:f,next:e.globalVars}}}}var aU={name:"this",next:{name:"arguments"}};function b(){bx.state.context={prev:bx.state.context,vars:bx.state.localVars};bx.state.localVars=aU}function a(){bx.state.localVars=bx.state.context.vars;bx.state.context=bx.state.context.prev}function b4(f,e){var g=function(){var i=bx.state,h=i.indented;if(i.lexical.type=="stat"){h=i.lexical.indented}else{for(var j=i.lexical;j&&j.type==")"&&j.align;j=j.prev){h=j.indented}}i.lexical=new bn(h,bx.stream.column(),f,null,i.lexical,e)};g.lex=true;return g}function a5(){var e=bx.state;if(e.lexical.prev){if(e.lexical.type==")"){e.indented=e.lexical.indented}e.lexical=e.lexical.prev}}a5.lex=true;function aT(f){function e(g){if(g==f){return bO()}else{if(f==";"){return bT()}else{return bO(e)}}}return e}function b2(f,e){if(f=="var"){return bO(b4("vardef",e.length),a9,aT(";"),a5)}if(f=="keyword a"){return bO(b4("form"),bB,b2,a5)}if(f=="keyword b"){return bO(b4("form"),b2,a5)}if(f=="{"){return bO(b4("}"),bH,a5)}if(f==";"){return bO()}if(f=="if"){if(bx.state.lexical.info=="else"&&bx.state.cc[bx.state.cc.length-1]==a5){bx.state.cc.pop()()}return bO(b4("form"),bB,b2,a5,a8)}if(f=="function"){return bO(bk)}if(f=="for"){return bO(b4("form"),aQ,b2,a5)}if(f=="variable"){return bO(b4("stat"),b1)}if(f=="switch"){return bO(b4("form"),bB,b4("}","switch"),aT("{"),bH,a5,a5)}if(f=="case"){return bO(bD,aT(":"))}if(f=="default"){return bO(aT(":"))}if(f=="catch"){return bO(b4("form"),b,aT("("),bP,aT(")"),b2,a5,a)}if(f=="class"){return bO(b4("form"),bY,a5)}if(f=="export"){return bO(b4("stat"),b3,a5)}if(f=="import"){return bO(b4("stat"),bN,a5)}if(f=="module"){return bO(b4("form"),a4,b4("}"),aT("{"),bH,a5,a5)}if(f=="type"){return bO(a3,aT("operator"),a3,aT(";"))}if(f=="async"){return bO(b2)}return bT(b4("stat"),bD,aT(";"),a5)}function bD(e){return bV(e,false)}function b5(e){return bV(e,true)}function bB(e){if(e!="("){return bT()}return bO(b4(")"),bD,aT(")"),a5)}function bV(g,e){if(bx.state.fatArrowAt==bx.stream.start){var h=e?bj:bX;if(g=="("){return bO(b,b4(")"),ce(a4,")"),a5,aT("=>"),h,a)}else{if(g=="variable"){return bT(b,a4,aT("=>"),h,a)}}}var f=e?a2:bS;if(ba.hasOwnProperty(g)){return bO(f)}if(g=="function"){return bO(bk,f)}if(g=="class"){return bO(b4("form"),bp,a5)}if(g=="keyword c"||g=="async"){return bO(e?bJ:bL)}if(g=="("){return bO(b4(")"),bL,aT(")"),a5,f)}if(g=="operator"||g=="spread"){return bO(e?b5:bD)}if(g=="["){return bO(b4("]"),aY,a5,f)}if(g=="{"){return ca(aR,"}",null,f)}if(g=="quasi"){return bT(bg,f)}if(g=="new"){return bO(bq(e))}return bO()}function bL(e){if(e.match(/[;\}\)\],]/)){return bT()}return bT(bD)}function bJ(e){if(e.match(/[;\}\)\],]/)){return bT()}return bT(b5)}function bS(f,e){if(f==","){return bO(bD)}return a2(f,e,false)}function a2(h,f,i){var g=i==false?bS:a2;var e=i==false?bD:b5;if(h=="=>"){return bO(b,i?bj:bX,a)}if(h=="operator"){if(/\+\+|--/.test(f)){return bO(g)}if(f=="?"){return bO(bD,aT(":"),e)}return bO(e)}if(h=="quasi"){return bT(bg,g)}if(h==";"){return}if(h=="("){return ca(b5,")","call",g)}if(h=="."){return bO(bI,g)}if(h=="["){return bO(b4("]"),bL,aT("]"),a5,g)}}function bg(f,e){if(f!="quasi"){return bT()}if(e.slice(e.length-2)!="${"){return bO(bg)}return bO(bD,aV)}function aV(e){if(e=="}"){bx.marked="string-2";bx.state.tokenize=b7;return bO(bg)}}function bX(e){cb(bx.stream,bx.state);return bT(e=="{"?b2:bD)}function bj(e){cb(bx.stream,bx.state);return bT(e=="{"?b2:b5)}function bq(e){return function(f){if(f=="."){return bO(e?aX:bZ)}else{return bT(e?b5:bD)}}}function bZ(f,e){if(e=="target"){bx.marked="keyword";return bO(bS)}}function aX(f,e){if(e=="target"){bx.marked="keyword";return bO(a2)}}function b1(e){if(e==":"){return bO(a5,b2)}return bT(bS,aT(";"),a5)}function bI(e){if(e=="variable"){bx.marked="property";return bO()}}function aR(f,e){if(f=="async"){bx.marked="property";return bO(aR)}else{if(f=="variable"||bx.style=="keyword"){bx.marked="property";if(e=="get"||e=="set"){return bO(bo)}return bO(bm)}else{if(f=="number"||f=="string"){bx.marked=b8?"property":(bx.style+" property");return bO(bm)}else{if(f=="jsonld-keyword"){return bO(bm)}else{if(f=="modifier"){return bO(aR)}else{if(f=="["){return bO(bD,aT("]"),bm)}else{if(f=="spread"){return bO(bD)}else{if(f==":"){return bT(bm)}}}}}}}}}function bo(e){if(e!="variable"){return bT(bm)}bx.marked="property";return bO(bk)}function bm(e){if(e==":"){return bO(b5)}if(e=="("){return bT(bk)}}function ce(e,g){function f(j,i){if(j==","){var h=bx.state.lexical;if(h.info=="call"){h.pos=(h.pos||0)+1}return bO(function(l,k){if(l==g||k==g){return bT()}return bT(e)},f)}if(j==g||i==g){return bO()}return bO(aT(g))}return function(h,i){if(h==g||i==g){return bO()}return bT(e,f)}}function ca(e,h,f){for(var g=3;g"){return bO(a3)}}function bF(e){if(e=="variable"||bx.style=="keyword"){bx.marked="property";return bO(bF)}else{if(e==":"){return bO(a3)}}}function bR(e){if(e=="variable"){return bO(bR)}else{if(e==":"){return bO(a3)}}}function br(f,e){if(e=="<"){return bO(ce(a3,">"),br)}if(f=="["){return bO(aT("]"),br)}}function a9(){return bT(a4,bc,bQ,bW)}function a4(f,e){if(f=="modifier"){return bO(a4)}if(f=="variable"){cc(e);return bO()}if(f=="spread"){return bO(a4)}if(f=="["){return ca(a4,"]")}if(f=="{"){return ca(b6,"}")}}function b6(f,e){if(f=="variable"&&!bx.stream.match(/^\s*:/,false)){cc(e);return bO(bQ)}if(f=="variable"){bx.marked="property"}if(f=="spread"){return bO(a4)}if(f=="}"){return bT()}return bO(aT(":"),a4,bQ)}function bQ(f,e){if(e=="="){return bO(b5)}}function bW(e){if(e==","){return bO(a9)}}function a8(f,e){if(f=="keyword b"&&e=="else"){return bO(b4("form","else"),b2,a5)}}function aQ(e){if(e=="("){return bO(b4(")"),bz,aT(")"),a5)}}function bz(e){if(e=="var"){return bO(a9,aT(";"),bA)}if(e==";"){return bO(bA)}if(e=="variable"){return bO(aP)}return bT(bD,aT(";"),bA)}function aP(f,e){if(e=="in"||e=="of"){bx.marked="keyword";return bO(bD)}return bO(bS,bA)}function bA(f,e){if(f==";"){return bO(bC)}if(e=="in"||e=="of"){bx.marked="keyword";return bO(bD)}return bT(bD,aT(";"),bC)}function bC(e){if(e!=")"){bO(bD)}}function bk(f,e){if(e=="*"){bx.marked="keyword";return bO(bk)}if(f=="variable"){cc(e);return bO(bk)}if(f=="("){return bO(b,b4(")"),ce(bP,")"),a5,bc,b2,a)}}function bP(e){if(e=="spread"){return bO(bP)}return bT(a4,bc,bQ)}function bp(f,e){if(f=="variable"){return bY(f,e)}return bi(f,e)}function bY(f,e){if(f=="variable"){cc(e);return bO(bi)}}function bi(f,e){if(e=="extends"||e=="implements"){return bO(a6?a3:bD,bi)}if(f=="{"){return bO(b4("}"),aW,a5)}}function aW(f,e){if(f=="variable"||bx.style=="keyword"){if((e=="static"||e=="get"||e=="set"||(a6&&(e=="public"||e=="private"||e=="protected"||e=="readonly"||e=="abstract")))&&bx.stream.match(/^\s+[\w$\xa1-\uffff]/,false)){bx.marked="keyword";return bO(aW)}bx.marked="property";return bO(a6?b0:bk,aW)}if(e=="*"){bx.marked="keyword";return bO(aW)}if(f==";"){return bO(aW)}if(f=="}"){return bO()}}function b0(f,e){if(e=="?"){return bO(b0)}if(f==":"){return bO(a3,bQ)}return bT(bk)}function b3(f,e){if(e=="*"){bx.marked="keyword";return bO(bM,aT(";"))}if(e=="default"){bx.marked="keyword";return bO(bD,aT(";"))}return bT(b2)}function bN(e){if(e=="string"){return bO()}return bT(bw,bM)}function bw(f,e){if(f=="{"){return ca(bw,"}")}if(f=="variable"){cc(e)}if(e=="*"){bx.marked="keyword"}return bO(a1)}function a1(f,e){if(e=="as"){bx.marked="keyword";return bO(bw)}}function bM(f,e){if(e=="from"){bx.marked="keyword";return bO(bD)}}function aY(e){if(e=="]"){return bO()}return bT(ce(b5,"]"))}function by(e,f){return e.lastType=="operator"||e.lastType==","||bh.test(f.charAt(0))||/[,.]/.test(f.charAt(0))}return{startState:function(e){var f={tokenize:bb,lastType:"sof",cc:[],lexical:new bn((e||0)-a0,0,"block",false),localVars:bK.localVars,context:bK.localVars&&{vars:bK.localVars},indented:e||0};if(bK.globalVars&&typeof bK.globalVars=="object"){f.globalVars=bK.globalVars}return f},token:function(e,f){if(e.sol()){if(!f.lexical.hasOwnProperty("align")){f.lexical.align=false}f.indented=e.indentation();cb(e,f)}if(f.tokenize!=b9&&e.eatSpace()){return null}var g=f.tokenize(e,f);if(bd=="comment"){return g}f.lastType=bd=="operator"&&(bs=="++"||bs=="--")?"incdec":bd;return a7(f,g,bd,bs,e)},indent:function(m,j){if(m.tokenize==b9){return d.Pass}if(m.tokenize!=bb){return 0}var f=j&&j.charAt(0),e=m.lexical,g;if(!/^\s*else\b/.test(j)){for(var k=m.cc.length-1;k>=0;--k){var i=m.cc[k];if(i==a5){e=e.prev}else{if(i!=a8){break}}}}while((e.type=="stat"||e.type=="form")&&(f=="}"||((g=m.cc[m.cc.length-1])&&(g==bS||g==a2)&&!/^[,\.=+\-*:?[\(]/.test(j)))){e=e.prev}if(bE&&e.type==")"&&e.prev.type=="stat"){e=e.prev}var h=e.type,l=f==h;if(h=="vardef"){return e.indented+(m.lastType=="operator"||m.lastType==","?e.info+1:0)}else{if(h=="form"&&f=="{"){return e.indented}else{if(h=="form"){return e.indented+a0}else{if(h=="stat"){return e.indented+(by(m,j)?bE||a0:0)}else{if(e.info=="switch"&&!l&&bK.doubleIndentSwitch!=false){return e.indented+(/^(?:case|default)\b/.test(j)?a0:2*a0)}else{if(e.align){return e.column+(l?0:1)}else{return e.indented+(l?0:a0)}}}}}}},electricInput:/^\s*(?:case .*?:|default:|\{|\})$/,blockCommentStart:bG?null:"/*",blockCommentEnd:bG?null:"*/",lineComment:bG?null:"//",fold:"brace",closeBrackets:"()[]{}''\"\"``",helperType:bG?"json":"javascript",jsonldMode:b8,jsonMode:bG,expressionAllowed:c,skipExpression:function(f){var e=f.cc[f.cc.length-1];if(e==bD||e==b5){f.cc.pop()}}}});d.registerHelper("wordChars","javascript",/[\w$]/);d.defineMIME("text/javascript","javascript");d.defineMIME("text/ecmascript","javascript");d.defineMIME("application/javascript","javascript");d.defineMIME("application/x-javascript","javascript");d.defineMIME("application/ecmascript","javascript");d.defineMIME("application/json",{name:"javascript",json:true});d.defineMIME("application/x-json",{name:"javascript",json:true});d.defineMIME("application/ld+json",{name:"javascript",jsonld:true});d.defineMIME("text/typescript",{name:"javascript",typescript:true});d.defineMIME("application/typescript",{name:"javascript",typescript:true})});(function(b){if(typeof exports=="object"&&typeof module=="object"){b(require("../../lib/codemirror"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror"],b)}else{b(CodeMirror)}}})(function(b){b.defineMode("nginx",function(w){function p(c){var e={},d=c.split(" ");for(var f=0;f*\/]/.test(f)){return r(null,"select-op")}else{if(/[;{}:\[\]]/.test(f)){return r(null,f)}else{c.eatWhile(/[\w\\\-]/);return r("variable","variable")}}}}}}}}}}}}function a(c,d){var f=false,e;while((e=c.next())!=null){if(f&&e=="/"){d.tokenize=v;break}f=(e=="*")}return r("comment","comment")}function x(c,d){var e=0,f;while((f=c.next())!=null){if(e>=2&&f==">"){d.tokenize=v;break}e=(f=="-")?e+1:0}return r("comment","comment")}function n(c){return function(d,f){var e=false,g;while((g=d.next())!=null){if(g==c&&!e){break}e=!e&&g=="\\"}if(!e){f.tokenize=v}return r("string","string")}}return{startState:function(c){return{tokenize:v,baseIndent:c||0,stack:[]}},token:function(c,d){if(c.eatSpace()){return null}o=null;var e=d.tokenize(c,d);var f=d.stack[d.stack.length-1];if(o=="hash"&&f=="rule"){e="atom"}else{if(e=="variable"){if(f=="rule"){e="number"}else{if(!f||f=="@media{"){e="tag"}}}}if(f=="rule"&&/^[\{\};]$/.test(o)){d.stack.pop()}if(o=="{"){if(f=="@media"){d.stack[d.stack.length-1]="@media{"}else{d.stack.push("{")}}else{if(o=="}"){d.stack.pop()}else{if(o=="@media"){d.stack.push("@media")}else{if(f=="{"&&o!="comment"){d.stack.push("rule")}}}}return e},indent:function(d,e){var c=d.stack.length;if(/^\}/.test(e)){c-=d.stack[d.stack.length-1]=="rule"?2:1}return d.baseIndent+c*s},electricChars:"}"}});b.defineMIME("text/x-nginx-conf","nginx")});(function(b){if(typeof exports=="object"&&typeof module=="object"){b(require("../../lib/codemirror"),require("../htmlmixed/htmlmixed"),require("../clike/clike"))}else{if(typeof define=="function"&&define.amd){define(["../../lib/codemirror","../htmlmixed/htmlmixed","../clike/clike"],b)}else{b(CodeMirror)}}})(function(o){function m(a){var c={},b=a.split(" ");for(var d=0;d\w/,false)){b.tokenize=n([[["->",null]],[[/[\w]+/,"variable"]]],c,d)}return"variable-2"}var a=false;while(!e.eol()&&(a||d===false||(!e.match("{$",false)&&!e.match(/^(\$[a-zA-Z_][a-zA-Z0-9_]*|\$\{)/,false)))){if(!a&&e.match(c)){b.tokenize=null;b.tokStack.pop();b.tokStack.pop();break}a=e.next()=="\\"&&!a}return"string"}var k="abstract and array as break case catch class clone const continue declare default do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final for foreach function global goto if implements interface instanceof namespace new or private protected public static switch throw trait try use var while xor die echo empty exit eval include include_once isset list require require_once return print unset __halt_compiler self static parent yield insteadof finally";var j="true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__";var r="func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents file_put_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists array_intersect_key array_combine array_column pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count";o.registerHelper("hintWords","php",[k,j,r].join(" ").split(" "));o.registerHelper("wordChars","php",/[\w$]/);var l={name:"clike",helperType:"php",keywords:m(k),blockKeywords:m("catch do else elseif for foreach if switch try while finally"),defKeywords:m("class function interface namespace trait"),atoms:m(j),builtin:m(r),multiLineStrings:true,hooks:{"$":function(a){a.eatWhile(/[\w\$_]/);return"variable-2"},"<":function(e,b){var c;if(c=e.match(/<<\s*/)){var d=e.eat(/['"]/);e.eatWhile(/[\w\.]/);var a=e.current().slice(c[0].length+(d?2:1));if(d){e.eat(d)}if(a){(b.tokStack||(b.tokStack=[])).push(a,0);b.tokenize=q(a,d!="'");return"string"}}return false},"#":function(a){while(!a.eol()&&!a.match("?>",false)){a.next()}return"comment"},"/":function(a){if(a.eat("/")){while(!a.eol()&&!a.match("?>",false)){a.next()}return"comment"}return false},'"':function(b,a){(a.tokStack||(a.tokStack=[])).push('"',0);a.tokenize=q('"');return"string"},"{":function(b,a){if(a.tokStack&&a.tokStack.length){a.tokStack[a.tokStack.length-1]++}return false},"}":function(b,a){if(a.tokStack&&a.tokStack.length>0&&!--a.tokStack[a.tokStack.length-1]){a.tokenize=q(a.tokStack[a.tokStack.length-2])}return false}}};o.defineMode("php",function(b,a){var e=o.getMode(b,"text/html");var d=o.getMode(b,l);function c(f,h){var i=h.curMode==d;if(f.sol()&&h.pending&&h.pending!='"'&&h.pending!="'"){h.pending=null}if(!i){if(f.match(/^<\?\w*/)){h.curMode=d;if(!h.php){h.php=o.startState(d,e.indent(h.html,""))}h.curState=h.php;return"meta"}if(h.pending=='"'||h.pending=="'"){while(!f.eol()&&f.next()!=h.pending){}var v="string"}else{if(h.pending&&f.pos/.test(g)){h.pending=x[0]}else{h.pending={end:f.pos,style:v}}f.backUp(g.length-w)}return v}else{if(i&&h.php.tokenize==null&&f.match("?>")){h.curMode=e;h.curState=h.html;if(!h.php.context.prev){h.php=null}return"meta"}else{return d.token(f,h.curState)}}}return{startState:function(){var g=o.startState(e);var f=a.startOpen?o.startState(d):null;return{html:g,php:f,curMode:a.startOpen?d:e,curState:a.startOpen?f:g,pending:null}},copyState:function(h){var u=h.html,i=o.copyState(e,u),f=h.php,v=f&&o.copyState(d,f),g;if(h.curMode==e){g=i}else{g=v}return{html:i,php:v,curMode:h.curMode,curState:g,pending:h.pending}},token:c,indent:function(f,g){if((f.curMode!=d&&/^\s*<\//.test(g))||(f.curMode==d&&/^\?>/.test(g))){return e.indent(f.html,g)}return f.curMode.indent(f.curState,g)},blockCommentStart:"/*",blockCommentEnd:"*/",lineComment:"//",innerMode:function(f){return{state:f.curState,mode:f.curMode}}}},"htmlmixed","clike");o.defineMIME("application/x-httpd-php","php");o.defineMIME("application/x-httpd-php-open",{name:"php",startOpen:true});o.defineMIME("text/x-php",l)}); \ No newline at end of file diff --git a/route/static/css/ensite.css b/route/static/css/ensite.css deleted file mode 100755 index 531a660fe..000000000 --- a/route/static/css/ensite.css +++ /dev/null @@ -1,3703 +0,0 @@ -body { - line-height: 1.4; - color: #333; - font-family: "微软雅黑", Arial, Helvetica, sans-serif; - font-size: 12px -} - -input, -textarea, -select { - font-size: 100%; - font-family: inherit -} - -body, -h1, -h2, -h3, -h4, -h5, -h6, -p, -ul, -ol, -form { - margin: 0 -} - -h4, -h5, -h6 { - font-size: 1em -} - -ul, -ol { - padding-left: 0; - list-style-type: none -} - -fieldset, -img { - border: 0 -} - -a { - color: #333; - border: 0; - text-decoration: none -} - -a:hover { - text-decoration: none -} - -a:link { - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - -webkit-touch-callout: none -} - -:-moz-placeholder { - color: #999 -} - -::-moz-placeholder { - color: #999 -} - -input:-ms-input-placeholder { - color: #999 -} - -input::-webkit-input-placeholder { - color: #999 -} - -body, -html { - height: 100% -} - -.f12 { - font-size: 12px -} - -.f14 { - font-size: 14px -} - -.f15 { - font-size: 15px -} - -.f16 { - font-size: 16px -} - -.f18 { - font-size: 18px -} - -.f20 { - font-size: 20px -} - -.cw { - color: white -} - -.c0 { - color: #000 -} - -.c3 { - color: #333 -} - -.c4 { - color: #444 -} - -.c5 { - color: #555 -} - -.c6 { - color: #666 -} - -.c7 { - color: #777 -} - -.c8 { - color: #888 -} - -.c9 { - color: #999 -} - -.cbt { - color: #20a53a -} - -.bgw { - background-color: white -} - -.bge6 { - background-color: #e6e9ee -} - -.plr10 { - padding: 0 10px -} - -.plr15 { - padding: 0 15px -} - -.plr20 { - padding: 0 20px -} - -.ptb10 { - padding: 10px 0 -} - -.ptb15 { - padding: 15px 0 -} - -.ptb20 { - padding: 20px 0 -} - -.pd0 { - padding: 0 -} - -.pd15 { - padding: 15px -} - -.pd20 { - padding: 20px -} - -.pr8 { - padding-right: 8px -} - -.pl7 { - padding-left: 7px -} - -.pb15 { - padding-bottom: 15px -} - -.pb55 { - padding-bottom: 55px -} - -.pb70 { - padding-bottom: 70px -} - -.mt10 { - margin-top: 10px -} - -.mtb10 { - margin: 10px 0 -} - -.mtb15 { - margin: 15px 0 -} - -.mtb20 { - margin: 20px 0 -} - -.mlr15 { - margin: 0 15px -} - -.mlr20 { - margin: 0 20px -} - -.mb15 { - margin-bottom: 15px -} - -.mr50 { - margin-right: 50px -} - -.ml5 { - margin-left: 5px -} - -.mr5 { - margin-right: 5px -} - -.mr20 { - margin-right: 20px -} - -.mg10 { - margin: 10px -} - -.va0 { - vertical-align: 0 -} - -.ico-font-ask { - border: 1px solid #999; - border-radius: 8px; - display: inline-block; - font-family: arial; - font-size: 11px; - font-style: normal; - height: 16px; - line-height: 16px; - margin-left: 5px; - text-align: center; - width: 16px; - cursor: help -} - -.btlink { - color: #20a53a -} - -.btlink:hover { - cursor: pointer -} - -.btn-btlink { - border-color: #20a53a; - color: #20a53a; - vertical-align: 1px -} - -.btn-btlink:hover { - border-color: orange; - color: orange -} - -.b-shadown { - transition: border-color .15s ease-in-out 0s, box-shadow .15s ease-in-out 0s -} - -.b-shadown:hover { - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -.important-title { - background-color: #fbfbfb; - border: 1px solid #eee; - border-radius: 3px; - line-height: 28px; - margin-bottom: 15px; - padding: 5px 10px -} - -.bt-input-text { - border: 1px solid #ccc; - height: 30px; - line-height: 30px; - padding-left: 5px; - border-radius: 3px; - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s -} - -.bt-input-text:focus, -.bt-input-text:active { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -.bt-submit { - background-color: #20a53a; - border-radius: 3px; - width: 140px; - height: 34px; - line-height: 34px; - text-align: center; - color: #fff; - cursor: pointer -} - -.cursor { - cursor: pointer -} - -.help-info-text { - margin-top: 15px -} - -.help-info-text>li { - list-style: inside disc; - line-height: 24px -} - -.relative { - position: relative -} - -.ico-copy { - background: url(../img/ico-copy.png) no-repeat; - height: 14px; - width: 12px; - display: inline-block; - vertical-align: -2px -} - -.zclip embed { - vertical-align: top -} - -.webDelete .options { - padding: 20px 0 -} - -.webDelete .options label { - width: 30%; - float: left; - font-weight: normal -} - -.webDelete .options label input { - float: left; - margin: 0 10px 0 0; - margin-top: 1px -} - -.webDelete .options label span { - float: left; - margin: 0; - line-height: 16px -} - -.webDelete .vcode { - background-color: #f0f0f0; - clear: both; - font-size: 14px; - height: 40px; - line-height: 40px; - margin: 10px 0; - padding-left: 12px; - text-align: left; - color: #444 -} - -.webDelete .vcode .text { - margin-right: 10px; - margin-left: 10px -} - -.webDelete .vcode #vcodeResult { - display: inline; - height: 26px; - line-height: 26px; - margin-left: 10px; - width: 50px; - color: #444 -} - -.btswitch { - display: none -} - -.btswitch+.btswitch-btn { - outline: 0; - display: block; - width: 3em; - height: 1.8em; - position: relative; - cursor: pointer -} - -.btswitch+.btswitch-btn:after, -.btswitch+.btswitch-btn:before { - position: relative; - display: block; - content: ""; - width: 50%; - height: 100% -} - -.btswitch+.btswitch-btn:after { - left: 0 -} - -.btswitch+.btswitch-btn:before { - display: none -} - -.btswitch:checked+.btswitch-btn:after { - left: 50% -} - -.btswitch-ios+.btswitch-btn { - background: #cdcdcd; - border-radius: .9em; - padding: 2px; - -webkit-transition: all .4s ease; - transition: all .4s ease; - border: 1px solid #e8eae9 -} - -.btswitch-ios+.btswitch-btn:after { - border-radius: .9em; - background: #fbfbfb; - -webkit-transition: left .3s cubic-bezier(.175, .885, .32, 1.275), padding .3s ease, margin .3s ease; - transition: left .3s cubic-bezier(.175, .885, .32, 1.275), padding .3s ease, margin .3s ease; - -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, .1), 0 4px 0 rgba(0, 0, 0, .08); - box-shadow: 0 0 0 1px rgba(0, 0, 0, .1), 0 4px 0 rgba(0, 0, 0, .08) -} - -.btswitch-ios+.btswitch-btn:active { - -webkit-box-shadow: inset 0 0 0 2em #e8eae9; - box-shadow: inset 0 0 0 2em #e8eae9 -} - -.btswitch-ios+.btswitch-btn:active:after { - padding-right: .8em -} - -.btswitch-ios:checked+.btswitch-btn { - background: #20a53a -} - -.btswitch-ios:checked+.btswitch-btn:active { - -webkit-box-shadow: none; - box-shadow: none -} - -.btswitch-ios:checked+.btswitch-btn:active:after { - margin-left: -.8em -} - -.bt-warp { - position: relative; - min-height: 100% -} - -.bt-warp>.container-fluid { - padding: 0 -} - -.main-content { - margin-left: 180px -} - -.sidebar-scroll { - background-color: #3c444d; - width: 180px; - z-index: 100; - height: 100%; - position: fixed; - overflow: hidden -} - -.sidebar-auto { - overflow: auto; - height: 100%; - margin-right: -18px -} - -.mypcip { - display: block; - padding: 0 10px; - position: relative; - transition-duration: 500ms; - transition-property: background; - transition-timing-function: ease; - width: 100%; - cursor: pointer; - margin: 1px 0 -} - -.mypcip:hover { - background: #20a53a; - opacity: 1 -} - -.mypcip span { - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAQCAYAAAAS7Y8mAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAGPSURBVDiNldQ9ixRBEAbgZ3Zn1fVOuQU/wK9AMDDS1MvuRxj5CwTBP2CiYmYmGBsYGRsaa2qkkSiCgRd4y3ri3XqzbdDV7Lgs7uwLRc90V1dXv29VVymluxiiRoVkNaoYG7wN+9chpTTBBn7jKA5YhYR+JPQEDxYd6rAPeIwDnOgQuMEIz3F8mUMdC9/wqkPAxb1P0Yv/6ziLX/hTy9c6jXPYXSPwBQywF+Mz7GCCSRGMbqK1Ufwbmb4beI9TuNZFqFWoZCH7Mp0XcaX33y3dUDKfmRdD1bM+BYuoWtaEKeIdyqSvg5+RZSWX6Ri3ZUqmRbxjcqnsx3cyF7Utbpmf4YxcapXcWFNcjiSbOk7Yxhu5BocRaGBeoyXYNII32MTJsJ7cvR/D92aN1xFsFA6HYZ/iiiWjTVyVGyrhBz7jXSQxxq3wPahxJ7JuX30sd9IoshviC77KzdSmag+XYm4QNq0tF+0e7sc1SwPs4xFeLPHvt2ibsfwl24rFl/KLV/jdCho2ZC3a+I6HOB9rR38B10ZjDE49T6kAAAAASUVORK5CYII=") no-repeat 0 center; - display: inline-block; - line-height: 46px; - padding-left: 30px -} - -.btpc-plus { - line-height: 40px; - color: #aaa; - font-family: arial; - font-size: 26px; - cursor: pointer; - padding-left: 80px; - transition-duration: 500ms; - transition-property: background; - transition-timing-function: ease -} - -.btpc-plus:hover { - background-color: #20a53a; - color: #fff -} - -.mypcip .btedit { - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFVSURBVDiNndO/ahRRFMfxz+zOav6goKC1S54gBBUECy0lIKTeyrXwFcROyBuENCHdNhaCoCgiWoggGPAJxFgIsbEJJBmTrCfFnBFZNjHrDy4z5849935/Z84tIsJ/6gneFhFR4AHuYHjM4jN4iZWM13ALSyWWcR+rOERrzAYFNvP9Ka7jKrZExKeIWI8IpxivIuJrRFxo5lqJXfzDbwfvcQU3cAnTUOIsfif6zeYDfuEDzuM5LuIatrGBu/hcInCAy1mH2STawyLmknI+56bykFZDsJsUP7CAdhIMUeELXo9YGmbOn4o3Nah6vd5Ot9vd6ff7lfr3dXKMVZnIVVp4MRgMZtLW6LpneJhxpB1lJnfwE49yblRtfPsrLhoLpbqA7fT15jjUERXNQeO6biK11DXYnyCnymdIjI+4jXuJdlJXHqq7cArf620izkXEu5hMj5u7cAQCwbENi6a1WQAAAABJRU5ErkJggg==) no-repeat center center; - width: 16px; - height: 16px; - display: none; - position: absolute; - left: 156px; - top: 14px -} - -.mypcip:hover .btedit { - display: block -} - -.task { - position: absolute; - right: 6px; - top: 14px; - height: 20px; - width: 20px; - line-height: 20px; - background-color: #fc6d26; - z-index: 99; - text-align: center; - border-radius: 6px; - cursor: pointer; - font-family: arial; - font-size: 14px; - font-weight: bold -} - -.softnum { - position: absolute; - left: 154px; - top: 12px; - height: 20px; - width: 20px; - line-height: 20px; - background-color: #fc6d26; - z-index: 99; - text-align: center; - border-radius: 6px; - cursor: pointer; - font-family: arial; - font-size: 14px; - font-weight: bold; - display: none; - color: #fff -} - -.cmdlist li { - border-bottom: 1px solid #dbdbea; - line-height: 48px -} - -.cmdlist li .titlename { - padding-left: 12px; - position: relative -} - -.cmdlist li .titlename:before { - background-color: #20a53a; - border-radius: 3px; - content: ""; - height: 5px; - left: 0; - position: absolute; - top: 6px; - width: 5px -} - -.cmdlist li .cmd { - height: 200px; - background-color: #424251; - overflow: auto; - line-height: 22px; - color: #fff; - padding-left: 10px; - font-family: arial -} - -#remind td { - vertical-align: middle -} - -#remind .titlename { - position: relative -} - -#remind .titlename:before { - background-color: #20a53a; - border-radius: 3px; - content: ""; - height: 5px; - left: -10px; - position: absolute; - top: 6px; - width: 5px -} - -.btn-default[disabled], -.btn-default:active[disabled] { - background-color: #f7f7f7; - color: #bbb; - opacity: 1 -} - -.table-page { - height: 32px -} - -.table-page a { - border: 1px solid #ccc; - float: left; - height: 30px; - margin-left: -1px; - outline: 0 none; - position: relative; - width: 34px; - z-index: 1; - color: #666; - vertical-align: middle; - text-align: center; - line-height: 30px -} - -.table-page a:hover { - border: 1px solid #20a53a; - color: #20a53a; - z-index: 3 -} - -.table-page a.disable { - background-color: #f3f3f3; - cursor: not-allowed; - color: #bbb -} - -.table-page a.disable:hover { - border: 1px solid #ccc -} - -.table-page-select { - float: left; - position: relative -} - -.table-page .table-page-num { - width: 60px -} - -.page-select-ul { - background-color: #fff; - border: 1px solid #ccc; - bottom: 29px; - left: -1px; - line-height: 22px; - max-height: 150px; - min-width: 100%; - overflow: auto; - position: absolute; - top: auto; - width: 60px; - display: none -} - -.page-selected .page-select-ul { - display: block -} - -.page-select-ul li { - padding: 0 12px -} - -.page-select-ul li:hover { - background-color: #f0f0f0 -} - -.sidebar-auto .menu { - background-color: #353d44 -} - -.menu li { - margin-bottom: 1px; - position: relative -} - -.menu li a { - font-size: 15px; - color: #d6d7d9; - display: block; - line-height: 44px; - padding-left: 52px; - background-repeat: no-repeat; - background-size: 16px auto; - background-position: 25px 14px; - border-left: #404040 2px solid -} - -.menu li.current a, -.menu li a:hover { - background-color: #2c3138; - color: #fff; - border-left: #20a53a 2px solid -} - -.menu .menu_home { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFZSURBVDiNpdO9a1VBEAXw3zOioIUQEdFGkQdWVtr6D1ikERtRppLU6awUsUkh2CgoBNwhoPhsLESxERR7wcI2XSBNMAHRNFmLbB6Xa64RXFjYOXvmzMfOjmqt/mcd2I+QmUuZOcgbDWWQmTN4iFms4wGu4jC+RMT7wQwy8xBe4FtE3MBXPMdqE5sfLCEzj+Il3kXEE4iIJTzFFbzB9z0FMvMYJphExLPuXUQs4y1eYfmPHmTmbHN+FBGv92zMDm+ulXAzItZHtVaZeRH3sBgRn4ecOyKXcRt3dkuYwyJOZeaJfZyP42TjX1Nrne5SyqSUMu5ho559tpQy2bUP9gL8wHYnzbtYzczTuB8RHzs8/H0Sz+ETbuEDxg3f7pL6AhW/2vknNiNiCxvY6uDT8e2XMINLmbmC89js3I0z8wLOdP36Aht43KIcwULD1+w82/WGT+dk8DP96/oNlqecb6uu8YEAAAAASUVORK5CYII=") -} - -.menu .current .menu_home, -.menu .menu_home:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKTQAACk0BtZPkxgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFVSURBVDiNpdO9atVBFATw3/UjBEGidkJSRFFIiKJBsAmaBxDFQptUIoJPYCc2Wmpjo4haRUFMnsEmnXUIKQXFRlEvaiIxY/HfheXCNUUWht2dM+fsYXe2l8Ruxp4d4jN4gDNlvw/70dMQw8ZRXMdFbOAvpjGJ93iHrWEdHMdDfMMcPuMpjiC4hYPDOpjCPazgceGeYS8m8BYj2AZJWpxOspTk9gBfcTPJSpJrlRtMXk5yY0hyxUKSF0lOtQWuJHmV5NIOyRWXk7xOcrVe4iSW8BXj/3mZ+jpfiv5ErThS5udJzjcnHUgyXubKnU3yUrcerR38wSH0sVm4C3iDO+Xm5wu/hX44jI3WB4OePoaPeIIPOm9U3XYVtQV6JfC77PtYxyrW8KPwP9vDWiNFZ5BzOtvO4Fejm8ZJzGqM1BbYLHiE77pPc7fEPuE+FjCGxaLV2+13/gdXJgTGYi2BZQAAAABJRU5ErkJggg==") -} - -.menu .menu_web { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAHuSURBVDiNpZO/S9ZRGMU/5xZl0NJQDf2ybAgrGgylGloiCR0jDAO3+4WQlqA/oiHkpUGvgYtFRENL5g+CCEIiqSjMoFKsaJFqScMXfU9DV3vVluiZDs997odzuM+Vbf6n1v+tmVJqA9okHba9CXgF9McY+1fPqtpBSmkH0Al8B45Iema7DDRlyHagK8b4ZQ2gp6dnn6QB2+2SdgFvJW2xPQeUbdcCM5L6gNYY4/QyIKW0AbgGPAb2AueALuAQMA9MApeAu7YnJZ0ALscYyyE7aZf0JsZ4R1I/MAKMAwFYl+0/AW4URXFb0gTQTh4AOGP7Xta7gbEY43NJw8BA1hPAHoA821wdYUxSCZi13QhslPTI9kmgLGkUOG37m6QXwGbbF2OMTUvPuGD7JzCbM2N7FigD87bnJC3r7HyhOsK0pNEY46CkoRxhRNJDYDjGOGz7fdYPsqNP1YBB2y0Ai4uLH4GjKaUG281AS0qpAThQqVSmsrsWYAj+bOIt4Gpvb+/ZEMJ+4BTwLp8ZaASOhRA6UkofgHrgyupFqpN0H7gA7ATGJW21/SNvY10IYcZ2n+3WoiimVgAAuru7a0MIncBXoF7SU9sLwHHgJbDN9vWiKD4v3VkBACiVSqqpqekAzgMHc/s1vz/TTVbVGsC/1i9dw/hm1FHr2QAAAABJRU5ErkJggg==") -} - -.menu .current .menu_web, -.menu .menu_web:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAGCSURBVDiNpdLPaw9wGAfw13fExWVq1IzYJEkc5kezgws5zM2S+irOWi7KH+EgB0e1m9ZycMGWm0jLRYqUWNNyIDkw2lq9Hb7P5rsfDvKpT717Pp/n/byf53k3kvif0/GX+AXcx3t8wgQurvexsUrBDozgGw7jBRZwHK+wHbeKdA1BLx6iiZ14i078LJLd+IJRnMVMO8Em3MQT7MH5qnQQ8/iAq7hXeBDXsLCxqjfxBuPVRide4xA2lPxnuIPP2Fo5o5JIMp6ku/BAkuHCp5MMFr6S5Gjh7iRjSSwp6MUpzOEYNmMRJ6v/rmqnG7uwpVpdJljEryKYr9hcJc+3DXIJd1TOMsEMnmMW36vSYzQq4Sn21Vqn0INz/DHSBIYKf8QR9ONMxfuxH9P1ZwiT7Qru4gaGsbfm8a7eUnMZwCUtdx7AdVYaqQ8PtCzbo7XGLvyo/vusNNL0agJabhvB16oyVcM6gZfYhts1q9LX2mv7bSS5nGQyyWzdR0ma6/xdo+Cfz28JnsxkWP6vVAAAAABJRU5ErkJggg==") -} - -.menu .menu_ftp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAH6SURBVDiNpdM/qJZlGAbw3/1+n31o/0BJrMFaGmoRRBsM14M4iINLixBxHs6pxSxBmhpCqEk5CL2Pp5ZAdBD/UIJNhdBQzTqJYKkoB8HEY+r53rvB99hnuXlNNzfXfT3X9dzPE5npWTB8WrPWuhvvYwvGOI+vSykn/8uNSQfz8/MvdV33DRaxBlMInMZ9LI7H449mZ2cfLM80y0Xbtiu7rjsZEecz8ws8xBEc7l3swfXBYHB8bm6u+Z9A0zSfYmtmXoyIg/gKV3AJLQ7hV+wcjUYzT0Sota7C2Yg4kJm7sQM/YQOWcAFb+ygn8HHXdVMzMzMPlx1swML09PSPEXEIpzCNc/gBH+D7zPyylHIGt5umeWsywou4B5n5BhZKKTdwFddKKTdxJyLe7PmLWD0pcB2v9PVlvN627WZsxKZa6ya8hj96zsuZuUD/DobD4YWlpaUVtdbP8A62RcRNrO+38SG2I2qtv2AUERcfXyLUWnfhu8zcGBEFv+PVPtp9vI2j+A3vlVKOPSHQi+zHrcz8NiLebZrmr3yE1ePx+OfBYLAfK0opnz9eY9u2WzJzHRJ/Yy3uRsTePnfgSmYexAu4gZX9/NUh9kXElH8xxgjPTfTWR8RmPMBg+XCcGOKTiHh+gtxFxKqu685gXd/7MyJ29g6bCe7teNbv/A8ZE8Q3GMBOFwAAAABJRU5ErkJggg==") -} - -.menu .current .menu_ftp, -.menu .menu_ftp:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAGVSURBVDiNpdLdaw9wFAbwzw+jzVspIxdIuXHhQqbGHVmSRLlx4w9YKbkhpVz4A6SkKOWlldA0uXA1Um645motL7NImsnYNI+LnZ9+W63IqW+dzvc8zznPOaeRxP/YgnnixzCISUzgEQ7/DcEK3MEefEMDbfiKI7iKxfMRtKMfT3EePwtwCdM4gVHcnoVL0nxnk0wl2ZvkYZLuJKeSHE+yO0lfkgOZsd4mrgnuSPI4SU+SW0nGkwwkeZ1kKMmDJGNJric5mGQwSVsrQXeSu+V3JbmRZE2SK0kuJuks4i2Vcz/J1iR/tCzH9/I34hM+YATv8bEGublyJrCqdYijWF3+MDagC9uwvd46vK2clVXEogq8rHWdwQ7sq6rraxu92F9rfYYleNVKMI3LuFlVh/ECQyVtEmPow3McLYzGnFM+jc+4hl0YR0rvk/pvw7kmoJFkJ9ZW4g90mrnCk6W7gTe4gGU13PbCjzSS9KOnpYvp0jjrZEvGFBY2i+NeI8kmLG1J/IUODFRn8A6HqsPW8/8ydwb/bL8B1eb4OuOuSusAAAAASUVORK5CYII=") -} - -.menu .menu_data { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAHISURBVDiNpZPPi81RGMY/z+3mpmasGBOZEgthQ5SFTDM2svMjOyt9X1eUEhshRbGx1e1cO5M/YVIzSWbjx4RidTeKbl3XgoyYW5rHwvlO35TZzFtn8fac5znPc855ZZvVVL3apJR2AGeAw5JGbI9m6Iukvu0ZIEVEp+TINimlOnANGAc6wCvbHUm/AAHrgZ3AHmA7MC3pVlEUS6WDWaALHIuIb/9xOw3QarVGa7XafduzwGQtg+PA+xXIy9VsNnvAHDBRvYMBcDyldEDSvO1nkj7lCNhea3uLpEO29wOjmbMsUAfOAYu2C+CC7XW2hzL+A1iw3QWu5/0vqgIAD4A7EXFxpQgppRPA1bIvBX4Dt4GJlNIV4DPQlTTIERrApryeA3eBh1WBBkBEnM+n7AO22R7O+ALwISJeZvxkySn/wQD4CDyV9Nj2XET0/7G+ATgIHAEmgbGIaJQO1gCngF22A7iRUloCfgLOpwnoA4+AFvC6GmEGOA1ciogpgHa7vRkYygLfi6LoVdzcI79CGUHAZf5+jh4wb/udpK/5EoclbQX2AruBt8DNiFhUdRpTSmPAWeCopI22R7KDfh6mJ8BURLwpOVrtOP8BlJPKP95zNKgAAAAASUVORK5CYII=") -} - -.menu .current .menu_data, -.menu .menu_data:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKdQAACnUBSiXd/QAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF3SURBVDiNpdI5a5VhEAXg58qFRDBi4RIiBkSJoiIoihZi0EpsFWy0FAtbSwvB0uUn2LiUliIWMWgT90i0sRFcYxqFiCYQcizufHK9hUQcGIZh3jnvOYdpJfE/sayn34pLeI4PWKj8jJe4jJHuhVYxaOM8RvEGT6r+RAursQ27sBl3cBGLkkgynuRmklXV/y0Hk9xOMpbkt4RRvMK3JciexkMc6pYwhyl8wlM8wPuSAMuxAQexF4PYif4GYAH7C+g0hrASKwrgO2bxEdfKs0doN7oWkkwmObEED44leVY7f0g4Wbr24Uv9Nl8M+orVECZwH9fR3+56AGer7sEmDFQ/i7d4XP3xZqdhMI93GMfdcnmmx/01OIAjOIxh9DUAwW5sx6lyeRE/atanc1AzuIXXOtfaaoy5l+Rqj1nrk2xJMlLH0z27kmSi28QWzpWJ0zq3MIWvRX8AG4vlDkziAuYagCaGcQZHsQ5rS8JM5Rhu4EWz0Avwz/ELJiL9PSckq44AAAAASUVORK5CYII=") -} - -.menu .menu_set { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAH3SURBVDiNpZI/SJZhFMV/90kicsv+EdEQRUNQYC1S0R+ag2hxMKPgvc/r0Ba2FFEpJARhiF/P9QsaGippimgoG4QgI6egICcJEkoIEyI/9L0NvV98llLQmS6c5x7OPc8Rd+d/0LQcUa1W1xZFcR9ocvfOGOPkXwXMbDvQoqov3b0NuA00i8gBYNLM9gBfVXWiviP1E8xsB3AZmANmgHVAt4jMu/sl4DuwFfgI9KvqO4DQYKAFmFXVU8Bj4JqqfsiybAroBZ6r6jFglbtv+MNB6aIHeKKqL4aGhra4eycQRORelmXvU0pHgPYYoy4SSCltFJGDwEngCjANXHT3WyGEeXfvAq4Dze7eKyJ3i6IYyfN8KgCIyAjQDFxQ1VfAaaA/xjiWZdm4iPQBHao6LiLdwJoQwsiiX3D3z0VR1NNd4e5zDfnUACnnWeATsNB4wjYR2Q8cdfcbIYSau58D+kRkwd273X1QRGaAAWC4KIrRPM8nfg/xqrs/jTGOmtlOoKN08zDGOGZm+0SkPcuys7+W3J3Sxd6U0s1ybk0ptda5SqWyOqW0u+QepJTa6lxjD2rASjO7A5wBBszsEEAI4TzQZWaDwBfg23I92AWsV9VnZnYC2OTur0Vks6oOm9lhYFpV3ywp0IiySI/42bzjMca3S71bVuBf8QODpRL9eTmkdgAAAABJRU5ErkJggg==") -} - -.menu .current .menu_set, -.menu .menu_set:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF0SURBVDiNpdJNiM5RGAXw38ukkJVvMYqZRsqYZmyYDTZWbCxsKLG3pCSaWNmNJU2NmhU7pQjFRhYsJCTWykfJRz5eH8fmefn39lLy1FPn3nPv7Zzz3FYS/1Oz/sItwg3cwuo/HWp1KRjEQtzBTizAfHzCDMbwDk97PTCECXzBWyzGYXzDCXzGGjzHJB6DJJ3ekuRc4R1JNja4VUl2FZ5OsrXDNTO4jRcYx1W8wTEcx1xcwna0cbM7xGXYgxF8LakTuIbLZWVdWevHXixvWniY5ECSkVqfbGBJBpOcKjyc5FCSR90WXjXSnV1hdqqNVuH3eInvTQUDSfYnmUkylmRDkvNJ1icZSjJV+wNJriQ5WKr01avPqtfW3O/hNPaVmrO1N14qp35pa/jclORM4dHqDjevMdYLSTZ3uD6/q405mMYHjOJojewIluJHjfdj51L3Vx7GElzHbqzAXazERWzDazzoZaG7+5PcT/Kkwux5rlvBP9dPgIpDWf6ENxgAAAAASUVORK5CYII=") -} - -.menu .menu_folder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAE3SURBVDiNpZO9SgNBFIW/OySiaBCtbCwE8ReUgGDpE9gExNJmd6O+hM8gmGJn3yKxt7WyEARBUUSbIPhTGFAwxyarSwoZ2AO3mjkf98y9Y5IoI1fKDVQA0jRtOOd2B8BRST5Jkk4IwCSRZVkbOAYeACdpD3iX9DrUpQO+gHaz2XwuRnCSLuM4vpU0AnSAvplNmdlkocbNbN3MTrz3de/9WKVA/07TdN85twbcA5/A8AsLuAO6wI6k2UrhoGZmy3EcH4Zk995vAAd5hA9gBXgJMQ+0ZWanOaAH1IGrEGer1TJgxszOckAFWATOQwDVanUO6EVR9JYD5oGJJEmeAtvfBG7gb4xLwEWgGWA1v59PoQs8Zlm2ANg/RgETwLRz7voXIOnIzBrAdgCgBvgoivowWOUyKv0bfwCvBmEVd9ynHgAAAABJRU5ErkJggg==") -} - -.menu .current .menu_folder, -.menu .menu_folder:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAADuSURBVDiNpdO9SkQxEIbh56ynUHQbQbCxEMR/BEGw9B4s1m6t9W5svQgL3d7Wyk4QLES0EcGfQkGbsTgJuywIwTMQEsh8b+ZLJlVEaBOdVmrUad7HQQJO4hQXJYAqWTjHCR4S5BAfeBursoOflP8CIkJEDCJiLq03ozymcgUD9NHDFu7xjfEbjmR7Oo2FemSjizUcl3jHDo6yv0+s47VQDHsYZMAXtnFTKK4wj8sMqLGCq0LAYjr0PQOWMIOnQsAu7hi+8SquC8WwkfPzKzzjEcsaf39FpEpnccuwE3uadp4oAHQ1rX42Cvh3tP6Nv5Cebn/RRiyLAAAAAElFTkSuQmCC") -} - -.menu .menu_day { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFrSURBVDiNpZMxSFxBEIa/ed49tQsBQdEqgrVgithFW1sr62PhKttXpjy0vWb2WjkQrk6rjWUgjY3ICorYGCFV9C7vxuJ24fE4Y8SBZf/9d3b2n/1ZMTPeE1l10ev1Frz3Wy8lq+pXVf1Y5cTM6Ha7jTzPd4BtYBUYAHO184/ALnAJnJRl+b3dbo8aAM1m8xB4AO6BT2b2C5ivFxCRv8AtsJ5l2SZQYGao6kBVFyNeNjOmDVVdifOSqh6Z2aQF7/0xcGNmF4BMkV9tw0RkDfjgnGs14kYJXInIOTALjGOhukUZ8AQ0gc+JIJI/nHNnIYRWCME5505CCC7i04hbzrkz4Gc8Q1IwEwedTmcvXfcSruZnNZKiKPpFUfT/hZnYX1YVyGu31hRk6X2SgnEi/lPBb6JTSUFuZsM3KPiTWk4FbkXki/d+CORMbK3aKEzsTXhDRK6rBb4B+2bWAkbVN5kSDRG5E5EDiJ/pPfEM08DH4VH64rEAAAAASUVORK5CYII=") -} - -.menu .current .menu_day, -.menu .menu_day:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAEPSURBVDiNpdO9SkNBEIbh58RoFKwEwT8QFOwEQQvt1NbW+7Cx8B7svAZLa1tbG0sbEQQh2KigjcZoxsI9YT2EGMnCMt8us+98s8sWEWGYUausp7HTJ38bU/lGPYt72MUyFjFeOfyOfdziAudol4BjPOMRS3jCRA/AJ5pYwxaORISIOIuImaTnU+w1F1KcjYjTiOi28IVD3KDoYT93EVhJ+hfgDtdooJNA1SeqoYVRbOSAFq5wmR0q+mhYzQEjaeYJ/XQ3v1bZlCrFH7rw03bXwSBVc10rYaWDzgBVc/0ivVTpYAwf/3DwJrVcAprYTJCx1F/15huZXsc9FOk3TuIAc2hXqlVHHQ84wWsx7Hf+BgvadUGnT3fcAAAAAElFTkSuQmCC") -} - -.menu .menu_control { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF5SURBVDiNpdM9ixNRFMbx3yRZ3/ANbCxEcBubpNJSLIQFaxs7D4JfQSz9CpYWItyPYCOIFtqvhQxopSyCb1gpEuNukrHImewQY7UHLsOce85z/vPMvVXTNA4SAyilfMI2Zpn/n2rV6RtGxOYgE9sITLK5Qa8jVGGezwpH8VgWwRTjiJigj6vYwrVcW5nrZ824pW0Jeh0xSVJhr4M86ez32/qBf6OH04nZCmzk1HbI0qN1AhO8zuYfOJ5i8xWKZq1AREzxpZRyAlfwJiI+r5RVrUCLNOvgtnER93FpDeV0laBnYcwMSimHcQpP7Z+V2xZelA7FkuA8opRyK9/P4gIeYq+Ucj2/fwM3Lc7Mua7AVzzH91LKHZxBExE/s/EudiwOz0u8wLeuwDgidvAMf3APr3LvHR7gfUT8joiPw+HwQwqrmqZRSnmSWL9wEpt4a/Hr5qPRaLeu60OdgcfwKCJutCZeTnNmuXZxpDWqruvG/j1o0vDhkuAg8RdE7nuSY6nc+gAAAABJRU5ErkJggg==") -} - -.menu .current .menu_control, -.menu .menu_control:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFaSURBVDiNpdM7a5RREAbg59uLJOgSkRQhfYSIhWWsQgj+gfgLTJEmBJLa0tZW0MbawtbaUkQEQ8Q/YCWIIAqb7G1S7JzNYdlUOfAxlzPznndevmkiwk1OJ+03nGKMBpNr6lsIdLGBrQJwimP05wCatFEBTHAHL2sGY/zHEMt4jBWMKqZ/8RkXVc8MoJX+MF8bJJth3nczV5i0s2cGUJ82eslgkLlbOU57vngRwAA/sqmftp35AhiFzSKAIX6mv4+3C2pa887YlTjlPMAJtjNeMtVC1k5qgA5Wa2Q8xQvsZnyI51jD3cK+jLCOA/xLyn1TET/m3U4+0M+xRpmfAfzGBzzBM/zB17Rf8B57+ISHpsI+AhEhIt6kbSLiKCLOImIzc72IOIyIbsblex0RMwZLuGf6t73Dd/zK/AVepYDLKV4v7zS5jfUyjXCO25Wok/TLnnRwH1vNTdf5EpkVg1v2kuagAAAAAElFTkSuQmCC") -} - -.menu .menu_soft { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF7SURBVDiNrdPPi41hFAfwz72uoVsUWZOFJbKThYkVe1mpx13YjcmU/NwrZCKRLNRZsrSZErvJ1h+glFgiYxC6Pyze8zSvOwsLzuZ5T9/n/T7n+z3ndCaTiX+JHkTELlzHHozQxQrOl1JeRcReLGIbxtiAN7jcS6JrCT5OcIx9mMcAZ/EBz5N8hCO4UQl240kp5XYtLSJmsZDpdjwspTxr4V9xppv5EP0peVuzEnlumcL7GFaCDn5OXfj+l/wHulXCBIci4rXG2BFmMZP4RhyLiBmNR0McxqgSLGBOY1iNIW7l96LGyNMtfBXznf81B/vzhR0pB37hbillOSIO4hw2JdbBJ9yrEu7gIx7504OLWMYFvMcLax6cwv1K0MPLUsrTWlr2eS7TEZZKKUstfCcGtY3jVnk1+i05E+vnZDPGlaBnfZ+/aMZWnqtT+Df0qoR3ON7qc92FlcQ/YxARB6ztwlG8rQRXNdt4Mn/u5AuXEn+AmziRcroaU6/8BgTXdRpxDzi5AAAAAElFTkSuQmCC") -} - -.menu .current .menu_soft, -.menu .menu_soft:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFsSURBVDiNrdO/jw1RFAfwz4xnVzYIydZeY0tEKETChkIoNGrNtkKyhQL/gUg0YgudQiciGpVEIRsdPdlkQ0usH4vNvHcUc27e9bbkm9zMmfnOPfP9nvneJiL8CwZ5PYS72I8xdmANN/EeQ9zGAkZosYHrTSp4gD14m+QIZ/ATl/AI83iRzcc4jC0RISIeR8S5rMu6EhFvsl6NiOUpfjEinrZpYZwKasyhy7rL+xp7MW6rB5tTL/xKO9Dg9xS/WQ9xJ85jJj12OJ2zgMBJvMs9IyxipgzxFK5lo4JvWMFrHMNV7Kv4DivN/8rBkVQwn3JhC/fwCiewjNnkGnzG/aLgJT7h4ZTHBVzEE3w0yUGHyxgWBQOs4lml7rvet2z4PFfBASzVOZj1N+YqO2F7DnapcjCwPQdfTXLQ6v9KjR8YFAsfcMEkByXrG8l/wRKOmpyVs1gvQzyoP23D3NzkF27oc3Acd7A77bT6od76A2AskgeNVoIQAAAAAElFTkSuQmCC") -} - -.menu .menu_firewall { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAG0SURBVDiNhdNPiI5RFAbw352ZsGBDMhkpYyE12cxOyYIFayW7SY2zkCxIlkrZKIrJjPe+X1FksrQQo/xp2IhZiPXUxIoaGxa+mGsx78dn+vDUvXVu53l6znO6qZSig6mpqbX9/f37cRB7sDOl1C6lzOIZHkXEK11IpRR1Xe8upRzFKNqYxzt8xGpsxy6swwCeYjIi5gcaoTt4i8BcRBQ90Gq11i8tLY3gCjZibABKKUu4FxGvexE7GB8fX8RsVVVPUkqDGjtQGqv/RM75CJ6nlH6gT+dCwvf/kE9hGlvwrZfAqrqu+3LOQz3Ix3EJpyPipeUwdQt8RbuUshkLjdUOeQzXcD4iLnfptvmdwXtsjYgPOefrmM45f0Y/buJiRJzrIm+wvOJfDl5gBCLiBFp4iPuYiIizK6YaSim96Ra4jeGc86ZG5BhyQz65Io9hDJZSZkApRSlFVVUTVVXd7dR/O1VVPaiq6mqn7jiQUjqD0Zxzd1B/IOc8iW1N7zKv+zM1I8zgE8YjYqF534EbTegHImKxp0DTnHABh3ALa3AY0ys20Vugg7qu95ZSHuML9kXEXK++ny1tzgEddf2OAAAAAElFTkSuQmCC") -} - -.menu .current .menu_firewall, -.menu .menu_firewall:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKdQAACnUBSiXd/QAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFrSURBVDiNhdMvaJZRGAXw36viFrRoENxw4IJYLDMJalkxC8OkrC5qNAwEi+CC4lAsikGMY0FQmGIQ/AMKyjDIUGYyqMXgmB7Dd9/P63j37cAN73OfczjPc+7bJFFhFyZxCsdxGGt4hqd4hFc1oSkCxzCNiUJYwXt8xRDGcQS7sQNPMI8VSST5lGQxydEkTal1nT1JTiR5k+RuEtuKkz9YwGv8N9MGfCvjLBUnfYEUq1vhDEbwu+W2Ag3WtyCfx32M4leXwM7yPdJBnsFVXMALvWWqBX7qbX8/PherLc7hBi5hrqqvURaBVRzAF9wsVr9jO+7gCmYr8l69iLXxXEyyUMV1O/9wrSPOpSRnk/QFxpK8S7Kvarq1CflgkuW2t764nuTBgEfUnoe1cH0xnORjkrkB5PkkH5IMdQkott4meVzGauuHkjxP8rI85z6n/ZlqNLiM07iHYUyVZGY3Ng+a9WSS9SQ/kkxs1vcXeVqZSyUF+yoAAAAASUVORK5CYII=") -} - -.menu .menu_exit { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAEYSURBVDiNpZOtTkNBEEbPFCQIUHgEBoJAYFDgERgSCBjSvU9Q0/AkX5+ApB6PI6lAYPhJUCgEgkApkHyI/nBpb28DPWozu3MyuzsTtpmG2f5CUs32BlABouBsAK8RUU8pPY4IImIfOLd9k4/n+IyII2AZGBUAM8BZlmXX48qVtAksFl6hx/y45B5N4C4fqExI+EVK6QJYkLT9L0GPJ+BU0hYUP9YAScfACtDh52c6wAPQlHRQKoiIN+B5WGC7DXzYfi8VVKvV5nCs0WgsRcSh7b0sy1qlgiJszwG1lFIL/viIknaAdkrpsh8bruBlgmOXbq8UduIXcCLpvkDc31+j20wDBgdty/Y6sMroMJnuda+A2/xGTDvO32OQXrvPg7l3AAAAAElFTkSuQmCC") -} - -.menu .current .menu_exit, -.menu .menu_exit:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKdQAACnUBSiXd/QAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAADvSURBVDiNpdO7LkRRFAbg7wwlBZVeoSEKEtHSayUSGs+g8RwqbyDR6ycaiYRC45KoVAqFuAySpdhzODn2mWMyf7KSvdflz7oWEWEUjFfee1hCB0XGt8AL9vGQI9jECa5r+hKf2MZsE8EYjnA1IOMVTDeVAJMDguEYt1VFpyWgji6msNaUwX/wiAN84LSNYAdz6PmdTA/3UjlbbQSveMoQvPUzeBcRpVxExGrl3yQzEdGNiOWIGLqJMCEt3TnDT2G9n/5Zqaj34LmFYEPalewmfmEXdxni0r4gdf8HVcdDLGLe32MKqdxL3FQNxajn/A0ZS19hUhhlTwAAAABJRU5ErkJggg==") -} - -html .menu .menu_home:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPPz8+np6d3d3c/Pz8vLy8XFxb+/v729vbu7u7e3t7W1tbGxsa+vr62traurq6mpqaenp6WlpaOjo6GhoZ2dnZubmwrPOpmZmQzLPJeXl5WVlRLFPhLDPhy5RI+Pjx63RImJiYeHhyypSiirSIODgzKfToGBgTabUDibUDiZUH5+fj6TUnx8fECRVEKPVHp6ekSNVkCPVESLVnZ2dnR0dEyDWnBwcFR4XlZ2Xlh2XlpyYGZmZgAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJHgA7ACwAAAEAEAAOAAAGhsCdcLgzJUjE5HCmECxMFYZHuWMRHDuJINIg2JIqAmQYOS6SpEEkORFciOkJlVIwCTUEDfVOyOweFyAzVDMgFw1DBSFDOkMkBUQ2B0g1HyIdNUUGX0OTOy0jNyMrRQecOzYIKjsyKDspMDsvpkQGFDklJzsoJTgVBkkIAAEYLjsuGAEACEJBACH5BAkeADsALAAAAQAQAA4AAAZxwJ1wuDMlSMTkcKYQLEwVhke5YxEcO4kg0iDYkioCZBg5LpKkQSQ5EVyI6QmVUjAJNQQN9U7I7B4XIDNUMyAXDUMFIVQkBUQ2B0hKJgZfQ5FUJgeWOzYIKlQvm0QGFEM6QxUGSQgAARYcIhsWAQAIQkEAIfkECR4AOwAsAAABABAADgAABnfAnXC4MyVIxORwphAsTBWGR7ljERw7iSDSINiSKgJkGDkukqRBJDkRXIjpCZVSMAk1BA31TsjsHhcgM1QzIBcNQwUhVCQFRDYHSEomBl9DkVQmB5Y7NggqQzpDL5tEBhQ5NR8iHTU4FQZJCAABGC47LhgBAAhCQQAh+QQJHgA7ACwAAAEAEAAOAAAGfcCdcLgzJUjE5HCmECxMFYZHuWMRHDuJINIg2JIqAmQYOS6SpEEkORFciOkJlVIwCTUEDfVOyOweFyAzVDMgFw1DBSFUJAVENgdISiYGX0ORQzpDJgeWOzYIKjs1HyIdNTsvnUQGFDklJzsoJTgVBkkIAAEYLjsuGAEACEJBACH5BAUeADsALAAAAQAQAA4AAAaCwJ1wuDMlSMTkcKYQLEwVhke5YxEcO4kg0iDYkioCZBg5LpKkQSQ5EVyI6QmVUjAJNQQN9U7I7B4XIDNUMyAXDUMFIVQkBUQ2B0hCOkMmBl9DkTs1HyIdNUUHmDs2CCo7Mig7KTA7L6JEBhQ5JSc7KCU4FQZJCAABGC47LhgBAAhCQQAh+QQFHgA7ACwGAAkABQADAAAGDsDdTiesfUSdWmt0G62CACH5BAkyADsALAAAAQAQAA4AAAYVwJ1wSCwaj8ikcslsOp/QqHRKdQYBADs=") -} - -html .menu .menu_web:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAPHx8e/v7+vr6+np6efn5+Xl5eHh4d/f393d3dXV1dHR0c3NzcvLy8nJycfHx7+/v729vbu7u7m5ube3t6+vr62traurq6mpqaenp6WlpaGhoZ+fn52dnZubm5mZmZeXlxTDPpOTkyqrSiqpSoeHhy6nSiynSoWFhS6lTDCjTDKhTjSfTjadTjadUDSdTjabUDyXUjqXUjyVUj6TUkCRVHp6ekSNVkaLVkSLVnh4eEqHWHZ2dkiHWEqFWE6BWlB+XFJ8XFJ6XFR6XFZ4XlZ2XlpyYFxwYmZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFDgBHACwAAAEADwAOAAAHbYBHgoIdCgIACRyDizsTFQ0XGA4WEjuLNQMnH4shJwU1gw8di4sdD4IaGKSkGBpHC5argzsMRwYbsoMbB0cHHrmCHghHDDnARzm1GhnHGa5HEL+yHxCDmJqcJASg1hEUDRgZkxLGqxoJAQGJi4EAIfkEBQ4ARwAsBAADAAgAAgAABxGAR0dDPkBDgi0gNCgqNCAsgQAh+QQFDgBHACwEAAUACAACAAAHEYBFKUAogyNGPjZHPT5HMD+BACH5BAUOAEcALAMABwAKAAIAAAcWgDcoJjclKDciKTc6Kig9KCo6Jis6gQAh+QQFDgBHACwEAAkACAABAAAHCoA+Nkc6PkcwP4EAIfkEBQ4ARwAsBAAKAAgAAQAABwqARShAJSlAI0aBACH5BAUOAEcALAQACwAIAAIAAAcRgCwgMyUpMiAvR0dDPkJDioEAIfkECTIARwAsAAABAA8ADgAABxaAR4KDhIWGh4iJiouMjY6PkJGSk5SBADs=") -} - -html .menu .menu_ftp:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAP////39/fv7+/f39+3t7evr6+Pj49/f39nZ2dfX19XV1dPT09HR0c/Pz83NzcvLy8nJycfHx8XFxcPDw8HBwb+/v729vbu7u7W1tbOzs7Gxsa+vr62trampqaenp6OjowDZNp+fnwLXNp2dnQTVOATTOJubmwrPOpmZmQrNOgzLPJeXl5WVlRi/QBa/QJGRkY+PjyC1RI2NjSSxRomJiYeHh4WFhYODg4GBgX5+fnx8fECRVESNVkSLVkaLVkiHWHR0dE6BWnJyclB+XFJ8XFR6XlR4Xlp0YFh0YGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJMgBJACwAAAEAEAAOAAAHvYBJgoIhCAICCiuDi4IOEQsBAQ4QE4yCCR06DRUWD0AYDYwaATALNBweMA8sAB6LCC8QAwwEBQwDESwIgzQOSTURQBMVQBA5SQw3grFJKBdJGRtJFCxJDzKCOApJNg01DA42DDZJCziDCRsMAhQGBxIBDBsJiygCORYjqB8XNgAmjDZ8SCLjxg0YSTRkGBSkh48fO3i0UKGiRY8dP3z4GBKDhMcSIkCIBCGihEcSM5AUWVlkyAmRKYiwLHIkEAAh+QQFDgBJACwAAAEADwALAAAHj4BJgoIhCAICCiuDi0kOEQsBAQ4QE4xJCR06DRUWD0AYDYsaATALNBweMA8sAB6DCC8QAwwEBQwDESwIgjQOSTURQBMVQBA5SQw3SbFJKBdJGRtJFCxJDzJJOApJNg01DA42DDZJCziCCRsMAhQGBxIBDBsJgygCORYjqB8XNgAmizZ8SCLjxg0YSTRkEBQIACH5BAUOAEkALAAADAACAAMAAAcIgEE9MSRIRYEAIfkEBQ4ASQAsAgAMAAEAAwAABwWAPiRFgQAh+QQJDgBJACwAAAEADwAOAAAHIIBJgoOEhYaHiImKi4yNjo+QkY8/OzyMJCUijEVFQ4mBACH5BAkOAEkALAAAAQAPAA4AAAepgEmCgiEIAgIKK4OLSQ4RCwEBDhATjEkJHToNFRYPQBgNixoBMAs0HB4wDywAHoMILxADDAQFDAMRLAiCNA5JNRFAExVAEDlJDDdJsUkoF0kZG0kULEkPMkk4Ckk2DTUMDjYMNkkLOIIJGwwCFAYHEgEMGwmDKAI5FiOoHxc2ACaLNnxIIuPGDRhJNGQQFKSHjx87eFgaFIOExRIiQGgEwQhJkY9FJg4KBAAh+QQFDgBJACwAAAEADwAOAAAHsYBJgoIhCAICCiuDi0kOEQsBAQ4QE4xJCR06DRUWD0AYDYsaATALNBweMA8sAB6DCC8QAwwEBQwDESwIgjQOSTURQBMVQBA5SQw3SbFJKBdJGRtJFCxJDzJJOApJNg01DA42DDZJCziCCRsMAhQGBxIBDBsJgygCORYjqB8XNgAmizZ8SCLjxg0YSTRkEBSkh48fO3gs6rHjh6AYJDKWEAGiIwgRJUgIQlKkpKUkJpMEAgAh+QQFDgBJACwNAAwAAwADAAAHC4A+PkMkJDNFRUeBACH5BAUOAEkALAQADAAGAAMAAAcOgElJLSoqLYKISUVDiYEAIfkECTIASQAsAAABABAADgAABx2ASYKDhIWGh4iJiouMjY6PkJGSk5SEJyAgKUSFgQA7") -} - -html .menu .menu_data:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAP////39/fv7+/Hx8e/v7+np6ePj4+Hh4d/f393d3dfX19XV1dPT08XFxcPDw8HBwb+/v7u7u7m5ube3t62traenp6OjowDZNgLXNp2dnZubmwrPOpmZmQrNOpeXlxLFPhTDQBTDPhbBQBTBQBa/QBq9Qhi9Qh63RBy5QiC1RCC1RiKzRiSxRouLi4mJiYeHhyirSCynSoWFhS6lTIODgzKhTjSfTjadTjKfToGBgTibUDiZUHx8fD6TUnh4eHZ2dkiHWHR0dEyDWnJyclB+XFZ4XlZ2Xlh0YFxwYGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFMgBJACwAAAEADwAOAAAHr4BJgkUzIB0XFxsfM0WCgjUlMD1CRERAOiwiNoIlLEiOoElIKSVJFzehoTsXSRggJzZCR0hIR0I2JyIYSQEyOTErKCQkKCoxNDIBvAYaqYIcB8oCHBAHDA4QEA4MBg8cAkkAHIIyGhYWGi/PAEkCBQ4cQaBBHg4F4AA0GQsGBgkJB6xloMFOQQRHP3jwkCcoAgJBFBg0qNAiRw4XGSIskADKxwQDAwIEIGAgAg1BgQAAIfkECQ8ASQAsAAABAA8ADgAABxaASYKDhIWGh4iJiouMjY6PkJGSk5SBACH5BAkPAEkALAAAAAAPAA8AAAe0gEmCRTMgHRcXGx8zRYKCNSUwPUJEREA6LCI2giUsSI6gSUgpJUkXN6GhOxdJGCAnNkJHSEhHQjYnIhigMSsoJCQoKjGgATI5qYI0MgFJAQYayUkcB80CHBAHDA4QEA4MBg8cAkkAHIIyGhYWGi+CHABJAgUOHEGgQR4OBeQANBkLDBhIkOCAtgw04imI4OgHDx73BEVAIIgCgwYVWuTI4SJDhAUSQPmYYGBAgAAEDESgISgQACH5BAkPAEkALAAAAQAPAA4AAAevgEmCRTMgHRcXGx8zRYKCNSUwPUJEREA6LCI2giUsSI6gSUgpJUkXN6GhOxdJGCAnNkJHSEhHQjYnIhhJATI5MSsoJCQoKjE0MgG8BhqpghwHygIcEAcMDhAQDgwGDxwCSQAcgjIaFhYaL88ASQIFDhxBoEEeDgXgADQZCwYGCQkHrGWgwU5BBEc/ePCQJygCAkEUGDSo0CJHDhcZIiyQAMrHBAMDAgQgYCACDUGBAAAh+QQJDwBJACwAAAAADwAPAAAHtIBJgkUzIB0XFxsfM0WCgjUlMD1CRERAOiwiNoIlLEiOoElIKSVJFzehoTsXSRggJzZCR0hIR0I2JyIYoDErKCQkKCoxoAEyOamCNDIBSQEGGslJHAfNAhwQBwwOEBAODAYPHAJJAByCMhoWFhovghwASQIFDhxBoEEeDgXkADQZCwwYSJDggLYMNOIpiODoBw8e9wRFQCCIAoMGFVrkyOEiQ4QFEkD5mGBgQIAABAxEoCEoEAA7") -} - -html .menu .menu_control:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAPcAAOTk5N/f38bUxsnQycTTxMfOx8XLxcPKw8bKxsfHx8TIxMbGxsXGxcXFxcfFx8PGw8TExMXExcbCxsfCx8PDw8PEw8PCw8PBw8HBwcW/xcHAwb7AvsK/whzVHBzTHCDOICLLIjC2MDO0MzOzMzKzMjWxNTOyMzWvNTavNjirOE2hTTqpOjunOzylPEmhSUqgSjqlOoaGhj+iPz6iPoSEhD6hPj6fPj+fP4ODg0CfQDqhOoKCgkGcQUObQ4CAgEWXRUaWRkWWRUqQSkiSSEiRSEyNTFuHW3p6eluGW3l5eU6KTliFWE6JTk2JTVaFVlWFVWB/YFGGUXV1dVGEUVWAVVSAVHRzdHNzc3FxcVV9VXZvdlp4Wm9vb2hyaGF0YVt2W1x1XFx0XGFyYWNwY15yXl1zXWtra15xXmNtY2FuYWpqamBvYG1obWJtYmlpaWJsYmhpaGNqY2hoaGJrYmRqZGRpZGdnZ2VpZWVoZWRoZGZmZmVnZWdlZ2VlZWdkZ2djZ2hjaGxdbG9ab2ZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCACDACwAAAEADwAOAAAIhgAHAUgAYcGCBggbGISQIMCgBlwG9dFjp6IdPX0GYaEwCMKgQWqO4NixA8cRNR89LvjoJglJkkncpBy0EqSPGDRoxPCBsiPNj3KwSLlyRQoWOTNrflzK1GeDplAHcWQQtWmFQQOqMi0wCIHWpQoGWfj6cSzEQXC63IG6UWCCBhA2KDS4sGFAACH5BAUIAIMALAIACQACAAMAAAgJAMUMcsHjSZSAACH5BAUIAIMALAMACgADAAQAAAgQAGGUGbSCxaBBJ4IM8gIlIAAh+QQFCACDACwEAAoAAwAEAAAIEAAHhTkzKAeVFiCoDDIyKCAAIfkEBQgAgwAsBQAIAAMABAAACBAABzFJM6jElj0pwAwaMiggACH5BAUIAIMALAYABwADAAUAAAgSAAfpGFSlQ5FBQEIMGpRiYZiAACH5BAUIAIMALAgACQADAAQAAAgQAAcN2jJIiIwpHogMujEoIAAh+QQFCACDACwJAAcABQAFAAAIGQAHCRw4kI2gHiK+iBlEwoQNFYOaBMqyJCAAIfkECTIAgwAsAAABAA8ADgAACBkABwkcSLCgwYMIEypcyLChw4cQI0qcSDEgADs=") -} - -html .menu .menu_firewall:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP////39/fv7+/f39+3t7evr6+np6efn5+Xl5ePj4+Hh4d/f393d3dnZ2dfX19XV1dHR0cvLy8nJycfHx8PDw8HBwb+/v729vbu7u7m5ube3t7W1tbOzs7Gxsa+vr6enp6WlpaOjo5+fnwDZNpeXl42NjYuLi4mJiSitSCqpSiypSoeHhyirSC6lTIWFhTKhTjiZUDyXUnx8fECRVEKPVHp6enh4eEqHWEiHWHR0dFB+XHBwcFpyYGZmZgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFAwA9ACwAAAAADwAQAAAGdMCesEeiHAACR2c17J0oiUXEE/pgHAsFptYjQFzN4c6UmPQKoXAYIzl/1M1M5AyCDzdzwtveu8wRdXwXbQ8afD0SGD0dEIcNIj01CDl2NQeUPRZzcA8WTQWKYRUGYTkJDlw9MgwLO3AaBh0bBhyHJQEDYE1BACH5BAUEAD0ALAMABgACAAMAAAYHwB6PN+qlggAh+QQFBAA9ACwFAAcAAQADAAAGBUDaCBUEACH5BAUIAD0ALAYACAACAAMAAAYHwBtuNGKpggAh+QQFCAA9ACwIAAcAAQADAAAGBcDZSBUEACH5BAUIAD0ALAkABgABAAMAAAYFwNioFQQAIfkEBQgAPQAsCgAFAAEAAwAABgVA2OgVBAAh+QQFBgA9ACwLAAQAAgADAAAGB0Aab6SD9YIAIfkECTIAPQAsAAAAAA8AEAAABhXAnnBILBqPyKRyyWw6n9CodEqtJoMAOw==") -} - -html .menu .menu_day:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAANPT09HR0c3NzcvLy8nJycfHx8XFxcPDw8HBwb+/v729vbu7u7m5ubW1tbOzs7Gxsa2trampqaenp6WlpaOjo6GhoQDZNpubm5WVlY+Pj42NjYuLi4mJiYeHh4WFhYODg35+fnZ2dnR0dHJycnBwcGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgAlACwAAAEADwAOAAAGesCSUKQRGkua0RGjAFQm0EkFoMAIFY9HAHPpXjABiINREohKoaMwHSKUBgvKJEKvTygLw1vCyWj+gBkcEnoGHCUWFmpCHAglBYeLRhsHjxuSRhqOBJeYSI4FnZgeegMdniUgBSULER8dHrCxG7QRDUIPCQq7vLsJDyVBACH5BAUKACUALAcABwACAAEAAAYEQIslCAAh+QQFCgAlACwKAAcAAgABAAAGBECLJQgAIfkEBQoAJQAsBAAJAAIAAQAABgRAiyUIACH5BAUKACUALAcACQACAAEAAAYEQIslCAAh+QQFCgAlACwKAAkAAgABAAAGBECLJQgAIfkEBQoAJQAsBAALAAIAAQAABgRAiyUIACH5BAUKACUALAcACwACAAEAAAYEQIslCAAh+QQFCgAlACwKAAsAAgABAAAGBECLJQgAIfkECTIAJQAsAAABAA8ADgAABhTAknBILBqPyKRyyWw6n9CodEoNAgA7") -} - -html .menu .menu_soft:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAOPj4+Hh4d/f393d3dvb29nZ2dfX19XV1dPT09HR0c/Pz83NzcvLy8nJycfHx8XFxcPDw7+/v729vbu7u6+vr62traurq6mpqaenp6WlpZmZmZeXl5WVlRTDQBTDPpOTkxLDPhbBQBi/QBTBQBq9Qha/QJGRkRi9Qhi9QI+Pjxq7Qhy5RB63RBy5QiC1RiC1RI2NjSKzRiSxRouLiyKxRiavSCitSCSvSImJiSatSCypSiirSCynSoWFhYODg4GBgTadUDadTjabUDSdTjibUDiZUDyXUn5+fjqXUjyVUnx8fHh4eESLVkaLVkaJWEaJVkqHWEiHWEqFWE6DWkyDWk6BWlB+WlB8XFJ8XFJ6XFR6XlR4Xlh2Xlp0YGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFHgBeACwAAAEAEAAPAAAHtoBeXTEdIR0qWF4/BgECAQtKXjIrQkBAMjVeEAwVFBUHCl4iRV6lUy5eCyalXhgAXidArE6oCxqsFQGiRKxStR+sGLonLk5MTTwsXgoSHBscCwJeWDUu1jFUXjMLCt0OOKzh4uI+DwndDDNeOAzdCQ89XgUKzhwTCcsR9dFeAxasKUItAFZKmBcCFFhxEHirVK5+F1jBwKeKVYZXDA50qtDAgZcHnDwdwKdEQaMABcD1KHAywZFAACH5BAUeAF4ALAAAAQAQAAcAAAdhgF5LDAECAQY+XloqHSEdMVxeDQcVFBUND147MkBAQisuXgMXXqUwCV4uUKVeRh1eBBSsHApeMUysRK8EFqwptTGrpa6wChwbHBMIqTxNTE4xIV49DwrWCzBeUzEu3TVWgQAh+QQFHgBeACwJAAEABwAPAAAHWIA/BgECAQtKEAwVFBUHCgsmXpIYAAsakl4Vhh+YGAEKEhwbHAsCMwsKqQ44mK1WMS6xNlguOk1MTjEhMVCYRh0xTJhEHS69kkkdNjJAQEIrLlckHSHFWoEAIfkECR4AXgAsAAABABAADwAAB3CAXoKDhIWGh4iJiouMjY6PiVg2LpQxVV44DAoKCQ89XiQuTkxNPCxeChEcGxwLAl4iRINSLl4LH4MYAV4nQINOtQsagxW7IkWDU8EmgxkAXjErQkBAMjVeDwwVFBUHCV5cLh0hHSRWXj0FAQIBCUeBADs=") -} - -html .menu .menu_set:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAMfHx8XFxbm5ube3t7W1tbOzs7GxsQbROGqpeg7JPBDHPpWVlRLFPmylemCnchTDQBbBQFqnbBTBQFSpaBi/QEqrYpGRkRq9QlinbBa/QI+RkRi9QBi9Qh65RI+Pjxq7QkqpYhy5RIuPjRy5Qh63RCC1RIePiSKzRiSxRn6Rg4uLiyKxRiavSCitSCSvSImJiSqrSiatSIeHhyypSiirSCqpSiynSoWFhS6lTHyJfjCjTHyHfjKhTDKhToODgzSfTmaNcIGBgTadTjKfTjabUDSdTjqZUDiZUH5+fjyXUjqXUDqXUj6VUjyVUj6TVHx8fD6TUkCRVEKPVHp6ekKNVnJ8dECPVESNVkaLVkSLVnh4eEiJWEaJVkaJWEiHWEqHWHZ2dkyFWkqFWE6DWkyDWk6BWlB+XFB+WlB8XFJ8XFR6XlR6XFJ6XGR0aFR4XlZ4Xlp0YFh2XlZ2XlxyYFh0YFpyYFxwYlxwYF5uYmZmZgAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJDwB5ACwAAAEADwAOAAAHpoB5goN4Hxdwg4l5b2Z5W1JMUnlpb4lvJywzKHBzMDYkMJWCZS15Fj6CWgt5LGSJOi95WgYFSHkeNINzVCQyUwEyNwFINyFSc3kKTml5BKh5TwR5akYKyVuCA7Z5UwN5cFIMeXFRKDdBAEhIATdPIU6ieTwqeUEDAjJ5LzOJZvw3NwahOlFmkBoYmSi46gGjITODYfJgUVLmSh4yahQNgsMgQcZEgQAAIfkECQ8AeQAsAAABAA8ADgAAB7CAeYJ5c15pgjo2eIODeEIXLExJQh1mjIM/aXhGUXlRW4xzZVcsGj6CWggwMEeLJxdMGHlaBgVIeSJfJGR5NUp5MlMBMjcBSDdwLId5QlJ5BKd5TwR5PE6DUUx5A7d5UwPMRIJpNnk3QQBISAE3T3MwOHlbJWMmeUEDAjJ5E1IbM3nsCNHRocKNQTtKYBmzKM8iL+LGJJmzJd6lPGQeMHESpUWJIxcdtgCYx0yYhnkCAQAh+QQJDwB5ACwAAAAADwAPAAAHqoB5goN5c4SHhEIoUYiDb3lvNk4wW2UoSYQlEEYhZXlqLFg2KIQtWGkNPoJVC2SMg2FOL3laBgVIeQ4zhFI2MlMBMjcBSDdOJFh5RD+CBKp5TwSCNUlwM06CA7h5UwN5Ui1feU41RjdBAEhIATdAEHCEFBF5QQMCMnkpRPCDRmo3OQS1AZEGgo5BaQ4YoQIjTZ4eLZpQ8DRISY85ZWjAqCEITyNBTpKMORQIACH5BAkPAHkALAAAAQAPAA4AAAemgHmCg3gfF3CDiXlvZnlbUkxSeWlviW8nLDMocHMwNiQwlYJlLXkWPoJaC3ksZIk6L3laBgVIeR40g3NUJDJTATI3AUg3IVJzeQpOaXkEqHlPBHlqRgrJW4IDtnlTA3lwUgx5cVEoN0EASEgBN08hTqJ5PCp5QQMCMnkvM4lm/Dc3BqE6UWaQGhiZKLjqAaMhM4Nh8mBRUuZKHjJqFA2CwyBBxkSBAAAh+QQJDwB5ACwAAAEADwAOAAAHsIB5gnlzXmmCOjZ4g4N4QhcsTElCHWaMgz9peEZReVFbjHNlVywaPoJaCDAwR4snF0wYeVoGBUh5Il8kZHk1SnkyUwEyNwFIN3Ash3lCUnkEp3lPBHk8ToNRTHkDt3lTA8xEgmk2eTdBAEhIATdPczA4eVslYyZ5QQMCMnkTUhszeewI0dGhwo1BO0pgGbMozyIv4sYkmbMl3qU8ZB4wcRKlRYkjFx22AJjHTJiGeQIBACH5BAkPAHkALAAAAAAPAA8AAAeqgHmCg3lzhIeEQihRiINveW82TjBbZShJhCUQRiFleWosWDYohC1YaQ0+glULZIyDYU4veVoGBUh5DjOEUjYyUwEyNwFIN04kWHlEP4IEqnlPBII1SXAzToIDuHlTA3lSLV95TjVGN0EASEgBN0AQcIQUEXlBAwIyeSlE8INGajc5BLUBkQaCjkFpDhihAiNNnh4tmlDwNEhJjzllaMCoIQhPI0FOkow5FAgAIfkECQ8AeQAsAAABAA8ADgAAB6aAeYKDeB8XcIOJeW9meVtSTFJ5aW+JbycsMyhwczA2JDCVgmUteRY+gloLeSxkiToveVoGBUh5HjSDc1QkMlMBMjcBSDchUnN5Ck5peQSoeU8EeWpGCslbggO2eVMDeXBSDHlxUSg3QQBISAE3TyFOonk8KnlBAwIyeS8ziWb8NzcGoTpRZpAaGJkouOoBoyEzg2HyYFFS5koeMmoUDYLDIEHGRIEAACH5BAkPAHkALAAAAQAPAA4AAAewgHmCeXNeaYI6NniDg3hCFyxMSUIdZoyDP2l4RlF5UVuMc2VXLBo+gloIMDBHiycXTBh5WgYFSHkiXyRkeTVKeTJTATI3AUg3cCyHeUJSeQSneU8EeTxOg1FMeQO3eVMDzESCaTZ5N0EASEgBN09zMDh5WyVjJnlBAwIyeRNSGzN57AjR0aHCjUE7SmAZsyjPIi/ixiSZsyXepTxkHjBxEqVFiSMXHbYAmMdMmIZ5AgEAOw==") -} - -html .menu .menu_folder:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPX19efn593d3dXV1dPT09HR0c/Pz83NzcvLy8nJycfHx8XFxcHBwb29vbm5ube3t7W1tbOzs7Gxsa+vr62traurq6mpqaenp6WlpaOjo6GhoQDZNp+fn52dnZubm5mZmZeXl5WVlZOTk4+Pj4WFhX5+fnx8fHp6enh4eHZ2dnJycnBwcGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFDgAsACwAAAIAEAAMAAAGYEAWCHEohFjIpJJVGJlKD0oGQ8VoVEkCdslFajEMiuVCJlskjwTz1OgmSQsWQhRxIysg1mLysbMcKywKCih2J3UsAgZ+Hh1IABJ+ECVIARwlmJmZJAxJHkQHoaKhA3ksQQAh+QQJDgAsACwAAAAAEAAOAAAGGUCWULgpGo/DpHLJbDqf0Kh0Sq1ar9isMAgAIfkECQ4ALAAsAAABABAADQAABmZAllC4KRqPQhDiUAgNn8/CyFR6UDKYLEajGhK60LDwi2FQLJd02iJ5JFiFU0M8JC1YCFGELqyAWAsTH3wsDissCgoofCd7LAIGhB4dQgAShBAlQgEcJZ6fnyQMQx5LB6eopwN/LEEAIfkECQ4ALAAsAAACABAADAAABmNAFghxKGyOyCSLVRiZSg9KBkPFaFRLFgGb7Xq1KgyDYrmYzRbJI8E8Nb5d0oKFEEXg2QqItZh88EsOKywKCiiAJ3csAgaALB4dSwASjhAlSwEcJZucnCQMWR5EB6SlpAN7LEEAIfkECQ4ALAAsAAACABAADAAABlxAFghxKIRYyKSSVRiZSpuodIokqJbYpBXDoFguYLBF8kgwT41skrRgIUQRNbICYi0mHznLsWIpFChyJ3EsAgZ6Hh1IABJ6ECVIARwllJWVJAxJHkQHnZ6dA3UsQQAh+QQFDgAsACwAAAIAEAAMAAAGZUAWCHEohFjIpJJVGJlKD0oGQ8VoVEkClrXper9ILYZBsVzOZ4vkkWCeGsslacFCiCJxZQXEWkw+eUkOKywKCiiBLCd4LAIGiSweHUgAEpAQJUgBHCWdnp4kDEkeRAemp6YDfCxBACH5BAkOACwALAAAAgAQAAwAAAYUQJZwSCwaj8ikcslsOp/QqHQqDAIAIfkECQ4ALAAsAAACABAADAAABlxAFghxKIRYyKSSVRiZSpuodIokqJbYpBXDoFguYLBF8kgwT41skrRgIUQRNbICYi0mHznLsWIpFChyJ3EsAgZ6Hh1IABJ6ECVIARwllJWVJAxJHkQHnZ6dA3UsQQAh+QQJDgAsACwAAAIAEAAMAAAGY0AWCHEobI7IJItVGJlKD0oGQ8VoVEsWAZvterUqDINiuZjNFskjwTw1vl3SgoUQReDZCoi1mHzwSw4rLAoKKIAndywCBoAsHh1LABKOECVLARwlm5ycJAxZHkQHpKWkA3ssQQAh+QQJDgAsACwAAAEAEAANAAAGZkCWULgpGo9CEOJQCA2fz8LIVHpQMpgsRqMaErrQsPCLYVAsl3TaInkkWIVTQzwkLVgIUYQurIBYCxMffCwOKywKCih8J3ssAgaEHh1CABKEECVCARwlnp+fJAxDHksHp6inA38sQQAh+QQJDgAsACwAAAAAEAAOAAAGZkCWULgpGo/DpHIJQhwKoaWyMDKVHpQMZovRqIaEr3QZxjAolotabZE8EqzCqTEWkhYshChSZ1VALAsTH30OKywKCih1J3wsAgZ9Hh1CABJ9ECVCARwlnp+fJAxDHk4Hp6inA4AsQQA7") -} - -html .menu .menu_exit:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAOPj4+Hh4dnZ2dXV1c3NzcvLy8fHx8PDw8HBwb+/v729vbu7u7W1ta2trampqaenp6Ojo6GhoZ2dnQzNOgjPOpeXlxq9Qhi9QI+Pj4uLi4mJiSatSCqpSjCjTDSfToGBgTadTjKfTjibUDqZUDqXUDyVUkCRVESNVnZ2dkyFWnR0dE6BWlB+XFB8XFR4Xlh2XlxwYF5uYmZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCAAyACwCAAEACwAOAAAGQMBGgUAsEgyoAEMSaTYlgwzgI6taZYkKQHO1ZlDb7jUsrpLLZ3G6ux5zxRgwVayoBBQPh17/EGQgCQiCgwcLKkEAIfkEBQgAMgAsBwAHAAEAAgAABgRA0ykIACH5BAUIADIALAgABwABAAIAAAYEwI4oCAAh+QQFCAAyACwJAAcAAgACAAAGBkCPhzQKAgAh+QQFCAAyACwLAAcAAQACAAAGBEDPKAgAIfkEBQgAMgAsDAAHAAIAAgAABgZA0KbECQIAIfkEBQgAMgAsDAAFAAIAAgAABgZAVypmCQIAIfkEBQgAMgAsDgAGAAIAAwAABgfAlYySmqyCACH5BAUIADIALAwACQADAAEAAAYFQNiFFQQAIfkEBQgAMgAsDAAKAAIAAQAABgTAFysIACH5BAkyADIALAIAAQAOAA4AAAYUQJlwSCwaj8ikcslsOp/QqHRqDAIAOw==") -} - -.pos-box { - height: 42px; - width: 100% -} - -.position { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFZSURBVDiNpdO9a1VBEAXw3zOioIUQEdFGkQdWVtr6D1ikERtRppLU6awUsUkh2CgoBNwhoPhsLESxERR7wcI2XSBNMAHRNFmLbB6Xa64RXFjYOXvmzMfOjmqt/mcd2I+QmUuZOcgbDWWQmTN4iFms4wGu4jC+RMT7wQwy8xBe4FtE3MBXPMdqE5sfLCEzj+Il3kXEE4iIJTzFFbzB9z0FMvMYJphExLPuXUQs4y1eYfmPHmTmbHN+FBGv92zMDm+ulXAzItZHtVaZeRH3sBgRn4ecOyKXcRt3dkuYwyJOZeaJfZyP42TjX1Nrne5SyqSUMu5ho559tpQy2bUP9gL8wHYnzbtYzczTuB8RHzs8/H0Sz+ETbuEDxg3f7pL6AhW/2vknNiNiCxvY6uDT8e2XMINLmbmC89js3I0z8wLOdP36Aht43KIcwULD1+w82/WGT+dk8DP96/oNlqecb6uu8YEAAAAASUVORK5CYII="); - background-position: 10px 10px; - background-repeat: no-repeat; - background-size: 22px auto; - line-height: 42px; - padding-left: 30px -} - -.search { - width: 306px -} - -.ser-text { - border: #20a53a 1px solid; - height: 30px; - width: 262px; - padding: 0 8px; - margin-top: 6px -} - -.ser-sub { - width: 38px; - height: 30px; - border: 0; - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAxAAAAMQBz4pYTAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAE2SURBVDiNndJBS1VRFMXx9UzNQUQD0caFk6YhEUFCOPETiJQfIygQbNQgCCeOGjYLGjRJkPoKoSGIRKADs1nQoEIEfw3cry6v+3oPN2z2ufuu8z/7nruC9ORNvMRXHOIFbrTogjQfOniKX87iR2P9HSv/A1zA+xK/wRwmMYV5vKt3r/sBnpTgcb9R8aw0D3sB1/GzTum3uZsfSnupCVgr8vwQgKXSLnR7I0nuJJFkN4PjY9WZbmMkyXGtx4YAjFY9aQI2knSS3B4CMFd1508HV3CE3QHfP17G2sdo7298UJfzChMtmy9jszTLbT4InpfgCx6Vme5hFd/8jW1cbQME93Hg39jDYmOKT5hGOmi7rNkk15KcJvmcZKv6E0k2k9xNsp1keZBx2vIi3tYkG+cBxJmV13HrN6Szg7ZCfX3NAAAAAElFTkSuQmCC") no-repeat center #20a53a; - cursor: pointer; - margin-right: 2px; - margin-top: 6px -} - -.title { - height: 50px; - line-height: 50px; - border-bottom: 1px solid #ddd -} - -.title h3 { - display: inline-block; - line-height: 50px; - margin-right: 12px -} - -.divtable .table { - border: 1px solid #ddd; - color: #666; - font-size: 12px; - margin-bottom: 0 -} - -.divtable .table thead th { - background-color: #f5f6fa; - border-bottom: 1px solid #e1e6eb; - color: #999; - font-weight: normal; - padding: 8px -} - -.table .btlinkbed { - color: #666; - padding: 1px 5px; - border: 1px solid transparent; - white-space: nowrap; - overflow: hidden; - float: left; - display: block -} - -.table .btlinkbed:hover { - border: #ddd 1px solid -} - -.dataTables_paginate { - display: block; - height: 30px; - margin-bottom: 10px; - margin-top: 15px -} - -.page { - line-height: 16px; - text-align: right; - margin-top: 5px; - margin-right: 1px; - height: 30px -} - -.page div { - float: right -} - -.page span, -.page a { - display: inline-block; - height: 28px; - padding: 0 11px; - border-right: #ddd solid 1px; - border-top: #ddd solid 1px; - border-bottom: #ddd solid 1px; - border-left: #ddd solid 1px; - line-height: 28px; - font-family: "Arial"; - font-size: 13px; - color: #20a53a; - float: left; - margin-left: -1px -} - -.page a.prev { - border-left: #ddd solid 1px -} - -.page a:hover { - background: #e8e8e8; - color: #222; - text-shadow: 0 1px 0 #fff; - cursor: pointer -} - -.page spanold { - font-size: 12px; - padding: 4px 10px !important; - border-style: solid; - border-width: 1px; - border-color: #ddd #ddd #ccc; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - display: inline-block; - line-height: 16px; - background: #f5f5f5; - color: #333; - text-shadow: 0 1px 0 #fff -} - -.page .Pcurrent { - color: #999; - background: #f5f5f5; - cursor: default -} - -.page .Pcount { - color: #777 -} - -.system-info-con { - width: 100% -} - -.sys-i-c-box { - box-sizing: border-box; - max-width: 350px -} - -.sys-i-c-box .siteinfo-box { - height: 110px; - position: relative; - width: 100% -} - -.sys-i-c-box .siteinfo-box h3 { - background: #20a53a; - color: #fff; - font-style: normal; - height: 110px; - left: 0; - line-height: 110px; - position: absolute; - text-align: center; - top: 0; - width: 56px -} - -.siteinfo-box-right { - border-bottom: 1px solid #ddd; - border-right: 1px solid #ddd; - border-top: 1px solid #ddd; - height: 110px; - margin-left: 56px; - overflow: hidden -} - -.siteinfo-box-right h4 { - font-size: 32px; - height: 68px; - line-height: 60px; - overflow: hidden; - padding-top: 8px; - text-align: center -} - -.siteinfo-box-right .sbr-btn { - color: #ccc; - display: block; - font-size: 14px; - height: 27px; - line-height: 22px; - padding-top: 5px; - text-align: center -} - -.siteinfo-box-right .sbr-btn a { - color: #20a53a; - font-size: 16px; - padding: 0 10px -} - -.circle-box h3, -.circle-box h4 { - line-height: 30px -} - -.circle { - width: 100px; - height: 100px; - position: relative; - border-radius: 50%; - background: #20a53a; - margin: 0 auto -} - -.pie_left, -.pie_right { - width: 100px; - height: 100px; - position: absolute; - top: 0; - left: 0 -} - -.left, -.right { - width: 100px; - height: 100px; - background: #ccc; - border-radius: 50%; - position: absolute; - top: 0; - left: 0 -} - -.pie_right, -.right { - clip: rect(0, auto, auto, 50px); - transition: transform 1s ease-in 0s; - -webkit-transition: -webkit-transform 1s ease-in 0s; - -moz-transition: -moz-transform 1s ease-in 0s -} - -.pie_left, -.left { - clip: rect(0, 50px, auto, 0); - transition: transform .4s ease-in 1s; - -webkit-transition: -webkit-transform .4s ease-in 1s; - -moz-transition: -moz-transform .4s ease-in 1s -} - -.mask { - width: 88px; - height: 88px; - border-radius: 50%; - left: 6px; - top: 6px; - background: #FFF; - position: absolute; - line-height: 88px; - font-size: 18px; - color: #20a53a -} - -@-webkit-keyframes shineGreen { - from { - -webkit-box-shadow: 0 0 10px #999 - } - - 50% { - -webkit-box-shadow: 0 0 15px #20a53a - } - - to { - -webkit-box-shadow: 0 0 10px #999 - } -} - -.shine_green { - -webkit-animation-name: shineGreen; - -webkit-animation-duration: 3s; - -webkit-animation-iteration-count: infinite -} - -.mem-release { - cursor: pointer -} - -.mem-re-min { - background: url(../img/ico/rocket_min.png) no-repeat center center; - height: 35px; - left: 40px; - position: absolute; - top: 8px; - width: 20px; - z-index: 9; - opacity: .9 -} - -.mem-re-con { - background: url(../img/ico/ico-rocket.gif) no-repeat center center; - width: 100%; - height: 100%; - position: absolute; - z-index: 9; - display: none; - top: 15px -} - -.mem-release:hover .mem-re-con { - display: block -} - -.soft-man .col-lg-3 { - border-bottom: 1px solid #ececfb; - border-left: 1px solid #ececfb; - border-right: 1px solid #ececfb; - margin-right: -1px; - margin-bottom: -1px; - height: 148px; - cursor: pointer; - padding: 0 -} - -.soft-man .col-lg-3:hover { - box-shadow: 0 0 38px rgba(0, 0, 0, .08) inset; - -webkit-transition: all .25s ease; - transition: all .25s ease -} - -.soft-man .dashed-border { - border: 1px dashed #20a53a; - z-index: 1 -} - -.soft-man .no-bg:hover { - box-shadow: 0 0 38px rgba(0, 0, 0, .01) inset; - -webkit-transition: all .15s ease; - transition: all .15s ease -} - -.soft-man .image { - height: 40px; - margin: 30px 0 20px; - text-align: center -} - -.soft-man .sname { - text-align: center; - color: #555 -} - -.soft-man .col-sm-3>div { - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 0 -} - -.soft-man .spanmove { - background: url(../images/move.png) no-repeat; - width: 30px; - height: 30px; - display: none; - position: absolute; - top: 5px; - right: 5px; - z-index: 3 -} - -.soft-man .col-lg-3:hover .spanmove { - display: block -} - -.soft-man .text { - background-color: rgba(0, 0, 0, 0.3); - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; - display: none -} - -.soft-man .text a { - background-color: #fff; - border-radius: 4px; - display: block; - height: 30px; - line-height: 30px; - margin: 59px auto; - text-align: center; - width: 100px -} - -.soft-man-con p.status { - line-height: 40px; - margin-bottom: 20px; - font-size: 14px -} - -.soft-man-con .sfm-opt .btn { - margin-right: 15px -} - -.soft-man .sname .glyphicon-True { - color: #20a53a; - margin-left: 10px -} - -.soft-man .sname .glyphicon-False { - color: red; - margin-left: 10px -} - -.soft-man { - position: relative -} - -.soft-man .rowbg { - position: absolute; - top: 0; - width: 100%; - z-index: 0 -} - -.soft-man .rowbg .col-lg-3 { - cursor: default -} - -.soft-man .rowbg .col-lg-3:hover { - box-shadow: 0 0 38px rgba(0, 0, 0, .01) inset; - -webkit-transition: all .15s ease; - transition: all .15s ease -} - -.bw-info ul li { - height: 56px; - line-height: 56px -} - -.bw-info ul li.bi-line { - border-bottom: #ddd solid 1px -} - -.bw-info ul li span { - display: block; - margin-right: 15px -} - -.btn-zhm { - border-radius: 0 3px 0 0; - display: none; - position: absolute; - right: 42px; - top: 0 -} - -.line { - padding: 5px 0 -} - -.line .span_tit { - display: inline-block; - text-align: right; - width: 140px -} - -.line .tname { - display: block; - float: left; - height: 30px; - line-height: 30px; - overflow: hidden; - padding-right: 20px; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; - width: 110px -} - -.line .info-r { - margin-bottom: 5px; - margin-left: 110px; - position: relative -} - -.placeholder { - cursor: text; - left: 20px; - line-height: 24px; - position: absolute; - top: 27px -} - -.bt-form-submit-btn { - background: #f6f8f8; - border-top: 1px solid #edf1f2; - bottom: 0; - left: 0; - padding: 8px 20px 10px; - position: absolute; - text-align: right; - width: 100% -} - -.bt-form-submit-btn .btn:first-child { - margin-right: 4px -} - -.btn-danger { - background-color: #cbcbcb; - border-color: #cbcbcb; - color: #fff -} - -.bt-w-main { - height: 525px -} - -.bt-form { - height: 100% -} - -.bt-w-menu { - float: left; - background-color: #f0f0f1; - height: 100%; - width: 110px -} - -.bt-w-menu p { - cursor: pointer; - height: 40px; - line-height: 40px; - padding-left: 20px; - position: relative; - text-overflow: ellipsis; - overflow: hidden -} - -.bt-w-menu p a { - display: block -} - -.bt-w-menu p .spanmove { - display: none -} - -.bt-w-con { - margin-left: 110px; - position: relative -} - -.label-input-group input { - margin-top: 0; - margin-right: 5px; - vertical-align: -2px -} - -.label-input-group label { - margin-bottom: 0; - font-weight: normal -} - -.bingfa .line { - margin-bottom: 10px -} - -.bingfa .line .span_tit { - text-align: right; - width: 120px; - display: inline-block -} - -.bingfa .bt-input-text { - width: 100px -} - -.ssl-con-key { - width: 47% -} - -.ssl-con-key textarea { - height: 240px; - line-height: 18px; - width: 100%; - margin-top: 5px -} - -.ssh-item { - float: left; - margin-left: 10px; - padding-top: 5px -} - -.ss-text em { - color: #555; - float: left; - font-style: normal; - line-height: 32px; - padding-right: 2px -} - -.view1, -.view2 { - margin-bottom: 15px -} - -.searcTime { - position: relative:z-index:999; - margin-top: 11px -} - -.searcTime .tit { - float: left; - padding: 5px 10px; - display: none -} - -.searcTime .gt { - padding: 5px 10px; - border: #ddd 1px solid; - margin-right: -1px; - cursor: pointer; - float: left; - line-height: 16px -} - -.searcTime .gt.on, -.searcTime .gt:hover, -.searcTime .ss .st:hover, -.searcTime .ss .st.on { - background-color: #20a53a; - color: #fff -} - -.searcTime .ss { - display: inline-block; - position: relative; - float: left; - line-height: 16px -} - -.searcTime .ss .st { - padding: 5px 10px; - border: #ddd 1px solid; - margin-right: -1px; - cursor: pointer; - float: left -} - -.searcTime .ss .time { - background-color: #fff; - border: 1px solid #ddd; - display: none; - padding: 10px; - position: absolute; - right: -1px; - top: 27px; - width: 217px; - z-index: 99 -} - -.searcTime .ss .time span { - margin-bottom: 10px; - display: block -} - -.searcTime .ss .time input { - border: 1px solid #ddd; - height: 22px; - padding: 0 5px; - width: 132px -} - -.searcTime .ss .time input:focus { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -.searcTime .ss .sbtn { - background-color: #20a53a; - border: 1px solid #20a53a; - color: #fff; - line-height: 24px; - text-align: center; - cursor: pointer -} - -.plan { - padding: 10px 20px; - margin-bottom: 10px -} - -.plan .typename { - line-height: 34px; - width: 120px -} - -.plan .textname { - height: 34px; - line-height: 34px; - float: left -} - -.plan .planname input { - height: 34px; - width: 260px; - padding: 0 12px -} - -.plan .dropdown button b { - font-weight: normal -} - -.plan .dropdown-menu { - min-width: 100px -} - -.dropdown-menu>li>a { - padding: 3px 12px -} - -.plan-submit { - margin-left: 141px -} - -.plan_hms { - position: relative; - height: 34px -} - -.plan_hms span { - float: left; - height: 32px; - line-height: 32px; - position: relative -} - -.plan_hms .name { - border-left: 1px solid #ccc; - width: 44px; - text-align: center; - background-color: #f6f6f6 -} - -.planSign { - margin-left: 10px; - height: 35px; - line-height: 35px -} - -.planSign i { - margin-right: 8px; - font-size: 12px; - font-style: initial; - color: red -} - -.plan_hms span input { - float: left; - width: 48px; - height: 32px; - line-height: 32px; - border: 0; - text-align: center; - font-size: 12px -} - -.plan_hms span:first-child input { - border-radius: 4px 0 0 4px -} - -.plan_hms span:last-child { - border-radius: 0 4px 4px 0 -} - -.plan .dropdown button { - width: 94px -} - -.txtsjs { - width: 260px; - height: 80px -} - -.sl-s-info { - width: 124px; - border: #ccc 1px solid -} - -.rec-install { - padding: 16px; - width: 100%; - float: left -} - -.rec-install .rec-box { - width: 296px; - float: left -} - -.rec-install h3 { - font-size: 20px; - margin-bottom: 5px -} - -.rec-box-con { - border: #ddd 1px solid; - border-radius: 3px; - padding: 10px -} - -.rec-install .rec-box:hover .rec-box-con { - background-color: #f9f9f9; - border: #20a53a 1px solid -} - -.rec-box-con .ico img { - width: 22px; - margin-right: 10px -} - -.rec-box-con ul li { - line-height: 26px; - margin-bottom: 5px -} - -.fangshi { - line-height: 19px; - margin-top: 10px; - display: block; - margin-bottom: 15px -} - -.fangshi label { - font-weight: normal; - margin-right: 44px; - float: right -} - -.fangshi label input { - vertical-align: -2px; - margin-left: 5px -} - -.zun-form-new .fangshi { - padding: 0 30px 0; - line-height: 30px -} - -.zun-form-new .fangshi label { - height: 20px; - line-height: 20px; - margin: 4px 20px 0 0 -} - -.zun-form-new .version { - padding: 0 30px -} - -.zun-form-new .version select { - height: 28px; - margin-left: 30px; - width: 120px -} - -.sec-install-btn { - font-family: "宋体"; - width: 90px; - height: 30px; - line-height: 30px; - text-align: center; - background-color: #20a53a; - border-radius: 3px; - color: #fff; - cursor: pointer; - margin-top: 10px -} - -.rec-box-con .onekey { - font-family: "宋体"; - width: 90px; - height: 30px; - line-height: 30px; - text-align: center; - background-color: #20a53a; - border-radius: 3px; - color: #fff; - margin: 5px auto; - cursor: pointer -} - -.rec-box-con .onekey:hover, -.sec-install-btn:hover { - background: #10952a; - background: linear-gradient(#10952a, #088d22) -} - -.phppz { - margin: 14px 0 -} - -.phppz span { - padding: 10px -} - -.phppz textarea { - width: 500px; - margin: 0; - vertical-align: bottom; - padding: 0 5px; - height: 38px; - margin-right: 20px -} - -.soft-man-con .user_pw { - display: none; - margin-top: 30px -} - -.soft-man-con .user_pw_tit { - margin-top: 30px; - padding-top: 30px; - border-top: #ccc 1px dashed -} - -.soft-man-con .user_pw p { - margin-bottom: 15px -} - -.soft-man-con .user_pw_tit .tit { - float: left; - line-height: 22px -} - -.soft-man-con .user_pw span, -.soft-man-con .user_pw_tit span { - width: 50px; - display: inline-block -} - -.soft-man-con .user_pw span { - margin-right: 10px -} - -.btswitch-p { - margin-left: 10px -} - -.soft-man-con .user_pw input { - width: 200px -} - -.soft-man-con .conf_p { - margin-bottom: 10px -} - -.soft-man-con .conf_p input { - width: 100px -} - -.soft-man-con .funarea { - width: 100%; - height: 80px; - line-height: 22px; - margin-bottom: 10px -} - -.soft-man-con .ver .btn { - vertical-align: 0; - margin-left: 10px -} - -.webEdit-box .soft-man-con .phpmyadmindk { - width: 100px; - display: inline-block; - margin: 0; - padding-right: 0 -} - -.softlist td img { - width: 24px; - height: 20px; - margin-right: 5px -} - -.softlist .glyphicon-folder-open { - cursor: pointer; - color: #efd566 -} - -.softlist .btswitch+.btswitch-btn { - width: 2.4em; - height: 1.4em; - margin-bottom: 0 -} - -.wafConf_checkbox label { - font-weight: normal; - margin-right: 22px -} - -.wafConf button { - vertical-align: 0 -} - -.wafConf_cc { - border-bottom: 1px solid #ddd; - border-top: 1px solid #ddd; - margin: 10px 0; - padding: 15px 0 -} - -.wafConf_cc span { - margin-right: 5px -} - -.wafConf fieldset { - border: 1px solid #ccc; - border-radius: 3px; - float: left; - padding-bottom: 0; - width: 240px -} - -.wafConf fieldset:nth-of-type(2) { - margin: 0 10px -} - -.wafConf legend { - border: 0 none; - font-size: 14px; - margin: 0 6px; - padding: 3px; - width: auto -} - -.wafConf fieldset input { - margin-left: 4px -} - -.wafConf fieldset .table { - margin-top: -1px; - margin-bottom: 0 -} - -.wafConf fieldset .table tr td:nth-of-type(2) { - width: 42px -} - -.wafConf fieldset .table-overflow { - height: 210px; - overflow: auto; - margin-top: 10px; - border-top: #ddd 1px solid -} - -.wafConf-btn { - border-bottom: #ddd 1px solid; - margin-bottom: 12px; - padding-bottom: 15px; - height: 45px -} - -.wafConf-btn span { - float: left; - margin-right: 8px; - line-height: 33px -} - -.wafConf-btn .btn { - margin-right: 10px -} - -.wafConf-btn .ssh-item { - margin-right: 50px -} - -.gzEdit { - padding: 13px 20px -} - -.gzEdit button { - margin: 8px; - width: 140px -} - -.setting-con p { - line-height: 30px -} - -.setting-con p .set-tit { - display: inline-block; - height: 22px; - margin-right: 10px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - vertical-align: -2px; - width: 90px -} - -.setting-con p .disable { - background-color: #ededed -} - -.setting-con p .inputtxt { - width: 260px -} - -.setting-con p .modify { - margin-left: -40px; - vertical-align: 0; - position: relative; - z-index: 10 -} - -.setting-con p .set-info { - margin-left: 20px -} - -.set-submit { - margin: 20px 0 10px 100px -} - -.changepath { - height: 500px -} - -.changepath .path-top { - height: 50px; - line-height: 50px; - padding-left: 10px; - border-bottom: #aaa 1px solid -} - -.changepath .path-top .btn { - margin-right: 10px -} - -.changepath .path-top .btn span { - -moz-transform: scaleX(-1); - -webkit-transform: scaleX(-1); - -o-transform: scaleX(-1); - transform: scaleX(-1); -} - -.changepath .path-top .place { - display: inline-block -} - -.changepath .path-top .place span { - color: #444; - font-size: 12px; - font-weight: bold -} - -.changepath .path-con-left { - width: 130px; - height: 450px; - float: left; - border-right: #aaa 1px solid; - padding-top: 5px -} - -.changepath .path-con-left dl dt { - background: url("../img/ico-computer.png") no-repeat left center; - height: 30px; - line-height: 30px; - padding-left: 23px; - font-size: 14px; - font-weight: normal; - margin-left: 10px -} - -.changepath .path-con-left dl dd { - line-height: 30px; - padding-left: 12px; - cursor: pointer -} - -.changepath .path-con-left dl dd span { - color: #666 -} - -.changepath .path-con-right { - float: left; - height: 450px; - overflow: auto; - width: 520px -} - -.changepath .path-con-right .default li { - width: 25%; - float: left; - text-align: center; - margin-top: 20px; - cursor: pointer -} - -.changepath .path-con-right .default li span { - font-size: 40px; - color: #666 -} - -.path-con-right .file-list { - display: none -} - -.path-con-right .list-list td { - padding-left: 10px; - cursor: pointer -} - -.list-list span.glyphicon { - color: #666; - margin-right: 10px; - font-size: 18px -} - -.list-list span.glyphicon-folder-open { - color: #edca5c -} - -.list-list span.glyphicon-cloud-download { - font-size: 12px -} - -.newFolderName { - border: #ccc 1px solid; - height: 30px; - padding: 0 5px; - width: 90% -} - -.path-con-right .list-list td .delfile-btn { - display: none; - float: right; - font-size: 14px; - text-align: center; - width: 18px -} - -.path-con-right .list-list td .delfile-btn:hover { - color: red -} - -.path-con-right .list-list tr:hover .delfile-btn { - display: block -} - -.path-con-right .list-list td .btn-xs { - margin-top: 4px -} - -.getfile-btn { - background: #f6f8f8 none repeat scroll 0 0; - border-top: 1px solid #edf1f2; - padding: 8px 20px 10px; - text-align: right; - width: 100% -} - -.success-msg { - width: 100%; - padding: 30px 50px; - font-family: "Microsoft Yahei"; - float: left; - position: relative -} - -.success-msg .pic { - position: absolute; - top: 50%; - margin-top: -100px -} - -.success-msg .pic img { - width: 158px -} - -.success-msg .suc-con { - float: right; - width: 260px -} - -.success-msg .suc-con h3 { - font-size: 16px; - margin-bottom: 15px -} - -.success-msg .suc-con p { - line-height: 23px -} - -.success-msg .suc-con .p1 { - border-bottom: 1px dashed #aaa; - color: #000; - height: 30px; - margin-bottom: 8px -} - -.success-msg .bottom-btn { - margin-top: 60px; - width: 100%; - float: left -} - -.success-msg .bottom-btn a { - width: 200px; - height: 40px; - line-height: 40px; - text-align: center; - display: inline-block; - border-radius: 5px; - margin-left: 30px; - color: #fff; - font-size: 16px; - cursor: pointer -} - -.success-msg .bottom-btn .close-btn { - background-color: #cbcbcb -} - -.success-msg .bottom-btn .blue-btn { - background-color: #5cb85c -} - -.tasklist { - padding: 15px -} - -.tab-nav { - border-bottom: #cacad9 1px solid -} - -.tab-nav span { - background-color: #ddd; - background: -moz-linear-gradient(top, #f6f6f6, #ddd); - background: -webkit-gradient(linear, 0% 0, 0% 100%, from(#f6f6f6), to(#ddd)); - background: -ms-linear-gradient(top, #f6f6f6, #ddd); - height: 32px; - line-height: 32px; - padding: 0 12px; - border: #cacad9 1px solid; - color: #444; - display: inline-block; - margin: 0 -1px -1px 0; - cursor: pointer -} - -.tab-nav .on { - background: #fff; - border-bottom: #fff 1px solid; - color: #444 -} - -.tab-con { - height: 380px; - margin-right: -15px; - overflow: auto; - padding: 10px -} - -.tab-con ul.cmdlist { - list-style-type: decimal -} - -.tab-con ul.cmdlist li { - position: relative; - list-style-type: decimal; - list-style-position: inside; - line-height: 40px; - border-bottom: #dbdbea 1px solid; - margin-top: 6px -} - -.tab-con ul.cmdlist li .com-progress, -.tab-con ul.cmdlist li .state, -.opencmd { - float: right; - margin-left: 20px; - color: #535362 -} - -.tab-con ul.cmdlist li .line-progress { - position: absolute; - bottom: -1px; - left: 0; - height: 1px; - background-color: #20a53a -} - -.tab-con ul.cmdlist li .cmd { - border: 0 none; - border-radius: 0; - display: block; - width: 570px; - height: 200px; - line-height: 22px; - padding: 0 10px; - background-color: #333; - color: #eee; - overflow: auto -} - -.yuandian { - width: 10px; - height: 10px; - border-radius: 5px; - background-color: #20a53a; - z-index: 999999 -} - -.DrawRecordL i, -.DrawRecordR-T i { - background: url(../img/Detailsbg.png) no-repeat -} - -.DrawRecordCon { - font-size: 14px; - height: 422px; - overflow: auto -} - -.DrawRecord { - background: url(../img/DrawRecordord.png) repeat-y -204px center; - margin: 0 auto; - overflow: hidden; - line-height: 22px -} - -.DrawRecordlist { - padding-top: 6px -} - -.DrawRecord .DrawRecordL { - float: left; - color: #9a9a9a; - margin-top: 9px; - padding-right: 22px; - position: relative; - text-align: right; - width: 110px -} - -.DrawRecord .DrawRecordL i { - background-position: 0 0; - display: inline-block; - height: 5px; - overflow: hidden; - position: absolute; - right: 0; - top: 9px; - width: 16px -} - -.DrawRecord .DrawRecordR { - margin: 1px 1px 30px 112px; - color: #666; - padding-right: 5px -} - -.DrawRecord .DrawRecordR h3 { - font-size: 14px; - font-weight: normal; - margin-bottom: 5px -} - -.footer { - bottom: 0; - position: absolute; - text-align: center; - width: 100%; - line-height: 50px -} - -.bingfa { - padding: 10px 0 60px 0 -} - -.bingfa p { - margin-bottom: 10px -} - -.bingfa p .span_tit { - display: inline-block; - text-align: right; - width: 120px -} - -.bingfa p input { - border: 1px solid #ccc; - height: 30px; - line-height: 30px; - padding-left: 5px; - border-radius: 3px; - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s -} - -.bingfa p input:focus, -.bingfa p input:active { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -.bingfa .submit-btn { - background: #f6f8f8 none repeat scroll 0 0; - border-top: 1px solid #edf1f2; - bottom: 0; - left: 0; - padding: 8px 20px 10px; - position: absolute; - text-align: right; - width: 100% -} - -.bingfa .submit-btn .btn-danger { - margin-right: 5px -} - -.transfer .backupdata { - height: 150px -} - -.selectdata .slabel { - font-weight: normal; - margin: 10px 41px 10px 0 -} - -.selectdata .slabel input { - margin-right: 5px; - margin-top: 0; - vertical-align: -2px -} - -.backupbtn { - margin: 20px 0; - width: 100% -} - -.neice_con { - padding: 7px 20px 20px -} - -.neice_con .tit { - font-weight: bold; - line-height: 30px; - color: #777 -} - -.neice_con .nc_con { - background-color: #fdfdfd; - border: 1px solid #ddd; - line-height: 28px; - padding: 10px; - border-radius: 3px -} - -.nc_opt { - padding-top: 6px; - text-align: right; - height: 24px -} - -.neice_con .nc_opt input { - margin-right: 6px; - vertical-align: -2px -} - -.neice_con .nc_opt label { - float: right; - width: 150px -} - -.nc_con_user p { - margin: 15px 0 -} - -.nc_con p span { - width: 80px; - display: inline-block -} - -.nc_con p input { - background-color: #fff; - border: #ccc 1px solid; - height: 30px; - line-height: 30px; - padding: 0 5px; - width: 280px -} - -.nc_con p input.disabled { - background-color: #eee -} - -.nc-tips { - padding: 20px -} - -.nc-tips p { - line-height: 30px -} - -.nc-tips p span { - font-size: 14px; - font-weight: bold -} - -.nc-tips p a { - color: #20a53a -} - -#BarTools .glyphicon { - color: #666 -} - -#BarTools .glyphicon:hover { - color: #fff -} - -.list-list .ico { - background-position: center center; - background-repeat: no-repeat; - display: inline-block; - height: 30px; - margin-right: 10px; - width: 33px; - z-index: 1; - float: left -} - -.fileList .ico { - background-position: center center; - background-repeat: no-repeat; - display: block; - height: 80px; - margin: 12px auto 4px; - width: 84px; - z-index: 1 -} - -.list-list .column-name .text { - color: #595c5f; - display: inline-block; - max-width: 85%; - overflow: hidden; - text-overflow: ellipsis; - line-height: 30px -} - -.list-list .column-name .cursor { - display: inline-block; - cursor: pointer; - width: 200px\9 -} - -.table>tbody>tr>td { - vertical-align: middle -} - -.showpicdiv { - max-height: 500px; - overflow: hidden; - width: 500px -} - -#BarTools { - font-size: 16px; - float: left; - margin-right: 20px -} - -#BarTools>button { - top: 0 -} - -.comlist { - float: left; - line-height: 28px; - margin-right: 10px -} - -.comlist>span { - margin-right: 20px; - font-size: 14px; - float: left; - padding: 0 10px; - border: #fff 1px solid; - border-radius: 3px; - cursor: pointer -} - -.comlist>span:hover { - border: #ccc 1px solid; - border-radius: 3px; - background-color: #f3f3f3 -} - -.PathPlaceText { - position: absolute; - right: 0; - top: 0; - background-color: #fff -} - -.PathPlace { - color: #666; - float: left; - font-size: 16px; - line-height: 30px; - margin-right: 5px; - width: 170px\9; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.PathPlaceInfo { - font-size: 14px; - line-height: 30px; - float: left; - margin-right: 20px; - color: #999 -} - -.onPath { - float: left; - line-height: 30px; - font-size: 14px; - color: #666 -} - -.editmenu span { - display: none; - text-align: right; - width: 300px -} - -.table tr.hover, -.table tr.on, -.ui-selecting, -.ui-selected { - background-color: #f5f5f5 -} - -.table tr.on .editmenu span, -.table tr.ui-selected .editmenu span, -.table tr.ui-selecting .editmenu span, -.table tr:hover .editmenu span { - display: inline -} - -.ui-selectable-helper { - background: #6bb0c9; - border: 1px dotted #072246; - opacity: .25; - overflow: hidden; - position: absolute; - z-index: 99999 -} - -.list-list .ico-folder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAMpJREFUeNpi/P//P8NAAiaGAQYD7gAWdIFPpxIoMS8ZiOcQqfY5SD0LFT1jAsRTuaU0GZjZuAgq/vPto+S3V3fmUssBokC8llNEjp2ZFWjk/1+Eg56TE0RJsgCD3BPImAvikGs7IzMLAxuvKAMrNz9ZaWAul5iKJAsXPwUBAMzK//9AaDIcIAkODiKCbbQcGHXAqANGHTDqgFEHjDpg1AGjDqCVA57/+f6Z7hZD7XwBahOmfHv1ANQqlqCzG54CcRrjaOd0oB0AEGAAscwsxMSUtNsAAAAASUVORK5CYII=") -} - -.list-list .ico-folder-unempty { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQRJREFUeNpi/P//P8NAAiaGAQYD7gAWcjR9OpWASyoZiOcQacxzkHoWKnrGBIincktpMjCzcRFU/OfbR8lvr+7MZSQnEbIzosbc65NxokDqDKeInBwrNz/xIfnwMgPjx5PxnkD2XCCWJNfrjMwsDGy8ogzs/CKkRSXQAaAomMslpiLJwsVPQegDQ/H/HwhNRiKUZOHkZPjx4xvD799/6ZLyWVmZGTjYWVBzAchybsUKujjg6/0OuAMGvBwAJcL/A14Q8SkYD4jlnx6chaaB/79GK6NRB4w6YNQBow4YdcDIdsDfP98/0d1iqJ1/QdVx47dXDxsGIDRADdBGxtHO6UA7ACDAAIASR69Q0kB9AAAAAElFTkSuQmCC") -} - -.list-list .ico-access, -.list-list .ico-mdb, -.list-list .ico-accdb, -.list-list .ico-db, -.list-list .ico-sql { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAwZJREFUeNrMV0trE1EU/jKJkzTTR4oJCX1lqq6q+GwriiLarS4VfCwqFEFxKShYUKE+Kv4GcVkquBE3glq0Vqt9iGg3qfZhSVttU0tNJk2aGc+9dGSSJmlaJqkfHG7OvZOc757v3HMnFk3TsJGwGZ2hoaEjNNwnazQxxh6yT8aJurq69AQIHT5CSUmJKZEDgQAbHpOdTCWhQ0jxfZIkQVVVU4zB7/c/XyaxOxcCpkMUxUtEojsTCaEQhUYkmmVZHkhHwpbPwKWlpXodMJxaHgfJLOsiEB4eRqi7B+HvI1CjUUQnp/i8o7ICgihC2rYVmw8fgrNW5vMejwderzddYa49A7NvujHzsgvlBw+gnIJYHXbYfT6+Fg0GocbiiAx/w/jDR/CdOA5XYz1f04sxpz6QDTMvXqHy7Gk4t9SuWHNUVPDRKft5NqafPvtHYE2NKBvUpSVYbDaEet5B2CRCS5BvoRoWBKjxGKx2OxKLi7C73dDo2XV1wmxQxsZRVFPNLRsSigJlYsJ8Aq76fZjvH4CtrIzLYBGSTzDbdYSKMzo1BVdDg/kEfvf1Q758EbFfM5j/2M/TzqRgEiSUCEnggJNOgaO6CsGOTvhbzptLgOnPdil63NwyShAOr8iOSRLsJQkGYS2WYKX7wkFHUKCjqOu+OP0TSwsLiM+G1iRBzlTn3n+Aa38DP2oMf6ihzHa95sa0Z5CoNtgzc7295mdAl4Dt3klWcAk8TUcRaLvLOyHLgoVaLxs16nTK+A/empXRMd4n3E3HzCdQde4MIiOjCL3twWQfOwVx7rPdFvlrIFAjYneAfKGF+6YT4K2WAugXjVkoyPvAf00gSYJQ602EChH1SWfmGrDa8vqShETKTZkx2s57t/H52vUkXwebN/pG6N/R143PGn8vp1Ogk0hHJlc/dS1vRcgCZdp1pmyZSoAFMu40VxJJEgjFxQlNUay72u+sSOFqNbDa7tln9keYxUi6Y4z/jvvaH7TGvny9pYbDeekPgiSp4o7tN+qvXmlLS2Aj8FeAAQC23WzgeNtzYAAAAABJRU5ErkJggg==") -} - -.list-list .ico-c { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFuKz0K4QDTjbbc0N4qrvP7+p0yABWY5sKTqf7mmAHMuHVy5ATg9k5wJ+VoZ/n/79Z2JkZCQqyElNhKCOMB8Lwz+cDjAVYGw69f5P3ac/xIWMy4rLJPkW6MG/QDuaUeqXge6eAwQYABvvATpIEEu4AAAAAElFTkSuQmCC") -} - -.list-list .ico-cpp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAoBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFuxZaFeIBpzttuYGSWIo1fef36RnQ5ChyIYh85EtxidG1TRAyFCy0wA5ABbk6CFErENZyLUUPbjJDSEUB/CzMvz79O8/EyMjI4aBhBIXMQDUEeZjYUBppTIi947LdlypP/X+f92nP7QpH4Ae/GsqwNjc5aHTiNUBAwEAAgwALWAgpic4qfUAAAAASUVORK5CYII=") -} - -.list-list .ico-cs { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAoFJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFvxZaFeIBpzttuYGhjiyGNbq+89v8rMhyBJ0S0F8ZHF0NTRNAzCLYI4gJgRolgiRHUE3B8AsRPY9qY5gRO6cOq64/PfTPxYmRkZGmiRCkF28jH/+HYjUZcaaCE0FGJtOvf9T9+kP7pBxWXGZJHFkwM/K8BdoRzPOEBgIABBgAMrJHxYdaBS2AAAAAElFTkSuQmCC") -} - -.list-list .ico-fla, -.list-list .ico-flv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYFJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDkV3BAwwofEluLm5Gf79+0cVDALy8vK7oI4wIMYBVAdsbGxZQEccweUIJnokNKAjEhQUFM5hcwQLLS3m4+ODpQMQCIPS54GYkS4OEBUVZRAXF8eWMMkLgZf1fQx/37yD85lFhBjEG4sYPizfCOYLRPpj6IElRqLKAWIAyBIuG1OqhRITwwCDAXcAyVEAim9YnLNrKDMI5ybS1wGjaWDA0wAu8O3IaTCGAVDaAKURQoARuUECbA/8V1ZWpqmP7969C2oPMI6mgVEHDM5syNI+k+EhkXn0DyUWLp6I2w5cliq1laDw71X1gMVANDa12MSxOZykggiXoXRLAyCfoYcELt8TUkfTECDWcqo3StHjHl9awB4F3Fx//5NhKbE+/g+1A2dldGPCrBqm6/caGb59Z6JJNuTi/PdPU6leoyCtBasDBgIABBgAWP2ttPWI30cAAAAASUVORK5CYII=") -} - -.list-list .ico-css { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAALdSURBVFiF7ZfLTxNRFMZ/M/RBnwik0FYpoJIYhJioLFSIupCoWzVqotGNCeEPcGPiI+50Q9xAYli5MaEbFyTiI0EWhhghGkohlgVREVzQhpfals64qB067ZRpoaALv6Rp73dPzvnuud/cOxVkWeZvwpA+CAaDZ4BewFPEGoeBkXSisbFRWwDQ6/V6PTabrSiVQ6EQQB9woaGhYUQrRswYeywWC5IkFeUD4PP5BoC+UCh0KB8BRYfZbO6ora19A/i1RGy5AACTyXS9rq5uWEtEpgeKCqfTmfIBwKU/3+8BYVsEuFwuqqurVVyaoK0XAChmzIVNe0CS4drrGeLSxg60ggRMRKJMLcRU3Mf5X+yvMGMUBaYWYkxEosUXEEvIdAfC9IxHKDeXqOYGPi9zqsYOQLm5hJ7xCN2BMLFEfh3RFRAIR+kcmqXKaqCr1U1l6ZqAuCQTDEc5UFkKQGVpCV2tbqqsBjqHZgmE9buha8Jbw9/pOeHFY80OfTv3kyNuC6KwxgnAud1OjrqtdAx+49lZ37r5dTtQYzfSP72kabKXX5Zp99mz+Lgk0z+9RI3dqJdeX8CjNg8Ok0jn0KzKYMtxibkfq+xxmlTxE5HkljlMIo/a9C9V3S0QBbi4t4xjbisPP8xzo7GcpgozgzMrHPdaVbGBcJTHwQh3W1zstOmvHgp4DHfZjXS1uql3JBO/+rqiuD+FekcyJt/iUOBJKAA2Y1JzV6s7az41Vwi25Tb8L+CfFqAy4ZWRBIx8UsaiIX835wNpNQ7A6NpLcfZToFX0xfl9yu92/+SGedFgVETkFKBVPDN5u3+yYD4XhPR/RgefjMmZHVgvQWqlmfO5eEhuw+jVZuX62pQJ2/2Tyqrz4bVQsIBU0sx91uNzQbUFJ5+OJRYlgygIgiqoWCaUZRmHsCoNXm5W3mpUAm4+D9x5F5FvL65uzflQZiTRskO4/+B00z1NAQqZ0YFiI73mb2NmSwowsJSgAAAAAElFTkSuQmCC") -} - -.list-list .ico-js { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAALFSURBVFiF7ZfLTxNRFIe/GTotffJKoa1SQCUx1cSFslBLdKFNdKtGFya6MSH8CybG6NJNwwYS496EblywwEeCLAwx1mgshVgWREVwQRteamk746K2dNopM5WCLvwlTXvPPTn3u+f+5lFBURT+pkylg3g8fgF4BHjruMYJIFoaCAQC2gDAI5/P57Xb7XVZOZFIAIwCV3p7e6NaOWLZ2Gu1WpFluS4fAL/fPw6MJhKJ40YA6i6LxTLQ1dX1EohoQew6AIDZbL7Z3d09pQVR7oG6yuVyFXwAcO339xtA2BMAt9tNR0eHKlYCtPsAQNGM1bRjD8gK3HixQEb+sxtaTQAzqTRzK5uq2PvlnxxptSCJAnMrm8yk0vUH2MwpDMeSjEynaLE0qObGP61zvtMBQIulgZHpFMOxJJs5Yx3RBYgl0wxOLtJuMxEOemhr3ALIyArxZJpjbY0AtDU2EA56aLeZGJxcJJbU74auCW9PfWPkrA+vrTL11dIPTnqsiMJWTAAuHXBxymNjYOIrTy76t62v24FOh8TY/JqmyZ59Xifkd1TEM7LC2PwanQ5Jr7w+wFC/F6dZZHByUWWw9YzM0vcsB11mVf5MKn9kTrPIUL/+Q1X3CEQBrh5q4rTHxoN3y9wKtHC01cLEwgZnfDZVbiyZ5mE8xd0+N/vs+ruHGi7D/Q6JcNBDjzNf+PmXjaL7C+px5nOMLg413gkFwC7lmcNBT8V8Ya4W7cnT8D/APw2gMuH1aA6iH4tj0WTczUYkZzMAvN16Ka68Cowu+vTyYUKR2eLvggoxLYkmqQhRFaBWlYJojfVUFw8Y7YCWdtyBWo+hXDV1oHSR8lgoMlvz7qGsA00S8qqsiIIgVMuvgAhFZg3vXlEUXCZUb6kqgL5m4d7rVPbOala7M+cef9AsXC1eriaJXF+zcL80Jhj5e75dR7aTkdq/AFCYGAYKWHWoAAAAAElFTkSuQmCC") -} - -.list-list .ico-htm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFtQstCtEg8FtzQ04GxsAycPkYGqR1SOL/fvzm7IGCbJjkA2maRoAWYbL97hCglgHMhHra2r4li65ABYKxDoYxQH8rMAmHg07qyCz+VgYUFqpjMgWlu24Un/q/f+6T39oUz4APfjXVICxuctDpxGrAwYCAAQYAPufFoHFXwuVAAAAAElFTkSuQmCC") -} - -.list-list .ico-html { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnpJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFkgW2hWiweC25gacjQ2A5GFyMLXI6mHyyHL//vwmLxsiOwbZQJqmAZBluHyPKyRIdSATIYOp6Vua5gJs8U2yA/hZgU08GnRWYVEJMpuPhQGllcqIbGHZjiv1p97/r/v0hzblA9CDf00FGJu7PHQasTpgIABAgAEAooAb7fMLYAoAAAAASUVORK5CYII=") -} - -.list-list .ico-java { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvRJREFUeNrUV0toE1EUPfPJTJNp82lNY1PaBGwtpn6q9bNQQRFFpSAKgrgQu3PpXlBEBMGNWwU3LtwoxYULhSoKgkJbawVDta3WT5XWEPpL2iSTifeNjqY6TeNkavDA5c375N1z7z3v8cLlcjmUE2J+JxqNHqDmOlmdjT42k/XlD0QiEXMCzHkwGKxTFMUWz0NDQ6y5RXa0ubm5z2wN/1u/zul0QtM0W4yhsbHxPiNBZNqLIWAZI+PzpuOyLJ8KhUKP6fO2GQlbCLyPpfAhll50XpKkk+Fw+JkZCbFU56qWw72BKRxq9/0x53a7DR0wHPvR9pJxthCYSmbR1RPHnlY32GEei6cRrJZ+7u73+xEIBMyEWXoJBt4ncfXBOLavrkLv2wSVIIX6POcGzIRZMoGZ+Szu9MbRuasW/aMJ+BQB25oqLQViiYDIcwj7ZfirRF0D6az129QSAafEY+uqSnyZzKBjow89IwlEx+b+HQGGNfVOVDg4uJ0C2kIuVMr88hNgwnv+LoHYjKr3fYoIjlS3b71Hzwq7jNJqDk9ez+Bu/yS0IipT9DFkG8dnVYxPZ/DyYxJM0FnyUOHg4RA5NNRIlAkFEn3vaKnCtYcTiH5KYm2Dyx4CbOPddN5Hv6b0aAMex4J5Rkbgvx9ClURpCNW2DBiYoAw8fTMLmerPiLhkQXfICO7f4NXHWIlO7FwBUeDsIZBMaxj8PId6n6Srn1kheFyCnhHbCDyKTuMFXThNKyvgdYkUNdWdNmcOHMKvtCdSml4iNn94S/Xfv4gWw8E2L/au8+jpj02rmJrLIp5QkSFhZsixTOmXSYw1dDF1bPLqp8PSk6wQWKSsBMzsBI8yo+wEFpTgUjddXd3Dy+70RqRABiRZXmCXO1tt7f/fGrh4vAlnbg7rrYH8vjFfaH3JGSi0mZX14lLRFtM3oix2/aIZUCT2btSW/JFVsL1d5CN/jMv/d3yl69W5wQmcTaaXR5wUYLalFhdOH2k9b0qgHPgmwABqjFw0GGyiFAAAAABJRU5ErkJggg==") -} - -.list-list .ico-log { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAVBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkLQSevnqLU05aTBivXlhiJKocINcSSgATwwADiqOA0pAbjYLRKBiNgtEoGHXA4MoFE+fsooulM/twt4pBzScMDY3VmQz1rdMxxGAAWQ6XOAz8+vWL9GxIyEEwPi7xAUsDhCynqQNAvkeODro7gBjfY6QBLk62f79+/2MCAqw+ghkMi290y9DFsTVQQXYgizEi946nzllbf+f+y7pv33/RJGS4ONn/qiiKNWenBDdidcBAAIAAAwBHPa+btF94QQAAAABJRU5ErkJggg==") -} - -.list-list .ico-mht { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFtQstCtEA0y7rbmBVQzEhsnBxNEBst5/f37TtkGC7BhkiylKAzCf4jIYJIbL93RJhCAHEeNbihyAHt80yYb8rMAmHg07qyCz+VgYUFqpjMgWlu24Un/q/f+6T39oUz4APfjXVICxuctDpxGrAwYCAAQYAIH+FoEeflonAAAAAElFTkSuQmCC") -} - -.list-list .ico-php { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA1ZJREFUeNrsVktPU0EUPn3dPq73QmmbFlRaAw1Q5KUiYCIkmmjUGEMUdGWMSze6cmViTFwYVv4Aw8KNRhe6kA0SAonRGGNBHpWkBAXFIFAeLS307ZyB1ku90EJvdeOXTO7c6XS+b8755tyRxeNx+JdQCl9cLlcLeXSQdlRCjjrSBoUDDodDXADBUwsBx3GSMLvdbnw8J63NbrcPis2Rp7xbWJaFWCwmSUNYrdZuFEHE1GYiQHIwDHODiHizlQj53zAaEXHNZrM5xUQoc0nM83zCB4j2jecAabJdCVgLx8D5xQ9j06swOR+CGDnCkWgcWLUCrCY1VO3XQlWxDhTy9fVNJhOYzWYxY+48Akj8eniZkOjgdE0+mHgVMMp1In8wBnPeMJ3TM+KFC0f0YLdo6G8JM2ZUB7ZCn8tLCW6esYBG9ds27pk1sns5FOkZYEkEbKShmGfvPOBbjcKhA2zatdOacGI2SMIdhLZGwyZyBI5j+IVAQVebjfB+fIWK3lElFEM/2f3Zunzax0WnF0IgI5E/V6eHmaUwdA8tQa2N1A5S0Ycm/RAhET9RycOp6jzo/+yDSw0F2UVgZjkM5jwV7X+dC0JLBQ+lZg18J0ICJNwXGwzwaTJAhVXs1dL8D5H3YqMapkiEsk6B8GPl8UVAv0cJPxZDNNScVgFoeJVCRsONXvjmCdHxTJFWgJFTUUKaL0L08sMC5OmUEIrEwWpkqOlYzfoyvaNeGJ4KQH0JS71TSARl7YGTB3l45VyC9sYCKCvS0BQkaoKJV9LdN9k56B1ZhvOH9clj2eVchCvHDNlHoITku5Sc6SdvPVBWqE2O44nAhkUH09G8IQzT9Kh3Fo6XczQlWUcAga42kNx39s3SAlNtZYHTyJMEaEDfWhS6Bvy0QrbW66G8SLvzC8l2qLHqwLFPS3P8cWIF5slOkRhNiLnGyohV8nKTIVmKJRWAwHxjdcukwkl2CnKN/wI2eeBBD6l6PeM5J33s2MaEjFr9xx86rpcm+7c7x5NjqX2xeakIBYO7SwEumEqSzTxJ7oSZkkgiIEEmDK0wBdvNk0RApgtmOk/UAywD0XSXyGyAayOHcEwmvHA8fDF6Z+wn3AuEclMfdAzEys1w91Zr5X1RAf8CvwQYAB0rcvQMbg3OAAAAAElFTkSuQmCC") -} - -.list-list .ico-url { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAr5JREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGakmgP+/P/LsP/FJYZzb+8wMDEyMchyizD4yVgwcLGwM4iKijKIi4tjS5jkh8DLHx8Ytj45xXDj4xOG59/fMfCwcDBYi2kxBMpZMajxSYPVgMRBDgABWGIkqhwgBO5+fs7QcWUVw4+/vxn42bgZtATkGAwElRjWPDzCsPf5RYZcTT8GIyFlBklOIfIKInwAZOnE6xsZ/gB9FKfszOAqaYgoWIAOmXlrG8P0m1sYuoyTGQTZeIh2ANG54Oirqwzvf31hiFC0R7EcnNe5xRhKtUMY/gGL9d3PzpNfFOMN/i8vGJR5JYHBfZhhyb19eNQ9J8kBRIeAOIcAgzA7Lzgq8IGfBOTJDgE9QUVwYmszTABnNWoBokPg+OvrYHrD42NY5S+9v88Qe6SH4dy7u7RxAMgCHQF5hlNvboFzw+sfH8Hiv/79AWbBC2AxDX5ZcDakSRR8+v2dockglmHu7Z0Mx4ChcebtbXBh8+3PT7A8yHHZGr6UNUjwATEOfgYWRmaGTHVvBlMRNYbTwJB4+u0tg7iAANByBQZHCT3KW0T4QISCHcPBl5eBiVGBwUBIicFEWJX6TTJ8ABS/IExtQJf2wKB2AEoUTHixg4HhBe0tnYXUKiYqBNqdMrDyQTQyxqWeau0BbKBy3wwUi5H5Qy8NkAOQg5tU3w+KKEBxADcT+9+v/34yY7OEVJ/C1KOrBdmBzGdE7h1PPbGm5vaPF43f/v2iSdrgYmL7p8ohUZ9tEdKC1QEDAQACDADzFSd59WqSKgAAAABJRU5ErkJggg==") -} - -.list-list .ico-xml { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57TwhAATCysK3hOhi5UPorHJIcujmwXCJEeB25obDLtCNMBsEA3i0z0NwByBbjk1HDc0cgHMdzDfEhM6ZDmAnxXYxEPrrCIbjssR+ByOrB5kNh8LA0orlRHZwrIdV+pPvf9f9+kPbaIG6MG/pgKMzV0eOo1YHTAQACDAAOGRHaFJlGXVAAAAAElFTkSuQmCC") -} - -.list-list .ico-ai { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAg9JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZ6eIAAQEBsCMYGRnh+OHDh7SLgvdXNjK82N8K57OysjIwMzMTXw4QC96dX87w7elRBin3HgYmVg64uKCOP5D0R7WAhYXh79+/OM0iOQT+/f4BtpyJjZfh65MzROkBhQITExM4Cih2AMhSNkFVBl5Vb4bvz1DLljen5oCjARuApQGKHQCylFPKmIFTQofh1/vbDL8/vSBaL8UOAFkGspRbxoSBhVOAgUPMgOHbi8sUJVySHACyDGQpLOGxi2owfH2wj3q1IcH4B1r279dnhidbclCj5cU1YJRo0dYBIEtAAD3rgbLk9+cXyXYA0VEAsoRDVA/FchDglNQHZ0tQ9iQHMCK3iIANkv/Kyso0rZju3r0LapAw0rU2pFouGHUAzVvFLEfSGR4eoYOFWodxhwA7C3FYLWweXj4uPDzSgHzQPIaH65LA9IAlQmpYTlaTDOZ7XHzKQoCV798/GvZVwWaz8v7DWRfc2Fxbz/TmbB3D78+0SZxsfH//CRs1a/g2N2J1wEAAgAADAIk82p3e8s5qAAAAAElFTkSuQmCC") -} - -.list-list .ico-bmp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmtJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7anvAKhs1Sx6biiIO46OLIetDVvPt708yygE0S5D5xOjDp4eoNAAyAIaRDUL2KSkOo1sIYIsmqldGsFDAZgkxDkVxABcT2z9gxcPEzMiEMyGSE8zIlRrIDpTGDXLveOqJNfW3f7yo+/bvF03KB24m9r8qHOLN2RYhjVgdMBAAIMAA9C4L2PHEWagAAAAASUVORK5CYII=") -} - -.list-list .ico-cdr { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmRJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7Cr/WPhHObj44H0OMGHFk8O3vT+ITIchAZEOQ+aSKUyUNEDKM5pURMQBblFEtBNDjGFcowTAx6lFCgIuJ7R+w4mFiZmSCG4bLR6T6FFapgexAadwg946nnlhTf/vHi7pv/37RpHzgZmL/q8Ih3pxtEdKI1QEDAQACDAD7sQauaYXLQgAAAABJRU5ErkJggg==") -} - -.list-list .ico-gif { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM77raBVTYiax6bChcDsZHlkOXRwbe/P8krCWEW4eLjs5QmaQCbZSBHoYcETeoCmCUDFgLkWERUCHAxsf0DVjxMzIxMWC0lNXixVWogO1AaN8i946kn1tTf/vGi7tu/XzQpH7iZ2P+qcIg3Z1uENGJ1wEAAgAADACWJ9+r79LDyAAAAAElFTkSuQmCC") -} - -.list-list .ico-ico { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAaVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLBSZ/Pc3w+/HVxhYZHUYGJlZMaT5+Phg6QAEIqD0GSBmpIoDfl7Zx/Dt4EIGLvt4BnZ9dwx5UVFRBnFxcWwJkzohwKpoxMD+9gmYxgVgiZGocoBUwMQnysDllEy9gogU8H5SFJzN5ZjEwK7rQp4nyHUAl1MKGFMKyHYAu44TGA+YA6gFRh0w4A5goTQLgsC3/fPAGAQE85bR3gGc1pEDGwIcxr6jiXD4OIARuVn+sECTLm10+QnX8TRIWNixa6rewvCw1QfOhjsaKoZPHAX8+UlZLkB2CDIflzjN0wAxlgzvXIAc7xSXhP85Bf4x/vrKxMDIhDfIsSU2XOIo4P8/oB38/3BmwzuzCutZ7h+vY/z+kSZR859L4O8fBYtmlbT+RqwOGAgAEGAAjiC9hXL1ZWMAAAAASUVORK5CYII=") -} - -.list-list .ico-jpeg { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnpJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7hoZa+0SG5oPzwTQMgPgwOWSATRwmBgLf/v6krCREtgDGRrYAXQ4bn+ppAGQBekhgczRV6gKYRciG4rMAm3qiQwCbr0AG4bOQFN9jhAAXE9s/YMXDxMzIRFT8YXMozIHYHA+q1EB2oDRukHvHU0+sqb/940Xdt3+/aFI+cDOx/1XhEG/OtghpxOqAgQAAAQYA3ogNywRusk8AAAAASUVORK5CYII=") -} - -.list-list .ico-jpg, -.list-list .ico-JPG { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAm1JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7/vaBVTZD07GpYBqeTYF8ZHls4jDw7e9P6pWEMAuQHYXuGGyOoFsaIGQ5xSEAC25sFuGTIysEkOMW2Ye4LCDG9xghwMXE9g9Y8TAxMzLhTYD4ghybQ5ErNZAdKI0b5N7x1BNr6m//eFH37d8vmqQNbib2vyoc4s3ZFiGNWB0wEAAgwADnlgGzkKaAXwAAAABJRU5ErkJggg==") -} - -.list-list .ico-png { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7anvAKhuRHY9NRRFD5qPLIcsjg29/f5JRDmCxCGvjBU2ekHqK0gDIYGTf4nM4xQURepBTUw8Tsb7FZhC+UCDWsSghwMXE9g9Y8TAxMzLRJFpAlRrIDpTGDXLveOqJNfW3f7yo+/bvF03KB24m9r8qHOLN2RYhjVgdMBAAIMAADIwGkBp21VIAAAAASUVORK5CYII=") -} - -.list-list .ico-psd { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAkRJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZ6eIAAQEBsCMYGRnh+OHDh/SLAlZWVgZmZmbiywFCYMKxewyvvv1CZHAJPoYgHSn8FrCwMPz9+5c6DgCBcC1JBn0pfoaXn38wLL/8jOHA3dcMDsqiePWAQgFU4mIrdclOA+K8HAyWMgIMl15+Ajvg5KN3DBtvvYLLt7looKgHxT9VQgAbAIUGyPIMYzkGOUEunOqwOYLsRPjo/TeGPfffMuiJ8zEIcLKCxd5//01ZZUQMWHntORiDgL+aGIO5nBA8bex/8JZhy+1XDC6KwnBxqjsAlgjRAUgMhC8++wh2oCQwjeCLDpqVAwpCXAzcrMwM3//8pU0I4EoPM84+gvPdgFGgLspLlF5G5LwJbJD8V1ZWpmnFdPfuXVCDhJGutSFd08CoAygqiGLOArPO2Vs0t/ScFp5syMTCiqFhmZ8anB216RaGOEwMWR26Whj49+c3eeUAsiUgNoxGFsPmQGyOoEoagDkCl0+pnghBlqH7CNkRNHcAyDJc8U+JI1DSAD8rw79P//4z4Wq9EBMFuBIsCICKfT4Whn8464KyHVfqT73/X/fpD23KB6AH/5oKMDZ3eeg0YnXAQACAAAMAI0n+E64by+4AAAAASUVORK5CYII=") -} - -.list-list .ico-webp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAo1JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7Q5V9PEPbwYUYGmHitfaJKOLNB+eD5ZABTD+yWpC6b39/UqckBBmGDpAdjewJmFqQYyr3zSCcBkAaYT5CppEtABmGHhIgNTCMLQSpWheQEgIwh2LTg9MBsFBApqnpWKwO4GJi+weseJiYGQnnTvTEhRxd6KGBXKmB7EBp3CD3jqeeWFN/+8eLum//ftGkfOBmYv+rwiHenG0R0ojVAQMBAAIMAErgG/MeljhhAAAAAElFTkSuQmCC") -} - -.list-list .ico-ape { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAZNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwxElUOEAM+dpYTpY6/vJP0gohYQMhwYh1Jt1ww6oBB7QCiE+Gn/lqG/79+DZwDiLH887Q2hn+fP9ImChjZ2FBorIUOiZaTFAJ8hc2EfcPLD3YEiKa6A4gBvFlVo9lw6DmAEblZ/rwwhi5tdMn+JbgbJGyMjFg1Cdf0MbxtKULhwwBMHFkMWRwZ/ELrh1CUC5AthrGxWUpxGoBZgO5DXGqJUUdyCGAzFCaG7GtSQ4CFWN9j45NqGUEH/OPk/vfvxzcmSvImtsQJNx9UqQHtwJkNH8zur2e7f6OO8ftXmpQP/7i4//5W0GhWSC1sxOqAgQAAAQYADkDCgkQGXJUAAAAASUVORK5CYII=") -} - -.list-list .ico-avi { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAtpJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08IsLGz48SdqZpE8dHFkfHQTQPNcUoMtYvugWkYQObD5GmaCJEtp0kiJOR7bHxYKJDje4wQ4GYDtSH/0Sy+QWZzAe1AFmNE7h1PWHe1/sYrhrpvv2iTOIEe/KsuxtBcEKTdiNUBAwEAAgwAcCQxq0cx9U0AAAAASUVORK5CYII=") -} - -.list-list .ico-flv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnx9DQHKeEwq9ddA8sBqKR1eDjw8Cvnz/JS4TYDKNrGgD5CD0ksIUKPt9TlA0HPASIdSQpvsdwADcbqA35j4Fa0YIOQGZzAe1AFmNE7h1PWHe1/sYrhrpvv2hTPgA9+FddjKG5IEi7EasDBgIABBgAhSU3wy5Zmw0AAAAASUVORK5CYII=") -} - -.list-list .ico-mkv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAq1JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYkayHfD333+GE4/fMVx88ZHhxecfYDEJXg4GfQl+BgtZIQZmJrjZDKKiogzi4uLYEiZ5IfDpx2+GBeceMvBzsDK4q4ozyPJzgsUff/zOcOTBG4azT98zJBjJM/AB5WEAlhiJKgcI+RxkuQ7Qp05KomCxGafugekMMyUGZSFuhn33XjPMB6rJtlBmYEEKCXyA6ER48sk7sM9hlsMAckEKkhMAqjkFVEtWSYgPXHj+ERzsyADkc3RgoyDCsPP2SwYrOWHqOuDpp+8MMnycKMGOjZ1opMDwDKiWWEBWOYAc7OhsRmDUMzEyUj8KpIG+fwL0GXKwY2PfffcVnC2pHgKgfA7KasgAFOywoIcBkBp1ER7qO8AcWMh8AJYDoKyGKzpAcg8/fAOnF2IBI3KDBNge+K+srEywIBLgZGWwlhfBKIhADowzlGNYevExgwgXO4OzsiiDKDc7ihl3794FtQcYyXIAclF8/tkHhldff4LFxICWGEoJwIviP0A1oLLg5OP3DIXWKngdQHJdALLAWl4YjHGmbKAaUDlATFlAl+p41AFEF0QxZ/8C26+3aG7pOS08IcDEwoqCt4ZogzEuMWQ5bGLofJpFweYANQbfDbdokwZABoMswGURuhgh9UMzF8B8hc03yD4mRj1OB/CzMvwjt7OKzRGY7Yj/DHwsDP9wVkZlO67Un3r/v+7TH9pEDdCDf00FGJu7PHQasTpgIABAgAEAZzJTrgcAEqEAAAAASUVORK5CYII=") -} - -.list-list .ico-mov { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAupJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnh7Ob45TAdO2iezjFYHx0MXQ9MP6vnz8pS4TIAJ9FVEsDIANhviTWAlL0DI1cAPMRKcFLrB6UNMDNxvDv579/TEBAkiXoiRAXADVQuYB2IIsxIveOJ6y7Wn/jFUPdt1+0iRqgB/+qizE0FwRpN2J1wEAAgAADAIjpSLqXWcBIAAAAAElFTkSuQmCC") -} - -.list-list .ico-mp3 { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAZBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwxElUOEAM+dpYTpY6/vJP0gohYQMhwYh1Jt1ww6oBB7QCiE+Gn/lqG/79+DZwDiLH887Q2hn+fP9ImChjZ2FBorIUOiZaTFAJ8hc2EfcPLD3YEiKa6A4gBvFlVo9lw6DmAEblZ/rwwhi5tdMn+JbgbJGyMjCh84Zo+MP22pQirGIyNTw0y+IXWD6FKFIAsQXYMiEYXoygNYDOckHpi1VIlBEAWYbOMaiGAbBg2H8GCGz3+SS4J/3Fy//v34xsTExXSBLZECGqe/gfagTMbPpjdX892/0Yd4/evNCkf/nFx//2toNGskFrYiNUBAwEAAgwArNbTCDlVe6IAAAAASUVORK5CYII=") -} - -.list-list .ico-mp4 { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnx9DQHKcEpmsX3cMqBmOjq4GpQxb79fMnbaIAZAm6Y5DZFKcBZMPRfTUoEiHIUcgOI9aRTKQGMy6DYVGALe3giwoUB3CzgdqQ/6iaJpATJshsLqAdyOoYkXvHE9Zdrb/xiqHu2y/alA9AD/5VF2NoLgjSbsTqgIEAAAEGAKRaSLN4Zv7KAAAAAElFTkSuQmCC") -} - -.list-list .ico-mpeg { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAwJJREFUeNrMV01ME0EUftS2221LscBSt0pbjBWsBjGAQRMT48FEL/5F44kYD8ajnjypMVw8cvFkSJSrxMSDMRoSUeNPDARJtKBVo0Sx0CZFC6Wldeu8laWz2y0sMoAvedmZN1/nfe/NezNQls/nYS3FTE/C4fAh8ukiKjL00UJ0gDaEQiF9Aujc6/WKDoeDiedIJIKfO0RPBoPBAT2MSTMXeZ4HSZKYKIrP53uIJAiZZiMEmAvHcef9fv8TMuzRI2FajUKzWq1nAoHAKz0S5qVuNp2R4Pn7JIyMpSGezMk2wWWGbRt52BN0goMrxORyuZQ6QDk99+0nWvZPBN5+m4F7/QnY6bfDsVY3eCossn38ZxaGRlPQ+SAKR1rcsGMT/5eYIIDH49ErzKVnAJ3fH5yEs/sFENdbVGu1VVZZm+sc0P0sLtsUEkoxlhKT0bRj5O37qoucq1qIrCEGsVNpyVBghgi8jExBo89e5LyrLwaJ6VwRCcS++JBkR2D4+ww0kXPXyqfxDNx4NAGRaFplRywWKTMCsV858Lr1U5+aleDW0zj0hQsRiwQbS+bYEVhUyHsmUY8a1t06gzsb6gLs87FEVq50rdgsJjjVVgkNXtu8Dduy0mlmRwAvGexzLYGAwMGJ3W6o0jhDbINoY3cEbVucMPQ1BT8msyr7uQNCkfMowSB279ZydgScNpN8w+EloyVBC67dJhjE4m+YHcH8zUbqrOtxDJoCdthFWk1wWea6JAuDJOo3X1JwtLVwFTMlIJOo5aGuhpMfo57XCfkxwmqvLjdDvcjDxcMbVI8RcwIo6OBgY4WsLGRV/h74rwmojuB6L6my3o8r7rQ7tEANWDkOOto3y+PL3Z/n7bRNGdMY2lbKjrbZTGZ5RajnQBnThLVryvzSzWFjNUBHqt2olCBOmwk90kwyoHdMC5FU8IYzQGdBb2O0GcnKYuRUGXBYQcpIkmm5WaEJao/ETnzQ8zL6v+POu++ujkzAldTsytwPJMDf9TXQceH49mu6BNZC/ggwAJR8VIseM6jsAAAAAElFTkSuQmCC") -} - -.list-list .ico-mpg { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvlJREFUeNrMV81PE0EUf61tt9uWYoGlbpW2GCtYDWIAgyYmxoOJXvyKxhMxHoxH/QPUGC4euXhsor1KTDwYoyERNX7EQJBEK1o1ahQLbVK0UPrl1nkr2+5ut7CVAfwlLzP75nXfb2Z+b2ZrKBaLsJYwyR8ikcgh0oSI8RRzdBMblTuCwaA2AUzu8Xh4u91OJXM0GsXmFrGTgUBgVCvGqHrmWZYFQRCoGMLr9d5HEoRMlx4C1MEwzHmfz/eIdAe1SBhXQ2gWi+WM3+9/oUXCVOvL5rICPH2XgonJDCRSBdHHOU2wbSMLewIOsDPlOTmdTkkHiNML7Qgxwz8ReP1tHu6MJGGnzwbHelzgrjeL/qmfeRj/moaBezE40u2CHZvYv8Q4Dtxut5Ywa18BTH53bAbO7ueAX29WjLU0WkTrarVD+ElC9EkkJDFWg1HvsuPM+/Y1VSRXlBAZwxiMnc0Iuiami8Dz6Cx0eG0VyUPDcUjOFSpIYOyz9yl6BN5+n4dOsu9qfJzKwvUH0xCNZRR+jEWRUiMQ/1UAj0t76dM5AW48TsBwpDxjnsTGUwV6BJYEuc8E2aWGulun8826qgDrfDKZF5WuhtVshFO9DdDusZZ8WJYNDhM9AnjIYJ2rCfg5Bk7sdkGjKhnGtvNWelvQu8UB41/S8GMmr/CfO8BVJI+RGIzdu7WOHgGH1SiecHjIqEnIgWM3SQzG4m+obUHpZCM6Cz2MQ6ffBrtIqXFO80KV5GGMzPrV5zQc7SkfxVQJiCRaWGhtZsTLaPBlUryMUO1NdSZo41m4eHiD4jKiTgCBCQ521ItGA6vyPfBfE1BswbUhorKhDyueNBxcRAMWhin1+/s2i+2l8CdNn9SvFqP2I3LZ7PJEqIaUAJNKpNRk1CRq0oB8pku9bDGC1MpQDa1t0jNWUxVoLa18rFoCPaulWAG7BYSsIBgJlqUJuQjlwA9UG8kh9xnk/44Hbr+5MjENl9O5lTkfyAR/tzVD/4Xj269qElgL/BFgADHlS7OoZ6a9AAAAAElFTkSuQmCC") -} - -.list-list .ico-rm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnx9DQHKeEwq9ddA9FHMbHJYYMfv38SV4iRLcElwV0TwMgh8B8TY7DiA4B5Giglu+pFgWwUCDHYShRwM0GakP+o1nqB5nNBbQDWYwRuXc8Yd3V+huvGOq+/aJN+QD04F91MYbmgiDtRqwOGAgAEGAAnMw5uSNz54cAAAAASUVORK5CYII=") -} - -.list-list .ico-rmvb { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08a6EzVROHXLroHppvjlFD46GIgNrocTBwZlM++TjgRYjOIEoCsH92DTKQaBPMRusPwyYH4MExUCCAHG6W+JxQCJEcBtvgmRo6oXMDNxvCPFlkTOUQbF17/NyNDG85nRO4dT1h3tf7GK4a6b79oUz4APfhXXYyhuSBIuxGrAwYCAAQYAPSMU/fnJ99KAAAAAElFTkSuQmCC") -} - -.list-list .ico-swf { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfVJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUKp4d/3H2D4tm8fw/9fPxlEOjtR5Pj4+GDpAATCoPR5IGakigM+LVrM8PfdWwbBokIGJkFBDHlRUVEGcXFxbAmT8hD4+/Ytw69LlxhEuoC+ZsIdk7DEiAuQnQZ+Xb3KwGFpgddykgsiUsCfR48ZPq9eDY4GEOAJDmbgi4ulnwP+fvrIINLezsCmpUlRCJAdfv+//2Bg5OSgOIuS7QBQ6mfi4hoYB/z/9Yvh78tXDMzAbEZ/BwCz1ad58xg4rawozgFkOeB1UTFQFzMDX0I89dsDxADRCf1UrSfoUhmNOmDUAfgAI3Kz/KaTM13a6Or79uJukACbTxgaFGfPAtP3U9PgfBgbXR1IHKYeBpDV/gKWoiSVA8iW4bIYGyBWHRMxBqGHALIYMo1sKYiPHhJkJ0JswUqMHmJCgYmYKMDmCBgfmaa8LuDj+/vv2zdmJqRaDlsUkAvADVSgHTiz4Y2GxhrG8+cbGT5/pk35wMv777+hYb1GQ30LVgcMBAAIMACmqfNs4ifjnwAAAABJRU5ErkJggg==") -} - -.list-list .ico-wav { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwxElUOEAM+dpYTpY6/vJP0gohYQMhwYh1Jt1ww6oBB7QCiE+Gn/lqG/79+DZwDiLH887Q2hn+fP9ImChjZ2FBorIUOiZaTFAJ8hc2EfcPLD3YEiKa6A4gBvFlVo9lw6DmAEblZ/rwwhi5tdMn+JYw4Q4CNkRGOJWv7Ufi4xEnhD6008LaliEG4pg/MRqZB4jAA48PkselDVk+TEEC2nOJEiC2ekH2JzffE8kHgF9A+nInwHyc3gSYkhQ1UUKUGtANnCDyY3V/Pdv9GHeP3rzRJnP+4uP/+VtBoVkgtbMTqgIEAAAEGAH8AyCqN8wbhAAAAAElFTkSuQmCC") -} - -.list-list .ico-webm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAwxJREFUeNrMV81PE0EUf137tbQUCyx1q7TFWEE0iAEMmpgYDyZ68SsaLxKjifGof4Aaw8UjHky8NFGuEhMPxmhIRI0fMRAkwYpWDRothTYpWmgLLVvnrSzdr2JLBuoveZndt2/m/ebNezOzhlwuB+WEUf4SDAYPkSZAhKfoo53IkFzR3NysTwCdu91u3mazUfEcCoWwuUfkpN/vH9KzYVTvPMuyIAgCFUF4PJ7HSIKQaSuGAHVYLJaLXq/3GXns0yPBrEWimc3msz6f740eCWOpg83OCfDyYwLGwmmIJbKijnMYYdtGFvb47WCz5OfkcDikPECcXmwHiRhWRGD0RwoeDMZhp7cCjnU4wVVlEvWTvzIw8j0JPY8icKTdCTs2sX+JcRy4XC69xCw9Auj84fA0nNvPAb/epPhWX2MWpa3BBr0vYqJOIiElYyEwxYYdZ961r1bjXFFC5BvaoO1MWihqYkUReB2agRZPhcZ5YCAK8dmshgTavvqUoEfgw88UtJJ1V+PL5BzcejIFoUhaoUdbTFJqBKK/s+B26oc+OS/AnecxGAjmZ8wT22giS4/AP0HOM0F2qGHerSty5KKqAOs8HM+Ima6G1cTAqc5qaHJbl3RYltV2Iz0CuMlgnasJ+DgLnNjthBqVM7Rt4q30lqBzix1GviVhYjqj0F84wGmcR4gN2u7dWkmPgN3KiDscbjJqEnLgt7vEBm2xD7UlWNrZSJ4Fnkah1VcBu0ipcQ7TYpVkYJjM+t14Eo525LdiqgREEvUsNNRZxMOo721cPIww22srjdDIs3D58AbFYUSdAAIdHGypEoUG1uQ+8F8TUCzBjX6SZf2fV91pb/5SDAb5f8FESshd6f2q6dDdtRlQj60chXRSH/m7XMezjGHFSahHUO1Ez6bwOUIiIMmZm6O5cHIhd/52SNNKz5Lo6dT6Qq3cZ9kjwOgNJl/zksJZgPBy4ygiYDODMCcIy5amPOnUCVcoQvl7gvaeqKiCnvvvr41NwdXk/OrsD2SCC4110H3p+PbrugTKgT8CDACPHLU9lTMolQAAAABJRU5ErkJggg==") -} - -.list-list .ico-wma { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAeFJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgSmX93C8ODzC6IsVOCVYMjU9oHzf//7w3DgySUGVxkj4soBbABkeadFClEOKD8xB85+9f0Dw9LbexlefHuP1wE0SYRnXt9imAEMOWsJbYJqaeKA1XcPMaRpeTOYiWkMjANAQIJLkCh1dCkHBsQBz76+HTgHhCrbMcy+vo3h3Js7pFXHVKt/RdUYZLhFGZbd3ke5A0CFC3L+xq9WHCUR5ur6Mxx4dokyByCXbKQCViYWvIXQ8M4Fow4gq1U84cUOBoYXtLd0FlKrGCMEuJjZwbjFKQ3ORsYwcRCNrgaXGDJ/6KSBtoMLGars48FsZBokjksNshyyGEzNgCRCfJYTLAlhrkem8anB5ntcfKwhwMXE9u/v/380Cw2Q2SA7kMUYkXvHU0+sqb/940Xdt3+/aBI13Ezsf1U4xJuzLUIasTpgIABAgAEAkufcCgWFyTcAAAAASUVORK5CYII=") -} - -.list-list .ico-wmv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAt9JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08IsLGzg3FnqiacjYxh4iAaXQ2yGDY5ED100kDtonsMzXFKYDYyDRLHpYZYuaGTC2C+QKbxqSFFDqsDuNlAbch/NPMtyGwuoB3IYozIveMJ667W33jFUPftF22iBujBv+piDM0FQdqNWB0wEAAgwAD570Ssrd8AngAAAABJRU5ErkJggg==") -} - -.list-list .ico-doc, -.list-list .ico-docm, -.list-list .ico-dotx, -.list-list .ico-dotm, -.list-list .ico-dot, -.list-list .ico-rtf { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAArdJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZ6eIAAQEBsCMYGRnh+OHDh/SLAlZWVgZmZmbiywFcYPnW+ww/f/1lSAhUQRFfsP4OmIaJP3j6hWHGiptgtoQIJ0NBvBYDCwsLw9+/f3GaTVQI6KkLMtx99Jnh959/cDEQ+97jz2AMExcWYGcIcpVn4OFiYdDXEIKrBYUCExMTOArIcoC6Ah/QAEawI2AAxJYU5QJjmDgvNyuDGlDt1+9/gA4QRDEDlgbIcgALCxODhhI/w417H+FiILamMj8YI4tfuf2eQVaSm0GInx3DHLIdAAK6aoIM1+5+YIBVXTfuf2TQVhFg0FIWQBG/fOsDgwFS8JNUGeGNBkU+hh8//zI8e/mNAeQRFmZGBlEhDkhqB4YQSBwUBU9efGWI9VeivgNYYdFwHxLcmkCfwwA4GoDinOzMDEqyvMBEyEq0A0gqB0C5ARTfIKyF4gABsNgVUPBrCpFUVpDkAHUFfoZXb38wvPvwk0FeGlFlK0jzMLx9/5Ph8cuvDDqqAuQ3SAiXbEzgbMYGpJmQUjQzMIuqAdPInz//GdjZmGnnABCI9sWewCK8FMkqrulSHY86gOg0MGHJIyD5iOaWzmrD3SoGNZ9Q+JU5Dij89ikHsMoRIw4Cv379Ij0XoBsO4sNoYsUHLA3gs3xk5AL0NIQ3F3BxMP/79ecfE6j5hMsQWJDC4ptYcRAA9ZZAdqA0UpB7x1MXHKm//eh73bcff2kSMtyczH9VZDmbsxNsGrE6YCAAQIABAHOPGcK/91HrAAAAAElFTkSuQmCC") -} - -.list-list .ico-docx { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAphJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGakiwMEBAQYQAmakZERjEHg8ePH9IsCVlZWBhYWhB9hjiA5ClZsu8/w4+dfhoRAFRTxBevvgGmY+IOnXxhmrLgJyU4inAwF8VpgB4AsBuUKbA4gKgR0VQUZ7j76zPD7zz+4GIh97/FnMIaJCwuwMwS5yjPwcrMy6GkIwtUyMzODMdkOUFPkA9MgR8AAiC0lxgXGMHGQxWoKfAyfv/5mMNAQQrWIiQmMyXIAKwsTg7aqAMONex/hYiC2hhI/GCOLX779nkFOiptBiJ8dwxyyQwAWDTfuIzngPpIDkMSv3v6A4Xt8gGgHgKLh67c/DC/efAdjZiZGcEIDYRAAiX349Ivh8YuvDLpqguTVhqREA7IlWsoQcQ52ZgYlGV5wWqC6A0BAW0WA4ei5V2C2h600XFxPXZBhx+GnYLaprghJZQVJBREovp+9+sbw+t0PcEKDARAbJAaSAzmS7AYJMdEAcgQTMP6ZkFI0iK0KzH6g8gAUDTRzAAhE+ShhFY/wUiSruKZLdTzqAKLTwIQlj4DkI5pbOqsNd6sY1HxiqMxxQBFrn3IAzkaWIyQOEkNX09i3i7hcgK4RxMdmID5xZDl0NTRNA8gW4bN8+OYC5PSAHAXYACNy57Swefefn78ZmavznGiWCHum7/3bX+vKgtUBUxcerbn98Fvjtx9/aRIyXBzM/1Tlueqz461bsDpgIABAgAEAbXE38hMP+G4AAAAASUVORK5CYII=") -} - -.list-list .ico-pdf, -.list-list .ico-fdf { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7CQa+jf9x8ZGFlZGJh4cKcXPj4+WDoAgQgofQaIGSl2wJupCxk4tNUYuG1MGVhEhbGqERAQADuCkZERjh8+fMhAeQgAExgoBPgD3IF+YcSpjJWVleHPnz/wBEm9RMjExMAiLsLw/fwVhj8v3+AvaFhYGJiZmYFamMAhQLVEyOfryvBu/mpgGuAiqJYmDuDQVGFgFhJg+H72MlHqYWmAag748+oNOBF+OXya4ffjZ0Q7gmoO+LL/OAOPszWDcGokw7t5qxh+P3lOeWVEdCb4+g2cC76fvsjw6/5jhv/AlP5m2iKGvx8+M3CZGwALib8MrDKSDLxudtR3wO/nrxjeL14HLoQYOdgZ+IBZkVVCFJwdf964y/Dz3kMwn0VCjPoh8PXIaYaPG3cx8Hk7MfA4WGKW+xrKYExSjiZW4ffzVxl+3rzLwGWix8Bjb0G1eoJoB/x5847h/6/fDHxeTnhLP5olQl5XWwYGEKYyoEt1POoAotMAS/tMhof0sHDxRNyJEFuqVGorgbPvVfVgiBEjDs9N5OYCZAtgbGTDcYkPSBoAOQY9JCguB2AGEuM7UkKA5CigaS5g4Ob89//rdyZGEkOFWAeCu8FcnCgtVEbk3vGN/ln1TDfu1TF8+06b8oGb6+8/dcVmjcK0RqwOGAgAEGAASrzyfwJnztEAAAAASUVORK5CYII=") -} - -.list-list .ico-ppt, -.list-list .ico-pptm, -.list-list .ico-pot, -.list-list .ico-potm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAdtJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGYkywHvJ0VhCjKzMLCpmDNwmAYwMAtJo0iJiooyiIuLY0uY5IUAb3Atw+e1zQxczmkMLKLyYLH/v74x/Lp1nOHzimoG3pB6BmYxRRQ9sMRIVDlACDBDLWUWlESxiEVGm4GJT4zh+/FVDDz+5SRFE9USIbuOE8OfZzdJ1kfdXMDINHAO+HXzGDyKyK4LiAV/3z9nYGRhgyTCP78Yfj84z/Dj/DYGHt9S+jjg295ZiFBn42JgkdVm4PEpZmCV06WPA0DZjUVKnSpRR5eieNQBVEsDoAQnmLdsNAqGlwMYkZvlDws06dJGl59wHU+DhIUdU0PVZoQj23yxiiHz0dWigD8/ycsFyBbD2NjEkNnDqxyABTGy77CJ0cwB2CyhxGKsUfCfS+Avw/9/tAtvoNlgO3Blwzuzi2pY7h1rZPz+kSZp4z8n/78/Slb1Kql9LVgdMBAAIMAARLzKcEnwqqcAAAAASUVORK5CYII=") -} - -.list-list .ico-pptx { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZyXLA+0lRGGKMrOwMrIrGDBymAQzMwjIocqKiogzi4uLYEiZ5IcATWMXwZX0bA5dzGgOLqDxY7P+fXwy/bh1j+Ly6noE3qJqBWUwJRQ8sMRJVDhACrLI6YJpZUBJokSLCECl1BiZeEYbvx1cz8PiXkxRNVEuE7LouDH+e3iBZHxVzATBd/f83cA74deMQA7O4MmV1AbHg39f3DP8+voIkwp9fGX7dOcXw88J2Bm6fIvo44Ov2SQgOMwsDCzDlc/sUM7DK6dLHAbwh9eCUTw1Al6J41AFUTQOCectGo2B4OYARuVn+sECTLm10+QnX8TRIWNghiqo2IxzW5otVDJmPLg7TA9MH5//5SXwuQLYYxsYmhm4hsiPQ5eiWBoixnGAIwIIYPTjRxWhWEGGzhFiLyYqC/5wC/8hp1eCyHCOxAs3+z8n/D2c2vDOrsJ7l/vE6xu8faZI2/nMJ/P2jYNGsktbfiNUBAwEAAgwA8LXlQoWLfqIAAAAASUVORK5CYII=") -} - -.list-list .ico-txt { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAVJJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkLQSevnqLU05aTBivXlhiJKocINcSSgATwwADiqOA0pAbjYLRKBiNgtEoGHXA4MoFE+fsooulM/twt4pBzScMDY3VmRhi9a3TweIgGp86mFoY+PXrF4ocI3LHJL1own9sDkC2ANkwZDFsjkFXC3PAzL4CRqqlAWyWj6xcgBwFFDuAi5PtH6FGJK40QYwjQGaD7MCZCKfOWVt/5/7Lum/ff9Ekarg42f+qKIo1Z6cEN2J1wEAAgAADALEDt/dBJDrPAAAAAElFTkSuQmCC") -} - -.list-list .ico-xls, -.list-list .ico-csv, -.list-list .ico-xlsm, -.list-list .ico-xlsb { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAiVJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDkV3BAwwofEluLm5Gf79+0cVDALy8vK7oI4wIMYBVAdsbGxZQEccweUIJnokNKAjEhQUFM5hcwQLLS3m4+ODpQMQCIPS54GYkS4OEBAQYAAlaEZGRjAGgcePH9MvClhZWRlYWBB+hDmCLAeceHmdIWZPO5gmpObYi6uIOAY6AISRQ4GsNGAhrgmmp1zegMJHtnzSpfUMGdo+DFYS2ihyzMzMYMuxlbokpQFcjoBZnqLpyWAnpYdVLxMTE+UOwOYIEABZHqfuyuAkY4hXL0VRgMsR/4C+ilJ1YvCQMyUroVKUC/5Bg1SEk586tSEpOQIU7AGK1gxyvGI4EyZNHACz3EfegiFMxR4uTq4jWMixXISDn0FPRInoLEoVB8As//PvL8OLb+8Y9j05z6AjpECxIxiR8yawRfRfWVmZpjXj3bt3QS0iRrpWxzTLhsPCASiJcPKr3QwMIExjMB2pVYwRAuyMLCi42DYKKx9dHCYGw+hyMExyFEw5soohxwbSmgLRID42AJODYZgeqqQBmIG4LEd3KIxPs7qAmo4gKgRgvscXrDA5WBSQlQu4mNj+/v7/l5kJ0WpGCXr0qEC3lFC8/2P4D7YDZ1E8/eS6mts/XzZ+//eLJuUDJxPbP1V28fpM86AWrA4YCAAQYADJhRMxzVK59wAAAABJRU5ErkJggg==") -} - -.list-list .ico-xlsx { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjlJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGakiwMEBAQYQAmakZERjEHg8ePH9IsCVlZWBhYWhB9hjiDLASdeXmeI2dMOpgmpOfbiKiKOgQ4AYeRQICsNWIhrgukplzeg8JEtn3RpPUOGtg+DlYQ2ihwzMzPYcmylLklpAJcjYJanaHoy2EnpYdXLxMREuQOwOQIEQJbHqbsyOMkY4tVLURTgcsQ/oK+iVJ0YPORMyUqoFOWCf9AgFeHkp05tSEqOAAV7gKI1gxyvGM6ESRMHwCz3kbdgCFOxh4uT6wgWciwX4eBn0BNRIjqLUsUBMMv//PvL8OLbO4Z9T84z6AgpUOwIRuS8CWwR/VdWVqZpzXj37l1Qi4iRrtUxzbLhsHAASiKc/Go3AwMI0xhMR2oVY4QAOyMLGBfbRsHZyHx0cZgYDOPTC8JER8GUI6sYcmwgrSgQDeJjAzA5GIbxidFLMA0gG0iMQ2F8YvVSrU2IyxEU5wL04MSlBmYpssXE6MUoCYsPTf/z+/9fZiZgqxk9+LAZCJPHZgk2vf8Y/jOwMjL/7bXLZMHqgOkn19Xc/vmy8fu/XzQpHziZ2P6psovXZ5oHtWB1wEAAgAADABRqMy9QT3WBAAAAAElFTkSuQmCC") -} - -.list-list .ico-7z { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAX9JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7bo012zDEHrZ4YYiDxHDX7z/JLweQDQZZCuOjiw9YGkB2FE0dgM0iciynWgiQazlGGvjPKfCP8ddXJgZGJpISIro4Tsf8/we0g/8fTgf8UbRoYrl/vI7x+0ecLnjY4EyUGFb7uQT+/lGwaMZZDgwEAAgwANHIuc9Vl0AAAAAAAElFTkSuQmCC") -} - -.list-list .ico-cab { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYxJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7quKabQgHtnhhyCGLIavFph5Sv/8kPhFiswCroTgsJUY9SQURNsPxhQIhyykuCdGDnJwQYCLHQpjBMIzNIWSVhP85Bf4x/vrKxMDIBPcNqUGKV/3/f0A7+P/hzIZ3ZhXWs9w/Xsf4/SNNyof/XAJ//yhYNKuk9TdidcBAAIAAAwBVN9K79PuTHAAAAABJRU5ErkJggg==") -} - -.list-list .ico-iso { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7bo012xgetnjB2WBHQ/nIYujiqPX7T8rLAXSHgNjIYuhqqJ4GYBbi9SmtEyGyI+juAGTfU+oIlDTwn1PgH+Ovr0wMjEwkRwG6Y7BGzf9/QDv4/+HMhndmFdaz3D9ex/j9I03Kh/9cAn//KFg0q6T1N2J1wEAAgAADALuKyAid7COMAAAAAElFTkSuQmCC") -} - -.list-list .ico-rar { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAX5JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7quKabSj8hy1eGPLIYoTUM/z5SXo5gG4BhqEUqKcoDcAMR/c11UtCZAuwRQEp6slyALYgxRb3MD7ZUfCfU+Afw/9/tEv+QLP/c/L/w5kN78wqrGe5f7yO8ftHmpQP/7kE/v5RsGhWSetvxOqAgQAAAQYAQvfLEuMZrKwAAAAASUVORK5CYII=") -} - -.list-list .ico-zip { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYFJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7bo0121D4D1u8wGIwGlkcd/3+k/xyANlgdMcgy8McRbM0QIoFVC8J8VkOCxVSHMdCTZ+TEyooDvjPKfCP8ddXJgZGJqISIskW/v8HtIP/H85seGdWYT3L/eN1jN8/0qR8+M8l8PePgkWzSlp/I1YHDAQACDAAtKS/DHmsv9AAAAAASUVORK5CYII=") -} - -.list-list .ico-gz { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAHtSURBVFiF7Ze/T8JAFMe/1QYCRKIDIe1AYcAfLJLowL/grCbGydEJTXBw0qiLA4NxNGFlkX/A0UlJJGEBQzoAC3EzxhgTgz0HPXI9jrahxTr4TZrm3r3r+/Tdu2tPIoTAT8lso9lsrgEoAVA8jLEKoMYaMpmMGABASVVVJRKJeBJZ13UAuAawmU6nayKfKa6thEIhGIbhyQUAiUTiBsC1rusrTgA8VzAY3NU07RZARQQxcQAACAQCO8lk8l4EwdeAp4pGo7QOAGDr5/4AQPoVgFgshng8brIxQM4AXisn6PdaYwHI6gJmNo6tfewe0u+1MJcvC/ueL7dH9tF+O7kqwrl82VGQiQHYZWDiAP8ZoBC+AriV7wCudkK38w/8lQxI0ve3odFoDDuoC2MXmqzMC+003gDASnZ7uVv5PgW+A5imIHK1jicA2sUjuvtLEwk49LvLngs6e4uEVaewTHjxtk5h2XTZ+u4tmg4iQ0XYPchCK9bRPcgO2lRasW7y48WOo758m5ejjWjUYKdiwXjZFiEdPOoBboJbArBvrRXrlllg0y3ytRovEUIGO1P7MPcpfbxNQRpvdfI1MPTmxAAJhI3UeXWamkw10E/lTuX23ZH0/iImkIOWADQDAITLmIRnP/vJ3BlQZYw+H8+/AFShLMorUUd8AAAAAElFTkSuQmCC") -} - -.list-list .ico-bt { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAbxJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDkV3BAwwofEluLm5Gf79+0cVDALy8vK7oI4wIMYBVAdsbGxZQEccweUIJnokNKAjEhQUFM5hcwQLLS3m4+ODpQMQCIPS54GYkS4OEBUVZRAXF8eWMOkTAiAAS4xElQO4QPmJORhivKycDO6yJgymYupY5WGg0yKFcgfgMqjm1AKwA5DlQI4hZCnVcsHvf38ojiK6ZEOqRAG+eKaLA7DFKzUcNeBRQJEDWJlY6BMFzIxMWIPbSkKLYgcwIreIgA2S/8rKyjQN8rt374IaJIzDIw2MOoDqBdGEFzsYGF7Q3tJZeFrFDFzM7DS1/Nvfn7iz4cPPL/8zQWOl99wqhmKjMLgcMh+dDQLIatHFkdUD28sM8rzi2LNh6+mlKBaAaBjGZTm6xTCAy0EwO8hOhDCHobNhlsD4+BxHlANgBsAwNoPRLYGpRfc10UVx4YGpf/4w/GMGlf20AH///wOmeqa//Q7ZLFhzgQqHeMPtHy8agSmVJi7gYmL7B7IDZwgMBAAIMABhJOYNvD7xMgAAAABJRU5ErkJggg==") -} - -.list-list .ico-file { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAOVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gJMDAMMRh0w6oBRB4w6YNQBow4YdcCoA0YdMOgc8Pzbt280swxq9gt8reKU58+fgzqnEjRyw1MgTkMWYBzo7jlAgAEAzk5sMbucHicAAAAASUVORK5CYII=") -} - -.list-list .ico-apk { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAhlJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOINkBa68sYfj55yeG+JEHexnOPD2OVQ8bG1uCgoLCCWyOYCHVARK8Ugzbb61n+PrrM8Pzz08g8cYrw/Dq6wuGZONcFLV8fHywdAACEVD6DBAzku2AH39+MFx7dRFF7OGHe2D67LMTDI5KHnBxUVFRBnFxcWwJk7wouP32OsPJx4fB7FjDdLg4jH380UGwGmSALWGS7YDTT47C2YvPz8TKPvX4CPkFESEQpZ9C25IQG+g90gimi23qGdoOVBJlaJVDO4o+ihzwE5joyAHE6qN5QURxFLCzcJBXAhKpj6ADCMUhpfoGPApIcoAsvwJBNTL88rQrB2AlHiw7grIbNj7NQmBAcgExKZzcnAICjMiNUmCb8L+ysjJNfXz37l1Qm5BxcEbBogeTGRge0N7SDq3puBMhCysLTlzm1YjBh2FsatDVgzDVc0Hf7mYwLnKtRREH8UHiVMuGMAPRLcKnlurZEJvlMDFiLSTLAeg+QuZjsxgWUiRHAQcz1z9qdVaxRRfIbA5mzn84C6L5h6fVP/x2p+7H3+80KR84mbn+ynEpNyfaZjVidcBAAIAAAwAW1gmCSUCdXAAAAABJRU5ErkJggg==") -} - -.fileList .ico-bookfolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmJJREFUeNrs3E9r1EAYx/HJ/3S3mwTb7a4u7kkPguLBky9A8OrVk9A3ISiK4mvwpNCLHj37GjwJ4smjFNZuQTbrYXe72/hM2lrBS6LuhITvDx7SlsIkn5lkMp22VpZlivx9LAABBBBAAAmAADYEMP3woK7Xsiv1ylBb+1J7Us/dhgyEW1Iv25euKcdvrb2xbHU0mE9GjxbpgdMEwK7Uu43toe94cjnZYv23ra1UEO8oAdy1a47nSL0JkotDrx2bffbZVt55dQd84UfdO0HcrewE6gx4z20lD4O4p59KlZ2EK7OuL8cnUnr6HdRFT/CU39k6u5WqOw+pZ360Iz3ZV5bj1WgAHsvAW1bfkfr9Sc8olp0ZmcGaFv0M7FZ9G9QdkAAIIIAAEgABBBBAAiCAAAJICsfIptJyPlU/vn2SY2rmooJIbfZuyLHTjBFoEu+kw9K8zUaNQB09KsJooLLjlZqOPv7xfZbtqk7/5q/PD7+8z4/bV+8WbmuW7hvtMEPPwJNNH4237phog0kEQAABJAA2A9DbuKAcr81KpHys/JXD3+zJW06mZpOv6mj2HcBCdJalWltX8iXX6RdUmAyVO4u4hYsJ2ud4v/dmmADIJEIABBBAAAmAAAII4H9c9+ro9e66c96Gmd/7NrIW1tuL+U7Zwee8yuZsc6lUm2HcnBGY79GG5v6WTbfV6V1v1ghMLt/mGUgABBBAAAmAAAIIIAEQQAABJBpwnK2WSJTMqdlYA76eT8YKxHJ42kyyp38e+HQxPfSl7svHfXgKZST1Vuox/8HyHwMggAACCCABsKr8FGAAiCO50cIM93UAAAAASUVORK5CYII=") -} - -.fileList .ico-folder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAY1JREFUeNrs3E9Kw0AUx/FM/kwwxSagqdVC9649gwdw60rIJQRFUTyDK4VudOlBvIdQjJvWVWPr9I3o3gaaNOH7g0eyCUM+mcybbKKMMQ4pHxcCAAEEEEACIIDNjF/mounr2SbeSyb1UNFYb1IjqVu/JRPhSOq+c3DoeDpa+2Bm8TWYTcYXxfTdawNgKvWytTvUXiC3Y4q1D6hk4QvjniOAWdPXQE/qKUz2h0EnrnRg5aqfh9d0wDvdTY/DOKULl8iJHyXnYbxnV6X6urB0VC3HKynbWgeN2T5EiaO3d/5epVq3MTe625Mn2XeUFzRoAn7LxJtvxD4wsx1FuaaSDtbGL5G07teATzkACYAAAgggARBAAAEkAAIIIIAEQAABBJAACCCAABIAAQQQQAIggAACSAAEEEAACYAAAgggARBAAAEEkAAIIIAAEgABBBBAAmD1gLlZzJFYMb9muQV8nE1yB8TV8KyZZGT/G3NdfH5oqVM578Pzr4ylnqUuFT/ipokACCCABEAAm5mlAAMAvOdJo4EAKcIAAAAASUVORK5CYII=") -} - -.fileList .ico-folder-empty { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABBJJREFUeNrs3MFPE3kUB/D3m+lMp62kpQgEiXDw5CaYuGsUXTxtOOllr3oxcvI/0JiYaEzWv0AvHrzgcU941kQPxuxmEzl42gNshAXRpSC0nbbz871fmdpGBdoK7LTfl/zSMm0ymU/f7/febyZBaa0J0XpYIAAgAAEIQAQAARjNiIVvVv+8GvVrmeLxcJ/O9ZbHIx53Yh2SCD/xuJ/M/kCWk9zzk+nAH/Y3lm6WNpfsTgDs5/F7vGfUteRyAn/PT6h4uMkBYsCpqK+BNo9pNzU04njpfT2xUsr8eFEHvOskBiYlG1CFm49fY/He625qUFalg6vCXH1dfr3F4wqP4ci0D/FechJ94VQ60DbmtpMcNL+kstzo5J8OeJT/F33glKwh5nfchwrWiTuR/oOeBtjKARABQABGM2LShAYrT8ny50jpva/CJTVEvnU00mjKdiiWyG71o+V/yA4WvvnlfKHaayW89u87qMxJ8vrGyYt41ulKiYq5f0kFmmJOsMz9X3GrKW3cEq3nFanBS9X3S4+pJ9H6lkn1niIr+yOfIvq9puKFL54eIH9tmWIq2OCLKtU+rHCDb1uf8dInfjPHc685G1emKeG2iHf4545a+5SlttbAuu2QoPn2CKniPLnDVbzc6xvms3j/BP03/5jKFaJDnqbd9t6diPdFEZEQmLI3RgPnZ6iUmyUnPUbrb+6R5aTJTjEqvw5dnDPH8gv3KRkHXkMbE7N5FGbp498PDJ5knpMZo+ToJYMoqMWVF7TJWbibaayypzseL7yZ0FAwDh27ZrJMc9HYXKxOWW2nKTs+XRUPclTgJTNfVJSI669iGry+s93TSNcXDMk8ybqgZ4JcRzfgfXh52aBtlDKUOTdDBeeEAe5WPAMYrn2CJ5knU9ROjpjpKlCCJ1NY8FLOau2Yqdib86ZidyueARQAgZA1zjtygeKHJ8zfghbiCag3dKEBLwQNq3E34pnrLj8/qvPc2wpOz/HrVNngFoZbFoELoQRPMlRgg1Kuhheufyp7hvHGuw5vbW62ugYKRMZbpcKbauWVKlyfZYXFJ8DbqQpLG+O5mnu8J7QmVZix6tc84O0AaHYbjuTlDKX4qOxYv7bmAW8bwBoiVfu87DngNQ0YhkznAk9naWvq8QROABE7AAqY7Hn7klRrVYDXBKCg1d8wAN4ONxO2/RLwms/Az3hnzS4D0UIGAq8NQOC1AQi8NgCB1wYg8NqowvL8Qh4CIVrIQOC1Aejbx4DXDmDJHoXCXm/lEAAEIAABiAAgAAEIQAQAAQhAACIACEAAAhABwO8CGOhKGRJNxpZZYGmlXhVz7wiIzeEZM6X+kOfCk8WP75/56ysnMaV3HQHj/cWvvyj8I24UEQACEIAIAAIwmvFJgAEAn5Nwqgtf3PMAAAAASUVORK5CYII=") -} - -.fileList .ico-folder-unempty { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlFJREFUeNrs3M1qE1EYxvH3zNcx09ovaUot6FIUNyJ4A+IFeAfRXINQFIS60IV7Vy7cuPUaXEtEkG68ANEYjaZNE+YjM56ZhBIxlIykMVP+D7zMJnCY35wz7zmBRKVpKuTfY0EAIIAAAkgABLCccYp8+Nf7+4t8L3VTL+c01mdTr0w9cc7IRLhp6oW/cU0s1z/1wdIk3AmPmo+iXtM+C4Cbpt7o85c9K7udJDz1AZUpz6+KAayX/R1om3rtLW1fcs+tznVgpVT+8FSRs7BWi+XdbNSeuZXqrl7ezhbW3Mfvtj6WtwsbvLuOXt/1lrb+C97xTPzZuOeZ62NTNVM7pdk+6HVxKxfEnkPTOGkGZk1kz/W38iepLK88UzBNTMULsQ+sZx0lfyWOOlgQxhJFA3bJY3FdW7TnTATcHHWU42R4K1ceoDaWg0/PJwJylJvlUa7XDyUIYjnqBTJot9EZy2Gnl69MrR3xK95kwAxv7fpDWcPrr6zc2ht+H7D/9A9AlvAslnDU/SJxvy16EEm/UUPlpNOYqX7LFaeyMQRUSWr2f6ksX7wqynYRmmYLaiZa0PkqmZ1jDsOiV6s5oqQhOtMc38yLLzMLD74N34HKUqgURlQ0kVkEQAABBBBAAiCAAAJIAAQQQAAJgAACCCABEEAAASQAAggggARAAAEEkAAIIIAAEgABBBBAAiCAAAJIAARwIQCTdBAjUTAjs8RKlXoXdFoCYjG83EypRvaL9TtB98fb8PD7DZb01EkM3gdzva34I26aCIAAAkgABLCc+S3AAB9MnrCxlmzHAAAAAElFTkSuQmCC") -} - -.fileList .ico-fromchromefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABCVJREFUeNrs3F1oW2UcBvDn5KQnbdYmTfPRLMnafdCLUcfGIhM3O2WgOPFiu/FCZBY6BooyvJAJiqNTL4ao6IV4oVJERbwYKoKiBTdlaLeVIV3Z/KhzW9O0OUuWpGnaJWnP3vd01LUGbba2W9LngT9Jc5JzyC/vef9vT0MVwzDA3HwsJCAgAQlIQIaABCzPWG/8IX2ivZzfS4eo95foWBFRXaIOWStkIIRFvbsisB6qZl/0gxmT+eDV1PCLuXRMrQRAr6gjNZ4mTa0Sb8fILfoBFTHx2Zw+CMCOcp8DVVGf2OpXNlWtcC7pgRWLYn545Q74qubwPmhzeu+MJlIs7ZEDi3LgruDhW93Fbqu9/gWbs1HOSrcNUEn1PKmJ25ellajgUkAuAB4EHrQ6N6zV9tuGl77QZ47ATs3hE5+kH4pa9d+Ne4HiWB1egL1MiYFXuCPWgR2yoygWY0k62PQ6IIdKiWwi3usdheGvcgQkIAEZAhKQgMsnC3I5q8VVgz13+dDqseO3xDi+P38FRy+lMGVwBP5v1jir8UpbM+Ra/KMzMRNw36aVaN/QyBE4n4QcGk5GR/HWqcjMiOu/nMWBe0L4OTKKs/EsAYtlVZ0N69016NOz+OlSetY2CZoYz+O17c0YyebxzqmhioUsGVCeqk9vDmBH0z9XgLsvJPHe6eisOa/z+EXUV1vxyFoXOu9rwv7uvxAdy1UcYMlz4KPrGrA1UIc3Tkbw2Jfn8HbvELaHnNi70T/reZFMzjyVXz8REaN0DE+0eityBJYMuFMAHvk9juODaRTEkDt2MSWQBvHQahfuDTqKvkZ25I2+WgLKuMVpeT41Meux3uEMvvgjjqc2+eGx//uibCY3BU1VCCgTnyiY674bU6up+OysDl00jufuDmDu5cWwvxYDyQkCynwzkMCuFjfuF02kUYy2h9e48OHOFrMrvynmO7kufFY0mWrr9K7ldlnfisU1u7DI1wKwoaYK+8OBmcc+7o/NnNaHewbx/JYQtoUc5hypqRZ8fk4350oCisilSlffCL76Mw6vgJTdNpObnNn+a2wMz3QPYIs4bSXe6ZGM+RwupOckMV4wq1iSYp787u8kr8YwBCQgAQnIELAcAO1qAds8UTRoE8sK8JauSFsUA2FXDA94I+JWh2aZXlD3pxvwQyyEX+J+ZCetBJybVkcCbd4hbHVHUWvNF90ua9/afvQkGvGjHkDvFd/yBlxlz6DNM4QdvsF5n6ZyRMrXyErmbTgaC+KYHqwoQPkNVcPRvIHd4CYiv6HKLsxlDAEJSECGgAQkIAEZAhKQgARkCEhAAhKQISABCUhAZl6AujFZoESJuW6mS8APrqZ0ELE0PGkm0iX/sH4wN3pZE/W4uO8nz7wyLOpTUS8p/EfcbCIEJCABGQISsDxzTYABAF5pTWwmgxlgAAAAAElFTkSuQmCC") -} - -.fileList .ico-documentfolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlZJREFUeNrsnE1rE1EUhu98ZCZOTSbYpB9GKgguBHelP8BFodtuuxLyJwqKovQ3uFLoRpeu/Q2CgktxJdgPTChNQtskTTI9N1hw4cKkk5nMzfPCyc3qXu4z57znTi7EiqJIocllgwCAAAQgABEAAZhNufqj9fmpCXupSbxNaK0DiX2J164hibAu8Wbh7iPleMHUF4sGl9Vu8/hZr/XbMQFgReLjrfKa5+RkO1Fv6gtaYnx+uKQEYC3rHuhIvPdLq2u5hTDRhS3bGj28rAPc84qVTT+s0IUn0LYblHb9cFm7UnpdWDqwJ+MLCd2Kq5k5PgQl5RUWr0sp1WPMK6+4JE9yRVlOLkMJOJTE68/EObCmO4plR4l0MBPfRCpplwGvcgBEAAQgAAGIAAhAAAIQARCAAJwbJXqpdHlxos4a31W/04zn6bt5VVxdV26+MB8Z2D76Fhs8rWG/o1pHX2XO9nxk4HDQHY1hdUPlgsUbz9f48UkgXqhO+5fK2/eVm8CdcKoArxUHvL/VOf05GvOFe4mXszFNRENMo5yN6sK6nDVE40s4LpUfbv3TE8lAzoHjddJJsg2AMw6GEgYgHhirB86qBeCBlDAA8UA8EA+khAEIQACirADsndXjn/O8YX4X1teQo5u0wy9TXcPYDLy9/FjZjj+9zcjceg1jM9ALyurOgyd4IAIgAAEIQARAAAIQgABEAAQgAOcbYD0a9CExpv4wq2uA77rNugLiePA0M9G+/j3wZa/d8CR25PsKeP5LxxIfJJ5b/BE3TQSAAAQgAiAAs6krAQYAXiGbDBfBqf8AAAAASUVORK5CYII=") -} - -.fileList .ico-fromphonefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjNJREFUeNrs3E1O20AYxvGxndgQq04ESfhIxaalgjVnQOq2264q5RJIIKoiztBVkdi0S9acoVfox6qoEQkgjNriNLF5J1Cpm6glJHEm/j/SqySbsfLzeGZsTWIlSaLI4LEhABBAAAEkAAJoZnJ/fwg/vjL5u9Sl3o3pWCdSh1J7uSnpCBtSb/3ldeW4hZEfLOn+rkWXje12eOpMA2BF6mi2vOI6efk6SXvkB7Rk4POKVSWAddPHQEfqvVdaWsn7xbEe2LKt3skzHXDfDSqbXrHCLDxAXuQKpS2vuKBHpfRmYZl5XXndldJTcM2Y5UOhpNxH838upVSXMW/coCpnclFZTt6gDhhLx+tMxDqwrmcUy07GMoNN451IJe3LgFs5AAmAAAIIIAEQQNOSzvPAJFY/z7+o6/BExZ3rh/WA3IyaCWqqMPfk9kFdFgB/nH1Wvy6+DueOWE6APhl6i4pffpaNSzgKvxnR5sQCxt22EW1O7hh4l/Lq86G00/p0zCzMMgZAAiCAAAJIAAQQQAAJgAACCCABEEAAASQAAggggARAAAGcwqS6tSPNLRlGA9qO23cz0L/2y/RD121mBtALHvfdHzhor/SCWnYA/fmnSv9YMAq/S0+MHtibPcFbkjZXMzQGWrbyy2u9YhZmGUMABBBAAAmAAJoK2Ey6HSTumTuzpgY8iC6bCsT74WkzyaG+lXvdvmq5Ui/l/SI8/5WG1AepHYs/4mYSARBAAAmAAJqZGwEGAN+Yh/QHkxJLAAAAAElFTkSuQmCC") -} - -.fileList .ico-mix { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAhFJREFUeNrsnL9KA0EQh/fukjtNNBc08S9oJSJo5TMItrY2CnkJQVEUn8FKwUZLa19BO7ESKwtRTBASEU1Mcs4GtVFRA5u44fvBcOF2YG+/m53Z4SBOFEUKNS8XBAAEIAABiAAIQDsV+8mhdLpsy1pyYrstmutabF9sK9YhgTArtpMcmVKenzA+WVR7GS0Xb1crpTuvEwBmxY66M2O+F5flRBXjEzqS+IJwQAnAnO050BM7CNLDY/Fk2NKJHddpvDzbAW77qexcEGapwk1oIZZIrwThoM5K7avCUmV9ua6L6XI7as3xIZFWfm//+1Zq6zFm008NyJscUo4XtygA6xJ41X9xDszpiuK4UUsqWCd2Itl2bwNaOQAiAAIQgABEAAQgAAGIAAhAAAIQARCANqgl34Vfnu7VY+FCVZ+Lnx+gK1TJzKSKd/cZ9bU6Ah9uzr5cpJa+r8dN+1odgfVauXHNTMx/GitcHn+Mm/QlB1JEAIgAaGkRcWNdql59biT278ZN+1odgT2D08r1gq8fQO7rcdO+puQUT5ai1PgMe7EJla7OyYG0crRytHK0cgiAAAQgxxhaOVo5WjlaOYoIAiAAAQhABEAAAhCACIAABCAAEQANA8xHtSok/qg3ZnkNcK9czCsg/g2eZiba199ENioPBV9sUX4PgedXuhU7FFtz+CNuiggAAQhABEAA2qlXAQYAvkAprthin2kAAAAASUVORK5CYII=") -} - -.fileList .ico-musicfolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAexJREFUeNrs3L9OwlAUx/HbFopCBKKAKImJg4OzPoCLiaurkwkvYaLRaHwGJ01cdHR28An0EYwjCREYwMTI33qukcVJVHq59ftLTiAMtP309B5yB5wgCBT5eRwAAQQQQAAJgAD+A8DW/e4kX0tZ6jykY1WkLqVOYhFphDWps9TiqvL85NgPFvS7pXazut9pPXtRAMxL3UznlnwvLpcTdMb/2LpKJTIFJYBl13I8T+oqkV1Yiqcy4a59rvNx82wHPPXT+c1EJm/sBGwG3I4ls3uJzLxelYydREwmqy+vh1J6xJZs0RM85c/MDR8lc+chdeynC3Ini8rx4hY14EAar2f+RurfT3qiOG4QygSLWvQamDf9GNgOSAAEEEAACYAAAgggARBAq2JsS7/xdKeCwfh2U9zYlJpd3ohuB44TT2fQe4t2Bw6TW9n68++sP96yBjJEACQAAggggARAAAEEkAAIIICRTCjbWc3Kg+q+NujAnyaqeKF1YJS72/o10HR3M0RsAPSTuZE+Zw38knRpnQ6c1JjubuunsOnuZogACCCAABIAAQQQQAIggAACSAAE0DLAWtDvITFiPs1qGvCi3awpEEfD02aSS70jfdR5qftSO/K+CM+3UpW6ljrgHyx/GQABBBBAAAmApvIuwAAZ2pRkuq79SQAAAABJRU5ErkJggg==") -} - -.fileList .ico-picturefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+tJREFUeNrsnE1PE2EQx2d3u7vttts3WlqKKYrBiO8Rj179AF49mfAlTDQajZ/BkyZc9OgX8O7BA0ExctGIECmlon2DvnedeaAQA9JW0227nX+YLIGQ7v52npn/PLtBsiwLWP8uiQEyQAbIABkgiwEywBEAmH93d5CvZR7juU2f9R1jAeOJyyGJMIfxzJuYBUUzev5hVqM2Wclt3q/mtxQnAIxivPZEkpqi4uVY1d4vWxlAD4wDApyXhxyegvFSD04kVW/A3tonS+LmDTvAp5o/eksPRPt2AsMM8LbLCN7TAzGqSn07CRd2Vg2PDzGoxU4OCz2EB5o51lpK/TsPjMeafxzvZBwkRR2iBGxi4tX7fyPJP1FHkWTLlg7mNFENjPZ7GQw7QBYDZIAMkAGyGCADZIAMkMUAGSADZICsjtXzh0r1SgGK6WU85u25IN0PvtglcXREBtoJb++G5fEzPzorA0n+xBxo3t4+/KkUUlDYfG/rDbOhBlogya6ewyPp5oTzamBbvFYD6qUsNOolUN0hUDQvN5FO1ajuQD61KI4tuQNJ8I1fYICdiOpVs1be65qaCaVfX6GcWwOXOwBu/3A8Ye2bD6RCT+EJnUZYpwQ0M34FZEXHZrDBRrqD4icO1GAOJMn4peDvmgywbe1w+0F2uXHZru7bDgt2f36GRm0XVCPCNbC9JGwWF7EOLkF27e3BT1VPCIzQGQbYicgbBpM3oVpMoY0pCxvTqZejzt3Ev6GMdemmyGaKkfOBiurBRjLdsSmnJV/KfhPwjs7B5ugB7MYzFtIfoF7OYf0MgieYRNPtE1lH42KjWoBKPsUA/zbjig0CSUKrc/XIMm/tvBjhGdj+8sb5AKs7Gax76T+Gfsom3YyDRh1Yko/srtCI509cP7HOCQvk5Ays7W5DMbOCy614eNGKSm+9C1BkoBXVACNyDnRfXCzN/MYiKLoPAokbA/n+om0Ay9k1AY8ageaLYQ2bQssSPqxxtRKOcevYIFahkFqCImYTZRs1CzN2eWBf/rQHIE4dxcwnHNM08EZnj7Uq1I29mHme0BTsbK1ApbgpvKI3ch6N9dho+0DasiIYtKlKM++JoxHOwubENRCGhEY6abAf29h2dkZ4ui28Y7pCt7fKuQCpOfS+zq4fjImOWMLUGFoqpJdF2HJRNk4kPc1A8mVG+KzwePbt8gTAh13bERlIXdcYmxHhVPGbCQyQATJABshigAyQATJAFgNkgAyQAbIYoN0AM1ajziS61D6zDAF8UcllgCF2B4+YoRZoQ/VRtfBDw7iD38cZT0eiZ66vMB7wf7D8TzFABsgAGSADZDHAfum3AAMAO2tj3VmRR4EAAAAASUVORK5CYII=") -} - -.fileList .ico-videofolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABSdJREFUeNrsnNtuG2UQgGft9a7Ph8R27KQyLVSgCCICBSTENQ/ALVdIeQkkEAjEM3BFpd7AJQ9RiQsqJGgJIIFapS0hjp3E8dler73MjO3QSNheO+na3sxIv7KKvPNrP88/x00Uy7JAZHbxCAIBKAAFoAAUEYACcDlFneWmyr2PFvFZdnB949Be+7ju4PpSdYkh3ML1dWh9E7xa8LlvZnU7G+1y/hOjUvC6AWAK1/eBZE7z+vBxLOO5b6ig49NjaUCAO8vuA724vtXj2ZwvFHN0Y8Wj8Je37AC/0qKp9/VYSqLwDPKBGox/rMfWyCvNLwpjRNXw52e4KLRuLE36EIyDFlkdHqW5pjFfaNE0fpMZULy+JTLAHhqeuRB54A5FFMVjORLB3FiJpOZ9DKSUE4AiAlAACkABKCIABaAAFIAiAlAACsArI4szVLJ6YDSOwagXwGyVwTRq/DsWxQOqFgbVHwMtlAYtuNqf7AhAZGR1oVnag+bpHo0LR8I12xVerfJTbvwG4tchkLgOiuK9ugA7zRLUDh9At9Ps+xPVD3okgxaWYkiqHiV6CK4Kva4BRq2A6xCv29A4/othRjKvgy+QuHoAW5V9hLfLgAhccPUm+KPX/ueTygAkINgkQHqTwdURYM9sQfnvexBeew3v3bg6ANvVA4T3K1/r4QwC2EKXZvcoKuCP5fC+LFTyv0AH/SbpUtAn6pGs+6MwBYjqAF4g/gJEsttTwHsGIx7x2MZbZ1ZLOs3WqdsBWlDN3+egQJYXSm1eUJ+C1vsq+Cgqo85q/gE4PSN2FCD5rm6nwT4vnNm6JK0KRDPbbJGkm/ZwJ0C0kMbxQ74MJV8Zm36cPvkBaoXfOMWxe5yDiRf5unHy6L/80U0AjcYRpx/DVGWsn8S0hSyp/PRHjrR2hPwpgaTPG/WiCwHWj/pRN7LOx85WwMHEuYTWSJF2shl6ODL3v6xj9wEcRkgtlJzu5GN1Ut7/CY/mw4mf9QVXzu3lKoDk4Dnx1CIzRW+qPCoHP6N7G+0XqV5+di8XAbTwwc0zhz+zG8AyrlbYHRNMtEG8Mh1LZxwCeHnv3li2I6ziriPsGVrHqI6LDaGmQXhM8m11jQtb+eIC9PXfnjeN6kz3BzDPi117h9OgkYGKeogoXl/IfQCHLadpczSypuj6LUy+X554LDuNk3N7uQqgFkqddWLsOngCkci9d3bvpEqnXTs4t5cTojpngSvg1ULQNeoMsZ9Qj4AdXgNVj0Bw5SXbwaB5+pj9K+1Be7mymRBI3OCf9aM/x9a50ewbCO+mbXjUrW6UHp3bw5UAqWus+uNcr1YP7l9ak4JaZGR9pNvpzrTD/UAFrWubUxqavtWLf1wc3uEu18qkk3Q7lf/NCWB/cBRdf5PbWeS3KljnjivPJtXI7eo/rIt0jktxXAOQIxceNc7pvDq3uUp7d6FVfmIvOqPV0Rj05PFd6DRPWAfpIp1zeRaYk9CQPJ57l2cZPBgq/M4NVy2c5uG5R9UxIQ5S7QZds9nv82EtTHlkb1BxUCs/srY1F8ubO8DhcY5tvM1W2MDIPBycT2rL05gziIk1jznnLAvxageB0HJJHrAbmAx3WmXo4fXQ0ihAeHwB8NGrHeEsWmYAFkUW6g+uCQzVvAFYHpG3swSgABSAAlBEAApAASgARQTgfAAWra4pJKaUAbMiAbzdLhdBIE4Hj5ih3KFmwudG9UjD9SFeZwSPLcnj+g7Xp4r8I24JIgJQAApAEQEoAJdT/hVgAK5f795U9dlNAAAAAElFTkSuQmCC") -} - -.fileList .ico-sefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA/9JREFUeNrs3G9IG2ccB/Dv5ZKLuUSN1miMq3/atVU6aauu615so6ulY+2gK0ihrFhwZRR801LGcLJRcXtRxmAwBhtzyFYHZUPYi/XNBlspdK3rnznHWitpFfvHNkprUqOJJrfnuZYiYiA6tRq/X/jhn3uSI588d7/nzqBiGAaY2cdCAgISkIAEZAhIwKUZa7IDgx0HFvtrqRP19QLt65aoVlFN1hSZCJWivnD6yqBq+rzvzIiNF0SGB96PBu+pqQDoEdXuyCnUVJt4OUZ03neoiBOfPTMXArBuqZ8DVVFtdnd+oc2ZuaA7ViyK+eYtdcBmLcOz3Z7pWbxNpP5c0bzs+GPL1v/7FG9adfd79sw8eVZ6aoDK8PlaTXz9QJRsswWJBjbEf1tMeBB40NJXwJqmPzW8YF+XOQOPaRm54p30QlFtiUdfnzvAjOLKOXiWuJh4E4tiHVgnO4piMRakgz1aB0SRKpFNxPO4ozC8lCMgAQnIEJCABFw+mfPbWT69FNW+d5Cl+TAWC6F7+Cx+H/iGgNNFVWzYln8QG7J34BnnevN3ofEhE6zN/y5ixjgcajqy7D7cj9wm4OR4HWtwcO1XyHOsMn8eivTjzmgP7BYduwsbsDlnD1p6DuHu6HVE42OcgZOjW92oLzsBt+bFucAPaO9rRnjigblNzridK4/g5bxa1Je2ofnvakRiI2wik7Mt/20TTx6qJ/xHn+DJjIrz3o+9x/DrnS/NQ/dVMZZdeEo2Zu80oU7d/CzhmJ/7PzVhN2S9xi48NTlpK3F6oBXHqzq5DpzNg87c/Q7lWduTGpvKDWTWM9Af/NNsEg0XqxAcD3AGzjQVOW/AoqioKWlKOKamuAmfb+lLeqYuK8ASVwX6Hv6FTdmv463Vn8CuOp9ss1nSsKeoEa94a9F1/xezeAhPiVzrfXSlGvtWH8cWT425aO4f6ULciKPQVW5eoXQMtuPkjUZeC0+X2+FuqBYNLdcO4cXcvXgpbz+KXBvNbTdCl8wmIwF5MyFBvvUfRuWKXbg8dAoXBn/CH/dO8m7MTBIY6xXrwF4wvB9IQAISkIBzEodY1lQ418KtutiFk9dXsF4vwWZXKZ7Ti2FTHj1dz9gtnA/9i86wH6PxKAGnZk1aAapc67DJ+Sx0cek23XZZe42t6Bzx48JIN/4J9y5vQK8tG88LtBfSy5I+TOWMlNCygrEwOh5eFXUlpQDlJ1SNjKJydoNZRH5ClV2YyxgCEpCADAEJSEACMgQkIAEJyBCQgAQkIENAAhKQgExSgAEjNkGJGeaxWUACtkSGAyDizPCkmUir/MP6h9HQoCZqn/jeS56kMiDqe1GNCv8RN5sIAQlIQIaABFya+U+AAQBLjB3QxtGdmwAAAABJRU5ErkJggg==") -} - -.fileList .ico-access, -.fileList .ico-mdb, -.fileList .ico-accdb, -.fileList .ico-sql, -.fileList .ico-db { - background-image: url("../img/ico/ico-access.png") -} - -.fileList .ico-c { - background-image: url("../img/ico/ico-c.png") -} - -.fileList .ico-cpp { - background-image: url("../img/ico/ico-cpp.png") -} - -.fileList .ico-cs { - background-image: url("../img/ico/ico-cs.png") -} - -.fileList .ico-js { - background-image: url("../img/ico/ico-js.png") -} - -.fileList .ico-fla, -.fileList .ico-flv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAz5JREFUeNrsnL1vEmEcxx8OOE1LEUyKthATgqYJ6eKEM8QRZ1+WmrropJs6mLjof6CJL9HFl7nMJY7SmLg0JIaBRBKrkBhDW2Io3Hm/B69eT6D3Qjm4+36TJwcctL1Pvr+3p+F8siwzyLoEIABARxUYdrJcLovK4YGyVpQVd8tFxxOJp8rhltH3nwiHrQFU9DASidyNRqMsEAi4Al6lUmGNev3mfCzGzEC0CnCV4AmCwCRJck3YnV5YYN+3tkYC8bAcOE/w3KZjosghkhOVp09QRByE6OkqPAqInm9j7EJEH2gTIgDahAiANiECoE2IAGgTom/YdpYyC8upVMpVcBqNBms2m6Y+k06nfVZHOddJme1ZKBTqucf3j8ugx7VaDSGsVTAY3N8Y0Uaf1Y1lzzlQhUjqdrv/uW6QEwGwD0QC1Ol0+NEsOM8D5Bf/N5S1W3UT78AfK9dNvf/U61d9P69/3Q5ECmVa+jBGCBuU3+/fd6IZeI4CHJWDRgmRoGmrsRGImES0MATBtAMBsA9EM//GAMB+862urUERsQgRDhwDRAC0mzOBAACdHQed+sWHjXST1mjDgW5z4LQ4DA4EQAAEQAgAARAAARCaqEb6KEfBcTbpcOC0OdCuOyZtBIQDARAAARAAIQAEQAAEQAgAARAAARACQACcIg39tuaXbA53JlO0VFy3/m1NURQ9Da/dbiOEj1Jj3dIP5/MscuVy33P1R4+ZmEzy81+vXhv6c44vL7PY/XusU6+zb7fveAegqkGACKARzVzIMGl3lwViMQ7z9+YmqrDhP3h2loWyWdZcK3AHEky0MSZE8Eg7xSJrlUpsJpPhUD0VwmfevjnwnGD8fPHScPi2PpZ4CNOR8ip3ZKGAHHhoS6XkSFo768Vei1Gt8kVQPQXQcvjmeuF78sYqX3q4BBMAhxQPynfktF/v3h94PfH8GXehEwCnpoioxYLynlZqLqQ86EQxmUgH6ouMCkrNeXq1NkrcgU4Uk+GbCflLXXFvz9PjXjsYlJYKa35LISyfO7tBd7Pw4u3i6Zr5tadSn6yH8OLixY4kf/BVq+fZ9ra3nDg3J8nJ5GeWiOcshzDkwlEOAF2mPwIMAMxjFWM7SdA6AAAAAElFTkSuQmCC") -} - -.fileList .ico-htm { - background-image: url("../img/ico/ico-htm.png") -} - -.fileList .ico-html { - background-image: url("../img/ico/ico-html.png") -} - -.fileList .ico-java { - background-image: url("../img/ico/ico-java.png") -} - -.fileList .ico-log { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA9lJREFUeNrsnE9IFFEcx9/srlMbq+2fXHZdD8pG0WIHKQ92iP6QYhRFtzyVHiI7FJ2KIAiMTkoeMoSwTnoqhKLQQ0kHgzI8FAaZGIiu7JKKRVuLO9P83jrjpOu4M7Pu6rzfF5aZffNmYT78/nzfWxhOFEWCMi4bIkCABZVD6+Lo6CgvHW5LnwvSJ2SVhw6Vlz+UDi3Zzt9ZUmIMoKQ7brf7hsfjIQ6HwxLwxsbGSDwWu1zq9xM9EI0CbAZ4NpuNCIJgmbQLBINkJhrNCcT1amApwLOatvE8hQiRKH3txCZSQIhMd+FcQGTexpiFiD7QJEQEaBIiAjQJEQGahIgATULktLazpLWwGA6HLQUnHo+ThYUFXfdEIhHO6FLOcpLW9sTlcqWjh1vmstb55OQkprBaRUVFysaIOvuMbiwzF4EyRFAqlVoVdWtFYt4BTsV+5AVGyO8zDBEALS4u0qNecBsO0OiD5VNyKqu36vSCZN7GAES73b4qjVdGJdZADckAIRIz1UIEmCVEgKbuxpjCOgW773ojEAFmgKjnbwxmbYzm+nYp+rIx10zbmGwgYgpvMEQEaLZmIgIEiAC39FJwq9uYQrsCtDGYwggQASJAFAJEG4M2Bm0MCgEiQASIAFFoY9DGYApjDUQhQASIABEgCgEiQASIAFEIEAFuWmluJnQ8GkBCkrraI8YAgnieZxpeMpnEFC5YCudKh2urSP2xg+TW3SdrzvF6iknd0QNk/74KZezbxDQZePORTEX/35wNBX107u7KsnXnWgLgegIgFxvryOzcT9LZ/ZxCAKBnGmpJS9Np0t3TT8YnonRuuDJImhrrKbDWth6S+JOk98tz2zqf0t9hqgtDNCUSSQneCyWCAMLjngEK6mzDIWUunMMYXAN4ILgn/f0vjXamIhCiB1Kx//VwxusfRr6S8+eOkKql1IbI7JdSdaUAZmtbrzVroJY87uJ0FM1krl3TS+Net0sZG5cikKkmspGC2qluJpD6UAeZATg3ny74oYBPaRRqlQXS/+7Nzv9aHgsuz4Xap9THk7UkXFHG1lIOGgA0hZrqvRmv11TvoVH1+ct3mrrQKDLNdW7n8w5v03Rh8G9OJy/ZkFO0qcjNQk7PvldDSqPofTZIvSJcA2hqGwT35Fuaby66dP2+mIulnGykM6nv5RDttGaN9Nt3n+i8emk8lzUQlnJd7dc4QwCv3nyQEkSO6eWejROFjntX7IZSuDzofQ8vp2HxdfHwzPDsoYBn2HAX9nldJwRRHJyJzVf/TiSZisQdTl4I+H0ju3zFxw3XQNQW6cIIkGH9E2AAawxpPqroZK8AAAAASUVORK5CYII=") -} - -.fileList .ico-mht { - background-image: url("../img/ico/ico-mht.png") -} - -.fileList .ico-php { - background-image: url("../img/ico/ico-php.png") -} - -.fileList .ico-url { - background-image: url("../img/ico/ico-url.png") -} - -.fileList .ico-xml { - background-image: url("../img/ico/ico-xml.png") -} - -.fileList .ico-ai { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABLNJREFUeNrsml2LE1cYx59MZpKNSTYb2tUsG0trqsX0YrVUL1pvpNSXFgr1poIgpUVEP4FXgvQ72ItCL7wuxStxlQVfSxFBS+lCXSPupl2XTeu62d1uncyL5zmzEyfZ3UlmEpJJzvOHw8mZOSdkfjxv52RCpmkCyb8kQkAAuyrZ7ebk5GSEdedZ+5q10X556NFs9nvWnW12fmpw0B9ApgtDQ0Pn0uk0yLLcF/CmpqagND9/ZnjrVvAC0S/AbxGeJElgGEbfuF1mZATmnj1rC8RGMXAY4fWbopEIh4iWyIYXKYl0EaLQWbgdEIUvY1qFSHVgixAJYIsQCWCLEAlgixAJYIsQQ27HWWwvbOZyub6CUyqVoFwue1qTz+dDfrdyfSe2t4dEImFZT+g1l80+F4tFcmGnFEWpHow4vc/vwbJwFmhDROm6vs7qNrNEArgBRASkaRrvvYITHiB/+DVXdh7V9bwFlsdPgbH0F0jJLAwe/qEjENGVsdW7cTMAA5VEtH/+4PC4VbAex51QOBzmzSu8wAFUn45bDzS0o2bcSYjOeNhTFmhWVqBSvMk/x/ZYp+w4xuudEp6+96wFqjM3wNRVCKfeBvnN93mPY7zeSSFEL39jSEFzX2Vkf02vTl9vav3yrXO8tUP1ZU3gAeqLT0FfmLKyYmZ/Ta8//5Pfb/jQksJbu9QsRDlI1heKJEF+Y7f1w1iPY1Nd4vdjY6ddvyN+4Lu2/66eSCI8zk1PWG6b+RBNyTYpa8zdeILPC6K6DrDy911uZZbb7qt1j7Ux3sd5bnrx01HehANYrfUcFlfdrzosspM1Yc8ANJZnQZv/rSbm1cQgR0zEeTifADqtz1Gi2Fl3XZZzXG+2pBEDoGkwt3wNxK776uW8zuebwXrJqWtlTGXuPhir/1bHS9fPNHZ5Nh/XbQZbKAv0mxSClky6YoHG/wtQmf3VShTRFAx+dglC4YhrrVi+chLMl4t8Ha6XBtLiWqAzlkVzn7vC45DZ/Wjuiw1jp6AAx9eDaSAn6CC5cccBaqXfq/Vc5J0j3IWb2peyeTi/vn4UDmC1lmM7jOjOLz2t5fPtncnMhHgAnafOSvYASPGMtx/L5uM6XgZ1+LQ6EADtU2fUwHtf+foOe103Tqu7D3At+MvbPqj+ceRVuA7XByWZCPd2llcVCgXXt7Po/cBePo0hgCQCSAAJIAEkgCQCSAAJIAEkEUACSACFk+v/wvKd0zB9hwBB/rY/gKioLDbAlxq5sHgxcPjgBcgevwzx3KFab9g2tuF1AuiMKYkMB2VUlmHLWx+TBXoVWhfCW3x4iYNEoATQC8B3D8HK42uwWrReKo9t/4gANqsYc1lJSXB4hrrC+2T+GAFsVluYtWnLc6A+L/Dxf8VfONBYgGNhYKo8jHWx7RYozLT1YFdn7hJAV/ddi3WzP5/g7msrNXYSkruPgRS5SC7cKPvasc8pO5nEc4cJoFvyQBfGmFcvjIfqQoEDlpSBwAF0fbno0Y9H9AisSCEQU0hGhbix65urYV8WaKZ23dMN64tEhMefPbXzvu8kYsZHP9XAuKGXn+yFypJYBw9K0jDTOx6Y8ewnvl2Y1INbOQIomF4JMACqJM9nuTUMmgAAAABJRU5ErkJggg==") -} - -.fileList .ico-bmp { - background-image: url("../img/ico/ico-bmp.png") -} - -.fileList .ico-cdr { - background-image: url("../img/ico/ico-cdr.png") -} - -.fileList .ico-gif { - background-image: url("../img/ico/ico-gif.png") -} - -.fileList .ico-ico { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA7lJREFUeNrsnEFIFGEUx9/szu66uOvumCtuSlREkB2ChII6dEgisFtCEQVBetBD4KU8lBAFURejg0HSqYgKjwke8tBBrEPSaYMMCrRW1NpCxTZmdvrep7vsrLObO+vubPO9P4zfOPNWmB//9733fYsj6boOJOtyEQICaKvkQjdjsZiXDQPsuMiOZqc8dHNLywM29G42PlRXZw0g041wONyvKArIsuwIeNPT07AwP98TaWyEYiBaBXgJ4blcLkilUo5Ju6ZoFObi8S2B+K85MILwnCaf18shohPZr0NURGyEKHQV3gqIwrcxpUKkPrBEiASwRIgEsESIBLBEiASwRIiyaHDq2LoWl3Ob1dfZ2Z5Qa2svAVwXW9tDIBDg55IkZa7nO5+ZmaEUzpbH48lsjGRvJlvdWBbOgWmIKE3TNrgunxMJoAlEBKSqKh+LBSc8QP7w66mcvVVXLMiqnwNXJ19A4v45PpYLotvt3pDGua78bwEm348ZxnIIAZpBdMRKxHfghGEsN8Rs5zmiiPiPnOVHJZT+6gJbGsc4sNJCiMV8jUEATbTZAlLVKYyVN1fK5acVhUgOrADEqnVgttvM3Fg1cyb5jAASQAJIAEkEkAASQAJIIoAEkAASQFIxqrrdmEI7L3bvEZIDRXCg3Y4iBxJAAkgASQSQABJAAkgigASQABJAEgEkgATQwSq4H1j78DTMCQ6oFn/c+2AN4FqET2yCapJS2LYULod8uw5C5MJdSIwOwsq70cz1UHsXBLP+H+RP/COsxl7D0sRzw+fN4lamRg1/y9EAzdTYPQSumoAB6rbO6xA63s3PESLeb2Dgc+OUU32gdPTx67mwhajCwaNnwBvdCz9Gbhlc9H3kJiQ/T7H7a26rbevgcYtPrhriEi8H4df4MIctK1HxAPpbj/FUxVTM1cLjKzA/3MvdhSAxTk3EN8SlgSJk4QCiq9Sf8fxFcB2YqyZoChmV+r3M4r6BHBbQgbQSKVHoKnRhvvmxZeAVuPxB5rKlvHGY4rKyvaCTHQsQ5zVsbczg1Oxu46mJabw08YzPl2aFIj332dHK2A4QHxoh1XdeMxQB7BUR7K/xR5k4dGvD+TuGOGxjsAJjJTYrMI7vA7EAYKUNtXfzfg6PdGpjv4cONcZ1mcbZ1UhLhV4486X/kCapSeZSCcSUDrrHn9p5+43bUgpr0f2TkNLwtT4CsmPPzJ5da9o3aTmF9fodJ1WAMffCp8PS8qJQ75jRAw2qFtnzFhlYTmESNdIEsNr1V4ABAKATVWjNArx4AAAAAElFTkSuQmCC") -} - -.fileList .ico-jpeg { - background-image: url("../img/ico/ico-jpeg.png") -} - -.fileList .ico-jpg, -.fileList .ico-JPG { - background-image: url("../img/ico/ico-jpg.png") -} - -.fileList .ico-png { - background-image: url("../img/ico/ico-png.png") -} - -.fileList .ico-psd { - background-image: url("../img/ico/ico-psd.png") -} - -.fileList .ico-webp { - background-image: url("../img/ico/ico-webp.png") -} - -.fileList .ico-ape { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA7JJREFUeNrsnE9M01Acx9+27g9jIkOZTMAYiTEZF4melJtwNJ6MHgwx/jlovHhSL0ZOejNGo4l6Mh7wZMSjHDyoB4NycolZNJpFZhiJSmRs0K6+X6FzsNKtLdtK+/0mTbf2tUs/+/5+v/fe8uaRZZlB5uUFAgBsqgS9k8lkMsB31/l2mm/dTnno7p6eB3x3sdb2W9vazAHkGm1vb78ajUaZIAiOgJdKpVh2ZuZCZyzGjEA0C/AswfN6vaxYLDom7LricfYzk9kQiNVyYCfBc5qCgYACkZzI395HEWkiRFdX4Y2A6PpujFWI6AdahAiAFiECoEWIAGgRIgBahOjRm87iY2G5r6/PUXCy2Sybm5szdE0ikfCYHco5TnxszyKRyLJ7PP+5rPc6nU4jhMvl9/tLEyPl0Wd2Ytl1DlQhkiRJqnDdek4EQA2IBEgURWVvFFzTAM4/e8zEb6m6f46wey9rPXFOv81KKJdP1RkF2fAc2Ah4Rj6HIPp8voowXutK24VwZOQS88V7N/y+UibN/j65Z+gaFSA5USsX2hJgPeBZuS9BJGjl1diWIWxn0ey7UQcCoAZEIz9jAKDW+LbGAuLqfmAtEOHABkAEQKs5EwgAEAABEAAhAARAAHSl6jaUmx97yMTvX+BAs3IDvLo6sBGyg8s3dQ60g8tRRADQoQCFPftMnUMRWVHr8TNwoO2/fRu4fFN3Y+zgchQRAARAAARACAABEAABEAJAAARAAIQMSXe1ZubyKfwzGVf89lPzqzUDBpY9OVGLVRYhIoQtqmkTqltGLimzxrmXY6zw4W3puJ8fo1VMayXnc2zh1bjSdr02pOKvWfbnzqizAXqj2xV4BCXQP7AKoCparrX09fMq4OGjJ1cdW9vGNVU4eOBwyVEEkoBWU27iRcmhru/GBA8eYoXJd2zx09RyoerfjxxYqyhkPaEwW0x+VFxIEEODwyz/ZkL3uvDQMWVPIeuLblNea+XBBe7Uavfa1AD9iQEl0UvTy3/msJScUqDSpjpSCw7BpoJD16oA7ZADGwqQch2BIkVv3K0AWw7QDnBsB1DNdb9vXVEcpaqFh2docIg7LIyxcLXqSy4rh6f09nk+VIsLiohO8aAQXpoYrzhH+ZA2ApybfV7zPbWKSKM70rqTCdPXzkvBQt7Vw71CMFTcefORz1QIi1273kscsBunZOiZ6dnFrt5J0yEsdcSG87L8Wpj5MeBZmHeVE+WW1qIY656SOnYcMR3CkE2HcgAIlfRPgAEAIIZMFb61acMAAAAASUVORK5CYII=") -} - -.fileList .ico-avi { - background-image: url("../img/ico/ico-avi.png") -} - -.fileList .ico-flv { - background-image: url("../img/ico/ico-flv.png") -} - -.fileList .ico-mkv { - background-image: url("../img/ico/ico-mkv.png") -} - -.fileList .ico-mov { - background-image: url("../img/ico/ico-mov.png") -} - -.fileList .ico-mp3 { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA/xJREFUeNrsnLtv01AUxm8SO0mbUOKKhj4RaoUqpQsFJuhGOyImHhNCBQYQf0BZECwgJgYQSBQxIIaiTsBIBwZgQIVOREIRCBTRogaBVEjzsmPucevgJm4aX+fhXp9Pct3G17b883fOufe6jkdVVYJilxcRIMCWSqi2MR6P++nqKl3O0qWPl4vu6++/T1eXam2/s6ODDSDV9UgkMiVJEhEEgQt4iUSCpJaXL3ZFo8QKRFaA5wCe1+slxWKRm7Dr7ukhP5aW6gJxqxzYBfB4U8Dv1yCCE+mf97CItBCiq6twPSC6vhtjFyL2A21CRIA2ISJAmxARoE2ICNAmRE+16Sw6FlaHhoa4gpNKpcjKyoqlfWKxmId1KMed6NiehMPhNfd4/nPZ7PdkMokhbJQoiqWJEWP0sU4su86BOkSQoigVrtvMiQjQBCIAkmVZW1sF1zKA6acPifw10fDzCHv3kdCp89XbrIeycarOKsim58BmwLNyHoDo8/kqwrjclY4L4fCZy8TXM1D34ypLSfL38V1L++gAwYlmudCRABsBz85xASJAM1ZjR4awkwWz71YdiABNIFp5jIEAzca3NRYQV/cDa4GIDmwCRARoN2ciAgSIABEgAkQhQASIAF2phg3l0jMPiPztMzqQVW6A11AHNkNOcPm2zoFOcDkWEQTIKUBhcJhpGxaRdYVOTKIDHX/3HeDybd2NcYLLsYggQASIABEgCgEiQASIAFEIEAEiQASIsqSqszHS9A3yy+WAJPhx+wkbQJDfwmtPPCq/xUuIGMKNDOF6Kjg2TtrGjxP5yyfyx+RNItgGbXLv35DVFzNEHBzW3mYql5pdJZmXz7V2xv10ZeaekezrOf4Alk5IwXilXaT4++eGzwOHDpu2h9e2ChS6rh0Uavux09pnABngAXAAGjh4RNumZjIlwNxVYXCQf2T/xjw7Mko8wfYKqGZapQ4DATy4GcpisgRLX/t6B/h1YG7+reYUY5iJsVEttK0qPfuoIk2ACh8X+O0H5uMftBAW15+agfPAgfkaL7qd5jwNkgE43BDp2p1Sji0w3Ixt40AIOVhECg0uFHIfhDUABJDlKi8k0BZynjHcIXRhgZsSOjmpPa0rdyc3AHUXBscmtGrqjx3Q4AGYWopINUE7SBEQyulZjodycJEQugAREj5rzopM3dLC1qxQcT0W1kMWnAKhyJqz9GPo+RRyIaSEfBOLSMv+M6EQX8t5dvprkAvL86TeEW+Wqn710+KVC0ogl3X1cC8XCBZ7b077mEJY7t7zTqGA3fhl8XDNcO1y98A8cwgrndGJrKq+Epa/j3oyaVc5UW0LFeVo34LSufsocwijHFqFESCqpH8CDACOqX4es1WjewAAAABJRU5ErkJggg==") -} - -.fileList .ico-mp4 { - background-image: url("../img/ico/ico-mp4.png") -} - -.fileList .ico-mpeg { - background-image: url("../img/ico/ico-mpeg.png") -} - -.fileList .ico-mpg { - background-image: url("../img/ico/ico-mpg.png") -} - -.fileList .ico-rm { - background-image: url("../img/ico/ico-rm.png") -} - -.fileList .ico-rmvb { - background-image: url("../img/ico/ico-rmvb.png") -} - -.fileList .ico-swf { - background-image: url("../img/ico/ico-swf.png") -} - -.fileList .ico-wav { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABE9JREFUeNrsnDts01AUhm8S59E2lKbQ0PQl1AgVpQsFJuhGOzMhFlQhXhKoCwuPBcEEGwMIJGBCDLDCyMICAyqEpRFV1AqIaFADKqpIX/EDn5vc5CZO09hpXNc+R4qcHD+i+/n/z7Fv5LgURSEYxsONCBDgtoZQa2UikfCpi1vq66z66rXLoHv7+h6riyv1br+7vd0YQDXudHR03AiFQkQQBFvASyaTJLOwcLkrHCZ6IBoFeB7gud1uIsuybWzXHYmQX+n0lkDcrAZ2ATy7hd/noxBBierHR9hEthGio7vwVkB0/GVMoxDxOrBBiAiwQYgIsEGICLBBiAiwQYiuWtNZ6r2wEo1GbQUnk8mQpaUlXfvEYjGX0Vs524V6b0+CwWBePa4Sl43ep1IptDAfXq+3ODHCu8/oxLLjFMggQkiSpFHdRkpEgFUgAiBRFOlSL7htA5h99YyI35JN/x5h/wHSdvpC7W0KVuan6vSCNL0GmgFPz/cARI/Ho7FxpSotZ+HgxCTxRPq3/LhSOkX+PX+oax8GEJRYrRZaEmAz4DVyXIAI0PhubEkLWzlg9l2vAhFgFYh6fsZAgNXub+tsII6+DqwHIirQBIgIsNGaiQgQIAJEgAgQAwEiQAToyGjarVz25RMifp9FBRoNJ8BrqgLNCCuofEfXQCuoHJsIArQpQGFwyNA6bCKFaDt1DhVo+bNvAZXv6MsYK6gcmwgCRIAIEAFiIEAEiAARIAYCRIAIEAFi6IqaT2umr57BfyZTI3L/hfGnNX06HnuyY6xv8hAiWtjqNdAd2ktCtx+QlrGTZfn2S9do3tNTejDGFWituu2uiUma9x85Xsx5B4doLjA6pnXN8AhdB8sdD1Be/E2k+RQdMA+KgePz3mj+vZT+UXYCYHpeWV0uA5Kbm6HH9cUOa77TGxuh269Px+3RhWGw7s49GlCimvdEBop5eA8Dz83OFHOgOsitvH1NQQLQYn1KfKYngs/ByYHjr019sM9lDCiKVx2AAvXk5r5SVcE6pkaAB8CKAI8eozCYmnzDh0oAp79ocgAPjgdwbQOQKYrZFZYwQFAmP2gAzNuXwYVtmSUDo+Oa8sDbGOzL8rYBCIOHAYHyGChWw1heKKiTQa0GI5eI0/35WsjbuGjfT+9N68Km/awJYMCOMHgGlOW9tElky/IAhIGCjlrZJJilwd7QtcHG8uKfvGIL1rYVwHwdHKOXHXyTgDzkvBXNg9W1v/eul9VEgAXbL79ppXl4ifQkHCTySv4EgGptdy/M4ICy+DrH8tBh+Tx0X1AZD49ZljWXUjOJ0/3BvmY1D9MB8vbkLVZpZ9Y8ADTUPI2SC3UTALPuzewMn826fKlrMmH+5kXJv7bq6Nu9NX9A7rn71GNIgWL3wEdJBezEKRkYM4xd7O6fMtxEpM7w+KqivBMWfo64VrKOUqLS0iaL4d641LnvhGELY1ioiSBAjKrxX4ABABrs14sDnYP6AAAAAElFTkSuQmCC") -} - -.fileList .ico-webm { - background-image: url("../img/ico/ico-webm.png") -} - -.fileList .ico-wma { - background-image: url("../img/ico/ico-wma.png") -} - -.fileList .ico-wmv { - background-image: url("../img/ico/ico-wmv.png") -} - -.fileList .ico-doc, -.fileList .ico-docm, -.fileList .ico-dotx, -.fileList .ico-dotm, -.fileList .ico-dot, -.fileList .ico-rtf { - background-image: url("../img/ico/ico-doc.png") -} - -.fileList .ico-docx { - background-image: url("../img/ico/ico-docx.png") -} - -.fileList .ico-pdf, -.fileList .ico-fdf { - background-image: url("../img/ico/ico-pdf.png") -} - -.fileList .ico-ppt, -.fileList .ico-pptm, -.fileList .ico-pot, -.fileList .ico-potm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABWZJREFUeNrsnF1oHFUUgM/O7272J7s1iVkT2mrAmBRREQUpiFCtCj74pFAoCOJDlIogSJ8K+qL45A8qqPhSEH0VLAit+lKF0tIXiaQBUaLd2m3zs5tkd+fnjufObtIkZmdmZzLb2Z1zYJifPXdm7rfn3HPO3ZlNWJYFJP4lQQAJYHQBzs7OKrg6hctLuIz1S6fHxsc/w9WrXvUHc7m2n0kubd/O5/MnC4UCSJLUF/Dm5+ehfP36zPDICHQC0S/Alzk8QRCAMdY3bjdaLMK1UmlPIAounw9zeP0mqqLYELkl4u6nYQLsW9kriLEFuFcQYw1wLyDGHmBQiAQwIEQCGBAiAQwIkQAGhOhWC1sTExO+b2Tth09AmzvvSVcsFEF98FlQp5/AnfDKxnK5DJVKpaM209PTCb+lXNfEXCrB+k9fQf3id5A+OgPS2FQo18HaHjKZTNN6Ere4tNteWFiA22aBG7L00TF7XXj967Y6xtU5G6B5c8G2wMxzb4J84AFP529UFyEhSCDKKoiK6qqv6zoYhtEW3E6AThYYmTFQumsScsfeBfmeh9EcDdv92frydshaHWEtQW25DGs3rm4eZwhDkhUw9ebnlmk4XkuWZXt2yQ3e1u3eCCIJAdLPnABx5G6w6qug/fZj8zh6SW35Bhi1VTROCdRU+v9fgKpCMpMDCS2wjhCN+roniBugNpat+z2ZxiQkBVKPvWBva1d+bfJjDC1MhmQaAaGbCg5BRk6mYCBXQLM0QUfgjlaPANtZYm9a4BZ3tl2zUm52RBRBVlPevwSuP5BB8CaY6PZuEEXU3+nGXq0wmnmgxVqe638Sl/edQ9dra/YQ4JhCIcDdIPZsJcKqN5s3ly5sEPV1HhHdXpRkMPSGu24L4s6xsCcB6n9carnyfUH4tSAqwHTNmzUJQscWGLlfinj0rV8+0yypph/f5tKOVc9i2SG4i96npxAiz429/twbKYCWocHqmQ9tiPL++0EaP+QJYKowsreZQMv6vECMDECjNA/r5z4Hc/EfELJDMHB0pjUgMtcgEEo6FUUX3ijp3FKY9NOvgTCQb4KzzN3zPTW5rRpxQAHpoWJoECNjgeK+MUg+8jwok4dvuS3mce2sT0ll7MU5G2JQW62Eet9dBeg0mbBtZgaTX17P8vo20AyPqdupTJgSyTSGD96m1gh8HlPTQFSS8QMoYadNE8swQ/cPD9vyc0hxBMjrMCWdBb1R95VD8za8LT8HeIym/QWQWyHWsQKWYXxGxergwSauy9vwtlIHExB9B9COtOkcr8VgvbKEFlVzLwHR6rgub2O37cYXDREXZSBrz6rUq8s4rmFkVhQst8TNOUGG0ZphumNgwODBJ5UfRuvrXrd64qnJBMJK5YfAQAsztBqmOHVgrQDDXZXP/0nJNLpssvtDTZTyP/dxMXlbIPXsGNgLQgAJ4G1Ot8I8eaO6HJmOqtl87wEM66bJhQkgCQEkgBSFKQpTFCYXJoAEkAASQBICSAAJIAEkIYAEsAfF8V25v96Yov+FQjnwwe8B3taU1HjTMxrkwmFKVx/tGD91dtfj1V++gZWzX7rqrF36HkZPnHa8xrWPj4OxVOpPgFxWzn0B1fPfbu5nD78Ig0desTvNAbnp/P3Ok5vHh4+/b6/Lp9+KbxTmoFi9Ckrx3kA6lMZQHuhPuHsKySxopSuBdGIRRLjwsYwvuwWITnRiC3BngPCrQy5MYyAJAQx7MuHPk4+aCaMh8Dce4ykWWJLKDr53QfRlgWbx0AX7jUmwYgmP952NTl30HYWtffufMi32s/Dv3EOJ2kqs3N1KDTJ25+RldsfBI75dmCTgGEhCAEOX/wQYAOhdFmkKB8pMAAAAAElFTkSuQmCC") -} - -.fileList .ico-pptx { - background-image: url("../img/ico/ico-pptx.png") -} - -.fileList .ico-txt { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2JJREFUeNrsnN9LU2EYx5/98KgxbXM6tuZFMSgaeiHmhV4IGSVFkX+C1FV20W0hBF1IVwVeaAQR/QNBERR5UeGFg1K8MAyySBCbbP1cP6zhzul9js6t1NP5oW6e8/3C4WznbBfvZ8/3fb7vOzguRVEIMi83EABgSeXVujk9PS2J02Vx9IojapdBRxsbb4hTn97P766tNQdQ6Irf778YCATI6/XaAt7MzAylU6lzDaEQGYFoFuBZhud2u0mWZdvYLhyJ0EIyuSkQ/zcHNjA8u6lSklSIXIni7TCaSAkhOroLbwZEx8cYqxCRAy1CBECLEAHQIkQAtAgRAC1CdGltZ4m1sBKLxWwFJ51OUyaTMfSdeDzuMruUs53E2p58Pt9y9bgKXDZ6PTc3BwsXq6KiYnVjpNh9ZjeWHVeBeYisXC63puo2qsRtBzif+rgtMKKhoGmIDGhpaUk9GwW35QDNDmw7lbdy8VadUZCOjzEM0ePxrLHxv1WJOVBDeYBcievNhQCoEyJDK+7GsLBB8e670QoEwHUgGvkbw7ExRnN9u1J9esK1o2OMHoiw8BZDBECrcyYQACAA7uil4E6PMaVOBYgxsDAAAiAAQgCIGIMYgxgDASAAAiAAQogxiDGwMOZACAABEAABEAJAAARAAIQAEADLVpqbCYO3RkBI6Ob1uDmALEmSHA0vm83CwiWz8GaqrWU/9Zzo2PD+18wP+v5jkeoCNXRt6C4t/ir88n1nTlJ1VaV6T0v3Ho7Ri8nX9gTIAyse3EB/Lz1+Mk6jiZer16KRoIB1irq7WgWMhHqts71JXK+n4dsPaD5Z2OXm691dh6h/4A66cF4MiKG2tRyg2L6ICpQhjSam/oLnSAvrFVdk08G91HO8Q9j4twD3QUCdQA40ovuPEup8x9bl1wjSBhXwF5rFnnB5/zlVdhaurpJEt26nqVez6ntuKG9nk/Tp8zdUoB6dXok6I08n1IN17HArLKw3KzaLBsJNgyuOj9GxKfUaxxYA1NByZGlVrVucF7krv3n3njo7mtVoU27SfPTThUtDOVlxOXq553Yp8uDV8x5TFdgYqXvOD6dx4uPiecw89mg4MG66CwfrfEdlRXm2kPrS8nMx66hK3FUtyeFQcLI+WHPEtIWhHRqkAdBB+iPAAD3HE9ONhnJoAAAAAElFTkSuQmCC") -} - -.fileList .ico-xls, -.fileList .ico-csv, -.fileList .ico-xlsm, -.fileList .ico-xlsb { - background-image: url("../img/ico/ico-xls.png") -} - -.fileList .ico-xlsx { - background-image: url("../img/ico/ico-xlsx.png") -} - -.fileList .ico-7z { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABAFJREFUeNrsnF1IU2EYx5+znW3aNvcR6qZS0shwXmUQVFAXKgTrKoSiiwjMiwRDb0ToA/q46E5JsCDpoiAKuuhCIajsAyqQspua5IgKq6mLmV+zyc45nfcs15pnunO2M7ed5w8vZ+Ocs5fz4/9/n/d956Q4jgOUfFEIEAHmLkCv16vnD+f5doJvlYXy0JVVVdf4Q1uq11tKSpKeo9e594LVau222WxA03RBwPP5fBCYnj5VWlYGUiDKBdhC4Gk0GmBZtmBi53A6YdLvzwhEzTrnSwm8QpNBrxcgEifyb/uVBFiwyhRE1QLMFERVA8wERNUDTBciAkwTIgJMEyICTBMiAkwT4nprYc7lchUUnEAgAHNzc5LucbvdlNylXMGJX9uDyWSKuof6xyXZ64mJCYxwvHQ6XWxjJD59crf1VOfAFYhEDMOscl0yJyJAEYgEUCQSEY5SwakeoPDwf6Mcv1WXcw6MTH2CxaEeYBeCWQekMdnB6OkEuty1JkQSZdISY5wKQMWLSGh4YEPgCc7i+w09ur7udVqtVmhS4WXFgczP6DTAeLAd9DV7Ur5v5uqx2Gvb6TuS+10efw2LD/uACX5P6XoCkECLr8Y54UDgouOLFHiZkJz+yO67VAfiUk4EopSvMXK2CsfHNt04S9WK+1KZXNPoubUhYoQVhpizDsx2bGWPmegzBLixy0GswuhABIgRVrgKowMxwqi8XMolVtxcjHVOA8yHcRAjrIaJNDoQq3AGxGX5r/yz1J/yAKloF+H3w1nlF+uPUvYRFR8DaYcLIn4fhJ7eFFrWB3mHK78daGxqA7qqbmMqJN8v6T+vHaixloP58BksIigEiAARIAJEIUAEiAARIAoBIkAEiABRRGv+WvNrRy3+XyheW3vH0vi1Jm1QN71IGCOspDb8a03boU4w1ntEz3272AiO9ltA2ypEz4c/j0Lgdpe6Ac4M9ggtFokiE5S19sPS2Avh/WTf8VX3mPcdAUtDKyy8HcIqLOZI9vcCzD4eED1f7D4gwJt/dReWvM8RYCIc0n4N9orHxebkAXfAsn88KWBVA7Q0tMDi6JAASEz25nPALs1D8P4lnEiLuY8Ui9AH8VhaGk+C3lnDw7sMkRk/AkzUJvd+HswPobImyrjLA+a9R2H2yY2k7lQ9QMO2+ljljRdxnc3TKRSM+Zf3cC0sJgJJU2ReFU0ypbE3nxWcGT/VwYl0gnQV26OrpuD/AHU82JVJdEXXg9WrLB6s2DwxZzYTvnTvZqhImHcpBeoUBxxtYKuvjGhlRZhx1o0AywgfpEZ45NlZR+0b2RHm7FuaGI59ppn6uJNamlXVxgNXbGHZ8h3v2M3VDbIjjEpzDEQhQMX1R4ABANtbXMt+ZZZBAAAAAElFTkSuQmCC") -} - -.fileList .ico-gz { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAP3SURBVHic7ZzBa9NQHMe/L8k6pd3WTra1rDihoGy7zJueZSB48+hJ8LSBMmEHQRC8C4qCepAd/Q8EQdi8CSroQSpaPJQhc61MHFtl0iQeutSsbV6T/vqS1/Z9Lm2T9/JLPvv98l6ypsy2bSg6R4t6B3odJZCIwVuZz+djAO4AuApgOowdCoPpbPYJgGW/7cdGRz3XcQUCuJtMJm+lUikYRrumvUGhUEC5VFqamJwEAkj0op2Va6lUCpqmwbIsaixpSGcy+LG11RWJ7c6BE5rWf6fJ4VgM6UwG5VJpCcBjyrb6z45PuiVxYAUC3ZE40AIBusSBFwjQJCqBh3QqUQl00YlEJbCBoBKVwBYEkch4t7Py+bydy+W6vX+RUi6Xsbu7G6jP3Nwc81rXHxe4AUgmk0gkEgAAxv578Xq/ubnJ3d7AlfDQ0FD9xoi7+jq9sTxwGQjUJAKAaZr1ZU7WeWWiFwMpEKhJZIyhWq2CMRZYnMPACgRQL2X3rTrpMrC6/Q37L+7D2tsRHaoJLTGO+KWbMKa8ZxKGYcA0TZim2VTGfgQKH0Qq688ikQcA1t4OKq+etm2n6zp0XQcQTB4QQgaaP2vTgPjF64idPu+736+HV+rvUzeeB4779+sb7L98BHPnu6/2uq6DMXZkNJYiA2HXzi9B5HWDTuJpmhY4AwduHtgOTdMQ5N8Y0o7C7rKllnNQnOzzM7mWVmDUqBLuAlLMAzsl7LLtFJWBRJRAItKWcJSjcBBUBhJRAolIW8LuspUZlYFElEAi0pYw0DziyljWUguUUVgjqoSJSJuBsk2YvVAZSCQ8gXbI3/IPKZ54gawW4uDTuvBQburxmNhDFH4ONNI5VLcKqGysobKxJjpcy/giEZ6B8cVlGNl50WFaYmTnEV8kP4zEjyF06wC05BRGLt8WHSYy1ChMRAkkogQSUQKJKIFElEAiSiARJZCIEkhECSSiBBJRAokogUS4T2sWV2bVL5MBmHnwmfC0pjHc1Z3pOaoH3NXCS3jm3kfRISJFnQOJCL0j7WSf81pcXTjy2cFZ7qwrri409Wm1XWd9Y59GWm2jWwgV6D44h8bPrZa1asNr647HayuC0EvYzwEFOWi/okUR2Vc7+mVwiUQgrwxFbF8koZdwmAcXBpFPY6jZ19g/7D9QKCXsnpI0Tjd40w93f0cKr3/jq7uNKNpfC/fApZzQrKsecK+FuSVsHxuxALnuJ4Q7etuHDrzhlrCZmX9rFN+dg6YD8P9bKiJpVfJiss8GLBNWevY9rxVXoD1+ctG0rdfa9pez7M/vyAcch+LKrPAY9vExy5o688E6ceoCrx33HOiXIL/0IxvU45cmq3oVJZDIP8OiU3PHSqspAAAAAElFTkSuQmCC") -} - -.fileList .ico-cab { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABLVJREFUeNrsnF1oU2cYx59zepp+JTSp1CZaNl3AYXo1hQ0V9EIFobsYQ5gMBMFVWGFjvRHBWZjbhXjTMaEKftwIoqCXBWGrzsI2lM3dzAwt4kbR1GZr1takq5wP3+dtczxJTr5OkpP0nOcPh3ydnJfzy///PO970lTQNA1I1iUSAgJYV0mFXoxGox52M8y2Q2xb75STXt/be/a/4fcGzV5LHrmR81wkErEGkOkrv99/LBAIgCRJjoA3OTkJ8ZmZT7tP3oV8EKvmQKbDCE8URVBV1TGxC4ZCMB2LVQVisRrYjfCcphaPh0NEJ/pP3h2lJlJHiK7uwtWA6PppTKUQaR6YBZFNcUYJoI0QCWCFEAlghRAJYIUQhUKXs9haWAuHw46CE4/HYX5+vqz3sLWwYHUp5zixtT14vd5l9wivueS7PzU1RRE2qrm5Wb8wYkyf1QvLrnNgGiJKUZQc1+VzIgE0gYiAZFnmt+WCcz1AfvIrUTZeqms4B8rPH0NybATUF7O2AxK9XdDRPwRST7ggRIwybtkxLgVgzZtI6taFusDjzmLjpr4/V3S/pqYmvpULzxYHKv8sTwM69n0Gnk3bSn5f4ruP9fuBz6+UPe7LR79A8uYZUGaflrQ/AkRoxm7cEA4Ebbm+lAOvGrIyHl59L9eBtJQzgVjO1xgN24WNsa00zuUq7b5SJtcSea4wRIpwjSE2rAPtjq3lmkk+I4D1XQ5SFyYHEkCKcI27MDmQIkxalUu57I7biLFuaICroQ5ShN0wkSYHUheugjSb/8rfpvFqD1BYHmLpj1u28tPHE2p7ijWvgVIwDHJsElK3L/HN9iIfDK9uB3bsHQSpt68+HZKNi+OvageK/h7wfXicmgiJABJAAkgASQSQABJAAkgigASQABJAEqrgrzX//mIz/Wcypje//bOCX2tKLe6mJy9RhGsp27/W7NzzCfi2H9Afv4w9gsXoHVj46VrOvt0HT0PLxi2QGBuB5G9j+vP4HL5mprnx86bHcgTAtQOjILZ6M4Cs2X8COncP8PvGE5cCIQ5K/X8B2iO7MgCmFb98FJae3NcfB94f4seSE9P8Q3FUF/bt+Ag8oU0we/2bDBj/Xv+aQ/DtOJCxf8fWfg5vbvwCB4lAiyn14M4K/KDzpjFtzEXoCoysmZNmzg9yd+oAt/RD8v4YLK5AaYvsLDpGe98uDn0xOuG8CKP7Fgxxy2l2iVgGbLHVx+BNMCAvOHh0aHZtM6uDWAONx3JcEylF7cxtcuKZ7tYUcxRCTbs4Xw3E2Af6h3LqqSMAIgx0Yb76iMV/+sxB3YGo3uEfcsAWag5YWzH6rW9tdR5APHGEhBCz6yCeMDoOo4cwUc9Of8Djmz39EVtHii+vDLXUMU0E3YGQuvZ/yaOWPdebG7+oNw+EbYTHP4AHE3pM8wlfww8Im4/jaiACwU7buWeA16l0rUI34rwQoWF0pcA6HWZ2CcANAcuJi3mbyMLPV03njHW5mPDXsXcVQV5iLhXAndJAk1rUDafuNVmKsBLquweqwg/kRnh47mpw86+WI6x1vbFX0dQfxecP3xEW51x14UFr61TVnrd/V9ds2G05wqQG6sIEkGSqVwIMAG021uWJKtY8AAAAAElFTkSuQmCC") -} - -.fileList .ico-iso { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABH9JREFUeNrsnEtoE1EUhs9MJmlLUpu0tk20+CCotK5UEMSFCxWEuhARFFERbBcWFN1oQRTUjbpRFFRocVMRBRduCoJvQQTxsdGIFlGpNm1TW9qa1pZ5eM9NZ4w1jZlJ85xz4DLp5N7czpf/v+fMTVNB0zSgsB4iISCAOQ0p2ZOhUMjFDidY28va/GK56Pl1dVfYoSXV/hVz5lgDyOKk1+tt9fl8IElSUcDr6uqCSH///uqaGjAD0SrAfQhPFEVQVbVobOcPBKA3HJ4ViP9bA6sRXrFFicvFIaIS2Y+XKYnkEKKts/BsQLR9GZMuRKoD04RIANOESADThEgA04RIANOEKCTbzmL3wlowGCwqOJFIBEZGRkyNaWhoEKzeyhVdsHt78Hg8MfUIf7jM9Li7u5ssHB9Op9PYGIl3n9WNZdspUIeIoSjKP6qbSYkEMAFEBCTLMj+aBWd7gPzip6wcv1WXdwqU+z5BtPM8qD8Hsw5I9FSCu/EwSLXBpBDRytim2zgVgBlPImMP23MCjyuLzTt27+p/+zkcDt7MwsuKApWBWBng3nQAXEvXpDxu6OJO47Hv4A3T805+fA7Ru5dAGfyeUn8EiNDis3FeKBC02PpiBt5shJX5cPfdrALpVi4BRDMfY+RtFo63bbp2Nhu6+lIpriXSXHKIZOEMQ8xbBWbbtpbXTNIZAczt7SBlYVIgASQLZzgLkwLJwhQFeSs3PePmo63zGmAhrINkYTsU0qRAysKzEFqW/8o/S/NlHqAQm2Li7cOs8jPmEzJ7iRlfAyV/EORwF4w9usZb1hd5f7CwFeje2AJS3fLcZEg2L85f0AoUvbVQvvUYJREKAkgACSABpCCABJAAEkAKAkgACSABpMBI+m3Nr4fq6T+TsVh44X0a39aUSuxNT54gC2cycvKxZvXuc+Dw+qH30p7YL+ELQMX6JihrWGf0GQ89geEH7SAPhY1zJYtXQsWGJnAFlhrnoq87Yfh+G6i/ftoziYilHpi76yw4GMSec1vg26kNEOk4As7AEn5eD/eqRg5eYUCn96tpvszfBFsCdDI1Sb55TEXthoomPr/mysLzqDaEjArFcz9un/6r3wCDKJaVQ/naHfYEqE3BQGsiKD1Gn93iKpsMf+TqE0vLIfqq85/xCBPBulc2/jW+qNfA+EBAww/amMKaYd6RO8a6Jg/1cIj8XWYK0/smTJRT6yQqdqY+RQtQVxs2VJqLWzrAFYVt4PpRysLJImZPDweIFo1CzKYIEpMDPq+OjxrnEilMTyCoWltmYbTv9PULQSEQfe1Tf41ymInGo1LR9rkoZXIOcDz0lMOZy0qU+PrOt/kwX9PG3j3hYLAmRFBV244bsLEuxHGo0NFnN+1pYUwAAx1HWRmynVs2XoFY52GpwhMLU6E8GObZWk82+VBIJ91M+NK6WhHkCaZSAewZGmhSibrozAuHJQsrgeUvQFX4C9kRHl676q9/adnCWuWCjYqmPhb7PqwQxodttfGglVWoau2yN2rVovWWLUxRAFmYANo8fgswAF4ssuGz3PXWAAAAAElFTkSuQmCC") -} - -.fileList .ico-rar { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABGNJREFUeNrsnEtoE1EUhs+k07Q1qclUahMtvoJK05UtCCrUhQpCXblRBEFQFxaUdiMFQVA34qaioC6KG0EU3BYE6xNUEI0bqWjxRZWo0UpfaSOZGe+5adIkjdPMpJkkM+fAZSYzk3uZL/85/52bpoKqqkBhPByEgACWNEStk0NDQ062Oc3aIdZWWuWmVzY3X2Wbrnyv9yxdagwgizNer7dXkiQQRdES8IaHhyHy8+exxuXLQQ9EowAPIzyHwwGKolgm7Xx+P3wPhxcF4kI1sBHhWS1qnE4OEZXIXl4hEykhRFu78GJAtP00plCINA8sECIBLBAiASwQIgEsECIBLBCioLWcxZ6F1UAgYCk4kUgExsfHdb0nGAwKRh/lLBfs2R7cbndCPcIcl//tj4yMUAqnR3V1dWphJD37jC4s206BSYgYsizPU93/lEgAc0BEQPF4nG/1grM9QH7zs6mcvlRXdgqM//gAUwN9oEyOmg7I4W4AV2cPiE0BTYiYytiy0zgfgEU3keiD/pLA48pi40bvXVvwuqqqKt70wjNFgfKvxDTAtfs4ODdsyft9fy4dSO1LJ27qHvfv++cwdfcyyKPf8roeASK0dDcuCwWCmqgveuAtRhgZD1ff9SqQHuVyQNTzNUbZunB62haaznojqb58JtciaU4bIqVwkSGWrQLNTlvDNZN0RgBL+zhILkwKJICUwkV2YVIgpTBFRT7KZTtuOaZ1WQOshDpIKWyHiTQpkFx4EUI1+a/8TRqv+ACFxBCxNw9M5ZcaTyjuLRa9Boq+AMTDwxB9eJ0304u8L1DZCnTt6gKxubU0DsnGxfErWoEObxPU7z1FJkJBAAkgASSAFASQABJAAkhBAAkgASSAFBiav9b80t1C/5mMxeqLbwv4taZYY2968RilcDHDtK81a9a2QePBCznPTTy7BWOD/ZmfbK0bfCdusG09fL98EOJ/wqlz9dv2gWfH0Xn9KDMTMHa/H6ZeDVgPYDIiN05C7FMo9drV3glSZw8HlH7jda3bOTyEUhfsgImnt+f19fXszozX0p4e3lfsYygDuKVdGKEhJKd/Q8ZxV1snTA89Zu0J38+3L672dW32m8ZgyiYDYWKLMngzTE2itIKXACN9WR4g1jNM1cmM9O3gqkwo8DHfd7cvrELPziP8WkvXwFxGgiaSURd5+j6ZS83QANRv3c9A9zFAk6njzacH5/X1+865jGssbyJJR535EOLH0VRQkQgxu/bhuXQzyTYR/HCkPd3MRF6ZBrEsTITXvRXr+XZJcDv8Db/ncNIbHqtj57QCywDCx5ppmxqISsG6xdNB8nOzwLqXHXgMjUXLTOTZqYtgJxPhEKcnuLpcs0aRywSSx7TMJPlB1AbMm8ZoLiZ87t0sC/EYgyyAPUMFVaxR1px/UWVIgbK/9QUoMu/IjvDw3hVfy0vDLqw2rNolq8ojx493m4TpMVstPKh1HkVp2vhaWbZmh+EUpqigRzkCaNP4J8AArrWcCArY0yoAAAAASUVORK5CYII=") -} - -.fileList .ico-zip { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+hJREFUeNrsnF1IU2EYx5+zHTdkU7eZuuUocWA4r/QiqC68UCHQqxCKiAiqiwRD74RKyKu6UhIqULoJouhWIcjsAyqQsJtY5IgKq5krze8m56P3PWtruA/Px77P84fDmTvvu5fz2/95n+d958aIoggo9TIgAgSYU7GpLvp8PhM5DZLjDDlqi+Wma93uW+TUI7d9RXm5OoBEV20224DdbgeWZYsCnt/vh+Di4oWq6mpQAlEtwLMUnsFgAEEQiibsnC4XLAQCaYG42xxYReEVm8wmkwSROpH8eROTSA4h6joLpwOi7ssYrRCxDtQIEQFqhIgANUJEgBohIkCNEJlU21lkLSx6PJ6ighMMBmF1dVVRH6/Xy6hdyhWdyNoerFZr2D3Mfy7JHs/Pz2MIx6qkpCS6MRIbfWo3lnXnwAhEKp7n41yXzIkIMAFECojjOOmsFJzuAUo3/y+UY7fq8s6B3I+PsDE5DML6UtYBGawOsHT2A1vjSQmRhjI9doaxHIAZTyKb0+M5gSc5i4y7+fj2ru2MRqN0KIWXFQfyP8NlgOVoL5gaDsnut3zjZPSx/eI9xeNuz72GjUejwC99k9WeAqTQYrNxXjgQxPD8ogReOqRmPLr7rtSBuJRLAFHJxxh5m4Vjw1ZrOCtVxH1yimsWPZcaIoZwhiHmrQOzHbaq50z0GQLM7XIQszA6EAFiCGc4C6MDMYRRBbmU25lx8zGs8xpgIcyDGMJ6KKTRgZiF0yAxy//ln6XxMg+QCQ8RejedVX7R8ZjM3mLG50DW6QEu4IfNp3ekI+uTvNNT2A60dPQA627KTYYk49LxC9qBBlsNlB27hEkEhQARIAJEgCgEiAARIAJEIUAEiAARIIoq5bc1v/Q14i+TEe0fea/h25qsWd/0uBCGcCaVk481WbsLnL13k15feTIGay8fgHtwCjZmJ2F5YhjKjhyHirbzcW23A3Pwe2JEOusGILccgK9D7XHPV3ZfAXN9C2z5XiTtG9uPvhF7Tl0HR/dlWBg9re8sXNF+Dkq9rcRtIxJguW8EdShr3yvB1C1ACq7s8AlYe3WfuO851oFK50N7Vx+EPs3CytS44r6Wlk7ixO+yXVsUc2CsHGTeE7bW4NfDIVntaWJJlER0lYUjsnf1g8nVAItjPSD8WZfVJ1Hy0WUI07KEhh8tWXJVghQsQOo6WtPRhEHrPSykFcpc3xzNvu7B1rjrkeK54DcTPg8c5BkuRFzKgD4lgsiahbprM0ZVIcy7mmZA4KUX0iM8eu+Cs/GN6hAWHfs6eFF4ZvjxoZnZWtHVxoNYWiEINQfeCpV1bapDGFVAa2EEqFP9FWAAVhNMgyXjjfoAAAAASUVORK5CYII=") -} - -.fileList .ico-bt { - background-image: url("../img/ico/ico-bt.png") -} - -.fileList .ico-file { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAehJREFUeNrs3D9PwkAcxvFrKXUhCAMEAhsbL8nJxElfgYPR+CZ0MnHyJbEwMkDoQGAG6h2CNlIo16t/cvd9kktL2g73ye9a7gj14jgWJH88AAH8v4CDwSCUm3vZLmXr2NLpTrf7LDc3p55/Xq0ePBZkXPtYq9Vu6/W6CILACrzhcCii6fS60WwKHcS8gFcKz/d9sV6vrRl2rXZbTMbjQhD9jOMNhWdbzsJwg6gqUX58+klAa1MUorOARSE6DVgEovOApogAGiICaIgIoCEigIaIWXPhuNfrWYUTRZFYLBZa1/T7fS/vVM66yLm9qFQqH9Xjfbkc2h+NRgzhZMrl8ufCSHL05V3Wc64Cd4gqq9Vqr+oOVSKAKYgKaLlcbra6cM4Dbjq/HcrJpTpdSOe/xijEUqm0N4y/VyUVeCQ7QFWJafdCAE9EVGjJpzFDWDNq9V23AgFMQdT5GQPAtPntiQ8Q7oEZiFTgLyACaHrPhABAAAEEkAAIIIAAEgABBBBAAiCAAAJIAAQQQAAJgAACCCABEEAAASQAAggggARAAAEEkAAIIIAAEgABBNBywCj5bhXXsu17ZAL4MpvNhIuIqs+q7zKvx87L+sP1w3w+D2W7kPstxwwnsr3JdnfsJF4FbxgAAfzbvAswAK/3kejP2oZLAAAAAElFTkSuQmCC") -} - -.fileList .ico-apk { - background-image: url("../img/ico/ico-apk.png") -} - -.fileList .ico-css { - background-image: url("../img/ico/ico-css.png") -} - -.ico-cmd { - background: url("../img/soft_ico/ico-cmd.png") no-repeat center center; - height: 18px; - width: 16px; - display: block -} - -.btn:hover .ico-cmd { - background: url("../img/soft_ico/ico-cmd-hover.png") no-repeat center center; - height: 18px; - width: 16px; - display: block -} - -.fileUploadDiv { - padding: 10px -} - -#file_input { - display: none -} - -#up_box { - background-color: #fafbfe; - border: #e4e5e9 1px solid; - margin: 0; - padding: 10px; - clear: both; - height: 360px; - overflow: auto -} - -#up_box li { - list-style-type: none; - height: 30px; - line-height: 30px; - font-size: 12px; - width: 100%; - margin: 0 0 5px; - padding: 0 -} - -#up_box li .filename { - display: inline-block; - height: 30px; - margin-right: 20px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: 240px -} - -#up_box li .filesize { - color: #999; - display: inline-block; - height: 30px; - overflow: hidden -} - -.fileUploadDiv button { - float: left; - border-radius: 3px; - border-style: solid; - border-width: 1px; - width: 75px; - height: 30px; - color: #fff; - margin-bottom: 10px; - cursor: pointer -} - -#up { - background-color: #5cb85c; - border-color: #4cae4c; - color: #fff; - text-align: center; - margin-left: 10px; - position: absolute; - bottom: 0; - right: 10px -} - -#filesClose { - position: absolute; - bottom: 0; - right: 95px; - background-color: #cbcbcb; - border-color: #cbcbcb; - color: #fff -} - -#opt { - background-color: #4592f0; - border-color: #367fa9 -} - -#up_box li em { - font-style: normal; - color: #06F; - float: right; - margin-right: 10px -} - -.cancel { - float: right; - cursor: pointer; - color: #c00 -} - -#textBody { - min-height: 450px; - _height: 450px -} - -#filesClose:hover { - background-color: #c9302c; - border-color: #ac2925; - color: #fff -} - -#up:hover { - background-color: #449d44; - border-color: #398439 -} - -#totalProgress progress { - width: 180px; - height: 10px; - border: 1px solid #ccc; - background-color: #e6e6e6; - color: #0064b4; - opacity: .9 -} - -#totalProgress p { - color: #666 -} - -.setchmod fieldset { - margin-left: 15px; - margin-bottom: 15px; - border: 1px solid #ccc; - float: left; - width: 114px; - padding-bottom: 10px; - border-radius: 3px -} - -.setchmod legend { - padding: 3px; - border: 0; - width: auto; - margin: 0 6px; - font-size: 14px -} - -.setchmod fieldset p { - margin-left: 10px -} - -.setchmod fieldset p input { - position: relative; - top: 2px; - margin-right: 5px -} - -.setchmodnum { - clear: both; - margin: 0 15px -10px -} - -.setchmodnum #access, -#chown { - width: 60px; - margin-right: 5px; - height: 28px -} - -.setchmodnum span { - margin-left: 10px -} - -#chown { - margin-left: 5px -} - -.shellcode { - margin-bottom: 5px; - height: 490px; - overflow: auto -} - -.shellcode pre { - background-color: #111; - color: white; - padding: 5px 10px; - border: 0 none; - border-radius: 0; - min-height: 490px; - margin: 0 -} - -#mExec { - width: 89%; - margin-top: 0; - float: left -} - -.editmenu { - text-align: right -} - -#filesBody { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none -} - -.folderBox input { - display: none -} - -.fileList>div:hover, -.fileList div.ui-selecting { - background: #f6f8fd; - border-color: #d3dfec -} - -.fileList div.active, -.fileList div.focus, -.fileList div.ui-selected { - background: #eff3f9 none repeat scroll 0 0; - border-color: #bcccde -} - -.fileList .file { - background: rgba(0, 0, 0, 0) none repeat scroll 0 0; - border: 1px solid #fff; - border-radius: 2px; - display: inline-block; - height: 136px; - margin: 0 5px 10px 0; - text-align: center; - width: 112px -} - -.fileList div.file { - transition: all .2s ease 0s -} - -.fileList .file .titleBox { - color: #595c5f; - display: block; - height: 23px; - line-height: 23px; - overflow: hidden; - text-align: center; - text-overflow: ellipsis; - width: 100px; - margin-left: 5px -} - -#DirPathPlace { - display: none; - padding: 0 -} - -#DirPathPlace>input { - border: 1px solid #ccc; - height: 28px; - line-height: 28px; - padding: 0 5px; - width: 280px -} - -#DirPathPlace>input:active, -#DirPathPlace>input:focus { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -#PathPlaceBtn { - background-color: #f3f3f3; - border: 1px solid #ccc; - height: 28px; - line-height: 26px; - padding: 0; - width: 290px; - cursor: text; - overflow: hidden -} - -#PathPlaceBtn ul { - display: inline-block; - height: 26px; - width: auto; - position: relative -} - -#PathPlaceBtn li { - background: url("../img/ico/ico-ltr.png") no-repeat right center; - padding-left: 10px; - padding-right: 18px; - float: left; - height: 26px; - line-height: 26px -} - -#PathPlaceBtn li a { - display: inline-block; - cursor: pointer; - height: 26px; - line-height: 26px -} - -.backBtn { - color: #666; - border-radius: 0; - height: 28px; - line-height: 8px; - margin-right: -1px; - margin-top: -1px -} - -#tipTools { - background: #fff url(../img/ico_line.png) repeat-x 0 55px; - height: 110px; - border-bottom: #ccc 1px solid; - position: absolute; - top: 0; - left: 0 -} - -.re-head { - border-bottom: 1px solid #ccc; - float: left; - padding: 15px 20px; - width: 100% -} - -.re-con { - float: left; - height: 500px; - width: 100% -} - -.re-con-menu { - background-color: #f0f0f1; - float: left; - height: 100%; - width: 120px -} - -.re-con-menu p { - cursor: pointer; - line-height: 40px; - padding-left: 30px; - position: relative -} - -.re-con-menu p.on { - background-color: #fff -} - -.re-con-con { - height: 500px; - margin-left: 120px; - overflow: auto -} - -@media only screen and (max-width:990px) { - .pr8 { - padding-right: 0 - } - - .pl7 { - padding-left: 0; - margin-top: 15px - } -} - -.bt-ico-ask { - border: 1px solid #fb7d00; - border-radius: 8px; - color: #fb7d00; - cursor: help; - display: inline-block; - font-family: arial; - font-size: 11px; - font-style: normal; - height: 16px; - line-height: 16px; - margin-left: 5px; - text-align: center; - width: 16px -} - -.bt-ico-ask:hover { - background-color: #fb7d00; - color: #fff -} - -#RecycleBody .tname { - line-height: 30px -} - -.menu-sub { - border-bottom: 1px solid #ccc; - height: 40px; - line-height: 30px; - margin-bottom: 15px -} - -.menu-sub span { - display: inline-block; - font-size: 14px; - height: 40px; - padding: 0 25px; - cursor: pointer -} - -.menu-sub .on { - border-bottom: 2px solid #20a53a; - color: #20a53a; - font-weight: bold -} - -.bt-progress { - background-color: #e2e2e2; - border-radius: 8px; - height: 16px; - line-height: 16px; - position: relative; - margin: 5px 0 -} - -.bt-progress-bar { - background-color: #5ab76c; - border-radius: 8px; - height: 16px; - max-width: 100%; - position: absolute; - text-align: right; - transition: all .3s ease 0s; - width: 0 -} - -.bt-progress-text { - font-size: 12px; - color: #fff; - padding: 0 10px; - position: static -} \ No newline at end of file diff --git a/route/static/css/index.html b/route/static/css/index.html deleted file mode 100755 index e69de29bb..000000000 diff --git a/route/static/css/install.css b/route/static/css/install.css deleted file mode 100755 index fa853aabc..000000000 --- a/route/static/css/install.css +++ /dev/null @@ -1,181 +0,0 @@ -html, -body, -div, -span, -p, -a, -table, -input, -ul, -li, -* { - margin: 0; - padding: 0; - font-size: 12px -} - -body { - background-color: #444; - height: 100%; - min-height: 768px -} - -.main { - height: 900px; - width: 100%; - background-size: cover; - position: relative; - z-index: 0 -} - -.warp { - background-color: #f6f6f6; - width: 700px; - height: 370px; - position: absolute; - top: 50%; - left: 50%; - margin-top: -340px; - margin-left: -350px; - box-shadow: 0 0 10px #666; - border-radius: 3px -} - -.warp .logo { - background: url(../images/logo.png) no-repeat; - height: 37px; - left: 50%; - margin-left: -52px; - position: absolute; - top: -67px; - width: 103px -} - -.warp .title { - height: 70px; - line-height: 70px; - text-align: center; - font-size: 26px; - border-bottom: #ababab 1px solid; - margin: 0 20px; - color: #444 -} - -.warp .form { - padding: 20px -} - -fieldset { - background-color: #fff; - padding: 10px; - margin-bottom: 20px; - border: #ccc 1px solid -} - -fieldset legend { - padding: 0 5px; - font-size: 14px; - color: #000 -} - -fieldset p { - line-height: 40px; - color: #555 -} - -fieldset p .tit { - display: inline-block; - margin-right: 10px; - text-align: right; - width: 100px -} - -fieldset input { - margin: 0 7px; - height: 28px; - border: #ccc 1px solid; - width: 200px; - padding: 0 5px; - border-radius: 2px; - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s -} - -fieldset input:focus, -fieldset input:active { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -.text { - background-color: #f3f3f3; - border: 1px solid #ccc; - border-radius: 2px; - display: inline-block; - height: 26px; - line-height: 28px; - margin: 0 7px; - padding: 0 5px; - width: 188px -} - -.submit-btn { - background-color: #20a53a; - border: 1px solid #20a53a; - color: #fff; - cursor: pointer; - font-size: 16px; - height: 42px; - line-height: 35px; - text-align: center; - width: 330px; - margin: 12px 164px; - border-radius: 3px -} - -.submit-btn:hover { - background-color: #10952a; - border: 1px solid #10952a -} - -.success { - background-color: #f6f6f6; - border-radius: 5px; - box-shadow: 0 0 140px #556789; - height: 140px; - left: 50%; - margin-left: -150px; - margin-top: -70px; - position: absolute; - text-align: center; - top: 50%; - width: 300px -} - -.success p { - font-size: 20px; - line-height: 90px -} - -.success a { - color: #3385d6; - font-size: 16px; - text-decoration: none -} - -.copyright { - color: #9d9d9d; - text-align: center; - margin-top: -50px; - position: relative; - z-index: 3 -} - -.copyright a { - color: #9d9d9d; - text-decoration: none -} \ No newline at end of file diff --git a/route/static/css/login.css b/route/static/css/login.css deleted file mode 100755 index 97116e984..000000000 --- a/route/static/css/login.css +++ /dev/null @@ -1,539 +0,0 @@ -body { - background-color: #444; - font-family: 华文黑体, "Microsoft YaHei", 微软雅黑, STHei, "Helvetica Neue", Helvetica, Arial, sans-serif -} - -input { - -webkit-appearance: none -} - -.main .login { - background-color: #fff; - border-radius: 4px; - height: 290px; - left: 50%; - margin-left: -220px; - margin-top: -180px; - padding: 35px 40px 50px; - position: absolute; - top: 50%; - width: 360px -} - -.main .login .rlogo { - text-align: center; - font-size: 26px; - color: #444; - cursor: pointer; - height: 40px; - margin-bottom: 40px; - overflow: hidden -} - -.main .login .title { - color: #444; - font-family: "微软雅黑"; - font-size: 28px; - margin: -10px 0 46px; - text-align: center -} - -.main .login .line { - height: 42px; - margin-bottom: 20px; - line-height: 42px; - position: relative -} - -.main .login .line span { - width: 70px; - float: left; - color: #757584 -} - -.main .login .line .inputtxt { - background: transparent; - float: left; - width: 338px; - height: 30px; - line-height: 30px; - border: #d1d1d1 1px solid; - padding: 5px 10px; - position: relative; - z-index: 1; - box-shadow: 0 3px 5px -4px rgba(0, 0, 0, 0.4) inset, -1px 0 3px -2px rgba(0, 0, 0, 0.1) inset; - border-radius: 3px -} - -.main .login .line .code { - width: 230px -} - -.main .login .line .getcode, -.main .login .line .picgetcode { - width: 100px; - text-align: center; - height: 38px; - line-height: 38px; - border: #cacad9 1px solid; - background-color: #dbdbea; - color: #757584; - font-size: 14px; - float: left; - margin-left: 17px; - cursor: pointer -} - -.main .login .line .getcode.no-drop { - cursor: no-drop -} - -.main .login .line .getcode.pointer { - border: #47acff 1px solid; - background-color: #47acff; - color: #fff -} - -.main .login .line .picgetcode img { - width: 100%; - height: 100% -} - -.main .login .line .codemsg { - background: #e53451; - border-radius: 3px; - color: #fff; - font-size: 12px; - height: 21px; - line-height: 22px; - overflow: hidden; - padding: 0 6px; - position: absolute; - right: 127px; - text-align: right; - top: 5px; - z-index: 0 -} - -.main .login .line .inputtxt:focus { - border: #70c3ec 1px solid; - box-shadow: 0 0 5px #eee -} - -.main .login .yzm .inputtxt { - width: 64% -} - -.login_btn input { - width: 100%; - height: 44px; - background: #20a53a; - background: linear-gradient(#20a53a, #189d32); - box-shadow: inset 0 1px 2px #30ad42; - color: #fff; - text-shadow: #00851a 0 -1px 0; - border: 1px solid #20a53a; - text-align: center; - font-size: 16px; - color: #fff; - border-radius: 3px; - cursor: pointer -} - -.login_btn input:hover { - background: #10952a; - background: linear-gradient(#10952a, #088d22); - border: 1px solid #10952a -} - -.login-link { - text-align: right; - margin-top: 22px -} - -.login-link span { - color: #cacad9; - padding: 0 8px; - text-align: center -} - -.login-link a { - color: #757584 -} - -.copyright { - color: #9d9d9d; - text-align: center; - margin-top: -50px; - position: relative; - z-index: 3 -} - -.copyright a { - color: #9d9d9d; - text-decoration: none -} - -.pwinfo { - bottom: 19px; - color: #999; - position: absolute -} - -.resetpw { - bottom: 20px; - color: #20a53a; - position: absolute; - right: 40px -} - -.Validform_checktip { - color: #999; - font-size: 12px; - height: 24px; - line-height: 24px; - overflow: hidden; - padding: 0 6px; - position: absolute; - right: 10px; - text-align: right; - top: 14px; - z-index: 1 -} - -.Validform_wrong { - background: #e53451; - border-radius: 3px; - color: #fff; - float: right -} - -.Validform_right { - padding: 0 10px; - color: #71b83d; - background: url(../images/right.png) no-repeat right center/16px auto -} - -.main .login .line .Validform_error { - border: #e53451 1px solid; - box-shadow: 0 0 2px #ea5d74 -} - -.main .login .yzm .Validform_wrong { - background: #e53451; - border-radius: 3px; - color: #fff; - font-size: 12px; - height: 24px; - line-height: 24px; - overflow: hidden; - padding: 0 6px; - position: absolute; - right: 127px; - text-align: right; - top: 14px; - z-index: 1 -} - -.main .login .yzm .Validform_right { - color: #71b83d; - padding: 0 10px; - right: 130px -} - -.main .login .line .inputcode { - width: 208px -} - -.zun-form { - background: #fff none repeat scroll 0 0; - border-radius: 5px; - color: #888; - font: 12px "Helvetica Neue", Helvetica, Arial, sans-serif; - margin-left: auto; - margin-right: auto; - max-width: 90%; - padding: 20px 10px; - text-shadow: 1px 1px 1px #fff -} - -.zun-form-new { - background: #FFF; - padding: 20px 0 0; - font: 12px "Helvetica Neue", Helvetica, Arial, sans-serif; - color: #888 -} - -.zun-form-new .line { - padding: 5px 20px -} - -.zun-form-new .line:first-child { - padding-top: 0 -} - -.zun-form-new .line:after { - display: table; - content: ""; - clear: both -} - -.zun-form-new h1 { - font: 25px "Helvetica Neue", Helvetica, Arial, sans-serif; - padding: 0 0 10px 40px; - display: block; - border-bottom: 1px solid #dadada; - margin: -10px -30px 30px -30px; - color: #888 -} - -.zun-form-new h1>span { - display: block; - font-size: 11px -} - -.zun-form-new label { - display: block; - float: left; - width: 80px -} - -.zun-form-new .info-r { - margin-bottom: 5px; - margin-left: 80px; - position: relative; - color: #666 -} - -.zun-form-new .info-r label { - margin-bottom: 0; - padding-top: 5px -} - -.zun-form-new .info-r label input { - margin-right: 5px -} - -.zun-form-new .info-r p { - margin-top: 10px; - color: #999 -} - -.zun-form-new label>span { - float: left; - text-align: right; - padding-right: 10px; - margin-top: 8px; - color: #565656; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-weight: bold -} - -.zun-form-new input[type="text"], -.zun-form-new input[type="email"], -.zun-form-new input[type="password"], -.zun-form-new input[type="number"], -.zun-form-new textarea, -.zun-form-new select, -.webEdit-box input, -.webEdit-box textarea, -.webEdit-box select { - border: 1px solid #ccc; - color: #888; - height: 30px; - line-height: 30px; - margin-right: 6px; - margin-top: 2px; - outline: 0 none; - padding-left: 5px; - width: 100%; - border-radius: 2px; - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s -} - -.zun-form-new input:focus, -.zun-form-new input:active, -.zun-form-new select:focus, -.zun-form-new select:active, -.dataTables_paginate select:focus, -.dataTables_paginate select:active, -textarea:focus, -textarea:active, -.webEdit-box input:focus, -.webEdit-box select:focus, -.webEdit-box input:active, -.webEdit-box select:active, -.dlt-text:focus, -.sp-text:focus { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -.zun-form-new select { - height: 32px; - padding: 0\9; - margin: 0\9 -} - -.zun-form-new textarea { - height: 100px; - padding: 5px 0 0 5px; - width: 80% -} - -.zun-form-new .submit-btn, -.getfile-btn { - text-align: right; - padding: 7px 20px 10px; - background: #f6f8f8; - border-top: 1px solid #edf1f2; - margin-top: 10px; - clear: both -} - -.zun-form-new .submit-btn button:first-child, -.getfile-btn button:first-child { - margin-right: 4px -} - -.zun-form-new .button { - background: #FFF; - border: 1px solid #CCC; - padding: 10px 25px 10px 25px; - color: #333; - border-radius: 0 -} - -.zun-form-new .button:hover { - color: #333; - background-color: #ebebeb; - border-color: #adadad -} - -.btn { - display: inline-block; - padding: 5px 12px; - margin-bottom: 0; - font-size: 14px; - font-weight: 400; - line-height: 1.42857143; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -ms-touch-action: manipulation; - touch-action: manipulation; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-image: none; - border: 1px solid transparent; - border-radius: 3px -} - -.btn-sm { - border-radius: 3px; - font-size: 12px; - line-height: 1.5; - padding: 4px 10px -} - -.btn-danger { - background-color: #cbcbcb; - border-color: #cbcbcb; - color: #fff -} - -.btn-danger:focus, -.btn-danger.focus { - color: #fff; - background-color: #c9302c; - border-color: #761c19 -} - -.btn-danger:hover { - color: #fff; - background-color: #c9302c; - border-color: #ac2925 -} - -.btn-danger:active, -.btn-danger.active, -.open>.dropdown-toggle.btn-danger { - color: #fff; - background-color: #c9302c; - border-color: #ac2925 -} - -.btn-success { - color: #fff; - background-color: #5cb85c; - border-color: #4cae4c -} - -.btn-success:focus, -.btn-success.focus { - color: #fff; - background-color: #449d44; - border-color: #255625 -} - -.btn-success:hover { - color: #fff; - background-color: #449d44; - border-color: #398439 -} - -.btn-success:active, -.btn-success.active, -.open>.dropdown-toggle.btn-success { - color: #fff; - background-color: #449d44; - border-color: #398439 -} - -@media screen and (max-width:640px) { - body { - background-color: #eee - } - - .main .login .rlogo { - background-color: rgba(0, 0, 0, 0.8); - color: #fff; - font-size: 20px; - height: 2.8em; - line-height: 2.8em; - margin: 0 -3.3% 25px -3.3% - } - - .main .login { - background-color: #eee; - border-radius: 0; - height: 330px; - left: 0; - margin-left: 0; - margin-top: 0; - padding: 0 3%; - position: absolute; - top: 0; - width: 94% - } - - .main .login .line .inputtxt { - background-color: #fff; - border: 1px solid #d1d1d1; - width: 93.6%; - padding: 5px 3%; - font-size: 1.2em - } - - .main .login .yzm .inputtxt { - width: 57% - } -} \ No newline at end of file diff --git a/route/static/css/site.css b/route/static/css/site.css deleted file mode 100755 index 358ac8c93..000000000 --- a/route/static/css/site.css +++ /dev/null @@ -1,5211 +0,0 @@ -body { - line-height: 1.4; - color: #333; - font-family: "微软雅黑", Arial, Helvetica, sans-serif; - font-size: 12px -} - -input, -textarea, -select { - font-size: 100%; - font-family: inherit; - outline: none; -} - -body, -h1, -h2, -h3, -h4, -h5, -h6, -p, -ul, -ol, -form { - margin: 0 -} - -h4, -h5, -h6 { - font-size: 1em -} - -ul, -ol { - padding-left: 0; - list-style-type: none -} - -fieldset, -img { - border: 0 -} - -a { - color: #333; - border: 0; - text-decoration: none -} - -a:hover { - text-decoration: none -} - -a:link { - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - -webkit-touch-callout: none -} - -:-moz-placeholder { - color: #999 -} - -::-moz-placeholder { - color: #999 -} - -input:-ms-input-placeholder { - color: #999 -} - -input::-webkit-input-placeholder { - color: #999 -} - -body, -html { - height: 100% -} - -.f12 { - font-size: 12px -} - -.f14 { - font-size: 14px -} - -.f15 { - font-size: 15px -} - -.f16 { - font-size: 16px -} - -.f18 { - font-size: 18px -} - -.f20 { - font-size: 20px -} - -.cw { - color: white -} - -.c0 { - color: #000 -} - -.c3 { - color: #333 -} - -.c4 { - color: #444 -} - -.c5 { - color: #555 -} - -.c6 { - color: #666 -} - -.c7 { - color: #777 -} - -.c8 { - color: #888 -} - -.c9 { - color: #999 -} - -.cbt { - color: #20a53a -} - -.bgw { - background-color: white -} -.move_class{ - float:right; -} -.bge6 { - background-color: #f2f2f2; -} -/* .bge6 { - background-color: #e6e9ee -} - */ - .btn{ - vertical-align: inherit; - } - .file-box .btn{ - vertical-align: middle; - } -.file-box .page{ - border:none; -} -.file-box .Pcount-item{ - border-right:1px solid #ececec; -} -.file-box{ - padding-bottom: 1px; -} - -.plr10 { - padding: 0 10px; -} - -.plr15 { - padding: 0 15px; -} - -.plr20 { - padding: 0 20px; -} - -.ptb10 { - padding: 10px 0; -} - -.ptb15 { - padding: 15px 0; -} - -.ptb20 { - padding: 20px 0; -} - -.pd0 { - padding: 0; -} - -.pd15 { - padding: 15px; -} - -.pd20 { - padding: 20px; -} - -.pr8 { - padding-right: 8px; -} - -.pl7 { - padding-left: 7px; -} - -.pb15 { - padding-bottom: 15px; -} - -.pb55 { - padding-bottom: 55px; -} - -.pb70 { - padding-bottom: 70px; -} - -.mt10 { - margin-top: 10px; -} - -.mtb10 { - margin: 10px 0; -} - -.mtb15 { - margin: 15px 0; -} - -.mtb20 { - margin: 20px 0; -} - -.mlr15 { - margin: 0 15px; -} - -.mlr20 { - margin: 0 20px; -} - -.mb15 { - margin-bottom: 15px; -} - -.mr50 { - margin-right: 50px; -} - -.mr20 { - margin-right: 20px; -} - -.ml33{ - margin-left: 38px; -} -.ml45 { - margin-left: 50px; -} - -.ml5 { - margin-left: 5px; -} - -.ml10 { - margin-left: 10px; -} - -.mr5 { - margin-right: 5px; -} - -.mr20 { - margin-right: 20px; -} - -.ml0{ - margin-left: 0; -} - -.mg10 { - margin: 10px; -} - -.va0 { - vertical-align: 0 -} - -.ico-font-ask { - border: 1px solid #999; - border-radius: 8px; - display: inline-block; - font-family: arial; - font-size: 11px; - font-style: normal; - height: 16px; - line-height: 16px; - margin-left: 5px; - text-align: center; - width: 16px; - cursor: help -} - -.btlink { - color: #20a53a -} - -.btlink:hover { - cursor: pointer -} - -.btn-btlink { - border-color: #20a53a; - color: #20a53a; - vertical-align: 1px -} - -.btn-btlink:hover { - border-color: orange; - color: orange -} -.input-edit{ - cursor: pointer; - border:transparent 1px solid; - min-height: 20px; - /*min-width: 150px;*/ - display: inline-block; -} -.input-edit:hover{ - border:#ccc 1px solid; - background-color: #fff; -} -.baktext{ - height: 20px; - border:#ccc 1px solid; - min-width: 150px; -} -.b-shadown { - transition: border-color .15s ease-in-out 0s, box-shadow .15s ease-in-out 0s -} - -.b-shadown:hover { - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - - - -.info-title-tips { - background-color: #fbfbfb; - border: 1px solid #eee; - line-height: 46px; - margin-bottom: 15px; - padding-left: 10px; -} - -.bt-input-text { - border: 1px solid #ccc; - height: 30px; - line-height: 30px; - padding-left: 5px; - border-radius: 2px; - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s -} - -.bt-input-text:focus, -.bt-input-text:active { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -.bt-submit { - background-color: #20a53a; - border-radius: 3px; - width: 140px; - height: 34px; - line-height: 34px; - text-align: center; - color: #fff; - cursor: pointer -} - -.cursor { - cursor: pointer; -} - -.help-info-text { - margin-top: 15px; -} - -.help-info-text>li { - list-style: inside disc; - line-height: 24px; -} - -.relative { - position: relative; -} - -.ico-copy { - background: url(/static/img/ico-copy.png) no-repeat; - height: 14px; - width: 12px; - display: inline-block; - vertical-align: -2px -} - -.zclip embed { - vertical-align: top; -} - -.webDelete .options { - padding: 20px 0; -} - -.webDelete .options label { - width: 30%; - float: left; - font-weight: normal -} - -.webDelete .options label input { - float: left; - margin: 0 10px 0 0; - margin-top: 1px -} - -.webDelete .options label span { - float: left; - margin: 0; - line-height: 16px -} - -.webDelete .vcode { - background-color: #f0f0f0; - clear: both; - font-size: 14px; - height: 40px; - line-height: 40px; - margin: 10px 0; - padding-left: 12px; - text-align: left; - color: #444 -} - -.webDelete .vcode .text { - margin-right: 10px; - margin-left: 10px -} - -.webDelete .vcode #vcodeResult { - display: inline; - height: 26px; - line-height: 26px; - margin-left: 10px; - width: 50px; - color: #444 -} - -.btswitch { - display: none -} - -.btswitch+.btswitch-btn { - outline: 0; - display: block; - width: 3em; - height: 1.8em; - position: relative; - cursor: pointer -} - -.btswitch+.btswitch-btn:after, -.btswitch+.btswitch-btn:before { - position: relative; - display: block; - content: ""; - width: 50%; - height: 100% -} - -.btswitch+.btswitch-btn:after { - left: 0 -} - -.btswitch+.btswitch-btn:before { - display: none -} - -.btswitch:checked+.btswitch-btn:after { - left: 50% -} - -.btswitch-ios+.btswitch-btn { - background: #cdcdcd; - border-radius: .9em; - padding: 2px; - -webkit-transition: all .4s ease; - transition: all .4s ease; - border: 1px solid #e8eae9 -} - -.btswitch-ios+.btswitch-btn:after { - border-radius: .9em; - background: #fbfbfb; - -webkit-transition: left .3s cubic-bezier(.175, .885, .32, 1.275), padding .3s ease, margin .3s ease; - transition: left .3s cubic-bezier(.175, .885, .32, 1.275), padding .3s ease, margin .3s ease; - -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, .1), 0 4px 0 rgba(0, 0, 0, .08); - box-shadow: 0 0 0 1px rgba(0, 0, 0, .1), 0 4px 0 rgba(0, 0, 0, .08) -} - -.btswitch-ios+.btswitch-btn:active { - -webkit-box-shadow: inset 0 0 0 2em #e8eae9; - box-shadow: inset 0 0 0 2em #e8eae9 -} - -.btswitch-ios+.btswitch-btn:active:after { - padding-right: .8em -} - -.btswitch-ios:checked+.btswitch-btn { - background: #20a53a -} - -.btswitch-ios:checked+.btswitch-btn:active { - -webkit-box-shadow: none; - box-shadow: none -} - -.btswitch-ios:checked+.btswitch-btn:active:after { - margin-left: -.8em -} - -.bt-warp { - position: relative; - min-height: 100% -} - -.bt-warp>.container-fluid { - padding: 0 -} - -.main-content { - margin-left: 180px -} - -.sidebar-scroll { - background-color: #3c444d; - width: 180px; - z-index: 100; - height: 100%; - position: fixed; - overflow: hidden -} - -.sidebar-auto { - overflow: auto; - height: 100%; - margin-right: -18px -} - -.mypcip { - display: block; - padding: 0 10px; - position: relative; - transition-duration: 500ms; - transition-property: background; - transition-timing-function: ease; - width: 100%; - cursor: pointer; - margin: 1px 0 -} - -.mypcip:hover { - background: #20a53a; - opacity: 1 -} - -.mypcip span { - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAQCAYAAAAS7Y8mAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAGPSURBVDiNldQ9ixRBEAbgZ3Zn1fVOuQU/wK9AMDDS1MvuRxj5CwTBP2CiYmYmGBsYGRsaa2qkkSiCgRd4y3ri3XqzbdDV7Lgs7uwLRc90V1dXv29VVymluxiiRoVkNaoYG7wN+9chpTTBBn7jKA5YhYR+JPQEDxYd6rAPeIwDnOgQuMEIz3F8mUMdC9/wqkPAxb1P0Yv/6ziLX/hTy9c6jXPYXSPwBQywF+Mz7GCCSRGMbqK1Ufwbmb4beI9TuNZFqFWoZCH7Mp0XcaX33y3dUDKfmRdD1bM+BYuoWtaEKeIdyqSvg5+RZSWX6Ri3ZUqmRbxjcqnsx3cyF7Utbpmf4YxcapXcWFNcjiSbOk7Yxhu5BocRaGBeoyXYNII32MTJsJ7cvR/D92aN1xFsFA6HYZ/iiiWjTVyVGyrhBz7jXSQxxq3wPahxJ7JuX30sd9IoshviC77KzdSmag+XYm4QNq0tF+0e7sc1SwPs4xFeLPHvt2ibsfwl24rFl/KLV/jdCho2ZC3a+I6HOB9rR38B10ZjDE49T6kAAAAASUVORK5CYII=") no-repeat 0 center; - display: inline-block; - line-height: 46px; - padding-left: 30px; - white-space: nowrap; - max-width: 146px; - overflow: hidden; -} - -.btpc-plus { - line-height: 40px; - color: #aaa; - font-family: arial; - font-size: 26px; - cursor: pointer; - padding-left: 80px; - transition-duration: 500ms; - transition-property: background; - transition-timing-function: ease -} - -.btpc-plus:hover { - background-color: #20a53a; - color: #fff -} - -.mypcip .btedit { - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFVSURBVDiNndO/ahRRFMfxz+zOav6goKC1S54gBBUECy0lIKTeyrXwFcROyBuENCHdNhaCoCgiWoggGPAJxFgIsbEJJBmTrCfFnBFZNjHrDy4z5849935/Z84tIsJ/6gneFhFR4AHuYHjM4jN4iZWM13ALSyWWcR+rOERrzAYFNvP9Ka7jKrZExKeIWI8IpxivIuJrRFxo5lqJXfzDbwfvcQU3cAnTUOIsfif6zeYDfuEDzuM5LuIatrGBu/hcInCAy1mH2STawyLmknI+56bykFZDsJsUP7CAdhIMUeELXo9YGmbOn4o3Nah6vd5Ot9vd6ff7lfr3dXKMVZnIVVp4MRgMZtLW6LpneJhxpB1lJnfwE49yblRtfPsrLhoLpbqA7fT15jjUERXNQeO6biK11DXYnyCnymdIjI+4jXuJdlJXHqq7cArf620izkXEu5hMj5u7cAQCwbENi6a1WQAAAABJRU5ErkJggg==) no-repeat center center; - width: 16px; - height: 16px; - display: none; - position: absolute; - left: 156px; - top: 14px -} - -.mypcip:hover .btedit { - display: block -} - -.task { - position: absolute; - right: 6px; - top: 14px; - height: 20px; - width: 20px; - line-height: 20px; - background-color: #fc6d26; - z-index: 99; - text-align: center; - border-radius: 6px; - cursor: pointer; - font-family: arial; - font-size: 14px; - font-weight: bold -} - -.softnum { - position: absolute; - left: 154px; - top: 12px; - height: 20px; - width: 20px; - line-height: 20px; - background-color: #fc6d26; - z-index: 99; - text-align: center; - border-radius: 6px; - cursor: pointer; - font-family: arial; - font-size: 14px; - font-weight: bold; - display: none; - color: #fff -} - -.cmdlist li { - border-bottom: 1px solid #dbdbea; - line-height: 48px -} - -.cmdlist li .titlename { - padding-left: 12px; - position: relative -} - -.cmdlist li .state { - float: right -} - -.cmdlist li .titlename:before { - background-color: #20a53a; - border-radius: 3px; - content: ""; - height: 5px; - left: 0; - position: absolute; - top: 6px; - width: 5px -} - -.cmdlist li .cmd { - height: 200px; - background-color: #424251; - overflow: auto; - line-height: 22px; - color: #fff; - padding-left: 10px; - font-family: arial -} - -#remind td { - vertical-align: middle -} - -#remind .titlename { - position: relative; - text-overflow: ellipsis; - height: 20px; - overflow: hidden; - width: 300px; - white-space: nowrap -} - -#remind .titlename:before { - background-color: #20a53a; - border-radius: 3px; - content: ""; - height: 5px; - left: -10px; - position: absolute; - top: 6px; - width: 5px -} - -.btn-default[disabled], -.btn-default:active[disabled] { - background-color: #f7f7f7; - color: #bbb; - opacity: 1 -} - -.table-page { - height: 32px -} - -.table-page a { - border: 1px solid #ccc; - float: left; - height: 30px; - margin-left: -1px; - outline: 0 none; - position: relative; - width: 34px; - z-index: 1; - color: #666; - vertical-align: middle; - text-align: center; - line-height: 30px -} - -.table-page a:hover { - border: 1px solid #20a53a; - color: #20a53a; - z-index: 3 -} - -.table-page a.disable { - background-color: #f3f3f3; - cursor: not-allowed; - color: #bbb -} - -.table-page a.disable:hover { - border: 1px solid #ccc -} - -.table-page-select { - float: left; - position: relative -} - -.table-page .table-page-num { - width: 60px -} - -.page-select-ul { - background-color: #fff; - border: 1px solid #ccc; - bottom: 29px; - left: -1px; - line-height: 22px; - max-height: 150px; - min-width: 100%; - overflow: auto; - position: absolute; - top: auto; - width: 60px; - display: none -} - -.page-selected .page-select-ul { - display: block -} - -.page-select-ul li { - padding: 0 12px -} - -.page-select-ul li:hover { - background-color: #f0f0f0 -} - -.sidebar-auto .menu { - background-color: #353d44 -} - -.menu li { - margin-bottom: 1px; - position: relative -} - -.menu li a { - font-size: 15px; - color: #d6d7d9; - display: block; - line-height: 44px; - padding-left: 52px; - background-repeat: no-repeat; - background-size: 16px auto; - background-position: 25px 14px; - border-left: #404040 2px solid -} - -.menu li.current a, -.menu li a:hover { - background-color: #2c3138; - color: #fff; - border-left: #20a53a 2px solid; -} - -.menu .menu_home { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFZSURBVDiNpdO9a1VBEAXw3zOioIUQEdFGkQdWVtr6D1ikERtRppLU6awUsUkh2CgoBNwhoPhsLESxERR7wcI2XSBNMAHRNFmLbB6Xa64RXFjYOXvmzMfOjmqt/mcd2I+QmUuZOcgbDWWQmTN4iFms4wGu4jC+RMT7wQwy8xBe4FtE3MBXPMdqE5sfLCEzj+Il3kXEE4iIJTzFFbzB9z0FMvMYJphExLPuXUQs4y1eYfmPHmTmbHN+FBGv92zMDm+ulXAzItZHtVaZeRH3sBgRn4ecOyKXcRt3dkuYwyJOZeaJfZyP42TjX1Nrne5SyqSUMu5ho559tpQy2bUP9gL8wHYnzbtYzczTuB8RHzs8/H0Sz+ETbuEDxg3f7pL6AhW/2vknNiNiCxvY6uDT8e2XMINLmbmC89js3I0z8wLOdP36Aht43KIcwULD1+w82/WGT+dk8DP96/oNlqecb6uu8YEAAAAASUVORK5CYII=") -} - -.menu .current .menu_home, -.menu .menu_home:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKTQAACk0BtZPkxgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFVSURBVDiNpdO9atVBFATw3/UjBEGidkJSRFFIiKJBsAmaBxDFQptUIoJPYCc2Wmpjo4haRUFMnsEmnXUIKQXFRlEvaiIxY/HfheXCNUUWht2dM+fsYXe2l8Ruxp4d4jN4gDNlvw/70dMQw8ZRXMdFbOAvpjGJ93iHrWEdHMdDfMMcPuMpjiC4hYPDOpjCPazgceGeYS8m8BYj2AZJWpxOspTk9gBfcTPJSpJrlRtMXk5yY0hyxUKSF0lOtQWuJHmV5NIOyRWXk7xOcrVe4iSW8BXj/3mZ+jpfiv5ErThS5udJzjcnHUgyXubKnU3yUrcerR38wSH0sVm4C3iDO+Xm5wu/hX44jI3WB4OePoaPeIIPOm9U3XYVtQV6JfC77PtYxyrW8KPwP9vDWiNFZ5BzOtvO4Fejm8ZJzGqM1BbYLHiE77pPc7fEPuE+FjCGxaLV2+13/gdXJgTGYi2BZQAAAABJRU5ErkJggg==") -} - -.menu .menu_web { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAHuSURBVDiNpZO/S9ZRGMU/5xZl0NJQDf2ybAgrGgylGloiCR0jDAO3+4WQlqA/oiHkpUGvgYtFRENL5g+CCEIiqSjMoFKsaJFqScMXfU9DV3vVluiZDs997odzuM+Vbf6n1v+tmVJqA9okHba9CXgF9McY+1fPqtpBSmkH0Al8B45Iema7DDRlyHagK8b4ZQ2gp6dnn6QB2+2SdgFvJW2xPQeUbdcCM5L6gNYY4/QyIKW0AbgGPAb2AueALuAQMA9MApeAu7YnJZ0ALscYyyE7aZf0JsZ4R1I/MAKMAwFYl+0/AW4URXFb0gTQTh4AOGP7Xta7gbEY43NJw8BA1hPAHoA821wdYUxSCZi13QhslPTI9kmgLGkUOG37m6QXwGbbF2OMTUvPuGD7JzCbM2N7FigD87bnJC3r7HyhOsK0pNEY46CkoRxhRNJDYDjGOGz7fdYPsqNP1YBB2y0Ai4uLH4GjKaUG281AS0qpAThQqVSmsrsWYAj+bOIt4Gpvb+/ZEMJ+4BTwLp8ZaASOhRA6UkofgHrgyupFqpN0H7gA7ATGJW21/SNvY10IYcZ2n+3WoiimVgAAuru7a0MIncBXoF7SU9sLwHHgJbDN9vWiKD4v3VkBACiVSqqpqekAzgMHc/s1vz/TTVbVGsC/1i9dw/hm1FHr2QAAAABJRU5ErkJggg==") -} - -.menu .current .menu_web, -.menu .menu_web:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAGCSURBVDiNpdLPaw9wGAfw13fExWVq1IzYJEkc5kezgws5zM2S+irOWi7KH+EgB0e1m9ZycMGWm0jLRYqUWNNyIDkw2lq9Hb7P5rsfDvKpT717Pp/n/byf53k3kvif0/GX+AXcx3t8wgQurvexsUrBDozgGw7jBRZwHK+wHbeKdA1BLx6iiZ14i078LJLd+IJRnMVMO8Em3MQT7MH5qnQQ8/iAq7hXeBDXsLCxqjfxBuPVRide4xA2lPxnuIPP2Fo5o5JIMp6ku/BAkuHCp5MMFr6S5Gjh7iRjSSwp6MUpzOEYNmMRJ6v/rmqnG7uwpVpdJljEryKYr9hcJc+3DXIJd1TOMsEMnmMW36vSYzQq4Sn21Vqn0INz/DHSBIYKf8QR9ONMxfuxH9P1ZwiT7Qru4gaGsbfm8a7eUnMZwCUtdx7AdVYaqQ8PtCzbo7XGLvyo/vusNNL0agJabhvB16oyVcM6gZfYhts1q9LX2mv7bSS5nGQyyWzdR0ma6/xdo+Cfz28JnsxkWP6vVAAAAABJRU5ErkJggg==") -} - -.menu .menu_ftp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAH6SURBVDiNpdM/qJZlGAbw3/1+n31o/0BJrMFaGmoRRBsM14M4iINLixBxHs6pxSxBmhpCqEk5CL2Pp5ZAdBD/UIJNhdBQzTqJYKkoB8HEY+r53rvB99hnuXlNNzfXfT3X9dzPE5npWTB8WrPWuhvvYwvGOI+vSykn/8uNSQfz8/MvdV33DRaxBlMInMZ9LI7H449mZ2cfLM80y0Xbtiu7rjsZEecz8ws8xBEc7l3swfXBYHB8bm6u+Z9A0zSfYmtmXoyIg/gKV3AJLQ7hV+wcjUYzT0Sota7C2Yg4kJm7sQM/YQOWcAFb+ygn8HHXdVMzMzMPlx1swML09PSPEXEIpzCNc/gBH+D7zPyylHIGt5umeWsywou4B5n5BhZKKTdwFddKKTdxJyLe7PmLWD0pcB2v9PVlvN627WZsxKZa6ya8hj96zsuZuUD/DobD4YWlpaUVtdbP8A62RcRNrO+38SG2I2qtv2AUERcfXyLUWnfhu8zcGBEFv+PVPtp9vI2j+A3vlVKOPSHQi+zHrcz8NiLebZrmr3yE1ePx+OfBYLAfK0opnz9eY9u2WzJzHRJ/Yy3uRsTePnfgSmYexAu4gZX9/NUh9kXElH8xxgjPTfTWR8RmPMBg+XCcGOKTiHh+gtxFxKqu685gXd/7MyJ29g6bCe7teNbv/A8ZE8Q3GMBOFwAAAABJRU5ErkJggg==") -} - -.menu .current .menu_ftp, -.menu .menu_ftp:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAGVSURBVDiNpdLdaw9wFAbwzw+jzVspIxdIuXHhQqbGHVmSRLlx4w9YKbkhpVz4A6SkKOWlldA0uXA1Um645motL7NImsnYNI+LnZ9+W63IqW+dzvc8zznPOaeRxP/YgnnixzCISUzgEQ7/DcEK3MEefEMDbfiKI7iKxfMRtKMfT3EePwtwCdM4gVHcnoVL0nxnk0wl2ZvkYZLuJKeSHE+yO0lfkgOZsd4mrgnuSPI4SU+SW0nGkwwkeZ1kKMmDJGNJric5mGQwSVsrQXeSu+V3JbmRZE2SK0kuJuks4i2Vcz/J1iR/tCzH9/I34hM+YATv8bEGublyJrCqdYijWF3+MDagC9uwvd46vK2clVXEogq8rHWdwQ7sq6rraxu92F9rfYYleNVKMI3LuFlVh/ECQyVtEmPow3McLYzGnFM+jc+4hl0YR0rvk/pvw7kmoJFkJ9ZW4g90mrnCk6W7gTe4gGU13PbCjzSS9KOnpYvp0jjrZEvGFBY2i+NeI8kmLG1J/IUODFRn8A6HqsPW8/8ydwb/bL8B1eb4OuOuSusAAAAASUVORK5CYII=") -} - -.menu .menu_data { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAHISURBVDiNpZPPi81RGMY/z+3mpmasGBOZEgthQ5SFTDM2svMjOyt9X1eUEhshRbGx1e1cO5M/YVIzSWbjx4RidTeKbl3XgoyYW5rHwvlO35TZzFtn8fac5znPc855ZZvVVL3apJR2AGeAw5JGbI9m6Iukvu0ZIEVEp+TINimlOnANGAc6wCvbHUm/AAHrgZ3AHmA7MC3pVlEUS6WDWaALHIuIb/9xOw3QarVGa7XafduzwGQtg+PA+xXIy9VsNnvAHDBRvYMBcDyldEDSvO1nkj7lCNhea3uLpEO29wOjmbMsUAfOAYu2C+CC7XW2hzL+A1iw3QWu5/0vqgIAD4A7EXFxpQgppRPA1bIvBX4Dt4GJlNIV4DPQlTTIERrApryeA3eBh1WBBkBEnM+n7AO22R7O+ALwISJeZvxkySn/wQD4CDyV9Nj2XET0/7G+ATgIHAEmgbGIaJQO1gCngF22A7iRUloCfgLOpwnoA4+AFvC6GmEGOA1ciogpgHa7vRkYygLfi6LoVdzcI79CGUHAZf5+jh4wb/udpK/5EoclbQX2AruBt8DNiFhUdRpTSmPAWeCopI22R7KDfh6mJ8BURLwpOVrtOP8BlJPKP95zNKgAAAAASUVORK5CYII=") -} - -.menu .current .menu_data, -.menu .menu_data:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKdQAACnUBSiXd/QAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF3SURBVDiNpdI5a5VhEAXg58qFRDBi4RIiBkSJoiIoihZi0EpsFWy0FAtbSwvB0uUn2LiUliIWMWgT90i0sRFcYxqFiCYQcizufHK9hUQcGIZh3jnvOYdpJfE/sayn34pLeI4PWKj8jJe4jJHuhVYxaOM8RvEGT6r+RAursQ27sBl3cBGLkkgynuRmklXV/y0Hk9xOMpbkt4RRvMK3JciexkMc6pYwhyl8wlM8wPuSAMuxAQexF4PYif4GYAH7C+g0hrASKwrgO2bxEdfKs0doN7oWkkwmObEED44leVY7f0g4Wbr24Uv9Nl8M+orVECZwH9fR3+56AGer7sEmDFQ/i7d4XP3xZqdhMI93GMfdcnmmx/01OIAjOIxh9DUAwW5sx6lyeRE/atanc1AzuIXXOtfaaoy5l+Rqj1nrk2xJMlLH0z27kmSi28QWzpWJ0zq3MIWvRX8AG4vlDkziAuYagCaGcQZHsQ5rS8JM5Rhu4EWz0Avwz/ELJiL9PSckq44AAAAASUVORK5CYII=") -} - -.menu .menu_set { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAH3SURBVDiNpZI/SJZhFMV/90kicsv+EdEQRUNQYC1S0R+ag2hxMKPgvc/r0Ba2FFEpJARhiF/P9QsaGippimgoG4QgI6egICcJEkoIEyI/9L0NvV98llLQmS6c5x7OPc8Rd+d/0LQcUa1W1xZFcR9ocvfOGOPkXwXMbDvQoqov3b0NuA00i8gBYNLM9gBfVXWiviP1E8xsB3AZmANmgHVAt4jMu/sl4DuwFfgI9KvqO4DQYKAFmFXVU8Bj4JqqfsiybAroBZ6r6jFglbtv+MNB6aIHeKKqL4aGhra4eycQRORelmXvU0pHgPYYoy4SSCltFJGDwEngCjANXHT3WyGEeXfvAq4Dze7eKyJ3i6IYyfN8KgCIyAjQDFxQ1VfAaaA/xjiWZdm4iPQBHao6LiLdwJoQwsiiX3D3z0VR1NNd4e5zDfnUACnnWeATsNB4wjYR2Q8cdfcbIYSau58D+kRkwd273X1QRGaAAWC4KIrRPM8nfg/xqrs/jTGOmtlOoKN08zDGOGZm+0SkPcuys7+W3J3Sxd6U0s1ybk0ptda5SqWyOqW0u+QepJTa6lxjD2rASjO7A5wBBszsEEAI4TzQZWaDwBfg23I92AWsV9VnZnYC2OTur0Vks6oOm9lhYFpV3ywp0IiySI/42bzjMca3S71bVuBf8QODpRL9eTmkdgAAAABJRU5ErkJggg==") -} - -.menu .current .menu_set, -.menu .menu_set:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF0SURBVDiNpdJNiM5RGAXw38ukkJVvMYqZRsqYZmyYDTZWbCxsKLG3pCSaWNmNJU2NmhU7pQjFRhYsJCTWykfJRz5eH8fmefn39lLy1FPn3nPv7Zzz3FYS/1Oz/sItwg3cwuo/HWp1KRjEQtzBTizAfHzCDMbwDk97PTCECXzBWyzGYXzDCXzGGjzHJB6DJJ3ekuRc4R1JNja4VUl2FZ5OsrXDNTO4jRcYx1W8wTEcx1xcwna0cbM7xGXYgxF8LakTuIbLZWVdWevHXixvWniY5ECSkVqfbGBJBpOcKjyc5FCSR90WXjXSnV1hdqqNVuH3eInvTQUDSfYnmUkylmRDkvNJ1icZSjJV+wNJriQ5WKr01avPqtfW3O/hNPaVmrO1N14qp35pa/jclORM4dHqDjevMdYLSTZ3uD6/q405mMYHjOJojewIluJHjfdj51L3Vx7GElzHbqzAXazERWzDazzoZaG7+5PcT/Kkwux5rlvBP9dPgIpDWf6ENxgAAAAASUVORK5CYII=") -} - -.menu .menu_folder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAE3SURBVDiNpZO9SgNBFIW/OySiaBCtbCwE8ReUgGDpE9gExNJmd6O+hM8gmGJn3yKxt7WyEARBUUSbIPhTGFAwxyarSwoZ2AO3mjkf98y9Y5IoI1fKDVQA0jRtOOd2B8BRST5Jkk4IwCSRZVkbOAYeACdpD3iX9DrUpQO+gHaz2XwuRnCSLuM4vpU0AnSAvplNmdlkocbNbN3MTrz3de/9WKVA/07TdN85twbcA5/A8AsLuAO6wI6k2UrhoGZmy3EcH4Zk995vAAd5hA9gBXgJMQ+0ZWanOaAH1IGrEGer1TJgxszOckAFWATOQwDVanUO6EVR9JYD5oGJJEmeAtvfBG7gb4xLwEWgGWA1v59PoQs8Zlm2ANg/RgETwLRz7voXIOnIzBrAdgCgBvgoivowWOUyKv0bfwCvBmEVd9ynHgAAAABJRU5ErkJggg==") -} - -.menu .current .menu_folder, -.menu .menu_folder:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAADuSURBVDiNpdO9SkQxEIbh56ynUHQbQbCxEMR/BEGw9B4s1m6t9W5svQgL3d7Wyk4QLES0EcGfQkGbsTgJuywIwTMQEsh8b+ZLJlVEaBOdVmrUad7HQQJO4hQXJYAqWTjHCR4S5BAfeBursoOflP8CIkJEDCJiLq03ozymcgUD9NHDFu7xjfEbjmR7Oo2FemSjizUcl3jHDo6yv0+s47VQDHsYZMAXtnFTKK4wj8sMqLGCq0LAYjr0PQOWMIOnQsAu7hi+8SquC8WwkfPzKzzjEcsaf39FpEpnccuwE3uadp4oAHQ1rX42Cvh3tP6Nv5Cebn/RRiyLAAAAAElFTkSuQmCC") -} - -.menu .menu_day { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFrSURBVDiNpZMxSFxBEIa/ed49tQsBQdEqgrVgithFW1sr62PhKttXpjy0vWb2WjkQrk6rjWUgjY3ICorYGCFV9C7vxuJ24fE4Y8SBZf/9d3b2n/1ZMTPeE1l10ev1Frz3Wy8lq+pXVf1Y5cTM6Ha7jTzPd4BtYBUYAHO184/ALnAJnJRl+b3dbo8aAM1m8xB4AO6BT2b2C5ivFxCRv8AtsJ5l2SZQYGao6kBVFyNeNjOmDVVdifOSqh6Z2aQF7/0xcGNmF4BMkV9tw0RkDfjgnGs14kYJXInIOTALjGOhukUZ8AQ0gc+JIJI/nHNnIYRWCME5505CCC7i04hbzrkz4Gc8Q1IwEwedTmcvXfcSruZnNZKiKPpFUfT/hZnYX1YVyGu31hRk6X2SgnEi/lPBb6JTSUFuZsM3KPiTWk4FbkXki/d+CORMbK3aKEzsTXhDRK6rBb4B+2bWAkbVN5kSDRG5E5EDiJ/pPfEM08DH4VH64rEAAAAASUVORK5CYII=") -} - -.menu .current .menu_day, -.menu .menu_day:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAEPSURBVDiNpdO9SkNBEIbh58RoFKwEwT8QFOwEQQvt1NbW+7Cx8B7svAZLa1tbG0sbEQQh2KigjcZoxsI9YT2EGMnCMt8us+98s8sWEWGYUausp7HTJ38bU/lGPYt72MUyFjFeOfyOfdziAudol4BjPOMRS3jCRA/AJ5pYwxaORISIOIuImaTnU+w1F1KcjYjTiOi28IVD3KDoYT93EVhJ+hfgDtdooJNA1SeqoYVRbOSAFq5wmR0q+mhYzQEjaeYJ/XQ3v1bZlCrFH7rw03bXwSBVc10rYaWDzgBVc/0ivVTpYAwf/3DwJrVcAprYTJCx1F/15huZXsc9FOk3TuIAc2hXqlVHHQ84wWsx7Hf+BgvadUGnT3fcAAAAAElFTkSuQmCC") -} - -.menu .menu_logs { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAApJJREFUeJztm71y4yAQx3dvzn4t41dIm8pzAV+6K65LIblId8WVMeReIa9g/FaZcbFXBGdkjBD6iBAyv5kUktB6+QcW7bAAZDKZWwZDGimlGBEV5pJ9nTuDoBFRc853IY2/hTQiogN8dJx192s0GBGVSqmiuWmAAKGGpgYRlSHtGqeAlJIuXkAMMhyLascRcc05177239sYR8QydG7FQCnFrFsrANC+d4JiwJzJAsR2IDZZgNgOxCYLENuB2LT6DnAhpXwFgB8D+BIEEb0tl8v7zWbzPoS9XiNgv98/wYidBwBAxLvT6fR3KHs3PwV6CbDdbp8B4N9AvgRBRG+LxeLXUPZ6xwAhxAMAPAzgSxTyFIjtQGyyALEdiE0WILYDsbl5AXIu0OflnAvMgJwL9DWQc4HEyQLEdmBIOOe6sncZtEXeOwZMDdPp4P3LWY2ALiQ/As71C0RU2rvXSqnCbJdrRNy5tspb1QdMbXu80kEbDY5qFpf/U8oFXoQQjy1+90BErOax874pnYGqCFPKBX5KKX+HNkZE3fF3jtWLmw+CU8oFXoQQf0Ibe4a/rvy5WFUvkguCJvAxuJ7nV/WBpr7x4DDzuSokNQVM4CvBHeF39j/HfBmuHaYYER2UUkVSAkB9oaauK4ezPo8vIKIyNQGcIGLnaTkLAcAKbG2YiwBePCtGWgL45nJdTbO5z2pMprUKcM53Jqpr+5lLhLpcARFLRFwLIdbJfQec8SRCTWghxOfSmNQIsDg2N7nGziFSFqBz5K+SsgBduRg5ITHgfFzm44WRD0z4Yo4rDhj/jgCwsp45zxK1FmBsQgJvNfo7kqHCd4iqUQBPRjUKX73yNMYAzrkWQmCks0IaOkb7TCaTCeE/v6cw3cuvVicAAAAASUVORK5CYII=") -} - -.menu .current .menu_logs, -.menu .menu_logs:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABHNCSVQICAgIfAhkiAAAAf9JREFUaIHtmUFOwzAQRccWmyaN2HIEOAXpIag4BnsWcRfsOQZSD9EgNhyBI3SLnMnSw6IpciI7iUMdB8lv0zTxKDPyOF/6AxCJ/AnW9xARCyLKGWP5TPm0IKKSMVamabqzrbEWgIgFAAgfibmilNpkWVaanl31xInzBREZg30zZuf7CgCAU/Lr9XpzkYwckFL+ti7n/B4AStM6PmNOXogFhCYWEJrBr5AOEV0j4idj7PbCeezTNN1OCXTagbqunzwkDwDwUNf145RApwKSJHkloq8pLxpgnyTJ25RApxZijH0DwN2UF/ni3x/iWEBoYgGhiUJ2IaKQjSIK2ZwgIiEiVVV1CJhDMfn9SyhgDItsISll3vhSxv86TofYN10zDRFBdwYRUQCA0J26PmeOANq+0B+E7Mg5365Wqw/bAillzjkf1a66UzeXkN0opZ4nxA0yl5AdOecvvYmc3LcWRFQO2ZrBhayxEAsAyPX7XUO3qqqDZjUemvMQ/hAzxgqDiSu6bjQR7TrrBMBCP6OmeUCWZcZ2WmQBNkx2+yILMImWTciCF9BojOjcFl0l7q4holIptXESMp+4CBloahx8B87YZmBDLKYAF5RS7+drawvpwnHpIZ9tdGoY6wr9t1Hmnb5bIceswjb/bQ5tq62klLmpzQYH3eChiDED7EhkJn4A1qwofp3F9mcAAAAASUVORK5CYII=") -} - -.menu .menu_control { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF5SURBVDiNpdM9ixNRFMbx3yRZ3/ANbCxEcBubpNJSLIQFaxs7D4JfQSz9CpYWItyPYCOIFtqvhQxopSyCb1gpEuNukrHImewQY7UHLsOce85z/vPMvVXTNA4SAyilfMI2Zpn/n2rV6RtGxOYgE9sITLK5Qa8jVGGezwpH8VgWwRTjiJigj6vYwrVcW5nrZ824pW0Jeh0xSVJhr4M86ez32/qBf6OH04nZCmzk1HbI0qN1AhO8zuYfOJ5i8xWKZq1AREzxpZRyAlfwJiI+r5RVrUCLNOvgtnER93FpDeV0laBnYcwMSimHcQpP7Z+V2xZelA7FkuA8opRyK9/P4gIeYq+Ucj2/fwM3Lc7Mua7AVzzH91LKHZxBExE/s/EudiwOz0u8wLeuwDgidvAMf3APr3LvHR7gfUT8joiPw+HwQwqrmqZRSnmSWL9wEpt4a/Hr5qPRaLeu60OdgcfwKCJutCZeTnNmuXZxpDWqruvG/j1o0vDhkuAg8RdE7nuSY6nc+gAAAABJRU5ErkJggg==") -} - -.menu .current .menu_control, -.menu .menu_control:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFaSURBVDiNpdM7a5RREAbg59uLJOgSkRQhfYSIhWWsQgj+gfgLTJEmBJLa0tZW0MbawtbaUkQEQ8Q/YCWIIAqb7G1S7JzNYdlUOfAxlzPznndevmkiwk1OJ+03nGKMBpNr6lsIdLGBrQJwimP05wCatFEBTHAHL2sGY/zHEMt4jBWMKqZ/8RkXVc8MoJX+MF8bJJth3nczV5i0s2cGUJ82eslgkLlbOU57vngRwAA/sqmftp35AhiFzSKAIX6mv4+3C2pa887YlTjlPMAJtjNeMtVC1k5qgA5Wa2Q8xQvsZnyI51jD3cK+jLCOA/xLyn1TET/m3U4+0M+xRpmfAfzGBzzBM/zB17Rf8B57+ISHpsI+AhEhIt6kbSLiKCLOImIzc72IOIyIbsblex0RMwZLuGf6t73Dd/zK/AVepYDLKV4v7zS5jfUyjXCO25Wok/TLnnRwH1vNTdf5EpkVg1v2kuagAAAAAElFTkSuQmCC") -} - -.menu .menu_soft { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAF7SURBVDiNrdPPi41hFAfwz72uoVsUWZOFJbKThYkVe1mpx13YjcmU/NwrZCKRLNRZsrSZErvJ1h+glFgiYxC6Pyze8zSvOwsLzuZ5T9/n/T7n+z3ndCaTiX+JHkTELlzHHozQxQrOl1JeRcReLGIbxtiAN7jcS6JrCT5OcIx9mMcAZ/EBz5N8hCO4UQl240kp5XYtLSJmsZDpdjwspTxr4V9xppv5EP0peVuzEnlumcL7GFaCDn5OXfj+l/wHulXCBIci4rXG2BFmMZP4RhyLiBmNR0McxqgSLGBOY1iNIW7l96LGyNMtfBXznf81B/vzhR0pB37hbillOSIO4hw2JdbBJ9yrEu7gIx7504OLWMYFvMcLax6cwv1K0MPLUsrTWlr2eS7TEZZKKUstfCcGtY3jVnk1+i05E+vnZDPGlaBnfZ+/aMZWnqtT+Df0qoR3ON7qc92FlcQ/YxARB6ztwlG8rQRXNdt4Mn/u5AuXEn+AmziRcroaU6/8BgTXdRpxDzi5AAAAAElFTkSuQmCC") -} - -.menu .current .menu_soft, -.menu .menu_soft:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKnAAACpwB9NLfEgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFsSURBVDiNrdO/jw1RFAfwz4xnVzYIydZeY0tEKETChkIoNGrNtkKyhQL/gUg0YgudQiciGpVEIRsdPdlkQ0usH4vNvHcUc27e9bbkm9zMmfnOPfP9nvneJiL8CwZ5PYS72I8xdmANN/EeQ9zGAkZosYHrTSp4gD14m+QIZ/ATl/AI83iRzcc4jC0RISIeR8S5rMu6EhFvsl6NiOUpfjEinrZpYZwKasyhy7rL+xp7MW6rB5tTL/xKO9Dg9xS/WQ9xJ85jJj12OJ2zgMBJvMs9IyxipgzxFK5lo4JvWMFrHMNV7Kv4DivN/8rBkVQwn3JhC/fwCiewjNnkGnzG/aLgJT7h4ZTHBVzEE3w0yUGHyxgWBQOs4lml7rvet2z4PFfBASzVOZj1N+YqO2F7DnapcjCwPQdfTXLQ6v9KjR8YFAsfcMEkByXrG8l/wRKOmpyVs1gvQzyoP23D3NzkF27oc3Acd7A77bT6od76A2AskgeNVoIQAAAAAElFTkSuQmCC") -} - -.menu .menu_firewall { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAG0SURBVDiNhdNPiI5RFAbw352ZsGBDMhkpYyE12cxOyYIFayW7SY2zkCxIlkrZKIrJjPe+X1FksrQQo/xp2IhZiPXUxIoaGxa+mGsx78dn+vDUvXVu53l6znO6qZSig6mpqbX9/f37cRB7sDOl1C6lzOIZHkXEK11IpRR1Xe8upRzFKNqYxzt8xGpsxy6swwCeYjIi5gcaoTt4i8BcRBQ90Gq11i8tLY3gCjZibABKKUu4FxGvexE7GB8fX8RsVVVPUkqDGjtQGqv/RM75CJ6nlH6gT+dCwvf/kE9hGlvwrZfAqrqu+3LOQz3Ix3EJpyPipeUwdQt8RbuUshkLjdUOeQzXcD4iLnfptvmdwXtsjYgPOefrmM45f0Y/buJiRJzrIm+wvOJfDl5gBCLiBFp4iPuYiIizK6YaSim96Ra4jeGc86ZG5BhyQz65Io9hDJZSZkApRSlFVVUTVVXd7dR/O1VVPaiq6mqn7jiQUjqD0Zxzd1B/IOc8iW1N7zKv+zM1I8zgE8YjYqF534EbTegHImKxp0DTnHABh3ALa3AY0ys20Vugg7qu95ZSHuML9kXEXK++ny1tzgEddf2OAAAAAElFTkSuQmCC") -} - -.menu .current .menu_firewall, -.menu .menu_firewall:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKdQAACnUBSiXd/QAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFrSURBVDiNhdMvaJZRGAXw36viFrRoENxw4IJYLDMJalkxC8OkrC5qNAwEi+CC4lAsikGMY0FQmGIQ/AMKyjDIUGYyqMXgmB7Dd9/P63j37cAN73OfczjPc+7bJFFhFyZxCsdxGGt4hqd4hFc1oSkCxzCNiUJYwXt8xRDGcQS7sQNPMI8VSST5lGQxydEkTal1nT1JTiR5k+RuEtuKkz9YwGv8N9MGfCvjLBUnfYEUq1vhDEbwu+W2Ag3WtyCfx32M4leXwM7yPdJBnsFVXMALvWWqBX7qbX8/PherLc7hBi5hrqqvURaBVRzAF9wsVr9jO+7gCmYr8l69iLXxXEyyUMV1O/9wrSPOpSRnk/QFxpK8S7Kvarq1CflgkuW2t764nuTBgEfUnoe1cH0xnORjkrkB5PkkH5IMdQkott4meVzGauuHkjxP8rI85z6n/ZlqNLiM07iHYUyVZGY3Ng+a9WSS9SQ/kkxs1vcXeVqZSyUF+yoAAAAASUVORK5CYII=") -} - -.menu .menu_exit { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAEYSURBVDiNpZOtTkNBEEbPFCQIUHgEBoJAYFDgERgSCBjSvU9Q0/AkX5+ApB6PI6lAYPhJUCgEgkApkHyI/nBpb28DPWozu3MyuzsTtpmG2f5CUs32BlABouBsAK8RUU8pPY4IImIfOLd9k4/n+IyII2AZGBUAM8BZlmXX48qVtAksFl6hx/y45B5N4C4fqExI+EVK6QJYkLT9L0GPJ+BU0hYUP9YAScfACtDh52c6wAPQlHRQKoiIN+B5WGC7DXzYfi8VVKvV5nCs0WgsRcSh7b0sy1qlgiJszwG1lFIL/viIknaAdkrpsh8bruBlgmOXbq8UduIXcCLpvkDc31+j20wDBgdty/Y6sMroMJnuda+A2/xGTDvO32OQXrvPg7l3AAAAAElFTkSuQmCC") -} - -.menu .current .menu_exit, -.menu .menu_exit:hover { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKdQAACnUBSiXd/QAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAADvSURBVDiNpdO7LkRRFAbg7wwlBZVeoSEKEtHSayUSGs+g8RwqbyDR6ycaiYRC45KoVAqFuAySpdhzODn2mWMyf7KSvdflz7oWEWEUjFfee1hCB0XGt8AL9vGQI9jECa5r+hKf2MZsE8EYjnA1IOMVTDeVAJMDguEYt1VFpyWgji6msNaUwX/wiAN84LSNYAdz6PmdTA/3UjlbbQSveMoQvPUzeBcRpVxExGrl3yQzEdGNiOWIGLqJMCEt3TnDT2G9n/5Zqaj34LmFYEPalewmfmEXdxni0r4gdf8HVcdDLGLe32MKqdxL3FQNxajn/A0ZS19hUhhlTwAAAABJRU5ErkJggg==") -} - -html .menu .menu_home:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPPz8+np6d3d3c/Pz8vLy8XFxb+/v729vbu7u7e3t7W1tbGxsa+vr62traurq6mpqaenp6WlpaOjo6GhoZ2dnZubmwrPOpmZmQzLPJeXl5WVlRLFPhLDPhy5RI+Pjx63RImJiYeHhyypSiirSIODgzKfToGBgTabUDibUDiZUH5+fj6TUnx8fECRVEKPVHp6ekSNVkCPVESLVnZ2dnR0dEyDWnBwcFR4XlZ2Xlh2XlpyYGZmZgAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJHgA7ACwAAAEAEAAOAAAGhsCdcLgzJUjE5HCmECxMFYZHuWMRHDuJINIg2JIqAmQYOS6SpEEkORFciOkJlVIwCTUEDfVOyOweFyAzVDMgFw1DBSFDOkMkBUQ2B0g1HyIdNUUGX0OTOy0jNyMrRQecOzYIKjsyKDspMDsvpkQGFDklJzsoJTgVBkkIAAEYLjsuGAEACEJBACH5BAkeADsALAAAAQAQAA4AAAZxwJ1wuDMlSMTkcKYQLEwVhke5YxEcO4kg0iDYkioCZBg5LpKkQSQ5EVyI6QmVUjAJNQQN9U7I7B4XIDNUMyAXDUMFIVQkBUQ2B0hKJgZfQ5FUJgeWOzYIKlQvm0QGFEM6QxUGSQgAARYcIhsWAQAIQkEAIfkECR4AOwAsAAABABAADgAABnfAnXC4MyVIxORwphAsTBWGR7ljERw7iSDSINiSKgJkGDkukqRBJDkRXIjpCZVSMAk1BA31TsjsHhcgM1QzIBcNQwUhVCQFRDYHSEomBl9DkVQmB5Y7NggqQzpDL5tEBhQ5NR8iHTU4FQZJCAABGC47LhgBAAhCQQAh+QQJHgA7ACwAAAEAEAAOAAAGfcCdcLgzJUjE5HCmECxMFYZHuWMRHDuJINIg2JIqAmQYOS6SpEEkORFciOkJlVIwCTUEDfVOyOweFyAzVDMgFw1DBSFUJAVENgdISiYGX0ORQzpDJgeWOzYIKjs1HyIdNTsvnUQGFDklJzsoJTgVBkkIAAEYLjsuGAEACEJBACH5BAUeADsALAAAAQAQAA4AAAaCwJ1wuDMlSMTkcKYQLEwVhke5YxEcO4kg0iDYkioCZBg5LpKkQSQ5EVyI6QmVUjAJNQQN9U7I7B4XIDNUMyAXDUMFIVQkBUQ2B0hCOkMmBl9DkTs1HyIdNUUHmDs2CCo7Mig7KTA7L6JEBhQ5JSc7KCU4FQZJCAABGC47LhgBAAhCQQAh+QQFHgA7ACwGAAkABQADAAAGDsDdTiesfUSdWmt0G62CACH5BAkyADsALAAAAQAQAA4AAAYVwJ1wSCwaj8ikcslsOp/QqHRKdQYBADs=") -} - -html .menu .menu_web:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAPHx8e/v7+vr6+np6efn5+Xl5eHh4d/f393d3dXV1dHR0c3NzcvLy8nJycfHx7+/v729vbu7u7m5ube3t6+vr62traurq6mpqaenp6WlpaGhoZ+fn52dnZubm5mZmZeXlxTDPpOTkyqrSiqpSoeHhy6nSiynSoWFhS6lTDCjTDKhTjSfTjadTjadUDSdTjabUDyXUjqXUjyVUj6TUkCRVHp6ekSNVkaLVkSLVnh4eEqHWHZ2dkiHWEqFWE6BWlB+XFJ8XFJ6XFR6XFZ4XlZ2XlpyYFxwYmZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFDgBHACwAAAEADwAOAAAHbYBHgoIdCgIACRyDizsTFQ0XGA4WEjuLNQMnH4shJwU1gw8di4sdD4IaGKSkGBpHC5argzsMRwYbsoMbB0cHHrmCHghHDDnARzm1GhnHGa5HEL+yHxCDmJqcJASg1hEUDRgZkxLGqxoJAQGJi4EAIfkEBQ4ARwAsBAADAAgAAgAABxGAR0dDPkBDgi0gNCgqNCAsgQAh+QQFDgBHACwEAAUACAACAAAHEYBFKUAogyNGPjZHPT5HMD+BACH5BAUOAEcALAMABwAKAAIAAAcWgDcoJjclKDciKTc6Kig9KCo6Jis6gQAh+QQFDgBHACwEAAkACAABAAAHCoA+Nkc6PkcwP4EAIfkEBQ4ARwAsBAAKAAgAAQAABwqARShAJSlAI0aBACH5BAUOAEcALAQACwAIAAIAAAcRgCwgMyUpMiAvR0dDPkJDioEAIfkECTIARwAsAAABAA8ADgAABxaAR4KDhIWGh4iJiouMjY6PkJGSk5SBADs=") -} - -html .menu .menu_ftp:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAP////39/fv7+/f39+3t7evr6+Pj49/f39nZ2dfX19XV1dPT09HR0c/Pz83NzcvLy8nJycfHx8XFxcPDw8HBwb+/v729vbu7u7W1tbOzs7Gxsa+vr62trampqaenp6OjowDZNp+fnwLXNp2dnQTVOATTOJubmwrPOpmZmQrNOgzLPJeXl5WVlRi/QBa/QJGRkY+PjyC1RI2NjSSxRomJiYeHh4WFhYODg4GBgX5+fnx8fECRVESNVkSLVkaLVkiHWHR0dE6BWnJyclB+XFJ8XFR6XlR4Xlp0YFh0YGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJMgBJACwAAAEAEAAOAAAHvYBJgoIhCAICCiuDi4IOEQsBAQ4QE4yCCR06DRUWD0AYDYwaATALNBweMA8sAB6LCC8QAwwEBQwDESwIgzQOSTURQBMVQBA5SQw3grFJKBdJGRtJFCxJDzKCOApJNg01DA42DDZJCziDCRsMAhQGBxIBDBsJiygCORYjqB8XNgAmjDZ8SCLjxg0YSTRkGBSkh48fO3i0UKGiRY8dP3z4GBKDhMcSIkCIBCGihEcSM5AUWVlkyAmRKYiwLHIkEAAh+QQFDgBJACwAAAEADwALAAAHj4BJgoIhCAICCiuDi0kOEQsBAQ4QE4xJCR06DRUWD0AYDYsaATALNBweMA8sAB6DCC8QAwwEBQwDESwIgjQOSTURQBMVQBA5SQw3SbFJKBdJGRtJFCxJDzJJOApJNg01DA42DDZJCziCCRsMAhQGBxIBDBsJgygCORYjqB8XNgAmizZ8SCLjxg0YSTRkEBQIACH5BAUOAEkALAAADAACAAMAAAcIgEE9MSRIRYEAIfkEBQ4ASQAsAgAMAAEAAwAABwWAPiRFgQAh+QQJDgBJACwAAAEADwAOAAAHIIBJgoOEhYaHiImKi4yNjo+QkY8/OzyMJCUijEVFQ4mBACH5BAkOAEkALAAAAQAPAA4AAAepgEmCgiEIAgIKK4OLSQ4RCwEBDhATjEkJHToNFRYPQBgNixoBMAs0HB4wDywAHoMILxADDAQFDAMRLAiCNA5JNRFAExVAEDlJDDdJsUkoF0kZG0kULEkPMkk4Ckk2DTUMDjYMNkkLOIIJGwwCFAYHEgEMGwmDKAI5FiOoHxc2ACaLNnxIIuPGDRhJNGQQFKSHjx87eFgaFIOExRIiQGgEwQhJkY9FJg4KBAAh+QQFDgBJACwAAAEADwAOAAAHsYBJgoIhCAICCiuDi0kOEQsBAQ4QE4xJCR06DRUWD0AYDYsaATALNBweMA8sAB6DCC8QAwwEBQwDESwIgjQOSTURQBMVQBA5SQw3SbFJKBdJGRtJFCxJDzJJOApJNg01DA42DDZJCziCCRsMAhQGBxIBDBsJgygCORYjqB8XNgAmizZ8SCLjxg0YSTRkEBSkh48fO3gs6rHjh6AYJDKWEAGiIwgRJUgIQlKkpKUkJpMEAgAh+QQFDgBJACwNAAwAAwADAAAHC4A+PkMkJDNFRUeBACH5BAUOAEkALAQADAAGAAMAAAcOgElJLSoqLYKISUVDiYEAIfkECTIASQAsAAABABAADgAABx2ASYKDhIWGh4iJiouMjY6PkJGSk5SEJyAgKUSFgQA7") -} - -html .menu .menu_data:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAP////39/fv7+/Hx8e/v7+np6ePj4+Hh4d/f393d3dfX19XV1dPT08XFxcPDw8HBwb+/v7u7u7m5ube3t62traenp6OjowDZNgLXNp2dnZubmwrPOpmZmQrNOpeXlxLFPhTDQBTDPhbBQBTBQBa/QBq9Qhi9Qh63RBy5QiC1RCC1RiKzRiSxRouLi4mJiYeHhyirSCynSoWFhS6lTIODgzKhTjSfTjadTjKfToGBgTibUDiZUHx8fD6TUnh4eHZ2dkiHWHR0dEyDWnJyclB+XFZ4XlZ2Xlh0YFxwYGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFMgBJACwAAAEADwAOAAAHr4BJgkUzIB0XFxsfM0WCgjUlMD1CRERAOiwiNoIlLEiOoElIKSVJFzehoTsXSRggJzZCR0hIR0I2JyIYSQEyOTErKCQkKCoxNDIBvAYaqYIcB8oCHBAHDA4QEA4MBg8cAkkAHIIyGhYWGi/PAEkCBQ4cQaBBHg4F4AA0GQsGBgkJB6xloMFOQQRHP3jwkCcoAgJBFBg0qNAiRw4XGSIskADKxwQDAwIEIGAgAg1BgQAAIfkECQ8ASQAsAAABAA8ADgAABxaASYKDhIWGh4iJiouMjY6PkJGSk5SBACH5BAkPAEkALAAAAAAPAA8AAAe0gEmCRTMgHRcXGx8zRYKCNSUwPUJEREA6LCI2giUsSI6gSUgpJUkXN6GhOxdJGCAnNkJHSEhHQjYnIhigMSsoJCQoKjGgATI5qYI0MgFJAQYayUkcB80CHBAHDA4QEA4MBg8cAkkAHIIyGhYWGi+CHABJAgUOHEGgQR4OBeQANBkLDBhIkOCAtgw04imI4OgHDx73BEVAIIgCgwYVWuTI4SJDhAUSQPmYYGBAgAAEDESgISgQACH5BAkPAEkALAAAAQAPAA4AAAevgEmCRTMgHRcXGx8zRYKCNSUwPUJEREA6LCI2giUsSI6gSUgpJUkXN6GhOxdJGCAnNkJHSEhHQjYnIhhJATI5MSsoJCQoKjE0MgG8BhqpghwHygIcEAcMDhAQDgwGDxwCSQAcgjIaFhYaL88ASQIFDhxBoEEeDgXgADQZCwYGCQkHrGWgwU5BBEc/ePCQJygCAkEUGDSo0CJHDhcZIiyQAMrHBAMDAgQgYCACDUGBAAAh+QQJDwBJACwAAAAADwAPAAAHtIBJgkUzIB0XFxsfM0WCgjUlMD1CRERAOiwiNoIlLEiOoElIKSVJFzehoTsXSRggJzZCR0hIR0I2JyIYoDErKCQkKCoxoAEyOamCNDIBSQEGGslJHAfNAhwQBwwOEBAODAYPHAJJAByCMhoWFhovghwASQIFDhxBoEEeDgXkADQZCwwYSJDggLYMNOIpiODoBw8e9wRFQCCIAoMGFVrkyOEiQ4QFEkD5mGBgQIAABAxEoCEoEAA7") -} - -html .menu .menu_control:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAPcAAOTk5N/f38bUxsnQycTTxMfOx8XLxcPKw8bKxsfHx8TIxMbGxsXGxcXFxcfFx8PGw8TExMXExcbCxsfCx8PDw8PEw8PCw8PBw8HBwcW/xcHAwb7AvsK/whzVHBzTHCDOICLLIjC2MDO0MzOzMzKzMjWxNTOyMzWvNTavNjirOE2hTTqpOjunOzylPEmhSUqgSjqlOoaGhj+iPz6iPoSEhD6hPj6fPj+fP4ODg0CfQDqhOoKCgkGcQUObQ4CAgEWXRUaWRkWWRUqQSkiSSEiRSEyNTFuHW3p6eluGW3l5eU6KTliFWE6JTk2JTVaFVlWFVWB/YFGGUXV1dVGEUVWAVVSAVHRzdHNzc3FxcVV9VXZvdlp4Wm9vb2hyaGF0YVt2W1x1XFx0XGFyYWNwY15yXl1zXWtra15xXmNtY2FuYWpqamBvYG1obWJtYmlpaWJsYmhpaGNqY2hoaGJrYmRqZGRpZGdnZ2VpZWVoZWRoZGZmZmVnZWdlZ2VlZWdkZ2djZ2hjaGxdbG9ab2ZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCACDACwAAAEADwAOAAAIhgAHAUgAYcGCBggbGISQIMCgBlwG9dFjp6IdPX0GYaEwCMKgQWqO4NixA8cRNR89LvjoJglJkkncpBy0EqSPGDRoxPCBsiPNj3KwSLlyRQoWOTNrflzK1GeDplAHcWQQtWmFQQOqMi0wCIHWpQoGWfj6cSzEQXC63IG6UWCCBhA2KDS4sGFAACH5BAUIAIMALAIACQACAAMAAAgJAMUMcsHjSZSAACH5BAUIAIMALAMACgADAAQAAAgQAGGUGbSCxaBBJ4IM8gIlIAAh+QQFCACDACwEAAoAAwAEAAAIEAAHhTkzKAeVFiCoDDIyKCAAIfkEBQgAgwAsBQAIAAMABAAACBAABzFJM6jElj0pwAwaMiggACH5BAUIAIMALAYABwADAAUAAAgSAAfpGFSlQ5FBQEIMGpRiYZiAACH5BAUIAIMALAgACQADAAQAAAgQAAcN2jJIiIwpHogMujEoIAAh+QQFCACDACwJAAcABQAFAAAIGQAHCRw4kI2gHiK+iBlEwoQNFYOaBMqyJCAAIfkECTIAgwAsAAABAA8ADgAACBkABwkcSLCgwYMIEypcyLChw4cQI0qcSDEgADs=") -} - -html .menu .menu_firewall:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP////39/fv7+/f39+3t7evr6+np6efn5+Xl5ePj4+Hh4d/f393d3dnZ2dfX19XV1dHR0cvLy8nJycfHx8PDw8HBwb+/v729vbu7u7m5ube3t7W1tbOzs7Gxsa+vr6enp6WlpaOjo5+fnwDZNpeXl42NjYuLi4mJiSitSCqpSiypSoeHhyirSC6lTIWFhTKhTjiZUDyXUnx8fECRVEKPVHp6enh4eEqHWEiHWHR0dFB+XHBwcFpyYGZmZgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFAwA9ACwAAAAADwAQAAAGdMCesEeiHAACR2c17J0oiUXEE/pgHAsFptYjQFzN4c6UmPQKoXAYIzl/1M1M5AyCDzdzwtveu8wRdXwXbQ8afD0SGD0dEIcNIj01CDl2NQeUPRZzcA8WTQWKYRUGYTkJDlw9MgwLO3AaBh0bBhyHJQEDYE1BACH5BAUEAD0ALAMABgACAAMAAAYHwB6PN+qlggAh+QQFBAA9ACwFAAcAAQADAAAGBUDaCBUEACH5BAUIAD0ALAYACAACAAMAAAYHwBtuNGKpggAh+QQFCAA9ACwIAAcAAQADAAAGBcDZSBUEACH5BAUIAD0ALAkABgABAAMAAAYFwNioFQQAIfkEBQgAPQAsCgAFAAEAAwAABgVA2OgVBAAh+QQFBgA9ACwLAAQAAgADAAAGB0Aab6SD9YIAIfkECTIAPQAsAAAAAA8AEAAABhXAnnBILBqPyKRyyWw6n9CodEqtJoMAOw==") -} - -html .menu .menu_day:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAANPT09HR0c3NzcvLy8nJycfHx8XFxcPDw8HBwb+/v729vbu7u7m5ubW1tbOzs7Gxsa2trampqaenp6WlpaOjo6GhoQDZNpubm5WVlY+Pj42NjYuLi4mJiYeHh4WFhYODg35+fnZ2dnR0dHJycnBwcGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgAlACwAAAEADwAOAAAGesCSUKQRGkua0RGjAFQm0EkFoMAIFY9HAHPpXjABiINREohKoaMwHSKUBgvKJEKvTygLw1vCyWj+gBkcEnoGHCUWFmpCHAglBYeLRhsHjxuSRhqOBJeYSI4FnZgeegMdniUgBSULER8dHrCxG7QRDUIPCQq7vLsJDyVBACH5BAUKACUALAcABwACAAEAAAYEQIslCAAh+QQFCgAlACwKAAcAAgABAAAGBECLJQgAIfkEBQoAJQAsBAAJAAIAAQAABgRAiyUIACH5BAUKACUALAcACQACAAEAAAYEQIslCAAh+QQFCgAlACwKAAkAAgABAAAGBECLJQgAIfkEBQoAJQAsBAALAAIAAQAABgRAiyUIACH5BAUKACUALAcACwACAAEAAAYEQIslCAAh+QQFCgAlACwKAAsAAgABAAAGBECLJQgAIfkECTIAJQAsAAABAA8ADgAABhTAknBILBqPyKRyyWw6n9CodEoNAgA7") -} - -html .menu .menu_soft:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAOPj4+Hh4d/f393d3dvb29nZ2dfX19XV1dPT09HR0c/Pz83NzcvLy8nJycfHx8XFxcPDw7+/v729vbu7u6+vr62traurq6mpqaenp6WlpZmZmZeXl5WVlRTDQBTDPpOTkxLDPhbBQBi/QBTBQBq9Qha/QJGRkRi9Qhi9QI+Pjxq7Qhy5RB63RBy5QiC1RiC1RI2NjSKzRiSxRouLiyKxRiavSCitSCSvSImJiSatSCypSiirSCynSoWFhYODg4GBgTadUDadTjabUDSdTjibUDiZUDyXUn5+fjqXUjyVUnx8fHh4eESLVkaLVkaJWEaJVkqHWEiHWEqFWE6DWkyDWk6BWlB+WlB8XFJ8XFJ6XFR6XlR4Xlh2Xlp0YGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFHgBeACwAAAEAEAAPAAAHtoBeXTEdIR0qWF4/BgECAQtKXjIrQkBAMjVeEAwVFBUHCl4iRV6lUy5eCyalXhgAXidArE6oCxqsFQGiRKxStR+sGLonLk5MTTwsXgoSHBscCwJeWDUu1jFUXjMLCt0OOKzh4uI+DwndDDNeOAzdCQ89XgUKzhwTCcsR9dFeAxasKUItAFZKmBcCFFhxEHirVK5+F1jBwKeKVYZXDA50qtDAgZcHnDwdwKdEQaMABcD1KHAywZFAACH5BAUeAF4ALAAAAQAQAAcAAAdhgF5LDAECAQY+XloqHSEdMVxeDQcVFBUND147MkBAQisuXgMXXqUwCV4uUKVeRh1eBBSsHApeMUysRK8EFqwptTGrpa6wChwbHBMIqTxNTE4xIV49DwrWCzBeUzEu3TVWgQAh+QQFHgBeACwJAAEABwAPAAAHWIA/BgECAQtKEAwVFBUHCgsmXpIYAAsakl4Vhh+YGAEKEhwbHAsCMwsKqQ44mK1WMS6xNlguOk1MTjEhMVCYRh0xTJhEHS69kkkdNjJAQEIrLlckHSHFWoEAIfkECR4AXgAsAAABABAADwAAB3CAXoKDhIWGh4iJiouMjY6PiVg2LpQxVV44DAoKCQ89XiQuTkxNPCxeChEcGxwLAl4iRINSLl4LH4MYAV4nQINOtQsagxW7IkWDU8EmgxkAXjErQkBAMjVeDwwVFBUHCV5cLh0hHSRWXj0FAQIBCUeBADs=") -} - -html .menu .menu_logs:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAPcAACszOzM7Q9vb2+Pj49PT0ztDQzMzOwPDM+vr6/Pz8wuzM5ubm2Nja3uDgys7O2tzc0NDS8vLy4ODi2trc7Ozs7u7u1NTWzs7Q3t7g8PLy0tTU0tTW8PDywPLMwurOyNbOyNLOwujMx9XLx9fNwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQEBAD/ACwAAAAAEAAQAAAIcgABADBAsKBBggIFVhBAoKFDAgIELDCQcEDCiwINELhoEWNCjRw9ftxYUWRGkgI7igRZ0iTLlCYHUgi5EgODCS09SkiAIMEFmB41IFgAgIMAoBg1DJgYoaNKjBgQ9NQg0GjEqxEgXGhw82OACwG+hvUYEAAh+QQEBAD/ACwAAAAAAQABAAAIBAABBAQAIfkEBAQA/wAsAAAAAAEAAQAACAQAAQQEACH5BAQEAP8ALAAAAAABAAEAAAgEAAEEBAAh+QQEBAD/ACwAAAAAAQABAAAIBAABBAQAIfkEBAQA/wAsAAAAAAEAAQAACAQAAQQEACH5BAQEAP8ALAQAAQAIAAQAAAgUAAkIHCgQgMGDCBMCCOHBgwOEAQEAIfkEBAQA/wAsBgAAAAgABQAACB8ALxgIQDDAhQAEEioUsACAw4cIH0KM4CFERQ8OEAYEACH5BAQEAP8ALAAAAAABAAEAAAgEAAEEBAAh+QQEBAD/ACwAAAAAAQABAAAIBAABBAQAIfkEBAQA/wAsAAAAAAEAAQAACAQAAQQEACH5BAQEAP8ALAQAAAAKAAgAAAgyAC8YCBBgIMELAQgoXKhQwAIAECNCNEBAosSEIRQcUMBRAYiEFiNSDDmRQIiTDiRSDAgAIfkEBAQA/wAsBwAAAAYACAAACCYALxgIYODCBQIIEQoAwJBhgIYOFSg4oEDEQ4gXGwYIwTGEgwABAQAh+QQEBAD/ACwAAAAAAQABAAAIBAABBAQAIfkEBAQA/wAsAAAAAAEAAQAACAQAAQQEACH5BAQEAP8ALAAAAAABAAEAAAgEAAEEBAAh+QQEBAD/ACwEAAAABwALAAAIJgANCBR4IQCBgwgJAFjIsGHDEAcURIzosCLDEBgzhrBoMYQIhwEBACH5BAQEAP8ALAUAAAAJAAsAAAhDAA0YuDDQQIALAQgoXCiAwAIAECMCuEBAYsSEBxRkPHBAxIUIFiEmDAkgYQgPIVKGcDAypAEKLjEw4HggBEQJCRAEBAAh+QQEBAD/ACwAAAAAAQABAAAIBAABBAQAIfkEBAQA/wAsAAAAAAEAAQAACAQAAQQEACH5BAQEAP8ALAAAAAABAAEAAAgEAAEEBAAh+QQEBAD/ACwAAAAAAQABAAAIBAABBAQAIfkEBAQA/wAsAAAAAAEAAQAACAQAAQQEACH5BAQEAP8ALAAAAAABAAEAAAgEAAEEBAAh+QQEBAD/ACwAAAAAAQABAAAIBAABBAQAIf8LWE1QIERhdGFYTVA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA2LjAtYzAwMyA3OS4xNjQ1MjcsIDIwMjAvMTAvMTUtMTc6NDg6MzIgICAgICAgICI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIKICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcERNPSJodHRwOi8vbnMuYWRvYmUuY29tL3htcC8xLjAvRHluYW1pY01lZGlhLyIKICAgIHhtbG5zOnN0RGltPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvRGltZW5zaW9ucyMiCiAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ZmYyZWY0OTItM2JkZC05OTQ3LTk5YWMtMmQzMzI3MDVhMzIzIgogICB4bXBNTTpEb2N1bWVudElEPSJmZWI4MmYyNS1mZmZiLTNkYjgtY2EzMC0wNjE4MDAwMDAwM2YiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxYTVlZTUyOS03NWE4LTg0NDctODMzMi01NDRiZTAyMTFlYzUiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjItMTItMTVUMTc6Mzc6NTErMDg6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDIyLTEyLTE1VDE3OjM3OjUxKzA4OjAwIgogICB4bXA6Q3JlYXRlRGF0ZT0iMjAyMi0xMi0xNVQxNzozNzoyMSswODowMCIKICAgZGM6Zm9ybWF0PSLliqjnlLsgR0lGIgogICB4bXBETTp2aWRlb0ZyYW1lUmF0ZT0iMjUuMDAwMDAwIgogICB4bXBETTp2aWRlb0ZpZWxkT3JkZXI9IlByb2dyZXNzaXZlIgogICB4bXBETTp2aWRlb1BpeGVsQXNwZWN0UmF0aW89IjEvMSIKICAgeG1wRE06c3RhcnRUaW1lU2NhbGU9IjI1IgogICB4bXBETTpzdGFydFRpbWVTYW1wbGVTaXplPSIxIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9IjlkMWU4YTJjLWE4NGUtM2U1Ni04ZDI0LWRjZjEwMDAwMDA2YyIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMi0xMi0xNVQxNzozNzo1MSswODowMCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgQWRvYmUgTWVkaWEgRW5jb2RlciAyMDIwLjAgKFdpbmRvd3MpIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249ImNyZWF0ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MTJjMmZmZmItZGM3NC02ODQyLWIxOTEtMDhhMzgzM2RhMDgyIgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTEyLTE1VDE0OjUwOjAyKzA4OjAwIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InNhdmVkIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmYxNDI3ZWYyLTQ1ZjEtMmY0Zi1hNDY0LTU4MzgyOGMxNjJlNyIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMi0xMi0xNVQxNDo1MjoxNCswODowMCIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iL2NvbnRlbnQiLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6NTZjODkxYmQtMzQ4YS01OTQ3LTk2MDItMjFkM2JlZmRiZjAwIgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTEyLTE1VDE1OjQwOjE5KzA4OjAwIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvY29udGVudCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJkZXJpdmVkIgogICAgICBzdEV2dDpwYXJhbWV0ZXJzPSJzYXZlZCB0byBuZXcgbG9jYXRpb24iLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6NThkMTQ4M2QtZjAyMy02ZTQ4LWE0NTYtZDg3YTA1YWEzMWQ1IgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTEyLTE1VDE1OjQwOjI2KzA4OjAwIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InNhdmVkIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmRmMzhmYzNiLTE5ZGEtNGY0MC1iMjU2LWVhYzIxOWFjZGNmMiIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMi0xMi0xNVQxNjowMzo0NiswODowMCIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iL2NvbnRlbnQiLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6NzZjOTFlOGYtZTkyZC1iMjQ1LWJkYWItODdiYThmMDU5ZjkwIgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTEyLTE1VDE3OjM3OjQ4KzA4OjAwIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvY29udGVudCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpiNDJmZDUyMi00NGM4LTdmNDUtYTJjNS1iMjU0ZDdiNDdjMDciCiAgICAgIHN0RXZ0OndoZW49IjIwMjItMTItMTVUMTc6Mzc6NDgrMDg6MDAiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MThjMjc4NjktMDAyMS04YzRmLThjMDMtMzVlZTU3MzAwZGQ5IgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTEyLTE1VDE3OjM3OjUxKzA4OjAwIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBBZG9iZSBNZWRpYSBFbmNvZGVyIDIwMjAuMCAoV2luZG93cykiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6ZmYyZWY0OTItM2JkZC05OTQ3LTk5YWMtMmQzMzI3MDVhMzIzIgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTEyLTE1VDE3OjM3OjUxKzA4OjAwIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBBZG9iZSBNZWRpYSBFbmNvZGVyIDIwMjAuMCAoV2luZG93cykiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii9tZXRhZGF0YSIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgIDx4bXBNTTpEZXJpdmVkRnJvbQogICAgc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo2ZGUxZmEzNC0wNzNlLTJiNDgtYTNmYi0yY2Y3ZjFhMzczNjAiCiAgICBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjZkZTFmYTM0LTA3M2UtMmI0OC1hM2ZiLTJjZjdmMWEzNzM2MCIKICAgIHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxMmMyZmZmYi1kYzc0LTY4NDItYjE5MS0wOGEzODMzZGEwODIiLz4KICAgPHhtcE1NOkluZ3JlZGllbnRzPgogICAgPHJkZjpCYWc+CiAgICAgPHJkZjpsaQogICAgICBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjg1NmYwYzA0LWU0MzUtNzk0Ny1iMmQwLTkwMDliMjlhYzdmNiIKICAgICAgc3RSZWY6ZnJvbVBhcnQ9InRpbWU6MGQ3NjgwMDBmMjU2MDAiCiAgICAgIHN0UmVmOnRvUGFydD0idGltZTowZDc2ODAwMGYyNTYwMCIKICAgICAgc3RSZWY6bWFza01hcmtlcnM9Ik5vbmUiLz4KICAgIDwvcmRmOkJhZz4KICAgPC94bXBNTTpJbmdyZWRpZW50cz4KICAgPHhtcE1NOlBhbnRyeT4KICAgIDxyZGY6QmFnPgogICAgIDxyZGY6bGk+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24KICAgICAgIGRjOmZvcm1hdD0iYXBwbGljYXRpb24vdm5kLmFkb2JlLmFmdGVyZWZmZWN0cy5sYXllciIKICAgICAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NWI1MGU1ZTktMTRlMy0zZDQyLWEyMmMtMDY5NjNjNGY3ZWJjIj4KICAgICAgPGRjOnRpdGxlPgogICAgICAgPHJkZjpBbHQ+CiAgICAgICAgPHJkZjpsaSB4bWw6bGFuZz0ieC1kZWZhdWx0Ij7mt7HoibIg5ZOB6JOd6ImyIOe6r+iJsiAxPC9yZGY6bGk+CiAgICAgICA8L3JkZjpBbHQ+CiAgICAgIDwvZGM6dGl0bGU+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgIDwvcmRmOmxpPgogICAgIDxyZGY6bGk+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24KICAgICAgIGRjOmZvcm1hdD0iYXBwbGljYXRpb24vdm5kLmFkb2JlLmFmdGVyZWZmZWN0cy5sYXllciIKICAgICAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6N2U1M2FmMjctYzQzNS1iNjQ5LTk4MTAtNTYzYjE4ZmI5ZDI1Ij4KICAgICAgPGRjOnRpdGxlPgogICAgICAgPHJkZjpBbHQ+CiAgICAgICAgPHJkZjpsaSB4bWw6bGFuZz0ieC1kZWZhdWx0Ij7nn6nlvaIgODEucG5nPC9yZGY6bGk+CiAgICAgICA8L3JkZjpBbHQ+CiAgICAgIDwvZGM6dGl0bGU+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgIDwvcmRmOmxpPgogICAgIDxyZGY6bGk+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24KICAgICAgIGRjOmZvcm1hdD0iYXBwbGljYXRpb24vdm5kLmFkb2JlLmFmdGVyZWZmZWN0cy5jb21wIgogICAgICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4NTZmMGMwNC1lNDM1LTc5NDctYjJkMC05MDA5YjI5YWM3ZjYiPgogICAgICA8ZGM6dGl0bGU+CiAgICAgICA8cmRmOkFsdD4KICAgICAgICA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPuWQiOaIkCAxPC9yZGY6bGk+CiAgICAgICA8L3JkZjpBbHQ+CiAgICAgIDwvZGM6dGl0bGU+CiAgICAgIDx4bXBNTTpJbmdyZWRpZW50cz4KICAgICAgIDxyZGY6QmFnPgogICAgICAgIDxyZGY6bGkKICAgICAgICAgc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo1YjUwZTVlOS0xNGUzLTNkNDItYTIyYy0wNjk2M2M0ZjdlYmMiCiAgICAgICAgIHN0UmVmOmZyb21QYXJ0PSJ0aW1lOjBkNzY4MDAwZjI1NjAwIgogICAgICAgICBzdFJlZjp0b1BhcnQ9InRpbWU6MGQ3NjgwMDBmMjU2MDAiCiAgICAgICAgIHN0UmVmOm1hc2tNYXJrZXJzPSJOb25lIi8+CiAgICAgICAgPHJkZjpsaQogICAgICAgICBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjdlNTNhZjI3LWM0MzUtYjY0OS05ODEwLTU2M2IxOGZiOWQyNSIKICAgICAgICAgc3RSZWY6ZnJvbVBhcnQ9InRpbWU6MGQ3NjgwMDBmMjU2MDAiCiAgICAgICAgIHN0UmVmOnRvUGFydD0idGltZTowZDc2ODAwMGYyNTYwMCIKICAgICAgICAgc3RSZWY6bWFza01hcmtlcnM9Ik5vbmUiLz4KICAgICAgICA8cmRmOmxpCiAgICAgICAgIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6YWM5NGVmNzAtMmVkNC03YjQzLTg5YTktNzU0NTkwMjExOGVmIgogICAgICAgICBzdFJlZjpmcm9tUGFydD0idGltZTowZDc2ODAwMGYyNTYwMCIKICAgICAgICAgc3RSZWY6dG9QYXJ0PSJ0aW1lOjBkNzY4MDAwZjI1NjAwIgogICAgICAgICBzdFJlZjptYXNrTWFya2Vycz0iTm9uZSIvPgogICAgICAgIDxyZGY6bGkKICAgICAgICAgc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpiMjY3NDgyNS05MjE4LTQ1NDYtYjA4ZS01ZTI3M2QyNDAyMjgiCiAgICAgICAgIHN0UmVmOmZyb21QYXJ0PSJ0aW1lOjBkNzY4MDAwZjI1NjAwIgogICAgICAgICBzdFJlZjp0b1BhcnQ9InRpbWU6MGQ3NjgwMDBmMjU2MDAiCiAgICAgICAgIHN0UmVmOm1hc2tNYXJrZXJzPSJOb25lIi8+CiAgICAgICAgPHJkZjpsaQogICAgICAgICBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOmNiYmJlOTY0LTM0MmYtYTM0ZC1iM2Q2LTI3ZGUyOTE3MWFhYSIKICAgICAgICAgc3RSZWY6ZnJvbVBhcnQ9InRpbWU6MGQ3NjgwMDBmMjU2MDAiCiAgICAgICAgIHN0UmVmOnRvUGFydD0idGltZTowZDc2ODAwMGYyNTYwMCIKICAgICAgICAgc3RSZWY6bWFza01hcmtlcnM9Ik5vbmUiLz4KICAgICAgIDwvcmRmOkJhZz4KICAgICAgPC94bXBNTTpJbmdyZWRpZW50cz4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgICAgPC9yZGY6bGk+CiAgICAgPHJkZjpsaT4KICAgICAgPHJkZjpEZXNjcmlwdGlvbgogICAgICAgZGM6Zm9ybWF0PSJhcHBsaWNhdGlvbi92bmQuYWRvYmUuYWZ0ZXJlZmZlY3RzLmxheWVyIgogICAgICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDphYzk0ZWY3MC0yZWQ0LTdiNDMtODlhOS03NTQ1OTAyMTE4ZWYiPgogICAgICA8ZGM6dGl0bGU+CiAgICAgICA8cmRmOkFsdD4KICAgICAgICA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPue7hOWQiCAxNS5wbmc8L3JkZjpsaT4KICAgICAgIDwvcmRmOkFsdD4KICAgICAgPC9kYzp0aXRsZT4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgICAgPC9yZGY6bGk+CiAgICAgPHJkZjpsaT4KICAgICAgPHJkZjpEZXNjcmlwdGlvbgogICAgICAgZGM6Zm9ybWF0PSJhcHBsaWNhdGlvbi92bmQuYWRvYmUuYWZ0ZXJlZmZlY3RzLmxheWVyIgogICAgICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpiMjY3NDgyNS05MjE4LTQ1NDYtYjA4ZS01ZTI3M2QyNDAyMjgiPgogICAgICA8ZGM6dGl0bGU+CiAgICAgICA8cmRmOkFsdD4KICAgICAgICA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPuefqeW9oiA2NS5wbmc8L3JkZjpsaT4KICAgICAgIDwvcmRmOkFsdD4KICAgICAgPC9kYzp0aXRsZT4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgICAgPC9yZGY6bGk+CiAgICAgPHJkZjpsaT4KICAgICAgPHJkZjpEZXNjcmlwdGlvbgogICAgICAgZGM6Zm9ybWF0PSJhcHBsaWNhdGlvbi92bmQuYWRvYmUuYWZ0ZXJlZmZlY3RzLmxheWVyIgogICAgICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpjYmJiZTk2NC0zNDJmLWEzNGQtYjNkNi0yN2RlMjkxNzFhYWEiPgogICAgICA8ZGM6dGl0bGU+CiAgICAgICA8cmRmOkFsdD4KICAgICAgICA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPuefqeW9oiA4MC5wbmc8L3JkZjpsaT4KICAgICAgIDwvcmRmOkFsdD4KICAgICAgPC9kYzp0aXRsZT4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgICAgPC9yZGY6bGk+CiAgICA8L3JkZjpCYWc+CiAgIDwveG1wTU06UGFudHJ5PgogICA8eG1wRE06dmlkZW9GcmFtZVNpemUKICAgIHN0RGltOnc9IjE2IgogICAgc3REaW06aD0iMTYiCiAgICBzdERpbTp1bml0PSJwaXhlbCIvPgogICA8eG1wRE06ZHVyYXRpb24KICAgIHhtcERNOnZhbHVlPSIyNSIKICAgIHhtcERNOnNjYWxlPSIxLzI1Ii8+CiAgIDx4bXBETTpzdGFydFRpbWVjb2RlCiAgICB4bXBETTp0aW1lRm9ybWF0PSIyNVRpbWVjb2RlIgogICAgeG1wRE06dGltZVZhbHVlPSIwMDowMDowMDowMCIvPgogICA8eG1wRE06YWx0VGltZWNvZGUKICAgIHhtcERNOnRpbWVWYWx1ZT0iMDA6MDA6MDA6MDAiCiAgICB4bXBETTp0aW1lRm9ybWF0PSIyNVRpbWVjb2RlIi8+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAOw==") -} - -html .menu .menu_set:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQAOYAAMfHx8XFxbm5ube3t7W1tbOzs7GxsQbROGqpeg7JPBDHPpWVlRLFPmylemCnchTDQBbBQFqnbBTBQFSpaBi/QEqrYpGRkRq9QlinbBa/QI+RkRi9QBi9Qh65RI+Pjxq7QkqpYhy5RIuPjRy5Qh63RCC1RIePiSKzRiSxRn6Rg4uLiyKxRiavSCitSCSvSImJiSqrSiatSIeHhyypSiirSCqpSiynSoWFhS6lTHyJfjCjTHyHfjKhTDKhToODgzSfTmaNcIGBgTadTjKfTjabUDSdTjqZUDiZUH5+fjyXUjqXUDqXUj6VUjyVUj6TVHx8fD6TUkCRVEKPVHp6ekKNVnJ8dECPVESNVkaLVkSLVnh4eEiJWEaJVkaJWEiHWEqHWHZ2dkyFWkqFWE6DWkyDWk6BWlB+XFB+WlB8XFJ8XFR6XlR6XFJ6XGR0aFR4XlZ4Xlp0YFh2XlZ2XlxyYFh0YFpyYFxwYlxwYF5uYmZmZgAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJDwB5ACwAAAEADwAOAAAHpoB5goN4Hxdwg4l5b2Z5W1JMUnlpb4lvJywzKHBzMDYkMJWCZS15Fj6CWgt5LGSJOi95WgYFSHkeNINzVCQyUwEyNwFINyFSc3kKTml5BKh5TwR5akYKyVuCA7Z5UwN5cFIMeXFRKDdBAEhIATdPIU6ieTwqeUEDAjJ5LzOJZvw3NwahOlFmkBoYmSi46gGjITODYfJgUVLmSh4yahQNgsMgQcZEgQAAIfkECQ8AeQAsAAABAA8ADgAAB7CAeYJ5c15pgjo2eIODeEIXLExJQh1mjIM/aXhGUXlRW4xzZVcsGj6CWggwMEeLJxdMGHlaBgVIeSJfJGR5NUp5MlMBMjcBSDdwLId5QlJ5BKd5TwR5PE6DUUx5A7d5UwPMRIJpNnk3QQBISAE3T3MwOHlbJWMmeUEDAjJ5E1IbM3nsCNHRocKNQTtKYBmzKM8iL+LGJJmzJd6lPGQeMHESpUWJIxcdtgCYx0yYhnkCAQAh+QQJDwB5ACwAAAAADwAPAAAHqoB5goN5c4SHhEIoUYiDb3lvNk4wW2UoSYQlEEYhZXlqLFg2KIQtWGkNPoJVC2SMg2FOL3laBgVIeQ4zhFI2MlMBMjcBSDdOJFh5RD+CBKp5TwSCNUlwM06CA7h5UwN5Ui1feU41RjdBAEhIATdAEHCEFBF5QQMCMnkpRPCDRmo3OQS1AZEGgo5BaQ4YoQIjTZ4eLZpQ8DRISY85ZWjAqCEITyNBTpKMORQIACH5BAkPAHkALAAAAQAPAA4AAAemgHmCg3gfF3CDiXlvZnlbUkxSeWlviW8nLDMocHMwNiQwlYJlLXkWPoJaC3ksZIk6L3laBgVIeR40g3NUJDJTATI3AUg3IVJzeQpOaXkEqHlPBHlqRgrJW4IDtnlTA3lwUgx5cVEoN0EASEgBN08hTqJ5PCp5QQMCMnkvM4lm/Dc3BqE6UWaQGhiZKLjqAaMhM4Nh8mBRUuZKHjJqFA2CwyBBxkSBAAAh+QQJDwB5ACwAAAEADwAOAAAHsIB5gnlzXmmCOjZ4g4N4QhcsTElCHWaMgz9peEZReVFbjHNlVywaPoJaCDAwR4snF0wYeVoGBUh5Il8kZHk1SnkyUwEyNwFIN3Ash3lCUnkEp3lPBHk8ToNRTHkDt3lTA8xEgmk2eTdBAEhIATdPczA4eVslYyZ5QQMCMnkTUhszeewI0dGhwo1BO0pgGbMozyIv4sYkmbMl3qU8ZB4wcRKlRYkjFx22AJjHTJiGeQIBACH5BAkPAHkALAAAAAAPAA8AAAeqgHmCg3lzhIeEQihRiINveW82TjBbZShJhCUQRiFleWosWDYohC1YaQ0+glULZIyDYU4veVoGBUh5DjOEUjYyUwEyNwFIN04kWHlEP4IEqnlPBII1SXAzToIDuHlTA3lSLV95TjVGN0EASEgBN0AQcIQUEXlBAwIyeSlE8INGajc5BLUBkQaCjkFpDhihAiNNnh4tmlDwNEhJjzllaMCoIQhPI0FOkow5FAgAIfkECQ8AeQAsAAABAA8ADgAAB6aAeYKDeB8XcIOJeW9meVtSTFJ5aW+JbycsMyhwczA2JDCVgmUteRY+gloLeSxkiToveVoGBUh5HjSDc1QkMlMBMjcBSDchUnN5Ck5peQSoeU8EeWpGCslbggO2eVMDeXBSDHlxUSg3QQBISAE3TyFOonk8KnlBAwIyeS8ziWb8NzcGoTpRZpAaGJkouOoBoyEzg2HyYFFS5koeMmoUDYLDIEHGRIEAACH5BAkPAHkALAAAAQAPAA4AAAewgHmCeXNeaYI6NniDg3hCFyxMSUIdZoyDP2l4RlF5UVuMc2VXLBo+gloIMDBHiycXTBh5WgYFSHkiXyRkeTVKeTJTATI3AUg3cCyHeUJSeQSneU8EeTxOg1FMeQO3eVMDzESCaTZ5N0EASEgBN09zMDh5WyVjJnlBAwIyeRNSGzN57AjR0aHCjUE7SmAZsyjPIi/ixiSZsyXepTxkHjBxEqVFiSMXHbYAmMdMmIZ5AgEAOw==") -} - -html .menu .menu_folder:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPX19efn593d3dXV1dPT09HR0c/Pz83NzcvLy8nJycfHx8XFxcHBwb29vbm5ube3t7W1tbOzs7Gxsa+vr62traurq6mpqaenp6WlpaOjo6GhoQDZNp+fn52dnZubm5mZmZeXl5WVlZOTk4+Pj4WFhX5+fnx8fHp6enh4eHZ2dnJycnBwcGZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFDgAsACwAAAIAEAAMAAAGYEAWCHEohFjIpJJVGJlKD0oGQ8VoVEkCdslFajEMiuVCJlskjwTz1OgmSQsWQhRxIysg1mLysbMcKywKCih2J3UsAgZ+Hh1IABJ+ECVIARwlmJmZJAxJHkQHoaKhA3ksQQAh+QQJDgAsACwAAAAAEAAOAAAGGUCWULgpGo/DpHLJbDqf0Kh0Sq1ar9isMAgAIfkECQ4ALAAsAAABABAADQAABmZAllC4KRqPQhDiUAgNn8/CyFR6UDKYLEajGhK60LDwi2FQLJd02iJ5JFiFU0M8JC1YCFGELqyAWAsTH3wsDissCgoofCd7LAIGhB4dQgAShBAlQgEcJZ6fnyQMQx5LB6eopwN/LEEAIfkECQ4ALAAsAAACABAADAAABmNAFghxKGyOyCSLVRiZSg9KBkPFaFRLFgGb7Xq1KgyDYrmYzRbJI8E8Nb5d0oKFEEXg2QqItZh88EsOKywKCiiAJ3csAgaALB4dSwASjhAlSwEcJZucnCQMWR5EB6SlpAN7LEEAIfkECQ4ALAAsAAACABAADAAABlxAFghxKIRYyKSSVRiZSpuodIokqJbYpBXDoFguYLBF8kgwT41skrRgIUQRNbICYi0mHznLsWIpFChyJ3EsAgZ6Hh1IABJ6ECVIARwllJWVJAxJHkQHnZ6dA3UsQQAh+QQFDgAsACwAAAIAEAAMAAAGZUAWCHEohFjIpJJVGJlKD0oGQ8VoVEkClrXper9ILYZBsVzOZ4vkkWCeGsslacFCiCJxZQXEWkw+eUkOKywKCiiBLCd4LAIGiSweHUgAEpAQJUgBHCWdnp4kDEkeRAemp6YDfCxBACH5BAkOACwALAAAAgAQAAwAAAYUQJZwSCwaj8ikcslsOp/QqHQqDAIAIfkECQ4ALAAsAAACABAADAAABlxAFghxKIRYyKSSVRiZSpuodIokqJbYpBXDoFguYLBF8kgwT41skrRgIUQRNbICYi0mHznLsWIpFChyJ3EsAgZ6Hh1IABJ6ECVIARwllJWVJAxJHkQHnZ6dA3UsQQAh+QQJDgAsACwAAAIAEAAMAAAGY0AWCHEobI7IJItVGJlKD0oGQ8VoVEsWAZvterUqDINiuZjNFskjwTw1vl3SgoUQReDZCoi1mHzwSw4rLAoKKIAndywCBoAsHh1LABKOECVLARwlm5ycJAxZHkQHpKWkA3ssQQAh+QQJDgAsACwAAAEAEAANAAAGZkCWULgpGo9CEOJQCA2fz8LIVHpQMpgsRqMaErrQsPCLYVAsl3TaInkkWIVTQzwkLVgIUYQurIBYCxMffCwOKywKCih8J3ssAgaEHh1CABKEECVCARwlnp+fJAxDHksHp6inA38sQQAh+QQJDgAsACwAAAAAEAAOAAAGZkCWULgpGo/DpHIJQhwKoaWyMDKVHpQMZovRqIaEr3QZxjAolotabZE8EqzCqTEWkhYshChSZ1VALAsTH30OKywKCih1J3wsAgZ9Hh1CABJ9ECVCARwlnp+fJAxDHk4Hp6inA4AsQQA7") -} - -html .menu .menu_exit:hover { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAOPj4+Hh4dnZ2dXV1c3NzcvLy8fHx8PDw8HBwb+/v729vbu7u7W1ta2trampqaenp6Ojo6GhoZ2dnQzNOgjPOpeXlxq9Qhi9QI+Pj4uLi4mJiSatSCqpSjCjTDSfToGBgTadTjKfTjibUDqZUDqXUDyVUkCRVESNVnZ2dkyFWnR0dE6BWlB+XFB8XFR4Xlh2XlxwYF5uYmZmZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCAAyACwCAAEACwAOAAAGQMBGgUAsEgyoAEMSaTYlgwzgI6taZYkKQHO1ZlDb7jUsrpLLZ3G6ux5zxRgwVayoBBQPh17/EGQgCQiCgwcLKkEAIfkEBQgAMgAsBwAHAAEAAgAABgRA0ykIACH5BAUIADIALAgABwABAAIAAAYEwI4oCAAh+QQFCAAyACwJAAcAAgACAAAGBkCPhzQKAgAh+QQFCAAyACwLAAcAAQACAAAGBEDPKAgAIfkEBQgAMgAsDAAHAAIAAgAABgZA0KbECQIAIfkEBQgAMgAsDAAFAAIAAgAABgZAVypmCQIAIfkEBQgAMgAsDgAGAAIAAwAABgfAlYySmqyCACH5BAUIADIALAwACQADAAEAAAYFQNiFFQQAIfkEBQgAMgAsDAAKAAIAAQAABgTAFysIACH5BAkyADIALAIAAQAOAA4AAAYUQJlwSCwaj8ikcslsOp/QqHRqDAIAOw==") -} - -.pos-box { - height: 50px; - width: 100%; - box-shadow: 0 3px 2px 0 rgba(0, 0, 0, 5%) !important; - border-radius: 4px; -} - -.conter-box{ - box-shadow: 0 1px 2px 0 rgba(0,0,0,.1); -} - -.position { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKwwAACsMBNCkkqwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAFZSURBVDiNpdO9a1VBEAXw3zOioIUQEdFGkQdWVtr6D1ikERtRppLU6awUsUkh2CgoBNwhoPhsLESxERR7wcI2XSBNMAHRNFmLbB6Xa64RXFjYOXvmzMfOjmqt/mcd2I+QmUuZOcgbDWWQmTN4iFms4wGu4jC+RMT7wQwy8xBe4FtE3MBXPMdqE5sfLCEzj+Il3kXEE4iIJTzFFbzB9z0FMvMYJphExLPuXUQs4y1eYfmPHmTmbHN+FBGv92zMDm+ulXAzItZHtVaZeRH3sBgRn4ecOyKXcRt3dkuYwyJOZeaJfZyP42TjX1Nrne5SyqSUMu5ho559tpQy2bUP9gL8wHYnzbtYzczTuB8RHzs8/H0Sz+ETbuEDxg3f7pL6AhW/2vknNiNiCxvY6uDT8e2XMINLmbmC89js3I0z8wLOdP36Aht43KIcwULD1+w82/WGT+dk8DP96/oNlqecb6uu8YEAAAAASUVORK5CYII="); - background-position: 17px 14px; - background-repeat: no-repeat; - background-size: 20px auto; - line-height: 50px; - padding-left: 30px; -} - -.search { - width: 306px; -} - -.ser-text { - border: #20a53a 1px solid; - height: 30px; - width: 250px; - padding: 0 8px; - margin-top: 10px; - outline: none; -} - -.ser-text:focus { - border-color: #20a53a; -} - -.ser-text:focus + span.glyphicon-search { - color: #20a53a; -} - -.ser-sub { - width: 38px; - height: 30px; - border: 0; - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAxAAAAMQBz4pYTAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAE2SURBVDiNndJBS1VRFMXx9UzNQUQD0caFk6YhEUFCOPETiJQfIygQbNQgCCeOGjYLGjRJkPoKoSGIRKADs1nQoEIEfw3cry6v+3oPN2z2ufuu8z/7nruC9ORNvMRXHOIFbrTogjQfOniKX87iR2P9HSv/A1zA+xK/wRwmMYV5vKt3r/sBnpTgcb9R8aw0D3sB1/GzTum3uZsfSnupCVgr8vwQgKXSLnR7I0nuJJFkN4PjY9WZbmMkyXGtx4YAjFY9aQI2knSS3B4CMFd1508HV3CE3QHfP17G2sdo7298UJfzChMtmy9jszTLbT4InpfgCx6Vme5hFd/8jW1cbQME93Hg39jDYmOKT5hGOmi7rNkk15KcJvmcZKv6E0k2k9xNsp1keZBx2vIi3tYkG+cBxJmV13HrN6Szg7ZCfX3NAAAAAElFTkSuQmCC") no-repeat center #20a53a; - cursor: pointer; - margin-right: 2px; - margin-top: 10px -} - -.title { - height: 50px; - line-height: 50px; - border-bottom: 1px solid #eee -} - -.title h3 { - display: inline-block; - line-height: 50px; - margin-right: 12px -} -.tablescroll { - overflow: auto; - border: #e6e6e6 1px solid; -} -.divtable .table { - border: 1px solid #ddd; - color: #666; - font-size: 12px; - margin-bottom: 0; -} -.divtable{ - position: relative; -} -.table-fixed-box{ - overflow-y:auto; -} -.ui-selectable{ - padding-bottom: 0; -} - -.divtable .table thead th { - vertical-align: inherit; - background-color: #f6f6f6; - border-bottom: 1px solid #e6e6e6; - color: #666; - font-weight: normal; - padding: 8px; -} -.divtable .table_toolbar{ - left: 8px; - bottom: 6px; - position: absolute; -} -.table .btlink{ - word-break: break-word; -} -.table .btlinkbed { - color: #666; - padding: 1px 5px; - border: 1px solid transparent; - white-space: nowrap; - overflow: hidden; - float: left; - display: block -} - -.table .btlinkbed:hover { - border: #ddd 1px solid; -} - -.dataTables_paginate { - display: block; - height: 35px; - margin-bottom: 0px; - margin-top: 5px; - position:relative; -} - -#filePage{ - margin-bottom: 10px; -} - -.page { - /* line-height: 16px; */ - text-align: right; - padding: 8px 0; - /* margin-right: 1px; */ - height: 44px; - /* border: 1px solid #e6e6e6;*/ - border-top: none; - margin: 0; -} - -.site_type { - height: 30px; - line-height: 30px; - bottom: -9px; - position: absolute; -} - -.site_type span{ - display: inline-block; - height: 30px; - line-height: 30px; - padding: 0 10px; - font-family: "Arial"; - font-size: 13px; - color: #666; - float: left; - border: 1px solid #ececec; - border-right: none; - /* border: 1px solid #ececec; */ -} - -.site_type select{ - border-radius: 0px; -} -.site_type_table{ - padding: 15px; - min-height: 100px; -} -.edit_site_type{ - padding:0 15px; -} -.edit_site_type .line .info-r{ - margin-left:0px; -} -.edit_site_type input{ - width: 80% !important; -} -.edit_site_type .btn{ - margin-right: 0px; -} -.page div { - float: right -} - -.page span, -.page a { - display: inline-block; - height: 28px; - line-height: 28px; - padding: 0 10px; - font-family: "Arial"; - font-size: 13px; - color: #666; - float: left; - border: 1px solid #ececec; - border-right: none; -} - -.page a.prev { - border-left: #ddd solid 1px -} - -.page a:hover { - background: #e8e8e8; - color: #20a53a; - text-shadow: 0 1px 0 #fff; - cursor: pointer -} - -.page spanold { - font-size: 12px; - padding: 4px 10px!important; - border-style: solid; - border-width: 1px; - border-color: #ddd #ddd #ccc; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - display: inline-block; - line-height: 16px; - background: #f5f5f5; - color: #333; - text-shadow: 0 1px 0 #fff -} - -.page .Pcurrent { - color: #fff; - background: #20a53a; - cursor: default; - border-color: #20a53a; -} - -.page .Pcount { - color: #333; - padding: 0 10px; - margin-right: 0px; - border-right: 1px solid #ececec; -} - -.system-info-con { - width: 100% -} -.sys-li-box{ - height:100px; - background-color:#f9f9f9; - border:#f0f0f0 1px solid; - margin-left:20px; - max-width:230px; -} -.sys-li-box .name{ - line-height:40px; - margin-top:8px; -} -.sys-li-box .val a{ - font-size:26px; - font-family:arial; -} -.sys-i-c-box { - box-sizing: border-box; - max-width: 350px -} - -.sys-i-c-box .siteinfo-box { - height: 110px; - position: relative; - width: 100% -} - -.sys-i-c-box .siteinfo-box h3 { - background: #20a53a; - color: #fff; - font-style: normal; - height: 110px; - left: 0; - line-height: 110px; - position: absolute; - text-align: center; - top: 0; - width: 56px -} - -.siteinfo-box-right { - border-bottom: 1px solid #ddd; - border-right: 1px solid #ddd; - border-top: 1px solid #ddd; - height: 110px; - margin-left: 56px; - overflow: hidden -} - -.siteinfo-box-right h4 { - font-size: 32px; - height: 68px; - line-height: 60px; - overflow: hidden; - padding-top: 8px; - text-align: center -} - -.siteinfo-box-right .sbr-btn { - color: #ccc; - display: block; - font-size: 14px; - height: 27px; - line-height: 22px; - padding-top: 5px; - text-align: center -} - -.siteinfo-box-right .sbr-btn a { - color: #20a53a; - font-size: 16px; - padding: 0 10px -} - -.circle-box h3, -.circle-box h4 { - line-height: 30px -} - -.circle { - width: 100px; - height: 100px; - position: relative; - border-radius: 50%; - background: #20a53a; - margin: 0 auto -} - -.pie_left, -.pie_right { - width: 100px; - height: 100px; - position: absolute; - top: 0; - left: 0 -} - -.left, -.right { - width: 100px; - height: 100px; - background: #ccc; - border-radius: 50%; - position: absolute; - top: 0; - left: 0 -} - -.pie_right, -.right { - clip: rect(0, auto, auto, 50px); - transition: transform 1s ease-in 0s; - -webkit-transition: -webkit-transform 1s ease-in 0s; - -moz-transition: -moz-transform 1s ease-in 0s -} - -.pie_left, -.left { - clip: rect(0, 50px, auto, 0); - transition: transform .4s ease-in 1s; - -webkit-transition: -webkit-transform .4s ease-in 1s; - -moz-transition: -moz-transform .4s ease-in 1s -} - -.mask { - width: 88px; - height: 88px; - border-radius: 50%; - left: 6px; - top: 6px; - background: #FFF; - position: absolute; - line-height: 88px; - font-size: 18px; - color: #20a53a -} - -@-webkit-keyframes shineGreen { - from { - -webkit-box-shadow: 0 0 10px #999 - } - 50% { - -webkit-box-shadow: 0 0 15px #20a53a - } - to { - -webkit-box-shadow: 0 0 10px #999 - } -} - -.shine_green { - -webkit-animation-name: shineGreen; - -webkit-animation-duration: 3s; - -webkit-animation-iteration-count: infinite -} - -.mem-release { - cursor: pointer -} - -.mem-re-min { - background: url(/static/img/ico/rocket_min.png) no-repeat center center; - height: 35px; - left: 35px; - position: absolute; - top: 2px; - width: 20px; - z-index: 9; - opacity: .9; -} - -.mem-re-con { - background: url(/static/img/ico/ico-rocket.gif) no-repeat center center; - width: 100%; - height: 100%; - position: absolute; - z-index: 9; - display: none; - top: 15px -} - -.mem-release:hover .mem-re-con { - display: block -} - -.soft-man .col-lg-3 { - border-bottom: 1px solid #ececfb; - border-left: 1px solid #ececfb; - border-right: 1px solid #ececfb; - margin-right: -1px; - margin-bottom: -1px; - height: 148px; - cursor: pointer; - padding: 0 -} - -.soft-man .col-lg-3:hover { - box-shadow: 0 0 38px rgba(0, 0, 0, .08) inset; - -webkit-transition: all .25s ease; - transition: all .25s ease -} - -.soft-man .dashed-border { - border: 1px dashed #20a53a; - z-index: 1 -} - -.soft-man .no-bg:hover { - box-shadow: 0 0 38px rgba(0, 0, 0, .01) inset; - -webkit-transition: all .15s ease; - transition: all .15s ease -} - -.soft-man .image { - height: 40px; - margin: 30px 0 20px; - text-align: center -} - -.soft-man .sname { - text-align: center; - color: #999 -} - -.soft-man .col-sm-3>div { - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 0 -} - -.soft-man .spanmove { - background: url(/static/images/move.png) no-repeat; - width: 30px; - height: 30px; - display: none; - position: absolute; - top: 5px; - right: 5px; - z-index: 3 -} - -.soft-man .col-lg-3:hover .spanmove { - display: block -} - -.soft-man .text { - background-color: rgba(0, 0, 0, 0.3); - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; - display: none -} - -.soft-man .text a { - background-color: #fff; - border-radius: 4px; - display: block; - height: 30px; - line-height: 30px; - margin: 59px auto; - text-align: center; - width: 100px -} - -.soft-man-con p.status { - line-height: 40px; - margin-bottom: 10px; - font-size: 14px -} - -.soft-man-con .sfm-opt .btn { - margin-right: 15px -} - -.soft-man .sname .glyphicon-True { - color: #20a53a; - margin-left: 10px -} - -.soft-man .sname .glyphicon-False { - color: red; - margin-left: 10px -} - -.soft-man { - position: relative -} - -.soft-man .rowbg { - position: absolute; - top: 0; - width: 100%; - z-index: 0; -} - -.soft-man .rowbg .col-lg-3 { - cursor: default; -} - -.soft-man .rowbg .col-lg-3:hover { - box-shadow: 0 0 38px rgba(0, 0, 0, .01) inset; - -webkit-transition: all .15s ease; - transition: all .15s ease -} -.bw-info{ - height:80px; -} -.bw-info > div{ - float:left; - padding-top:25px; - text-align:center; -} -.bw-info > div p{ - margin-bottom:7px; -} -.bw-info > div a{ - font-size:14px; - color: #666; -} -.bw-info .ico-up{ - width:14px; - height:8px; - background-color:#f7b851; - display:inline-block; - margin-right:3px; -} -.bw-info .ico-down{ - width:14px; - height:8px; - background-color:#52a9ff; - display:inline-block; - margin-right:3px; -} -.bw-info ul li { - height: 56px; - line-height: 56px; -} - -.bw-info ul li.bi-line { - border-bottom: #ddd solid 1px; -} - -.bw-info ul li span { - display: block; - margin-right: 15px; -} - -.btn-zhm { - border-radius: 0 3px 0 0; - display: none; - position: absolute; - right: 42px; - top: 0; -} - -.ellipsis_text { - width: 220px; - text-overflow: ellipsis; - overflow: hidden; -} - -.ssl_state_info{ - padding: 15px 30px; - background: #f5faf2; - margin-top: 5px; - border-radius: 4px; - border: 1px solid #EAF1F2; - font-size: 12px; - position: relative; -} - -.ssl_state_info .state_item{ - width: 49%; - height: 25px; - line-height: 25px; - display: inline-block; - vertical-align: top; -} - -.ssl_state_info .state_item span{ - height: 25px; - line-height: 25px; - vertical-align: top; -} - - -.ssl_state_info .state_item span:nth-child(1){ - font-weight: bold; - color: #666; -} - - - -.ssl_state_info .state_item .switch{ - display: inline-block; -} - - -.ssl_state_info .state_item .btswitch-btn{ - margin-bottom: 0; - height: 1.9rem; - width: 3.2rem; - position: relative; - top:2.5px; -} - -.custom_certificate_info{ - margin-top: 15px; -} - -.custom_certificate_info .state_item{ - width: 48%; - height: 50px; - display: inline-block; - margin-right: 4%; -} -.custom_certificate_info .state_item:nth-child(2){ - margin-right: 0; -} - -.custom_certificate_info .state_item span{ - width:100%; - margin-bottom: 5px; -} - -.custom_certificate_info .state_item textarea{ - width: 100%; - height: 250px; - line-height: 25px; -} - -.line { - padding: 5px 0 -} - -.line .span_tit { - display: inline-block; - text-align: right; - width: 64px -} - -.line .tname { - display: block; - float: left; - height: 32px; - line-height: 32px; - overflow: hidden; - padding-right: 20px; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; - width: 100px -} - -.line .info-r { - margin-bottom: 5px; - margin-left: 100px; - position: relative -} - -.placeholder { - cursor: text; - left: 20px; - line-height: 24px; - position: absolute; - top: 27px -} - -.bt-form-submit-btn { - background: #f6f8f8; - border-top: 1px solid #edf1f2; - bottom: 0; - left: 0; - padding: 8px 20px 10px; - position: absolute; - text-align: right; - width: 100% -} - -.bt-form-submit-btn .btn:first-child { - margin-right: 4px -} - -.btn-danger { - background-color: #cbcbcb; - border-color: #cbcbcb; - color: #fff -} - -.bt-w-main { - height: 555px -} - -.bt-form { - height: 100% -} - -.bt-w-menu { - float: left; - background-color: #f0f0f1; - height: 100%; - width: 110px -} - -.bt-w-menu p { - cursor: pointer; - height: 40px; - line-height: 40px; - padding-left: 20px; - position: relative; - text-overflow: ellipsis; - overflow: hidden -} - -.bt-w-menu p a { - display: block -} - -.bt-w-menu p .spanmove { - display: none -} - -.bt-w-con { - margin-left: 110px; - position: relative -} - -.explain-describe-list{ - border: 1px solid #ececec; - border-radius: 6px; - margin: 0 auto; - margin-top: 20px; - margin-bottom: 20px; - background: #f7f7f7; - padding: 15px; - list-style-type: inherit; -} - - -.agreement-box label{ - font-weight: 400; - margin: 0 0 0 5px; - vertical-align: middle; - cursor: pointer; -} - -.agreement-box input{ - height: 15px; - width: 15px; - vertical-align: middle; - margin-top: 0; - cursor: pointer; -} - -.label-input-group input { - margin-top: 0; - margin-right: 5px; - vertical-align: -2px -} - -.label-input-group label { - margin-bottom: 0; - font-weight: normal -} - -.bingfa .line { - margin-bottom: 10px -} - -.bingfa .line .span_tit { - text-align: right; - width: 120px; - display: inline-block -} - -.bingfa .bt-input-text { - width: 100px -} - -.ssl-con-key { - width: 47% -} - -.ssl-con-key textarea { - height: 240px; - line-height: 18px; - width: 100%; - margin-top: 5px -} - -.ssh-item { - float: left; - margin-left: 10px; - padding-top: 5px -} - -.ss-text em { - color: #555; - float: left; - font-style: normal; - line-height: 32px; - padding-right: 2px -} - -.view1, -.view2 { - margin-bottom: 15px; -} - -.searcTime { - position: relative:z-index:999; - margin-top: 11px -} - -.searcTime .tit { - float: left; - padding: 5px 10px; - display: none -} - -.searcTime .gt { - padding: 5px 10px; - border: #ddd 1px solid; - margin-right: -1px; - cursor: pointer; - float: left; - line-height: 16px -} - -.searcTime .gt.on, -.searcTime .gt:hover, -.searcTime .ss .st:hover, -.searcTime .ss .st.on { - background-color: #20a53a; - color: #fff -} - -.searcTime .ss { - display: inline-block; - position: relative; - float: left; - line-height: 16px; -} - -.searcTime .ss .st { - padding: 5px 10px; - border: #ddd 1px solid; - margin-right: -1px; - cursor: pointer; - float: left; -} - -.searcTime .ss .time { - background-color: #fff; - border: 1px solid #ddd; - display: none; - padding: 10px; - position: absolute; - right: -1px; - top: 27px; - width: 306px; - z-index: 99; -} - -.searcTime .ss .time span { - margin-bottom: 10px; - display: block; -} - -.searcTime .ss .time input { - border: 1px solid #ddd; - height: 22px; - padding: 0 5px; - margin-left:5px; - width: 227px; -} - -.searcTime .ss .time input:focus { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); -} - -.searcTime .ss .sbtn { - background-color: #20a53a; - border: 1px solid #20a53a; - color: #fff; - line-height: 24px; - text-align: center; - cursor: pointer; -} - -.plan { - padding: 10px 20px; - margin-bottom: 10px; -} - -.plan .typename { - line-height: 34px; - width: 120px; - font-size: 12px; -} - -.plan .textname { - height: 34px; - line-height: 34px; - float: left; -} - -.plan .planname input { - height: 34px; - width: 260px; - padding: 0 12px; -} - -.plan .dropdown button b { - font-weight: normal; - font-size: 12px; -} - -.plan .dropdown-menu { - min-width: 100px -} - -.dropdown-menu>li>a { - padding: 3px 12px -} - -.plan-submit { - margin-left: 141px -} - -.plan_hms { - position: relative; - height: 34px -} - -.plan_hms span { - float: left; - height: 32px; - line-height: 32px; - position: relative -} - -.plan_hms .name { - border-left: 1px solid #ccc; - width: 44px; - text-align: center; - background-color: #f6f6f6 -} - -.planSign { - margin-left: 10px; - height: 35px; - line-height: 35px -} - -.planSign i { - margin-right: 8px; - font-size: 12px; - font-style: initial; - color: red -} - -.plan_hms span input { - float: left; - width: 48px; - height: 32px; - line-height: 32px; - border: 0; - text-align: center; - font-size: 12px -} - -.plan_hms span:first-child input { - border-radius: 4px 0 0 4px -} - -.plan_hms span:last-child { - border-radius: 0 4px 4px 0 -} - -.plan .dropdown button { - width: 94px -} - -.txtsjs { - width: 450px; - height: 80px; -} - -.sl-s-info { - width: 210px; - border: #ccc 1px solid; -} - -.important-title { - display: flex; - color:red; - background-color: #fef3e2; - line-height: 46px; - margin-bottom: 15px; - padding-left: 10px; - box-shadow: 0 1px 2px 0 rgba(0,0,0,.2); -} - -.important-title>p { - flex: 1; -} - -.rec-box-con .onekey { - font-family: "宋体"; - width: 90px; - height: 30px; - line-height: 30px; - text-align: center; - background-color: #20a53a; - border-radius: 3px; - color: #fff; - margin: 20px auto 15px; - cursor: pointer; -} - -.rec-install { - padding: 16px; - width: 100%; - float: left; -} - -.rec-install .rec-box { - width: 290px; - float: left; -} - -.rec-install h3 { - font-size: 20px; - margin-bottom: 5px -} - -.rec-box-con { - border: #ddd 1px solid; - border-radius: 3px; - padding: 10px -} - -.rec-install .rec-box:hover .rec-box-con,.rec-box-con-hover { - background-color: #f9f9f9; - border: #20a53a 1px solid -} - -.rec-box-con .ico img { - width: 22px; - margin-right: 10px -} - -.rec-box-con ul li { - line-height: 26px; - margin-bottom: 5px -} - -.fangshi { - line-height: 19px; - margin-top: 10px; - display: block; - margin-bottom: 15px -} - -.fangshi label { - font-weight: normal; - margin-right: 44px; - float: right -} - -.fangshi label input { - vertical-align: -2px; - margin-left: 5px -} - -.zun-form-new .fangshi { - padding: 0 30px 0; - line-height: 30px -} - -.zun-form-new .fangshi label { - height: 20px; - line-height: 20px; - margin: 4px 20px 0 0 -} - -.zun-form-new .version { - padding: 0 30px -} - -.zun-form-new .version select { - height: 28px; - margin-left: 30px; - width: 120px -} - -.sec-install-btn { - font-family: "宋体"; - width: 90px; - height: 30px; - line-height: 30px; - text-align: center; - background-color: #20a53a; - border-radius: 3px; - color: #fff; - cursor: pointer; - margin-top: 10px -} - -.rec-box-con .onekey { - font-family: "宋体"; - width: 90px; - height: 30px; - line-height: 30px; - text-align: center; - background-color: #20a53a; - border-radius: 3px; - color: #fff; - margin: 20px auto 15px; - cursor: pointer -} - -.rec-box-con .onekey:hover, -.sec-install-btn:hover { - background: #10952a; - background: linear-gradient(#10952a, #088d22) -} - -.phppz { - margin: 14px 0 -} - -.phppz span { - padding: 10px -} - -.phppz textarea { - width: 500px; - margin: 0; - vertical-align: bottom; - padding: 0 5px; - height: 38px; - margin-right: 20px -} - -.soft-man-con .user_pw { - display: none; - margin-top: 30px -} - -.soft-man-con .user_pw_tit { - margin-top: 30px; - padding-top: 30px; - border-top: #ccc 1px dashed -} - -.soft-man-con .user_pw p { - margin-bottom: 15px -} - -.soft-man-con .user_pw_tit .tit { - float: left; - line-height: 22px -} - -.soft-man-con .user_pw span, -.soft-man-con .user_pw_tit span { - width: 75px; - display: inline-block; - text-align: left; -} - -.soft-man-con .user_pw span { - margin-right: 10px -} - -.btswitch-p { - margin-left: 10px -} - -.soft-man-con .user_pw input { - width: 200px -} - -.soft-man-con .conf_p { - margin-bottom: 10px -} - -.soft-man-con .conf_p input { - width: 100px -} - -.soft-man-con .funarea { - width: 100%; - height: 80px; - line-height: 22px; - margin-bottom: 10px -} - -.soft-man-con .ver .btn { - vertical-align: 0; - margin-left: 10px -} - -.webEdit-box .soft-man-con .phpmyadmindk { - width: 100px; - display: inline-block; - margin: 0; - padding-right: 0 -} - -.softlist td img { - width: 24px; - height: 20px; - margin-right: 5px -} - -.softlist .glyphicon-folder-open { - cursor: pointer; - color: #efd566 -} - -.softlist .btswitch+.btswitch-btn { - width: 2.4em; - height: 1.4em; - margin-bottom: 0 -} - -.wafConf_checkbox label { - font-weight: normal; - margin-right: 20px -} - -.wafConf button { - vertical-align: 0 -} - -.wafConf_cc { - border-bottom: 1px solid #ddd; - border-top: 1px solid #ddd; - margin: 10px 0; - padding: 15px 0 -} - -.wafConf_cc span { - margin-right: 5px -} - -.wafConf fieldset { - border: 1px solid #ccc; - border-radius: 3px; - float: left; - padding-bottom: 0; - width: 240px -} - -.wafConf fieldset:nth-of-type(2) { - margin: 0 10px -} - -.wafConf legend { - border: 0 none; - font-size: 14px; - margin: 0 6px; - padding: 3px; - width: auto -} - -.wafConf fieldset input { - margin-left: 4px -} - -.wafConf fieldset .table { - margin-top: -1px; - margin-bottom: 0 -} - -.wafConf fieldset .table tr td:nth-of-type(2) { - width: 42px -} - -.wafConf fieldset .table-overflow { - height: 210px; - overflow: auto; - margin-top: 10px; - border-top: #ddd 1px solid -} - -.wafConf-btn { - border-bottom: #ddd 1px solid; - margin-bottom: 12px; - padding-bottom: 15px; - height: 45px -} - -.wafConf-btn span { - float: left; - margin-right: 8px; - line-height: 33px -} - -.wafConf-btn .btn { - margin-right: 10px -} - -.wafConf-btn .ssh-item { - margin-right: 50px -} - -.gzEdit { - padding: 13px 20px -} - -.gzEdit button { - margin: 8px; - width: 140px -} - -.setting-con p { - line-height: 30px -} - -.setting-con p .set-tit { - display: inline-block; - height: 22px; - margin-right: 10px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - vertical-align: -2px; - width: 90px -} - -.setting-con p .disable { - background-color: #ededed -} - -.setting-con p .inputtxt { - width: 260px -} - -.setting-con p .modify { - margin-left: -40px; - vertical-align: 0; - position: relative; - z-index: 10; -} - -.setting-con p .set-info { - margin-left: 10px; -} - -.set-submit { - margin: 20px 0 10px 100px; -} - -.changepath { - height: 500px; -} - -.changepath .path-top { - height: 50px; - line-height: 50px; - padding-left: 10px; - border-bottom: #aaa 1px solid; -} - -.changepath .path-top .btn { - margin-right: 10px -} - -.changepath .path-top .btn span { - -moz-transform: scaleX(-1); - -webkit-transform: scaleX(-1); - -o-transform: scaleX(-1); - transform: scaleX(-1); -} - -.changepath .path-top .place { - display: inline-block -} - -.changepath .path-top .place span { - color: #444; - font-size: 12px; - font-weight: bold -} - -.changepath .path-con-left { - width: 130px; - height: 450px; - float: left; - border-right: #aaa 1px solid; - padding-top: 5px -} - -.changepath .path-con-left dl dt { - background: url("../img/ico-computer.png") no-repeat left center; - height: 30px; - line-height: 30px; - padding-left: 23px; - font-size: 14px; - font-weight: normal; - margin-left: 10px -} - -.changepath .path-con-left dl dd { - line-height: 30px; - padding-left: 12px; - cursor: pointer -} - -.changepath .path-con-left dl dd span { - color: #666 -} - -.changepath .path-con-right { - float: left; - height: 450px; - overflow: auto; - width: 520px -} - -.changepath .path-con-right .default li { - width: 25%; - float: left; - text-align: center; - margin-top: 20px; - cursor: pointer -} - -.changepath .path-con-right .default li span { - font-size: 40px; - color: #666 -} - -.path-con-right .file-list { - display: none -} - -.path-con-right .list-list td { - padding-left: 10px; - cursor: pointer -} - -.list-list span.glyphicon { - color: #666; - margin-right: 10px; - font-size: 18px -} - -.list-list span.glyphicon-folder-open { - color: #edca5c -} - -.list-list span.glyphicon-cloud-download { - font-size: 12px -} - -.newFolderName { - border: #ccc 1px solid; - height: 30px; - padding: 0 5px; - width: 90% -} - -.path-con-right .list-list td .delfile-btn { - display: none; - float: right; - font-size: 14px; - text-align: center; - width: 18px -} - -.path-con-right .list-list td .delfile-btn:hover { - color: red -} - -.path-con-right .list-list tr:hover .delfile-btn { - display: block -} - -.path-con-right .list-list td .btn-xs { - margin-top: 4px -} - -.getfile-btn { - background: #f6f8f8 none repeat scroll 0 0; - border-top: 1px solid #edf1f2; - padding: 8px 20px 10px; - text-align: right; - width: 100% -} - -.warning_info { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; - height: 40px; - line-height: 20px; - padding: 10px 15px; - border-radius: 4px; -} - -.success-msg { - width: 100%; - padding: 30px 50px; - font-family: "Microsoft Yahei"; - float: left; - position: relative -} - -.success-msg .pic { - position: absolute; - top: 50%; - margin-top: -100px -} - -.success-msg .pic img { - width: 158px -} - -.success-msg .suc-con { - float: right; - width: 260px -} - -.success-msg .suc-con h3 { - font-size: 16px; - margin-bottom: 15px -} - -.success-msg .suc-con p { - line-height: 23px -} - -.success-msg .suc-con .p1 { - border-bottom: 1px dashed #aaa; - color: #000; - height: 30px; - margin-bottom: 8px -} - -.success-msg .bottom-btn { - margin-top: 60px; - width: 100%; - float: left -} - -.success-msg .bottom-btn a { - width: 200px; - height: 40px; - line-height: 40px; - text-align: center; - display: inline-block; - border-radius: 5px; - margin-left: 30px; - color: #fff; - font-size: 16px; - cursor: pointer -} - -.success-msg .bottom-btn .close-btn { - background-color: #cbcbcb -} - -.success-msg .bottom-btn .blue-btn { - background-color: #5cb85c -} - -.tasklist { - padding: 15px -} - -.tab-nav { - border-bottom: #cacad9 1px solid -} - -.tab-nav span { - background-color: #ddd; - background: -moz-linear-gradient(top, #f6f6f6, #ddd); - background: -webkit-gradient(linear, 0% 0, 0% 100%, from(#f6f6f6), to(#ddd)); - background: -ms-linear-gradient(top, #f6f6f6, #ddd); - height: 32px; - line-height: 32px; - padding: 0 12px; - border: #cacad9 1px solid; - color: #444; - display: inline-block; - margin: 0 -5px -1px 0; - cursor: pointer -} - -.tab-nav .on { - background: #fff; - border-bottom: #fff 1px solid; - color: #444 -} - -.tab-con { - overflow: auto; - padding: 10px -} - -.tab-con ul.cmdlist { - list-style-type: decimal -} - -.tab-con ul.cmdlist li { - position: relative; - list-style-type: decimal; - list-style-position: inside; - line-height: 40px; - border-bottom: #dbdbea 1px solid; - margin-top: 6px -} - -.tab-con ul.cmdlist li .com-progress, -.tab-con ul.cmdlist li .state, -.opencmd { - float: right; - margin-left: 20px; - color: #535362 -} - -.tab-con ul.cmdlist li .line-progress { - position: absolute; - bottom: -1px; - left: 0; - height: 1px; - background-color: #20a53a -} - -.tab-con ul.cmdlist li .cmd { - border: 0 none; - border-radius: 0; - display: block; - width: 570px; - height: 200px; - line-height: 22px; - padding: 0 10px; - background-color: #333; - color: #eee; - overflow: auto -} - -.yuandian { - width: 10px; - height: 10px; - border-radius: 5px; - background-color: #20a53a; - z-index: 999999 -} - -.DrawRecordL i, -.DrawRecordR-T i { - background: url(/static/img/Detailsbg.png) no-repeat -} - -.DrawRecordCon { - font-size: 14px; - height: 422px; - overflow: auto -} - -.DrawRecord { - background: url(/static/img/DrawRecordord.png) repeat-y -204px center; - margin: 0 auto; - overflow: hidden; - line-height: 22px -} - -.DrawRecordlist { - padding-top: 6px -} - -.DrawRecord .DrawRecordL { - float: left; - color: #9a9a9a; - margin-top: 9px; - padding-right: 22px; - position: relative; - text-align: right; - width: 110px -} - -.DrawRecord .DrawRecordL i { - background-position: 0 0; - display: inline-block; - height: 5px; - overflow: hidden; - position: absolute; - right: 0; - top: 9px; - width: 16px -} - -.DrawRecord .DrawRecordR { - margin: 1px 1px 30px 112px; - color: #666; - padding-right: 5px -} - -.DrawRecord .DrawRecordR h3 { - font-size: 14px; - font-weight: normal; - margin-bottom: 5px -} - -.footer { - bottom: 0; - position: absolute; - text-align: center; - width: 100%; - line-height: 50px -} - -.bingfa { - padding: 10px 0 60px 0 -} - -.bingfa p { - margin-bottom: 10px -} - -.bingfa p .span_tit { - display: inline-block; - text-align: right; - width: 120px -} - -.bingfa p input { - border: 1px solid #ccc; - height: 30px; - line-height: 30px; - padding-left: 5px; - border-radius: 3px; - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s -} - -.bingfa p input:focus, -.bingfa p input:active { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -.bingfa .submit-btn { - background: #f6f8f8 none repeat scroll 0 0; - border-top: 1px solid #edf1f2; - bottom: 0; - left: 0; - padding: 8px 20px 10px; - position: absolute; - text-align: right; - width: 100% -} - -.bingfa .submit-btn .btn-danger { - margin-right: 5px -} - -.transfer .backupdata { - height: 150px -} - -.selectdata .slabel { - font-weight: normal; - margin: 10px 41px 10px 0 -} - -.selectdata .slabel input { - margin-right: 5px; - margin-top: 0; - vertical-align: -2px -} - -.backupbtn { - margin: 20px 0; - width: 100% -} - -.neice_con { - padding: 7px 20px 20px -} - -.neice_con .tit { - font-weight: bold; - line-height: 30px; - color: #777 -} - -.neice_con .nc_con { - background-color: #fdfdfd; - border: 1px solid #ddd; - line-height: 28px; - padding: 10px; - border-radius: 3px -} - -.nc_opt { - padding-top: 6px; - text-align: right; - height: 24px -} - -.neice_con .nc_opt input { - margin-right: 6px; - vertical-align: -2px -} - -.neice_con .nc_opt label { - float: right; - width: 150px -} - -.nc_con_user p { - margin: 15px 0 -} - -.nc_con p span { - width: 80px; - display: inline-block -} - -.nc_con p input { - background-color: #fff; - border: #ccc 1px solid; - height: 30px; - line-height: 30px; - padding: 0 5px; - width: 280px -} - -.nc_con p input.disabled { - background-color: #eee -} - -.nc-tips { - padding: 20px -} - -.nc-tips p { - line-height: 30px -} - -.nc-tips p span { - font-size: 14px; - font-weight: bold -} - -.nc-tips p a { - color: #20a53a -} - -#BarTools .glyphicon { - color: #666 -} - -#BarTools .glyphicon:hover { - color: #fff -} - -.list-list .ico { - background-position: center center; - background-repeat: no-repeat; - display: inline-block; - height: 30px; - margin-right: 10px; - width: 33px; - z-index: 1; - float: left -} - -.fileList .ico { - background-position: center center; - background-repeat: no-repeat; - display: block; - height: 80px; - margin: 12px auto 4px; - width: 84px; - z-index: 1 -} - -.list-list .column-name .text { - color: #595c5f; - display: inline-block; - max-width: 85%; - overflow: hidden; - text-overflow: ellipsis; - line-height: 30px -} - -.list-list .column-name .cursor { - display: inline-block; - cursor: pointer; - width: 200px\9 -} - -.table>tbody>tr>td { - vertical-align: middle; -} - -.showpicdiv { - max-height: 400px; - overflow: hidden; - width: 400px; -} - -#BarTools { - font-size: 16px; - float: left; - margin-right: 20px; -} - -#BarTools>button { - top: 0; -} - -.comlist { - float: left; - line-height: 28px; - margin-right: 10px -} - -.comlist>span { - margin-right: 20px; - font-size: 14px; - float: left; - padding: 0 10px; - border: #fff 1px solid; - border-radius: 3px; - cursor: pointer -} - -.comlist>span:hover { - border: #ccc 1px solid; - border-radius: 3px; - background-color: #f3f3f3 -} - -.PathPlaceText { - position: absolute; - right: 0; - top: 0; - background-color: #fff -} - -.PathPlace { - color: #666; - float: left; - font-size: 16px; - line-height: 30px; - margin-right: 5px; - width: 170px\9; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.PathPlaceInfo { - font-size: 14px; - line-height: 30px; - float: left; - margin-right: 20px; - color: #999 -} - -.onPath { - float: left; - line-height: 30px; - font-size: 14px; - color: #666 -} - -.editmenu span { - display: none; - text-align: right; - width: 300px -} - -.table tr.hover, -.table tr.on, -.ui-selecting, -.ui-selected { - background-color: #f5f5f5 -} - -.table tr.on .editmenu span, -.table tr.ui-selected .editmenu span, -.table tr.ui-selecting .editmenu span, -.table tr:hover .editmenu span { - display: inline -} - -.ui-selectable-helper { - background: #6bb0c9; - border: 1px dotted #072246; - opacity: .25; - overflow: hidden; - position: absolute; - z-index: 99999 -} - -.list-list .ico-folder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAMpJREFUeNpi/P//P8NAAiaGAQYD7gAWdIFPpxIoMS8ZiOcQqfY5SD0LFT1jAsRTuaU0GZjZuAgq/vPto+S3V3fmUssBokC8llNEjp2ZFWjk/1+Eg56TE0RJsgCD3BPImAvikGs7IzMLAxuvKAMrNz9ZaWAul5iKJAsXPwUBAMzK//9AaDIcIAkODiKCbbQcGHXAqANGHTDqgFEHjDpg1AGjDqCVA57/+f6Z7hZD7XwBahOmfHv1ANQqlqCzG54CcRrjaOd0oB0AEGAAscwsxMSUtNsAAAAASUVORK5CYII=") -} - -.list-list .ico-folder-unempty { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQRJREFUeNpi/P//P8NAAiaGAQYD7gAWcjR9OpWASyoZiOcQacxzkHoWKnrGBIincktpMjCzcRFU/OfbR8lvr+7MZSQnEbIzosbc65NxokDqDKeInBwrNz/xIfnwMgPjx5PxnkD2XCCWJNfrjMwsDGy8ogzs/CKkRSXQAaAomMslpiLJwsVPQegDQ/H/HwhNRiKUZOHkZPjx4xvD799/6ZLyWVmZGTjYWVBzAchybsUKujjg6/0OuAMGvBwAJcL/A14Q8SkYD4jlnx6chaaB/79GK6NRB4w6YNQBow4YdcDIdsDfP98/0d1iqJ1/QdVx47dXDxsGIDRADdBGxtHO6UA7ACDAAIASR69Q0kB9AAAAAElFTkSuQmCC") -} - -.list-list .ico-access, -.list-list .ico-mdb, -.list-list .ico-accdb, -.list-list .ico-db, -.list-list .ico-sql { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAwZJREFUeNrMV0trE1EU/jKJkzTTR4oJCX1lqq6q+GwriiLarS4VfCwqFEFxKShYUKE+Kv4GcVkquBE3glq0Vqt9iGg3qfZhSVttU0tNJk2aGc+9dGSSJmlaJqkfHG7OvZOc757v3HMnFk3TsJGwGZ2hoaEjNNwnazQxxh6yT8aJurq69AQIHT5CSUmJKZEDgQAbHpOdTCWhQ0jxfZIkQVVVU4zB7/c/XyaxOxcCpkMUxUtEojsTCaEQhUYkmmVZHkhHwpbPwKWlpXodMJxaHgfJLOsiEB4eRqi7B+HvI1CjUUQnp/i8o7ICgihC2rYVmw8fgrNW5vMejwderzddYa49A7NvujHzsgvlBw+gnIJYHXbYfT6+Fg0GocbiiAx/w/jDR/CdOA5XYz1f04sxpz6QDTMvXqHy7Gk4t9SuWHNUVPDRKft5NqafPvtHYE2NKBvUpSVYbDaEet5B2CRCS5BvoRoWBKjxGKx2OxKLi7C73dDo2XV1wmxQxsZRVFPNLRsSigJlYsJ8Aq76fZjvH4CtrIzLYBGSTzDbdYSKMzo1BVdDg/kEfvf1Q758EbFfM5j/2M/TzqRgEiSUCEnggJNOgaO6CsGOTvhbzptLgOnPdil63NwyShAOr8iOSRLsJQkGYS2WYKX7wkFHUKCjqOu+OP0TSwsLiM+G1iRBzlTn3n+Aa38DP2oMf6ihzHa95sa0Z5CoNtgzc7295mdAl4Dt3klWcAk8TUcRaLvLOyHLgoVaLxs16nTK+A/empXRMd4n3E3HzCdQde4MIiOjCL3twWQfOwVx7rPdFvlrIFAjYneAfKGF+6YT4K2WAugXjVkoyPvAf00gSYJQ602EChH1SWfmGrDa8vqShETKTZkx2s57t/H52vUkXwebN/pG6N/R143PGn8vp1Ogk0hHJlc/dS1vRcgCZdp1pmyZSoAFMu40VxJJEgjFxQlNUay72u+sSOFqNbDa7tln9keYxUi6Y4z/jvvaH7TGvny9pYbDeekPgiSp4o7tN+qvXmlLS2Aj8FeAAQC23WzgeNtzYAAAAABJRU5ErkJggg==") -} - -.list-list .ico-c { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFuKz0K4QDTjbbc0N4qrvP7+p0yABWY5sKTqf7mmAHMuHVy5ATg9k5wJ+VoZ/n/79Z2JkZCQqyElNhKCOMB8Lwz+cDjAVYGw69f5P3ac/xIWMy4rLJPkW6MG/QDuaUeqXge6eAwQYABvvATpIEEu4AAAAAElFTkSuQmCC") -} - -.list-list .ico-cpp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAoBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFuxZaFeIBpzttuYGSWIo1fef36RnQ5ChyIYh85EtxidG1TRAyFCy0wA5ABbk6CFErENZyLUUPbjJDSEUB/CzMvz79O8/EyMjI4aBhBIXMQDUEeZjYUBppTIi947LdlypP/X+f92nP7QpH4Ae/GsqwNjc5aHTiNUBAwEAAgwALWAgpic4qfUAAAAASUVORK5CYII=") -} - -.list-list .ico-cs { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAoFJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFvxZaFeIBpzttuYGhjiyGNbq+89v8rMhyBJ0S0F8ZHF0NTRNAzCLYI4gJgRolgiRHUE3B8AsRPY9qY5gRO6cOq64/PfTPxYmRkZGmiRCkF28jH/+HYjUZcaaCE0FGJtOvf9T9+kP7pBxWXGZJHFkwM/K8BdoRzPOEBgIABBgAMrJHxYdaBS2AAAAAElFTkSuQmCC") -} - -.list-list .ico-fla, -.list-list .ico-flv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYFJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDkV3BAwwofEluLm5Gf79+0cVDALy8vK7oI4wIMYBVAdsbGxZQEccweUIJnokNKAjEhQUFM5hcwQLLS3m4+ODpQMQCIPS54GYkS4OEBUVZRAXF8eWMMkLgZf1fQx/37yD85lFhBjEG4sYPizfCOYLRPpj6IElRqLKAWIAyBIuG1OqhRITwwCDAXcAyVEAim9YnLNrKDMI5ybS1wGjaWDA0wAu8O3IaTCGAVDaAKURQoARuUECbA/8V1ZWpqmP7969C2oPMI6mgVEHDM5syNI+k+EhkXn0DyUWLp6I2w5cliq1laDw71X1gMVANDa12MSxOZykggiXoXRLAyCfoYcELt8TUkfTECDWcqo3StHjHl9awB4F3Fx//5NhKbE+/g+1A2dldGPCrBqm6/caGb59Z6JJNuTi/PdPU6leoyCtBasDBgIABBgAWP2ttPWI30cAAAAASUVORK5CYII=") -} - -.list-list .ico-css { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAALdSURBVFiF7ZfLTxNRFMZ/M/RBnwik0FYpoJIYhJioLFSIupCoWzVqotGNCeEPcGPiI+50Q9xAYli5MaEbFyTiI0EWhhghGkohlgVREVzQhpfals64qB067ZRpoaALv6Rp73dPzvnuud/cOxVkWeZvwpA+CAaDZ4BewFPEGoeBkXSisbFRWwDQ6/V6PTabrSiVQ6EQQB9woaGhYUQrRswYeywWC5IkFeUD4PP5BoC+UCh0KB8BRYfZbO6ora19A/i1RGy5AACTyXS9rq5uWEtEpgeKCqfTmfIBwKU/3+8BYVsEuFwuqqurVVyaoK0XAChmzIVNe0CS4drrGeLSxg60ggRMRKJMLcRU3Mf5X+yvMGMUBaYWYkxEosUXEEvIdAfC9IxHKDeXqOYGPi9zqsYOQLm5hJ7xCN2BMLFEfh3RFRAIR+kcmqXKaqCr1U1l6ZqAuCQTDEc5UFkKQGVpCV2tbqqsBjqHZgmE9buha8Jbw9/pOeHFY80OfTv3kyNuC6KwxgnAud1OjrqtdAx+49lZ37r5dTtQYzfSP72kabKXX5Zp99mz+Lgk0z+9RI3dqJdeX8CjNg8Ok0jn0KzKYMtxibkfq+xxmlTxE5HkljlMIo/a9C9V3S0QBbi4t4xjbisPP8xzo7GcpgozgzMrHPdaVbGBcJTHwQh3W1zstOmvHgp4DHfZjXS1uql3JBO/+rqiuD+FekcyJt/iUOBJKAA2Y1JzV6s7az41Vwi25Tb8L+CfFqAy4ZWRBIx8UsaiIX835wNpNQ7A6NpLcfZToFX0xfl9yu92/+SGedFgVETkFKBVPDN5u3+yYD4XhPR/RgefjMmZHVgvQWqlmfO5eEhuw+jVZuX62pQJ2/2Tyqrz4bVQsIBU0sx91uNzQbUFJ5+OJRYlgygIgiqoWCaUZRmHsCoNXm5W3mpUAm4+D9x5F5FvL65uzflQZiTRskO4/+B00z1NAQqZ0YFiI73mb2NmSwowsJSgAAAAAElFTkSuQmCC") -} - -.list-list .ico-js { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAALFSURBVFiF7ZfLTxNRFIe/GTotffJKoa1SQCUx1cSFslBLdKFNdKtGFya6MSH8CybG6NJNwwYS496EblywwEeCLAwx1mgshVgWREVwQRteamk746K2dNopM5WCLvwlTXvPPTn3u+f+5lFBURT+pkylg3g8fgF4BHjruMYJIFoaCAQC2gDAI5/P57Xb7XVZOZFIAIwCV3p7e6NaOWLZ2Gu1WpFluS4fAL/fPw6MJhKJ40YA6i6LxTLQ1dX1EohoQew6AIDZbL7Z3d09pQVR7oG6yuVyFXwAcO339xtA2BMAt9tNR0eHKlYCtPsAQNGM1bRjD8gK3HixQEb+sxtaTQAzqTRzK5uq2PvlnxxptSCJAnMrm8yk0vUH2MwpDMeSjEynaLE0qObGP61zvtMBQIulgZHpFMOxJJs5Yx3RBYgl0wxOLtJuMxEOemhr3ALIyArxZJpjbY0AtDU2EA56aLeZGJxcJJbU74auCW9PfWPkrA+vrTL11dIPTnqsiMJWTAAuHXBxymNjYOIrTy76t62v24FOh8TY/JqmyZ59Xifkd1TEM7LC2PwanQ5Jr7w+wFC/F6dZZHByUWWw9YzM0vcsB11mVf5MKn9kTrPIUL/+Q1X3CEQBrh5q4rTHxoN3y9wKtHC01cLEwgZnfDZVbiyZ5mE8xd0+N/vs+ruHGi7D/Q6JcNBDjzNf+PmXjaL7C+px5nOMLg413gkFwC7lmcNBT8V8Ya4W7cnT8D/APw2gMuH1aA6iH4tj0WTczUYkZzMAvN16Ka68Cowu+vTyYUKR2eLvggoxLYkmqQhRFaBWlYJojfVUFw8Y7YCWdtyBWo+hXDV1oHSR8lgoMlvz7qGsA00S8qqsiIIgVMuvgAhFZg3vXlEUXCZUb6kqgL5m4d7rVPbOala7M+cef9AsXC1eriaJXF+zcL80Jhj5e75dR7aTkdq/AFCYGAYKWHWoAAAAAElFTkSuQmCC") -} - -.list-list .ico-htm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFtQstCtEg8FtzQ04GxsAycPkYGqR1SOL/fvzm7IGCbJjkA2maRoAWYbL97hCglgHMhHra2r4li65ABYKxDoYxQH8rMAmHg07qyCz+VgYUFqpjMgWlu24Un/q/f+6T39oUz4APfjXVICxuctDpxGrAwYCAAQYAPufFoHFXwuVAAAAAElFTkSuQmCC") -} - -.list-list .ico-html { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnpJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFkgW2hWiweC25gacjQ2A5GFyMLXI6mHyyHL//vwmLxsiOwbZQJqmAZBluHyPKyRIdSATIYOp6Vua5gJs8U2yA/hZgU08GnRWYVEJMpuPhQGllcqIbGHZjiv1p97/r/v0hzblA9CDf00FGJu7PHQasTpgIABAgAEAooAb7fMLYAoAAAAASUVORK5CYII=") -} - -.list-list .ico-java { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvRJREFUeNrUV0toE1EUPfPJTJNp82lNY1PaBGwtpn6q9bNQQRFFpSAKgrgQu3PpXlBEBMGNWwU3LtwoxYULhSoKgkJbawVDta3WT5XWEPpL2iSTifeNjqY6TeNkavDA5c375N1z7z3v8cLlcjmUE2J+JxqNHqDmOlmdjT42k/XlD0QiEXMCzHkwGKxTFMUWz0NDQ6y5RXa0ubm5z2wN/1u/zul0QtM0W4yhsbHxPiNBZNqLIWAZI+PzpuOyLJ8KhUKP6fO2GQlbCLyPpfAhll50XpKkk+Fw+JkZCbFU56qWw72BKRxq9/0x53a7DR0wHPvR9pJxthCYSmbR1RPHnlY32GEei6cRrJZ+7u73+xEIBMyEWXoJBt4ncfXBOLavrkLv2wSVIIX6POcGzIRZMoGZ+Szu9MbRuasW/aMJ+BQB25oqLQViiYDIcwj7ZfirRF0D6az129QSAafEY+uqSnyZzKBjow89IwlEx+b+HQGGNfVOVDg4uJ0C2kIuVMr88hNgwnv+LoHYjKr3fYoIjlS3b71Hzwq7jNJqDk9ez+Bu/yS0IipT9DFkG8dnVYxPZ/DyYxJM0FnyUOHg4RA5NNRIlAkFEn3vaKnCtYcTiH5KYm2Dyx4CbOPddN5Hv6b0aAMex4J5Rkbgvx9ClURpCNW2DBiYoAw8fTMLmerPiLhkQXfICO7f4NXHWIlO7FwBUeDsIZBMaxj8PId6n6Srn1kheFyCnhHbCDyKTuMFXThNKyvgdYkUNdWdNmcOHMKvtCdSml4iNn94S/Xfv4gWw8E2L/au8+jpj02rmJrLIp5QkSFhZsixTOmXSYw1dDF1bPLqp8PSk6wQWKSsBMzsBI8yo+wEFpTgUjddXd3Dy+70RqRABiRZXmCXO1tt7f/fGrh4vAlnbg7rrYH8vjFfaH3JGSi0mZX14lLRFtM3oix2/aIZUCT2btSW/JFVsL1d5CN/jMv/d3yl69W5wQmcTaaXR5wUYLalFhdOH2k9b0qgHPgmwABqjFw0GGyiFAAAAABJRU5ErkJggg==") -} - -.list-list .ico-log { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAVBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkLQSevnqLU05aTBivXlhiJKocINcSSgATwwADiqOA0pAbjYLRKBiNgtEoGHXA4MoFE+fsooulM/twt4pBzScMDY3VmQz1rdMxxGAAWQ6XOAz8+vWL9GxIyEEwPi7xAUsDhCynqQNAvkeODro7gBjfY6QBLk62f79+/2MCAqw+ghkMi290y9DFsTVQQXYgizEi946nzllbf+f+y7pv33/RJGS4ONn/qiiKNWenBDdidcBAAIAAAwBHPa+btF94QQAAAABJRU5ErkJggg==") -} - -.list-list .ico-mht { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57Tw5AImFtQstCtEA0y7rbmBVQzEhsnBxNEBst5/f37TtkGC7BhkiylKAzCf4jIYJIbL93RJhCAHEeNbihyAHt80yYb8rMAmHg07qyCz+VgYUFqpjMgWlu24Un/q/f+6T39oUz4APfjXVICxuctDpxGrAwYCAAQYAIH+FoEeflonAAAAAElFTkSuQmCC") -} - -.list-list .ico-php { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA1ZJREFUeNrsVktPU0EUPn3dPq73QmmbFlRaAw1Q5KUiYCIkmmjUGEMUdGWMSze6cmViTFwYVv4Aw8KNRhe6kA0SAonRGGNBHpWkBAXFIFAeLS307ZyB1ku90EJvdeOXTO7c6XS+b8755tyRxeNx+JdQCl9cLlcLeXSQdlRCjjrSBoUDDodDXADBUwsBx3GSMLvdbnw8J63NbrcPis2Rp7xbWJaFWCwmSUNYrdZuFEHE1GYiQHIwDHODiHizlQj53zAaEXHNZrM5xUQoc0nM83zCB4j2jecAabJdCVgLx8D5xQ9j06swOR+CGDnCkWgcWLUCrCY1VO3XQlWxDhTy9fVNJhOYzWYxY+48Akj8eniZkOjgdE0+mHgVMMp1In8wBnPeMJ3TM+KFC0f0YLdo6G8JM2ZUB7ZCn8tLCW6esYBG9ds27pk1sns5FOkZYEkEbKShmGfvPOBbjcKhA2zatdOacGI2SMIdhLZGwyZyBI5j+IVAQVebjfB+fIWK3lElFEM/2f3Zunzax0WnF0IgI5E/V6eHmaUwdA8tQa2N1A5S0Ycm/RAhET9RycOp6jzo/+yDSw0F2UVgZjkM5jwV7X+dC0JLBQ+lZg18J0ICJNwXGwzwaTJAhVXs1dL8D5H3YqMapkiEsk6B8GPl8UVAv0cJPxZDNNScVgFoeJVCRsONXvjmCdHxTJFWgJFTUUKaL0L08sMC5OmUEIrEwWpkqOlYzfoyvaNeGJ4KQH0JS71TSARl7YGTB3l45VyC9sYCKCvS0BQkaoKJV9LdN9k56B1ZhvOH9clj2eVchCvHDNlHoITku5Sc6SdvPVBWqE2O44nAhkUH09G8IQzT9Kh3Fo6XczQlWUcAga42kNx39s3SAlNtZYHTyJMEaEDfWhS6Bvy0QrbW66G8SLvzC8l2qLHqwLFPS3P8cWIF5slOkRhNiLnGyohV8nKTIVmKJRWAwHxjdcukwkl2CnKN/wI2eeBBD6l6PeM5J33s2MaEjFr9xx86rpcm+7c7x5NjqX2xeakIBYO7SwEumEqSzTxJ7oSZkkgiIEEmDK0wBdvNk0RApgtmOk/UAywD0XSXyGyAayOHcEwmvHA8fDF6Z+wn3AuEclMfdAzEys1w91Zr5X1RAf8CvwQYAB0rcvQMbg3OAAAAAElFTkSuQmCC") -} - -.list-list .ico-url { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAr5JREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGakmgP+/P/LsP/FJYZzb+8wMDEyMchyizD4yVgwcLGwM4iKijKIi4tjS5jkh8DLHx8Ytj45xXDj4xOG59/fMfCwcDBYi2kxBMpZMajxSYPVgMRBDgABWGIkqhwgBO5+fs7QcWUVw4+/vxn42bgZtATkGAwElRjWPDzCsPf5RYZcTT8GIyFlBklOIfIKInwAZOnE6xsZ/gB9FKfszOAqaYgoWIAOmXlrG8P0m1sYuoyTGQTZeIh2ANG54Oirqwzvf31hiFC0R7EcnNe5xRhKtUMY/gGL9d3PzpNfFOMN/i8vGJR5JYHBfZhhyb19eNQ9J8kBRIeAOIcAgzA7Lzgq8IGfBOTJDgE9QUVwYmszTABnNWoBokPg+OvrYHrD42NY5S+9v88Qe6SH4dy7u7RxAMgCHQF5hlNvboFzw+sfH8Hiv/79AWbBC2AxDX5ZcDakSRR8+v2dockglmHu7Z0Mx4ChcebtbXBh8+3PT7A8yHHZGr6UNUjwATEOfgYWRmaGTHVvBlMRNYbTwJB4+u0tg7iAANByBQZHCT3KW0T4QISCHcPBl5eBiVGBwUBIicFEWJX6TTJ8ABS/IExtQJf2wKB2AEoUTHixg4HhBe0tnYXUKiYqBNqdMrDyQTQyxqWeau0BbKBy3wwUi5H5Qy8NkAOQg5tU3w+KKEBxADcT+9+v/34yY7OEVJ/C1KOrBdmBzGdE7h1PPbGm5vaPF43f/v2iSdrgYmL7p8ohUZ9tEdKC1QEDAQACDADzFSd59WqSKgAAAABJRU5ErkJggg==") -} - -.list-list .ico-xml { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gIUp4F/wHIsfu9Tht//yCvQSHLA9fc/Ge58/IUidvHtDwZtIXYGViZGsBxIDdUd8Ovvf4bpV94xzLj6nkGQnRlFbuejLwyusjxgNkgOpAakFqSHKg648u4nQ9ah5wxiXCwME2wkGIQ5EA4ABfs1oLy+MAeYD5IDqQGpBekB6SWpLsAGqk+8ZJjhIMUgyYWp9NiL7wyWEpwMTIwIMRAzWImPwUqCiyHjwDOGjV5ylIWALA8rw9YHn7Emst2PvzC4yfFgiIPUgvSA9FIcBZNsJRl42ZjAQYqcwL78/sfw4tsfBmU+NoyEClIL0gPSS3EUgII3XIWfwRoYpN0X3jKkagky6ABT/YGnXxnspbgw0svsa+8ZGkxFGaS5WambDWWAwQlKYIq8EIP3PPkKT/0wAJIDqSHWcpJLQlAC42aFuBlkETqAydGsIKIFGHXAgDsAJRHGnP0LbL/eorml57TwhAATCysK3hOhi5UPorHJIcujmwXCJEeB25obDLtCNMBsEA3i0z0NwByBbjk1HDc0cgHMdzDfEhM6ZDmAnxXYxEPrrCIbjssR+ByOrB5kNh8LA0orlRHZwrIdV+pPvf9f9+kPbaIG6MG/pgKMzV0eOo1YHTAQACDAAOGRHaFJlGXVAAAAAElFTkSuQmCC") -} - -.list-list .ico-ai { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAg9JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZ6eIAAQEBsCMYGRnh+OHDh7SLgvdXNjK82N8K57OysjIwMzMTXw4QC96dX87w7elRBin3HgYmVg64uKCOP5D0R7WAhYXh79+/OM0iOQT+/f4BtpyJjZfh65MzROkBhQITExM4Cih2AMhSNkFVBl5Vb4bvz1DLljen5oCjARuApQGKHQCylFPKmIFTQofh1/vbDL8/vSBaL8UOAFkGspRbxoSBhVOAgUPMgOHbi8sUJVySHACyDGQpLOGxi2owfH2wj3q1IcH4B1r279dnhidbclCj5cU1YJRo0dYBIEtAAD3rgbLk9+cXyXYA0VEAsoRDVA/FchDglNQHZ0tQ9iQHMCK3iIANkv/Kyso0rZju3r0LapAw0rU2pFouGHUAzVvFLEfSGR4eoYOFWodxhwA7C3FYLWweXj4uPDzSgHzQPIaH65LA9IAlQmpYTlaTDOZ7XHzKQoCV798/GvZVwWaz8v7DWRfc2Fxbz/TmbB3D78+0SZxsfH//CRs1a/g2N2J1wEAAgAADAIk82p3e8s5qAAAAAElFTkSuQmCC") -} - -.list-list .ico-bmp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmtJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7anvAKhs1Sx6biiIO46OLIetDVvPt708yygE0S5D5xOjDp4eoNAAyAIaRDUL2KSkOo1sIYIsmqldGsFDAZgkxDkVxABcT2z9gxcPEzMiEMyGSE8zIlRrIDpTGDXLveOqJNfW3f7yo+/bvF03KB24m9r8qHOLN2RYhjVgdMBAAIMAA9C4L2PHEWagAAAAASUVORK5CYII=") -} - -.list-list .ico-cdr { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmRJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7Cr/WPhHObj44H0OMGHFk8O3vT+ITIchAZEOQ+aSKUyUNEDKM5pURMQBblFEtBNDjGFcowTAx6lFCgIuJ7R+w4mFiZmSCG4bLR6T6FFapgexAadwg946nnlhTf/vHi7pv/37RpHzgZmL/q8Ih3pxtEdKI1QEDAQACDAD7sQauaYXLQgAAAABJRU5ErkJggg==") -} - -.list-list .ico-gif { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM77raBVTYiax6bChcDsZHlkOXRwbe/P8krCWEW4eLjs5QmaQCbZSBHoYcETeoCmCUDFgLkWERUCHAxsf0DVjxMzIxMWC0lNXixVWogO1AaN8i946kn1tTf/vGi7tu/XzQpH7iZ2P+qcIg3Z1uENGJ1wEAAgAADACWJ9+r79LDyAAAAAElFTkSuQmCC") -} - -.list-list .ico-ico { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAaVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLBSZ/Pc3w+/HVxhYZHUYGJlZMaT5+Phg6QAEIqD0GSBmpIoDfl7Zx/Dt4EIGLvt4BnZ9dwx5UVFRBnFxcWwJkzohwKpoxMD+9gmYxgVgiZGocoBUwMQnysDllEy9gogU8H5SFJzN5ZjEwK7rQp4nyHUAl1MKGFMKyHYAu44TGA+YA6gFRh0w4A5goTQLgsC3/fPAGAQE85bR3gGc1pEDGwIcxr6jiXD4OIARuVn+sECTLm10+QnX8TRIWNixa6rewvCw1QfOhjsaKoZPHAX8+UlZLkB2CDIflzjN0wAxlgzvXIAc7xSXhP85Bf4x/vrKxMDIhDfIsSU2XOIo4P8/oB38/3BmwzuzCutZ7h+vY/z+kSZR859L4O8fBYtmlbT+RqwOGAgAEGAAjiC9hXL1ZWMAAAAASUVORK5CYII=") -} - -.list-list .ico-jpeg { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnpJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7hoZa+0SG5oPzwTQMgPgwOWSATRwmBgLf/v6krCREtgDGRrYAXQ4bn+ppAGQBekhgczRV6gKYRciG4rMAm3qiQwCbr0AG4bOQFN9jhAAXE9s/YMXDxMzIRFT8YXMozIHYHA+q1EB2oDRukHvHU0+sqb/940Xdt3+/aFI+cDOx/1XhEG/OtghpxOqAgQAAAQYA3ogNywRusk8AAAAASUVORK5CYII=") -} - -.list-list .ico-jpg, -.list-list .ico-JPG { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAm1JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7/vaBVTZD07GpYBqeTYF8ZHls4jDw7e9P6pWEMAuQHYXuGGyOoFsaIGQ5xSEAC25sFuGTIysEkOMW2Ye4LCDG9xghwMXE9g9Y8TAxMzLhTYD4ghybQ5ErNZAdKI0b5N7x1BNr6m//eFH37d8vmqQNbib2vyoc4s3ZFiGNWB0wEAAgwADnlgGzkKaAXwAAAABJRU5ErkJggg==") -} - -.list-list .ico-png { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7anvAKhuRHY9NRRFD5qPLIcsjg29/f5JRDmCxCGvjBU2ekHqK0gDIYGTf4nM4xQURepBTUw8Tsb7FZhC+UCDWsSghwMXE9g9Y8TAxMzLRJFpAlRrIDpTGDXLveOqJNfW3f7yo+/bvF03KB24m9r8qHOLN2RYhjVgdMBAAIMAADIwGkBp21VIAAAAASUVORK5CYII=") -} - -.list-list .ico-psd { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAkRJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZ6eIAAQEBsCMYGRnh+OHDh/SLAlZWVgZmZmbiywFCYMKxewyvvv1CZHAJPoYgHSn8FrCwMPz9+5c6DgCBcC1JBn0pfoaXn38wLL/8jOHA3dcMDsqiePWAQgFU4mIrdclOA+K8HAyWMgIMl15+Ajvg5KN3DBtvvYLLt7looKgHxT9VQgAbAIUGyPIMYzkGOUEunOqwOYLsRPjo/TeGPfffMuiJ8zEIcLKCxd5//01ZZUQMWHntORiDgL+aGIO5nBA8bex/8JZhy+1XDC6KwnBxqjsAlgjRAUgMhC8++wh2oCQwjeCLDpqVAwpCXAzcrMwM3//8pU0I4EoPM84+gvPdgFGgLspLlF5G5LwJbJD8V1ZWpmnFdPfuXVCDhJGutSFd08CoAygqiGLOArPO2Vs0t/ScFp5syMTCiqFhmZ8anB216RaGOEwMWR26Whj49+c3eeUAsiUgNoxGFsPmQGyOoEoagDkCl0+pnghBlqH7CNkRNHcAyDJc8U+JI1DSAD8rw79P//4z4Wq9EBMFuBIsCICKfT4Whn8464KyHVfqT73/X/fpD23KB6AH/5oKMDZ3eeg0YnXAQACAAAMAI0n+E64by+4AAAAASUVORK5CYII=") -} - -.list-list .ico-webp { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAo1JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgTqTi1guPH+MVkO0BCUZWgySyC+HMAGQJavcq8lywFhO5tJK4gIgfNv7jDMuLIZzM7Q8WUwFFGhOJpIygUgywv1Q8AY5hC6OgAGGIFpmFo1CElRkKHty9B3cQ0DE9AFmUA23R1gKKrCMMuhEKvc+59fGA49u8Tw5MtrBjZmFgZNQXnqOwAXAFm88f5xBjdZYwYXIP725wfDqZc3qeeAv///MRx5foXh3OvbDF9+f2eQ4BJicJDWZ1Dll2a48OYuw9EXVxlaLRIZOJjZEKEFzCF7n5yjjgOaTi9m4GblYHCVMWYQYOdhePb1DcP86zuAYpwMr769Z2ixSEKxnOppQFVAmiFGzQXOV+STYLCQ0GK49OYegxS3MAMv0CE0S4SsTCwMm4DxC8KkAnUBWcodsNS1kqZVNV3aA4PaAShRMOHFDgaGF7S3dBaeVjEDFzM7Q5V9PEPbwYUYGmHitfaJKOLNB+eD5ZABTD+yWpC6b39/UqckBBmGDpAdjewJmFqQYyr3zSCcBkAaYT5CppEtABmGHhIgNTCMLQSpWheQEgIwh2LTg9MBsFBApqnpWKwO4GJi+weseJiYGQnnTvTEhRxd6KGBXKmB7EBp3CD3jqeeWFN/+8eLum//ftGkfOBmYv+rwiHenG0R0ojVAQMBAAIMAErgG/MeljhhAAAAAElFTkSuQmCC") -} - -.list-list .ico-ape { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAZNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwxElUOEAM+dpYTpY6/vJP0gohYQMhwYh1Jt1ww6oBB7QCiE+Gn/lqG/79+DZwDiLH887Q2hn+fP9ImChjZ2FBorIUOiZaTFAJ8hc2EfcPLD3YEiKa6A4gBvFlVo9lw6DmAEblZ/rwwhi5tdMn+JbgbJGyMjFg1Cdf0MbxtKULhwwBMHFkMWRwZ/ELrh1CUC5AthrGxWUpxGoBZgO5DXGqJUUdyCGAzFCaG7GtSQ4CFWN9j45NqGUEH/OPk/vfvxzcmSvImtsQJNx9UqQHtwJkNH8zur2e7f6OO8ftXmpQP/7i4//5W0GhWSC1sxOqAgQAAAQYADkDCgkQGXJUAAAAASUVORK5CYII=") -} - -.list-list .ico-avi { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAtpJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08IsLGz48SdqZpE8dHFkfHQTQPNcUoMtYvugWkYQObD5GmaCJEtp0kiJOR7bHxYKJDje4wQ4GYDtSH/0Sy+QWZzAe1AFmNE7h1PWHe1/sYrhrpvv2iTOIEe/KsuxtBcEKTdiNUBAwEAAgwAcCQxq0cx9U0AAAAASUVORK5CYII=") -} - -.list-list .ico-flv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnx9DQHKeEwq9ddA8sBqKR1eDjw8Cvnz/JS4TYDKNrGgD5CD0ksIUKPt9TlA0HPASIdSQpvsdwADcbqA35j4Fa0YIOQGZzAe1AFmNE7h1PWHe1/sYrhrpvv2hTPgA9+FddjKG5IEi7EasDBgIABBgAhSU3wy5Zmw0AAAAASUVORK5CYII=") -} - -.list-list .ico-mkv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAq1JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYkayHfD333+GE4/fMVx88ZHhxecfYDEJXg4GfQl+BgtZIQZmJrjZDKKiogzi4uLYEiZ5IfDpx2+GBeceMvBzsDK4q4ozyPJzgsUff/zOcOTBG4azT98zJBjJM/AB5WEAlhiJKgcI+RxkuQ7Qp05KomCxGafugekMMyUGZSFuhn33XjPMB6rJtlBmYEEKCXyA6ER48sk7sM9hlsMAckEKkhMAqjkFVEtWSYgPXHj+ERzsyADkc3RgoyDCsPP2SwYrOWHqOuDpp+8MMnycKMGOjZ1opMDwDKiWWEBWOYAc7OhsRmDUMzEyUj8KpIG+fwL0GXKwY2PfffcVnC2pHgKgfA7KasgAFOywoIcBkBp1ER7qO8AcWMh8AJYDoKyGKzpAcg8/fAOnF2IBI3KDBNge+K+srEywIBLgZGWwlhfBKIhADowzlGNYevExgwgXO4OzsiiDKDc7ihl3794FtQcYyXIAclF8/tkHhldff4LFxICWGEoJwIviP0A1oLLg5OP3DIXWKngdQHJdALLAWl4YjHGmbKAaUDlATFlAl+p41AFEF0QxZ/8C26+3aG7pOS08IcDEwoqCt4ZogzEuMWQ5bGLofJpFweYANQbfDbdokwZABoMswGURuhgh9UMzF8B8hc03yD4mRj1OB/CzMvwjt7OKzRGY7Yj/DHwsDP9wVkZlO67Un3r/v+7TH9pEDdCDf00FGJu7PHQasTpgIABAgAEAZzJTrgcAEqEAAAAASUVORK5CYII=") -} - -.list-list .ico-mov { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAupJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnh7Ob45TAdO2iezjFYHx0MXQ9MP6vnz8pS4TIAJ9FVEsDIANhviTWAlL0DI1cAPMRKcFLrB6UNMDNxvDv579/TEBAkiXoiRAXADVQuYB2IIsxIveOJ6y7Wn/jFUPdt1+0iRqgB/+qizE0FwRpN2J1wEAAgAADAIjpSLqXWcBIAAAAAElFTkSuQmCC") -} - -.list-list .ico-mp3 { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAZBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwxElUOEAM+dpYTpY6/vJP0gohYQMhwYh1Jt1ww6oBB7QCiE+Gn/lqG/79+DZwDiLH887Q2hn+fP9ImChjZ2FBorIUOiZaTFAJ8hc2EfcPLD3YEiKa6A4gBvFlVo9lw6DmAEblZ/rwwhi5tdMn+JbgbJGyMjCh84Zo+MP22pQirGIyNTw0y+IXWD6FKFIAsQXYMiEYXoygNYDOckHpi1VIlBEAWYbOMaiGAbBg2H8GCGz3+SS4J/3Fy//v34xsTExXSBLZECGqe/gfagTMbPpjdX892/0Yd4/evNCkf/nFx//2toNGskFrYiNUBAwEAAgwArNbTCDlVe6IAAAAASUVORK5CYII=") -} - -.list-list .ico-mp4 { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvBJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnx9DQHKcEpmsX3cMqBmOjq4GpQxb79fMnbaIAZAm6Y5DZFKcBZMPRfTUoEiHIUcgOI9aRTKQGMy6DYVGALe3giwoUB3CzgdqQ/6iaJpATJshsLqAdyOoYkXvHE9Zdrb/xiqHu2y/alA9AD/5VF2NoLgjSbsTqgIEAAAEGAKRaSLN4Zv7KAAAAAElFTkSuQmCC") -} - -.list-list .ico-mpeg { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAwJJREFUeNrMV01ME0EUftS2221LscBSt0pbjBWsBjGAQRMT48FEL/5F44kYD8ajnjypMVw8cvFkSJSrxMSDMRoSUeNPDARJtKBVo0Sx0CZFC6Wldeu8laWz2y0sMoAvedmZN1/nfe/NezNQls/nYS3FTE/C4fAh8ukiKjL00UJ0gDaEQiF9Aujc6/WKDoeDiedIJIKfO0RPBoPBAT2MSTMXeZ4HSZKYKIrP53uIJAiZZiMEmAvHcef9fv8TMuzRI2FajUKzWq1nAoHAKz0S5qVuNp2R4Pn7JIyMpSGezMk2wWWGbRt52BN0goMrxORyuZQ6QDk99+0nWvZPBN5+m4F7/QnY6bfDsVY3eCossn38ZxaGRlPQ+SAKR1rcsGMT/5eYIIDH49ErzKVnAJ3fH5yEs/sFENdbVGu1VVZZm+sc0P0sLtsUEkoxlhKT0bRj5O37qoucq1qIrCEGsVNpyVBghgi8jExBo89e5LyrLwaJ6VwRCcS++JBkR2D4+ww0kXPXyqfxDNx4NAGRaFplRywWKTMCsV858Lr1U5+aleDW0zj0hQsRiwQbS+bYEVhUyHsmUY8a1t06gzsb6gLs87FEVq50rdgsJjjVVgkNXtu8Dduy0mlmRwAvGexzLYGAwMGJ3W6o0jhDbINoY3cEbVucMPQ1BT8msyr7uQNCkfMowSB279ZydgScNpN8w+EloyVBC67dJhjE4m+YHcH8zUbqrOtxDJoCdthFWk1wWea6JAuDJOo3X1JwtLVwFTMlIJOo5aGuhpMfo57XCfkxwmqvLjdDvcjDxcMbVI8RcwIo6OBgY4WsLGRV/h74rwmojuB6L6my3o8r7rQ7tEANWDkOOto3y+PL3Z/n7bRNGdMY2lbKjrbZTGZ5RajnQBnThLVryvzSzWFjNUBHqt2olCBOmwk90kwyoHdMC5FU8IYzQGdBb2O0GcnKYuRUGXBYQcpIkmm5WaEJao/ETnzQ8zL6v+POu++ujkzAldTsytwPJMDf9TXQceH49mu6BNZC/ggwAJR8VIseM6jsAAAAAElFTkSuQmCC") -} - -.list-list .ico-mpg { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvlJREFUeNrMV81PE0EUf61tt9uWYoGlbpW2GCtYDWIAgyYmxoOJXvyKxhMxHoxH/QPUGC4euXhsor1KTDwYoyERNX7EQJBEK1o1ahQLbVK0UPrl1nkr2+5ut7CVAfwlLzP75nXfb2Z+b2ZrKBaLsJYwyR8ikcgh0oSI8RRzdBMblTuCwaA2AUzu8Xh4u91OJXM0GsXmFrGTgUBgVCvGqHrmWZYFQRCoGMLr9d5HEoRMlx4C1MEwzHmfz/eIdAe1SBhXQ2gWi+WM3+9/oUXCVOvL5rICPH2XgonJDCRSBdHHOU2wbSMLewIOsDPlOTmdTkkHiNML7Qgxwz8ReP1tHu6MJGGnzwbHelzgrjeL/qmfeRj/moaBezE40u2CHZvYv8Q4Dtxut5Ywa18BTH53bAbO7ueAX29WjLU0WkTrarVD+ElC9EkkJDFWg1HvsuPM+/Y1VSRXlBAZwxiMnc0Iuiami8Dz6Cx0eG0VyUPDcUjOFSpIYOyz9yl6BN5+n4dOsu9qfJzKwvUH0xCNZRR+jEWRUiMQ/1UAj0t76dM5AW48TsBwpDxjnsTGUwV6BJYEuc8E2aWGulun8826qgDrfDKZF5WuhtVshFO9DdDusZZ8WJYNDhM9AnjIYJ2rCfg5Bk7sdkGjKhnGtvNWelvQu8UB41/S8GMmr/CfO8BVJI+RGIzdu7WOHgGH1SiecHjIqEnIgWM3SQzG4m+obUHpZCM6Cz2MQ6ffBrtIqXFO80KV5GGMzPrV5zQc7SkfxVQJiCRaWGhtZsTLaPBlUryMUO1NdSZo41m4eHiD4jKiTgCBCQ521ItGA6vyPfBfE1BswbUhorKhDyueNBxcRAMWhin1+/s2i+2l8CdNn9SvFqP2I3LZ7PJEqIaUAJNKpNRk1CRq0oB8pku9bDGC1MpQDa1t0jNWUxVoLa18rFoCPaulWAG7BYSsIBgJlqUJuQjlwA9UG8kh9xnk/44Hbr+5MjENl9O5lTkfyAR/tzVD/4Xj269qElgL/BFgADHlS7OoZ6a9AAAAAElFTkSuQmCC") -} - -.list-list .ico-rm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08aYGNnx9DQHKeEwq9ddA9FHMbHJYYMfv38SV4iRLcElwV0TwMgh8B8TY7DiA4B5Giglu+pFgWwUCDHYShRwM0GakP+o1nqB5nNBbQDWYwRuXc8Yd3V+huvGOq+/aJN+QD04F91MYbmgiDtRqwOGAgAEGAAnMw5uSNz54cAAAAASUVORK5CYII=") -} - -.list-list .ico-rmvb { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAvNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08a6EzVROHXLroHppvjlFD46GIgNrocTBwZlM++TjgRYjOIEoCsH92DTKQaBPMRusPwyYH4MExUCCAHG6W+JxQCJEcBtvgmRo6oXMDNxvCPFlkTOUQbF17/NyNDG85nRO4dT1h3tf7GK4a6b79oUz4APfhXXYyhuSBIuxGrAwYCAAQYAPSMU/fnJ99KAAAAAElFTkSuQmCC") -} - -.list-list .ico-swf { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfVJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUKp4d/3H2D4tm8fw/9fPxlEOjtR5Pj4+GDpAATCoPR5IGakigM+LVrM8PfdWwbBokIGJkFBDHlRUVEGcXFxbAmT8hD4+/Ytw69LlxhEuoC+ZsIdk7DEiAuQnQZ+Xb3KwGFpgddykgsiUsCfR48ZPq9eDY4GEOAJDmbgi4ulnwP+fvrIINLezsCmpUlRCJAdfv+//2Bg5OSgOIuS7QBQ6mfi4hoYB/z/9Yvh78tXDMzAbEZ/BwCz1ad58xg4rawozgFkOeB1UTFQFzMDX0I89dsDxADRCf1UrSfoUhmNOmDUAfgAI3Kz/KaTM13a6Or79uJukACbTxgaFGfPAtP3U9PgfBgbXR1IHKYeBpDV/gKWoiSVA8iW4bIYGyBWHRMxBqGHALIYMo1sKYiPHhJkJ0JswUqMHmJCgYmYKMDmCBgfmaa8LuDj+/vv2zdmJqRaDlsUkAvADVSgHTiz4Y2GxhrG8+cbGT5/pk35wMv777+hYb1GQ30LVgcMBAAIMACmqfNs4ifjnwAAAABJRU5ErkJggg==") -} - -.list-list .ico-wav { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwxElUOEAM+dpYTpY6/vJP0gohYQMhwYh1Jt1ww6oBB7QCiE+Gn/lqG/79+DZwDiLH887Q2hn+fP9ImChjZ2FBorIUOiZaTFAJ8hc2EfcPLD3YEiKa6A4gBvFlVo9lw6DmAEblZ/rwwhi5tdMn+JYw4Q4CNkRGOJWv7Ufi4xEnhD6008LaliEG4pg/MRqZB4jAA48PkselDVk+TEEC2nOJEiC2ekH2JzffE8kHgF9A+nInwHyc3gSYkhQ1UUKUGtANnCDyY3V/Pdv9GHeP3rzRJnP+4uP/+VtBoVkgtbMTqgIEAAAEGAH8AyCqN8wbhAAAAAElFTkSuQmCC") -} - -.list-list .ico-webm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAwxJREFUeNrMV81PE0EUf137tbQUCyx1q7TFWEE0iAEMmpgYDyZ68SsaLxKjifGof4Aaw8UjHky8NFGuEhMPxmhIRI0fMRAkwYpWDRothTYpWmgLLVvnrSzdr2JLBuoveZndt2/m/ebNezOzhlwuB+WEUf4SDAYPkSZAhKfoo53IkFzR3NysTwCdu91u3mazUfEcCoWwuUfkpN/vH9KzYVTvPMuyIAgCFUF4PJ7HSIKQaSuGAHVYLJaLXq/3GXns0yPBrEWimc3msz6f740eCWOpg83OCfDyYwLGwmmIJbKijnMYYdtGFvb47WCz5OfkcDikPECcXmwHiRhWRGD0RwoeDMZhp7cCjnU4wVVlEvWTvzIw8j0JPY8icKTdCTs2sX+JcRy4XC69xCw9Auj84fA0nNvPAb/epPhWX2MWpa3BBr0vYqJOIiElYyEwxYYdZ961r1bjXFFC5BvaoO1MWihqYkUReB2agRZPhcZ5YCAK8dmshgTavvqUoEfgw88UtJJ1V+PL5BzcejIFoUhaoUdbTFJqBKK/s+B26oc+OS/AnecxGAjmZ8wT22giS4/AP0HOM0F2qGHerSty5KKqAOs8HM+Ima6G1cTAqc5qaHJbl3RYltV2Iz0CuMlgnasJ+DgLnNjthBqVM7Rt4q30lqBzix1GviVhYjqj0F84wGmcR4gN2u7dWkmPgN3KiDscbjJqEnLgt7vEBm2xD7UlWNrZSJ4Fnkah1VcBu0ipcQ7TYpVkYJjM+t14Eo525LdiqgREEvUsNNRZxMOo721cPIww22srjdDIs3D58AbFYUSdAAIdHGypEoUG1uQ+8F8TUCzBjX6SZf2fV91pb/5SDAb5f8FESshd6f2q6dDdtRlQj60chXRSH/m7XMezjGHFSahHUO1Ez6bwOUIiIMmZm6O5cHIhd/52SNNKz5Lo6dT6Qq3cZ9kjwOgNJl/zksJZgPBy4ygiYDODMCcIy5amPOnUCVcoQvl7gvaeqKiCnvvvr41NwdXk/OrsD2SCC4110H3p+PbrugTKgT8CDACPHLU9lTMolQAAAABJRU5ErkJggg==") -} - -.list-list .ico-wma { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAeFJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgSmX93C8ODzC6IsVOCVYMjU9oHzf//7w3DgySUGVxkj4soBbABkeadFClEOKD8xB85+9f0Dw9LbexlefHuP1wE0SYRnXt9imAEMOWsJbYJqaeKA1XcPMaRpeTOYiWkMjANAQIJLkCh1dCkHBsQBz76+HTgHhCrbMcy+vo3h3Js7pFXHVKt/RdUYZLhFGZbd3ke5A0CFC3L+xq9WHCUR5ur6Mxx4dokyByCXbKQCViYWvIXQ8M4Fow4gq1U84cUOBoYXtLd0FlKrGCMEuJjZwbjFKQ3ORsYwcRCNrgaXGDJ/6KSBtoMLGars48FsZBokjksNshyyGEzNgCRCfJYTLAlhrkem8anB5ntcfKwhwMXE9u/v/380Cw2Q2SA7kMUYkXvHU0+sqb/940Xdt3+/aBI13Ezsf1U4xJuzLUIasTpgIABAgAEAkufcCgWFyTcAAAAASUVORK5CYII=") -} - -.list-list .ico-wmv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAt9JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLKQa9vXnP4ajNz8z3Hj2g+HN5z9gMVE+FgZNaU4GS1UeBm52hJ/4+Phg6QAEIqD0GSBmJMsBV558Z9h45j2DvjwXQ6CpIIM4PytY/OXH3wwXH31jmLD9BYO/iSCDjgwnxGGiogzi4uLYEibpIQCyfOv5DwxJDqIMkgKsKHKywmxgbKzIzbDo8BuwGMwRsMSICzARG+wgn8fZimBYjpKFgHIgNSC1X378I8pjRDng+O0vDHpyXBiWzz3wmuH91z8YjgCpPXbrM/UccP3pdwYDYLyjg7svfzJM3fWK4faLHyjiILWgREo1B7z+9IdBShB70H/79Y9hwaE3DAeuIXwsCVT7+vMf6jmAIADWZ/+QKjVQumMm0mSicgEonz97/xuc0tEBBysTQ5iFEIOGFAdcDJQthXhYqOcAUCEDyufoDlAQZWcINhNkEEazDKRWQ5KDelFgocLDcPHhN4bnH36jiKc5iWJY/gKoBqTWSo2Xeg7g4WACl3CgQgbdEcgAJLcQqAakFqSHalEAL9mA6Wzu/tcMBgpcDIbArCbKxwrNJb8ZzgN9feHBN4YAU0RRTFUHgB0hy8mgKMYOrozWnHoProxAqV2El4VBXZKTodBLAqUyoroDQABkgZsePxhTA9ClPTCoHYASBR17gKlszx2aW7pIC08IsLGzg3FnqiacjYxh4iAaXQ2yGDY5ED100kDtonsMzXFKYDYyDRLHpYZYuaGTC2C+QKbxqSFFDqsDuNlAbch/NPMtyGwuoB3IYozIveMJ667W33jFUPftF22iBujBv+piDM0FQdqNWB0wEAAgwAD570Ssrd8AngAAAABJRU5ErkJggg==") -} - -.list-list .ico-doc, -.list-list .ico-docm, -.list-list .ico-dotx, -.list-list .ico-dotm, -.list-list .ico-dot, -.list-list .ico-rtf { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAArdJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZ6eIAAQEBsCMYGRnh+OHDh/SLAlZWVgZmZmbiywFcYPnW+ww/f/1lSAhUQRFfsP4OmIaJP3j6hWHGiptgtoQIJ0NBvBYDCwsLw9+/f3GaTVQI6KkLMtx99Jnh959/cDEQ+97jz2AMExcWYGcIcpVn4OFiYdDXEIKrBYUCExMTOArIcoC6Ah/QAEawI2AAxJYU5QJjmDgvNyuDGlDt1+9/gA4QRDEDlgbIcgALCxODhhI/w417H+FiILamMj8YI4tfuf2eQVaSm0GInx3DHLIdAAK6aoIM1+5+YIBVXTfuf2TQVhFg0FIWQBG/fOsDgwFS8JNUGeGNBkU+hh8//zI8e/mNAeQRFmZGBlEhDkhqB4YQSBwUBU9efGWI9VeivgNYYdFwHxLcmkCfwwA4GoDinOzMDEqyvMBEyEq0A0gqB0C5ARTfIKyF4gABsNgVUPBrCpFUVpDkAHUFfoZXb38wvPvwk0FeGlFlK0jzMLx9/5Ph8cuvDDqqAuQ3SAiXbEzgbMYGpJmQUjQzMIuqAdPInz//GdjZmGnnABCI9sWewCK8FMkqrulSHY86gOg0MGHJIyD5iOaWzmrD3SoGNZ9Q+JU5Dij89ikHsMoRIw4Cv379Ij0XoBsO4sNoYsUHLA3gs3xk5AL0NIQ3F3BxMP/79ecfE6j5hMsQWJDC4ptYcRAA9ZZAdqA0UpB7x1MXHKm//eh73bcff2kSMtyczH9VZDmbsxNsGrE6YCAAQIABAHOPGcK/91HrAAAAAElFTkSuQmCC") -} - -.list-list .ico-docx { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAphJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGakiwMEBAQYQAmakZERjEHg8ePH9IsCVlZWBhYWhB9hjiA5ClZsu8/w4+dfhoRAFRTxBevvgGmY+IOnXxhmrLgJyU4inAwF8VpgB4AsBuUKbA4gKgR0VQUZ7j76zPD7zz+4GIh97/FnMIaJCwuwMwS5yjPwcrMy6GkIwtUyMzODMdkOUFPkA9MgR8AAiC0lxgXGMHGQxWoKfAyfv/5mMNAQQrWIiQmMyXIAKwsTg7aqAMONex/hYiC2hhI/GCOLX779nkFOiptBiJ8dwxyyQwAWDTfuIzngPpIDkMSv3v6A4Xt8gGgHgKLh67c/DC/efAdjZiZGcEIDYRAAiX349Ivh8YuvDLpqguTVhqREA7IlWsoQcQ52ZgYlGV5wWqC6A0BAW0WA4ei5V2C2h600XFxPXZBhx+GnYLaprghJZQVJBREovp+9+sbw+t0PcEKDARAbJAaSAzmS7AYJMdEAcgQTMP6ZkFI0iK0KzH6g8gAUDTRzAAhE+ShhFY/wUiSruKZLdTzqAKLTwIQlj4DkI5pbOqsNd6sY1HxiqMxxQBFrn3IAzkaWIyQOEkNX09i3i7hcgK4RxMdmID5xZDl0NTRNA8gW4bN8+OYC5PSAHAXYACNy57Swefefn78ZmavznGiWCHum7/3bX+vKgtUBUxcerbn98Fvjtx9/aRIyXBzM/1Tlueqz461bsDpgIABAgAEAbXE38hMP+G4AAAAASUVORK5CYII=") -} - -.list-list .ico-pdf, -.list-list .ico-fdf { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmhJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7CQa+jf9x8ZGFlZGJh4cKcXPj4+WDoAgQgofQaIGSl2wJupCxk4tNUYuG1MGVhEhbGqERAQADuCkZERjh8+fMhAeQgAExgoBPgD3IF+YcSpjJWVleHPnz/wBEm9RMjExMAiLsLw/fwVhj8v3+AvaFhYGJiZmYFamMAhQLVEyOfryvBu/mpgGuAiqJYmDuDQVGFgFhJg+H72MlHqYWmAag748+oNOBF+OXya4ffjZ0Q7gmoO+LL/OAOPszWDcGokw7t5qxh+P3lOeWVEdCb4+g2cC76fvsjw6/5jhv/AlP5m2iKGvx8+M3CZGwALib8MrDKSDLxudtR3wO/nrxjeL14HLoQYOdgZ+IBZkVVCFJwdf964y/Dz3kMwn0VCjPoh8PXIaYaPG3cx8Hk7MfA4WGKW+xrKYExSjiZW4ffzVxl+3rzLwGWix8Bjb0G1eoJoB/x5847h/6/fDHxeTnhLP5olQl5XWwYGEKYyoEt1POoAotMAS/tMhof0sHDxRNyJEFuqVGorgbPvVfVgiBEjDs9N5OYCZAtgbGTDcYkPSBoAOQY9JCguB2AGEuM7UkKA5CigaS5g4Ob89//rdyZGEkOFWAeCu8FcnCgtVEbk3vGN/ln1TDfu1TF8+06b8oGb6+8/dcVmjcK0RqwOGAgAEGAASrzyfwJnztEAAAAASUVORK5CYII=") -} - -.list-list .ico-ppt, -.list-list .ico-pptm, -.list-list .ico-pot, -.list-list .ico-potm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAdtJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGYkywHvJ0VhCjKzMLCpmDNwmAYwMAtJo0iJiooyiIuLY0uY5IUAb3Atw+e1zQxczmkMLKLyYLH/v74x/Lp1nOHzimoG3pB6BmYxRRQ9sMRIVDlACDBDLWUWlESxiEVGm4GJT4zh+/FVDDz+5SRFE9USIbuOE8OfZzdJ1kfdXMDINHAO+HXzGDyKyK4LiAV/3z9nYGRhgyTCP78Yfj84z/Dj/DYGHt9S+jjg295ZiFBn42JgkdVm4PEpZmCV06WPA0DZjUVKnSpRR5eieNQBVEsDoAQnmLdsNAqGlwMYkZvlDws06dJGl59wHU+DhIUdU0PVZoQj23yxiiHz0dWigD8/ycsFyBbD2NjEkNnDqxyABTGy77CJ0cwB2CyhxGKsUfCfS+Avw/9/tAtvoNlgO3Blwzuzi2pY7h1rZPz+kSZp4z8n/78/Slb1Kql9LVgdMBAAIMAARLzKcEnwqqcAAAAASUVORK5CYII=") -} - -.list-list .ico-pptx { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfNJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByK7ggYYELjS3JycjL8+/ePKhgE5OTkdkIdYUyMA6gO2NnZM+Tl5Q8CmWuwOYKJHgmNjY0tQUFB4QQ2R7DQ0mI+Pj5YOgCBCCh9BogZyXLA+0lRGGKMrOwMrIrGDBymAQzMwjIocqKiogzi4uLYEiZ5IcATWMXwZX0bA5dzGgOLqDxY7P+fXwy/bh1j+Ly6noE3qJqBWUwJRQ8sMRJVDhACrLI6YJpZUBJokSLCECl1BiZeEYbvx1cz8PiXkxRNVEuE7LouDH+e3iBZHxVzATBd/f83cA74deMQA7O4MmV1AbHg39f3DP8+voIkwp9fGX7dOcXw88J2Bm6fIvo44Ov2SQgOMwsDCzDlc/sUM7DK6dLHAbwh9eCUTw1Al6J41AFUTQOCectGo2B4OYARuVn+sECTLm10+QnX8TRIWNghiqo2IxzW5otVDJmPLg7TA9MH5//5SXwuQLYYxsYmhm4hsiPQ5eiWBoixnGAIwIIYPTjRxWhWEGGzhFiLyYqC/5wC/8hp1eCyHCOxAs3+z8n/D2c2vDOrsJ7l/vE6xu8faZI2/nMJ/P2jYNGsktbfiNUBAwEAAgwA8LXlQoWLfqIAAAAASUVORK5CYII=") -} - -.list-list .ico-txt { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAVJJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkLQSevnqLU05aTBivXlhiJKocINcSSgATwwADiqOA0pAbjYLRKBiNgtEoGHXA4MoFE+fsooulM/twt4pBzScMDY3VmRhi9a3TweIgGp86mFoY+PXrF4ocI3LHJL1own9sDkC2ANkwZDFsjkFXC3PAzL4CRqqlAWyWj6xcgBwFFDuAi5PtH6FGJK40QYwjQGaD7MCZCKfOWVt/5/7Lum/ff9Ekarg42f+qKIo1Z6cEN2J1wEAAgAADALEDt/dBJDrPAAAAAElFTkSuQmCC") -} - -.list-list .ico-xls, -.list-list .ico-csv, -.list-list .ico-xlsm, -.list-list .ico-xlsb { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAiVJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDkV3BAwwofEluLm5Gf79+0cVDALy8vK7oI4wIMYBVAdsbGxZQEccweUIJnokNKAjEhQUFM5hcwQLLS3m4+ODpQMQCIPS54GYkS4OEBAQYAAlaEZGRjAGgcePH9MvClhZWRlYWBB+hDmCLAeceHmdIWZPO5gmpObYi6uIOAY6AISRQ4GsNGAhrgmmp1zegMJHtnzSpfUMGdo+DFYS2ihyzMzMYMuxlbokpQFcjoBZnqLpyWAnpYdVLxMTE+UOwOYIEABZHqfuyuAkY4hXL0VRgMsR/4C+ilJ1YvCQMyUroVKUC/5Bg1SEk586tSEpOQIU7AGK1gxyvGI4EyZNHACz3EfegiFMxR4uTq4jWMixXISDn0FPRInoLEoVB8As//PvL8OLb+8Y9j05z6AjpECxIxiR8yawRfRfWVmZpjXj3bt3QS0iRrpWxzTLhsPCASiJcPKr3QwMIExjMB2pVYwRAuyMLCi42DYKKx9dHCYGw+hyMExyFEw5soohxwbSmgLRID42AJODYZgeqqQBmIG4LEd3KIxPs7qAmo4gKgRgvscXrDA5WBSQlQu4mNj+/v7/l5kJ0WpGCXr0qEC3lFC8/2P4D7YDZ1E8/eS6mts/XzZ+//eLJuUDJxPbP1V28fpM86AWrA4YCAAQYADJhRMxzVK59wAAAABJRU5ErkJggg==") -} - -.list-list .ico-xlsx { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjlJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDlVVVb2ATQ0TGl+Cm5ub4d+/f1TBICAvL78L5AigYwyIcQDVARsbWxbQEUdwOYKJHgkN6IgEBQWFc9gcwUJLi/n4+GDpAATCoPR5IGakiwMEBAQYQAmakZERjEHg8ePH9IsCVlZWBhYWhB9hjiDLASdeXmeI2dMOpgmpOfbiKiKOgQ4AYeRQICsNWIhrgukplzeg8JEtn3RpPUOGtg+DlYQ2ihwzMzPYcmylLklpAJcjYJanaHoy2EnpYdXLxMREuQOwOQIEQJbHqbsyOMkY4tVLURTgcsQ/oK+iVJ0YPORMyUqoFOWCf9AgFeHkp05tSEqOAAV7gKI1gxyvGM6ESRMHwCz3kbdgCFOxh4uT6wgWciwX4eBn0BNRIjqLUsUBMMv//PvL8OLbO4Z9T84z6AgpUOwIRuS8CWwR/VdWVqZpzXj37l1Qi4iRrtUxzbLhsHAASiKc/Go3AwMI0xhMR2oVY4QAOyMLGBfbRsHZyHx0cZgYDOPTC8JER8GUI6sYcmwgrSgQDeJjAzA5GIbxidFLMA0gG0iMQ2F8YvVSrU2IyxEU5wL04MSlBmYpssXE6MUoCYsPTf/z+/9fZiZgqxk9+LAZCJPHZgk2vf8Y/jOwMjL/7bXLZMHqgOkn19Xc/vmy8fu/XzQpHziZ2P6psovXZ5oHtWB1wEAAgAADABRqMy9QT3WBAAAAAElFTkSuQmCC") -} - -.list-list .ico-7z { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAX9JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7bo012zDEHrZ4YYiDxHDX7z/JLweQDQZZCuOjiw9YGkB2FE0dgM0iciynWgiQazlGGvjPKfCP8ddXJgZGJpISIro4Tsf8/we0g/8fTgf8UbRoYrl/vI7x+0ecLnjY4EyUGFb7uQT+/lGwaMZZDgwEAAgwANHIuc9Vl0AAAAAAAElFTkSuQmCC") -} - -.list-list .ico-cab { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYxJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7quKabQgHtnhhyCGLIavFph5Sv/8kPhFiswCroTgsJUY9SQURNsPxhQIhyykuCdGDnJwQYCLHQpjBMIzNIWSVhP85Bf4x/vrKxMDIBPcNqUGKV/3/f0A7+P/hzIZ3ZhXWs9w/Xsf4/SNNyof/XAJ//yhYNKuk9TdidcBAAIAAAwBVN9K79PuTHAAAAABJRU5ErkJggg==") -} - -.list-list .ico-iso { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7bo012xgetnjB2WBHQ/nIYujiqPX7T8rLAXSHgNjIYuhqqJ4GYBbi9SmtEyGyI+juAGTfU+oIlDTwn1PgH+Ovr0wMjEwkRwG6Y7BGzf9/QDv4/+HMhndmFdaz3D9ex/j9I03Kh/9cAn//KFg0q6T1N2J1wEAAgAADALuKyAid7COMAAAAAElFTkSuQmCC") -} - -.list-list .ico-rar { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAX5JREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7quKabSj8hy1eGPLIYoTUM/z5SXo5gG4BhqEUqKcoDcAMR/c11UtCZAuwRQEp6slyALYgxRb3MD7ZUfCfU+Afw/9/tEv+QLP/c/L/w5kN78wqrGe5f7yO8ftHmpQP/7kE/v5RsGhWSetvxOqAgQAAAQYAQvfLEuMZrKwAAAAASUVORK5CYII=") -} - -.list-list .ico-zip { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYFJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8IkPgQ+r2lk+PPsJnl5XEqdgTeknvhyABsAWS6Ytwyr3PtJUTjlYPKEAEWJEGQ5MZbQzAGEQoDmDhgNAWITGk0dQCkYcAewUJoIh38IgIpTchMai6Qa5Q4gVJYP+SgYcAcwIjfLHxZo0qWNLj/hOp4GCQs7bo0121D4D1u8wGIwGlkcd/3+k/xyANlgdMcgy8McRbM0QIoFVC8J8VkOCxVSHMdCTZ+TEyooDvjPKfCP8ddXJgZGJqISIskW/v8HtIP/H85seGdWYT3L/eN1jN8/0qR8+M8l8PePgkWzSlp/I1YHDAQACDAAtKS/DHmsv9AAAAAASUVORK5CYII=") -} - -.list-list .ico-gz { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAHtSURBVFiF7Ze/T8JAFMe/1QYCRKIDIe1AYcAfLJLowL/grCbGydEJTXBw0qiLA4NxNGFlkX/A0UlJJGEBQzoAC3EzxhgTgz0HPXI9jrahxTr4TZrm3r3r+/Tdu2tPIoTAT8lso9lsrgEoAVA8jLEKoMYaMpmMGABASVVVJRKJeBJZ13UAuAawmU6nayKfKa6thEIhGIbhyQUAiUTiBsC1rusrTgA8VzAY3NU07RZARQQxcQAACAQCO8lk8l4EwdeAp4pGo7QOAGDr5/4AQPoVgFgshng8brIxQM4AXisn6PdaYwHI6gJmNo6tfewe0u+1MJcvC/ueL7dH9tF+O7kqwrl82VGQiQHYZWDiAP8ZoBC+AriV7wCudkK38w/8lQxI0ve3odFoDDuoC2MXmqzMC+003gDASnZ7uVv5PgW+A5imIHK1jicA2sUjuvtLEwk49LvLngs6e4uEVaewTHjxtk5h2XTZ+u4tmg4iQ0XYPchCK9bRPcgO2lRasW7y48WOo758m5ejjWjUYKdiwXjZFiEdPOoBboJbArBvrRXrlllg0y3ytRovEUIGO1P7MPcpfbxNQRpvdfI1MPTmxAAJhI3UeXWamkw10E/lTuX23ZH0/iImkIOWADQDAITLmIRnP/vJ3BlQZYw+H8+/AFShLMorUUd8AAAAAElFTkSuQmCC") -} - -.list-list .ico-bt { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAbxJREFUeNpi/P//P8NAAhZkzrVr1+yBVBcQm1HRDkMgvoAsoKWlhd0BQLBCAgh4eXmpYvPt27dB1GogDkV3BAwwofEluLm5Gf79+0cVDALy8vK7oI4wIMYBVAdsbGxZQEccweUIJnokNKAjEhQUFM5hcwQLLS3m4+ODpQMQCIPS54GYkS4OEBUVZRAXF8eWMOkTAiAAS4xElQO4QPmJORhivKycDO6yJgymYupY5WGg0yKFcgfgMqjm1AKwA5DlQI4hZCnVcsHvf38ojiK6ZEOqRAG+eKaLA7DFKzUcNeBRQJEDWJlY6BMFzIxMWIPbSkKLYgcwIreIgA2S/8rKyjQN8rt374IaJIzDIw2MOoDqBdGEFzsYGF7Q3tJZeFrFDFzM7DS1/Nvfn7iz4cPPL/8zQWOl99wqhmKjMLgcMh+dDQLIatHFkdUD28sM8rzi2LNh6+mlKBaAaBjGZTm6xTCAy0EwO8hOhDCHobNhlsD4+BxHlANgBsAwNoPRLYGpRfc10UVx4YGpf/4w/GMGlf20AH///wOmeqa//Q7ZLFhzgQqHeMPtHy8agSmVJi7gYmL7B7IDZwgMBAAIMABhJOYNvD7xMgAAAABJRU5ErkJggg==") -} - -.list-list .ico-file { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAOVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gJMDAMMRh0w6oBRB4w6YNQBow4YdcCoA0YdMOgc8Pzbt280swxq9gt8reKU58+fgzqnEjRyw1MgTkMWYBzo7jlAgAEAzk5sMbucHicAAAAASUVORK5CYII=") -} - -.list-list .ico-svg { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAOVJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOYKJHQmNjY0tQUFA4gc0RLLS0mI+PD5YOQCACSp8BYka6OEBUVJRBXFwcW8KkTwiAACwx4gJMDAMMRh0w6oBRB4w6YNQBow4YdcCoA0YdMOgc8Pzbt280swxq9gt8reKU58+fgzqnEjRyw1MgTkMWYBzo7jlAgAEAzk5sMbucHicAAAAASUVORK5CYII=") -} - -.list-list .ico-apk { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAhlJREFUeNpi/P//P8NAAhZkzrVr1zyB1FwglqSiHSZAfBZZQEtLC7sDQJZLSUlJcnNzU8Xm27dvg6jVQByqqqp6FpsaJjS+JCcnJ8O/f/+ogkFATk5uJ8gRQMcYE+MAqgN2dvYMeXn5g0DmGmyOINkBa68sYfj55yeG+JEHexnOPD2OVQ8bG1uCgoLCCWyOYCHVARK8Ugzbb61n+PrrM8Pzz08g8cYrw/Dq6wuGZONcFLV8fHywdAACEVD6DBAzku2AH39+MFx7dRFF7OGHe2D67LMTDI5KHnBxUVFRBnFxcWwJk7wouP32OsPJx4fB7FjDdLg4jH380UGwGmSALWGS7YDTT47C2YvPz8TKPvX4CPkFESEQpZ9C25IQG+g90gimi23qGdoOVBJlaJVDO4o+ihzwE5joyAHE6qN5QURxFLCzcJBXAhKpj6ADCMUhpfoGPApIcoAsvwJBNTL88rQrB2AlHiw7grIbNj7NQmBAcgExKZzcnAICjMiNUmCb8L+ysjJNfXz37l1Qm5BxcEbBogeTGRge0N7SDq3puBMhCysLTlzm1YjBh2FsatDVgzDVc0Hf7mYwLnKtRREH8UHiVMuGMAPRLcKnlurZEJvlMDFiLSTLAeg+QuZjsxgWUiRHAQcz1z9qdVaxRRfIbA5mzn84C6L5h6fVP/x2p+7H3+80KR84mbn+ynEpNyfaZjVidcBAAIAAAwAW1gmCSUCdXAAAAABJRU5ErkJggg==") -} - -.fileList .ico-bookfolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmJJREFUeNrs3E9r1EAYx/HJ/3S3mwTb7a4u7kkPguLBky9A8OrVk9A3ISiK4mvwpNCLHj37GjwJ4smjFNZuQTbrYXe72/hM2lrBS6LuhITvDx7SlsIkn5lkMp22VpZlivx9LAABBBBAAAmAADYEMP3woK7Xsiv1ylBb+1J7Us/dhgyEW1Iv25euKcdvrb2xbHU0mE9GjxbpgdMEwK7Uu43toe94cjnZYv23ra1UEO8oAdy1a47nSL0JkotDrx2bffbZVt55dQd84UfdO0HcrewE6gx4z20lD4O4p59KlZ2EK7OuL8cnUnr6HdRFT/CU39k6u5WqOw+pZ360Iz3ZV5bj1WgAHsvAW1bfkfr9Sc8olp0ZmcGaFv0M7FZ9G9QdkAAIIIAAEgABBBBAAiCAAAJICsfIptJyPlU/vn2SY2rmooJIbfZuyLHTjBFoEu+kw9K8zUaNQB09KsJooLLjlZqOPv7xfZbtqk7/5q/PD7+8z4/bV+8WbmuW7hvtMEPPwJNNH4237phog0kEQAABJAA2A9DbuKAcr81KpHys/JXD3+zJW06mZpOv6mj2HcBCdJalWltX8iXX6RdUmAyVO4u4hYsJ2ud4v/dmmADIJEIABBBAAAmAAAII4H9c9+ro9e66c96Gmd/7NrIW1tuL+U7Zwee8yuZsc6lUm2HcnBGY79GG5v6WTbfV6V1v1ghMLt/mGUgABBBAAAmAAAIIIAEQQAABJBpwnK2WSJTMqdlYA76eT8YKxHJ42kyyp38e+HQxPfSl7svHfXgKZST1Vuox/8HyHwMggAACCCABsKr8FGAAiCO50cIM93UAAAAASUVORK5CYII=") -} - -.fileList .ico-folder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAY1JREFUeNrs3E9Kw0AUx/FM/kwwxSagqdVC9649gwdw60rIJQRFUTyDK4VudOlBvIdQjJvWVWPr9I3o3gaaNOH7g0eyCUM+mcybbKKMMQ4pHxcCAAEEEEACIIDNjF/mounr2SbeSyb1UNFYb1IjqVu/JRPhSOq+c3DoeDpa+2Bm8TWYTcYXxfTdawNgKvWytTvUXiC3Y4q1D6hk4QvjniOAWdPXQE/qKUz2h0EnrnRg5aqfh9d0wDvdTY/DOKULl8iJHyXnYbxnV6X6urB0VC3HKynbWgeN2T5EiaO3d/5epVq3MTe625Mn2XeUFzRoAn7LxJtvxD4wsx1FuaaSDtbGL5G07teATzkACYAAAgggARBAAAEkAAIIIIAEQAABBJAACCCAABIAAQQQQAIggAACSAAEEEAACYAAAgggARBAAAEEkAAIIIAAEgABBBBAAmD1gLlZzJFYMb9muQV8nE1yB8TV8KyZZGT/G3NdfH5oqVM578Pzr4ylnqUuFT/ipokACCCABEAAm5mlAAMAvOdJo4EAKcIAAAAASUVORK5CYII=") -} - -.fileList .ico-folder-empty { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABBJJREFUeNrs3MFPE3kUB/D3m+lMp62kpQgEiXDw5CaYuGsUXTxtOOllr3oxcvI/0JiYaEzWv0AvHrzgcU941kQPxuxmEzl42gNshAXRpSC0nbbz871fmdpGBdoK7LTfl/zSMm0ymU/f7/febyZBaa0J0XpYIAAgAAEIQAQAARjNiIVvVv+8GvVrmeLxcJ/O9ZbHIx53Yh2SCD/xuJ/M/kCWk9zzk+nAH/Y3lm6WNpfsTgDs5/F7vGfUteRyAn/PT6h4uMkBYsCpqK+BNo9pNzU04njpfT2xUsr8eFEHvOskBiYlG1CFm49fY/He625qUFalg6vCXH1dfr3F4wqP4ci0D/FechJ94VQ60DbmtpMcNL+kstzo5J8OeJT/F33glKwh5nfchwrWiTuR/oOeBtjKARABQABGM2LShAYrT8ny50jpva/CJTVEvnU00mjKdiiWyG71o+V/yA4WvvnlfKHaayW89u87qMxJ8vrGyYt41ulKiYq5f0kFmmJOsMz9X3GrKW3cEq3nFanBS9X3S4+pJ9H6lkn1niIr+yOfIvq9puKFL54eIH9tmWIq2OCLKtU+rHCDb1uf8dInfjPHc685G1emKeG2iHf4545a+5SlttbAuu2QoPn2CKniPLnDVbzc6xvms3j/BP03/5jKFaJDnqbd9t6diPdFEZEQmLI3RgPnZ6iUmyUnPUbrb+6R5aTJTjEqvw5dnDPH8gv3KRkHXkMbE7N5FGbp498PDJ5knpMZo+ToJYMoqMWVF7TJWbibaayypzseL7yZ0FAwDh27ZrJMc9HYXKxOWW2nKTs+XRUPclTgJTNfVJSI669iGry+s93TSNcXDMk8ybqgZ4JcRzfgfXh52aBtlDKUOTdDBeeEAe5WPAMYrn2CJ5knU9ROjpjpKlCCJ1NY8FLOau2Yqdib86ZidyueARQAgZA1zjtygeKHJ8zfghbiCag3dKEBLwQNq3E34pnrLj8/qvPc2wpOz/HrVNngFoZbFoELoQRPMlRgg1Kuhheufyp7hvHGuw5vbW62ugYKRMZbpcKbauWVKlyfZYXFJ8DbqQpLG+O5mnu8J7QmVZix6tc84O0AaHYbjuTlDKX4qOxYv7bmAW8bwBoiVfu87DngNQ0YhkznAk9naWvq8QROABE7AAqY7Hn7klRrVYDXBKCg1d8wAN4ONxO2/RLwms/Az3hnzS4D0UIGAq8NQOC1AQi8NgCB1wYg8NqowvL8Qh4CIVrIQOC1Aejbx4DXDmDJHoXCXm/lEAAEIAABiAAgAAEIQAQAAQhAACIACEAAAhABwO8CGOhKGRJNxpZZYGmlXhVz7wiIzeEZM6X+kOfCk8WP75/56ysnMaV3HQHj/cWvvyj8I24UEQACEIAIAAIwmvFJgAEAn5Nwqgtf3PMAAAAASUVORK5CYII=") -} - -.fileList .ico-folder-unempty { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlFJREFUeNrs3M1qE1EYxvH3zNcx09ovaUot6FIUNyJ4A+IFeAfRXINQFIS60IV7Vy7cuPUaXEtEkG68ANEYjaZNE+YjM56ZhBIxlIykMVP+D7zMJnCY35wz7zmBRKVpKuTfY0EAIIAAAkgABLCccYp8+Nf7+4t8L3VTL+c01mdTr0w9cc7IRLhp6oW/cU0s1z/1wdIk3AmPmo+iXtM+C4Cbpt7o85c9K7udJDz1AZUpz6+KAayX/R1om3rtLW1fcs+tznVgpVT+8FSRs7BWi+XdbNSeuZXqrl7ezhbW3Mfvtj6WtwsbvLuOXt/1lrb+C97xTPzZuOeZ62NTNVM7pdk+6HVxKxfEnkPTOGkGZk1kz/W38iepLK88UzBNTMULsQ+sZx0lfyWOOlgQxhJFA3bJY3FdW7TnTATcHHWU42R4K1ceoDaWg0/PJwJylJvlUa7XDyUIYjnqBTJot9EZy2Gnl69MrR3xK95kwAxv7fpDWcPrr6zc2ht+H7D/9A9AlvAslnDU/SJxvy16EEm/UUPlpNOYqX7LFaeyMQRUSWr2f6ksX7wqynYRmmYLaiZa0PkqmZ1jDsOiV6s5oqQhOtMc38yLLzMLD74N34HKUqgURlQ0kVkEQAABBBBAAiCAAAJIAAQQQAAJgAACCCABEEAAASQAAggggARAAAEEkAAIIIAAEgABBBBAAiCAAAJIAARwIQCTdBAjUTAjs8RKlXoXdFoCYjG83EypRvaL9TtB98fb8PD7DZb01EkM3gdzva34I26aCIAAAkgABLCc+S3AAB9MnrCxlmzHAAAAAElFTkSuQmCC") -} - -.fileList .ico-fromchromefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABCVJREFUeNrs3F1oW2UcBvDn5KQnbdYmTfPRLMnafdCLUcfGIhM3O2WgOPFiu/FCZBY6BooyvJAJiqNTL4ao6IV4oVJERbwYKoKiBTdlaLeVIV3Z/KhzW9O0OUuWpGnaJWnP3vd01LUGbba2W9LngT9Jc5JzyC/vef9vT0MVwzDA3HwsJCAgAQlIQIaABCzPWG/8IX2ivZzfS4eo95foWBFRXaIOWStkIIRFvbsisB6qZl/0gxmT+eDV1PCLuXRMrQRAr6gjNZ4mTa0Sb8fILfoBFTHx2Zw+CMCOcp8DVVGf2OpXNlWtcC7pgRWLYn545Q74qubwPmhzeu+MJlIs7ZEDi3LgruDhW93Fbqu9/gWbs1HOSrcNUEn1PKmJ25ellajgUkAuAB4EHrQ6N6zV9tuGl77QZ47ATs3hE5+kH4pa9d+Ne4HiWB1egL1MiYFXuCPWgR2yoygWY0k62PQ6IIdKiWwi3usdheGvcgQkIAEZAhKQgMsnC3I5q8VVgz13+dDqseO3xDi+P38FRy+lMGVwBP5v1jir8UpbM+Ra/KMzMRNw36aVaN/QyBE4n4QcGk5GR/HWqcjMiOu/nMWBe0L4OTKKs/EsAYtlVZ0N69016NOz+OlSetY2CZoYz+O17c0YyebxzqmhioUsGVCeqk9vDmBH0z9XgLsvJPHe6eisOa/z+EXUV1vxyFoXOu9rwv7uvxAdy1UcYMlz4KPrGrA1UIc3Tkbw2Jfn8HbvELaHnNi70T/reZFMzjyVXz8REaN0DE+0eityBJYMuFMAHvk9juODaRTEkDt2MSWQBvHQahfuDTqKvkZ25I2+WgLKuMVpeT41Meux3uEMvvgjjqc2+eGx//uibCY3BU1VCCgTnyiY674bU6up+OysDl00jufuDmDu5cWwvxYDyQkCynwzkMCuFjfuF02kUYy2h9e48OHOFrMrvynmO7kufFY0mWrr9K7ldlnfisU1u7DI1wKwoaYK+8OBmcc+7o/NnNaHewbx/JYQtoUc5hypqRZ8fk4350oCisilSlffCL76Mw6vgJTdNpObnNn+a2wMz3QPYIs4bSXe6ZGM+RwupOckMV4wq1iSYp787u8kr8YwBCQgAQnIELAcAO1qAds8UTRoE8sK8JauSFsUA2FXDA94I+JWh2aZXlD3pxvwQyyEX+J+ZCetBJybVkcCbd4hbHVHUWvNF90ua9/afvQkGvGjHkDvFd/yBlxlz6DNM4QdvsF5n6ZyRMrXyErmbTgaC+KYHqwoQPkNVcPRvIHd4CYiv6HKLsxlDAEJSECGgAQkIAEZAhKQgARkCEhAAhKQISABCUhAZl6AujFZoESJuW6mS8APrqZ0ELE0PGkm0iX/sH4wN3pZE/W4uO8nz7wyLOpTUS8p/EfcbCIEJCABGQISsDxzTYABAF5pTWwmgxlgAAAAAElFTkSuQmCC") -} - -.fileList .ico-documentfolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlZJREFUeNrsnE1rE1EUhu98ZCZOTSbYpB9GKgguBHelP8BFodtuuxLyJwqKovQ3uFLoRpeu/Q2CgktxJdgPTChNQtskTTI9N1hw4cKkk5nMzfPCyc3qXu4z57znTi7EiqJIocllgwCAAAQgABEAAZhNufqj9fmpCXupSbxNaK0DiX2J164hibAu8Wbh7iPleMHUF4sGl9Vu8/hZr/XbMQFgReLjrfKa5+RkO1Fv6gtaYnx+uKQEYC3rHuhIvPdLq2u5hTDRhS3bGj28rAPc84qVTT+s0IUn0LYblHb9cFm7UnpdWDqwJ+MLCd2Kq5k5PgQl5RUWr0sp1WPMK6+4JE9yRVlOLkMJOJTE68/EObCmO4plR4l0MBPfRCpplwGvcgBEAAQgAAGIAAhAAAIQARCAAJwbJXqpdHlxos4a31W/04zn6bt5VVxdV26+MB8Z2D76Fhs8rWG/o1pHX2XO9nxk4HDQHY1hdUPlgsUbz9f48UkgXqhO+5fK2/eVm8CdcKoArxUHvL/VOf05GvOFe4mXszFNRENMo5yN6sK6nDVE40s4LpUfbv3TE8lAzoHjddJJsg2AMw6GEgYgHhirB86qBeCBlDAA8UA8EA+khAEIQACirADsndXjn/O8YX4X1teQo5u0wy9TXcPYDLy9/FjZjj+9zcjceg1jM9ALyurOgyd4IAIgAAEIQARAAAIQgABEAAQgAOcbYD0a9CExpv4wq2uA77rNugLiePA0M9G+/j3wZa/d8CR25PsKeP5LxxIfJJ5b/BE3TQSAAAQgAiAAs6krAQYAXiGbDBfBqf8AAAAASUVORK5CYII=") -} - -.fileList .ico-fromphonefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjNJREFUeNrs3E1O20AYxvGxndgQq04ESfhIxaalgjVnQOq2264q5RJIIKoiztBVkdi0S9acoVfox6qoEQkgjNriNLF5J1Cpm6glJHEm/j/SqySbsfLzeGZsTWIlSaLI4LEhABBAAAEkAAJoZnJ/fwg/vjL5u9Sl3o3pWCdSh1J7uSnpCBtSb/3ldeW4hZEfLOn+rkWXje12eOpMA2BF6mi2vOI6efk6SXvkB7Rk4POKVSWAddPHQEfqvVdaWsn7xbEe2LKt3skzHXDfDSqbXrHCLDxAXuQKpS2vuKBHpfRmYZl5XXndldJTcM2Y5UOhpNxH838upVSXMW/coCpnclFZTt6gDhhLx+tMxDqwrmcUy07GMoNN451IJe3LgFs5AAmAAAIIIAEQQNOSzvPAJFY/z7+o6/BExZ3rh/WA3IyaCWqqMPfk9kFdFgB/nH1Wvy6+DueOWE6APhl6i4pffpaNSzgKvxnR5sQCxt22EW1O7hh4l/Lq86G00/p0zCzMMgZAAiCAAAJIAAQQQAAJgAACCCABEEAAASQAAggggARAAAGcwqS6tSPNLRlGA9qO23cz0L/2y/RD121mBtALHvfdHzhor/SCWnYA/fmnSv9YMAq/S0+MHtibPcFbkjZXMzQGWrbyy2u9YhZmGUMABBBAAAmAAJoK2Ey6HSTumTuzpgY8iC6bCsT74WkzyaG+lXvdvmq5Ui/l/SI8/5WG1AepHYs/4mYSARBAAAmAAJqZGwEGAN+Yh/QHkxJLAAAAAElFTkSuQmCC") -} - -.fileList .ico-mix { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAhFJREFUeNrsnL9KA0EQh/fukjtNNBc08S9oJSJo5TMItrY2CnkJQVEUn8FKwUZLa19BO7ESKwtRTBASEU1Mcs4GtVFRA5u44fvBcOF2YG+/m53Z4SBOFEUKNS8XBAAEIAABiAAIQDsV+8mhdLpsy1pyYrstmutabF9sK9YhgTArtpMcmVKenzA+WVR7GS0Xb1crpTuvEwBmxY66M2O+F5flRBXjEzqS+IJwQAnAnO050BM7CNLDY/Fk2NKJHddpvDzbAW77qexcEGapwk1oIZZIrwThoM5K7avCUmV9ua6L6XI7as3xIZFWfm//+1Zq6zFm008NyJscUo4XtygA6xJ41X9xDszpiuK4UUsqWCd2Itl2bwNaOQAiAAIQgABEAAQgAAGIAAhAAAIQARCANqgl34Vfnu7VY+FCVZ+Lnx+gK1TJzKSKd/cZ9bU6Ah9uzr5cpJa+r8dN+1odgfVauXHNTMx/GitcHn+Mm/QlB1JEAIgAaGkRcWNdql59biT278ZN+1odgT2D08r1gq8fQO7rcdO+puQUT5ai1PgMe7EJla7OyYG0crRytHK0cgiAAAQgxxhaOVo5WjlaOYoIAiAAAQhABEAAAhCACIAABCAAEQANA8xHtSok/qg3ZnkNcK9czCsg/g2eZiba199ENioPBV9sUX4PgedXuhU7FFtz+CNuiggAAQhABEAA2qlXAQYAvkAprthin2kAAAAASUVORK5CYII=") -} - -.fileList .ico-musicfolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAexJREFUeNrs3L9OwlAUx/HbFopCBKKAKImJg4OzPoCLiaurkwkvYaLRaHwGJ01cdHR28An0EYwjCREYwMTI33qukcVJVHq59ftLTiAMtP309B5yB5wgCBT5eRwAAQQQQAAJgAD+A8DW/e4kX0tZ6jykY1WkLqVOYhFphDWps9TiqvL85NgPFvS7pXazut9pPXtRAMxL3UznlnwvLpcTdMb/2LpKJTIFJYBl13I8T+oqkV1Yiqcy4a59rvNx82wHPPXT+c1EJm/sBGwG3I4ls3uJzLxelYydREwmqy+vh1J6xJZs0RM85c/MDR8lc+chdeynC3Ini8rx4hY14EAar2f+RurfT3qiOG4QygSLWvQamDf9GNgOSAAEEEAACYAAAgggARBAq2JsS7/xdKeCwfh2U9zYlJpd3ohuB44TT2fQe4t2Bw6TW9n68++sP96yBjJEACQAAggggARAAAEEkAAIIICRTCjbWc3Kg+q+NujAnyaqeKF1YJS72/o10HR3M0RsAPSTuZE+Zw38knRpnQ6c1JjubuunsOnuZogACCCAABIAAQQQQAIggAACSAAE0DLAWtDvITFiPs1qGvCi3awpEEfD02aSS70jfdR5qftSO/K+CM+3UpW6ljrgHyx/GQABBBBAAAmApvIuwAAZ2pRkuq79SQAAAABJRU5ErkJggg==") -} - -.fileList .ico-picturefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+tJREFUeNrsnE1PE2EQx2d3u7vttts3WlqKKYrBiO8Rj179AF49mfAlTDQajZ/BkyZc9OgX8O7BA0ExctGIECmlon2DvnedeaAQA9JW0227nX+YLIGQ7v52npn/PLtBsiwLWP8uiQEyQAbIABkgiwEywBEAmH93d5CvZR7juU2f9R1jAeOJyyGJMIfxzJuYBUUzev5hVqM2Wclt3q/mtxQnAIxivPZEkpqi4uVY1d4vWxlAD4wDApyXhxyegvFSD04kVW/A3tonS+LmDTvAp5o/eksPRPt2AsMM8LbLCN7TAzGqSn07CRd2Vg2PDzGoxU4OCz2EB5o51lpK/TsPjMeafxzvZBwkRR2iBGxi4tX7fyPJP1FHkWTLlg7mNFENjPZ7GQw7QBYDZIAMkAGyGCADZIAMkMUAGSADZICsjtXzh0r1SgGK6WU85u25IN0PvtglcXREBtoJb++G5fEzPzorA0n+xBxo3t4+/KkUUlDYfG/rDbOhBlogya6ewyPp5oTzamBbvFYD6qUsNOolUN0hUDQvN5FO1ajuQD61KI4tuQNJ8I1fYICdiOpVs1be65qaCaVfX6GcWwOXOwBu/3A8Ye2bD6RCT+EJnUZYpwQ0M34FZEXHZrDBRrqD4icO1GAOJMn4peDvmgywbe1w+0F2uXHZru7bDgt2f36GRm0XVCPCNbC9JGwWF7EOLkF27e3BT1VPCIzQGQbYicgbBpM3oVpMoY0pCxvTqZejzt3Ev6GMdemmyGaKkfOBiurBRjLdsSmnJV/KfhPwjs7B5ugB7MYzFtIfoF7OYf0MgieYRNPtE1lH42KjWoBKPsUA/zbjig0CSUKrc/XIMm/tvBjhGdj+8sb5AKs7Gax76T+Gfsom3YyDRh1Yko/srtCI509cP7HOCQvk5Ays7W5DMbOCy614eNGKSm+9C1BkoBXVACNyDnRfXCzN/MYiKLoPAokbA/n+om0Ay9k1AY8ageaLYQ2bQssSPqxxtRKOcevYIFahkFqCImYTZRs1CzN2eWBf/rQHIE4dxcwnHNM08EZnj7Uq1I29mHme0BTsbK1ApbgpvKI3ch6N9dho+0DasiIYtKlKM++JoxHOwubENRCGhEY6abAf29h2dkZ4ui28Y7pCt7fKuQCpOfS+zq4fjImOWMLUGFoqpJdF2HJRNk4kPc1A8mVG+KzwePbt8gTAh13bERlIXdcYmxHhVPGbCQyQATJABshigAyQATJAFgNkgAyQAbIYoN0AM1ajziS61D6zDAF8UcllgCF2B4+YoRZoQ/VRtfBDw7iD38cZT0eiZ66vMB7wf7D8TzFABsgAGSADZDHAfum3AAMAO2tj3VmRR4EAAAAASUVORK5CYII=") -} - -.fileList .ico-videofolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABSdJREFUeNrsnNtuG2UQgGft9a7Ph8R27KQyLVSgCCICBSTENQ/ALVdIeQkkEAjEM3BFpd7AJQ9RiQsqJGgJIIFapS0hjp3E8dler73MjO3QSNheO+na3sxIv7KKvPNrP88/x00Uy7JAZHbxCAIBKAAFoAAUEYACcDlFneWmyr2PFvFZdnB949Be+7ju4PpSdYkh3ML1dWh9E7xa8LlvZnU7G+1y/hOjUvC6AWAK1/eBZE7z+vBxLOO5b6ig49NjaUCAO8vuA724vtXj2ZwvFHN0Y8Wj8Je37AC/0qKp9/VYSqLwDPKBGox/rMfWyCvNLwpjRNXw52e4KLRuLE36EIyDFlkdHqW5pjFfaNE0fpMZULy+JTLAHhqeuRB54A5FFMVjORLB3FiJpOZ9DKSUE4AiAlAACkABKCIABaAAFIAiAlAACsArI4szVLJ6YDSOwagXwGyVwTRq/DsWxQOqFgbVHwMtlAYtuNqf7AhAZGR1oVnag+bpHo0LR8I12xVerfJTbvwG4tchkLgOiuK9ugA7zRLUDh9At9Ps+xPVD3okgxaWYkiqHiV6CK4Kva4BRq2A6xCv29A4/othRjKvgy+QuHoAW5V9hLfLgAhccPUm+KPX/ueTygAkINgkQHqTwdURYM9sQfnvexBeew3v3bg6ANvVA4T3K1/r4QwC2EKXZvcoKuCP5fC+LFTyv0AH/SbpUtAn6pGs+6MwBYjqAF4g/gJEsttTwHsGIx7x2MZbZ1ZLOs3WqdsBWlDN3+egQJYXSm1eUJ+C1vsq+Cgqo85q/gE4PSN2FCD5rm6nwT4vnNm6JK0KRDPbbJGkm/ZwJ0C0kMbxQ74MJV8Zm36cPvkBaoXfOMWxe5yDiRf5unHy6L/80U0AjcYRpx/DVGWsn8S0hSyp/PRHjrR2hPwpgaTPG/WiCwHWj/pRN7LOx85WwMHEuYTWSJF2shl6ODL3v6xj9wEcRkgtlJzu5GN1Ut7/CY/mw4mf9QVXzu3lKoDk4Dnx1CIzRW+qPCoHP6N7G+0XqV5+di8XAbTwwc0zhz+zG8AyrlbYHRNMtEG8Mh1LZxwCeHnv3li2I6ziriPsGVrHqI6LDaGmQXhM8m11jQtb+eIC9PXfnjeN6kz3BzDPi117h9OgkYGKeogoXl/IfQCHLadpczSypuj6LUy+X554LDuNk3N7uQqgFkqddWLsOngCkci9d3bvpEqnXTs4t5cTojpngSvg1ULQNeoMsZ9Qj4AdXgNVj0Bw5SXbwaB5+pj9K+1Be7mymRBI3OCf9aM/x9a50ewbCO+mbXjUrW6UHp3bw5UAqWus+uNcr1YP7l9ak4JaZGR9pNvpzrTD/UAFrWubUxqavtWLf1wc3uEu18qkk3Q7lf/NCWB/cBRdf5PbWeS3KljnjivPJtXI7eo/rIt0jktxXAOQIxceNc7pvDq3uUp7d6FVfmIvOqPV0Rj05PFd6DRPWAfpIp1zeRaYk9CQPJ57l2cZPBgq/M4NVy2c5uG5R9UxIQ5S7QZds9nv82EtTHlkb1BxUCs/srY1F8ubO8DhcY5tvM1W2MDIPBycT2rL05gziIk1jznnLAvxageB0HJJHrAbmAx3WmXo4fXQ0ihAeHwB8NGrHeEsWmYAFkUW6g+uCQzVvAFYHpG3swSgABSAAlBEAApAASgARQTgfAAWra4pJKaUAbMiAbzdLhdBIE4Hj5ih3KFmwudG9UjD9SFeZwSPLcnj+g7Xp4r8I24JIgJQAApAEQEoAJdT/hVgAK5f795U9dlNAAAAAElFTkSuQmCC") -} - -.fileList .ico-sefolder { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA/9JREFUeNrs3G9IG2ccB/Dv5ZKLuUSN1miMq3/atVU6aauu615so6ulY+2gK0ihrFhwZRR801LGcLJRcXtRxmAwBhtzyFYHZUPYi/XNBlspdK3rnznHWitpFfvHNkprUqOJJrfnuZYiYiA6tRq/X/jhn3uSI588d7/nzqBiGAaY2cdCAgISkIAEZAhIwKUZa7IDgx0HFvtrqRP19QLt65aoVlFN1hSZCJWivnD6yqBq+rzvzIiNF0SGB96PBu+pqQDoEdXuyCnUVJt4OUZ03neoiBOfPTMXArBuqZ8DVVFtdnd+oc2ZuaA7ViyK+eYtdcBmLcOz3Z7pWbxNpP5c0bzs+GPL1v/7FG9adfd79sw8eVZ6aoDK8PlaTXz9QJRsswWJBjbEf1tMeBB40NJXwJqmPzW8YF+XOQOPaRm54p30QlFtiUdfnzvAjOLKOXiWuJh4E4tiHVgnO4piMRakgz1aB0SRKpFNxPO4ozC8lCMgAQnIEJCABFw+mfPbWT69FNW+d5Cl+TAWC6F7+Cx+H/iGgNNFVWzYln8QG7J34BnnevN3ofEhE6zN/y5ixjgcajqy7D7cj9wm4OR4HWtwcO1XyHOsMn8eivTjzmgP7BYduwsbsDlnD1p6DuHu6HVE42OcgZOjW92oLzsBt+bFucAPaO9rRnjigblNzridK4/g5bxa1Je2ofnvakRiI2wik7Mt/20TTx6qJ/xHn+DJjIrz3o+9x/DrnS/NQ/dVMZZdeEo2Zu80oU7d/CzhmJ/7PzVhN2S9xi48NTlpK3F6oBXHqzq5DpzNg87c/Q7lWduTGpvKDWTWM9Af/NNsEg0XqxAcD3AGzjQVOW/AoqioKWlKOKamuAmfb+lLeqYuK8ASVwX6Hv6FTdmv463Vn8CuOp9ss1nSsKeoEa94a9F1/xezeAhPiVzrfXSlGvtWH8cWT425aO4f6ULciKPQVW5eoXQMtuPkjUZeC0+X2+FuqBYNLdcO4cXcvXgpbz+KXBvNbTdCl8wmIwF5MyFBvvUfRuWKXbg8dAoXBn/CH/dO8m7MTBIY6xXrwF4wvB9IQAISkIBzEodY1lQ418KtutiFk9dXsF4vwWZXKZ7Ti2FTHj1dz9gtnA/9i86wH6PxKAGnZk1aAapc67DJ+Sx0cek23XZZe42t6Bzx48JIN/4J9y5vQK8tG88LtBfSy5I+TOWMlNCygrEwOh5eFXUlpQDlJ1SNjKJydoNZRH5ClV2YyxgCEpCADAEJSEACMgQkIAEJyBCQgAQkIENAAhKQgExSgAEjNkGJGeaxWUACtkSGAyDizPCkmUir/MP6h9HQoCZqn/jeS56kMiDqe1GNCv8RN5sIAQlIQIaABFya+U+AAQBLjB3QxtGdmwAAAABJRU5ErkJggg==") -} - -.fileList .ico-access, -.fileList .ico-mdb, -.fileList .ico-accdb, -.fileList .ico-sql, -.fileList .ico-db { - background-image: url("../img/ico/ico-access.png") -} - -.fileList .ico-c { - background-image: url("../img/ico/ico-c.png") -} - -.fileList .ico-cpp { - background-image: url("../img/ico/ico-cpp.png") -} - -.fileList .ico-cs { - background-image: url("../img/ico/ico-cs.png") -} - -.fileList .ico-js { - background-image: url("../img/ico/ico-js.png") -} - -.fileList .ico-fla, -.fileList .ico-flv { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAz5JREFUeNrsnL1vEmEcxx8OOE1LEUyKthATgqYJ6eKEM8QRZ1+WmrropJs6mLjof6CJL9HFl7nMJY7SmLg0JIaBRBKrkBhDW2Io3Hm/B69eT6D3Qjm4+36TJwcctL1Pvr+3p+F8siwzyLoEIABARxUYdrJcLovK4YGyVpQVd8tFxxOJp8rhltH3nwiHrQFU9DASidyNRqMsEAi4Al6lUmGNev3mfCzGzEC0CnCV4AmCwCRJck3YnV5YYN+3tkYC8bAcOE/w3KZjosghkhOVp09QRByE6OkqPAqInm9j7EJEH2gTIgDahAiANiECoE2IAGgTom/YdpYyC8upVMpVcBqNBms2m6Y+k06nfVZHOddJme1ZKBTqucf3j8ugx7VaDSGsVTAY3N8Y0Uaf1Y1lzzlQhUjqdrv/uW6QEwGwD0QC1Ol0+NEsOM8D5Bf/N5S1W3UT78AfK9dNvf/U61d9P69/3Q5ECmVa+jBGCBuU3+/fd6IZeI4CHJWDRgmRoGmrsRGImES0MATBtAMBsA9EM//GAMB+862urUERsQgRDhwDRAC0mzOBAACdHQed+sWHjXST1mjDgW5z4LQ4DA4EQAAEQAgAARAAARCaqEb6KEfBcTbpcOC0OdCuOyZtBIQDARAAARAAIQAEQAAEQAgAARAAARACQACcIg39tuaXbA53JlO0VFy3/m1NURQ9Da/dbiOEj1Jj3dIP5/MscuVy33P1R4+ZmEzy81+vXhv6c44vL7PY/XusU6+zb7fveAegqkGACKARzVzIMGl3lwViMQ7z9+YmqrDhP3h2loWyWdZcK3AHEky0MSZE8Eg7xSJrlUpsJpPhUD0VwmfevjnwnGD8fPHScPi2PpZ4CNOR8ip3ZKGAHHhoS6XkSFo768Vei1Gt8kVQPQXQcvjmeuF78sYqX3q4BBMAhxQPynfktF/v3h94PfH8GXehEwCnpoioxYLynlZqLqQ86EQxmUgH6ouMCkrNeXq1NkrcgU4Uk+GbCflLXXFvz9PjXjsYlJYKa35LISyfO7tBd7Pw4u3i6Zr5tadSn6yH8OLixY4kf/BVq+fZ9ra3nDg3J8nJ5GeWiOcshzDkwlEOAF2mPwIMAMxjFWM7SdA6AAAAAElFTkSuQmCC") -} - -.fileList .ico-htm { - background-image: url("../img/ico/ico-htm.png") -} - -.fileList .ico-html { - background-image: url("../img/ico/ico-html.png") -} - -.fileList .ico-java { - background-image: url("../img/ico/ico-java.png") -} - -.fileList .ico-log { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA9lJREFUeNrsnE9IFFEcx9/srlMbq+2fXHZdD8pG0WIHKQ92iP6QYhRFtzyVHiI7FJ2KIAiMTkoeMoSwTnoqhKLQQ0kHgzI8FAaZGIiu7JKKRVuLO9P83jrjpOu4M7Pu6rzfF5aZffNmYT78/nzfWxhOFEWCMi4bIkCABZVD6+Lo6CgvHW5LnwvSJ2SVhw6Vlz+UDi3Zzt9ZUmIMoKQ7brf7hsfjIQ6HwxLwxsbGSDwWu1zq9xM9EI0CbAZ4NpuNCIJgmbQLBINkJhrNCcT1amApwLOatvE8hQiRKH3txCZSQIhMd+FcQGTexpiFiD7QJEQEaBIiAjQJEQGahIgATULktLazpLWwGA6HLQUnHo+ThYUFXfdEIhHO6FLOcpLW9sTlcqWjh1vmstb55OQkprBaRUVFysaIOvuMbiwzF4EyRFAqlVoVdWtFYt4BTsV+5AVGyO8zDBEALS4u0qNecBsO0OiD5VNyKqu36vSCZN7GAES73b4qjVdGJdZADckAIRIz1UIEmCVEgKbuxpjCOgW773ojEAFmgKjnbwxmbYzm+nYp+rIx10zbmGwgYgpvMEQEaLZmIgIEiAC39FJwq9uYQrsCtDGYwggQASJAFAJEG4M2Bm0MCgEiQASIAFFoY9DGYApjDUQhQASIABEgCgEiQASIAFEIEAFuWmluJnQ8GkBCkrraI8YAgnieZxpeMpnEFC5YCudKh2urSP2xg+TW3SdrzvF6iknd0QNk/74KZezbxDQZePORTEX/35wNBX107u7KsnXnWgLgegIgFxvryOzcT9LZ/ZxCAKBnGmpJS9Np0t3TT8YnonRuuDJImhrrKbDWth6S+JOk98tz2zqf0t9hqgtDNCUSSQneCyWCAMLjngEK6mzDIWUunMMYXAN4ILgn/f0vjXamIhCiB1Kx//VwxusfRr6S8+eOkKql1IbI7JdSdaUAZmtbrzVroJY87uJ0FM1krl3TS+Net0sZG5cikKkmspGC2qluJpD6UAeZATg3ny74oYBPaRRqlQXS/+7Nzv9aHgsuz4Xap9THk7UkXFHG1lIOGgA0hZrqvRmv11TvoVH1+ct3mrrQKDLNdW7n8w5v03Rh8G9OJy/ZkFO0qcjNQk7PvldDSqPofTZIvSJcA2hqGwT35Fuaby66dP2+mIulnGykM6nv5RDttGaN9Nt3n+i8emk8lzUQlnJd7dc4QwCv3nyQEkSO6eWejROFjntX7IZSuDzofQ8vp2HxdfHwzPDsoYBn2HAX9nldJwRRHJyJzVf/TiSZisQdTl4I+H0ju3zFxw3XQNQW6cIIkGH9E2AAawxpPqroZK8AAAAASUVORK5CYII=") -} - -.fileList .ico-mht { - background-image: url("../img/ico/ico-mht.png") -} - -.fileList .ico-php { - background-image: url("../img/ico/ico-php.png") -} - -.fileList .ico-url { - background-image: url("../img/ico/ico-url.png") -} - -.fileList .ico-xml { - background-image: url("../img/ico/ico-xml.png") -} - -.fileList .ico-ai { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABLNJREFUeNrsml2LE1cYx59MZpKNSTYb2tUsG0trqsX0YrVUL1pvpNSXFgr1poIgpUVEP4FXgvQ72ItCL7wuxStxlQVfSxFBS+lCXSPupl2XTeu62d1uncyL5zmzEyfZ3UlmEpJJzvOHw8mZOSdkfjxv52RCpmkCyb8kQkAAuyrZ7ebk5GSEdedZ+5q10X556NFs9nvWnW12fmpw0B9ApgtDQ0Pn0uk0yLLcF/CmpqagND9/ZnjrVvAC0S/AbxGeJElgGEbfuF1mZATmnj1rC8RGMXAY4fWbopEIh4iWyIYXKYl0EaLQWbgdEIUvY1qFSHVgixAJYIsQCWCLEAlgixAJYIsQQ27HWWwvbOZyub6CUyqVoFwue1qTz+dDfrdyfSe2t4dEImFZT+g1l80+F4tFcmGnFEWpHow4vc/vwbJwFmhDROm6vs7qNrNEArgBRASkaRrvvYITHiB/+DVXdh7V9bwFlsdPgbH0F0jJLAwe/qEjENGVsdW7cTMAA5VEtH/+4PC4VbAex51QOBzmzSu8wAFUn45bDzS0o2bcSYjOeNhTFmhWVqBSvMk/x/ZYp+w4xuudEp6+96wFqjM3wNRVCKfeBvnN93mPY7zeSSFEL39jSEFzX2Vkf02vTl9vav3yrXO8tUP1ZU3gAeqLT0FfmLKyYmZ/Ta8//5Pfb/jQksJbu9QsRDlI1heKJEF+Y7f1w1iPY1Nd4vdjY6ddvyN+4Lu2/66eSCI8zk1PWG6b+RBNyTYpa8zdeILPC6K6DrDy911uZZbb7qt1j7Ux3sd5bnrx01HehANYrfUcFlfdrzosspM1Yc8ANJZnQZv/rSbm1cQgR0zEeTifADqtz1Gi2Fl3XZZzXG+2pBEDoGkwt3wNxK776uW8zuebwXrJqWtlTGXuPhir/1bHS9fPNHZ5Nh/XbQZbKAv0mxSClky6YoHG/wtQmf3VShTRFAx+dglC4YhrrVi+chLMl4t8Ha6XBtLiWqAzlkVzn7vC45DZ/Wjuiw1jp6AAx9eDaSAn6CC5cccBaqXfq/Vc5J0j3IWb2peyeTi/vn4UDmC1lmM7jOjOLz2t5fPtncnMhHgAnafOSvYASPGMtx/L5uM6XgZ1+LQ6EADtU2fUwHtf+foOe103Tqu7D3At+MvbPqj+ceRVuA7XByWZCPd2llcVCgXXt7Po/cBePo0hgCQCSAAJIAEkgCQCSAAJIAEkEUACSACFk+v/wvKd0zB9hwBB/rY/gKioLDbAlxq5sHgxcPjgBcgevwzx3KFab9g2tuF1AuiMKYkMB2VUlmHLWx+TBXoVWhfCW3x4iYNEoATQC8B3D8HK42uwWrReKo9t/4gANqsYc1lJSXB4hrrC+2T+GAFsVluYtWnLc6A+L/Dxf8VfONBYgGNhYKo8jHWx7RYozLT1YFdn7hJAV/ddi3WzP5/g7msrNXYSkruPgRS5SC7cKPvasc8pO5nEc4cJoFvyQBfGmFcvjIfqQoEDlpSBwAF0fbno0Y9H9AisSCEQU0hGhbix65urYV8WaKZ23dMN64tEhMefPbXzvu8kYsZHP9XAuKGXn+yFypJYBw9K0jDTOx6Y8ewnvl2Y1INbOQIomF4JMACqJM9nuTUMmgAAAABJRU5ErkJggg==") -} - -.fileList .ico-bmp { - background-image: url("../img/ico/ico-bmp.png") -} - -.fileList .ico-cdr { - background-image: url("../img/ico/ico-cdr.png") -} - -.fileList .ico-gif { - background-image: url("../img/ico/ico-gif.png") -} - -.fileList .ico-ico { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA7lJREFUeNrsnEFIFGEUx9/szu66uOvumCtuSlREkB2ChII6dEgisFtCEQVBetBD4KU8lBAFURejg0HSqYgKjwke8tBBrEPSaYMMCrRW1NpCxTZmdvrep7vsrLObO+vubPO9P4zfOPNWmB//9733fYsj6boOJOtyEQICaKvkQjdjsZiXDQPsuMiOZqc8dHNLywM29G42PlRXZw0g041wONyvKArIsuwIeNPT07AwP98TaWyEYiBaBXgJ4blcLkilUo5Ju6ZoFObi8S2B+K85MILwnCaf18shohPZr0NURGyEKHQV3gqIwrcxpUKkPrBEiASwRIgEsESIBLBEiASwRIiyaHDq2LoWl3Ob1dfZ2Z5Qa2svAVwXW9tDIBDg55IkZa7nO5+ZmaEUzpbH48lsjGRvJlvdWBbOgWmIKE3TNrgunxMJoAlEBKSqKh+LBSc8QP7w66mcvVVXLMiqnwNXJ19A4v45PpYLotvt3pDGua78bwEm348ZxnIIAZpBdMRKxHfghGEsN8Rs5zmiiPiPnOVHJZT+6gJbGsc4sNJCiMV8jUEATbTZAlLVKYyVN1fK5acVhUgOrADEqnVgttvM3Fg1cyb5jAASQAJIAEkEkAASQAJIIoAEkAASQFIxqrrdmEI7L3bvEZIDRXCg3Y4iBxJAAkgASQSQABJAAkgigASQABJAEgEkgATQwSq4H1j78DTMCQ6oFn/c+2AN4FqET2yCapJS2LYULod8uw5C5MJdSIwOwsq70cz1UHsXBLP+H+RP/COsxl7D0sRzw+fN4lamRg1/y9EAzdTYPQSumoAB6rbO6xA63s3PESLeb2Dgc+OUU32gdPTx67mwhajCwaNnwBvdCz9Gbhlc9H3kJiQ/T7H7a26rbevgcYtPrhriEi8H4df4MIctK1HxAPpbj/FUxVTM1cLjKzA/3MvdhSAxTk3EN8SlgSJk4QCiq9Sf8fxFcB2YqyZoChmV+r3M4r6BHBbQgbQSKVHoKnRhvvmxZeAVuPxB5rKlvHGY4rKyvaCTHQsQ5zVsbczg1Oxu46mJabw08YzPl2aFIj332dHK2A4QHxoh1XdeMxQB7BUR7K/xR5k4dGvD+TuGOGxjsAJjJTYrMI7vA7EAYKUNtXfzfg6PdGpjv4cONcZ1mcbZ1UhLhV4486X/kCapSeZSCcSUDrrHn9p5+43bUgpr0f2TkNLwtT4CsmPPzJ5da9o3aTmF9fodJ1WAMffCp8PS8qJQ75jRAw2qFtnzFhlYTmESNdIEsNr1V4ABAKATVWjNArx4AAAAAElFTkSuQmCC") -} - -.fileList .ico-jpeg { - background-image: url("../img/ico/ico-jpeg.png") -} - -.fileList .ico-jpg, -.fileList .ico-JPG { - background-image: url("../img/ico/ico-jpg.png") -} - -.fileList .ico-png { - background-image: url("../img/ico/ico-png.png") -} - -.fileList .ico-psd { - background-image: url("../img/ico/ico-psd.png") -} - -.fileList .ico-webp { - background-image: url("../img/ico/ico-webp.png") -} - -.fileList .ico-ape { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA7JJREFUeNrsnE9M01Acx9+27g9jIkOZTMAYiTEZF4melJtwNJ6MHgwx/jlovHhSL0ZOejNGo4l6Mh7wZMSjHDyoB4NycolZNJpFZhiJSmRs0K6+X6FzsNKtLdtK+/0mTbf2tUs/+/5+v/fe8uaRZZlB5uUFAgBsqgS9k8lkMsB31/l2mm/dTnno7p6eB3x3sdb2W9vazAHkGm1vb78ajUaZIAiOgJdKpVh2ZuZCZyzGjEA0C/AswfN6vaxYLDom7LricfYzk9kQiNVyYCfBc5qCgYACkZzI395HEWkiRFdX4Y2A6PpujFWI6AdahAiAFiECoEWIAGgRIgBahOjRm87iY2G5r6/PUXCy2Sybm5szdE0ikfCYHco5TnxszyKRyLJ7PP+5rPc6nU4jhMvl9/tLEyPl0Wd2Ytl1DlQhkiRJqnDdek4EQA2IBEgURWVvFFzTAM4/e8zEb6m6f46wey9rPXFOv81KKJdP1RkF2fAc2Ah4Rj6HIPp8voowXutK24VwZOQS88V7N/y+UibN/j65Z+gaFSA5USsX2hJgPeBZuS9BJGjl1diWIWxn0ey7UQcCoAZEIz9jAKDW+LbGAuLqfmAtEOHABkAEQKs5EwgAEAABEAAhAARAAHSl6jaUmx97yMTvX+BAs3IDvLo6sBGyg8s3dQ60g8tRRADQoQCFPftMnUMRWVHr8TNwoO2/fRu4fFN3Y+zgchQRAARAAARACAABEAABEAJAAARAAIQMSXe1ZubyKfwzGVf89lPzqzUDBpY9OVGLVRYhIoQtqmkTqltGLimzxrmXY6zw4W3puJ8fo1VMayXnc2zh1bjSdr02pOKvWfbnzqizAXqj2xV4BCXQP7AKoCparrX09fMq4OGjJ1cdW9vGNVU4eOBwyVEEkoBWU27iRcmhru/GBA8eYoXJd2zx09RyoerfjxxYqyhkPaEwW0x+VFxIEEODwyz/ZkL3uvDQMWVPIeuLblNea+XBBe7Uavfa1AD9iQEl0UvTy3/msJScUqDSpjpSCw7BpoJD16oA7ZADGwqQch2BIkVv3K0AWw7QDnBsB1DNdb9vXVEcpaqFh2docIg7LIyxcLXqSy4rh6f09nk+VIsLiohO8aAQXpoYrzhH+ZA2ApybfV7zPbWKSKM70rqTCdPXzkvBQt7Vw71CMFTcefORz1QIi1273kscsBunZOiZ6dnFrt5J0yEsdcSG87L8Wpj5MeBZmHeVE+WW1qIY656SOnYcMR3CkE2HcgAIlfRPgAEAIIZMFb61acMAAAAASUVORK5CYII=") -} - -.fileList .ico-avi { - background-image: url("../img/ico/ico-avi.png") -} - -.fileList .ico-flv { - background-image: url("../img/ico/ico-flv.png") -} - -.fileList .ico-mkv { - background-image: url("../img/ico/ico-mkv.png") -} - -.fileList .ico-mov { - background-image: url("../img/ico/ico-mov.png") -} - -.fileList .ico-mp3 { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA/xJREFUeNrsnLtv01AUxm8SO0mbUOKKhj4RaoUqpQsFJuhGOyImHhNCBQYQf0BZECwgJgYQSBQxIIaiTsBIBwZgQIVOREIRCBTRogaBVEjzsmPucevgJm4aX+fhXp9Pct3G17b883fOufe6jkdVVYJilxcRIMCWSqi2MR6P++nqKl3O0qWPl4vu6++/T1eXam2/s6ODDSDV9UgkMiVJEhEEgQt4iUSCpJaXL3ZFo8QKRFaA5wCe1+slxWKRm7Dr7ukhP5aW6gJxqxzYBfB4U8Dv1yCCE+mf97CItBCiq6twPSC6vhtjFyL2A21CRIA2ISJAmxARoE2ICNAmRE+16Sw6FlaHhoa4gpNKpcjKyoqlfWKxmId1KMed6NiehMPhNfd4/nPZ7PdkMokhbJQoiqWJEWP0sU4su86BOkSQoigVrtvMiQjQBCIAkmVZW1sF1zKA6acPifw10fDzCHv3kdCp89XbrIeycarOKsim58BmwLNyHoDo8/kqwrjclY4L4fCZy8TXM1D34ypLSfL38V1L++gAwYlmudCRABsBz85xASJAM1ZjR4awkwWz71YdiABNIFp5jIEAzca3NRYQV/cDa4GIDmwCRARoN2ciAgSIABEgAkQhQASIAF2phg3l0jMPiPztMzqQVW6A11AHNkNOcPm2zoFOcDkWEQTIKUBhcJhpGxaRdYVOTKIDHX/3HeDybd2NcYLLsYggQASIABEgCgEiQASIAFEIEAEiQASIsqSqszHS9A3yy+WAJPhx+wkbQJDfwmtPPCq/xUuIGMKNDOF6Kjg2TtrGjxP5yyfyx+RNItgGbXLv35DVFzNEHBzW3mYql5pdJZmXz7V2xv10ZeaekezrOf4Alk5IwXilXaT4++eGzwOHDpu2h9e2ChS6rh0Uavux09pnABngAXAAGjh4RNumZjIlwNxVYXCQf2T/xjw7Mko8wfYKqGZapQ4DATy4GcpisgRLX/t6B/h1YG7+reYUY5iJsVEttK0qPfuoIk2ACh8X+O0H5uMftBAW15+agfPAgfkaL7qd5jwNkgE43BDp2p1Sji0w3Ixt40AIOVhECg0uFHIfhDUABJDlKi8k0BZynjHcIXRhgZsSOjmpPa0rdyc3AHUXBscmtGrqjx3Q4AGYWopINUE7SBEQyulZjodycJEQugAREj5rzopM3dLC1qxQcT0W1kMWnAKhyJqz9GPo+RRyIaSEfBOLSMv+M6EQX8t5dvprkAvL86TeEW+Wqn710+KVC0ogl3X1cC8XCBZ7b077mEJY7t7zTqGA3fhl8XDNcO1y98A8cwgrndGJrKq+Epa/j3oyaVc5UW0LFeVo34LSufsocwijHFqFESCqpH8CDACOqX4es1WjewAAAABJRU5ErkJggg==") -} - -.fileList .ico-mp4 { - background-image: url("../img/ico/ico-mp4.png") -} - -.fileList .ico-mpeg { - background-image: url("../img/ico/ico-mpeg.png") -} - -.fileList .ico-mpg { - background-image: url("../img/ico/ico-mpg.png") -} - -.fileList .ico-rm { - background-image: url("../img/ico/ico-rm.png") -} - -.fileList .ico-rmvb { - background-image: url("../img/ico/ico-rmvb.png") -} - -.fileList .ico-swf { - background-image: url("../img/ico/ico-swf.png") -} - -.fileList .ico-wav { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABE9JREFUeNrsnDts01AUhm8S59E2lKbQ0PQl1AgVpQsFJuhGOzMhFlQhXhKoCwuPBcEEGwMIJGBCDLDCyMICAyqEpRFV1AqIaFADKqpIX/EDn5vc5CZO09hpXNc+R4qcHD+i+/n/z7Fv5LgURSEYxsONCBDgtoZQa2UikfCpi1vq66z66rXLoHv7+h6riyv1br+7vd0YQDXudHR03AiFQkQQBFvASyaTJLOwcLkrHCZ6IBoFeB7gud1uIsuybWzXHYmQX+n0lkDcrAZ2ATy7hd/noxBBierHR9hEthGio7vwVkB0/GVMoxDxOrBBiAiwQYgIsEGICLBBiAiwQYiuWtNZ6r2wEo1GbQUnk8mQpaUlXfvEYjGX0Vs524V6b0+CwWBePa4Sl43ep1IptDAfXq+3ODHCu8/oxLLjFMggQkiSpFHdRkpEgFUgAiBRFOlSL7htA5h99YyI35JN/x5h/wHSdvpC7W0KVuan6vSCNL0GmgFPz/cARI/Ho7FxpSotZ+HgxCTxRPq3/LhSOkX+PX+oax8GEJRYrRZaEmAz4DVyXIAI0PhubEkLWzlg9l2vAhFgFYh6fsZAgNXub+tsII6+DqwHIirQBIgIsNGaiQgQIAJEgAgQAwEiQAToyGjarVz25RMifp9FBRoNJ8BrqgLNCCuofEfXQCuoHJsIArQpQGFwyNA6bCKFaDt1DhVo+bNvAZXv6MsYK6gcmwgCRIAIEAFiIEAEiAARIAYCRIAIEAFi6IqaT2umr57BfyZTI3L/hfGnNX06HnuyY6xv8hAiWtjqNdAd2ktCtx+QlrGTZfn2S9do3tNTejDGFWituu2uiUma9x85Xsx5B4doLjA6pnXN8AhdB8sdD1Be/E2k+RQdMA+KgePz3mj+vZT+UXYCYHpeWV0uA5Kbm6HH9cUOa77TGxuh269Px+3RhWGw7s49GlCimvdEBop5eA8Dz83OFHOgOsitvH1NQQLQYn1KfKYngs/ByYHjr019sM9lDCiKVx2AAvXk5r5SVcE6pkaAB8CKAI8eozCYmnzDh0oAp79ocgAPjgdwbQOQKYrZFZYwQFAmP2gAzNuXwYVtmSUDo+Oa8sDbGOzL8rYBCIOHAYHyGChWw1heKKiTQa0GI5eI0/35WsjbuGjfT+9N68Km/awJYMCOMHgGlOW9tElky/IAhIGCjlrZJJilwd7QtcHG8uKfvGIL1rYVwHwdHKOXHXyTgDzkvBXNg9W1v/eul9VEgAXbL79ppXl4ifQkHCTySv4EgGptdy/M4ICy+DrH8tBh+Tx0X1AZD49ZljWXUjOJ0/3BvmY1D9MB8vbkLVZpZ9Y8ADTUPI2SC3UTALPuzewMn826fKlrMmH+5kXJv7bq6Nu9NX9A7rn71GNIgWL3wEdJBezEKRkYM4xd7O6fMtxEpM7w+KqivBMWfo64VrKOUqLS0iaL4d641LnvhGELY1ioiSBAjKrxX4ABABrs14sDnYP6AAAAAElFTkSuQmCC") -} - -.fileList .ico-webm { - background-image: url("../img/ico/ico-webm.png") -} - -.fileList .ico-wma { - background-image: url("../img/ico/ico-wma.png") -} - -.fileList .ico-wmv { - background-image: url("../img/ico/ico-wmv.png") -} - -.fileList .ico-doc, -.fileList .ico-docm, -.fileList .ico-dotx, -.fileList .ico-dotm, -.fileList .ico-dot, -.fileList .ico-rtf { - background-image: url("../img/ico/ico-doc.png") -} - -.fileList .ico-docx { - background-image: url("../img/ico/ico-docx.png") -} - -.fileList .ico-pdf, -.fileList .ico-fdf { - background-image: url("../img/ico/ico-pdf.png") -} - -.fileList .ico-ppt, -.fileList .ico-pptm, -.fileList .ico-pot, -.fileList .ico-potm { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABWZJREFUeNrsnF1oHFUUgM/O7272J7s1iVkT2mrAmBRREQUpiFCtCj74pFAoCOJDlIogSJ8K+qL45A8qqPhSEH0VLAit+lKF0tIXiaQBUaLd2m3zs5tkd+fnjufObtIkZmdmZzLb2Z1zYJifPXdm7rfn3HPO3ZlNWJYFJP4lQQAJYHQBzs7OKrg6hctLuIz1S6fHxsc/w9WrXvUHc7m2n0kubd/O5/MnC4UCSJLUF/Dm5+ehfP36zPDICHQC0S/Alzk8QRCAMdY3bjdaLMK1UmlPIAounw9zeP0mqqLYELkl4u6nYQLsW9kriLEFuFcQYw1wLyDGHmBQiAQwIEQCGBAiAQwIkQAGhOhWC1sTExO+b2Tth09AmzvvSVcsFEF98FlQp5/AnfDKxnK5DJVKpaM209PTCb+lXNfEXCrB+k9fQf3id5A+OgPS2FQo18HaHjKZTNN6Ere4tNteWFiA22aBG7L00TF7XXj967Y6xtU5G6B5c8G2wMxzb4J84AFP529UFyEhSCDKKoiK6qqv6zoYhtEW3E6AThYYmTFQumsScsfeBfmeh9EcDdv92frydshaHWEtQW25DGs3rm4eZwhDkhUw9ebnlmk4XkuWZXt2yQ3e1u3eCCIJAdLPnABx5G6w6qug/fZj8zh6SW35Bhi1VTROCdRU+v9fgKpCMpMDCS2wjhCN+roniBugNpat+z2ZxiQkBVKPvWBva1d+bfJjDC1MhmQaAaGbCg5BRk6mYCBXQLM0QUfgjlaPANtZYm9a4BZ3tl2zUm52RBRBVlPevwSuP5BB8CaY6PZuEEXU3+nGXq0wmnmgxVqe638Sl/edQ9dra/YQ4JhCIcDdIPZsJcKqN5s3ly5sEPV1HhHdXpRkMPSGu24L4s6xsCcB6n9carnyfUH4tSAqwHTNmzUJQscWGLlfinj0rV8+0yypph/f5tKOVc9i2SG4i96npxAiz429/twbKYCWocHqmQ9tiPL++0EaP+QJYKowsreZQMv6vECMDECjNA/r5z4Hc/EfELJDMHB0pjUgMtcgEEo6FUUX3ijp3FKY9NOvgTCQb4KzzN3zPTW5rRpxQAHpoWJoECNjgeK+MUg+8jwok4dvuS3mce2sT0ll7MU5G2JQW62Eet9dBeg0mbBtZgaTX17P8vo20AyPqdupTJgSyTSGD96m1gh8HlPTQFSS8QMoYadNE8swQ/cPD9vyc0hxBMjrMCWdBb1R95VD8za8LT8HeIym/QWQWyHWsQKWYXxGxergwSauy9vwtlIHExB9B9COtOkcr8VgvbKEFlVzLwHR6rgub2O37cYXDREXZSBrz6rUq8s4rmFkVhQst8TNOUGG0ZphumNgwODBJ5UfRuvrXrd64qnJBMJK5YfAQAsztBqmOHVgrQDDXZXP/0nJNLpssvtDTZTyP/dxMXlbIPXsGNgLQgAJ4G1Ot8I8eaO6HJmOqtl87wEM66bJhQkgCQEkgBSFKQpTFCYXJoAEkAASQBICSAAJIAEkIYAEsAfF8V25v96Yov+FQjnwwe8B3taU1HjTMxrkwmFKVx/tGD91dtfj1V++gZWzX7rqrF36HkZPnHa8xrWPj4OxVOpPgFxWzn0B1fPfbu5nD78Ig0desTvNAbnp/P3Ok5vHh4+/b6/Lp9+KbxTmoFi9Ckrx3kA6lMZQHuhPuHsKySxopSuBdGIRRLjwsYwvuwWITnRiC3BngPCrQy5MYyAJAQx7MuHPk4+aCaMh8Dce4ykWWJLKDr53QfRlgWbx0AX7jUmwYgmP952NTl30HYWtffufMi32s/Dv3EOJ2kqs3N1KDTJ25+RldsfBI75dmCTgGEhCAEOX/wQYAOhdFmkKB8pMAAAAAElFTkSuQmCC") -} - -.fileList .ico-pptx { - background-image: url("../img/ico/ico-pptx.png") -} - -.fileList .ico-txt { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2JJREFUeNrsnN9LU2EYx5/98KgxbXM6tuZFMSgaeiHmhV4IGSVFkX+C1FV20W0hBF1IVwVeaAQR/QNBERR5UeGFg1K8MAyySBCbbP1cP6zhzul9js6t1NP5oW6e8/3C4WznbBfvZ8/3fb7vOzguRVEIMi83EABgSeXVujk9PS2J02Vx9IojapdBRxsbb4hTn97P766tNQdQ6Irf778YCATI6/XaAt7MzAylU6lzDaEQGYFoFuBZhud2u0mWZdvYLhyJ0EIyuSkQ/zcHNjA8u6lSklSIXIni7TCaSAkhOroLbwZEx8cYqxCRAy1CBECLEAHQIkQAtAgRAC1CdGltZ4m1sBKLxWwFJ51OUyaTMfSdeDzuMruUs53E2p58Pt9y9bgKXDZ6PTc3BwsXq6KiYnVjpNh9ZjeWHVeBeYisXC63puo2qsRtBzif+rgtMKKhoGmIDGhpaUk9GwW35QDNDmw7lbdy8VadUZCOjzEM0ePxrLHxv1WJOVBDeYBcievNhQCoEyJDK+7GsLBB8e670QoEwHUgGvkbw7ExRnN9u1J9esK1o2OMHoiw8BZDBECrcyYQACAA7uil4E6PMaVOBYgxsDAAAiAAQgCIGIMYgxgDASAAAiAAQogxiDGwMOZACAABEAABEAJAAARAAIQAEADLVpqbCYO3RkBI6Ob1uDmALEmSHA0vm83CwiWz8GaqrWU/9Zzo2PD+18wP+v5jkeoCNXRt6C4t/ir88n1nTlJ1VaV6T0v3Ho7Ri8nX9gTIAyse3EB/Lz1+Mk6jiZer16KRoIB1irq7WgWMhHqts71JXK+n4dsPaD5Z2OXm691dh6h/4A66cF4MiKG2tRyg2L6ICpQhjSam/oLnSAvrFVdk08G91HO8Q9j4twD3QUCdQA40ovuPEup8x9bl1wjSBhXwF5rFnnB5/zlVdhaurpJEt26nqVez6ntuKG9nk/Tp8zdUoB6dXok6I08n1IN17HArLKw3KzaLBsJNgyuOj9GxKfUaxxYA1NByZGlVrVucF7krv3n3njo7mtVoU27SfPTThUtDOVlxOXq553Yp8uDV8x5TFdgYqXvOD6dx4uPiecw89mg4MG66CwfrfEdlRXm2kPrS8nMx66hK3FUtyeFQcLI+WHPEtIWhHRqkAdBB+iPAAD3HE9ONhnJoAAAAAElFTkSuQmCC") -} - -.fileList .ico-xls, -.fileList .ico-csv, -.fileList .ico-xlsm, -.fileList .ico-xlsb { - background-image: url("../img/ico/ico-xls.png") -} - -.fileList .ico-xlsx { - background-image: url("../img/ico/ico-xlsx.png") -} - -.fileList .ico-7z { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABAFJREFUeNrsnF1IU2EYx5+znW3aNvcR6qZS0shwXmUQVFAXKgTrKoSiiwjMiwRDb0ToA/q46E5JsCDpoiAKuuhCIajsAyqQspua5IgKq6mLmV+zyc45nfcs15pnunO2M7ed5w8vZ+Ocs5fz4/9/n/d956Q4jgOUfFEIEAHmLkCv16vnD+f5doJvlYXy0JVVVdf4Q1uq11tKSpKeo9e594LVau222WxA03RBwPP5fBCYnj5VWlYGUiDKBdhC4Gk0GmBZtmBi53A6YdLvzwhEzTrnSwm8QpNBrxcgEifyb/uVBFiwyhRE1QLMFERVA8wERNUDTBciAkwTIgJMEyICTBMiAkwT4nprYc7lchUUnEAgAHNzc5LucbvdlNylXMGJX9uDyWSKuof6xyXZ64mJCYxwvHQ6XWxjJD59crf1VOfAFYhEDMOscl0yJyJAEYgEUCQSEY5SwakeoPDwf6Mcv1WXcw6MTH2CxaEeYBeCWQekMdnB6OkEuty1JkQSZdISY5wKQMWLSGh4YEPgCc7i+w09ur7udVqtVmhS4WXFgczP6DTAeLAd9DV7Ur5v5uqx2Gvb6TuS+10efw2LD/uACX5P6XoCkECLr8Y54UDgouOLFHiZkJz+yO67VAfiUk4EopSvMXK2CsfHNt04S9WK+1KZXNPoubUhYoQVhpizDsx2bGWPmegzBLixy0GswuhABIgRVrgKowMxwqi8XMolVtxcjHVOA8yHcRAjrIaJNDoQq3AGxGX5r/yz1J/yAKloF+H3w1nlF+uPUvYRFR8DaYcLIn4fhJ7eFFrWB3mHK78daGxqA7qqbmMqJN8v6T+vHaixloP58BksIigEiAARIAJEIUAEiAARIAoBIkAEiABRRGv+WvNrRy3+XyheW3vH0vi1Jm1QN71IGCOspDb8a03boU4w1ntEz3272AiO9ltA2ypEz4c/j0Lgdpe6Ac4M9ggtFokiE5S19sPS2Avh/WTf8VX3mPcdAUtDKyy8HcIqLOZI9vcCzD4eED1f7D4gwJt/dReWvM8RYCIc0n4N9orHxebkAXfAsn88KWBVA7Q0tMDi6JAASEz25nPALs1D8P4lnEiLuY8Ui9AH8VhaGk+C3lnDw7sMkRk/AkzUJvd+HswPobImyrjLA+a9R2H2yY2k7lQ9QMO2+ljljRdxnc3TKRSM+Zf3cC0sJgJJU2ReFU0ypbE3nxWcGT/VwYl0gnQV26OrpuD/AHU82JVJdEXXg9WrLB6s2DwxZzYTvnTvZqhImHcpBeoUBxxtYKuvjGhlRZhx1o0AywgfpEZ45NlZR+0b2RHm7FuaGI59ppn6uJNamlXVxgNXbGHZ8h3v2M3VDbIjjEpzDEQhQMX1R4ABANtbXMt+ZZZBAAAAAElFTkSuQmCC") -} - -.fileList .ico-gz { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAP3SURBVHic7ZzBa9NQHMe/L8k6pd3WTra1rDihoGy7zJueZSB48+hJ8LSBMmEHQRC8C4qCepAd/Q8EQdi8CSroQSpaPJQhc61MHFtl0iQeutSsbV6T/vqS1/Z9Lm2T9/JLPvv98l6ypsy2bSg6R4t6B3odJZCIwVuZz+djAO4AuApgOowdCoPpbPYJgGW/7cdGRz3XcQUCuJtMJm+lUikYRrumvUGhUEC5VFqamJwEAkj0op2Va6lUCpqmwbIsaixpSGcy+LG11RWJ7c6BE5rWf6fJ4VgM6UwG5VJpCcBjyrb6z45PuiVxYAUC3ZE40AIBusSBFwjQJCqBh3QqUQl00YlEJbCBoBKVwBYEkch4t7Py+bydy+W6vX+RUi6Xsbu7G6jP3Nwc81rXHxe4AUgmk0gkEgAAxv578Xq/ubnJ3d7AlfDQ0FD9xoi7+jq9sTxwGQjUJAKAaZr1ZU7WeWWiFwMpEKhJZIyhWq2CMRZYnMPACgRQL2X3rTrpMrC6/Q37L+7D2tsRHaoJLTGO+KWbMKa8ZxKGYcA0TZim2VTGfgQKH0Qq688ikQcA1t4OKq+etm2n6zp0XQcQTB4QQgaaP2vTgPjF64idPu+736+HV+rvUzeeB4779+sb7L98BHPnu6/2uq6DMXZkNJYiA2HXzi9B5HWDTuJpmhY4AwduHtgOTdMQ5N8Y0o7C7rKllnNQnOzzM7mWVmDUqBLuAlLMAzsl7LLtFJWBRJRAItKWcJSjcBBUBhJRAolIW8LuspUZlYFElEAi0pYw0DziyljWUguUUVgjqoSJSJuBsk2YvVAZSCQ8gXbI3/IPKZ54gawW4uDTuvBQburxmNhDFH4ONNI5VLcKqGysobKxJjpcy/giEZ6B8cVlGNl50WFaYmTnEV8kP4zEjyF06wC05BRGLt8WHSYy1ChMRAkkogQSUQKJKIFElEAiSiARJZCIEkhECSSiBBJRAokogUS4T2sWV2bVL5MBmHnwmfC0pjHc1Z3pOaoH3NXCS3jm3kfRISJFnQOJCL0j7WSf81pcXTjy2cFZ7qwrri409Wm1XWd9Y59GWm2jWwgV6D44h8bPrZa1asNr647HayuC0EvYzwEFOWi/okUR2Vc7+mVwiUQgrwxFbF8koZdwmAcXBpFPY6jZ19g/7D9QKCXsnpI0Tjd40w93f0cKr3/jq7uNKNpfC/fApZzQrKsecK+FuSVsHxuxALnuJ4Q7etuHDrzhlrCZmX9rFN+dg6YD8P9bKiJpVfJiss8GLBNWevY9rxVXoD1+ctG0rdfa9pez7M/vyAcch+LKrPAY9vExy5o688E6ceoCrx33HOiXIL/0IxvU45cmq3oVJZDIP8OiU3PHSqspAAAAAElFTkSuQmCC") -} - -.fileList .ico-cab { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABLVJREFUeNrsnF1oU2cYx59zepp+JTSp1CZaNl3AYXo1hQ0V9EIFobsYQ5gMBMFVWGFjvRHBWZjbhXjTMaEKftwIoqCXBWGrzsI2lM3dzAwt4kbR1GZr1takq5wP3+dtczxJTr5OkpP0nOcPh3ydnJfzy///PO970lTQNA1I1iUSAgJYV0mFXoxGox52M8y2Q2xb75STXt/be/a/4fcGzV5LHrmR81wkErEGkOkrv99/LBAIgCRJjoA3OTkJ8ZmZT7tP3oV8EKvmQKbDCE8URVBV1TGxC4ZCMB2LVQVisRrYjfCcphaPh0NEJ/pP3h2lJlJHiK7uwtWA6PppTKUQaR6YBZFNcUYJoI0QCWCFEAlghRAJYIUQhUKXs9haWAuHw46CE4/HYX5+vqz3sLWwYHUp5zixtT14vd5l9wivueS7PzU1RRE2qrm5Wb8wYkyf1QvLrnNgGiJKUZQc1+VzIgE0gYiAZFnmt+WCcz1AfvIrUTZeqms4B8rPH0NybATUF7O2AxK9XdDRPwRST7ggRIwybtkxLgVgzZtI6taFusDjzmLjpr4/V3S/pqYmvpULzxYHKv8sTwM69n0Gnk3bSn5f4ruP9fuBz6+UPe7LR79A8uYZUGaflrQ/AkRoxm7cEA4Ebbm+lAOvGrIyHl59L9eBtJQzgVjO1xgN24WNsa00zuUq7b5SJtcSea4wRIpwjSE2rAPtjq3lmkk+I4D1XQ5SFyYHEkCKcI27MDmQIkxalUu57I7biLFuaICroQ5ShN0wkSYHUheugjSb/8rfpvFqD1BYHmLpj1u28tPHE2p7ijWvgVIwDHJsElK3L/HN9iIfDK9uB3bsHQSpt68+HZKNi+OvageK/h7wfXicmgiJABJAAkgASQSQABJAAkgigASQABJAEqrgrzX//mIz/Wcypje//bOCX2tKLe6mJy9RhGsp27/W7NzzCfi2H9Afv4w9gsXoHVj46VrOvt0HT0PLxi2QGBuB5G9j+vP4HL5mprnx86bHcgTAtQOjILZ6M4Cs2X8COncP8PvGE5cCIQ5K/X8B2iO7MgCmFb98FJae3NcfB94f4seSE9P8Q3FUF/bt+Ag8oU0we/2bDBj/Xv+aQ/DtOJCxf8fWfg5vbvwCB4lAiyn14M4K/KDzpjFtzEXoCoysmZNmzg9yd+oAt/RD8v4YLK5AaYvsLDpGe98uDn0xOuG8CKP7Fgxxy2l2iVgGbLHVx+BNMCAvOHh0aHZtM6uDWAONx3JcEylF7cxtcuKZ7tYUcxRCTbs4Xw3E2Af6h3LqqSMAIgx0Yb76iMV/+sxB3YGo3uEfcsAWag5YWzH6rW9tdR5APHGEhBCz6yCeMDoOo4cwUc9Of8Djmz39EVtHii+vDLXUMU0E3YGQuvZ/yaOWPdebG7+oNw+EbYTHP4AHE3pM8wlfww8Im4/jaiACwU7buWeA16l0rUI34rwQoWF0pcA6HWZ2CcANAcuJi3mbyMLPV03njHW5mPDXsXcVQV5iLhXAndJAk1rUDafuNVmKsBLquweqwg/kRnh47mpw86+WI6x1vbFX0dQfxecP3xEW51x14UFr61TVnrd/V9ds2G05wqQG6sIEkGSqVwIMAG021uWJKtY8AAAAAElFTkSuQmCC") -} - -.fileList .ico-iso { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABH9JREFUeNrsnEtoE1EUhs9MJmlLUpu0tk20+CCotK5UEMSFCxWEuhARFFERbBcWFN1oQRTUjbpRFFRocVMRBRduCoJvQQTxsdGIFlGpNm1TW9qa1pZ5eM9NZ4w1jZlJ85xz4DLp5N7czpf/v+fMTVNB0zSgsB4iISCAOQ0p2ZOhUMjFDidY28va/GK56Pl1dVfYoSXV/hVz5lgDyOKk1+tt9fl8IElSUcDr6uqCSH///uqaGjAD0SrAfQhPFEVQVbVobOcPBKA3HJ4ViP9bA6sRXrFFicvFIaIS2Y+XKYnkEKKts/BsQLR9GZMuRKoD04RIANOESADThEgA04RIANOEKCTbzmL3wlowGCwqOJFIBEZGRkyNaWhoEKzeyhVdsHt78Hg8MfUIf7jM9Li7u5ssHB9Op9PYGIl3n9WNZdspUIeIoSjKP6qbSYkEMAFEBCTLMj+aBWd7gPzip6wcv1WXdwqU+z5BtPM8qD8Hsw5I9FSCu/EwSLXBpBDRytim2zgVgBlPImMP23MCjyuLzTt27+p/+zkcDt7MwsuKApWBWBng3nQAXEvXpDxu6OJO47Hv4A3T805+fA7Ru5dAGfyeUn8EiNDis3FeKBC02PpiBt5shJX5cPfdrALpVi4BRDMfY+RtFo63bbp2Nhu6+lIpriXSXHKIZOEMQ8xbBWbbtpbXTNIZAczt7SBlYVIgASQLZzgLkwLJwhQFeSs3PePmo63zGmAhrINkYTsU0qRAysKzEFqW/8o/S/NlHqAQm2Li7cOs8jPmEzJ7iRlfAyV/EORwF4w9usZb1hd5f7CwFeje2AJS3fLcZEg2L85f0AoUvbVQvvUYJREKAkgACSABpCCABJAAEkAKAkgACSABpMBI+m3Nr4fq6T+TsVh44X0a39aUSuxNT54gC2cycvKxZvXuc+Dw+qH30p7YL+ELQMX6JihrWGf0GQ89geEH7SAPhY1zJYtXQsWGJnAFlhrnoq87Yfh+G6i/ftoziYilHpi76yw4GMSec1vg26kNEOk4As7AEn5eD/eqRg5eYUCn96tpvszfBFsCdDI1Sb55TEXthoomPr/mysLzqDaEjArFcz9un/6r3wCDKJaVQ/naHfYEqE3BQGsiKD1Gn93iKpsMf+TqE0vLIfqq85/xCBPBulc2/jW+qNfA+EBAww/amMKaYd6RO8a6Jg/1cIj8XWYK0/smTJRT6yQqdqY+RQtQVxs2VJqLWzrAFYVt4PpRysLJImZPDweIFo1CzKYIEpMDPq+OjxrnEilMTyCoWltmYbTv9PULQSEQfe1Tf41ymInGo1LR9rkoZXIOcDz0lMOZy0qU+PrOt/kwX9PG3j3hYLAmRFBV244bsLEuxHGo0NFnN+1pYUwAAx1HWRmynVs2XoFY52GpwhMLU6E8GObZWk82+VBIJ91M+NK6WhHkCaZSAewZGmhSibrozAuHJQsrgeUvQFX4C9kRHl676q9/adnCWuWCjYqmPhb7PqwQxodttfGglVWoau2yN2rVovWWLUxRAFmYANo8fgswAF4ssuGz3PXWAAAAAElFTkSuQmCC") -} - -.fileList .ico-rar { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABGNJREFUeNrsnEtoE1EUhs+k07Q1qclUahMtvoJK05UtCCrUhQpCXblRBEFQFxaUdiMFQVA34qaioC6KG0EU3BYE6xNUEI0bqWjxRZWo0UpfaSOZGe+5adIkjdPMpJkkM+fAZSYzk3uZL/85/52bpoKqqkBhPByEgACWNEStk0NDQ062Oc3aIdZWWuWmVzY3X2Wbrnyv9yxdagwgizNer7dXkiQQRdES8IaHhyHy8+exxuXLQQ9EowAPIzyHwwGKolgm7Xx+P3wPhxcF4kI1sBHhWS1qnE4OEZXIXl4hEykhRFu78GJAtP00plCINA8sECIBLBAiASwQIgEsECIBLBCioLWcxZ6F1UAgYCk4kUgExsfHdb0nGAwKRh/lLBfs2R7cbndCPcIcl//tj4yMUAqnR3V1dWphJD37jC4s206BSYgYsizPU93/lEgAc0BEQPF4nG/1grM9QH7zs6mcvlRXdgqM//gAUwN9oEyOmg7I4W4AV2cPiE0BTYiYytiy0zgfgEU3keiD/pLA48pi40bvXVvwuqqqKt70wjNFgfKvxDTAtfs4ODdsyft9fy4dSO1LJ27qHvfv++cwdfcyyKPf8roeASK0dDcuCwWCmqgveuAtRhgZD1ff9SqQHuVyQNTzNUbZunB62haaznojqb58JtciaU4bIqVwkSGWrQLNTlvDNZN0RgBL+zhILkwKJICUwkV2YVIgpTBFRT7KZTtuOaZ1WQOshDpIKWyHiTQpkFx4EUI1+a/8TRqv+ACFxBCxNw9M5ZcaTyjuLRa9Boq+AMTDwxB9eJ0304u8L1DZCnTt6gKxubU0DsnGxfErWoEObxPU7z1FJkJBAAkgASSAFASQABJAAkhBAAkgASSAFBiav9b80t1C/5mMxeqLbwv4taZYY2968RilcDHDtK81a9a2QePBCznPTTy7BWOD/ZmfbK0bfCdusG09fL98EOJ/wqlz9dv2gWfH0Xn9KDMTMHa/H6ZeDVgPYDIiN05C7FMo9drV3glSZw8HlH7jda3bOTyEUhfsgImnt+f19fXszozX0p4e3lfsYygDuKVdGKEhJKd/Q8ZxV1snTA89Zu0J38+3L672dW32m8ZgyiYDYWKLMngzTE2itIKXACN9WR4g1jNM1cmM9O3gqkwo8DHfd7cvrELPziP8WkvXwFxGgiaSURd5+j6ZS83QANRv3c9A9zFAk6njzacH5/X1+865jGssbyJJR535EOLH0VRQkQgxu/bhuXQzyTYR/HCkPd3MRF6ZBrEsTITXvRXr+XZJcDv8Db/ncNIbHqtj57QCywDCx5ppmxqISsG6xdNB8nOzwLqXHXgMjUXLTOTZqYtgJxPhEKcnuLpcs0aRywSSx7TMJPlB1AbMm8ZoLiZ87t0sC/EYgyyAPUMFVaxR1px/UWVIgbK/9QUoMu/IjvDw3hVfy0vDLqw2rNolq8ojx493m4TpMVstPKh1HkVp2vhaWbZmh+EUpqigRzkCaNP4J8AArrWcCArY0yoAAAAASUVORK5CYII=") -} - -.fileList .ico-zip { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+hJREFUeNrsnF1IU2EYx5+zHTdkU7eZuuUocWA4r/QiqC68UCHQqxCKiAiqiwRD74RKyKu6UhIqULoJouhWIcjsAyqQsJtY5IgKq5krze8m56P3PWtruA/Px77P84fDmTvvu5fz2/95n+d958aIoggo9TIgAgSYU7GpLvp8PhM5DZLjDDlqi+Wma93uW+TUI7d9RXm5OoBEV20224DdbgeWZYsCnt/vh+Di4oWq6mpQAlEtwLMUnsFgAEEQiibsnC4XLAQCaYG42xxYReEVm8wmkwSROpH8eROTSA4h6joLpwOi7ssYrRCxDtQIEQFqhIgANUJEgBohIkCNEJlU21lkLSx6PJ6ighMMBmF1dVVRH6/Xy6hdyhWdyNoerFZr2D3Mfy7JHs/Pz2MIx6qkpCS6MRIbfWo3lnXnwAhEKp7n41yXzIkIMAFECojjOOmsFJzuAUo3/y+UY7fq8s6B3I+PsDE5DML6UtYBGawOsHT2A1vjSQmRhjI9doaxHIAZTyKb0+M5gSc5i4y7+fj2ru2MRqN0KIWXFQfyP8NlgOVoL5gaDsnut3zjZPSx/eI9xeNuz72GjUejwC99k9WeAqTQYrNxXjgQxPD8ogReOqRmPLr7rtSBuJRLAFHJxxh5m4Vjw1ZrOCtVxH1yimsWPZcaIoZwhiHmrQOzHbaq50z0GQLM7XIQszA6EAFiCGc4C6MDMYRRBbmU25lx8zGs8xpgIcyDGMJ6KKTRgZiF0yAxy//ln6XxMg+QCQ8RejedVX7R8ZjM3mLG50DW6QEu4IfNp3ekI+uTvNNT2A60dPQA627KTYYk49LxC9qBBlsNlB27hEkEhQARIAJEgCgEiAARIAJEIUAEiAARIIoq5bc1v/Q14i+TEe0fea/h25qsWd/0uBCGcCaVk481WbsLnL13k15feTIGay8fgHtwCjZmJ2F5YhjKjhyHirbzcW23A3Pwe2JEOusGILccgK9D7XHPV3ZfAXN9C2z5XiTtG9uPvhF7Tl0HR/dlWBg9re8sXNF+Dkq9rcRtIxJguW8EdShr3yvB1C1ACq7s8AlYe3WfuO851oFK50N7Vx+EPs3CytS44r6Wlk7ixO+yXVsUc2CsHGTeE7bW4NfDIVntaWJJlER0lYUjsnf1g8nVAItjPSD8WZfVJ1Hy0WUI07KEhh8tWXJVghQsQOo6WtPRhEHrPSykFcpc3xzNvu7B1rjrkeK54DcTPg8c5BkuRFzKgD4lgsiahbprM0ZVIcy7mmZA4KUX0iM8eu+Cs/GN6hAWHfs6eFF4ZvjxoZnZWtHVxoNYWiEINQfeCpV1bapDGFVAa2EEqFP9FWAAVhNMgyXjjfoAAAAASUVORK5CYII=") -} - -.fileList .ico-bt { - background-image: url("../img/ico/ico-bt.png") -} - -.fileList .ico-file { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAehJREFUeNrs3D9PwkAcxvFrKXUhCAMEAhsbL8nJxElfgYPR+CZ0MnHyJbEwMkDoQGAG6h2CNlIo16t/cvd9kktL2g73ye9a7gj14jgWJH88AAH8v4CDwSCUm3vZLmXr2NLpTrf7LDc3p55/Xq0ePBZkXPtYq9Vu6/W6CILACrzhcCii6fS60WwKHcS8gFcKz/d9sV6vrRl2rXZbTMbjQhD9jOMNhWdbzsJwg6gqUX58+klAa1MUorOARSE6DVgEovOApogAGiICaIgIoCEigIaIWXPhuNfrWYUTRZFYLBZa1/T7fS/vVM66yLm9qFQqH9Xjfbkc2h+NRgzhZMrl8ufCSHL05V3Wc64Cd4gqq9Vqr+oOVSKAKYgKaLlcbra6cM4Dbjq/HcrJpTpdSOe/xijEUqm0N4y/VyUVeCQ7QFWJafdCAE9EVGjJpzFDWDNq9V23AgFMQdT5GQPAtPntiQ8Q7oEZiFTgLyACaHrPhABAAAEEkAAIIIAAEgABBBBAAiCAAAJIAAQQQAAJgAACCCABEEAAASQAAggggARAAAEEkAAIIIAAEgABBNBywCj5bhXXsu17ZAL4MpvNhIuIqs+q7zKvx87L+sP1w3w+D2W7kPstxwwnsr3JdnfsJF4FbxgAAfzbvAswAK/3kejP2oZLAAAAAElFTkSuQmCC") -} - -.fileList .ico-svg,.ico-css { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAehJREFUeNrs3D9PwkAcxvFrKXUhCAMEAhsbL8nJxElfgYPR+CZ0MnHyJbEwMkDoQGAG6h2CNlIo16t/cvd9kktL2g73ye9a7gj14jgWJH88AAH8v4CDwSCUm3vZLmXr2NLpTrf7LDc3p55/Xq0ePBZkXPtYq9Vu6/W6CILACrzhcCii6fS60WwKHcS8gFcKz/d9sV6vrRl2rXZbTMbjQhD9jOMNhWdbzsJwg6gqUX58+klAa1MUorOARSE6DVgEovOApogAGiICaIgIoCEigIaIWXPhuNfrWYUTRZFYLBZa1/T7fS/vVM66yLm9qFQqH9Xjfbkc2h+NRgzhZMrl8ufCSHL05V3Wc64Cd4gqq9Vqr+oOVSKAKYgKaLlcbra6cM4Dbjq/HcrJpTpdSOe/xijEUqm0N4y/VyUVeCQ7QFWJafdCAE9EVGjJpzFDWDNq9V23AgFMQdT5GQPAtPntiQ8Q7oEZiFTgLyACaHrPhABAAAEEkAAIIIAAEgABBBBAAiCAAAJIAAQQQAAJgAACCCABEEAAASQAAggggARAAAEEkAAIIIAAEgABBNBywCj5bhXXsu17ZAL4MpvNhIuIqs+q7zKvx87L+sP1w3w+D2W7kPstxwwnsr3JdnfsJF4FbxgAAfzbvAswAK/3kejP2oZLAAAAAElFTkSuQmCC") -} - -.fileList .ico-apk { - background-image: url("../img/ico/ico-apk.png") -} - -.ico-cmd { - background: url("../img/soft_ico/ico-cmd.png") no-repeat center center; - height: 18px; - width: 16px; - display: block -} - -.btn:hover .ico-cmd { - background: url("../img/soft_ico/ico-cmd-hover.png") no-repeat center center; - height: 18px; - width: 16px; - display: block -} - -.fileUploadDiv { - padding: 10px -} - -#file_input { - display: none -} - -#up_box { - background-color: #fafbfe; - border: #e4e5e9 1px solid; - margin: 0; - padding: 10px; - clear: both; - height: 160px; - overflow: auto; -} - -#up_box li { - list-style-type: none; - height: 30px; - line-height: 30px; - font-size: 12px; - width: 100%; - margin: 0 0 5px; - padding: 0 -} - -#up_box li .filename { - display: inline-block; - height: 30px; - margin-right: 20px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: 240px -} - -#up_box li .filesize { - color: #999; - display: inline-block; - height: 30px; - overflow: hidden -} - -.fileUploadDiv button { - float: left; - border-radius: 3px; - border-style: solid; - border-width: 1px; - width: 75px; - height: 30px; - color: #fff; - margin-bottom: 10px; - cursor: pointer -} - -#up { - background-color: #5cb85c; - border-color: #4cae4c; - color: #fff; - text-align: center; - margin-left: 10px; - position: absolute; - bottom: 0; - right: 10px -} - -#filesClose { - position: absolute; - bottom: 0; - right: 95px; - background-color: #cbcbcb; - border-color: #cbcbcb; - color: #fff -} - -#opt { - background-color: #4592f0; - border-color: #367fa9 -} - -#up_box li em { - font-style: normal; - color: #06F; - float: right; - margin-right: 10px -} - -.cancel { - float: right; - cursor: pointer; - color: #c00 -} - -#textBody { - min-height: 450px; - _height: 450px -} - -#filesClose:hover { - background-color: #c9302c; - border-color: #ac2925; - color: #fff -} - -#up:hover { - background-color: #449d44; - border-color: #398439 -} - -#totalProgress progress { - width: 180px; - height: 10px; - border: 1px solid #ccc; - background-color: #e6e6e6; - color: #0064b4; - opacity: .9 -} - -#totalProgress p { - color: #666 -} - -.setchmod fieldset { - margin-left: 15px; - margin-bottom: 15px; - border: 1px solid #ccc; - float: left; - width: 114px; - padding-bottom: 10px; - border-radius: 3px -} - -.setchmod legend { - padding: 3px; - border: 0; - width: auto; - margin: 0 6px; - font-size: 14px -} - -.setchmod fieldset p { - margin-left: 10px -} - -.setchmod fieldset p input { - position: relative; - top: 2px; - margin-right: 5px -} - -.setchmodnum { - clear: both; - margin: 0 15px -10px -} - -.setchmodnum #access, -#chown { - width: 60px; - margin-right: 5px; - height: 28px -} - -.setchmodnum span { - margin-left: 10px -} - -#chown { - margin-left: 5px -} - -.shellcode { - margin-bottom: 5px; - height: 490px; - overflow: auto -} - -.shellcode pre { - background-color: #111; - color: white; - padding: 5px 10px; - border: 0 none; - border-radius: 0; - min-height: 490px; - margin: 0 -} - -#mExec { - width: 89%; - margin-top: 0; - float: left -} - -.editmenu { - text-align: right -} - -#filesBody { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none -} - -.folderBox input { - display: none -} - -.fileList>div:hover, -.fileList div.ui-selecting { - background: #f6f8fd; - border-color: #d3dfec -} - -.fileList div.active, -.fileList div.focus, -.fileList div.ui-selected { - background: #eff3f9 none repeat scroll 0 0; - border-color: #bcccde -} - -.fileList .file { - background: rgba(0, 0, 0, 0) none repeat scroll 0 0; - border: 1px solid #fff; - border-radius: 2px; - display: inline-block; - height: 136px; - margin: 0 5px 10px 0; - text-align: center; - width: 112px -} - -.fileList div.file { - transition: all .2s ease 0s -} - -.fileList .file .titleBox { - color: #595c5f; - display: block; - height: 23px; - line-height: 23px; - overflow: hidden; - text-align: center; - text-overflow: ellipsis; - width: 100px; - margin-left: 5px -} - -#DirPathPlace { - display: none; - padding: 0 -} - -#DirPathPlace>input { - border: 1px solid #ccc; - height: 28px; - line-height: 28px; - padding: 0 5px; - width: 280px -} - -#DirPathPlace>input:active, -#DirPathPlace>input:focus { - border: #66afe9 1px solid; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) -} - -#PathPlaceBtn { - background-color: #f3f3f3; - border: 1px solid #ccc; - height: 28px; - line-height: 26px; - padding: 0; - width: 290px; - cursor: text; - overflow: hidden -} - -#PathPlaceBtn ul { - display: inline-block; - height: 26px; - width: auto; - position: relative -} - -#PathPlaceBtn li { - background: url("../img/ico/ico-ltr.png") no-repeat right center; - padding-left: 10px; - padding-right: 18px; - float: left; - height: 26px; - line-height: 26px -} - -#PathPlaceBtn li a { - display: inline-block; - cursor: pointer; - height: 26px; - line-height: 26px -} - -.backBtn { - color: #666; - border-radius: 0; - height: 28px; - line-height: 8px; - margin-right: -1px; - margin-top: -1px -} - -#tipTools { - background: #fff url(/static/img/ico_line.png) repeat-x 0 55px; - height: 110px; - border-bottom: #ccc 1px solid; - position: absolute; - top: 0; - left: 0; - z-index: 999; -} - -.re-head { - border-bottom: 1px solid #ccc; - float: left; - padding: 15px 20px; - width: 100% -} - -.re-con { - float: left; - height: 500px; - width: 100% -} - -.re-con-menu { - background-color: #f0f0f1; - float: left; - height: 100%; - width: 120px -} - -.re-con-menu p { - cursor: pointer; - line-height: 40px; - padding-left: 30px; - position: relative -} - -.re-con-menu p.on { - background-color: #fff -} - -.re-con-con { - height: 500px; - margin-left: 120px; - overflow: auto -} - -@media only screen and (max-width:768px) { - .sys-i-c-box:nth-of-type(3){ - margin-top: 20px; - } -} -@media only screen and (max-width:990px) { - .pr8 { - padding-right: 0 - } - .pl7 { - padding-left: 0; - margin-top: 15px - } -} -@media only screen and (max-width:1200px) { - .btvipbox{ - margin-top: 20px; - } -} - -.bt-ico-ask { - border: 1px solid #fb7d00; - border-radius: 8px; - color: #fb7d00; - cursor: help; - display: inline-block; - font-family: arial; - font-size: 11px; - font-style: normal; - height: 16px; - line-height: 16px; - margin-left: 5px; - text-align: center; - width: 16px -} - -.bt-ico-ask:hover { - background-color: #fb7d00; - color: #fff -} - -#RecycleBody .tname { - line-height: 30px -} - -.menu-sub { - border-bottom: 1px solid #ccc; - height: 40px; - line-height: 30px; - margin-bottom: 15px -} - -.menu-sub span { - display: inline-block; - font-size: 13px; - height: 40px; - padding: 0 25px; - cursor: pointer -} - -.menu-sub .on { - border-bottom: 2px solid #20a53a; - color: #20a53a; - font-weight: bold -} - -.bt-progress { - background-color: #e2e2e2; - border-radius: 8px; - height: 16px; - line-height: 16px; - position: relative; - margin: 5px 0 -} - -.bt-progress-bar { - background-color: #5ab76c; - border-radius: 8px; - height: 16px; - max-width: 100%; - position: absolute; - text-align: right; - transition: all .3s ease 0s; - width: 0 -} - -.bt-progress-text { - font-size: 12px; - color: #fff; - padding: 0 10px; - position: static -} -.conf_p{ - overflow: hidden; -} -.conf_p p{ - margin-bottom: 1px; -} -.conf_p span{ - display: inline-block; - margin-right: 10px; - width: 140px; - text-align: right; -} -.btvipbox{ - border: #ddd 1px solid; -} -.btvip{ - border-right: #ddd 1px solid; - float: left; - width: 40%; - height: 108px; - text-align: center; -} -.btvip-r{ - width: 60%; - float: right; - overflow: hidden; -} -.btvip .t2{ - font-size: 16px; - padding: 10px 0 5px; - float: left; - width: 100%; -} -.btvip p{ - float: left; - width: 100%; - margin-bottom: 8px; - font-size: 14px; -} -.btvip p .price{ - margin-right: 3px; -} -.btvipinfo{ - width: 200px; - float: left; - padding:5px 10px; - line-height: 32px; -} -/*付费插件*/ -.libLogin{ - width: 340px; - margin: 0 auto 20px; -} -.libLogin .bt-input-text { - width: 300px; - height: 34px; - line-height: 34px; -} -.libLogin .line{ - margin-bottom: 10px; -} -.libLogin .login-button,.shuoming .login-button{ - width: 300px; - height: 36px; - background: #20a53a; - background: linear-gradient(#20a53a,#189d32); - box-shadow: inset 0 1px 2px #30ad42; - color: #fff; - text-shadow: #00851a 0 -1px 0; - border: 1px solid #20a53a; - text-align: center; - font-size: 16px; - color: #fff; - border-radius: 3px; - cursor: pointer; -} -.libLogin .login-button:hover,.shuoming .login-button:hover{background:#10952a;background:linear-gradient(#10952a,#088d22);border:1px solid #10952a} -.shuoming{ - line-height: 24px; -} -.shuoming .login-button{ - width: 100px; - height: 30px; - font-size: 14px; - margin: 15px; -} -.sm-content{ - border: #ccc 1px solid; - padding: 10px; - height: 200px; - background-color: #f9f9f9; - overflow: auto; -} -.libPay .btn-group .btn{ - width: 80px; -} -.libPay .btn-group .btn em{ - position: absolute; - right: -2px; - top: -9px; - width: 33px; - height: 19px; - background: #ff7300; - border-radius: 2px; - font-size: 10px; - line-height: 19px; - color: #fff; - transform: scale(.9, .8); - font-style: normal; -} -.libPay .price{ - font-size: 28px; - color: #FF6600; - margin-right: 3px; - line-height: 30px; -} -.libPay .sprice{ - font-size: 14px; - display: inline-block; - line-height: 30px; - margin-right: 3px; -} -.wxPay{ - width: 160px; - margin: 10px auto 20px; -} -.wxPay .ewm canvas{ - border: #ccc 1px solid; - padding: 6px; - width: 160px; -} -.wx-pic{ - width: 160px; - height: 36px; - background: url(/static/images/weixin.png) no-repeat top center; - margin-bottom: 10px; -} -.wx-pay-info{ - width: 160px; - height: 30px; - background-color: #00C800; - color: #fff; - font-size: 14px; - line-height: 30px; - text-align: center; - margin-top: 10px; -} -.color-red{ - color:red; -} -.color-green{ - color:green; -} -.btpro{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADsAAAAUCAMAAAAEAbViAAAAA3NCSVQICAjb4U/gAAABXFBMVEX///+7jAC7jAC7jAC7jAC7jAC7jAC7jAC7jAC7jAC7jAC7jAC7jAC7jAC7jAD////+/fv+/fr8+vX8+vP69uv38eD28N3179v07NXz69Lz6tHz6c/x6Mzv48Pu48Hr3rX74jnq27D54Djp2av43zfo16fo2Kj33TX33Tbm1aLl1Z/12Tbl057z1zLk0Znj0Jfx1DDizpPu0DPv0S7fyIbqzCroyC/cxH3oySjawXXZv3DkwyTYvWzivyzXu2jWumXevCDUtVzctinathzSslXSs1bRsVHRsVLWsBjVrSbNqkPTrBXLqD3PpCLJozTOphHIozLIojDIoS/HoCzKoQ7GninGnyrFnifJnB/FnCTFnSbEmiDEmyLFmQnCmBvBlxjBlhbBlRXAlRPCkhy/kxDAkwW+kQq9kAi9jwa9jwK9jQC8jgS8jQO8ihm7jAC2gRWweBKpbw+jZgydXgnhk9RtAAAAdHRSTlMAESIzRFVmd4iZqrvM3e7//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wol0KMAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDUvMzEvMTikOWwBAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAg5JREFUOI2dlGlz0zAQhhUHJ45jST2gXKUVR4BijmKKCwX1CKcKLoVQcCmFYgcSjqiy8/9nWCtOJvlEwusZe3Q80rurlRFCyLDsSi7bMtAkKlSlU602W61o+3VDvrnOxlTGliUxkdFcfRjN0aWm3ByXBdgglaqN7N2pmb2t6edba0e3xmctBxWxjecoPfPh2afTtPGkN+J6/2SLxC6ajrQvLC4uPJXvLs6/aC7rkZD35/Awb8RMMc5FGAc9FktMiJS42W5HX+SP6Nv+n13GlFJdlWmEVfCkYcD9fF9MTNO0iHX15o3aRnTncq0W7WcDQvQmeEHO1rlm1ZBnw6lAvrBJQY+3s9fOPSbCMA21BEvdHnvssa7qCm3Hz3N1gpQRsu1ZSk8drFC60nrAmM/DmGv5LAg06yVM75mF0fFyFpWkiUrEoHTx6wKt/d7U/R2hUZjkppoFBxCrYCrkfd9ZcVSkUcAlOrW+Q8+3X13S/SIzHHeFXiZjlctYwiFevwNrDdgCdsD09OzbtZmPh9d6/QFQbiJGzsgFksM5Zcv0WVSUlumcPHdwdv3Xcp5DF5KVowMWvioW7nHKh1gIuYznlxq35aOhshFJCBvEaqC4ztKkntQ9FQ+xEDK5u3r08+Vo0dVHm3Ayvu9nhr1htuDIvc/y/RU2ifqX2MBSHo59g0ZZZJLv9/8ThXyZE/1tQH8B/ly2lqVFKXEAAAAASUVORK5CYII="); - background-repeat:no-repeat; - padding:2px 0 2px 64px; - margin-right:5px; -} -.btpro-gray{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADsAAAAUCAMAAAAEAbViAAAAA3NCSVQICAjb4U/gAAAA51BMVEX////6+vr4+Pj29vbx8fHu7u7r6+vp6eno6Ojl5eXk5OTj4+Pi4uLf39/e3t7d3d3b29vU1NTT09PNzc3Ly8vIyMjFxcXDw8PAwMC+vr67u7u6urq5ubm4uLi3t7e2tra0tLSzs7OysrKxsbGvr6+srKyrq6upqamoqKikpKSjo6OgoKCfn5+enp6dnZ2cnJybm5uampqZmZmYmJiXl5eWlpaVlZWTk5OSkpKQkJCPj4+NjY2MjIyLi4uKioqIiIiHh4eGhoaFhYWDg4OCgoKBgYGAgIB/f39+fn59fX14eHh0dHT///9HdDDXAAAATXRSTlP/////////////////////////////////////////////////////////////////////////////////////////////////////ANNqEYAAAAAJcEhZcwAADpwAAA6cAQeUU90AAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAFnRFWHRDcmVhdGlvbiBUaW1lADA1LzMxLzE4pDlsAQAAAeZJREFUOI2VlH1XmzAUxjNbZutxL2hnXeiGA1kiNdspbJlEC9FmLtD7/T/PkkC78tfw4Rw4IfeXPDf3AgIALyBRJxJ4MFgIYLTU+XJZbTYluVnp4jMeKMvOtfTBq84/lAAnlV4OZTECT0ZLAoQB5AuIFhcPX4azQQ5TQYQxgLKrnwhWq3YmjP/LTiWZ+rkmr8bjoyv93UNR1UKc7mIo7wYFVphSxousZYUWUmotKqVKoZ/Kkj8zjJVSW2XVY5W5Gp7RpNtXSN/3Axkcn55OPpZvX08mJbcTjLUBcdaxKXWsOvDs5ZGpsPBtwebE3m6vMeO84U4MN2HLPsZ4q7bM2Um6s3on5wDEUnB3BnC2IRgnlBfUKcFZ5ti4xm5Pm8Y67liYaR9m0vTT+P4IJr/b+q6ZQ01Q2DjWODC5Mqw43fk2LETaG4kZwOUtQoov3HtmDRdb5paxrAoxrqnJN1lT/o8didyZ/naBflRdS2aGCmvWq1FoSGrqZJfZsTDVgZ8DukOXf3YNEZrD6tA9a56qYOFjQw9Yk/JcoJPVG31z0Das5maDQu1VpLip0zqNVXHAmpTl+/OH57zfdGl/aCqTJIk1HB+yo1znv/T9J/wSdazpDq2rwV9QnwVfbr6+DIU9CzN/+N+m1V+TNr+ixpmAWwAAAABJRU5ErkJggg=="); - background-repeat:no-repeat; - padding:2px 0 2px 64px; - margin-right:5px; -} - -/*插件付费*/ -.wx-pay-ico{ - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAMAAAAR8Wy4AAAAA3NCSVQICAjb4U/gAAAAtFBMVEUAzABM2Ezx/PHB8sEazhqf6p+B5IE61TrU9tRm3mYq0Sqz77P///8Lygvj+ePI88iP549U2lR54nlF10Wm7KY31Dcj0CPS9dL1/fW58Lnr++sx0zHY99jD8sMSzBJc3FxA1kCU6JQfzx+t7a1s32zm+uaj66O377cPyw+q7ar////c99zH88fL9Mu177VG10Yk0CSR55Eczxw81TzX9tdp32k41Ti78Lsy0zLF88UhzyHB88EyB+unAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFnRFWHRDcmVhdGlvbiBUaW1lADA1LzI5LzE4XO1tXgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAACTSURBVAiZRU8JDsIwDHNXGMcCjHNjMBhnQTAVcQjE/x9G0qHOUtLYal0HJEiiXaQzN4JrU/TAmL2WTgjG8GiyEHxqvj2yEHra0pdYEW7AgwtqTnkBXMF+w35n3aanlWtnEZATpfvq3QRd7jaNV3+jAWI5Gt6Yc0zrb3EqJZjy/Ktd9DdwF7oYZdUuoU0OxpjS7UY/XkQJtfApswsAAAAASUVORK5CYII=") no-repeat; - display: inline-block; - width: 16px; - height: 14px; -} -.libPay-item{ - margin-bottom: 30px; -} -.libPay-item .li-tit{ - margin-bottom: 10px; -} -.libPay-item .li-c-item li{ - font-size: 0; - margin-bottom:20px; - border: #ddd 1px solid; - border-radius: 4px; - height: 42px; - line-height: 40px; - padding: 0 20px; - cursor: pointer; - box-sizing: border-box; - transition: border-color .1s ease-in,color .1s ease-in; -} -.libPay-item .li-c-item li:hover{ - border: #20a53a 1px solid; - color: #20A53A; -} -.libPay-item .li-c-item li.active,.libPay-item .li-c-item li.active:hover{ - border: #20a53a 2px solid; - color: #20A53A; - padding: 0 19px; - line-height: 38px; -} -.libPay-item .li-c-item li .item-name{ - font-size: 14px; - vertical-align: middle; -} -.libPay-item .li-c-item li .item-info{ - font-size: 14px; - vertical-align: middle; -} -.pay-btn-group{ - position: relative; - display: inline-block; - vertical-align: middle; -} -.pay-btn-group>.pay-cycle-btn { - position: relative; - float: left; -} -.pay-cycle-btn { - font-size: 0; - width: 106px; - height: 40px; - line-height: 38px; - display: block; - margin-bottom: -1px; - text-align: center; - vertical-align: middle; - border: 1px solid #ddd; - box-sizing: border-box; - margin-left: -1px; - transition: border-color .1s ease-in,color .1s ease-in; -} -.pay-cycle-btn span{ - font-size: 14px; - line-height: 25px; - vertical-align: middle; -} -.pay-btn-group>.pay-cycle-btn:first-child { - margin-left: 0; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} -.pay-btn-group>.pay-cycle-btn:nth-of-type(5n+1){ - margin-left: 0; -} -.pay-btn-group>.pay-cycle-btn:last-child { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} -.pay-btn-group .active,.li-c-item .active{ - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAMAAADzapwJAAAAA3NCSVQICAjb4U/gAAAAclBMVEX///8fpTofpTogpTogpTogpTrw+fLt+O/i8+be8uLb8d/T7djP7NXR7NbE58u548G34sCr3bWU1KGT05+FzpN+y413yIZ0x4RxxoJwxYFnwnliv3RWu2pUumhMt2FGtVxDs1k6sFE4r1Avq0cjpjwgpTpRW86VAAAAJnRSTlMAEXd3iO7//////////////////////////////////////////0c8mUAAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDUvMjkvMThc7W1eAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAHhJREFUGJVtzskSgkAMBFB2AUcR2QTcEPr/fxFmmFiW0znk8CrVac9jE1CNYqoAV8JaXTbq8K7/bBVcwRVcwRVcwbRTIDqcWrjaJ3cJ0VrdjDaHh2Sb2/F43XadPuWlTfjkJersLU2+ufNFnSdb0P/5thQv6R2CzQo9Oh+AlBF9bAAAAABJRU5ErkJggg==") no-repeat right bottom; -} -.pay-btn-group .pay-cycle-btn:hover{ - border: 1px solid #20a53a; - color: #20a53a; - z-index: 2; - cursor: pointer; -} -.pay-btn-group .active,.pay-btn-group .active:hover{ - border: 2px solid #20a53a; - color: #20a53a; - z-index: 2; - cursor: pointer; -} -.pay-btn-group .active span{ - line-height: 22px; -} -.pay-cycle-btn em { - position: absolute; - right: -2px; - top: -9px; - width: 33px; - height: 19px; - background: #ff7300; - border-radius: 2px; - font-size: 10px; - line-height: 19px; - color: #fff; - transform: scale(.9, .8); - font-style: normal; -} -.pay-btn-group .active em{ - right: -3px; - top: -10px; -} -.paymethod{ - margin: 0 auto; - width: 160px; -} -.paymethod .pay-wx{ - height:175px; -} -.paymethod .pay-wx canvas { - width: 160px; - height: 160px; - border: #ddd 1px solid; - margin: 6px auto; - padding: 5px; - border-bottom-color: #00c800; -} -.lib-price-box{ - padding-bottom: 5px; -} -.price-txt{ - color:#FF7301 -} -.sale-price{ - font-size: 20px; - margin-right: 5px; -} -.lib-price-name,.price-txt{ - margin-right: 15px; -} -/*input样式*/ -.bt-label{ - margin:20px 20px 0 0; - display:inline-block; - font-weight:normal -} -.bt-radio,.bt-checked{ - display:none -} -.bt-radioinput{ - background-color:#fff; - border:1px solid #ccc; - border-radius:100%; - display:inline-block; - height:16px; - margin-right:10px; - margin-top:-1px; - vertical-align:middle; - width:16px; - line-height:1 -} -.bt-radio:checked + .bt-radioinput,.bt-checked:checked + .bt-radioinput{ - border:1px solid #20a53a; - background-color: #20A53A; -} -.bt-radio:checked + .bt-radioinput:after{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAKBAMAAAB293L0AAAAA3NCSVQICAjb4U/gAAAAKlBMVEX///////////////////////////////////////////////////////9q+00tAAAADnRSTlMAETNEVWZ3iJmqu93u/wlUiAQAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDYvMDIvMTjAxKOiAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAADpJREFUCJljYACDTAjFcgRC+xpAuFcYGCqAtK0DA4P2BAbmq0A2404G3QSQpPTi62BFjGsTIJpEQQQATscJDpRQCfQAAAAASUVORK5CYII="); - background-repeat: no-repeat; - border-radius:100%; - content:""; - display:inline-block; - height:12px; - width:12px; - margin: 1px; -} -.bt-checkbox.bt-radioinput,.bt-radio:checked + .bt-checkbox.bt-radioinput:after{ - border-radius:0 -} -.loading{ - background-image: url("data:image/gif;base64,R0lGODlhJQAlAJECAL3L2AYrTv///wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgACACwAAAAAJQAlAAACi5SPqcvtDyGYIFpF690i8xUw3qJBwUlSadmcLqYmGQu6KDIeM13beGzYWWy3DlB4IYaMk+Dso2RWkFCfLPcRvFbZxFLUDTt21BW56TyjRep1e20+i+eYMR145W2eefj+6VFmgTQi+ECVY8iGxcg35phGo/iDFwlTyXWphwlm1imGRdcnuqhHeop6UAAAIfkEBQoAAgAsEAACAAQACwAAAgWMj6nLXAAh+QQFCgACACwVAAUACgALAAACFZQvgRi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwXABEADAADAAACBYyPqcsFACH5BAUKAAIALBUAFQAKAAsAAAITlGKZwWoMHYxqtmplxlNT7ixGAQAh+QQFCgACACwQABgABAALAAACBYyPqctcACH5BAUKAAIALAUAFQAKAAsAAAIVlC+BGL3Z3IlxUmUuhtR2LzHhsiEFACH5BAUKAAIALAEAEQAMAAMAAAIFjI+pywUAIfkEBQoAAgAsBQAFAAoACwAAAhOUYJnAagwdjGq2amXGU1PuLEYBACH5BAUKAAIALBAAAgAEAAsAAAIFhI+py1wAIfkEBQoAAgAsFQAFAAoACwAAAhWUL4AIvdnciXFSZS6G1HYvMeGyIQUAIfkEBQoAAgAsFwARAAwAAwAAAgWEj6nLBQAh+QQFCgACACwVABUACgALAAACE5RgmcBqDB2MarZqZcZTU+4sRgEAIfkEBQoAAgAsEAAYAAQACwAAAgWEj6nLXAAh+QQFCgACACwFABUACgALAAACFZQvgAi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwBABEADAADAAACBYSPqcsFADs="); - background-repeat:no-repeat; - background-position:62px 41px; - border: #ddd 1px solid; - width: 160px; - height: 160px; - padding:5px; - background-color: #787878; - color: #fff; - display: inline-block; - margin: 6px auto; - line-height: 200px; - font-size: 14px; - text-align:center; -} - -.cloading { - background-image: url("data:image/gif;base64,R0lGODlhJQAlAJECAL3L2AYrTv///wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgACACwAAAAAJQAlAAACi5SPqcvtDyGYIFpF690i8xUw3qJBwUlSadmcLqYmGQu6KDIeM13beGzYWWy3DlB4IYaMk+Dso2RWkFCfLPcRvFbZxFLUDTt21BW56TyjRep1e20+i+eYMR145W2eefj+6VFmgTQi+ECVY8iGxcg35phGo/iDFwlTyXWphwlm1imGRdcnuqhHeop6UAAAIfkEBQoAAgAsEAACAAQACwAAAgWMj6nLXAAh+QQFCgACACwVAAUACgALAAACFZQvgRi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwXABEADAADAAACBYyPqcsFACH5BAUKAAIALBUAFQAKAAsAAAITlGKZwWoMHYxqtmplxlNT7ixGAQAh+QQFCgACACwQABgABAALAAACBYyPqctcACH5BAUKAAIALAUAFQAKAAsAAAIVlC+BGL3Z3IlxUmUuhtR2LzHhsiEFACH5BAUKAAIALAEAEQAMAAMAAAIFjI+pywUAIfkEBQoAAgAsBQAFAAoACwAAAhOUYJnAagwdjGq2amXGU1PuLEYBACH5BAUKAAIALBAAAgAEAAsAAAIFhI+py1wAIfkEBQoAAgAsFQAFAAoACwAAAhWUL4AIvdnciXFSZS6G1HYvMeGyIQUAIfkEBQoAAgAsFwARAAwAAwAAAgWEj6nLBQAh+QQFCgACACwVABUACgALAAACE5RgmcBqDB2MarZqZcZTU+4sRgEAIfkEBQoAAgAsEAAYAAQACwAAAgWEj6nLXAAh+QQFCgACACwFABUACgALAAACFZQvgAi92dyJcVJlLobUdi8x4bIhBQAh+QQFCgACACwBABEADAADAAACBYSPqcsFADs="); - background-repeat: no-repeat; - background-position: center 45px; - height: 150px; - width: 100%; - line-height: 210px; - text-align: center; - font-size: 14px; -} - -/*重启*/ -.rebt-con { - height: 106px; - padding: 15px; - width: 330px; -} -.rebt-li { - float: left; - line-height: 40px; - margin: 15px; - text-align: center; - width: 120px; -} -.rebt-li a { - display: block; - background: #eee; - border: #ddd 1px solid; -} -.rebt-li a:hover{ - background-color:#20a53a; - color:#fff; -} -/*绑定宝塔微信*/ -.bind-weixin{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAASBAMAAACtCzMeAAAAA3NCSVQICAjb4U/gAAAAKlBMVEX19fXr6+vg4ODW1tbMzMzCwsK4uLitra2jo6OZmZmPj4+FhYV6enr////oR1GpAAAADnRSTlP/////////////////AEXA3MgAAAAJcEhZcwAADpwAAA6cAQeUU90AAAAWdEVYdENyZWF0aW9uIFRpbWUAMDgvMTYvMTgi+WYYAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAK9JREFUCJljuHuXyS1I4C4IMNy9MiNYpF0XzL5Qznv37uUpYPbVWBDF4Qtii9+N8b2y5WIRiB1810L3UuHdEBC7FqTkYlhnqyyUbcJ798rSuwxBQOYVkE5OX6Deu3dVedMulF1oYLgCNDPpbujd1LtJDEDqbhDYCc4Md9WUBIrB7ESGu15pLiUg5oUAhrvKd+9etQWy2WUZwEJAPUCbwey7zCHu04ug7LsMDExL7gIAh3d+c1Jd4JYAAAAASUVORK5CYII="); - background-repeat: no-repeat; - padding-left:26px; - margin-right:30px; -} -.bind-user{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASBAMAAACk4JNkAAAAA3NCSVQICAjb4U/gAAAAMFBMVEX19fXr6+vg4ODW1tbMzMzCwsK4uLitra2jo6OZmZmPj4+FhYV6enpwcHBmZmb///+wFcT2AAAAEHRSTlP///////////////////8A4CNdGQAAAAlwSFlzAAAOnAAADpwBB5RT3QAAABZ0RVh0Q3JlYXRpb24gVGltZQAwOC8xNi8xOCL5ZhgAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAh0lEQVQImWP4//+/2KxiIMnw//+X2+1rCsCsuP7/n26CWB+PA6X04oGsbweBrJ/rgawf64GsbxtQWBDZ+TAdNv5gUxL+s74EmfJpz6mOtWdAYnYTNFZP/vb0P8OHU///f/j/P0+f4duF/yDwaz4Ddz2Y9ekQg448mPV/G4M3hPG/g8ESygoHALeIhpF8DGw6AAAAAElFTkSuQmCC"); - background-repeat: no-repeat; - padding-left:24px; - margin-right:20px; -} -/*系统图标*/ -.ico-linux{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAATBAMAAACEi/vCAAAAA3NCSVQICAjb4U/gAAAAMFBMVEX///9mZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmambAcKAAAAEHRSTlMAESIzRFVmd4iZqrvM3e7/dpUBFQAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABZ0RVh0Q3JlYXRpb24gVGltZQAwOC8xNi8xOCL5ZhgAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAmUlEQVQImWNgYGCQ/GvAAAHz/1+CMBjPvvoOYTFVLXkNZZ2u+isAZjE/4vyvAGaxlgbnJ4BZHAoM3A/BLA8GBpafYNYiIL4PMpt1A5DYPwFIcDWArDkAJPgWAInzF4BE9jMGBvb/QM2SBfEGDFy7UhkY3Bk4CxisGdgCGMwYGI97vGKwSmAQC1G8e+aN2WagDuP0IEGbawIMAPDJKG706GgtAAAAAElFTkSuQmCC"); - background-repeat:no-repeat; - padding-left: 20px; -} -.ico-windows{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAA3NCSVQICAjb4U/gAAAALVBMVEX///+ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZny8jBSAAAAD3RSTlMAESIzRFVmd4iZqrvM7v/Y8bBbAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFnRFWHRDcmVhdGlvbiBUaW1lADA4LzE2LzE4IvlmGAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAABGSURBVAiZY2AAAkbTqrMMDEJuHvfevXvHcPbdu+53IMY7Ihi7d+9O7QACBiB2BTOAol0wNbgY59696wQzGFQqw8GWwpwBAGURWs03JAEWAAAAAElFTkSuQmCC"); - background-repeat:no-repeat; - background-position: 0 2px; - padding-left: 20px; -} -.ico-centos{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAPCAMAAADjyg5GAAAAA3NCSVQICAjb4U/gAAAASFBMVEX///9mZmZmZmZiYmJaWlpYWFhmZmZiYmJgYGBmZmZgYGBmZmZiYmJmZmZiYmJmZmZiYmJmZmZmZmZmZmZmZmZmZmZmZmZmZmbwNFvuAAAAGHRSTlMAESIiIiIzMzNERFVVZmZ3d4iZqrvM3f93YyfbAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFnRFWHRDcmVhdGlvbiBUaW1lADA4LzE3LzE4mkUBfQAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAB6SURBVAiZVY5ZEsIwDEMdUpYQghIv9f1vWpNCp/hH1oz1LKI5if5G5ezuzLff7ibO7KK+H2LaBp1WDMLcYDNf3A31CXMvRCy995rSK0SExhjAkh/XEK5E2YOCN5r75RNGUJgtZJLXoMQjw/ot2LR3baJHSbN8Ll3LrhtQxAfrvVHLpQAAAABJRU5ErkJggg=="); - background-repeat:no-repeat; - padding-left: 20px; -} -.ico-ubuntu{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAAA3NCSVQICAjb4U/gAAAAOVBMVEX///9mZmZhYWFmZmZiYmJmZmZiYmJmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZVQ1Z+AAAAE3RSTlMAEREiIjMzRFVmd4iZqrvM3e7/67N/KAAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABZ0RVh0Q3JlYXRpb24gVGltZQAwOC8xNy8xOJpFAX0AAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAbElEQVQImU2OWQ6AMAhEcW3Z2jL3P6xWjHE+CMPwCESfDOXtFmutAp6OtwC0jUwZVlWHaaaBniyYVq6B/Rn3G3aEjDxRuPwtN6NFzkB97IAlE3ddHTimPUKkd3TlhLb5huvLzAWWE2j0k0elCynoBUe/zXGJAAAAAElFTkSuQmCC"); - background-repeat:no-repeat; - padding-left: 20px; -} -.ico-debian{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAA3NCSVQICAjb4U/gAAAAPFBMVEX///9paWlmZmZgYGBdXV1mZmZgYGBmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYdQ+x6AAAAFHRSTlMAERERESIiM0RVZneImaq7zN3u/ynguF0AAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDgvMTcvMTiaRQF9AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAHJJREFUCJlVjlESwjAIRLFFIRhCaO5/VyHijN2vt+zCABBq87IhT/hK1+zdfOl2Y71r2rPp1UKfAOdFgaTtAWIRWgacMUUquSi7TR6U2LflFuW0nOVjYB7JQBqwHklOe49f9dOAf+HvjRIZ3L3ePTIWfQAgiAODqr7Z+QAAAABJRU5ErkJggg=="); - background-repeat:no-repeat; - padding-left: 20px; -} -.ico-fedora{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAAA3NCSVQICAjb4U/gAAAAPFBMVEX///9VVVVLS0tmZmZaWlpmZmZiYmJmZmZiYmJmZmZiYmJiYmJmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYd2jWRAAAAFHRSTlMAEREiIjMzRERVVWaImaq7zN3u/2KENScAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDgvMTcvMTiaRQF9AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAGJJREFUCJlFj1kSwCAIQ9N9Lxjuf9faUKf5IPNQiAJAZ4yg9ZCWkMj5pxWwGCqK5skLg8AphMv2dIJC0106nNmu5VLA1bBmfqclF7fZO9HTYqzO4tMuOt5XGLA1enMzQ194ADH/CrSqGfFbAAAAAElFTkSuQmCC"); - background-repeat:no-repeat; - padding-left: 20px; -} -.ico-mac{ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAA9ElEQVQ4T5XSvyvGURTH8dej+AsMyGAykEWSTSkyPFJMyvAsYjFZJWZ/gQyKYjEZLcrkRzZZTXomgyzKj3TkW99u6l63bt3OPe/z43NOQ/6sI+5g6trIsMs4wjUm/gs/og+TuMrBA1jAO06wgn3MohfHeKqC1MvexBY6fz/fcI+xWsZXNHEZtgpu4SCvnRfM4KYOn2O6AF7DXr3syB4ldhXAw3hIe/5ERwEc/d+lcBs9BfAOtlM4RrBUAH8hprKLj0rtRZwWwOES4xupqx3vWMHxggDzOEvhftxmer/A1F8bFrYhbKAbh3jGKOYQoq4ituznfAP/VikQF8NdKgAAAABJRU5ErkJggg=="); - background-repeat:no-repeat; - padding-top: 2px; - padding-left: 20px; -} - -.index-pos-box{ - height: 52px; - box-shadow: 0 1px 2px 0 rgba(0,0,0,.1); -} -.index-pos-box .position{ - line-height: 52px; -} -.change-id{ - margin-left: 8px; - color: #20a53a; -} -.bind-user .glyphicon{ - color: #20a53a; - font-size: 12px; - margin-right: 3px; -} -/*微信二维码*/ -.boxConter { - height: 458px; - position: relative; - overflow: auto; -} - -.iconCode { - padding: 50px 60px; -} - -.box-conter { - width: 100%; -} - -#QRcode { - margin-bottom: 25px; - text-align: center; -} - -.iconCode #QRcode, -.iconCode .codeTip { - width: 100%; - text-align: center; - font-size: 17px; -} - -.iconCode .weChatSamll img { - width: 100%; -} - -.iconCode .weChatSamll { - display: none; - width: 200px; - height: 200px; - position: absolute; - border: 1px solid #ececec; - border-radius: 5px; - bottom: 150px; - right: 50px; - padding: 20px; - background-color: #fff; -} - -.iconCode .weChatSamll:after { - content: ''; - width: 15px; - height: 15px; - background: #ffffff; - border-bottom: 1px solid #ececec; - border-right: 1px solid #ececec; - transform: rotate(45deg); - position: absolute; - border-radius: 4px; - left: 90px; - bottom: -8px; -} - -.iconCode .weChat { - margin-left: 15px; -} - -.iconCode .weChat:hover .weChatSamll { - display: block; -} - -.iconCode .QRcode { - margin-bottom: 15px; - text-align: center; -} - -.codeTip ul li { - margin-bottom: 10px; -} - -.personalDetails .head_img { - width: 50px; - height: 50px; - float: left; - margin-right: 30px; -} - -.personalDetails .head_img img { - height: 100%; - border-radius: 50%; -} - -.personalDetails .nick_name { - height: 50px; - line-height: 50px; - width: 148px; - float: left; - font-size: 15px; - color: #808080; -} - -.personalDetails .userList { - height: 100%; -} - -.personalDetails .userList .addweChat { - height: 50px; - text-align: center; - padding-top: 20px; - color: #20a53a; - font-size: 16px; -} - -.personalDetails .userList .addweChat:hover { -} - -.personalDetails .userList .item { - height: 70px; - padding: 10px 15px; - border: 1px solid #ececec; - margin: 15px 65px; - border-radius: 5px; -} - -.personalDetails .userList .cancelBind { - height: 50px; - width: 60px; - float: right; - line-height: 50px; - text-align: center; -} - -.libLogin .line{ - position:relative; -} -.libLogin .tips { - position: absolute; - top: 13px; - right: 28px; - background: #ff4949; - color: #fff; - padding: 4px 6px; - border-radius: 5px; -} -.change-id{ - cursor:pointer; -} -.red{ - color: red; -} - -select[disabled]{ - border:1px solid #DDD; - background-color:#F5F5F5; - color:#ACA899; -} - -.cicle { - position: relative; - width: 88px; - height: 88px; - margin: 0 auto; - border-width: 6px; - border-color: #ccc; - border-style: solid; - border-radius: 50%; -} - -.cicle *, -.cicle{ - box-sizing:content-box; - -webkit-box-sizing:content-box; - -moz-box-sizing: content-box; -} - -.cicle .bar { - position: absolute; - width: 50px; - height: 100px; - overflow: hidden; -} - -.cicle .bar-left { - top: -6px; - left: -6px; -} - -.cicle .bar-an { - position: absolute; - width: 88px; - height: 88px; - border-width:6px; - border-style: solid; - border-radius: 50%; - transform: rotate(-135deg); -} - -.cicle .bar-left .bar-left-an { - z-index: 10; - border-color: transparent transparent #20a53a #20a53a; -} - -.cicle .bar-right { - top: -6px; - left: 44px; -} - -.cicle .bar-right .bar-right-an { - left: -50px; - z-index: 20; - border-color: #20a53a #20a53a transparent transparent; -} - -.cicle .occupy { - position: absolute; - width: 88px; - height: 88px; - line-height: 88px; - text-align: center; - font-size: 18px; - color: #20a53a; -} - -.mem-action .occupy{ - font-size: 14px; -} -.mem-action .occupy.line{ - line-height: 22px; - padding-top: 22px; -} - -.change-default button{ - width: 188px; - margin: 10px -} -.bt-w-con .line .info-r{ - margin-left:0; -} - -.bt-logs textarea{ - white-space: pre; - margin: 0px; - width: 500px; - height: 520px; - background-color: #333; - color: #fff; - padding: 0 5px; -} -.dir-rewrite-man-con{ - margin-left: 15px; -} - -.domain-ul-list{ - padding: 5px 10px; - max-height: 180px; - overflow: auto; - width: 260px; - border: #ccc 1px solid; - border-radius: 3px; -} -.domain-ul-list li{ - line-height:26px; -} -.domain-ul-list li .checkbox-text{ - margin-right:5px; - vertical-align:-2px -} -.btswitch-btn.bt-waf-firewall{ - width:2.4em;height:1.4em;margin-bottom: 0 -} - - -/*6.0终端样式*/ -.term-box{ - padding:5px 10px 10px; - background-color:#333333; -} -.shell-text-input .bt-input-text-shell{ - width:100%; - padding:10px 10px 0; - border:0 none; - height:60px; - overflow:auto; - resize:none; -} - -.shell-text-input .bt-input-text-shell:focus,.shell-text-input .bt-input-text-shell:active{ - border:0 none; - outline:none; -} - -.shell-btn-group { - height:30px; - position:absolute; - bottom:20px; - right:10px; -} -.shell_btn_close { - margin-right:8px; -} - -.file_search { - position: absolute; - right: 58px; - font-size: 12px; - color: #666; - width: 85px; - border-top: none; - background: #fff; - height: 8px; - line-height: 48px; - top: 0; -} - -.file_search .file_search_checked { - position: relative; - height: 15px; - width: 15px; - border-radius: 1px; - cursor: pointer; - text-align: center; - margin: 0; - vertical-align: sub; - border:1px solid #aaa; - border-radius: 2px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: 400; - line-height: 1; - -webkit-font-smoothing: antialiased; -} - -.file_search .file_search_checked.active { - background: #20a53a; - color: #fff; - border-color:#20a53a ; -} - -.file_search .file_search_checked.active:after { - content: "\e013"; - font-size: 12px; - transform: scale(.85); - position: absolute; - left: .75px; - top: .75px; -} - - -.file_search label { - position: absolute; - right: 5px; - cursor: pointer; - top: 0; - font-weight: 400; -} - -#form_proxy .sub-groud .bt-input-text{ - margin-right: 10px; -} -#form_proxy .sub-groud { - margin-bottom: 15px; -} -#form_proxy .btswitch-btn{ - display:inline-block; -} -#form_proxy .add-replace-prosy { - position: relative; - top: -10px; -} -#form_proxy .disabled{ - background-color: #eee !important; - cursor: default; - border: 1px solid #dbdbea; -} - - -/* site的tab切换 */ -.tab-list .tabs-item:hover { - background: #20a53a08; -} - -.tab-list .tabs-item:first-child { - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} - -.tab-list .tabs-item { - display: inline-block; - height: 45px; - min-width: 100px; - padding: 0 20px; - line-height: 45px; - font-size: 13px; - cursor: pointer; - white-space: nowrap; - position: relative; - text-align: center; -} -@media screen and (max-width: 1300px) { - .tab-list .tabs-item{ - padding: 0 10px; - min-width: 80px; - } -} -.tab-list .tabs-item.active:after { - content: ''; - width: 20px; - height: 2px; - position: absolute; - left: 50%; - bottom: 0px; - background: red; - margin-left: -10px; - background: #20a53a; -} - -.tab-list .tabs-item.active { - color: #20a53a; - background: #20a53a10; -} - -.tab-view-box { - padding: 15px; - border-radius: 4px; - box-shadow: 0 0 1px 0 rgb(0 0 0 / 5%); -} - -.radius4 { - border-radius: 4px; - box-shadow: 0 0 1px 0 rgb(0 0 0 / 5%); -} -.radius2 { - border-radius: 2px; -} - - -.tootls_group { - height: 30px; - line-height: 30px; -} - -.tootls_group .search { - display: inline-block; - height: 30px; - line-height: 30px; - position: relative; - width: 230px; -} - -.tootls_group .search .search_input { - height: 30px; - line-height: 30px; - border-radius: 2px; - border: 1px solid #ccc; - outline: none; - padding-left: 8px; - vertical-align: top; - width: 230px; -} - -.tootls_group .search .search_input:focus { - border-color: #20a53a; -} - -.tootls_group .search .search_input:focus + span.glyphicon-search { - color: #20a53a; -} - -.tootls_group .search .glyphicon-remove-sign { - position: absolute; - top: 8px; - right: 30px; - font-size: 15px; - cursor: pointer; -} - -.tootls_group .search .glyphicon-search { - height: 28px; - line-height: 28px; - padding: 0 10px; - color: #888; - position: absolute; - right: 0; - font-size: 14px; - cursor: pointer; - float: left; -} - -.tootls_group .page .page_jump_group .page_jump_btn:hover { - background-color: #10952a; - border-color: #398439; -} - -.tootls_group .search .search-btn span { - font-size: 15px; - vertical-align: middle; - position: relative; - top: -1px; -} - -.tootls_group .page { - padding: 0; - height: 30px; -} - -.tootls_group .page .page_select_number, -.tootls_group .page .page_jump_group { - height: 28px; - line-height: 26px; - margin-left: 8px; - vertical-align: top; - float: left; -} - -.tootls_group .page .page_jump_group span { - border: none; - padding: 0 8px; -} - -.tootls_group .page .page_select_number { - border: 1px solid #ececec; - border-radius: 0; - padding: 0 5px; - border-left: 0; - margin-left: 0; -} - -.tootls_group .page .page_jump_group input { - height: 28px; - line-height: 28px; - width: 30px; - max-width: 50px; - vertical-align: top; - border: 1px solid #ececec; - outline: none; - text-align: center; - float: left; -} - -.tootls_group .page .page_jump_group input:focus { - border-color: #20a53a; -} - -.tootls_group .page .page_jump_group .page-jump-title { - height: 28px; - line-height: 28px; - color: #555; - border: none; - padding: 0 5px; -} - -.tootls_group .page .page_jump_group .page_jump_btn { - height: 28px; - line-height: 24px; - border-radius: 0; - outline: none; - margin-left: 8px; - border-radius: 2px; - color: #555; - background-color: #fff; - border: 1px solid #ccc; -} - -.tootls_group .page .page_jump_group .page_jump_btn:hover { - color: #fff; - background-color: #20a53a; - border-color: #20a53a; -} - -.size_ellipsis { - display: inline-block; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - - -.title.tabs-nav { - padding: 0; - position: relative; -} - -.title.tabs-nav span { - display: inline-block; - height: 50px; - min-width: 100px; - padding: 0 20px; - line-height: 50px; - font-size: 15px; - cursor: pointer; - position: relative; - text-align: center; -} - -.title.tabs-nav span.active { - color: #20a53a; - background: #20a53a08; -} - - -.title.tabs-nav select { - position: absolute; - right: 5px; - top: 50%; - margin-top: -12px; -} - -select[name="network-io"], select[name="disk-io"] { - font-size: 13px; - height: 24px; - line-height: 24px; - padding: 2px; - border-radius: 0; - border-color: #ececec; -} - -.tabs-content .tabs-item { - display: none; - height: 100%; - width: 100%; - background-color: #fff; -} - -.tabs-content .tabs-active.tabs-item { - display: inline-block; -} - -.bw-info { - height: 80px; -} - -.bw-info>div { - float: left; - padding-top: 35px; - text-align: center; -} - -.bw-info>div p { - margin-bottom: 7px; -} - -.bw-info>div a { - font-size: 14px; - color: #666; -} - -.bw-info .ico-down, -.bw-info .ico-up, -.bw-info .ico-write, -.bw-info .ico-read { - width: 10px; - height: 10px; - display: inline-block; - margin-right: 3px; - border-radius: 5px; -} - -.bw-info .ico-down { - background-color: #52a9ff; -} - -.bw-info .ico-up { - background-color: #f7b851; -} - -.bw-info .ico-write { - background-color: #6CC0CF; -} - -.bw-info .ico-read { - background-color: #FF4683; -} - -.bw-info ul li { - height: 56px; - line-height: 56px; -} - -.bw-info ul li.bi-line { - border-bottom: #ddd solid 1px; -} \ No newline at end of file diff --git a/route/static/favicon.ico b/route/static/favicon.ico deleted file mode 100644 index 17e918a80..000000000 Binary files a/route/static/favicon.ico and /dev/null differ diff --git a/route/static/images/ico-ts-cpu-active.png b/route/static/images/ico-ts-cpu-active.png deleted file mode 100755 index 1abf4f009..000000000 Binary files a/route/static/images/ico-ts-cpu-active.png and /dev/null differ diff --git a/route/static/images/ico-ts-cpu.png b/route/static/images/ico-ts-cpu.png deleted file mode 100755 index 5c9d3eb38..000000000 Binary files a/route/static/images/ico-ts-cpu.png and /dev/null differ diff --git a/route/static/images/ico-ts-disk-active.png b/route/static/images/ico-ts-disk-active.png deleted file mode 100755 index acaca696e..000000000 Binary files a/route/static/images/ico-ts-disk-active.png and /dev/null differ diff --git a/route/static/images/ico-ts-disk.png b/route/static/images/ico-ts-disk.png deleted file mode 100755 index 73da82c16..000000000 Binary files a/route/static/images/ico-ts-disk.png and /dev/null differ diff --git a/route/static/images/ico-ts-mem-active.png b/route/static/images/ico-ts-mem-active.png deleted file mode 100755 index d71de28ed..000000000 Binary files a/route/static/images/ico-ts-mem-active.png and /dev/null differ diff --git a/route/static/images/ico-ts-mem.png b/route/static/images/ico-ts-mem.png deleted file mode 100755 index d12b65438..000000000 Binary files a/route/static/images/ico-ts-mem.png and /dev/null differ diff --git a/route/static/images/ico-ts-refresh.png b/route/static/images/ico-ts-refresh.png deleted file mode 100755 index b292189d8..000000000 Binary files a/route/static/images/ico-ts-refresh.png and /dev/null differ diff --git a/route/static/images/ico-ts-score.gif b/route/static/images/ico-ts-score.gif deleted file mode 100755 index e4c8a65ca..000000000 Binary files a/route/static/images/ico-ts-score.gif and /dev/null differ diff --git a/route/static/images/ico-ts-score.jpg b/route/static/images/ico-ts-score.jpg deleted file mode 100755 index 8eb5d62e5..000000000 Binary files a/route/static/images/ico-ts-score.jpg and /dev/null differ diff --git a/route/static/images/ico-ts-score1.png b/route/static/images/ico-ts-score1.png deleted file mode 100755 index 8219d32af..000000000 Binary files a/route/static/images/ico-ts-score1.png and /dev/null differ diff --git a/route/static/images/ico-ts-score2.png b/route/static/images/ico-ts-score2.png deleted file mode 100755 index 1eff7f3b3..000000000 Binary files a/route/static/images/ico-ts-score2.png and /dev/null differ diff --git a/route/static/images/ico-ts-score3.png b/route/static/images/ico-ts-score3.png deleted file mode 100755 index 43cf9600f..000000000 Binary files a/route/static/images/ico-ts-score3.png and /dev/null differ diff --git a/route/static/images/index.html b/route/static/images/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/images/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/images/logo.png b/route/static/images/logo.png deleted file mode 100755 index 4d96d6b6e..000000000 Binary files a/route/static/images/logo.png and /dev/null differ diff --git a/route/static/images/move.png b/route/static/images/move.png deleted file mode 100755 index 85763763e..000000000 Binary files a/route/static/images/move.png and /dev/null differ diff --git a/route/static/images/right.png b/route/static/images/right.png deleted file mode 100755 index 86e3aa594..000000000 Binary files a/route/static/images/right.png and /dev/null differ diff --git a/route/static/images/weixin.png b/route/static/images/weixin.png deleted file mode 100755 index b0083a93b..000000000 Binary files a/route/static/images/weixin.png and /dev/null differ diff --git a/route/static/img/Detailsbg.png b/route/static/img/Detailsbg.png deleted file mode 100755 index 28f5277d9..000000000 Binary files a/route/static/img/Detailsbg.png and /dev/null differ diff --git a/route/static/img/DrawRecordord.png b/route/static/img/DrawRecordord.png deleted file mode 100755 index 72c3966bd..000000000 Binary files a/route/static/img/DrawRecordord.png and /dev/null differ diff --git a/route/static/img/account.png b/route/static/img/account.png deleted file mode 100755 index 8755ebff1..000000000 Binary files a/route/static/img/account.png and /dev/null differ diff --git a/route/static/img/alipay_zz.png b/route/static/img/alipay_zz.png deleted file mode 100644 index 98d99a533..000000000 Binary files a/route/static/img/alipay_zz.png and /dev/null differ diff --git a/route/static/img/ico-close.png b/route/static/img/ico-close.png deleted file mode 100755 index d5b8a22c9..000000000 Binary files a/route/static/img/ico-close.png and /dev/null differ diff --git a/route/static/img/ico-computer.png b/route/static/img/ico-computer.png deleted file mode 100755 index e383922da..000000000 Binary files a/route/static/img/ico-computer.png and /dev/null differ diff --git a/route/static/img/ico-copy.png b/route/static/img/ico-copy.png deleted file mode 100755 index adede38d2..000000000 Binary files a/route/static/img/ico-copy.png and /dev/null differ diff --git a/route/static/img/ico-home.png b/route/static/img/ico-home.png deleted file mode 100755 index 9de21b9d0..000000000 Binary files a/route/static/img/ico-home.png and /dev/null differ diff --git a/route/static/img/ico-success.png b/route/static/img/ico-success.png deleted file mode 100755 index ba677b2c3..000000000 Binary files a/route/static/img/ico-success.png and /dev/null differ diff --git a/route/static/img/ico/ico-access.png b/route/static/img/ico/ico-access.png deleted file mode 100755 index 1a69b17de..000000000 Binary files a/route/static/img/ico/ico-access.png and /dev/null differ diff --git a/route/static/img/ico/ico-apk.png b/route/static/img/ico/ico-apk.png deleted file mode 100755 index 27880ca2b..000000000 Binary files a/route/static/img/ico/ico-apk.png and /dev/null differ diff --git a/route/static/img/ico/ico-avi.png b/route/static/img/ico/ico-avi.png deleted file mode 100755 index e35454ca9..000000000 Binary files a/route/static/img/ico/ico-avi.png and /dev/null differ diff --git a/route/static/img/ico/ico-bmp.png b/route/static/img/ico/ico-bmp.png deleted file mode 100755 index 83f127829..000000000 Binary files a/route/static/img/ico/ico-bmp.png and /dev/null differ diff --git a/route/static/img/ico/ico-bt.png b/route/static/img/ico/ico-bt.png deleted file mode 100755 index 387293f80..000000000 Binary files a/route/static/img/ico/ico-bt.png and /dev/null differ diff --git a/route/static/img/ico/ico-c.png b/route/static/img/ico/ico-c.png deleted file mode 100755 index 21e4deb3e..000000000 Binary files a/route/static/img/ico/ico-c.png and /dev/null differ diff --git a/route/static/img/ico/ico-cdr.png b/route/static/img/ico/ico-cdr.png deleted file mode 100755 index 2e16c24e0..000000000 Binary files a/route/static/img/ico/ico-cdr.png and /dev/null differ diff --git a/route/static/img/ico/ico-cpp.png b/route/static/img/ico/ico-cpp.png deleted file mode 100755 index 7a8a5cf90..000000000 Binary files a/route/static/img/ico/ico-cpp.png and /dev/null differ diff --git a/route/static/img/ico/ico-cs.png b/route/static/img/ico/ico-cs.png deleted file mode 100755 index 658831976..000000000 Binary files a/route/static/img/ico/ico-cs.png and /dev/null differ diff --git a/route/static/img/ico/ico-doc.png b/route/static/img/ico/ico-doc.png deleted file mode 100755 index ca27a9666..000000000 Binary files a/route/static/img/ico/ico-doc.png and /dev/null differ diff --git a/route/static/img/ico/ico-docx.png b/route/static/img/ico/ico-docx.png deleted file mode 100755 index 702d2633d..000000000 Binary files a/route/static/img/ico/ico-docx.png and /dev/null differ diff --git a/route/static/img/ico/ico-flv.png b/route/static/img/ico/ico-flv.png deleted file mode 100755 index 973435f5d..000000000 Binary files a/route/static/img/ico/ico-flv.png and /dev/null differ diff --git a/route/static/img/ico/ico-gif.png b/route/static/img/ico/ico-gif.png deleted file mode 100755 index 53a135168..000000000 Binary files a/route/static/img/ico/ico-gif.png and /dev/null differ diff --git a/route/static/img/ico/ico-htm.png b/route/static/img/ico/ico-htm.png deleted file mode 100755 index 14665dda0..000000000 Binary files a/route/static/img/ico/ico-htm.png and /dev/null differ diff --git a/route/static/img/ico/ico-html.png b/route/static/img/ico/ico-html.png deleted file mode 100755 index a29b6297f..000000000 Binary files a/route/static/img/ico/ico-html.png and /dev/null differ diff --git a/route/static/img/ico/ico-java.png b/route/static/img/ico/ico-java.png deleted file mode 100755 index 20affd320..000000000 Binary files a/route/static/img/ico/ico-java.png and /dev/null differ diff --git a/route/static/img/ico/ico-jpeg.png b/route/static/img/ico/ico-jpeg.png deleted file mode 100755 index f28f52905..000000000 Binary files a/route/static/img/ico/ico-jpeg.png and /dev/null differ diff --git a/route/static/img/ico/ico-jpg.png b/route/static/img/ico/ico-jpg.png deleted file mode 100755 index 989551b66..000000000 Binary files a/route/static/img/ico/ico-jpg.png and /dev/null differ diff --git a/route/static/img/ico/ico-js.png b/route/static/img/ico/ico-js.png deleted file mode 100755 index 35691771d..000000000 Binary files a/route/static/img/ico/ico-js.png and /dev/null differ diff --git a/route/static/img/ico/ico-ltr.png b/route/static/img/ico/ico-ltr.png deleted file mode 100755 index dc6d48718..000000000 Binary files a/route/static/img/ico/ico-ltr.png and /dev/null differ diff --git a/route/static/img/ico/ico-mht.png b/route/static/img/ico/ico-mht.png deleted file mode 100755 index 296a7ca14..000000000 Binary files a/route/static/img/ico/ico-mht.png and /dev/null differ diff --git a/route/static/img/ico/ico-mkv.png b/route/static/img/ico/ico-mkv.png deleted file mode 100755 index 00abc0be9..000000000 Binary files a/route/static/img/ico/ico-mkv.png and /dev/null differ diff --git a/route/static/img/ico/ico-mov.png b/route/static/img/ico/ico-mov.png deleted file mode 100755 index 4ab1a5c3d..000000000 Binary files a/route/static/img/ico/ico-mov.png and /dev/null differ diff --git a/route/static/img/ico/ico-mp4.png b/route/static/img/ico/ico-mp4.png deleted file mode 100755 index 9dd90f5c4..000000000 Binary files a/route/static/img/ico/ico-mp4.png and /dev/null differ diff --git a/route/static/img/ico/ico-mpeg.png b/route/static/img/ico/ico-mpeg.png deleted file mode 100755 index c092f67ed..000000000 Binary files a/route/static/img/ico/ico-mpeg.png and /dev/null differ diff --git a/route/static/img/ico/ico-mpg.png b/route/static/img/ico/ico-mpg.png deleted file mode 100755 index 4cc52b6fe..000000000 Binary files a/route/static/img/ico/ico-mpg.png and /dev/null differ diff --git a/route/static/img/ico/ico-pdf.png b/route/static/img/ico/ico-pdf.png deleted file mode 100755 index 2dbcafb38..000000000 Binary files a/route/static/img/ico/ico-pdf.png and /dev/null differ diff --git a/route/static/img/ico/ico-php.png b/route/static/img/ico/ico-php.png deleted file mode 100755 index 7145c652f..000000000 Binary files a/route/static/img/ico/ico-php.png and /dev/null differ diff --git a/route/static/img/ico/ico-png.png b/route/static/img/ico/ico-png.png deleted file mode 100755 index f39804a9c..000000000 Binary files a/route/static/img/ico/ico-png.png and /dev/null differ diff --git a/route/static/img/ico/ico-pptx.png b/route/static/img/ico/ico-pptx.png deleted file mode 100755 index f1be14b5c..000000000 Binary files a/route/static/img/ico/ico-pptx.png and /dev/null differ diff --git a/route/static/img/ico/ico-psd.png b/route/static/img/ico/ico-psd.png deleted file mode 100755 index f25968f48..000000000 Binary files a/route/static/img/ico/ico-psd.png and /dev/null differ diff --git a/route/static/img/ico/ico-rm.png b/route/static/img/ico/ico-rm.png deleted file mode 100755 index e4cb6ca3e..000000000 Binary files a/route/static/img/ico/ico-rm.png and /dev/null differ diff --git a/route/static/img/ico/ico-rmvb.png b/route/static/img/ico/ico-rmvb.png deleted file mode 100755 index 5b9805a01..000000000 Binary files a/route/static/img/ico/ico-rmvb.png and /dev/null differ diff --git a/route/static/img/ico/ico-rocket.gif b/route/static/img/ico/ico-rocket.gif deleted file mode 100755 index 470b2e1c5..000000000 Binary files a/route/static/img/ico/ico-rocket.gif and /dev/null differ diff --git a/route/static/img/ico/ico-swf.png b/route/static/img/ico/ico-swf.png deleted file mode 100755 index d043a2e2a..000000000 Binary files a/route/static/img/ico/ico-swf.png and /dev/null differ diff --git a/route/static/img/ico/ico-url.png b/route/static/img/ico/ico-url.png deleted file mode 100755 index ab86ae7f9..000000000 Binary files a/route/static/img/ico/ico-url.png and /dev/null differ diff --git a/route/static/img/ico/ico-webm.png b/route/static/img/ico/ico-webm.png deleted file mode 100755 index e568c4250..000000000 Binary files a/route/static/img/ico/ico-webm.png and /dev/null differ diff --git a/route/static/img/ico/ico-webp.png b/route/static/img/ico/ico-webp.png deleted file mode 100755 index 6b8cc4dc7..000000000 Binary files a/route/static/img/ico/ico-webp.png and /dev/null differ diff --git a/route/static/img/ico/ico-wma.png b/route/static/img/ico/ico-wma.png deleted file mode 100755 index 036e0b642..000000000 Binary files a/route/static/img/ico/ico-wma.png and /dev/null differ diff --git a/route/static/img/ico/ico-wmv.png b/route/static/img/ico/ico-wmv.png deleted file mode 100755 index ba03d507f..000000000 Binary files a/route/static/img/ico/ico-wmv.png and /dev/null differ diff --git a/route/static/img/ico/ico-xls.png b/route/static/img/ico/ico-xls.png deleted file mode 100755 index e141b11b2..000000000 Binary files a/route/static/img/ico/ico-xls.png and /dev/null differ diff --git a/route/static/img/ico/ico-xlsx.png b/route/static/img/ico/ico-xlsx.png deleted file mode 100755 index 66fb34215..000000000 Binary files a/route/static/img/ico/ico-xlsx.png and /dev/null differ diff --git a/route/static/img/ico/ico-xml.png b/route/static/img/ico/ico-xml.png deleted file mode 100755 index 8368fb67b..000000000 Binary files a/route/static/img/ico/ico-xml.png and /dev/null differ diff --git a/route/static/img/ico/index.html b/route/static/img/ico/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/img/ico/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/img/ico/menu_icon_control.png b/route/static/img/ico/menu_icon_control.png deleted file mode 100755 index c2ecbc080..000000000 Binary files a/route/static/img/ico/menu_icon_control.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_control_active.gif b/route/static/img/ico/menu_icon_control_active.gif deleted file mode 100755 index ba56df8e9..000000000 Binary files a/route/static/img/ico/menu_icon_control_active.gif and /dev/null differ diff --git a/route/static/img/ico/menu_icon_control_active.png b/route/static/img/ico/menu_icon_control_active.png deleted file mode 100755 index d698ab70e..000000000 Binary files a/route/static/img/ico/menu_icon_control_active.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_data.png b/route/static/img/ico/menu_icon_data.png deleted file mode 100755 index c8ee474bf..000000000 Binary files a/route/static/img/ico/menu_icon_data.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_data_active.gif b/route/static/img/ico/menu_icon_data_active.gif deleted file mode 100755 index 04b6e0d42..000000000 Binary files a/route/static/img/ico/menu_icon_data_active.gif and /dev/null differ diff --git a/route/static/img/ico/menu_icon_data_active.png b/route/static/img/ico/menu_icon_data_active.png deleted file mode 100755 index fe30c4b63..000000000 Binary files a/route/static/img/ico/menu_icon_data_active.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_day.png b/route/static/img/ico/menu_icon_day.png deleted file mode 100755 index 9fbccf0be..000000000 Binary files a/route/static/img/ico/menu_icon_day.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_day_active.gif b/route/static/img/ico/menu_icon_day_active.gif deleted file mode 100755 index 6f98a0f01..000000000 Binary files a/route/static/img/ico/menu_icon_day_active.gif and /dev/null differ diff --git a/route/static/img/ico/menu_icon_day_active.png b/route/static/img/ico/menu_icon_day_active.png deleted file mode 100755 index e128e163f..000000000 Binary files a/route/static/img/ico/menu_icon_day_active.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_exit.png b/route/static/img/ico/menu_icon_exit.png deleted file mode 100755 index 75feec4b1..000000000 Binary files a/route/static/img/ico/menu_icon_exit.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_exit_active.gif b/route/static/img/ico/menu_icon_exit_active.gif deleted file mode 100755 index b11bd5d3d..000000000 Binary files a/route/static/img/ico/menu_icon_exit_active.gif and /dev/null differ diff --git a/route/static/img/ico/menu_icon_exit_active.png b/route/static/img/ico/menu_icon_exit_active.png deleted file mode 100755 index dc81d480f..000000000 Binary files a/route/static/img/ico/menu_icon_exit_active.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_firewall.png b/route/static/img/ico/menu_icon_firewall.png deleted file mode 100755 index f05938dd8..000000000 Binary files a/route/static/img/ico/menu_icon_firewall.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_firewall_active.gif b/route/static/img/ico/menu_icon_firewall_active.gif deleted file mode 100755 index 3597a5b42..000000000 Binary files a/route/static/img/ico/menu_icon_firewall_active.gif and /dev/null differ diff --git a/route/static/img/ico/menu_icon_firewall_active.png b/route/static/img/ico/menu_icon_firewall_active.png deleted file mode 100755 index 5c2f49f79..000000000 Binary files a/route/static/img/ico/menu_icon_firewall_active.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_folder.png b/route/static/img/ico/menu_icon_folder.png deleted file mode 100755 index eb3461c42..000000000 Binary files a/route/static/img/ico/menu_icon_folder.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_folder_active.gif b/route/static/img/ico/menu_icon_folder_active.gif deleted file mode 100755 index ac4564368..000000000 Binary files a/route/static/img/ico/menu_icon_folder_active.gif and /dev/null differ diff --git a/route/static/img/ico/menu_icon_folder_active.png b/route/static/img/ico/menu_icon_folder_active.png deleted file mode 100755 index 871688f67..000000000 Binary files a/route/static/img/ico/menu_icon_folder_active.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_ftp.png b/route/static/img/ico/menu_icon_ftp.png deleted file mode 100755 index d57b21cd9..000000000 Binary files a/route/static/img/ico/menu_icon_ftp.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_ftp_active.gif b/route/static/img/ico/menu_icon_ftp_active.gif deleted file mode 100755 index 84d708723..000000000 Binary files a/route/static/img/ico/menu_icon_ftp_active.gif and /dev/null differ diff --git a/route/static/img/ico/menu_icon_ftp_active.png b/route/static/img/ico/menu_icon_ftp_active.png deleted file mode 100755 index 394a71a80..000000000 Binary files a/route/static/img/ico/menu_icon_ftp_active.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_home.png b/route/static/img/ico/menu_icon_home.png deleted file mode 100755 index e198671c0..000000000 Binary files a/route/static/img/ico/menu_icon_home.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_home_active.gif b/route/static/img/ico/menu_icon_home_active.gif deleted file mode 100755 index 96c3bcbf9..000000000 Binary files a/route/static/img/ico/menu_icon_home_active.gif and /dev/null differ diff --git a/route/static/img/ico/menu_icon_home_active.png b/route/static/img/ico/menu_icon_home_active.png deleted file mode 100755 index 636e26ebe..000000000 Binary files a/route/static/img/ico/menu_icon_home_active.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_log.png b/route/static/img/ico/menu_icon_log.png deleted file mode 100755 index 774743d9c..000000000 Binary files a/route/static/img/ico/menu_icon_log.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_log_active.png b/route/static/img/ico/menu_icon_log_active.png deleted file mode 100755 index 0d0e2f156..000000000 Binary files a/route/static/img/ico/menu_icon_log_active.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_set.png b/route/static/img/ico/menu_icon_set.png deleted file mode 100755 index 74b247163..000000000 Binary files a/route/static/img/ico/menu_icon_set.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_set_active.gif b/route/static/img/ico/menu_icon_set_active.gif deleted file mode 100755 index 82fa4f264..000000000 Binary files a/route/static/img/ico/menu_icon_set_active.gif and /dev/null differ diff --git a/route/static/img/ico/menu_icon_set_active.png b/route/static/img/ico/menu_icon_set_active.png deleted file mode 100755 index 1f61f2539..000000000 Binary files a/route/static/img/ico/menu_icon_set_active.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_soft.png b/route/static/img/ico/menu_icon_soft.png deleted file mode 100755 index 0ea48356b..000000000 Binary files a/route/static/img/ico/menu_icon_soft.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_soft_active.gif b/route/static/img/ico/menu_icon_soft_active.gif deleted file mode 100755 index 779c78f23..000000000 Binary files a/route/static/img/ico/menu_icon_soft_active.gif and /dev/null differ diff --git a/route/static/img/ico/menu_icon_soft_active.png b/route/static/img/ico/menu_icon_soft_active.png deleted file mode 100755 index f0c8e4a98..000000000 Binary files a/route/static/img/ico/menu_icon_soft_active.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_web.png b/route/static/img/ico/menu_icon_web.png deleted file mode 100755 index cad87fbe3..000000000 Binary files a/route/static/img/ico/menu_icon_web.png and /dev/null differ diff --git a/route/static/img/ico/menu_icon_web_active.gif b/route/static/img/ico/menu_icon_web_active.gif deleted file mode 100755 index 81c90c85d..000000000 Binary files a/route/static/img/ico/menu_icon_web_active.gif and /dev/null differ diff --git a/route/static/img/ico/menu_icon_web_active.png b/route/static/img/ico/menu_icon_web_active.png deleted file mode 100755 index 262302881..000000000 Binary files a/route/static/img/ico/menu_icon_web_active.png and /dev/null differ diff --git a/route/static/img/ico/rocket_min.png b/route/static/img/ico/rocket_min.png deleted file mode 100755 index 2198d4086..000000000 Binary files a/route/static/img/ico/rocket_min.png and /dev/null differ diff --git a/route/static/img/ico_line.png b/route/static/img/ico_line.png deleted file mode 100755 index 330c8a93b..000000000 Binary files a/route/static/img/ico_line.png and /dev/null differ diff --git a/route/static/img/icon-item.png b/route/static/img/icon-item.png deleted file mode 100755 index 4f7afd85f..000000000 Binary files a/route/static/img/icon-item.png and /dev/null differ diff --git a/route/static/img/icon3.png b/route/static/img/icon3.png deleted file mode 100755 index d2b25e28d..000000000 Binary files a/route/static/img/icon3.png and /dev/null differ diff --git a/route/static/img/icon4.png b/route/static/img/icon4.png deleted file mode 100755 index ef34ed5f9..000000000 Binary files a/route/static/img/icon4.png and /dev/null differ diff --git a/route/static/img/index.html b/route/static/img/index.html deleted file mode 100755 index e69de29bb..000000000 diff --git a/route/static/img/ing.gif b/route/static/img/ing.gif deleted file mode 100755 index 6b87c9271..000000000 Binary files a/route/static/img/ing.gif and /dev/null differ diff --git a/route/static/img/ings.gif b/route/static/img/ings.gif deleted file mode 100755 index 88a262116..000000000 Binary files a/route/static/img/ings.gif and /dev/null differ diff --git a/route/static/img/label-icon.png b/route/static/img/label-icon.png deleted file mode 100755 index 5d0aa3dce..000000000 Binary files a/route/static/img/label-icon.png and /dev/null differ diff --git a/route/static/img/loading.gif b/route/static/img/loading.gif deleted file mode 100755 index e1691ac98..000000000 Binary files a/route/static/img/loading.gif and /dev/null differ diff --git a/route/static/img/morebtn.png b/route/static/img/morebtn.png deleted file mode 100755 index caf1d593f..000000000 Binary files a/route/static/img/morebtn.png and /dev/null differ diff --git a/route/static/img/ns-loading.gif b/route/static/img/ns-loading.gif deleted file mode 100755 index 095a1b5af..000000000 Binary files a/route/static/img/ns-loading.gif and /dev/null differ diff --git a/route/static/img/ref-icon.png b/route/static/img/ref-icon.png deleted file mode 100755 index 8e6435218..000000000 Binary files a/route/static/img/ref-icon.png and /dev/null differ diff --git a/route/static/img/return-icon.png b/route/static/img/return-icon.png deleted file mode 100755 index 538fcdc67..000000000 Binary files a/route/static/img/return-icon.png and /dev/null differ diff --git a/route/static/img/safety_ico.png b/route/static/img/safety_ico.png deleted file mode 100755 index c3fd93edf..000000000 Binary files a/route/static/img/safety_ico.png and /dev/null differ diff --git a/route/static/img/ser-icon.png b/route/static/img/ser-icon.png deleted file mode 100755 index 6ac0130be..000000000 Binary files a/route/static/img/ser-icon.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-app.png b/route/static/img/soft_ico/ico-app.png deleted file mode 100755 index 1f00d6712..000000000 Binary files a/route/static/img/soft_ico/ico-app.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-beta.png b/route/static/img/soft_ico/ico-beta.png deleted file mode 100755 index bdbd32afb..000000000 Binary files a/route/static/img/soft_ico/ico-beta.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-cmd-hover.png b/route/static/img/soft_ico/ico-cmd-hover.png deleted file mode 100755 index cad45fc5d..000000000 Binary files a/route/static/img/soft_ico/ico-cmd-hover.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-cmd.png b/route/static/img/soft_ico/ico-cmd.png deleted file mode 100755 index 981e03f4c..000000000 Binary files a/route/static/img/soft_ico/ico-cmd.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-deployment.png b/route/static/img/soft_ico/ico-deployment.png deleted file mode 100755 index dd2d565aa..000000000 Binary files a/route/static/img/soft_ico/ico-deployment.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-load_leveling.png b/route/static/img/soft_ico/ico-load_leveling.png deleted file mode 100755 index c20e1edc6..000000000 Binary files a/route/static/img/soft_ico/ico-load_leveling.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-log.png b/route/static/img/soft_ico/ico-log.png deleted file mode 100755 index efe1b77c1..000000000 Binary files a/route/static/img/soft_ico/ico-log.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-logs.png b/route/static/img/soft_ico/ico-logs.png deleted file mode 100755 index a27db7bba..000000000 Binary files a/route/static/img/soft_ico/ico-logs.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-mongodb.png b/route/static/img/soft_ico/ico-mongodb.png deleted file mode 100755 index 9c1eaac3e..000000000 Binary files a/route/static/img/soft_ico/ico-mongodb.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-node.png b/route/static/img/soft_ico/ico-node.png deleted file mode 100755 index 0474de49a..000000000 Binary files a/route/static/img/soft_ico/ico-node.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-phpsafe.png b/route/static/img/soft_ico/ico-phpsafe.png deleted file mode 100755 index a3e1b6115..000000000 Binary files a/route/static/img/soft_ico/ico-phpsafe.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-pm2.png b/route/static/img/soft_ico/ico-pm2.png deleted file mode 100755 index 62cd3d1a1..000000000 Binary files a/route/static/img/soft_ico/ico-pm2.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-psync.png b/route/static/img/soft_ico/ico-psync.png deleted file mode 100755 index 31015316d..000000000 Binary files a/route/static/img/soft_ico/ico-psync.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-rsync.png b/route/static/img/soft_ico/ico-rsync.png deleted file mode 100755 index 2843739f1..000000000 Binary files a/route/static/img/soft_ico/ico-rsync.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-safelogin.png b/route/static/img/soft_ico/ico-safelogin.png deleted file mode 100755 index 452dc0fbf..000000000 Binary files a/route/static/img/soft_ico/ico-safelogin.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-score.png b/route/static/img/soft_ico/ico-score.png deleted file mode 100755 index 68e9077a6..000000000 Binary files a/route/static/img/soft_ico/ico-score.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-task_manager.png b/route/static/img/soft_ico/ico-task_manager.png deleted file mode 100755 index ad97d2181..000000000 Binary files a/route/static/img/soft_ico/ico-task_manager.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-test.png b/route/static/img/soft_ico/ico-test.png deleted file mode 100755 index cb28b12e5..000000000 Binary files a/route/static/img/soft_ico/ico-test.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-tomcat.png b/route/static/img/soft_ico/ico-tomcat.png deleted file mode 100755 index 2e3e7dd5b..000000000 Binary files a/route/static/img/soft_ico/ico-tomcat.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-txcos.png b/route/static/img/soft_ico/ico-txcos.png deleted file mode 100755 index 9bc7d21d2..000000000 Binary files a/route/static/img/soft_ico/ico-txcos.png and /dev/null differ diff --git a/route/static/img/soft_ico/ico-webhook.png b/route/static/img/soft_ico/ico-webhook.png deleted file mode 100755 index bca24ab1b..000000000 Binary files a/route/static/img/soft_ico/ico-webhook.png and /dev/null differ diff --git a/route/static/img/soft_ico/index.html b/route/static/img/soft_ico/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/img/soft_ico/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/img/success-pic.png b/route/static/img/success-pic.png deleted file mode 100755 index c46be849e..000000000 Binary files a/route/static/img/success-pic.png and /dev/null differ diff --git a/route/static/img/tip_suu.png b/route/static/img/tip_suu.png deleted file mode 100755 index 3884ec96c..000000000 Binary files a/route/static/img/tip_suu.png and /dev/null differ diff --git a/route/static/img/weixin_zz.jpg b/route/static/img/weixin_zz.jpg deleted file mode 100644 index 4d3c6105b..000000000 Binary files a/route/static/img/weixin_zz.jpg and /dev/null differ diff --git a/route/static/index.html b/route/static/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/js/Validform_v5.3.2_min.js b/route/static/js/Validform_v5.3.2_min.js deleted file mode 100755 index 4eb4f7d89..000000000 --- a/route/static/js/Validform_v5.3.2_min.js +++ /dev/null @@ -1,8 +0,0 @@ -/* - Validform version 5.3.2 - By sean during April 7, 2010 - March 26, 2013 - For more information, please visit http://validform.rjboy.cn - Validform is available under the terms of the MIT license. -*/ - -(function(d,f,b){var g=null,j=null,i=true;var e={tit:"提示信息",w:{"*":"不能为空!","*6-16":"请填写6到16位任意字符!","n":"请填写数字!","n6-16":"请填写6到16位数字!","s":"不能输入特殊字符!","s6-18":"请填写6到18位字符!","p":"请填写邮政编码!","m":"请填写手机号码!","e":"邮箱地址格式不对!","url":"请填写网址!"},def:"请填写正确信息!",undef:"datatype未定义!",reck:"两次输入的内容不一致!",r:"",c:"正在检测信息…",s:"请{填写|选择}{0|信息}!",v:"所填信息没有经过验证,请稍后…",p:"正在提交数据…"};d.Tipmsg=e;var a=function(l,n,k){var n=d.extend({},a.defaults,n);n.datatype&&d.extend(a.util.dataType,n.datatype);var m=this;m.tipmsg={w:{}};m.forms=l;m.objects=[];if(k===true){return false}l.each(function(){if(this.validform_inited=="inited"){return true}this.validform_inited="inited";var p=this;p.settings=d.extend({},n);var o=d(p);p.validform_status="normal";o.data("tipmsg",m.tipmsg);o.delegate("[datatype]","blur",function(){var q=arguments[1];a.util.check.call(this,o,q)});o.delegate(":text","keypress",function(q){if(q.keyCode==13&&o.find(":submit").length==0){o.submit()}});a.util.enhance.call(o,p.settings.tiptype,p.settings.usePlugin,p.settings.tipSweep);p.settings.btnSubmit&&o.find(p.settings.btnSubmit).bind("click",function(){o.trigger("submit");return false});o.submit(function(){var q=a.util.submitForm.call(o,p.settings);q===b&&(q=true);return q});o.find("[type='reset']").add(o.find(p.settings.btnReset)).bind("click",function(){a.util.resetForm.call(o)})});if(n.tiptype==1||(n.tiptype==2||n.tiptype==3)&&n.ajaxPost){c()}};a.defaults={tiptype:1,tipSweep:false,showAllError:false,postonce:false,ajaxPost:false};a.util={dataType:{"*":/[\w\W]+/,"*6-16":/^[\w\W]{6,16}$/,n:/^\d+$/,"n6-16":/^\d{6,16}$/,s:/^[\u4E00-\u9FA5\uf900-\ufa2d\w\.\s]+$/,"s6-18":/^[\u4E00-\u9FA5\uf900-\ufa2d\w\.\s]{6,18}$/,p:/^[0-9]{6}$/,m:/^13[0-9]{9}$|14[0-9]{9}|15[0-9]{9}$|18[0-9]{9}$/,e:/^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,url:/^(\w+:\/\/)?\w+(\.\w+)+.*$/},toString:Object.prototype.toString,isEmpty:function(k){return k===""||k===d.trim(this.attr("tip"))},getValue:function(m){var l,k=this;if(m.is(":radio")){l=k.find(":radio[name='"+m.attr("name")+"']:checked").val();l=l===b?"":l}else{if(m.is(":checkbox")){l="";k.find(":checkbox[name='"+m.attr("name")+"']:checked").each(function(){l+=d(this).val()+","});l=l===b?"":l}else{l=m.val()}}l=d.trim(l);return a.util.isEmpty.call(m,l)?"":l},enhance:function(l,m,n,k){var o=this;o.find("[datatype]").each(function(){if(l==2){if(d(this).parent().next().find(".Validform_checktip").length==0){d(this).parent().next().append("");d(this).siblings(".Validform_checktip").remove()}}else{if(l==3||l==4){if(d(this).siblings(".Validform_checktip").length==0){d(this).parent().append("");d(this).parent().next().find(".Validform_checktip").remove()}}}});o.find("input[recheck]").each(function(){if(this.validform_inited=="inited"){return true}this.validform_inited="inited";var q=d(this);var p=o.find("input[name='"+d(this).attr("recheck")+"']");p.bind("keyup",function(){if(p.val()==q.val()&&p.val()!=""){if(p.attr("tip")){if(p.attr("tip")==p.val()){return false}}q.trigger("blur")}}).bind("blur",function(){if(p.val()!=q.val()&&q.val()!=""){if(q.attr("tip")){if(q.attr("tip")==q.val()){return false}}q.trigger("blur")}})});o.find("[tip]").each(function(){if(this.validform_inited=="inited"){return true}this.validform_inited="inited";var q=d(this).attr("tip");var p=d(this).attr("altercss");d(this).focus(function(){if(d(this).val()==q){d(this).val("");if(p){d(this).removeClass(p)}}}).blur(function(){if(d.trim(d(this).val())===""){d(this).val(q);if(p){d(this).addClass(p)}}})});o.find(":checkbox[datatype],:radio[datatype]").each(function(){if(this.validform_inited=="inited"){return true}this.validform_inited="inited";var q=d(this);var p=q.attr("name");o.find("[name='"+p+"']").filter(":checkbox,:radio").bind("click",function(){setTimeout(function(){q.trigger("blur")},0)})});o.find("select[datatype][multiple]").bind("click",function(){var p=d(this);setTimeout(function(){p.trigger("blur")},0)});a.util.usePlugin.call(o,m,l,n,k)},usePlugin:function(o,l,n,r){var s=this,o=o||{};if(s.find("input[plugin='swfupload']").length&&typeof(swfuploadhandler)!="undefined"){var k={custom_settings:{form:s,showmsg:function(v,t,u){a.util.showmsg.call(s,v,l,{obj:s.find("input[plugin='swfupload']"),type:t,sweep:n})}}};k=d.extend(true,{},o.swfupload,k);s.find("input[plugin='swfupload']").each(function(t){if(this.validform_inited=="inited"){return true}this.validform_inited="inited";d(this).val("");swfuploadhandler.init(k,t)})}if(s.find("input[plugin='datepicker']").length&&d.fn.datePicker){o.datepicker=o.datepicker||{};if(o.datepicker.format){Date.format=o.datepicker.format;delete o.datepicker.format}if(o.datepicker.firstDayOfWeek){Date.firstDayOfWeek=o.datepicker.firstDayOfWeek;delete o.datepicker.firstDayOfWeek}s.find("input[plugin='datepicker']").each(function(t){if(this.validform_inited=="inited"){return true}this.validform_inited="inited";o.datepicker.callback&&d(this).bind("dateSelected",function(){var u=new Date(d.event._dpCache[this._dpId].getSelected()[0]).asString(Date.format);o.datepicker.callback(u,this)});d(this).datePicker(o.datepicker)})}if(s.find("input[plugin*='passwordStrength']").length&&d.fn.passwordStrength){o.passwordstrength=o.passwordstrength||{};o.passwordstrength.showmsg=function(u,v,t){a.util.showmsg.call(s,v,l,{obj:u,type:t,sweep:n})};s.find("input[plugin='passwordStrength']").each(function(t){if(this.validform_inited=="inited"){return true}this.validform_inited="inited";d(this).passwordStrength(o.passwordstrength)})}if(r!="addRule"&&o.jqtransform&&d.fn.jqTransSelect){if(s[0].jqTransSelected=="true"){return}s[0].jqTransSelected="true";var m=function(t){var u=d(".jqTransformSelectWrapper ul:visible");u.each(function(){var v=d(this).parents(".jqTransformSelectWrapper:first").find("select").get(0);if(!(t&&v.oLabel&&v.oLabel.get(0)==t.get(0))){d(this).hide()}})};var p=function(t){if(d(t.target).parents(".jqTransformSelectWrapper").length===0){m(d(t.target))}};var q=function(){d(document).mousedown(p)};if(o.jqtransform.selector){s.find(o.jqtransform.selector).filter('input:submit, input:reset, input[type="button"]').jqTransInputButton();s.find(o.jqtransform.selector).filter("input:text, input:password").jqTransInputText();s.find(o.jqtransform.selector).filter("input:checkbox").jqTransCheckBox();s.find(o.jqtransform.selector).filter("input:radio").jqTransRadio();s.find(o.jqtransform.selector).filter("textarea").jqTransTextarea();if(s.find(o.jqtransform.selector).filter("select").length>0){s.find(o.jqtransform.selector).filter("select").jqTransSelect();q()}}else{s.jqTransform()}s.find(".jqTransformSelectWrapper").find("li a").click(function(){d(this).parents(".jqTransformSelectWrapper").find("select").trigger("blur")})}},getNullmsg:function(o){var n=this;var m=/[\u4E00-\u9FA5\uf900-\ufa2da-zA-Z\s]+/g;var k;var l=o[0].settings.label||".Validform_label";l=n.siblings(l).eq(0).text()||n.siblings().find(l).eq(0).text()||n.parent().siblings(l).eq(0).text()||n.parent().siblings().find(l).eq(0).text();l=l.replace(/\s(?![a-zA-Z])/g,"").match(m);l=l?l.join(""):[""];m=/\{(.+)\|(.+)\}/;k=o.data("tipmsg").s||e.s;if(l!=""){k=k.replace(/\{0\|(.+)\}/,l);if(n.attr("recheck")){k=k.replace(/\{(.+)\}/,"");n.attr("nullmsg",k);return k}}else{k=n.is(":checkbox,:radio,select")?k.replace(/\{0\|(.+)\}/,""):k.replace(/\{0\|(.+)\}/,"$1")}k=n.is(":checkbox,:radio,select")?k.replace(m,"$2"):k.replace(m,"$1");n.attr("nullmsg",k);return k},getErrormsg:function(s,n,u){var o=/^(.+?)((\d+)-(\d+))?$/,m=/^(.+?)(\d+)-(\d+)$/,l=/(.*?)\d+(.+?)\d+(.*)/,q=n.match(o),t,r;if(u=="recheck"){r=s.data("tipmsg").reck||e.reck;return r}var p=d.extend({},e.w,s.data("tipmsg").w);if(q[0] in p){return s.data("tipmsg").w[q[0]]||e.w[q[0]]}for(var k in p){if(k.indexOf(q[1])!=-1&&m.test(k)){r=(s.data("tipmsg").w[k]||e.w[k]).replace(l,"$1"+q[3]+"$2"+q[4]+"$3");s.data("tipmsg").w[q[0]]=r;return r}}return s.data("tipmsg").def||e.def},_regcheck:function(t,n,u,A){var A=A,y=null,v=false,o=/\/.+\//g,k=/^(.+?)(\d+)-(\d+)$/,l=3;if(o.test(t)){var s=t.match(o)[0].slice(1,-1);var r=t.replace(o,"");var q=RegExp(s,r);v=q.test(n)}else{if(a.util.toString.call(a.util.dataType[t])=="[object Function]"){v=a.util.dataType[t](n,u,A,a.util.dataType);if(v===true||v===b){v=true}else{y=v;v=false}}else{if(!(t in a.util.dataType)){var m=t.match(k),z;if(!m){v=false;y=A.data("tipmsg").undef||e.undef}else{for(var B in a.util.dataType){z=B.match(k);if(!z){continue}if(m[1]===z[1]){var w=a.util.dataType[B].toString(),r=w.match(/\/[mgi]*/g)[1].replace("/",""),x=new RegExp("\\{"+z[2]+","+z[3]+"\\}","g");w=w.replace(/\/[mgi]*/g,"/").replace(x,"{"+m[2]+","+m[3]+"}").replace(/^\//,"").replace(/\/$/,"");a.util.dataType[t]=new RegExp(w,r);break}}}}if(a.util.toString.call(a.util.dataType[t])=="[object RegExp]"){v=a.util.dataType[t].test(n)}}}if(v){l=2;y=u.attr("sucmsg")||A.data("tipmsg").r||e.r;if(u.attr("recheck")){var p=A.find("input[name='"+u.attr("recheck")+"']:first");if(n!=p.val()){v=false;l=3;y=u.attr("errormsg")||a.util.getErrormsg.call(u,A,t,"recheck")}}}else{y=y||u.attr("errormsg")||a.util.getErrormsg.call(u,A,t);if(a.util.isEmpty.call(u,n)){y=u.attr("nullmsg")||a.util.getNullmsg.call(u,A)}}return{passed:v,type:l,info:y}},regcheck:function(n,s,m){var t=this,k=null,l=false,r=3;if(m.attr("ignore")==="ignore"&&a.util.isEmpty.call(m,s)){if(m.data("cked")){k=""}return{passed:true,type:4,info:k}}m.data("cked","cked");var u=a.util.parseDatatype(n);var q;for(var p=0;p=k.forms.length){return null}if(!(l in k.objects)){k.objects[l]=new a(d(k.forms[l]).get(),{},true)}return k.objects[l]},resetStatus:function(){var k=this;d(k.forms).each(function(){this.validform_status="normal"});return this},setStatus:function(k){var l=this;d(l.forms).each(function(){this.validform_status=k||"posting"});return this},getStatus:function(){var l=this;var k=d(l.forms)[0].validform_status;return k},ignore:function(k){var l=this;var k=k||"[datatype]";d(l.forms).find(k).each(function(){d(this).data("dataIgnore","dataIgnore").removeClass("Validform_error")});return this},unignore:function(k){var l=this;var k=k||"[datatype]";d(l.forms).find(k).each(function(){d(this).removeData("dataIgnore")});return this},addRule:function(n){var m=this;var n=n||[];for(var l=0;l0?k:0);n.css({left:l}).animate({top:k},{duration:m,queue:false})}function c(){if(d("#Validform_msg").length!==0){return false}j=d('
                ').appendTo("body");j.find("a.Validform_close").click(function(){j.hide();i=true;if(g){g.focus().addClass("Validform_error")}return false}).focus(function(){this.blur()});d(window).bind("scroll resize",function(){!i&&h(j,400)})}d.Showmsg=function(k){c();a.util.showmsg.call(f,k,1,{})};d.Hidemsg=function(){j.hide();i=true}})(jQuery,window); diff --git a/route/static/js/bootstrap.min.js b/route/static/js/bootstrap.min.js deleted file mode 100755 index 133aeecb9..000000000 --- a/route/static/js/bootstrap.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under the MIT license - */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
                ',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file diff --git a/route/static/js/clipboard.min.js b/route/static/js/clipboard.min.js deleted file mode 100755 index 7a4fde688..000000000 --- a/route/static/js/clipboard.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * clipboard.js v2.0.1 - * https://zenorocha.github.io/clipboard.js - * - * Licensed MIT © Zeno Rocha - */ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=3)}([function(t,e,n){var o,r,i;!function(a,c){r=[t,n(7)],o=c,void 0!==(i="function"==typeof o?o.apply(e,r):o)&&(t.exports=i)}(0,function(t,e){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var o=function(t){return t&&t.__esModule?t:{default:t}}(e),r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,o.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,o.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":r(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}();t.exports=a})},function(t,e,n){function o(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!c.string(e))throw new TypeError("Second argument must be a String");if(!c.fn(n))throw new TypeError("Third argument must be a Function");if(c.node(t))return r(t,e,n);if(c.nodeList(t))return i(t,e,n);if(c.string(t))return a(t,e,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function r(t,e,n){return t.addEventListener(e,n),{destroy:function(){t.removeEventListener(e,n)}}}function i(t,e,n){return Array.prototype.forEach.call(t,function(t){t.addEventListener(e,n)}),{destroy:function(){Array.prototype.forEach.call(t,function(t){t.removeEventListener(e,n)})}}}function a(t,e,n){return u(document.body,t,e,n)}var c=n(6),u=n(5);t.exports=o},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){function o(){r.off(t,o),e.apply(n,arguments)}var r=this;return o._=e,this.on(t,o,n)},emit:function(t){var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;for(o;o0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===d(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,f.default)(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new l.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return u("action",t)}},{key:"defaultTarget",value:function(t){var e=u("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return u("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach(function(t){n=n&&!!document.queryCommandSupported(t)}),n}}]),e}(s.default);t.exports=p})},function(t,e){function n(t,e){for(;t&&t.nodeType!==o;){if("function"==typeof t.matches&&t.matches(e))return t;t=t.parentNode}}var o=9;if("undefined"!=typeof Element&&!Element.prototype.matches){var r=Element.prototype;r.matches=r.matchesSelector||r.mozMatchesSelector||r.msMatchesSelector||r.oMatchesSelector||r.webkitMatchesSelector}t.exports=n},function(t,e,n){function o(t,e,n,o,r){var a=i.apply(this,arguments);return t.addEventListener(n,a,r),{destroy:function(){t.removeEventListener(n,a,r)}}}function r(t,e,n,r,i){return"function"==typeof t.addEventListener?o.apply(null,arguments):"function"==typeof n?o.bind(null,document).apply(null,arguments):("string"==typeof t&&(t=document.querySelectorAll(t)),Array.prototype.map.call(t,function(t){return o(t,e,n,r,i)}))}function i(t,e,n,o){return function(n){n.delegateTarget=a(n.target,e),n.delegateTarget&&o.call(t,n)}}var a=n(4);t.exports=r},function(t,e){e.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},e.nodeList=function(t){var n=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===n||"[object HTMLCollection]"===n)&&"length"in t&&(0===t.length||e.node(t[0]))},e.string=function(t){return"string"==typeof t||t instanceof String},e.fn=function(t){return"[object Function]"===Object.prototype.toString.call(t)}},function(t,e){function n(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}t.exports=n}])}); \ No newline at end of file diff --git a/route/static/js/echarts.min.js b/route/static/js/echarts.min.js deleted file mode 100644 index 85daf7e8d..000000000 --- a/route/static/js/echarts.min.js +++ /dev/null @@ -1,45 +0,0 @@ - -/* -* 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])},e(t,n)};function n(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}var i=function(){this.firefox=!1,this.ie=!1,this.edge=!1,this.newEdge=!1,this.weChat=!1},r=new function(){this.browser=new i,this.node=!1,this.wxa=!1,this.worker=!1,this.svgSupported=!1,this.touchEventsSupported=!1,this.pointerEventsSupported=!1,this.domSupported=!1,this.transformSupported=!1,this.transform3dSupported=!1,this.hasGlobalWindow="undefined"!=typeof window};"object"==typeof wx&&"function"==typeof wx.getSystemInfoSync?(r.wxa=!0,r.touchEventsSupported=!0):"undefined"==typeof document&&"undefined"!=typeof self?r.worker=!0:"undefined"==typeof navigator?(r.node=!0,r.svgSupported=!0):function(t,e){var n=e.browser,i=t.match(/Firefox\/([\d.]+)/),r=t.match(/MSIE\s([\d.]+)/)||t.match(/Trident\/.+?rv:(([\d.]+))/),o=t.match(/Edge?\/([\d.]+)/),a=/micromessenger/i.test(t);i&&(n.firefox=!0,n.version=i[1]);r&&(n.ie=!0,n.version=r[1]);o&&(n.edge=!0,n.version=o[1],n.newEdge=+o[1].split(".")[0]>18);a&&(n.weChat=!0);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;var s=document.documentElement.style;e.transform3dSupported=(n.ie&&"transition"in s||n.edge||"WebKitCSSMatrix"in window&&"m11"in new WebKitCSSMatrix||"MozPerspective"in s)&&!("OTransition"in s),e.transformSupported=e.transform3dSupported||n.ie&&+n.version>=9}(navigator.userAgent,r);var o="sans-serif",a="12px sans-serif";var s,l,u=function(t){var e={};if("undefined"==typeof JSON)return e;for(var n=0;n=0)o=r*t.length;else for(var c=0;c>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,a),l=function(t,e,n){for(var i=n?"invTrans":"trans",r=e[i],o=e.srcCoords,a=[],s=[],l=!0,u=0;u<4;u++){var h=t[u].getBoundingClientRect(),c=2*u,p=h.left,d=h.top;a.push(p,d),l=l&&o&&p===o[c]&&d===o[c+1],s.push(t[u].offsetLeft,t[u].offsetTop)}return l&&r?r:(e.srcCoords=a,e[i]=n?$t(s,a):$t(a,s))}(s,a,o);if(l)return l(t,n,i),!0}return!1}function te(t){return"CANVAS"===t.nodeName.toUpperCase()}var ee=/([&<>"'])/g,ne={"&":"&","<":"<",">":">",'"':""","'":"'"};function ie(t){return null==t?"":(t+"").replace(ee,(function(t,e){return ne[e]}))}var re=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,oe=[],ae=r.browser.firefox&&+r.browser.version.split(".")[0]<39;function se(t,e,n,i){return n=n||{},i?le(t,e,n):ae&&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):le(t,e,n),n}function le(t,e,n){if(r.domSupported&&t.getBoundingClientRect){var i=e.clientX,o=e.clientY;if(te(t)){var a=t.getBoundingClientRect();return n.zrX=i-a.left,void(n.zrY=o-a.top)}if(Qt(oe,t,i,o))return n.zrX=oe[0],void(n.zrY=oe[1])}n.zrX=n.zrY=0}function ue(t){return t||window.event}function he(t,e,n){if(null!=(e=ue(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&&se(t,r,e,n)}else{se(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&&re.test(e.type)&&(e.which=1&a?1:2&a?3:4&a?2:0),e}function ce(t,e,n,i){t.addEventListener(e,n,i)}var pe=function(t){t.preventDefault(),t.stopPropagation(),t.cancelBubble=!0};function de(t){return 2===t.which||3===t.which}var fe=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=ge(r)/ge(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}}}}};function ve(){return[1,0,0,1,0,0]}function me(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t}function xe(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 _e(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 be(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 we(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 Se(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 Me(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 Ie(t){var e=[1,0,0,1,0,0];return xe(e,t),e}var Te=Object.freeze({__proto__:null,create:ve,identity:me,copy:xe,mul:_e,translate:be,rotate:we,scale:Se,invert:Me,clone:Ie}),Ce=function(){function t(t,e){this.x=t||0,this.y=e||0}return t.prototype.copy=function(t){return this.x=t.x,this.y=t.y,this},t.prototype.clone=function(){return new t(this.x,this.y)},t.prototype.set=function(t,e){return this.x=t,this.y=e,this},t.prototype.equal=function(t){return t.x===this.x&&t.y===this.y},t.prototype.add=function(t){return this.x+=t.x,this.y+=t.y,this},t.prototype.scale=function(t){this.x*=t,this.y*=t},t.prototype.scaleAndAdd=function(t,e){this.x+=t.x*e,this.y+=t.y*e},t.prototype.sub=function(t){return this.x-=t.x,this.y-=t.y,this},t.prototype.dot=function(t){return this.x*t.x+this.y*t.y},t.prototype.len=function(){return Math.sqrt(this.x*this.x+this.y*this.y)},t.prototype.lenSquare=function(){return this.x*this.x+this.y*this.y},t.prototype.normalize=function(){var t=this.len();return this.x/=t,this.y/=t,this},t.prototype.distance=function(t){var e=this.x-t.x,n=this.y-t.y;return Math.sqrt(e*e+n*n)},t.prototype.distanceSquare=function(t){var e=this.x-t.x,n=this.y-t.y;return e*e+n*n},t.prototype.negate=function(){return this.x=-this.x,this.y=-this.y,this},t.prototype.transform=function(t){if(t){var e=this.x,n=this.y;return this.x=t[0]*e+t[2]*n+t[4],this.y=t[1]*e+t[3]*n+t[5],this}},t.prototype.toArray=function(t){return t[0]=this.x,t[1]=this.y,t},t.prototype.fromArray=function(t){this.x=t[0],this.y=t[1]},t.set=function(t,e,n){t.x=e,t.y=n},t.copy=function(t,e){t.x=e.x,t.y=e.y},t.len=function(t){return Math.sqrt(t.x*t.x+t.y*t.y)},t.lenSquare=function(t){return t.x*t.x+t.y*t.y},t.dot=function(t,e){return t.x*e.x+t.y*e.y},t.add=function(t,e,n){t.x=e.x+n.x,t.y=e.y+n.y},t.sub=function(t,e,n){t.x=e.x-n.x,t.y=e.y-n.y},t.scale=function(t,e,n){t.x=e.x*n,t.y=e.y*n},t.scaleAndAdd=function(t,e,n,i){t.x=e.x+n.x*i,t.y=e.y+n.y*i},t.lerp=function(t,e,n,i){var r=1-i;t.x=r*e.x+i*n.x,t.y=r*e.y+i*n.y},t}(),De=Math.min,Ae=Math.max,ke=new Ce,Le=new Ce,Pe=new Ce,Oe=new Ce,Re=new Ce,Ne=new Ce,Ee=function(){function t(t,e,n,i){n<0&&(t+=n,n=-n),i<0&&(e+=i,i=-i),this.x=t,this.y=e,this.width=n,this.height=i}return t.prototype.union=function(t){var e=De(t.x,this.x),n=De(t.y,this.y);isFinite(this.x)&&isFinite(this.width)?this.width=Ae(t.x+t.width,this.x+this.width)-e:this.width=t.width,isFinite(this.y)&&isFinite(this.height)?this.height=Ae(t.y+t.height,this.y+this.height)-n:this.height=t.height,this.x=e,this.y=n},t.prototype.applyTransform=function(e){t.applyTransform(this,this,e)},t.prototype.calculateTransform=function(t){var e=this,n=t.width/e.width,i=t.height/e.height,r=[1,0,0,1,0,0];return be(r,r,[-e.x,-e.y]),Se(r,r,[n,i]),be(r,r,[t.x,t.y]),r},t.prototype.intersect=function(e,n){if(!e)return!1;e instanceof t||(e=t.create(e));var i=this,r=i.x,o=i.x+i.width,a=i.y,s=i.y+i.height,l=e.x,u=e.x+e.width,h=e.y,c=e.y+e.height,p=!(of&&(f=x,gf&&(f=_,v=n.x&&t<=n.x+n.width&&e>=n.y&&e<=n.y+n.height},t.prototype.clone=function(){return new t(this.x,this.y,this.width,this.height)},t.prototype.copy=function(e){t.copy(this,e)},t.prototype.plain=function(){return{x:this.x,y:this.y,width:this.width,height:this.height}},t.prototype.isFinite=function(){return isFinite(this.x)&&isFinite(this.y)&&isFinite(this.width)&&isFinite(this.height)},t.prototype.isZero=function(){return 0===this.width||0===this.height},t.create=function(e){return new t(e.x,e.y,e.width,e.height)},t.copy=function(t,e){t.x=e.x,t.y=e.y,t.width=e.width,t.height=e.height},t.applyTransform=function(e,n,i){if(i){if(i[1]<1e-5&&i[1]>-1e-5&&i[2]<1e-5&&i[2]>-1e-5){var r=i[0],o=i[3],a=i[4],s=i[5];return e.x=n.x*r+a,e.y=n.y*o+s,e.width=n.width*r,e.height=n.height*o,e.width<0&&(e.x+=e.width,e.width=-e.width),void(e.height<0&&(e.y+=e.height,e.height=-e.height))}ke.x=Pe.x=n.x,ke.y=Oe.y=n.y,Le.x=Oe.x=n.x+n.width,Le.y=Pe.y=n.y+n.height,ke.transform(i),Oe.transform(i),Le.transform(i),Pe.transform(i),e.x=De(ke.x,Le.x,Pe.x,Oe.x),e.y=De(ke.y,Le.y,Pe.y,Oe.y);var l=Ae(ke.x,Le.x,Pe.x,Oe.x),u=Ae(ke.y,Le.y,Pe.y,Oe.y);e.width=l-e.x,e.height=u-e.y}else e!==n&&t.copy(e,n)},t}(),ze="silent";function Ve(){pe(this.event)}var Be=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}(jt),Fe=function(t,e){this.x=t,this.y=e},Ge=["click","dblclick","mousewheel","mouseout","mouseup","mousedown","mousemove","contextmenu"],We=new Ee(0,0,0,0),He=function(t){function e(e,n,i,r,o){var a=t.call(this)||this;return a._hovered=new Fe(0,0),a.storage=e,a.painter=n,a.painterRoot=r,a._pointerSize=o,i=i||new Be,a.proxy=null,a.setHandlerProxy(i),a._draggingMgr=new Zt(a),a}return n(e,t),e.prototype.setHandlerProxy=function(t){this.proxy&&this.proxy.dispose(),t&&(E(Ge,(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=Xe(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 Fe(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 Fe(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:Ve}}(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){var i=this.storage.getDisplayList(),r=new Fe(t,e);if(Ue(i,r,t,e,n),this._pointerSize&&!r.target){for(var o=[],a=this._pointerSize,s=a/2,l=new Ee(t-s,e-s,a,a),u=i.length-1;u>=0;u--){var h=i[u];h===n||h.ignore||h.ignoreCoarsePointer||h.parent&&h.parent.ignoreCoarsePointer||(We.copy(h.getBoundingRect()),h.transform&&We.applyTransform(h.transform),We.intersect(l)&&o.push(h))}if(o.length)for(var c=Math.PI/12,p=2*Math.PI,d=0;d=0;o--){var a=t[o],s=void 0;if(a!==r&&!a.ignore&&(s=Ye(a,n,i))&&(!e.topTarget&&(e.topTarget=a),s!==ze)){e.target=a;break}}}function Xe(t,e,n){var i=t.painter;return e<0||e>i.getWidth()||n<0||n>i.getHeight()}E(["click","mousedown","mouseup","mousewheel","dblclick","contextmenu"],(function(t){He.prototype[t]=function(e){var n,i,r=e.zrX,o=e.zrY,a=Xe(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||Vt(this._downPoint,[e.zrX,e.zrY])>4)return;this._downPoint=null}this.dispatchToElement(n,t,e)}}));function Ze(t,e,n,i){var r=e+1;if(r===n)return 1;if(i(t[r++],t[e])<0){for(;r=0;)r++;return r-e}function je(t,e,n,i,r){for(i===e&&i++;i>>1])<0?l=o:s=o+1;var u=i-s;switch(u){case 3:t[s+3]=t[s+2];case 2:t[s+2]=t[s+1];case 1:t[s+1]=t[s];break;default:for(;u>0;)t[s+u]=t[s+u-1],u--}t[s]=a}}function qe(t,e,n,i,r,o){var a=0,s=0,l=1;if(o(t,e[n+r])>0){for(s=i-r;l0;)a=l,(l=1+(l<<1))<=0&&(l=s);l>s&&(l=s),a+=r,l+=r}else{for(s=r+1;ls&&(l=s);var u=a;a=r-l,l=r-u}for(a++;a>>1);o(t,e[n+h])>0?a=h+1:l=h}return l}function Ke(t,e,n,i,r,o){var a=0,s=0,l=1;if(o(t,e[n+r])<0){for(s=r+1;ls&&(l=s);var u=a;a=r-l,l=r-u}else{for(s=i-r;l=0;)a=l,(l=1+(l<<1))<=0&&(l=s);l>s&&(l=s),a+=r,l+=r}for(a++;a>>1);o(t,e[n+h])<0?l=h:a=h+1}return l}function $e(t,e){var n,i,r=7,o=0;t.length;var a=[];function s(s){var l=n[s],u=i[s],h=n[s+1],c=i[s+1];i[s]=u+c,s===o-3&&(n[s+1]=n[s+2],i[s+1]=i[s+2]),o--;var p=Ke(t[h],t,l,u,0,e);l+=p,0!==(u-=p)&&0!==(c=qe(t[l+u-1],t,h,c,c-1,e))&&(u<=c?function(n,i,o,s){var l=0;for(l=0;l=7||d>=7);if(f)break;g<0&&(g=0),g+=2}if((r=g)<1&&(r=1),1===i){for(l=0;l=0;l--)t[d+l]=t[p+l];return void(t[c]=a[h])}var f=r;for(;;){var g=0,y=0,v=!1;do{if(e(a[h],t[u])<0){if(t[c--]=t[u--],g++,y=0,0==--i){v=!0;break}}else if(t[c--]=a[h--],y++,g=0,1==--s){v=!0;break}}while((g|y)=0;l--)t[d+l]=t[p+l];if(0===i){v=!0;break}}if(t[c--]=a[h--],1==--s){v=!0;break}if(0!==(y=s-qe(t[u],a,0,s,s-1,e))){for(s-=y,d=(c-=y)+1,p=(h-=y)+1,l=0;l=7||y>=7);if(v)break;f<0&&(f=0),f+=2}(r=f)<1&&(r=1);if(1===s){for(d=(c-=i)+1,p=(u-=i)+1,l=i-1;l>=0;l--)t[d+l]=t[p+l];t[c]=a[h]}else{if(0===s)throw new Error;for(p=c-(s-1),l=0;l1;){var t=o-2;if(t>=1&&i[t-1]<=i[t]+i[t+1]||t>=2&&i[t-2]<=i[t]+i[t-1])i[t-1]i[t+1])break;s(t)}},forceMergeRuns:function(){for(;o>1;){var t=o-2;t>0&&i[t-1]=32;)e|=1&t,t>>=1;return t+e}(r);do{if((o=Ze(t,n,i,e))s&&(l=s),je(t,n,n+l,n+o,e),o=l}a.pushRun(n,o),a.mergeRuns(),r-=o,n+=o}while(0!==r);a.forceMergeRuns()}}}var Qe=!1;function tn(){Qe||(Qe=!0,console.warn("z / z2 / zlevel of displayable is invalid, which may cause unexpected errors"))}function en(t,e){return t.zlevel===e.zlevel?t.z===e.z?t.z2-e.z2:t.z-e.z:t.zlevel-e.zlevel}var nn=function(){function t(){this._roots=[],this._displayList=[],this._displayListLen=0,this.displayableSortFunc=en}return t.prototype.traverse=function(t,e){for(var n=0;n0&&(u.__clipPaths=[]),isNaN(u.z)&&(tn(),u.z=0),isNaN(u.z2)&&(tn(),u.z2=0),isNaN(u.zlevel)&&(tn(),u.zlevel=0),this._displayList[this._displayListLen++]=u}var h=t.getDecalElement&&t.getDecalElement();h&&this._updateAndAddDisplayable(h,e,n);var c=t.getTextGuideLine();c&&this._updateAndAddDisplayable(c,e,n);var p=t.getTextContent();p&&this._updateAndAddDisplayable(p,e,n)}},t.prototype.addRoot=function(t){t.__zr&&t.__zr.storage===this||this._roots.push(t)},t.prototype.delRoot=function(t){if(t instanceof Array)for(var e=0,n=t.length;e=0&&this._roots.splice(i,1)}},t.prototype.delAllRoots=function(){this._roots=[],this._displayList=[],this._displayListLen=0},t.prototype.getRoots=function(){return this._roots},t.prototype.dispose=function(){this._displayList=null,this._roots=null},t}(),rn=r.hasGlobalWindow&&(window.requestAnimationFrame&&window.requestAnimationFrame.bind(window)||window.msRequestAnimationFrame&&window.msRequestAnimationFrame.bind(window)||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame)||function(t){return setTimeout(t,16)},on={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-on.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*on.bounceIn(2*t):.5*on.bounceOut(2*t-1)+.5}},an=Math.pow,sn=Math.sqrt,ln=1e-8,un=1e-4,hn=sn(3),cn=1/3,pn=Mt(),dn=Mt(),fn=Mt();function gn(t){return t>-1e-8&&tln||t<-1e-8}function vn(t,e,n,i,r){var o=1-r;return o*o*(o*t+3*r*e)+r*r*(r*i+3*o*n)}function mn(t,e,n,i,r){var o=1-r;return 3*(((e-t)*o+2*(n-e)*r)*o+(i-n)*r*r)}function xn(t,e,n,i,r,o){var a=i+3*(e-n)-t,s=3*(n-2*e+t),l=3*(e-t),u=t-r,h=s*s-3*a*l,c=s*l-9*a*u,p=l*l-3*s*u,d=0;if(gn(h)&&gn(c)){if(gn(s))o[0]=0;else(M=-l/s)>=0&&M<=1&&(o[d++]=M)}else{var f=c*c-4*h*p;if(gn(f)){var g=c/h,y=-g/2;(M=-s/a+g)>=0&&M<=1&&(o[d++]=M),y>=0&&y<=1&&(o[d++]=y)}else if(f>0){var v=sn(f),m=h*s+1.5*a*(-c+v),x=h*s+1.5*a*(-c-v);(M=(-s-((m=m<0?-an(-m,cn):an(m,cn))+(x=x<0?-an(-x,cn):an(x,cn))))/(3*a))>=0&&M<=1&&(o[d++]=M)}else{var _=(2*h*s-3*a*c)/(2*sn(h*h*h)),b=Math.acos(_)/3,w=sn(h),S=Math.cos(b),M=(-s-2*w*S)/(3*a),I=(y=(-s+w*(S+hn*Math.sin(b)))/(3*a),(-s+w*(S-hn*Math.sin(b)))/(3*a));M>=0&&M<=1&&(o[d++]=M),y>=0&&y<=1&&(o[d++]=y),I>=0&&I<=1&&(o[d++]=I)}}return d}function _n(t,e,n,i,r){var o=6*n-12*e+6*t,a=9*e+3*i-3*t-9*n,s=3*e-3*t,l=0;if(gn(a)){if(yn(o))(h=-s/o)>=0&&h<=1&&(r[l++]=h)}else{var u=o*o-4*a*s;if(gn(u))r[0]=-o/(2*a);else if(u>0){var h,c=sn(u),p=(-o-c)/(2*a);(h=(-o+c)/(2*a))>=0&&h<=1&&(r[l++]=h),p>=0&&p<=1&&(r[l++]=p)}}return l}function bn(t,e,n,i,r,o){var a=(e-t)*r+t,s=(n-e)*r+e,l=(i-n)*r+n,u=(s-a)*r+a,h=(l-s)*r+s,c=(h-u)*r+u;o[0]=t,o[1]=a,o[2]=u,o[3]=c,o[4]=c,o[5]=h,o[6]=l,o[7]=i}function wn(t,e,n,i,r,o,a,s,l,u,h){var c,p,d,f,g,y=.005,v=1/0;pn[0]=l,pn[1]=u;for(var m=0;m<1;m+=.05)dn[0]=vn(t,n,r,a,m),dn[1]=vn(e,i,o,s,m),(f=Ft(pn,dn))=0&&f=0&&y=1?1:xn(0,i,o,1,t,s)&&vn(0,r,a,1,s[0])}}}var Pn=function(){function t(t){this._inited=!1,this._startTime=0,this._pausedTime=0,this._paused=!1,this._life=t.life||1e3,this._delay=t.delay||0,this.loop=t.loop||!1,this.onframe=t.onframe||bt,this.ondestroy=t.ondestroy||bt,this.onrestart=t.onrestart||bt,t.easing&&this.setEasing(t.easing)}return t.prototype.step=function(t,e){if(this._inited||(this._startTime=t+this._delay,this._inited=!0),!this._paused){var n=this._life,i=t-this._startTime-this._pausedTime,r=i/n;r<0&&(r=0),r=Math.min(r,1);var o=this.easingFunc,a=o?o(r):r;if(this.onframe(a),1===r){if(!this.loop)return!0;var s=i%n;this._startTime=t-s,this._pausedTime=0,this.onrestart()}return!1}this._pausedTime+=e},t.prototype.pause=function(){this._paused=!0},t.prototype.resume=function(){this._paused=!1},t.prototype.setEasing=function(t){this.easing=t,this.easingFunc=U(t)?t:on[t]||Ln(t)},t}(),On=function(t){this.value=t},Rn=function(){function t(){this._len=0}return t.prototype.insert=function(t){var e=new On(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}(),Nn=function(){function t(t){this._list=new Rn,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 On(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}(),En={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 zn(t){return(t=Math.round(t))<0?0:t>255?255:t}function Vn(t){return t<0?0:t>1?1:t}function Bn(t){var e=t;return e.length&&"%"===e.charAt(e.length-1)?zn(parseFloat(e)/100*255):zn(parseInt(e,10))}function Fn(t){var e=t;return e.length&&"%"===e.charAt(e.length-1)?Vn(parseFloat(e)/100):Vn(parseFloat(e))}function Gn(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 Wn(t,e,n){return t+(e-t)*n}function Hn(t,e,n,i,r){return t[0]=e,t[1]=n,t[2]=i,t[3]=r,t}function Yn(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t}var Un=new Nn(20),Xn=null;function Zn(t,e){Xn&&Yn(Xn,e),Xn=Un.put(t,Xn||e.slice())}function jn(t,e){if(t){e=e||[];var n=Un.get(t);if(n)return Yn(e,n);var i=(t+="").replace(/ /g,"").toLowerCase();if(i in En)return Yn(e,En[i]),Zn(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?(Hn(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),Zn(t,e),e):void Hn(e,0,0,0,1):7===o||9===o?(r=parseInt(i.slice(1,7),16))>=0&&r<=16777215?(Hn(e,(16711680&r)>>16,(65280&r)>>8,255&r,9===o?parseInt(i.slice(7),16)/255:1),Zn(t,e),e):void Hn(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?Hn(e,+u[0],+u[1],+u[2],1):Hn(e,0,0,0,1);h=Fn(u.pop());case"rgb":return u.length>=3?(Hn(e,Bn(u[0]),Bn(u[1]),Bn(u[2]),3===u.length?h:Fn(u[3])),Zn(t,e),e):void Hn(e,0,0,0,1);case"hsla":return 4!==u.length?void Hn(e,0,0,0,1):(u[3]=Fn(u[3]),qn(u,e),Zn(t,e),e);case"hsl":return 3!==u.length?void Hn(e,0,0,0,1):(qn(u,e),Zn(t,e),e);default:return}}Hn(e,0,0,0,1)}}function qn(t,e){var n=(parseFloat(t[0])%360+360)%360/360,i=Fn(t[1]),r=Fn(t[2]),o=r<=.5?r*(i+1):r+i-r*i,a=2*r-o;return Hn(e=e||[],zn(255*Gn(a,o,n+1/3)),zn(255*Gn(a,o,n)),zn(255*Gn(a,o,n-1/3)),1),4===t.length&&(e[3]=t[3]),e}function Kn(t,e){var n=jn(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 ii(n,4===n.length?"rgba":"rgb")}}function $n(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]=zn(Wn(a[0],s[0],l)),n[1]=zn(Wn(a[1],s[1],l)),n[2]=zn(Wn(a[2],s[2],l)),n[3]=Vn(Wn(a[3],s[3],l)),n}}var Jn=$n;function Qn(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=jn(e[r]),s=jn(e[o]),l=i-r,u=ii([zn(Wn(a[0],s[0],l)),zn(Wn(a[1],s[1],l)),zn(Wn(a[2],s[2],l)),Vn(Wn(a[3],s[3],l))],"rgba");return n?{color:u,leftIndex:r,rightIndex:o,value:i}:u}}var ti=Qn;function ei(t,e,n,i){var r=jn(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]=Fn(n)),null!=i&&(r[2]=Fn(i)),ii(qn(r),"rgba")}function ni(t,e){var n=jn(t);if(n&&null!=e)return n[3]=Vn(e),ii(n,"rgba")}function ii(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 ri(t,e){var n=jn(t);return n?(.299*n[0]+.587*n[1]+.114*n[2])*n[3]/255+(1-n[3])*e:0}var oi=Object.freeze({__proto__:null,parse:jn,lift:Kn,toHex:function(t){var e=jn(t);if(e)return((1<<24)+(e[0]<<16)+(e[1]<<8)+ +e[2]).toString(16).slice(1)},fastLerp:$n,fastMapToColor:Jn,lerp:Qn,mapToColor:ti,modifyHSL:ei,modifyAlpha:ni,stringify:ii,lum:ri,random:function(){return ii([Math.round(255*Math.random()),Math.round(255*Math.random()),Math.round(255*Math.random())],"rgb")}}),ai=Math.round;function si(t){var e;if(t&&"transparent"!==t){if("string"==typeof t&&t.indexOf("rgba")>-1){var n=jn(t);n&&(t="rgb("+n[0]+","+n[1]+","+n[2]+")",e=n[3])}}else t="none";return{color:t,opacity:null==e?1:e}}var li=1e-4;function ui(t){return t-1e-4}function hi(t){return ai(1e3*t)/1e3}function ci(t){return ai(1e4*t)/1e4}var pi={left:"start",right:"end",center:"middle",middle:"middle"};function di(t){return t&&!!t.image}function fi(t){return di(t)||function(t){return t&&!!t.svgElement}(t)}function gi(t){return"linear"===t.type}function yi(t){return"radial"===t.type}function vi(t){return t&&("linear"===t.type||"radial"===t.type)}function mi(t){return"url(#"+t+")"}function xi(t){var e=t.getGlobalScale(),n=Math.max(e[0],e[1]);return Math.max(Math.ceil(Math.log(n)/Math.log(10)),1)}function _i(t){var e=t.x||0,n=t.y||0,i=(t.rotation||0)*wt,r=rt(t.scaleX,1),o=rt(t.scaleY,1),a=t.skewX||0,s=t.skewY||0,l=[];return(e||n)&&l.push("translate("+e+"px,"+n+"px)"),i&&l.push("rotate("+i+")"),1===r&&1===o||l.push("scale("+r+","+o+")"),(a||s)&&l.push("skew("+ai(a*wt)+"deg, "+ai(s*wt)+"deg)"),l.join(" ")}var bi=r.hasGlobalWindow&&U(window.btoa)?function(t){return window.btoa(unescape(encodeURIComponent(t)))}:"undefined"!=typeof Buffer?function(t){return Buffer.from(t).toString("base64")}:function(t){return null},wi=Array.prototype.slice;function Si(t,e,n){return(e-t)*n+t}function Mi(t,e,n,i){for(var r=e.length,o=0;oi?e:t,o=Math.min(n,i),a=r[o-1]||{color:[0,0,0,0],offset:0},s=o;sa)i.length=a;else for(var s=o;s=1},t.prototype.getAdditiveTrack=function(){return this._additiveTrack},t.prototype.addKeyframe=function(t,e,n){this._needsSort=!0;var i=this.keyframes,r=i.length,o=!1,a=6,s=e;if(N(e)){var l=function(t){return N(t&&t[0])?2:1}(e);a=l,(1===l&&!j(e[0])||2===l&&!j(e[0][0]))&&(o=!0)}else if(j(e)&&!nt(e))a=0;else if(X(e))if(isNaN(+e)){var u=jn(e);u&&(s=u,a=3)}else a=0;else if(Q(e)){var h=A({},s);h.colorStops=z(e.colorStops,(function(t){return{offset:t.offset,color:jn(t.color)}})),gi(e)?a=4:yi(e)&&(a=5),s=h}0===r?this.valType=a:a===this.valType&&6!==a||(o=!0),this.discrete=this.discrete||o;var c={time:t,value:s,rawValue:e,percent:0};return n&&(c.easing=n,c.easingFunc=U(n)?n:on[n]||Ln(n)),i.push(c),c},t.prototype.prepare=function(t,e){var n=this.keyframes;this._needsSort&&n.sort((function(t,e){return t.time-e.time}));for(var i=this.valType,r=n.length,o=n[r-1],a=this.discrete,s=Pi(i),l=Li(i),u=0;u=0&&!(l[n].percent<=e);n--);n=d(n,u-2)}else{for(n=p;ne);n++);n=d(n-1,u-2)}r=l[n+1],i=l[n]}if(i&&r){this._lastFr=n,this._lastFrP=e;var f=r.percent-i.percent,g=0===f?1:d((e-i.percent)/f,1);r.easingFunc&&(g=r.easingFunc(g));var y=o?this._additiveValue:c?Oi:t[h];if(!Pi(s)&&!c||y||(y=this._additiveValue=[]),this.discrete)t[h]=g<1?i.rawValue:r.rawValue;else if(Pi(s))1===s?Mi(y,i[a],r[a],g):function(t,e,n,i){for(var r=e.length,o=r&&e[0].length,a=0;a0&&s.addKeyframe(0,Ai(l),i),this._trackKeys.push(a)}s.addKeyframe(t,Ai(e[a]),i)}return this._maxTime=Math.max(this._maxTime,t),this},t.prototype.pause=function(){this._clip.pause(),this._paused=!0},t.prototype.resume=function(){this._clip.resume(),this._paused=!1},t.prototype.isPaused=function(){return!!this._paused},t.prototype.duration=function(t){return this._maxTime=t,this._force=!0,this},t.prototype._doneCallback=function(){this._setTracksFinished(),this._clip=null;var t=this._doneCbs;if(t)for(var e=t.length,n=0;n0)){this._started=1;for(var e=this,n=[],i=this._maxTime||0,r=0;r1){var a=o.pop();r.addKeyframe(a.time,t[i]),r.prepare(this._maxTime,r.getAdditiveTrack())}}}},t}();function Ei(){return(new Date).getTime()}var zi,Vi,Bi=function(t){function e(e){var n=t.call(this)||this;return n._running=!1,n._time=0,n._pausedTime=0,n._pauseStart=0,n._paused=!1,e=e||{},n.stage=e.stage||{},n}return n(e,t),e.prototype.addClip=function(t){t.animation&&this.removeClip(t),this._head?(this._tail.next=t,t.prev=this._tail,t.next=null,this._tail=t):this._head=this._tail=t,t.animation=this},e.prototype.addAnimator=function(t){t.animation=this;var e=t.getClip();e&&this.addClip(e)},e.prototype.removeClip=function(t){if(t.animation){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=t.animation=null}},e.prototype.removeAnimator=function(t){var e=t.getClip();e&&this.removeClip(e),t.animation=null},e.prototype.update=function(t){for(var e=Ei()-this._pausedTime,n=e-this._time,i=this._head;i;){var r=i.next;i.step(e,n)?(i.ondestroy(),this.removeClip(i),i=r):i=r}this._time=e,t||(this.trigger("frame",n),this.stage.update&&this.stage.update())},e.prototype._startLoop=function(){var t=this;this._running=!0,rn((function e(){t._running&&(rn(e),!t._paused&&t.update())}))},e.prototype.start=function(){this._running||(this._time=Ei(),this._pausedTime=0,this._startLoop())},e.prototype.stop=function(){this._running=!1},e.prototype.pause=function(){this._paused||(this._pauseStart=Ei(),this._paused=!0)},e.prototype.resume=function(){this._paused&&(this._pausedTime+=Ei()-this._pauseStart,this._paused=!1)},e.prototype.clear=function(){for(var t=this._head;t;){var e=t.next;t.prev=t.next=t.animation=null,t=e}this._head=this._tail=null},e.prototype.isFinished=function(){return null==this._head},e.prototype.animate=function(t,e){e=e||{},this.start();var n=new Ni(t,e.loop);return this.addAnimator(n),n},e}(jt),Fi=r.domSupported,Gi=(Vi={pointerdown:1,pointerup:1,pointermove:1,pointerout:1},{mouse:zi=["click","dblclick","mousewheel","wheel","mouseout","mouseup","mousedown","mousemove","contextmenu"],touch:["touchstart","touchend","touchmove"],pointer:z(zi,(function(t){var e=t.replace("mouse","pointer");return Vi.hasOwnProperty(e)?e:t}))}),Wi=["mousemove","mouseup"],Hi=["pointermove","pointerup"],Yi=!1;function Ui(t){var e=t.pointerType;return"pen"===e||"touch"===e}function Xi(t){t&&(t.zrByTouch=!0)}function Zi(t,e){for(var n=e,i=!1;n&&9!==n.nodeType&&!(i=n.domBelongToZr||n!==e&&n===t.painterRoot);)n=n.parentNode;return i}var ji=function(t,e){this.stopPropagation=bt,this.stopImmediatePropagation=bt,this.preventDefault=bt,this.type=e.type,this.target=this.currentTarget=t.dom,this.pointerType=e.pointerType,this.clientX=e.clientX,this.clientY=e.clientY},qi={mousedown:function(t){t=he(this.dom,t),this.__mayPointerCapture=[t.zrX,t.zrY],this.trigger("mousedown",t)},mousemove:function(t){t=he(this.dom,t);var e=this.__mayPointerCapture;!e||t.zrX===e[0]&&t.zrY===e[1]||this.__togglePointerCapture(!0),this.trigger("mousemove",t)},mouseup:function(t){t=he(this.dom,t),this.__togglePointerCapture(!1),this.trigger("mouseup",t)},mouseout:function(t){Zi(this,(t=he(this.dom,t)).toElement||t.relatedTarget)||(this.__pointerCapturing&&(t.zrEventControl="no_globalout"),this.trigger("mouseout",t))},wheel:function(t){Yi=!0,t=he(this.dom,t),this.trigger("mousewheel",t)},mousewheel:function(t){Yi||(t=he(this.dom,t),this.trigger("mousewheel",t))},touchstart:function(t){Xi(t=he(this.dom,t)),this.__lastTouchMoment=new Date,this.handler.processGesture(t,"start"),qi.mousemove.call(this,t),qi.mousedown.call(this,t)},touchmove:function(t){Xi(t=he(this.dom,t)),this.handler.processGesture(t,"change"),qi.mousemove.call(this,t)},touchend:function(t){Xi(t=he(this.dom,t)),this.handler.processGesture(t,"end"),qi.mouseup.call(this,t),+new Date-+this.__lastTouchMoment<300&&qi.click.call(this,t)},pointerdown:function(t){qi.mousedown.call(this,t)},pointermove:function(t){Ui(t)||qi.mousemove.call(this,t)},pointerup:function(t){qi.mouseup.call(this,t)},pointerout:function(t){Ui(t)||qi.mouseout.call(this,t)}};E(["click","dblclick","contextmenu"],(function(t){qi[t]=function(e){e=he(this.dom,e),this.trigger(t,e)}}));var Ki={pointermove:function(t){Ui(t)||Ki.mousemove.call(this,t)},pointerup:function(t){Ki.mouseup.call(this,t)},mousemove:function(t){this.trigger("mousemove",t)},mouseup:function(t){var e=this.__pointerCapturing;this.__togglePointerCapture(!1),this.trigger("mouseup",t),e&&(t.zrEventControl="only_globalout",this.trigger("mouseout",t))}};function $i(t,e){var n=e.domHandlers;r.pointerEventsSupported?E(Gi.pointer,(function(i){Qi(e,i,(function(e){n[i].call(t,e)}))})):(r.touchEventsSupported&&E(Gi.touch,(function(i){Qi(e,i,(function(r){n[i].call(t,r),function(t){t.touching=!0,null!=t.touchTimer&&(clearTimeout(t.touchTimer),t.touchTimer=null),t.touchTimer=setTimeout((function(){t.touching=!1,t.touchTimer=null}),700)}(e)}))})),E(Gi.mouse,(function(i){Qi(e,i,(function(r){r=ue(r),e.touching||n[i].call(t,r)}))})))}function Ji(t,e){function n(n){Qi(e,n,(function(i){i=ue(i),Zi(t,i.target)||(i=function(t,e){return he(t.dom,new ji(t,e),!0)}(t,i),e.domHandlers[n].call(t,i))}),{capture:!0})}r.pointerEventsSupported?E(Hi,n):r.touchEventsSupported||E(Wi,n)}function Qi(t,e,n,i){t.mounted[e]=n,t.listenerOpts[e]=i,ce(t.domTarget,e,n,i)}function tr(t){var e,n,i,r,o=t.mounted;for(var a in o)o.hasOwnProperty(a)&&(e=t.domTarget,n=a,i=o[a],r=t.listenerOpts[a],e.removeEventListener(n,i,r));t.mounted={}}var er=function(t,e){this.mounted={},this.listenerOpts={},this.touching=!1,this.domTarget=t,this.domHandlers=e},nr=function(t){function e(e,n){var i=t.call(this)||this;return i.__pointerCapturing=!1,i.dom=e,i.painterRoot=n,i._localHandlerScope=new er(e,qi),Fi&&(i._globalHandlerScope=new er(document,Ki)),$i(i,i._localHandlerScope),i}return n(e,t),e.prototype.dispose=function(){tr(this._localHandlerScope),Fi&&tr(this._globalHandlerScope)},e.prototype.setCursor=function(t){this.dom.style&&(this.dom.style.cursor=t||"default")},e.prototype.__togglePointerCapture=function(t){if(this.__mayPointerCapture=null,Fi&&+this.__pointerCapturing^+t){this.__pointerCapturing=t;var e=this._globalHandlerScope;t?Ji(this,e):tr(e)}},e}(jt),ir=1;r.hasGlobalWindow&&(ir=Math.max(window.devicePixelRatio||window.screen&&window.screen.deviceXDPI/window.screen.logicalXDPI||1,1));var rr=ir,or="#333",ar="#ccc",sr=me,lr=5e-5;function ur(t){return t>lr||t<-5e-5}var hr=[],cr=[],pr=[1,0,0,1,0,0],dr=Math.abs,fr=function(){function t(){}return t.prototype.getLocalTransform=function(e){return t.getLocalTransform(this,e)},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.setSkew=function(t){this.skewX=t[0],this.skewY=t[1]},t.prototype.setOrigin=function(t){this.originX=t[0],this.originY=t[1]},t.prototype.needLocalTransform=function(){return ur(this.rotation)||ur(this.x)||ur(this.y)||ur(this.scaleX-1)||ur(this.scaleY-1)||ur(this.skewX)||ur(this.skewY)},t.prototype.updateTransform=function(){var t=this.parent&&this.parent.transform,e=this.needLocalTransform(),n=this.transform;e||t?(n=n||[1,0,0,1,0,0],e?this.getLocalTransform(n):sr(n),t&&(e?_e(n,t,n):xe(n,t)),this.transform=n,this._resolveGlobalScaleRatio(n)):n&&sr(n)},t.prototype._resolveGlobalScaleRatio=function(t){var e=this.globalScaleRatio;if(null!=e&&1!==e){this.getGlobalScale(hr);var n=hr[0]<0?-1:1,i=hr[1]<0?-1:1,r=((hr[0]-n)*e+n)/hr[0]||0,o=((hr[1]-i)*e+i)/hr[1]||0;t[0]*=r,t[1]*=r,t[2]*=o,t[3]*=o}this.invTransform=this.invTransform||[1,0,0,1,0,0],Me(this.invTransform,t)},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],i=Math.atan2(t[1],t[0]),r=Math.PI/2+i-Math.atan2(t[3],t[2]);n=Math.sqrt(n)*Math.cos(r),e=Math.sqrt(e),this.skewX=r,this.skewY=0,this.rotation=-i,this.x=+t[4],this.y=+t[5],this.scaleX=e,this.scaleY=n,this.originX=0,this.originY=0}},t.prototype.decomposeTransform=function(){if(this.transform){var t=this.parent,e=this.transform;t&&t.transform&&(_e(cr,t.invTransform,e),e=cr);var n=this.originX,i=this.originY;(n||i)&&(pr[4]=n,pr[5]=i,_e(cr,e,pr),cr[4]-=n,cr[5]-=i,e=cr),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&&Wt(n,n,i),n},t.prototype.transformCoordToGlobal=function(t,e){var n=[t,e],i=this.transform;return i&&Wt(n,n,i),n},t.prototype.getLineScale=function(){var t=this.transform;return t&&dr(t[0]-1)>1e-10&&dr(t[3]-1)>1e-10?Math.sqrt(dr(t[0]*t[3]-t[2]*t[1])):1},t.prototype.copyTransform=function(t){yr(this,t)},t.getLocalTransform=function(t,e){e=e||[];var n=t.originX||0,i=t.originY||0,r=t.scaleX,o=t.scaleY,a=t.anchorX,s=t.anchorY,l=t.rotation||0,u=t.x,h=t.y,c=t.skewX?Math.tan(t.skewX):0,p=t.skewY?Math.tan(-t.skewY):0;if(n||i||a||s){var d=n+a,f=i+s;e[4]=-d*r-c*f*o,e[5]=-f*o-p*d*r}else e[4]=e[5]=0;return e[0]=r,e[3]=o,e[1]=p*r,e[2]=c*o,l&&we(e,e,l),e[4]+=n+u,e[5]+=i+h,e},t.initDefaultProps=function(){var e=t.prototype;e.scaleX=e.scaleY=e.globalScaleRatio=1,e.x=e.y=e.originX=e.originY=e.skewX=e.skewY=e.rotation=e.anchorX=e.anchorY=0}(),t}(),gr=["x","y","originX","originY","anchorX","anchorY","rotation","scaleX","scaleY","skewX","skewY"];function yr(t,e){for(var n=0;n=0?parseFloat(t)/100*e:parseFloat(t):t}function Ir(t,e,n){var i=e.position||"inside",r=null!=e.distance?e.distance:5,o=n.height,a=n.width,s=o/2,l=n.x,u=n.y,h="left",c="top";if(i instanceof Array)l+=Mr(i[0],n.width),u+=Mr(i[1],n.height),h=null,c=null;else switch(i){case"left":l-=r,u+=s,h="right",c="middle";break;case"right":l+=r+a,u+=s,c="middle";break;case"top":l+=a/2,u-=r,h="center",c="bottom";break;case"bottom":l+=a/2,u+=o+r,h="center";break;case"inside":l+=a/2,u+=s,h="center",c="middle";break;case"insideLeft":l+=r,u+=s,c="middle";break;case"insideRight":l+=a-r,u+=s,h="right",c="middle";break;case"insideTop":l+=a/2,u+=r,h="center";break;case"insideBottom":l+=a/2,u+=o-r,h="center",c="bottom";break;case"insideTopLeft":l+=r,u+=r;break;case"insideTopRight":l+=a-r,u+=r,h="right";break;case"insideBottomLeft":l+=r,u+=o-r,c="bottom";break;case"insideBottomRight":l+=a-r,u+=o-r,h="right",c="bottom"}return(t=t||{}).x=l,t.y=u,t.align=h,t.verticalAlign=c,t}var Tr="__zr_normal__",Cr=gr.concat(["ignore"]),Dr=V(gr,(function(t,e){return t[e]=!0,t}),{ignore:!1}),Ar={},kr=new Ee(0,0,0,0),Lr=function(){function t(t){this.id=M(),this.animators=[],this.currentStates=[],this.states={},this._init(t)}return t.prototype._init=function(t){this.attr(t)},t.prototype.drift=function(t,e,n){switch(this.draggable){case"horizontal":e=0;break;case"vertical":t=0}var i=this.transform;i||(i=this.transform=[1,0,0,1,0,0]),i[4]+=t,i[5]+=e,this.decomposeTransform(),this.markRedraw()},t.prototype.beforeUpdate=function(){},t.prototype.afterUpdate=function(){},t.prototype.update=function(){this.updateTransform(),this.__dirty&&this.updateInnerText()},t.prototype.updateInnerText=function(t){var e=this._textContent;if(e&&(!e.ignore||t)){this.textConfig||(this.textConfig={});var n=this.textConfig,i=n.local,r=e.innerTransformable,o=void 0,a=void 0,s=!1;r.parent=i?this:null;var l=!1;if(r.copyTransform(e),null!=n.position){var u=kr;n.layoutRect?u.copy(n.layoutRect):u.copy(this.getBoundingRect()),i||u.applyTransform(this.transform),this.calculateTextPosition?this.calculateTextPosition(Ar,n,u):Ir(Ar,n,u),r.x=Ar.x,r.y=Ar.y,o=Ar.align,a=Ar.verticalAlign;var h=n.origin;if(h&&null!=n.rotation){var c=void 0,p=void 0;"center"===h?(c=.5*u.width,p=.5*u.height):(c=Mr(h[0],u.width),p=Mr(h[1],u.height)),l=!0,r.originX=-r.x+c+(i?0:u.x),r.originY=-r.y+p+(i?0:u.y)}}null!=n.rotation&&(r.rotation=n.rotation);var d=n.offset;d&&(r.x+=d[0],r.y+=d[1],l||(r.originX=-d[0],r.originY=-d[1]));var f=null==n.inside?"string"==typeof n.position&&n.position.indexOf("inside")>=0:n.inside,g=this._innerTextDefaultStyle||(this._innerTextDefaultStyle={}),y=void 0,v=void 0,m=void 0;f&&this.canBeInsideText()?(y=n.insideFill,v=n.insideStroke,null!=y&&"auto"!==y||(y=this.getInsideTextFill()),null!=v&&"auto"!==v||(v=this.getInsideTextStroke(y),m=!0)):(y=n.outsideFill,v=n.outsideStroke,null!=y&&"auto"!==y||(y=this.getOutsideFill()),null!=v&&"auto"!==v||(v=this.getOutsideStroke(y),m=!0)),(y=y||"#000")===g.fill&&v===g.stroke&&m===g.autoStroke&&o===g.align&&a===g.verticalAlign||(s=!0,g.fill=y,g.stroke=v,g.autoStroke=m,g.align=o,g.verticalAlign=a,e.setDefaultTextStyle(g)),e.__dirty|=1,s&&e.dirtyStyle(!0)}},t.prototype.canBeInsideText=function(){return!0},t.prototype.getInsideTextFill=function(){return"#fff"},t.prototype.getInsideTextStroke=function(t){return"#000"},t.prototype.getOutsideFill=function(){return this.__zr&&this.__zr.isDarkMode()?ar:or},t.prototype.getOutsideStroke=function(t){var e=this.__zr&&this.__zr.getBackgroundColor(),n="string"==typeof e&&jn(e);n||(n=[255,255,255,1]);for(var i=n[3],r=this.__zr.isDarkMode(),o=0;o<3;o++)n[o]=n[o]*i+(r?0:255)*(1-i);return n[3]=1,ii(n,"rgba")},t.prototype.traverse=function(t,e){},t.prototype.attrKV=function(t,e){"textConfig"===t?this.setTextConfig(e):"textContent"===t?this.setTextContent(e):"clipPath"===t?this.setClipPath(e):"extra"===t?(this.extra=this.extra||{},A(this.extra,e)):this[t]=e},t.prototype.hide=function(){this.ignore=!0,this.markRedraw()},t.prototype.show=function(){this.ignore=!1,this.markRedraw()},t.prototype.attr=function(t,e){if("string"==typeof t)this.attrKV(t,e);else if(q(t))for(var n=G(t),i=0;i0},t.prototype.getState=function(t){return this.states[t]},t.prototype.ensureState=function(t){var e=this.states;return e[t]||(e[t]={}),e[t]},t.prototype.clearStates=function(t){this.useState(Tr,!1,t)},t.prototype.useState=function(t,e,n,i){var r=t===Tr;if(this.hasState()||!r){var o=this.currentStates,a=this.stateTransition;if(!(P(o,t)>=0)||!e&&1!==o.length){var s;if(this.stateProxy&&!r&&(s=this.stateProxy(t)),s||(s=this.states&&this.states[t]),s||r){r||this.saveCurrentToNormalState(s);var l=!!(s&&s.hoverLayer||i);l&&this._toggleHoverLayerFlag(!0),this._applyStateObj(t,s,this._normalState,e,!n&&!this.__inHover&&a&&a.duration>0,a);var u=this._textContent,h=this._textGuide;return u&&u.useState(t,e,n,l),h&&h.useState(t,e,n,l),r?(this.currentStates=[],this._normalState={}):e?this.currentStates.push(t):this.currentStates=[t],this._updateAnimationTargets(),this.markRedraw(),!l&&this.__inHover&&(this._toggleHoverLayerFlag(!1),this.__dirty&=-2),s}I("State "+t+" not exists.")}}},t.prototype.useStates=function(t,e,n){if(t.length){var i=[],r=this.currentStates,o=t.length,a=o===r.length;if(a)for(var s=0;s0,d);var f=this._textContent,g=this._textGuide;f&&f.useStates(t,e,c),g&&g.useStates(t,e,c),this._updateAnimationTargets(),this.currentStates=t.slice(),this.markRedraw(),!c&&this.__inHover&&(this._toggleHoverLayerFlag(!1),this.__dirty&=-2)}else this.clearStates()},t.prototype._updateAnimationTargets=function(){for(var t=0;t=0){var n=this.currentStates.slice();n.splice(e,1),this.useStates(n)}},t.prototype.replaceState=function(t,e,n){var i=this.currentStates.slice(),r=P(i,t),o=P(i,e)>=0;r>=0?o?i.splice(r,1):i[r]=e:n&&!o&&i.push(e),this.useStates(i)},t.prototype.toggleState=function(t,e){e?this.useState(t,!0):this.removeState(t)},t.prototype._mergeStates=function(t){for(var e,n={},i=0;i=0&&e.splice(n,1)})),this.animators.push(t),n&&n.animation.addAnimator(t),n&&n.wakeUp()},t.prototype.updateDuringAnimation=function(t){this.markRedraw()},t.prototype.stopAnimation=function(t,e){for(var n=this.animators,i=n.length,r=[],o=0;o0&&n.during&&o[0].during((function(t,e){n.during(e)}));for(var p=0;p0||r.force&&!a.length){var w,S=void 0,M=void 0,I=void 0;if(s){M={},p&&(S={});for(_=0;_=0&&(n.splice(i,0,t),this._doAdd(t))}return this},e.prototype.replace=function(t,e){var n=P(this._children,t);return n>=0&&this.replaceAt(e,n),this},e.prototype.replaceAt=function(t,e){var n=this._children,i=n[e];if(t&&t!==this&&t.parent!==this&&t!==i){n[e]=t,i.parent=null;var r=this.__zr;r&&i.removeSelfFromZr(r),this._doAdd(t)}return this},e.prototype._doAdd=function(t){t.parent&&t.parent.remove(t),t.parent=this;var e=this.__zr;e&&e!==t.__zr&&t.addSelfToZr(e),e&&e.refresh()},e.prototype.remove=function(t){var e=this.__zr,n=this._children,i=P(n,t);return i<0||(n.splice(i,1),t.parent=null,e&&t.removeSelfFromZr(e),e&&e.refresh()),this},e.prototype.removeAll=function(){for(var t=this._children,e=this.__zr,n=0;n0&&(this._stillFrameAccum++,this._stillFrameAccum>this._sleepAfterStill&&this.animation.stop())},t.prototype.setSleepAfterStill=function(t){this._sleepAfterStill=t},t.prototype.wakeUp=function(){this.animation.start(),this._stillFrameAccum=0},t.prototype.refreshHover=function(){this._needsRefreshHover=!0},t.prototype.refreshHoverImmediately=function(){this._needsRefreshHover=!1,this.painter.refreshHover&&"canvas"===this.painter.getType()&&this.painter.refreshHover()},t.prototype.resize=function(t){t=t||{},this.painter.resize(t.width,t.height),this.handler.resize()},t.prototype.clearAnimation=function(){this.animation.clear()},t.prototype.getWidth=function(){return this.painter.getWidth()},t.prototype.getHeight=function(){return this.painter.getHeight()},t.prototype.setCursorStyle=function(t){this.handler.setCursorStyle(t)},t.prototype.findHover=function(t,e){return this.handler.findHover(t,e)},t.prototype.on=function(t,e,n){return this.handler.on(t,e,n),this},t.prototype.off=function(t,e){this.handler.off(t,e)},t.prototype.trigger=function(t,e){this.handler.trigger(t,e)},t.prototype.clear=function(){for(var t=this.storage.getRoots(),e=0;e0){if(t<=r)return a;if(t>=o)return s}else{if(t>=r)return a;if(t<=o)return s}else{if(t===r)return a;if(t===o)return s}return(t-r)/l*u+a}function Ur(t,e){switch(t){case"center":case"middle":t="50%";break;case"left":case"top":t="0%";break;case"right":case"bottom":t="100%"}return X(t)?(n=t,n.replace(/^\s+|\s+$/g,"")).match(/%$/)?parseFloat(t)/100*e:parseFloat(t):null==t?NaN:+t;var n}function Xr(t,e,n){return null==e&&(e=10),e=Math.min(Math.max(0,e),20),t=(+t).toFixed(e),n?t:+t}function Zr(t){return t.sort((function(t,e){return t-e})),t}function jr(t){if(t=+t,isNaN(t))return 0;if(t>1e-14)for(var e=1,n=0;n<15;n++,e*=10)if(Math.round(t*e)/e===t)return n;return qr(t)}function qr(t){var e=t.toString().toLowerCase(),n=e.indexOf("e"),i=n>0?+e.slice(n+1):0,r=n>0?n:e.length,o=e.indexOf("."),a=o<0?0:r-1-o;return Math.max(0,a-i)}function Kr(t,e){var n=Math.log,i=Math.LN10,r=Math.floor(n(t[1]-t[0])/i),o=Math.round(n(Math.abs(e[1]-e[0]))/i),a=Math.min(Math.max(-r+o,0),20);return isFinite(a)?a:20}function $r(t,e){var n=V(t,(function(t,e){return t+(isNaN(e)?0:e)}),0);if(0===n)return[];for(var i=Math.pow(10,e),r=z(t,(function(t){return(isNaN(t)?0:t)/n*i*100})),o=100*i,a=z(r,(function(t){return Math.floor(t)})),s=V(a,(function(t,e){return t+e}),0),l=z(r,(function(t,e){return t-a[e]}));su&&(u=l[c],h=c);++a[h],l[h]=0,++s}return z(a,(function(t){return t/i}))}function Jr(t,e){var n=Math.max(jr(t),jr(e)),i=t+e;return n>20?i:Xr(i,n)}var Qr=9007199254740991;function to(t){var e=2*Math.PI;return(t%e+e)%e}function eo(t){return t>-1e-4&&t=10&&e++,e}function ao(t,e){var n=oo(t),i=Math.pow(10,n),r=t/i;return t=(e?r<1.5?1:r<2.5?2:r<4?3:r<7?5:10:r<1?1:r<2?2:r<3?3:r<5?5:10)*i,n>=-20?+t.toFixed(n<0?-n:0):t}function so(t,e){var n=(t.length-1)*e+1,i=Math.floor(n),r=+t[i-1],o=n-i;return o?r+o*(t[i]-r):r}function lo(t){t.sort((function(t,e){return s(t,e,0)?-1:1}));for(var e=-1/0,n=1,i=0;i=0||r&&P(r,s)<0)){var l=n.getShallow(s,e);null!=l&&(o[t[a][0]]=l)}}return o}}var Jo=$o([["fill","color"],["shadowBlur"],["shadowOffsetX"],["shadowOffsetY"],["opacity"],["shadowColor"]]),Qo=function(){function t(){}return t.prototype.getAreaStyle=function(t,e){return Jo(this,t,e)},t}(),ta=new Nn(50);function ea(t){if("string"==typeof t){var e=ta.get(t);return e&&e.image}return t}function na(t,e,n,i,r){if(t){if("string"==typeof t){if(e&&e.__zrImageSrc===t||!n)return e;var o=ta.get(t),a={hostEl:n,cb:i,cbPayload:r};return o?!ra(e=o.image)&&o.pending.push(a):((e=h.loadImage(t,ia,ia)).__zrImageSrc=t,ta.put(t,e.__cachedImgObj={image:e,pending:[a]})),e}return t}return e}function ia(){var t=this.__cachedImgObj;this.onload=this.onerror=this.__cachedImgObj=null;for(var e=0;e=a;l++)s-=a;var u=mr(n,e);return u>s&&(n="",u=0),s=t-u,r.ellipsis=n,r.ellipsisWidth=u,r.contentWidth=s,r.containerWidth=t,r}function la(t,e){var n=e.containerWidth,i=e.font,r=e.contentWidth;if(!n)return"";var o=mr(t,i);if(o<=n)return t;for(var a=0;;a++){if(o<=r||a>=e.maxIterations){t+=e.ellipsis;break}var s=0===a?ua(t,r,e.ascCharWidth,e.cnCharWidth):o>0?Math.floor(t.length*r/o):0;o=mr(t=t.substr(0,s),i)}return""===t&&(t=e.placeholder),t}function ua(t,e,n,i){for(var r=0,o=0,a=t.length;o0&&f+i.accumWidth>i.width&&(o=e.split("\n"),c=!0),i.accumWidth=f}else{var g=ya(e,h,i.width,i.breakAll,i.accumWidth);i.accumWidth=g.accumWidth+d,a=g.linesWidths,o=g.lines}}else o=e.split("\n");for(var y=0;y=33&&e<=383}(t)||!!fa[t]}function ya(t,e,n,i,r){for(var o=[],a=[],s="",l="",u=0,h=0,c=0;cn:r+h+d>n)?h?(s||l)&&(f?(s||(s=l,l="",h=u=0),o.push(s),a.push(h-u),l+=p,s="",h=u+=d):(l&&(s+=l,l="",u=0),o.push(s),a.push(h),s=p,h=d)):f?(o.push(l),a.push(u),l=p,u=d):(o.push(p),a.push(d)):(h+=d,f?(l+=p,u+=d):(l&&(s+=l,l="",u=0),s+=p))}else l&&(s+=l,h+=u),o.push(s),a.push(h),s="",l="",u=0,h=0}return o.length||s||(s=t,l="",u=0),l&&(s+=l),s&&(o.push(s),a.push(h)),1===o.length&&(h+=r),{accumWidth:h,lines:o,linesWidths:a}}var va="__zr_style_"+Math.round(10*Math.random()),ma={shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,shadowColor:"#000",opacity:1,blend:"source-over"},xa={style:{shadowBlur:!0,shadowOffsetX:!0,shadowOffsetY:!0,shadowColor:!0,opacity:!0}};ma[va]=!0;var _a=["z","z2","invisible"],ba=["invisible"],wa=function(t){function e(e){return t.call(this,e)||this}var i;return n(e,t),e.prototype._init=function(e){for(var n=G(e),i=0;i1e-4)return s[0]=t-n,s[1]=e-i,l[0]=t+n,void(l[1]=e+i);if(ka[0]=Da(r)*n+t,ka[1]=Ca(r)*i+e,La[0]=Da(o)*n+t,La[1]=Ca(o)*i+e,u(s,ka,La),h(l,ka,La),(r%=Aa)<0&&(r+=Aa),(o%=Aa)<0&&(o+=Aa),r>o&&!a?o+=Aa:rr&&(Pa[0]=Da(d)*n+t,Pa[1]=Ca(d)*i+e,u(s,Pa,s),h(l,Pa,l))}var Fa={M:1,L:2,C:3,Q:4,A:5,Z:6,R:7},Ga=[],Wa=[],Ha=[],Ya=[],Ua=[],Xa=[],Za=Math.min,ja=Math.max,qa=Math.cos,Ka=Math.sin,$a=Math.abs,Ja=Math.PI,Qa=2*Ja,ts="undefined"!=typeof Float32Array,es=[];function ns(t){return Math.round(t/Ja*1e8)/1e8%2*Ja}function is(t,e){var n=ns(t[0]);n<0&&(n+=Qa);var i=n-t[0],r=t[1];r+=i,!e&&r-n>=Qa?r=n+Qa:e&&n-r>=Qa?r=n-Qa:!e&&n>r?r=n+(Qa-ns(n-r)):e&&n0&&(this._ux=$a(n/rr/t)||0,this._uy=$a(n/rr/e)||0)},t.prototype.setDPR=function(t){this.dpr=t},t.prototype.setContext=function(t){this._ctx=t},t.prototype.getContext=function(){return this._ctx},t.prototype.beginPath=function(){return this._ctx&&this._ctx.beginPath(),this.reset(),this},t.prototype.reset=function(){this._saveData&&(this._len=0),this._pathSegLen&&(this._pathSegLen=null,this._pathLen=0),this._version++},t.prototype.moveTo=function(t,e){return this._drawPendingPt(),this.addData(Fa.M,t,e),this._ctx&&this._ctx.moveTo(t,e),this._x0=t,this._y0=e,this._xi=t,this._yi=e,this},t.prototype.lineTo=function(t,e){var n=$a(t-this._xi),i=$a(e-this._yi),r=n>this._ux||i>this._uy;if(this.addData(Fa.L,t,e),this._ctx&&r&&this._ctx.lineTo(t,e),r)this._xi=t,this._yi=e,this._pendingPtDist=0;else{var o=n*n+i*i;o>this._pendingPtDist&&(this._pendingPtX=t,this._pendingPtY=e,this._pendingPtDist=o)}return this},t.prototype.bezierCurveTo=function(t,e,n,i,r,o){return this._drawPendingPt(),this.addData(Fa.C,t,e,n,i,r,o),this._ctx&&this._ctx.bezierCurveTo(t,e,n,i,r,o),this._xi=r,this._yi=o,this},t.prototype.quadraticCurveTo=function(t,e,n,i){return this._drawPendingPt(),this.addData(Fa.Q,t,e,n,i),this._ctx&&this._ctx.quadraticCurveTo(t,e,n,i),this._xi=n,this._yi=i,this},t.prototype.arc=function(t,e,n,i,r,o){this._drawPendingPt(),es[0]=i,es[1]=r,is(es,o),i=es[0];var a=(r=es[1])-i;return this.addData(Fa.A,t,e,n,n,i,a,0,o?0:1),this._ctx&&this._ctx.arc(t,e,n,i,r,o),this._xi=qa(r)*n+t,this._yi=Ka(r)*n+e,this},t.prototype.arcTo=function(t,e,n,i,r){return this._drawPendingPt(),this._ctx&&this._ctx.arcTo(t,e,n,i,r),this},t.prototype.rect=function(t,e,n,i){return this._drawPendingPt(),this._ctx&&this._ctx.rect(t,e,n,i),this.addData(Fa.R,t,e,n,i),this},t.prototype.closePath=function(){this._drawPendingPt(),this.addData(Fa.Z);var t=this._ctx,e=this._x0,n=this._y0;return t&&t.closePath(),this._xi=e,this._yi=n,this},t.prototype.fill=function(t){t&&t.fill(),this.toStatic()},t.prototype.stroke=function(t){t&&t.stroke(),this.toStatic()},t.prototype.len=function(){return this._len},t.prototype.setData=function(t){var e=t.length;this.data&&this.data.length===e||!ts||(this.data=new Float32Array(e));for(var n=0;nu.length&&(this._expandData(),u=this.data);for(var h=0;h0&&(this._ctx&&this._ctx.lineTo(this._pendingPtX,this._pendingPtY),this._pendingPtDist=0)},t.prototype._expandData=function(){if(!(this.data instanceof Array)){for(var t=[],e=0;e11&&(this.data=new Float32Array(t)))}},t.prototype.getBoundingRect=function(){Ha[0]=Ha[1]=Ua[0]=Ua[1]=Number.MAX_VALUE,Ya[0]=Ya[1]=Xa[0]=Xa[1]=-Number.MAX_VALUE;var t,e=this.data,n=0,i=0,r=0,o=0;for(t=0;tn||$a(y)>i||c===e-1)&&(f=Math.sqrt(A*A+y*y),r=g,o=x);break;case Fa.C:var v=t[c++],m=t[c++],x=(g=t[c++],t[c++]),_=t[c++],b=t[c++];f=Sn(r,o,v,m,g,x,_,b,10),r=_,o=b;break;case Fa.Q:f=An(r,o,v=t[c++],m=t[c++],g=t[c++],x=t[c++],10),r=g,o=x;break;case Fa.A:var w=t[c++],S=t[c++],M=t[c++],I=t[c++],T=t[c++],C=t[c++],D=C+T;c+=1;t[c++];d&&(a=qa(T)*M+w,s=Ka(T)*I+S),f=ja(M,I)*Za(Qa,Math.abs(C)),r=qa(D)*M+w,o=Ka(D)*I+S;break;case Fa.R:a=r=t[c++],s=o=t[c++],f=2*t[c++]+2*t[c++];break;case Fa.Z:var A=a-r;y=s-o;f=Math.sqrt(A*A+y*y),r=a,o=s}f>=0&&(l[h++]=f,u+=f)}return this._pathLen=u,u},t.prototype.rebuildPath=function(t,e){var n,i,r,o,a,s,l,u,h,c,p=this.data,d=this._ux,f=this._uy,g=this._len,y=e<1,v=0,m=0,x=0;if(!y||(this._pathSegLen||this._calculateLength(),l=this._pathSegLen,u=e*this._pathLen))t:for(var _=0;_0&&(t.lineTo(h,c),x=0),b){case Fa.M:n=r=p[_++],i=o=p[_++],t.moveTo(r,o);break;case Fa.L:a=p[_++],s=p[_++];var S=$a(a-r),M=$a(s-o);if(S>d||M>f){if(y){if(v+(j=l[m++])>u){var I=(u-v)/j;t.lineTo(r*(1-I)+a*I,o*(1-I)+s*I);break t}v+=j}t.lineTo(a,s),r=a,o=s,x=0}else{var T=S*S+M*M;T>x&&(h=a,c=s,x=T)}break;case Fa.C:var C=p[_++],D=p[_++],A=p[_++],k=p[_++],L=p[_++],P=p[_++];if(y){if(v+(j=l[m++])>u){bn(r,C,A,L,I=(u-v)/j,Ga),bn(o,D,k,P,I,Wa),t.bezierCurveTo(Ga[1],Wa[1],Ga[2],Wa[2],Ga[3],Wa[3]);break t}v+=j}t.bezierCurveTo(C,D,A,k,L,P),r=L,o=P;break;case Fa.Q:C=p[_++],D=p[_++],A=p[_++],k=p[_++];if(y){if(v+(j=l[m++])>u){Cn(r,C,A,I=(u-v)/j,Ga),Cn(o,D,k,I,Wa),t.quadraticCurveTo(Ga[1],Wa[1],Ga[2],Wa[2]);break t}v+=j}t.quadraticCurveTo(C,D,A,k),r=A,o=k;break;case Fa.A:var O=p[_++],R=p[_++],N=p[_++],E=p[_++],z=p[_++],V=p[_++],B=p[_++],F=!p[_++],G=N>E?N:E,W=$a(N-E)>.001,H=z+V,Y=!1;if(y)v+(j=l[m++])>u&&(H=z+V*(u-v)/j,Y=!0),v+=j;if(W&&t.ellipse?t.ellipse(O,R,N,E,B,z,H,F):t.arc(O,R,G,z,H,F),Y)break t;w&&(n=qa(z)*N+O,i=Ka(z)*E+R),r=qa(H)*N+O,o=Ka(H)*E+R;break;case Fa.R:n=r=p[_],i=o=p[_+1],a=p[_++],s=p[_++];var U=p[_++],X=p[_++];if(y){if(v+(j=l[m++])>u){var Z=u-v;t.moveTo(a,s),t.lineTo(a+Za(Z,U),s),(Z-=U)>0&&t.lineTo(a+U,s+Za(Z,X)),(Z-=X)>0&&t.lineTo(a+ja(U-Z,0),s+X),(Z-=U)>0&&t.lineTo(a,s+ja(X-Z,0));break t}v+=j}t.rect(a,s,U,X);break;case Fa.Z:if(y){var j;if(v+(j=l[m++])>u){I=(u-v)/j;t.lineTo(r*(1-I)+n*I,o*(1-I)+i*I);break t}v+=j}t.closePath(),r=n,o=i}}},t.prototype.clone=function(){var e=new t,n=this.data;return e.data=n.slice?n.slice():Array.prototype.slice.call(n),e._len=this._len,e},t.CMD=Fa,t.initDefaultProps=function(){var e=t.prototype;e._saveData=!0,e._ux=0,e._uy=0,e._pendingPtDist=0,e._version=0}(),t}();function os(t,e,n,i,r,o,a){if(0===r)return!1;var s=r,l=0;if(a>e+s&&a>i+s||at+s&&o>n+s||oe+c&&h>i+c&&h>o+c&&h>s+c||ht+c&&u>n+c&&u>r+c&&u>a+c||ue+u&&l>i+u&&l>o+u||lt+u&&s>n+u&&s>r+u||sn||h+ur&&(r+=hs);var p=Math.atan2(l,s);return p<0&&(p+=hs),p>=i&&p<=r||p+hs>=i&&p+hs<=r}function ps(t,e,n,i,r,o){if(o>e&&o>i||or?s:0}var ds=rs.CMD,fs=2*Math.PI;var gs=[-1,-1,-1],ys=[-1,-1];function vs(t,e,n,i,r,o,a,s,l,u){if(u>e&&u>i&&u>o&&u>s||u1&&(h=void 0,h=ys[0],ys[0]=ys[1],ys[1]=h),f=vn(e,i,o,s,ys[0]),d>1&&(g=vn(e,i,o,s,ys[1]))),2===d?ve&&s>i&&s>o||s=0&&h<=1&&(r[l++]=h);else{var u=a*a-4*o*s;if(gn(u))(h=-a/(2*o))>=0&&h<=1&&(r[l++]=h);else if(u>0){var h,c=sn(u),p=(-a-c)/(2*o);(h=(-a+c)/(2*o))>=0&&h<=1&&(r[l++]=h),p>=0&&p<=1&&(r[l++]=p)}}return l}(e,i,o,s,gs);if(0===l)return 0;var u=Tn(e,i,o);if(u>=0&&u<=1){for(var h=0,c=Mn(e,i,o,u),p=0;pn||s<-n)return 0;var l=Math.sqrt(n*n-s*s);gs[0]=-l,gs[1]=l;var u=Math.abs(i-r);if(u<1e-4)return 0;if(u>=fs-1e-4){i=0,r=fs;var h=o?1:-1;return a>=gs[0]+t&&a<=gs[1]+t?h:0}if(i>r){var c=i;i=r,r=c}i<0&&(i+=fs,r+=fs);for(var p=0,d=0;d<2;d++){var f=gs[d];if(f+t>a){var g=Math.atan2(s,f);h=o?1:-1;g<0&&(g=fs+g),(g>=i&&g<=r||g+fs>=i&&g+fs<=r)&&(g>Math.PI/2&&g<1.5*Math.PI&&(h=-h),p+=h)}}return p}function _s(t,e,n,i,r){for(var o,a,s,l,u=t.data,h=t.len(),c=0,p=0,d=0,f=0,g=0,y=0;y1&&(n||(c+=ps(p,d,f,g,i,r))),m&&(f=p=u[y],g=d=u[y+1]),v){case ds.M:p=f=u[y++],d=g=u[y++];break;case ds.L:if(n){if(os(p,d,u[y],u[y+1],e,i,r))return!0}else c+=ps(p,d,u[y],u[y+1],i,r)||0;p=u[y++],d=u[y++];break;case ds.C:if(n){if(as(p,d,u[y++],u[y++],u[y++],u[y++],u[y],u[y+1],e,i,r))return!0}else c+=vs(p,d,u[y++],u[y++],u[y++],u[y++],u[y],u[y+1],i,r)||0;p=u[y++],d=u[y++];break;case ds.Q:if(n){if(ss(p,d,u[y++],u[y++],u[y],u[y+1],e,i,r))return!0}else c+=ms(p,d,u[y++],u[y++],u[y],u[y+1],i,r)||0;p=u[y++],d=u[y++];break;case ds.A:var x=u[y++],_=u[y++],b=u[y++],w=u[y++],S=u[y++],M=u[y++];y+=1;var I=!!(1-u[y++]);o=Math.cos(S)*b+x,a=Math.sin(S)*w+_,m?(f=o,g=a):c+=ps(p,d,o,a,i,r);var T=(i-x)*w/b+x;if(n){if(cs(x,_,w,S,S+M,I,e,T,r))return!0}else c+=xs(x,_,w,S,S+M,I,T,r);p=Math.cos(S+M)*b+x,d=Math.sin(S+M)*w+_;break;case ds.R:if(f=p=u[y++],g=d=u[y++],o=f+u[y++],a=g+u[y++],n){if(os(f,g,o,g,e,i,r)||os(o,g,o,a,e,i,r)||os(o,a,f,a,e,i,r)||os(f,a,f,g,e,i,r))return!0}else c+=ps(o,g,o,a,i,r),c+=ps(f,a,f,g,i,r);break;case ds.Z:if(n){if(os(p,d,f,g,e,i,r))return!0}else c+=ps(p,d,f,g,i,r);p=f,d=g}}return n||(s=d,l=g,Math.abs(s-l)<1e-4)||(c+=ps(p,d,f,g,i,r)||0),0!==c}var bs=k({fill:"#000",stroke:null,strokePercent:1,fillOpacity:1,strokeOpacity:1,lineDashOffset:0,lineWidth:1,lineCap:"butt",miterLimit:10,strokeNoScale:!1,strokeFirst:!1},ma),ws={style:k({fill:!0,stroke:!0,strokePercent:!0,fillOpacity:!0,strokeOpacity:!0,lineDashOffset:!0,lineWidth:!0,miterLimit:!0},xa.style)},Ss=gr.concat(["invisible","culling","z","z2","zlevel","parent"]),Ms=function(t){function e(e){return t.call(this,e)||this}var i;return n(e,t),e.prototype.update=function(){var n=this;t.prototype.update.call(this);var i=this.style;if(i.decal){var r=this._decalEl=this._decalEl||new e;r.buildPath===e.prototype.buildPath&&(r.buildPath=function(t){n.buildPath(t,n.shape)}),r.silent=!0;var o=r.style;for(var a in i)o[a]!==i[a]&&(o[a]=i[a]);o.fill=i.fill?i.decal:null,o.decal=null,o.shadowColor=null,i.strokeFirst&&(o.stroke=null);for(var s=0;s.5?or:e>.2?"#eee":ar}if(t)return ar}return or},e.prototype.getInsideTextStroke=function(t){var e=this.style.fill;if(X(e)){var n=this.__zr;if(!(!n||!n.isDarkMode())===ri(t,0)<.4)return e}},e.prototype.buildPath=function(t,e,n){},e.prototype.pathUpdated=function(){this.__dirty&=-5},e.prototype.getUpdatedPathProxy=function(t){return!this.path&&this.createPathProxy(),this.path.beginPath(),this.buildPath(this.path,this.shape,t),this.path},e.prototype.createPathProxy=function(){this.path=new rs(!1)},e.prototype.hasStroke=function(){var t=this.style,e=t.stroke;return!(null==e||"none"===e||!(t.lineWidth>0))},e.prototype.hasFill=function(){var t=this.style.fill;return null!=t&&"none"!==t},e.prototype.getBoundingRect=function(){var t=this._rect,e=this.style,n=!t;if(n){var i=!1;this.path||(i=!0,this.createPathProxy());var r=this.path;(i||4&this.__dirty)&&(r.beginPath(),this.buildPath(r,this.shape,!1),this.pathUpdated()),t=r.getBoundingRect()}if(this._rect=t,this.hasStroke()&&this.path&&this.path.len()>0){var o=this._rectStroke||(this._rectStroke=t.clone());if(this.__dirty||n){o.copy(t);var a=e.strokeNoScale?this.getLineScale():1,s=e.lineWidth;if(!this.hasFill()){var l=this.strokeContainThreshold;s=Math.max(s,null==l?4:l)}a>1e-10&&(o.width+=s/a,o.height+=s/a,o.x-=s/a/2,o.y-=s/a/2)}return o}return t},e.prototype.contain=function(t,e){var n=this.transformCoordToLocal(t,e),i=this.getBoundingRect(),r=this.style;if(t=n[0],e=n[1],i.contain(t,e)){var o=this.path;if(this.hasStroke()){var a=r.lineWidth,s=r.strokeNoScale?this.getLineScale():1;if(s>1e-10&&(this.hasFill()||(a=Math.max(a,this.strokeContainThreshold)),function(t,e,n,i){return _s(t,e,!0,n,i)}(o,a/s,t,e)))return!0}if(this.hasFill())return function(t,e,n){return _s(t,0,!1,e,n)}(o,t,e)}return!1},e.prototype.dirtyShape=function(){this.__dirty|=4,this._rect&&(this._rect=null),this._decalEl&&this._decalEl.dirtyShape(),this.markRedraw()},e.prototype.dirty=function(){this.dirtyStyle(),this.dirtyShape()},e.prototype.animateShape=function(t){return this.animate("shape",t)},e.prototype.updateDuringAnimation=function(t){"style"===t?this.dirtyStyle():"shape"===t?this.dirtyShape():this.markRedraw()},e.prototype.attrKV=function(e,n){"shape"===e?this.setShape(n):t.prototype.attrKV.call(this,e,n)},e.prototype.setShape=function(t,e){var n=this.shape;return n||(n=this.shape={}),"string"==typeof t?n[t]=e:A(n,t),this.dirtyShape(),this},e.prototype.shapeChanged=function(){return!!(4&this.__dirty)},e.prototype.createStyle=function(t){return mt(bs,t)},e.prototype._innerSaveToNormal=function(e){t.prototype._innerSaveToNormal.call(this,e);var n=this._normalState;e.shape&&!n.shape&&(n.shape=A({},this.shape))},e.prototype._applyStateObj=function(e,n,i,r,o,a){t.prototype._applyStateObj.call(this,e,n,i,r,o,a);var s,l=!(n&&r);if(n&&n.shape?o?r?s=n.shape:(s=A({},i.shape),A(s,n.shape)):(s=A({},r?this.shape:i.shape),A(s,n.shape)):l&&(s=i.shape),s)if(o){this.shape=A({},this.shape);for(var u={},h=G(s),c=0;c0},e.prototype.hasFill=function(){var t=this.style.fill;return null!=t&&"none"!==t},e.prototype.createStyle=function(t){return mt(Is,t)},e.prototype.setBoundingRect=function(t){this._rect=t},e.prototype.getBoundingRect=function(){var t=this.style;if(!this._rect){var e=t.text;null!=e?e+="":e="";var n=_r(e,t.font,t.textAlign,t.textBaseline);if(n.x+=t.x||0,n.y+=t.y||0,this.hasStroke()){var i=t.lineWidth;n.x-=i/2,n.y-=i/2,n.width+=i,n.height+=i}this._rect=n}return this._rect},e.initDefaultProps=void(e.prototype.dirtyRectTolerance=10),e}(wa);Ts.prototype.type="tspan";var Cs=k({x:0,y:0},ma),Ds={style:k({x:!0,y:!0,width:!0,height:!0,sx:!0,sy:!0,sWidth:!0,sHeight:!0},xa.style)};var As=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.createStyle=function(t){return mt(Cs,t)},e.prototype._getSize=function(t){var e=this.style,n=e[t];if(null!=n)return n;var i,r=(i=e.image)&&"string"!=typeof i&&i.width&&i.height?e.image:this.__image;if(!r)return 0;var o="width"===t?"height":"width",a=e[o];return null==a?r[t]:r[t]/r[o]*a},e.prototype.getWidth=function(){return this._getSize("width")},e.prototype.getHeight=function(){return this._getSize("height")},e.prototype.getAnimationStyleProps=function(){return Ds},e.prototype.getBoundingRect=function(){var t=this.style;return this._rect||(this._rect=new Ee(t.x||0,t.y||0,this.getWidth(),this.getHeight())),this._rect},e}(wa);As.prototype.type="image";var ks=Math.round;function Ls(t,e,n){if(e){var i=e.x1,r=e.x2,o=e.y1,a=e.y2;t.x1=i,t.x2=r,t.y1=o,t.y2=a;var s=n&&n.lineWidth;return s?(ks(2*i)===ks(2*r)&&(t.x1=t.x2=Os(i,s,!0)),ks(2*o)===ks(2*a)&&(t.y1=t.y2=Os(o,s,!0)),t):t}}function Ps(t,e,n){if(e){var i=e.x,r=e.y,o=e.width,a=e.height;t.x=i,t.y=r,t.width=o,t.height=a;var s=n&&n.lineWidth;return s?(t.x=Os(i,s,!0),t.y=Os(r,s,!0),t.width=Math.max(Os(i+o,s,!1)-t.x,0===o?0:1),t.height=Math.max(Os(r+a,s,!1)-t.y,0===a?0:1),t):t}}function Os(t,e,n){if(!e)return t;var i=ks(2*t);return(i+ks(e))%2==0?i/2:(i+(n?1:-1))/2}var Rs=function(){this.x=0,this.y=0,this.width=0,this.height=0},Ns={},Es=function(t){function e(e){return t.call(this,e)||this}return n(e,t),e.prototype.getDefaultShape=function(){return new Rs},e.prototype.buildPath=function(t,e){var n,i,r,o;if(this.subPixelOptimize){var a=Ps(Ns,e,this.style);n=a.x,i=a.y,r=a.width,o=a.height,a.r=e.r,e=a}else n=e.x,i=e.y,r=e.width,o=e.height;e.r?function(t,e){var n,i,r,o,a,s=e.x,l=e.y,u=e.width,h=e.height,c=e.r;u<0&&(s+=u,u=-u),h<0&&(l+=h,h=-h),"number"==typeof c?n=i=r=o=c:c instanceof Array?1===c.length?n=i=r=o=c[0]:2===c.length?(n=r=c[0],i=o=c[1]):3===c.length?(n=c[0],i=o=c[1],r=c[2]):(n=c[0],i=c[1],r=c[2],o=c[3]):n=i=r=o=0,n+i>u&&(n*=u/(a=n+i),i*=u/a),r+o>u&&(r*=u/(a=r+o),o*=u/a),i+r>h&&(i*=h/(a=i+r),r*=h/a),n+o>h&&(n*=h/(a=n+o),o*=h/a),t.moveTo(s+n,l),t.lineTo(s+u-i,l),0!==i&&t.arc(s+u-i,l+i,i,-Math.PI/2,0),t.lineTo(s+u,l+h-r),0!==r&&t.arc(s+u-r,l+h-r,r,0,Math.PI/2),t.lineTo(s+o,l+h),0!==o&&t.arc(s+o,l+h-o,o,Math.PI/2,Math.PI),t.lineTo(s,l+n),0!==n&&t.arc(s+n,l+n,n,Math.PI,1.5*Math.PI)}(t,e):t.rect(n,i,r,o)},e.prototype.isZeroArea=function(){return!this.shape.width||!this.shape.height},e}(Ms);Es.prototype.type="rect";var zs={fill:"#000"},Vs={style:k({fill:!0,stroke:!0,fillOpacity:!0,strokeOpacity:!0,lineWidth:!0,fontSize:!0,lineHeight:!0,width:!0,height:!0,textShadowColor:!0,textShadowBlur:!0,textShadowOffsetX:!0,textShadowOffsetY:!0,backgroundColor:!0,padding:!0,borderColor:!0,borderWidth:!0,borderRadius:!0},xa.style)},Bs=function(t){function e(e){var n=t.call(this)||this;return n.type="text",n._children=[],n._defaultStyle=zs,n.attr(e),n}return n(e,t),e.prototype.childrenRef=function(){return this._children},e.prototype.update=function(){t.prototype.update.call(this),this.styleChanged()&&this._updateSubTexts();for(var e=0;ed&&h){var f=Math.floor(d/l);n=n.slice(0,f)}if(t&&a&&null!=c)for(var g=sa(c,o,e.ellipsis,{minChar:e.truncateMinChar,placeholder:e.placeholder}),y=0;y0,T=null!=t.width&&("truncate"===t.overflow||"break"===t.overflow||"breakAll"===t.overflow),C=i.calculatedLineHeight,D=0;Dl&&da(n,t.substring(l,u),e,s),da(n,i[2],e,s,i[1]),l=oa.lastIndex}lo){b>0?(m.tokens=m.tokens.slice(0,b),y(m,_,x),n.lines=n.lines.slice(0,v+1)):n.lines=n.lines.slice(0,v);break t}var C=w.width,D=null==C||"auto"===C;if("string"==typeof C&&"%"===C.charAt(C.length-1))P.percentWidth=C,h.push(P),P.contentWidth=mr(P.text,I);else{if(D){var A=w.backgroundColor,k=A&&A.image;k&&ra(k=ea(k))&&(P.width=Math.max(P.width,k.width*T/k.height))}var L=f&&null!=r?r-_:null;null!=L&&L=0&&"right"===(C=x[T]).align;)this._placeToken(C,t,b,f,I,"right",y),w-=C.width,I-=C.width,T--;for(M+=(n-(M-d)-(g-I)-w)/2;S<=T;)C=x[S],this._placeToken(C,t,b,f,M+C.width/2,"center",y),M+=C.width,S++;f+=b}},e.prototype._placeToken=function(t,e,n,i,r,o,s){var l=e.rich[t.styleName]||{};l.text=t.text;var u=t.verticalAlign,h=i+n/2;"top"===u?h=i+t.height/2:"bottom"===u&&(h=i+n-t.height/2),!t.isLineHolder&&$s(l)&&this._renderBackground(l,e,"right"===o?r-t.width:"center"===o?r-t.width/2:r,h-t.height/2,t.width,t.height);var c=!!l.backgroundColor,p=t.textPadding;p&&(r=qs(r,o,p),h-=t.height/2-p[0]-t.innerHeight/2);var d=this._getOrCreateChild(Ts),f=d.createStyle();d.useStyle(f);var g=this._defaultStyle,y=!1,v=0,m=js("fill"in l?l.fill:"fill"in e?e.fill:(y=!0,g.fill)),x=Zs("stroke"in l?l.stroke:"stroke"in e?e.stroke:c||s||g.autoStroke&&!y?null:(v=2,g.stroke)),_=l.textShadowBlur>0||e.textShadowBlur>0;f.text=t.text,f.x=r,f.y=h,_&&(f.shadowBlur=l.textShadowBlur||e.textShadowBlur||0,f.shadowColor=l.textShadowColor||e.textShadowColor||"transparent",f.shadowOffsetX=l.textShadowOffsetX||e.textShadowOffsetX||0,f.shadowOffsetY=l.textShadowOffsetY||e.textShadowOffsetY||0),f.textAlign=o,f.textBaseline="middle",f.font=t.font||a,f.opacity=ot(l.opacity,e.opacity,1),Ys(f,l),x&&(f.lineWidth=ot(l.lineWidth,e.lineWidth,v),f.lineDash=rt(l.lineDash,e.lineDash),f.lineDashOffset=e.lineDashOffset||0,f.stroke=x),m&&(f.fill=m);var b=t.contentWidth,w=t.contentHeight;d.setBoundingRect(new Ee(br(f.x,b,f.textAlign),wr(f.y,w,f.textBaseline),b,w))},e.prototype._renderBackground=function(t,e,n,i,r,o){var a,s,l,u=t.backgroundColor,h=t.borderWidth,c=t.borderColor,p=u&&u.image,d=u&&!p,f=t.borderRadius,g=this;if(d||t.lineHeight||h&&c){(a=this._getOrCreateChild(Es)).useStyle(a.createStyle()),a.style.fill=null;var y=a.shape;y.x=n,y.y=i,y.width=r,y.height=o,y.r=f,a.dirtyShape()}if(d)(l=a.style).fill=u||null,l.fillOpacity=rt(t.fillOpacity,1);else if(p){(s=this._getOrCreateChild(As)).onload=function(){g.dirtyStyle()};var v=s.style;v.image=u.image,v.x=n,v.y=i,v.width=r,v.height=o}h&&c&&((l=a.style).lineWidth=h,l.stroke=c,l.strokeOpacity=rt(t.strokeOpacity,1),l.lineDash=t.borderDash,l.lineDashOffset=t.borderDashOffset||0,a.strokeContainThreshold=0,a.hasFill()&&a.hasStroke()&&(l.strokeFirst=!0,l.lineWidth*=2));var m=(a||s).style;m.shadowBlur=t.shadowBlur||0,m.shadowColor=t.shadowColor||"transparent",m.shadowOffsetX=t.shadowOffsetX||0,m.shadowOffsetY=t.shadowOffsetY||0,m.opacity=ot(t.opacity,e.opacity,1)},e.makeFont=function(t){var e="";return Us(t)&&(e=[t.fontStyle,t.fontWeight,Hs(t.fontSize),t.fontFamily||"sans-serif"].join(" ")),e&&ut(e)||t.textFont||t.font},e}(wa),Fs={left:!0,right:1,center:1},Gs={top:1,bottom:1,middle:1},Ws=["fontStyle","fontWeight","fontSize","fontFamily"];function Hs(t){return"string"!=typeof t||-1===t.indexOf("px")&&-1===t.indexOf("rem")&&-1===t.indexOf("em")?isNaN(+t)?"12px":t+"px":t}function Ys(t,e){for(var n=0;n=0,o=!1;if(t instanceof Ms){var a=nl(t),s=r&&a.selectFill||a.normalFill,l=r&&a.selectStroke||a.normalStroke;if(pl(s)||pl(l)){var u=(i=i||{}).style||{};"inherit"===u.fill?(o=!0,i=A({},i),(u=A({},u)).fill=s):!pl(u.fill)&&pl(s)?(o=!0,i=A({},i),(u=A({},u)).fill=fl(s)):!pl(u.stroke)&&pl(l)&&(o||(i=A({},i),u=A({},u)),u.stroke=fl(l)),i.style=u}}if(i&&null==i.z2){o||(i=A({},i));var h=t.z2EmphasisLift;i.z2=t.z2+(null!=h?h:al)}return i}(this,0,e,n);if("blur"===t)return function(t,e,n){var i=P(t.currentStates,e)>=0,r=t.style.opacity,o=i?null:function(t,e,n,i){for(var r=t.style,o={},a=0;a0){var o={dataIndex:r,seriesIndex:t.seriesIndex};null!=i&&(o.dataType=i),e.push(o)}}))})),e}function Wl(t,e,n){jl(t,!0),Sl(t,Tl),Yl(t,e,n)}function Hl(t,e,n,i){i?function(t){jl(t,!1)}(t):Wl(t,e,n)}function Yl(t,e,n){var i=Js(t);null!=e?(i.focus=e,i.blurScope=n):i.focus&&(i.focus=null)}var Ul=["emphasis","blur","select"],Xl={itemStyle:"getItemStyle",lineStyle:"getLineStyle",areaStyle:"getAreaStyle"};function Zl(t,e,n,i){n=n||"itemStyle";for(var r=0;r1&&(a*=iu(f),s*=iu(f));var g=(r===o?-1:1)*iu((a*a*(s*s)-a*a*(d*d)-s*s*(p*p))/(a*a*(d*d)+s*s*(p*p)))||0,y=g*a*d/s,v=g*-s*p/a,m=(t+n)/2+ou(c)*y-ru(c)*v,x=(e+i)/2+ru(c)*y+ou(c)*v,_=uu([1,0],[(p-y)/a,(d-v)/s]),b=[(p-y)/a,(d-v)/s],w=[(-1*p-y)/a,(-1*d-v)/s],S=uu(b,w);if(lu(b,w)<=-1&&(S=au),lu(b,w)>=1&&(S=0),S<0){var M=Math.round(S/au*1e6)/1e6;S=2*au+M%2*au}h.addData(u,m,x,a,s,_,S,c,o)}var cu=/([mlvhzcqtsa])([^mlvhzcqtsa]*)/gi,pu=/-?([0-9]*\.)?[0-9]+([eE]-?[0-9]+)?/g;var du=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.applyTransform=function(t){},e}(Ms);function fu(t){return null!=t.setData}function gu(t,e){var n=function(t){var e=new rs;if(!t)return e;var n,i=0,r=0,o=i,a=r,s=rs.CMD,l=t.match(cu);if(!l)return e;for(var u=0;uk*k+L*L&&(M=T,I=C),{cx:M,cy:I,x0:-h,y0:-c,x1:M*(r/b-1),y1:I*(r/b-1)}}function Ru(t,e){var n,i=ku(e.r,0),r=ku(e.r0||0,0),o=i>0;if(o||r>0){if(o||(i=r,r=0),r>i){var a=i;i=r,r=a}var s=e.startAngle,l=e.endAngle;if(!isNaN(s)&&!isNaN(l)){var u=e.cx,h=e.cy,c=!!e.clockwise,p=Du(l-s),d=p>Su&&p%Su;if(d>Pu&&(p=d),i>Pu)if(p>Su-Pu)t.moveTo(u+i*Iu(s),h+i*Mu(s)),t.arc(u,h,i,s,l,!c),r>Pu&&(t.moveTo(u+r*Iu(l),h+r*Mu(l)),t.arc(u,h,r,l,s,c));else{var f=void 0,g=void 0,y=void 0,v=void 0,m=void 0,x=void 0,_=void 0,b=void 0,w=void 0,S=void 0,M=void 0,I=void 0,T=void 0,C=void 0,D=void 0,A=void 0,k=i*Iu(s),L=i*Mu(s),P=r*Iu(l),O=r*Mu(l),R=p>Pu;if(R){var N=e.cornerRadius;N&&(n=function(t){var e;if(Y(t)){var n=t.length;if(!n)return t;e=1===n?[t[0],t[0],0,0]:2===n?[t[0],t[0],t[1],t[1]]:3===n?t.concat(t[2]):t}else e=[t,t,t,t];return e}(N),f=n[0],g=n[1],y=n[2],v=n[3]);var E=Du(i-r)/2;if(m=Lu(E,y),x=Lu(E,v),_=Lu(E,f),b=Lu(E,g),M=w=ku(m,x),I=S=ku(_,b),(w>Pu||S>Pu)&&(T=i*Iu(l),C=i*Mu(l),D=r*Iu(s),A=r*Mu(s),pPu){var U=Lu(y,M),X=Lu(v,M),Z=Ou(D,A,k,L,i,U,c),j=Ou(T,C,P,O,i,X,c);t.moveTo(u+Z.cx+Z.x0,h+Z.cy+Z.y0),M0&&t.arc(u+Z.cx,h+Z.cy,U,Cu(Z.y0,Z.x0),Cu(Z.y1,Z.x1),!c),t.arc(u,h,i,Cu(Z.cy+Z.y1,Z.cx+Z.x1),Cu(j.cy+j.y1,j.cx+j.x1),!c),X>0&&t.arc(u+j.cx,h+j.cy,X,Cu(j.y1,j.x1),Cu(j.y0,j.x0),!c))}else t.moveTo(u+k,h+L),t.arc(u,h,i,s,l,!c);else t.moveTo(u+k,h+L);if(r>Pu&&R)if(I>Pu){U=Lu(f,I),Z=Ou(P,O,T,C,r,-(X=Lu(g,I)),c),j=Ou(k,L,D,A,r,-U,c);t.lineTo(u+Z.cx+Z.x0,h+Z.cy+Z.y0),I0&&t.arc(u+Z.cx,h+Z.cy,X,Cu(Z.y0,Z.x0),Cu(Z.y1,Z.x1),!c),t.arc(u,h,r,Cu(Z.cy+Z.y1,Z.cx+Z.x1),Cu(j.cy+j.y1,j.cx+j.x1),c),U>0&&t.arc(u+j.cx,h+j.cy,U,Cu(j.y1,j.x1),Cu(j.y0,j.x0),!c))}else t.lineTo(u+P,h+O),t.arc(u,h,r,l,s,c);else t.lineTo(u+P,h+O)}else t.moveTo(u,h);t.closePath()}}}var Nu=function(){this.cx=0,this.cy=0,this.r0=0,this.r=0,this.startAngle=0,this.endAngle=2*Math.PI,this.clockwise=!0,this.cornerRadius=0},Eu=function(t){function e(e){return t.call(this,e)||this}return n(e,t),e.prototype.getDefaultShape=function(){return new Nu},e.prototype.buildPath=function(t,e){Ru(t,e)},e.prototype.isZeroArea=function(){return this.shape.startAngle===this.shape.endAngle||this.shape.r===this.shape.r0},e}(Ms);Eu.prototype.type="sector";var zu=function(){this.cx=0,this.cy=0,this.r=0,this.r0=0},Vu=function(t){function e(e){return t.call(this,e)||this}return n(e,t),e.prototype.getDefaultShape=function(){return new zu},e.prototype.buildPath=function(t,e){var n=e.cx,i=e.cy,r=2*Math.PI;t.moveTo(n+e.r,i),t.arc(n,i,e.r,0,r,!1),t.moveTo(n+e.r0,i),t.arc(n,i,e.r0,0,r,!0)},e}(Ms);function Bu(t,e,n){var i=e.smooth,r=e.points;if(r&&r.length>=2){if(i){var o=function(t,e,n,i){var r,o,a,s,l=[],u=[],h=[],c=[];if(i){a=[1/0,1/0],s=[-1/0,-1/0];for(var p=0,d=t.length;prh[1]){if(a=!1,r)return a;var u=Math.abs(rh[0]-ih[1]),h=Math.abs(ih[0]-rh[1]);Math.min(u,h)>i.len()&&(u0){var c={duration:h.duration,delay:h.delay||0,easing:h.easing,done:o,force:!!o||!!a,setToFinal:!u,scope:t,during:a};l?e.animateFrom(n,c):e.animateTo(n,c)}else e.stopAnimation(),!l&&e.attr(n),a&&a(1),o&&o()}function dh(t,e,n,i,r,o){ph("update",t,e,n,i,r,o)}function fh(t,e,n,i,r,o){ph("enter",t,e,n,i,r,o)}function gh(t){if(!t.__zr)return!0;for(var e=0;eMath.abs(o[1])?o[0]>0?"right":"left":o[1]>0?"bottom":"top"}function Vh(t){return!t.isGroup}function Bh(t,e,n){if(t&&e){var i,r=(i={},t.traverse((function(t){Vh(t)&&t.anid&&(i[t.anid]=t)})),i);e.traverse((function(t){if(Vh(t)&&t.anid){var e=r[t.anid];if(e){var i=o(t);t.attr(o(e)),dh(t,i,n,Js(t).dataIndex)}}}))}function o(t){var e={x:t.x,y:t.y,rotation:t.rotation};return function(t){return null!=t.shape}(t)&&(e.shape=A({},t.shape)),e}}function Fh(t,e){return z(t,(function(t){var n=t[0];n=_h(n,e.x),n=bh(n,e.x+e.width);var i=t[1];return i=_h(i,e.y),[n,i=bh(i,e.y+e.height)]}))}function Gh(t,e){var n=_h(t.x,e.x),i=bh(t.x+t.width,e.x+e.width),r=_h(t.y,e.y),o=bh(t.y+t.height,e.y+e.height);if(i>=n&&o>=r)return{x:n,y:r,width:i-n,height:o-r}}function Wh(t,e,n){var i=A({rectHover:!0},e),r=i.style={strokeNoScale:!0};if(n=n||{x:-1,y:-1,width:2,height:2},t)return 0===t.indexOf("image://")?(r.image=t.slice(8),k(r,n),new As(i)):Dh(t.replace("path://",""),i,n,"center")}function Hh(t,e,n,i,r){for(var o=0,a=r[r.length-1];o=-1e-6)return!1;var f=t-r,g=e-o,y=Uh(f,g,u,h)/d;if(y<0||y>1)return!1;var v=Uh(f,g,c,p)/d;return!(v<0||v>1)}function Uh(t,e,n,i){return t*i-n*e}function Xh(t){var e=t.itemTooltipOption,n=t.componentModel,i=t.itemName,r=X(e)?{formatter:e}:e,o=n.mainType,a=n.componentIndex,s={componentType:o,name:i,$vars:["name"]};s[o+"Index"]=a;var l=t.formatterParamsExtra;l&&E(G(l),(function(t){_t(s,t)||(s[t]=l[t],s.$vars.push(t))}));var u=Js(t.el);u.componentMainType=o,u.componentIndex=a,u.tooltipConfig={name:i,option:k({content:i,formatterParams:s},r)}}function Zh(t,e){var n;t.isGroup&&(n=e(t)),n||t.traverse(e)}function jh(t,e){if(t)if(Y(t))for(var n=0;n-1?Cc:Ac;function Oc(t,e){t=t.toUpperCase(),Lc[t]=new Sc(e),kc[t]=e}function Rc(t){return Lc[t]}Oc(Dc,{time:{month:["January","February","March","April","May","June","July","August","September","October","November","December"],monthAbbr:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayOfWeek:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayOfWeekAbbr:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]},legend:{selector:{all:"All",inverse:"Inv"}},toolbox:{brush:{title:{rect:"Box Select",polygon:"Lasso Select",lineX:"Horizontally Select",lineY:"Vertically Select",keep:"Keep Selections",clear:"Clear Selections"}},dataView:{title:"Data View",lang:["Data View","Close","Refresh"]},dataZoom:{title:{zoom:"Zoom",back:"Zoom Reset"}},magicType:{title:{line:"Switch to Line Chart",bar:"Switch to Bar Chart",stack:"Stack",tiled:"Tile"}},restore:{title:"Restore"},saveAsImage:{title:"Save as Image",lang:["Right Click to Save Image"]}},series:{typeNames:{pie:"Pie chart",bar:"Bar chart",line:"Line chart",scatter:"Scatter plot",effectScatter:"Ripple scatter plot",radar:"Radar chart",tree:"Tree",treemap:"Treemap",boxplot:"Boxplot",candlestick:"Candlestick",k:"K line chart",heatmap:"Heat map",map:"Map",parallel:"Parallel coordinate map",lines:"Line graph",graph:"Relationship graph",sankey:"Sankey diagram",funnel:"Funnel chart",gauge:"Gauge",pictorialBar:"Pictorial bar",themeRiver:"Theme River Map",sunburst:"Sunburst"}},aria:{general:{withTitle:'This is a chart about "{title}"',withoutTitle:"This is a chart"},series:{single:{prefix:"",withName:" with type {seriesType} named {seriesName}.",withoutName:" with type {seriesType}."},multiple:{prefix:". It consists of {seriesCount} series count.",withName:" The {seriesId} series is a {seriesType} representing {seriesName}.",withoutName:" The {seriesId} series is a {seriesType}.",separator:{middle:"",end:""}}},data:{allData:"The data is as follows: ",partialData:"The first {displayCnt} items are: ",withName:"the data for {name} is {value}",withoutName:"{value}",separator:{middle:", ",end:". "}}}}),Oc(Cc,{time:{month:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthAbbr:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],dayOfWeek:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],dayOfWeekAbbr:["日","一","二","三","四","五","六"]},legend:{selector:{all:"全选",inverse:"反选"}},toolbox:{brush:{title:{rect:"矩形选择",polygon:"圈选",lineX:"横向选择",lineY:"纵向选择",keep:"保持选择",clear:"清除选择"}},dataView:{title:"数据视图",lang:["数据视图","关闭","刷新"]},dataZoom:{title:{zoom:"区域缩放",back:"区域缩放还原"}},magicType:{title:{line:"切换为折线图",bar:"切换为柱状图",stack:"切换为堆叠",tiled:"切换为平铺"}},restore:{title:"还原"},saveAsImage:{title:"保存为图片",lang:["右键另存为图片"]}},series:{typeNames:{pie:"饼图",bar:"柱状图",line:"折线图",scatter:"散点图",effectScatter:"涟漪散点图",radar:"雷达图",tree:"树图",treemap:"矩形树图",boxplot:"箱型图",candlestick:"K线图",k:"K线图",heatmap:"热力图",map:"地图",parallel:"平行坐标图",lines:"线图",graph:"关系图",sankey:"桑基图",funnel:"漏斗图",gauge:"仪表盘图",pictorialBar:"象形柱图",themeRiver:"主题河流图",sunburst:"旭日图"}},aria:{general:{withTitle:"这是一个关于“{title}”的图表。",withoutTitle:"这是一个图表,"},series:{single:{prefix:"",withName:"图表类型是{seriesType},表示{seriesName}。",withoutName:"图表类型是{seriesType}。"},multiple:{prefix:"它由{seriesCount}个图表系列组成。",withName:"第{seriesId}个系列是一个表示{seriesName}的{seriesType},",withoutName:"第{seriesId}个系列是一个{seriesType},",separator:{middle:";",end:"。"}}},data:{allData:"其数据是——",partialData:"其中,前{displayCnt}项是——",withName:"{name}的数据是{value}",withoutName:"{value}",separator:{middle:",",end:""}}}});var Nc=1e3,Ec=6e4,zc=36e5,Vc=864e5,Bc=31536e6,Fc={year:"{yyyy}",month:"{MMM}",day:"{d}",hour:"{HH}:{mm}",minute:"{HH}:{mm}",second:"{HH}:{mm}:{ss}",millisecond:"{HH}:{mm}:{ss} {SSS}",none:"{yyyy}-{MM}-{dd} {HH}:{mm}:{ss} {SSS}"},Gc="{yyyy}-{MM}-{dd}",Wc={year:"{yyyy}",month:"{yyyy}-{MM}",day:Gc,hour:"{yyyy}-{MM}-{dd} "+Fc.hour,minute:"{yyyy}-{MM}-{dd} "+Fc.minute,second:"{yyyy}-{MM}-{dd} "+Fc.second,millisecond:Fc.none},Hc=["year","month","day","hour","minute","second","millisecond"],Yc=["year","half-year","quarter","month","week","half-week","day","half-day","quarter-day","hour","minute","second","millisecond"];function Uc(t,e){return"0000".substr(0,e-(t+="").length)+t}function Xc(t){switch(t){case"half-year":case"quarter":return"month";case"week":case"half-week":return"day";case"half-day":case"quarter-day":return"hour";default:return t}}function Zc(t){return t===Xc(t)}function jc(t,e,n,i){var r=io(t),o=r[$c(n)](),a=r[Jc(n)]()+1,s=Math.floor((a-1)/3)+1,l=r[Qc(n)](),u=r["get"+(n?"UTC":"")+"Day"](),h=r[tp(n)](),c=(h-1)%12+1,p=r[ep(n)](),d=r[np(n)](),f=r[ip(n)](),g=(i instanceof Sc?i:Rc(i||Pc)||Lc.EN).getModel("time"),y=g.get("month"),v=g.get("monthAbbr"),m=g.get("dayOfWeek"),x=g.get("dayOfWeekAbbr");return(e||"").replace(/{yyyy}/g,o+"").replace(/{yy}/g,o%100+"").replace(/{Q}/g,s+"").replace(/{MMMM}/g,y[a-1]).replace(/{MMM}/g,v[a-1]).replace(/{MM}/g,Uc(a,2)).replace(/{M}/g,a+"").replace(/{dd}/g,Uc(l,2)).replace(/{d}/g,l+"").replace(/{eeee}/g,m[u]).replace(/{ee}/g,x[u]).replace(/{e}/g,u+"").replace(/{HH}/g,Uc(h,2)).replace(/{H}/g,h+"").replace(/{hh}/g,Uc(c+"",2)).replace(/{h}/g,c+"").replace(/{mm}/g,Uc(p,2)).replace(/{m}/g,p+"").replace(/{ss}/g,Uc(d,2)).replace(/{s}/g,d+"").replace(/{SSS}/g,Uc(f,3)).replace(/{S}/g,f+"")}function qc(t,e){var n=io(t),i=n[Jc(e)]()+1,r=n[Qc(e)](),o=n[tp(e)](),a=n[ep(e)](),s=n[np(e)](),l=0===n[ip(e)](),u=l&&0===s,h=u&&0===a,c=h&&0===o,p=c&&1===r;return p&&1===i?"year":p?"month":c?"day":h?"hour":u?"minute":l?"second":"millisecond"}function Kc(t,e,n){var i=j(t)?io(t):t;switch(e=e||qc(t,n)){case"year":return i[$c(n)]();case"half-year":return i[Jc(n)]()>=6?1:0;case"quarter":return Math.floor((i[Jc(n)]()+1)/4);case"month":return i[Jc(n)]();case"day":return i[Qc(n)]();case"half-day":return i[tp(n)]()/24;case"hour":return i[tp(n)]();case"minute":return i[ep(n)]();case"second":return i[np(n)]();case"millisecond":return i[ip(n)]()}}function $c(t){return t?"getUTCFullYear":"getFullYear"}function Jc(t){return t?"getUTCMonth":"getMonth"}function Qc(t){return t?"getUTCDate":"getDate"}function tp(t){return t?"getUTCHours":"getHours"}function ep(t){return t?"getUTCMinutes":"getMinutes"}function np(t){return t?"getUTCSeconds":"getSeconds"}function ip(t){return t?"getUTCMilliseconds":"getMilliseconds"}function rp(t){return t?"setUTCFullYear":"setFullYear"}function op(t){return t?"setUTCMonth":"setMonth"}function ap(t){return t?"setUTCDate":"setDate"}function sp(t){return t?"setUTCHours":"setHours"}function lp(t){return t?"setUTCMinutes":"setMinutes"}function up(t){return t?"setUTCSeconds":"setSeconds"}function hp(t){return t?"setUTCMilliseconds":"setMilliseconds"}function cp(t){if(!ho(t))return X(t)?t:"-";var e=(t+"").split(".");return e[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,"$1,")+(e.length>1?"."+e[1]:"")}function pp(t,e){return t=(t||"").toLowerCase().replace(/-(.)/g,(function(t,e){return e.toUpperCase()})),e&&t&&(t=t.charAt(0).toUpperCase()+t.slice(1)),t}var dp=st;function fp(t,e,n){function i(t){return t&&ut(t)?t:"-"}function r(t){return!(null==t||isNaN(t)||!isFinite(t))}var o="time"===e,a=t instanceof Date;if(o||a){var s=o?io(t):t;if(!isNaN(+s))return jc(s,"{yyyy}-{MM}-{dd} {HH}:{mm}:{ss}",n);if(a)return"-"}if("ordinal"===e)return Z(t)?i(t):j(t)&&r(t)?t+"":"-";var l=uo(t);return r(l)?cp(l):Z(t)?i(t):"boolean"==typeof t?t+"":"-"}var gp=["a","b","c","d","e","f","g"],yp=function(t,e){return"{"+t+(null==e?"":e)+"}"};function vp(t,e,n){Y(e)||(e=[e]);var i=e.length;if(!i)return"";for(var r=e[0].$vars||[],o=0;o':'':{renderMode:o,content:"{"+(n.markerId||"markerX")+"|} ",style:"subItem"===r?{width:4,height:4,borderRadius:2,backgroundColor:i}:{width:10,height:10,borderRadius:5,backgroundColor:i}}:""}function xp(t,e){return e=e||"transparent",X(t)?t:q(t)&&t.colorStops&&(t.colorStops[0]||{}).color||e}function _p(t,e){if("_blank"===e||"blank"===e){var n=window.open();n.opener=null,n.location.href=t}else window.open(t,e)}var bp=E,wp=["left","right","top","bottom","width","height"],Sp=[["width","left","right"],["height","top","bottom"]];function Mp(t,e,n,i,r){var o=0,a=0;null==i&&(i=1/0),null==r&&(r=1/0);var s=0;e.eachChild((function(l,u){var h,c,p=l.getBoundingRect(),d=e.childAt(u+1),f=d&&d.getBoundingRect();if("horizontal"===t){var g=p.width+(f?-f.x+p.x:0);(h=o+g)>i||l.newline?(o=0,h=g,a+=s+n,s=p.height):s=Math.max(s,p.height)}else{var y=p.height+(f?-f.y+p.y:0);(c=a+y)>r||l.newline?(o+=s+n,a=0,c=y,s=p.width):s=Math.max(s,p.width)}l.newline||(l.x=o,l.y=a,l.markRedraw(),"horizontal"===t?o=h+n:a=c+n)}))}var Ip=Mp;H(Mp,"vertical"),H(Mp,"horizontal");function Tp(t,e,n){n=dp(n||0);var i=e.width,r=e.height,o=Ur(t.left,i),a=Ur(t.top,r),s=Ur(t.right,i),l=Ur(t.bottom,r),u=Ur(t.width,i),h=Ur(t.height,r),c=n[2]+n[0],p=n[1]+n[3],d=t.aspect;switch(isNaN(u)&&(u=i-s-p-o),isNaN(h)&&(h=r-l-c-a),null!=d&&(isNaN(u)&&isNaN(h)&&(d>i/r?u=.8*i:h=.8*r),isNaN(u)&&(u=d*h),isNaN(h)&&(h=u/d)),isNaN(o)&&(o=i-s-u-p),isNaN(a)&&(a=r-l-h-c),t.left||t.right){case"center":o=i/2-u/2-n[3];break;case"right":o=i-u-p}switch(t.top||t.bottom){case"middle":case"center":a=r/2-h/2-n[0];break;case"bottom":a=r-h-c}o=o||0,a=a||0,isNaN(u)&&(u=i-p-o-(s||0)),isNaN(h)&&(h=r-c-a-(l||0));var f=new Ee(o+n[3],a+n[0],u,h);return f.margin=n,f}function Cp(t,e,n,i,r,o){var a,s=!r||!r.hv||r.hv[0],l=!r||!r.hv||r.hv[1],u=r&&r.boundingMode||"all";if((o=o||t).x=t.x,o.y=t.y,!s&&!l)return!1;if("raw"===u)a="group"===t.type?new Ee(0,0,+e.width||0,+e.height||0):t.getBoundingRect();else if(a=t.getBoundingRect(),t.needLocalTransform()){var h=t.getLocalTransform();(a=a.clone()).applyTransform(h)}var c=Tp(k({width:a.width,height:a.height},e),n,i),p=s?c.x-a.x:0,d=l?c.y-a.y:0;return"raw"===u?(o.x=p,o.y=d):(o.x+=p,o.y+=d),o===t&&t.markRedraw(),!0}function Dp(t){var e=t.layoutMode||t.constructor.layoutMode;return q(e)?e:e?{type:e}:null}function Ap(t,e,n){var i=n&&n.ignoreSize;!Y(i)&&(i=[i,i]);var r=a(Sp[0],0),o=a(Sp[1],1);function a(n,r){var o={},a=0,u={},h=0;if(bp(n,(function(e){u[e]=t[e]})),bp(n,(function(t){s(e,t)&&(o[t]=u[t]=e[t]),l(o,t)&&a++,l(u,t)&&h++})),i[r])return l(e,n[1])?u[n[2]]=null:l(e,n[2])&&(u[n[1]]=null),u;if(2!==h&&a){if(a>=2)return o;for(var c=0;c=0;a--)o=C(o,n[a],!0);e.defaultOption=o}return e.defaultOption},e.prototype.getReferringComponents=function(t,e){var n=t+"Index",i=t+"Id";return Vo(this.ecModel,t,{index:this.get(n,!0),id:this.get(i,!0)},e)},e.prototype.getBoxLayoutParams=function(){var t=this;return{left:t.get("left"),top:t.get("top"),right:t.get("right"),bottom:t.get("bottom"),width:t.get("width"),height:t.get("height")}},e.prototype.getZLevelKey=function(){return""},e.prototype.setZLevel=function(t){this.option.zlevel=t},e.protoInitialize=function(){var t=e.prototype;t.type="component",t.id="",t.name="",t.mainType="",t.subType="",t.componentIndex=0}(),e}(Sc);Xo(Op,Sc),Ko(Op),function(t){var e={};t.registerSubTypeDefaulter=function(t,n){var i=Yo(t);e[i.main]=n},t.determineSubType=function(n,i){var r=i.type;if(!r){var o=Yo(n).main;t.hasSubTypes(n)&&e[o]&&(r=e[o](i))}return r}}(Op),function(t,e){function n(t,e){return t[e]||(t[e]={predecessor:[],successor:[]}),t[e]}t.topologicalTravel=function(t,i,r,o){if(t.length){var a=function(t){var i={},r=[];return E(t,(function(o){var a=n(i,o),s=function(t,e){var n=[];return E(t,(function(t){P(e,t)>=0&&n.push(t)})),n}(a.originalDeps=e(o),t);a.entryCount=s.length,0===a.entryCount&&r.push(o),E(s,(function(t){P(a.predecessor,t)<0&&a.predecessor.push(t);var e=n(i,t);P(e.successor,t)<0&&e.successor.push(o)}))})),{graph:i,noEntryList:r}}(i),s=a.graph,l=a.noEntryList,u={};for(E(t,(function(t){u[t]=!0}));l.length;){var h=l.pop(),c=s[h],p=!!u[h];p&&(r.call(o,h,c.originalDeps.slice()),delete u[h]),E(c.successor,p?f:d)}E(u,(function(){var t="";throw new Error(t)}))}function d(t){s[t].entryCount--,0===s[t].entryCount&&l.push(t)}function f(t){u[t]=!0,d(t)}}}(Op,(function(t){var e=[];E(Op.getClassesByMainType(t),(function(t){e=e.concat(t.dependencies||t.prototype.dependencies||[])})),e=z(e,(function(t){return Yo(t).main})),"dataset"!==t&&P(e,"dataset")<=0&&e.unshift("dataset");return e}));var Rp="";"undefined"!=typeof navigator&&(Rp=navigator.platform||"");var Np="rgba(0, 0, 0, 0.2)",Ep={darkMode:"auto",colorBy:"series",color:["#5470c6","#91cc75","#fac858","#ee6666","#73c0de","#3ba272","#fc8452","#9a60b4","#ea7ccc"],gradientColor:["#f6efa6","#d88273","#bf444c"],aria:{decal:{decals:[{color:Np,dashArrayX:[1,0],dashArrayY:[2,5],symbolSize:1,rotation:Math.PI/6},{color:Np,symbol:"circle",dashArrayX:[[8,8],[0,8,8,0]],dashArrayY:[6,0],symbolSize:.8},{color:Np,dashArrayX:[1,0],dashArrayY:[4,3],rotation:-Math.PI/4},{color:Np,dashArrayX:[[6,6],[0,6,6,0]],dashArrayY:[6,0]},{color:Np,dashArrayX:[[1,0],[1,6]],dashArrayY:[1,0,6,0],rotation:Math.PI/4},{color:Np,symbol:"triangle",dashArrayX:[[9,9],[0,9,9,0]],dashArrayY:[7,2],symbolSize:.75}]}},textStyle:{fontFamily:Rp.match(/^Win/)?"Microsoft YaHei":"sans-serif",fontSize:12,fontStyle:"normal",fontWeight:"normal"},blendMode:null,stateAnimation:{duration:300,easing:"cubicOut"},animation:"auto",animationDuration:1e3,animationDurationUpdate:500,animationEasing:"cubicInOut",animationEasingUpdate:"cubicInOut",animationThreshold:2e3,progressiveThreshold:3e3,progressive:400,hoverLayerThreshold:3e3,useUTC:!1},zp=yt(["tooltip","label","itemName","itemId","itemGroupId","seriesName"]),Vp="original",Bp="arrayRows",Fp="objectRows",Gp="keyedColumns",Wp="typedArray",Hp="unknown",Yp="column",Up="row",Xp=1,Zp=2,jp=3,qp=Po();function Kp(t,e,n){var i={},r=Jp(e);if(!r||!t)return i;var o,a,s=[],l=[],u=e.ecModel,h=qp(u).datasetMap,c=r.uid+"_"+n.seriesLayoutBy;E(t=t.slice(),(function(e,n){var r=q(e)?e:t[n]={name:e};"ordinal"===r.type&&null==o&&(o=n,a=f(r)),i[r.name]=[]}));var p=h.get(c)||h.set(c,{categoryWayDim:a,valueWayDim:0});function d(t,e,n){for(var i=0;ie)return t[i];return t[n-1]}(i,a):n;if((h=h||n)&&h.length){var c=h[l];return r&&(u[r]=c),s.paletteIdx=(l+1)%h.length,c}}var hd=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.init=function(t,e,n,i,r,o){i=i||{},this.option=null,this._theme=new Sc(i),this._locale=new Sc(r),this._optionManager=o},e.prototype.setOption=function(t,e,n){var i=dd(e);this._optionManager.setOption(t,n,i),this._resetOption(null,i)},e.prototype.resetOption=function(t,e){return this._resetOption(t,dd(e))},e.prototype._resetOption=function(t,e){var n=!1,i=this._optionManager;if(!t||"recreate"===t){var r=i.mountOption("recreate"===t);0,this.option&&"recreate"!==t?(this.restoreData(),this._mergeOption(r,e)):rd(this,r),n=!0}if("timeline"!==t&&"media"!==t||this.restoreData(),!t||"recreate"===t||"timeline"===t){var o=i.getTimelineOption(this);o&&(n=!0,this._mergeOption(o,e))}if(!t||"recreate"===t||"media"===t){var a=i.getMediaOption(this);a.length&&E(a,(function(t){n=!0,this._mergeOption(t,e)}),this)}return n},e.prototype.mergeOption=function(t){this._mergeOption(t,null)},e.prototype._mergeOption=function(t,e){var n=this.option,i=this._componentsMap,r=this._componentsCount,o=[],a=yt(),s=e&&e.replaceMergeMainTypeMap;qp(this).datasetMap=yt(),E(t,(function(t,e){null!=t&&(Op.hasClass(e)?e&&(o.push(e),a.set(e,!0)):n[e]=null==n[e]?T(t):C(n[e],t,!0))})),s&&s.each((function(t,e){Op.hasClass(e)&&!a.get(e)&&(o.push(e),a.set(e,!0))})),Op.topologicalTravel(o,Op.getAllClassMainTypes(),(function(e){var o=function(t,e,n){var i=ed.get(e);if(!i)return n;var r=i(t);return r?n.concat(r):n}(this,e,_o(t[e])),a=i.get(e),l=a?s&&s.get(e)?"replaceMerge":"normalMerge":"replaceAll",u=Io(a,o,l);(function(t,e,n){E(t,(function(t){var i=t.newOption;q(i)&&(t.keyInfo.mainType=e,t.keyInfo.subType=function(t,e,n,i){return e.type?e.type:n?n.subType:i.determineSubType(t,e)}(e,i,t.existing,n))}))})(u,e,Op),n[e]=null,i.set(e,null),r.set(e,0);var h,c=[],p=[],d=0;E(u,(function(t,n){var i=t.existing,r=t.newOption;if(r){var o="series"===e,a=Op.getClass(e,t.keyInfo.subType,!o);if(!a)return;if("tooltip"===e){if(h)return void 0;h=!0}if(i&&i.constructor===a)i.name=t.keyInfo.name,i.mergeOption(r,this),i.optionUpdated(r,!1);else{var s=A({componentIndex:n},t.keyInfo);A(i=new a(r,this,this,s),s),t.brandNew&&(i.__requireNewView=!0),i.init(r,this,this),i.optionUpdated(null,!0)}}else i&&(i.mergeOption({},this),i.optionUpdated({},!1));i?(c.push(i.option),p.push(i),d++):(c.push(void 0),p.push(void 0))}),this),n[e]=c,i.set(e,p),r.set(e,d),"series"===e&&nd(this)}),this),this._seriesIndices||nd(this)},e.prototype.getOption=function(){var t=T(this.option);return E(t,(function(e,n){if(Op.hasClass(n)){for(var i=_o(e),r=i.length,o=!1,a=r-1;a>=0;a--)i[a]&&!ko(i[a])?o=!0:(i[a]=null,!o&&r--);i.length=r,t[n]=i}})),delete t["\0_ec_inner"],t},e.prototype.getTheme=function(){return this._theme},e.prototype.getLocaleModel=function(){return this._locale},e.prototype.setUpdatePayload=function(t){this._payload=t},e.prototype.getUpdatePayload=function(){return this._payload},e.prototype.getComponent=function(t,e){var n=this._componentsMap.get(t);if(n){var i=n[e||0];if(i)return i;if(null==e)for(var r=0;r=e:"max"===n?t<=e:t===e})(i[a],t,o)||(r=!1)}})),r}var bd=E,wd=q,Sd=["areaStyle","lineStyle","nodeStyle","linkStyle","chordStyle","label","labelLine"];function Md(t){var e=t&&t.itemStyle;if(e)for(var n=0,i=Sd.length;n=0;g--){var y=t[g];if(s||(p=y.data.rawIndexOf(y.stackedByDimension,c)),p>=0){var v=y.data.getByRawIndex(y.stackResultDimension,p);if("all"===l||"positive"===l&&v>0||"negative"===l&&v<0||"samesign"===l&&d>=0&&v>0||"samesign"===l&&d<=0&&v<0){d=Jr(d,v),f=v;break}}}return i[0]=d,i[1]=f,i}))}))}var Wd,Hd,Yd,Ud,Xd,Zd=function(t){this.data=t.data||(t.sourceFormat===Gp?{}:[]),this.sourceFormat=t.sourceFormat||Hp,this.seriesLayoutBy=t.seriesLayoutBy||Yp,this.startIndex=t.startIndex||0,this.dimensionsDetectedCount=t.dimensionsDetectedCount,this.metaRawOption=t.metaRawOption;var e=this.dimensionsDefine=t.dimensionsDefine;if(e)for(var n=0;nu&&(u=d)}s[0]=l,s[1]=u}},i=function(){return this._data?this._data.length/this._dimSize:0};function r(t){for(var e=0;e=0&&(s=o.interpolatedValue[l])}return null!=s?s+"":""})):void 0},t.prototype.getRawValue=function(t,e){return df(this.getData(e),t)},t.prototype.formatTooltip=function(t,e,n){},t}();function yf(t){var e,n;return q(t)?t.type&&(n=t):e=t,{text:e,frag:n}}function vf(t){return new mf(t)}var mf=function(){function t(t){t=t||{},this._reset=t.reset,this._plan=t.plan,this._count=t.count,this._onDirty=t.onDirty,this._dirty=!0}return t.prototype.perform=function(t){var e,n=this._upstream,i=t&&t.skip;if(this._dirty&&n){var r=this.context;r.data=r.outputData=n.context.outputData}this.__pipeline&&(this.__pipeline.currentTask=this),this._plan&&!i&&(e=this._plan(this.context));var o,a=h(this._modBy),s=this._modDataCount||0,l=h(t&&t.modBy),u=t&&t.modDataCount||0;function h(t){return!(t>=1)&&(t=1),t}a===l&&s===u||(e="reset"),(this._dirty||"reset"===e)&&(this._dirty=!1,o=this._doReset(i)),this._modBy=l,this._modDataCount=u;var c=t&&t.step;if(this._dueEnd=n?n._outputDueEnd:this._count?this._count(this.context):1/0,this._progress){var p=this._dueIndex,d=Math.min(null!=c?this._dueIndex+c:1/0,this._dueEnd);if(!i&&(o||p1&&i>0?s:a}};return o;function a(){return e=t?null:oe},gte:function(t,e){return t>=e}},Mf=function(){function t(t,e){if(!j(e)){var n="";0,yo(n)}this._opFn=Sf[t],this._rvalFloat=uo(e)}return t.prototype.evaluate=function(t){return j(t)?this._opFn(t,this._rvalFloat):this._opFn(uo(t),this._rvalFloat)},t}(),If=function(){function t(t,e){var n="desc"===t;this._resultLT=n?1:-1,null==e&&(e=n?"min":"max"),this._incomparable="min"===e?-1/0:1/0}return t.prototype.evaluate=function(t,e){var n=j(t)?t:uo(t),i=j(e)?e:uo(e),r=isNaN(n),o=isNaN(i);if(r&&(n=this._incomparable),o&&(i=this._incomparable),r&&o){var a=X(t),s=X(e);a&&(n=s?t:0),s&&(i=a?e:0)}return ni?-this._resultLT:0},t}(),Tf=function(){function t(t,e){this._rval=e,this._isEQ=t,this._rvalTypeof=typeof e,this._rvalFloat=uo(e)}return t.prototype.evaluate=function(t){var e=t===this._rval;if(!e){var n=typeof t;n===this._rvalTypeof||"number"!==n&&"number"!==this._rvalTypeof||(e=uo(t)===this._rvalFloat)}return this._isEQ?e:!e},t}();function Cf(t,e){return"eq"===t||"ne"===t?new Tf("eq"===t,e):_t(Sf,t)?new Mf(t,e):null}var Df=function(){function t(){}return t.prototype.getRawData=function(){throw new Error("not supported")},t.prototype.getRawDataItem=function(t){throw new Error("not supported")},t.prototype.cloneRawData=function(){},t.prototype.getDimensionInfo=function(t){},t.prototype.cloneAllDimensionInfo=function(){},t.prototype.count=function(){},t.prototype.retrieveValue=function(t,e){},t.prototype.retrieveValueFromItem=function(t,e){},t.prototype.convertValue=function(t,e){return _f(t,e)},t}();function Af(t){var e=t.sourceFormat;if(!Nf(e)){var n="";0,yo(n)}return t.data}function kf(t){var e=t.sourceFormat,n=t.data;if(!Nf(e)){var i="";0,yo(i)}if(e===Bp){for(var r=[],o=0,a=n.length;o65535?Vf:Bf}function Yf(t,e,n,i,r){var o=Wf[n||"float"];if(r){var a=t[e],s=a&&a.length;if(s!==i){for(var l=new o(i),u=0;ug[1]&&(g[1]=f)}return this._rawCount=this._count=s,{start:a,end:s}},t.prototype._initDataFromProvider=function(t,e,n){for(var i=this._provider,r=this._chunks,o=this._dimensions,a=o.length,s=this._rawExtent,l=z(o,(function(t){return t.property})),u=0;uy[1]&&(y[1]=g)}}!i.persistent&&i.clean&&i.clean(),this._rawCount=this._count=e,this._extent=[]},t.prototype.count=function(){return this._count},t.prototype.get=function(t,e){if(!(e>=0&&e=0&&e=this._rawCount||t<0)return-1;if(!this._indices)return t;var e=this._indices,n=e[t];if(null!=n&&nt))return o;r=o-1}}return-1},t.prototype.indicesOfNearest=function(t,e,n){var i=this._chunks[t],r=[];if(!i)return r;null==n&&(n=1/0);for(var o=1/0,a=-1,s=0,l=0,u=this.count();l=0&&a<0)&&(o=c,a=h,s=0),h===a&&(r[s++]=l))}return r.length=s,r},t.prototype.getIndices=function(){var t,e=this._indices;if(e){var n=e.constructor,i=this._count;if(n===Array){t=new n(i);for(var r=0;r=u&&x<=h||isNaN(x))&&(a[s++]=d),d++}p=!0}else if(2===r){f=c[i[0]];var y=c[i[1]],v=t[i[1]][0],m=t[i[1]][1];for(g=0;g=u&&x<=h||isNaN(x))&&(_>=v&&_<=m||isNaN(_))&&(a[s++]=d),d++}p=!0}}if(!p)if(1===r)for(g=0;g=u&&x<=h||isNaN(x))&&(a[s++]=b)}else for(g=0;gt[M][1])&&(w=!1)}w&&(a[s++]=e.getRawIndex(g))}return sy[1]&&(y[1]=g)}}}},t.prototype.lttbDownSample=function(t,e){var n,i,r,o=this.clone([t],!0),a=o._chunks[t],s=this.count(),l=0,u=Math.floor(1/e),h=this.getRawIndex(0),c=new(Hf(this._rawCount))(Math.min(2*(Math.ceil(s/u)+2),s));c[l++]=h;for(var p=1;pn&&(n=i,r=I)}M>0&&M<_-x&&(c[l++]=Math.min(S,r),r=Math.max(S,r)),c[l++]=r,h=r}return c[l++]=this.getRawIndex(s-1),o._count=l,o._indices=c,o.getRawIndex=this._getRawIdx,o},t.prototype.downSample=function(t,e,n,i){for(var r=this.clone([t],!0),o=r._chunks,a=[],s=Math.floor(1/e),l=o[t],u=this.count(),h=r._rawExtent[t]=[1/0,-1/0],c=new(Hf(this._rawCount))(Math.ceil(u/s)),p=0,d=0;du-d&&(s=u-d,a.length=s);for(var f=0;fh[1]&&(h[1]=y),c[p++]=v}return r._count=p,r._indices=c,r._updateGetRawIdx(),r},t.prototype.each=function(t,e){if(this._count)for(var n=t.length,i=this._chunks,r=0,o=this.count();ra&&(a=l)}return i=[o,a],this._extent[t]=i,i},t.prototype.getRawDataItem=function(t){var e=this.getRawIndex(t);if(this._provider.persistent)return this._provider.getItem(e);for(var n=[],i=this._chunks,r=0;r=0?this._indices[t]:-1},t.prototype._updateGetRawIdx=function(){this.getRawIndex=this._indices?this._getRawIdx:this._getRawIdxIdentity},t.internalField=function(){function t(t,e,n,i){return _f(t[i],this._dimensions[i])}Ef={arrayRows:t,objectRows:function(t,e,n,i){return _f(t[e],this._dimensions[i])},keyedColumns:t,original:function(t,e,n,i){var r=t&&(null==t.value?t:t.value);return _f(r instanceof Array?r[i]:r,this._dimensions[i])},typedArray:function(t,e,n,i){return t[i]}}}(),t}(),Xf=function(){function t(t){this._sourceList=[],this._storeList=[],this._upstreamSignList=[],this._versionSignBase=0,this._dirty=!0,this._sourceHost=t}return t.prototype.dirty=function(){this._setLocalSource([],[]),this._storeList=[],this._dirty=!0},t.prototype._setLocalSource=function(t,e){this._sourceList=t,this._upstreamSignList=e,this._versionSignBase++,this._versionSignBase>9e10&&(this._versionSignBase=0)},t.prototype._getVersionSign=function(){return this._sourceHost.uid+"_"+this._versionSignBase},t.prototype.prepareSource=function(){this._isDirty()&&(this._createSource(),this._dirty=!1)},t.prototype._createSource=function(){this._setLocalSource([],[]);var t,e,n=this._sourceHost,i=this._getUpstreamSourceManagers(),r=!!i.length;if(jf(n)){var o=n,a=void 0,s=void 0,l=void 0;if(r){var u=i[0];u.prepareSource(),a=(l=u.getSource()).data,s=l.sourceFormat,e=[u._getVersionSign()]}else s=$(a=o.get("data",!0))?Wp:Vp,e=[];var h=this._getSourceMetaRawOption()||{},c=l&&l.metaRawOption||{},p=rt(h.seriesLayoutBy,c.seriesLayoutBy)||null,d=rt(h.sourceHeader,c.sourceHeader),f=rt(h.dimensions,c.dimensions);t=p!==c.seriesLayoutBy||!!d!=!!c.sourceHeader||f?[qd(a,{seriesLayoutBy:p,sourceHeader:d,dimensions:f},s)]:[]}else{var g=n;if(r){var y=this._applyTransform(i);t=y.sourceList,e=y.upstreamSignList}else{t=[qd(g.get("source",!0),this._getSourceMetaRawOption(),null)],e=[]}}this._setLocalSource(t,e)},t.prototype._applyTransform=function(t){var e,n=this._sourceHost,i=n.get("transform",!0),r=n.get("fromTransformResult",!0);if(null!=r){var o="";1!==t.length&&qf(o)}var a,s=[],l=[];return E(t,(function(t){t.prepareSource();var e=t.getSource(r||0),n="";null==r||e||qf(n),s.push(e),l.push(t._getVersionSign())})),i?e=function(t,e,n){var i=_o(t),r=i.length,o="";r||yo(o);for(var a=0,s=r;a1||n>0&&!t.noHeader;return E(t.blocks,(function(t){var n=ng(t);n>=e&&(e=n+ +(i&&(!n||tg(t)&&!t.noHeader)))})),e}return 0}function ig(t,e,n,i){var r,o=e.noHeader,a=(r=ng(e),{html:$f[r],richText:Jf[r]}),s=[],l=e.blocks||[];lt(!l||Y(l)),l=l||[];var u=t.orderMode;if(e.sortBlocks&&u){l=l.slice();var h={valueAsc:"asc",valueDesc:"desc"};if(_t(h,u)){var c=new If(h[u],null);l.sort((function(t,e){return c.evaluate(t.sortParam,e.sortParam)}))}else"seriesDesc"===u&&l.reverse()}E(l,(function(n,r){var o=e.valueFormatter,l=eg(n)(o?A(A({},t),{valueFormatter:o}):t,n,r>0?a.html:0,i);null!=l&&s.push(l)}));var p="richText"===t.renderMode?s.join(a.richText):ag(s.join(""),o?n:a.html);if(o)return p;var d=fp(e.header,"ordinal",t.useUTC),f=Kf(i,t.renderMode).nameStyle;return"richText"===t.renderMode?sg(t,d,f)+a.richText+p:ag('
                '+ie(d)+"
                "+p,n)}function rg(t,e,n,i){var r=t.renderMode,o=e.noName,a=e.noValue,s=!e.markerType,l=e.name,u=t.useUTC,h=e.valueFormatter||t.valueFormatter||function(t){return z(t=Y(t)?t:[t],(function(t,e){return fp(t,Y(d)?d[e]:d,u)}))};if(!o||!a){var c=s?"":t.markupStyleCreator.makeTooltipMarker(e.markerType,e.markerColor||"#333",r),p=o?"":fp(l,"ordinal",u),d=e.valueType,f=a?[]:h(e.value),g=!s||!o,y=!s&&o,v=Kf(i,r),m=v.nameStyle,x=v.valueStyle;return"richText"===r?(s?"":c)+(o?"":sg(t,p,m))+(a?"":function(t,e,n,i,r){var o=[r],a=i?10:20;return n&&o.push({padding:[0,0,0,a],align:"right"}),t.markupStyleCreator.wrapRichTextStyle(Y(e)?e.join(" "):e,o)}(t,f,g,y,x)):ag((s?"":c)+(o?"":function(t,e,n){return''+ie(t)+""}(p,!s,m))+(a?"":function(t,e,n,i){var r=n?"10px":"20px",o=e?"float:right;margin-left:"+r:"";return t=Y(t)?t:[t],''+z(t,(function(t){return ie(t)})).join("  ")+""}(f,g,y,x)),n)}}function og(t,e,n,i,r,o){if(t)return eg(t)({useUTC:r,renderMode:n,orderMode:i,markupStyleCreator:e,valueFormatter:t.valueFormatter},t,0,o)}function ag(t,e){return'
                '+t+'
                '}function sg(t,e,n){return t.markupStyleCreator.wrapRichTextStyle(e,n)}function lg(t,e){return xp(t.getData().getItemVisual(e,"style")[t.visualDrawType])}function ug(t,e){var n=t.get("padding");return null!=n?n:"richText"===e?[8,10]:10}var hg=function(){function t(){this.richTextStyles={},this._nextStyleNameId=co()}return t.prototype._generateStyleName=function(){return"__EC_aUTo_"+this._nextStyleNameId++},t.prototype.makeTooltipMarker=function(t,e,n){var i="richText"===n?this._generateStyleName():null,r=mp({color:e,type:t,renderMode:n,markerId:i});return X(r)?r:(this.richTextStyles[i]=r.style,r.content)},t.prototype.wrapRichTextStyle=function(t,e){var n={};Y(e)?E(e,(function(t){return A(n,t)})):A(n,e);var i=this._generateStyleName();return this.richTextStyles[i]=n,"{"+i+"|"+t+"}"},t}();function cg(t){var e,n,i,r,o=t.series,a=t.dataIndex,s=t.multipleSeries,l=o.getData(),u=l.mapDimensionsAll("defaultedTooltip"),h=u.length,c=o.getRawValue(a),p=Y(c),d=lg(o,a);if(h>1||p&&!h){var f=function(t,e,n,i,r){var o=e.getData(),a=V(t,(function(t,e,n){var i=o.getDimensionInfo(n);return t||i&&!1!==i.tooltip&&null!=i.displayName}),!1),s=[],l=[],u=[];function h(t,e){var n=o.getDimensionInfo(e);n&&!1!==n.otherDims.tooltip&&(a?u.push(Qf("nameValue",{markerType:"subItem",markerColor:r,name:n.displayName,value:t,valueType:n.type})):(s.push(t),l.push(n.type)))}return i.length?E(i,(function(t){h(df(o,n,t),t)})):E(t,h),{inlineValues:s,inlineValueTypes:l,blocks:u}}(c,o,a,u,d);e=f.inlineValues,n=f.inlineValueTypes,i=f.blocks,r=f.inlineValues[0]}else if(h){var g=l.getDimensionInfo(u[0]);r=e=df(l,a,u[0]),n=g.type}else r=e=p?c[0]:c;var y=Ao(o),v=y&&o.name||"",m=l.getName(a),x=s?v:m;return Qf("section",{header:v,noHeader:s||!y,sortParam:r,blocks:[Qf("nameValue",{markerType:"item",markerColor:d,name:x,noName:!ut(x),value:e,valueType:n})].concat(i||[])})}var pg=Po();function dg(t,e){return t.getName(e)||t.getId(e)}var fg=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e._selectedDataIndicesMap={},e}return n(e,t),e.prototype.init=function(t,e,n){this.seriesIndex=this.componentIndex,this.dataTask=vf({count:yg,reset:vg}),this.dataTask.context={model:this},this.mergeDefaultAndTheme(t,n),(pg(this).sourceManager=new Xf(this)).prepareSource();var i=this.getInitialData(t,n);xg(i,this),this.dataTask.context.data=i,pg(this).dataBeforeProcessed=i,gg(this),this._initSelectedMapFromData(i)},e.prototype.mergeDefaultAndTheme=function(t,e){var n=Dp(this),i=n?kp(t):{},r=this.subType;Op.hasClass(r)&&(r+="Series"),C(t,e.getTheme().get(this.subType)),C(t,this.getDefaultOption()),bo(t,"label",["show"]),this.fillDataTextStyle(t.data),n&&Ap(t,i,n)},e.prototype.mergeOption=function(t,e){t=C(this.option,t,!0),this.fillDataTextStyle(t.data);var n=Dp(this);n&&Ap(this.option,t,n);var i=pg(this).sourceManager;i.dirty(),i.prepareSource();var r=this.getInitialData(t,e);xg(r,this),this.dataTask.dirty(),this.dataTask.context.data=r,pg(this).dataBeforeProcessed=r,gg(this),this._initSelectedMapFromData(r)},e.prototype.fillDataTextStyle=function(t){if(t&&!$(t))for(var e=["show"],n=0;nthis.getShallow("animationThreshold")&&(e=!1),!!e},e.prototype.restoreData=function(){this.dataTask.dirty()},e.prototype.getColorFromPalette=function(t,e,n){var i=this.ecModel,r=sd.prototype.getColorFromPalette.call(this,t,e,n);return r||(r=i.getColorFromPalette(t,e,n)),r},e.prototype.coordDimToDataDim=function(t){return this.getRawData().mapDimensionsAll(t)},e.prototype.getProgressive=function(){return this.get("progressive")},e.prototype.getProgressiveThreshold=function(){return this.get("progressiveThreshold")},e.prototype.select=function(t,e){this._innerSelect(this.getData(e),t)},e.prototype.unselect=function(t,e){var n=this.option.selectedMap;if(n){var i=this.option.selectedMode,r=this.getData(e);if("series"===i||"all"===n)return this.option.selectedMap={},void(this._selectedDataIndicesMap={});for(var o=0;o=0&&n.push(r)}return n},e.prototype.isSelected=function(t,e){var n=this.option.selectedMap;if(!n)return!1;var i=this.getData(e);return("all"===n||n[dg(i,t)])&&!i.getItemModel(t).get(["select","disabled"])},e.prototype.isUniversalTransitionEnabled=function(){if(this.__universalTransitionEnabled)return!0;var t=this.option.universalTransition;return!!t&&(!0===t||t&&t.enabled)},e.prototype._innerSelect=function(t,e){var n,i,r=this.option,o=r.selectedMode,a=e.length;if(o&&a)if("series"===o)r.selectedMap="all";else if("multiple"===o){q(r.selectedMap)||(r.selectedMap={});for(var s=r.selectedMap,l=0;l0&&this._innerSelect(t,e)}},e.registerClass=function(t){return Op.registerClass(t)},e.protoInitialize=function(){var t=e.prototype;t.type="series.__base__",t.seriesIndex=0,t.ignoreStyleOnData=!1,t.hasSymbolVisual=!1,t.defaultSymbol="circle",t.visualStyleAccessPath="itemStyle",t.visualDrawType="fill"}(),e}(Op);function gg(t){var e=t.name;Ao(t)||(t.name=function(t){var e=t.getRawData(),n=e.mapDimensionsAll("seriesName"),i=[];return E(n,(function(t){var n=e.getDimensionInfo(t);n.displayName&&i.push(n.displayName)})),i.join(" ")}(t)||e)}function yg(t){return t.model.getRawData().count()}function vg(t){var e=t.model;return e.setData(e.getRawData().cloneShallow()),mg}function mg(t,e){e.outputData&&t.end>e.outputData.count()&&e.model.getRawData().cloneShallow(e.outputData)}function xg(t,e){E(vt(t.CHANGABLE_METHODS,t.DOWNSAMPLE_METHODS),(function(n){t.wrapMethod(n,H(_g,e))}))}function _g(t,e){var n=bg(t);return n&&n.setOutputEnd((e||this).count()),e}function bg(t){var e=(t.ecModel||{}).scheduler,n=e&&e.getPipeline(t.uid);if(n){var i=n.currentTask;if(i){var r=i.agentStubMap;r&&(i=r.get(t.uid))}return i}}R(fg,gf),R(fg,sd),Xo(fg,Op);var wg=function(){function t(){this.group=new Er,this.uid=Ic("viewComponent")}return t.prototype.init=function(t,e){},t.prototype.render=function(t,e,n,i){},t.prototype.dispose=function(t,e){},t.prototype.updateView=function(t,e,n,i){},t.prototype.updateLayout=function(t,e,n,i){},t.prototype.updateVisual=function(t,e,n,i){},t.prototype.toggleBlurSeries=function(t,e,n){},t.prototype.eachRendered=function(t){var e=this.group;e&&e.traverse(t)},t}();function Sg(){var t=Po();return function(e){var n=t(e),i=e.pipelineContext,r=!!n.large,o=!!n.progressiveRender,a=n.large=!(!i||!i.large),s=n.progressiveRender=!(!i||!i.progressiveRender);return!(r===a&&o===s)&&"reset"}}Uo(wg),Ko(wg);var Mg=Po(),Ig=Sg(),Tg=function(){function t(){this.group=new Er,this.uid=Ic("viewChart"),this.renderTask=vf({plan:Ag,reset:kg}),this.renderTask.context={view:this}}return t.prototype.init=function(t,e){},t.prototype.render=function(t,e,n,i){0},t.prototype.highlight=function(t,e,n,i){var r=t.getData(i&&i.dataType);r&&Dg(r,i,"emphasis")},t.prototype.downplay=function(t,e,n,i){var r=t.getData(i&&i.dataType);r&&Dg(r,i,"normal")},t.prototype.remove=function(t,e){this.group.removeAll()},t.prototype.dispose=function(t,e){},t.prototype.updateView=function(t,e,n,i){this.render(t,e,n,i)},t.prototype.updateLayout=function(t,e,n,i){this.render(t,e,n,i)},t.prototype.updateVisual=function(t,e,n,i){this.render(t,e,n,i)},t.prototype.eachRendered=function(t){jh(this.group,t)},t.markUpdateMethod=function(t,e){Mg(t).updateMethod=e},t.protoInitialize=void(t.prototype.type="chart"),t}();function Cg(t,e,n){t&&ql(t)&&("emphasis"===e?Al:kl)(t,n)}function Dg(t,e,n){var i=Lo(t,e),r=e&&null!=e.highlightKey?function(t){var e=el[t];return null==e&&tl<=32&&(e=el[t]=tl++),e}(e.highlightKey):null;null!=i?E(_o(i),(function(e){Cg(t.getItemGraphicEl(e),n,r)})):t.eachItemGraphicEl((function(t){Cg(t,n,r)}))}function Ag(t){return Ig(t.model)}function kg(t){var e=t.model,n=t.ecModel,i=t.api,r=t.payload,o=e.pipelineContext.progressiveRender,a=t.view,s=r&&Mg(r).updateMethod,l=o?"incrementalPrepareRender":s&&a[s]?s:"render";return"render"!==l&&a[l](e,n,i,r),Lg[l]}Uo(Tg),Ko(Tg);var Lg={incrementalPrepareRender:{progress:function(t,e){e.view.incrementalRender(t,e.model,e.ecModel,e.api,e.payload)}},render:{forceFirstProgress:!0,progress:function(t,e){e.view.render(e.model,e.ecModel,e.api,e.payload)}}},Pg="\0__throttleOriginMethod",Og="\0__throttleRate",Rg="\0__throttleType";function Ng(t,e,n){var i,r,o,a,s,l=0,u=0,h=null;function c(){u=(new Date).getTime(),h=null,t.apply(o,a||[])}e=e||0;var p=function(){for(var t=[],p=0;p=0?c():h=setTimeout(c,-r),l=i};return p.clear=function(){h&&(clearTimeout(h),h=null)},p.debounceNextCall=function(t){s=t},p}function Eg(t,e,n,i){var r=t[e];if(r){var o=r[Pg]||r,a=r[Rg];if(r[Og]!==n||a!==i){if(null==n||!i)return t[e]=o;(r=t[e]=Ng(o,n,"debounce"===i))[Pg]=o,r[Rg]=i,r[Og]=n}return r}}function zg(t,e){var n=t[e];n&&n[Pg]&&(n.clear&&n.clear(),t[e]=n[Pg])}var Vg=Po(),Bg={itemStyle:$o(_c,!0),lineStyle:$o(vc,!0)},Fg={lineStyle:"stroke",itemStyle:"fill"};function Gg(t,e){var n=t.visualStyleMapper||Bg[e];return n||(console.warn("Unknown style type '"+e+"'."),Bg.itemStyle)}function Wg(t,e){var n=t.visualDrawType||Fg[e];return n||(console.warn("Unknown style type '"+e+"'."),"fill")}var Hg={createOnAllSeries:!0,performRawSeries:!0,reset:function(t,e){var n=t.getData(),i=t.visualStyleAccessPath||"itemStyle",r=t.getModel(i),o=Gg(t,i)(r),a=r.getShallow("decal");a&&(n.setVisual("decal",a),a.dirty=!0);var s=Wg(t,i),l=o[s],u=U(l)?l:null,h="auto"===o.fill||"auto"===o.stroke;if(!o[s]||u||h){var c=t.getColorFromPalette(t.name,null,e.getSeriesCount());o[s]||(o[s]=c,n.setVisual("colorFromPalette",!0)),o.fill="auto"===o.fill||U(o.fill)?c:o.fill,o.stroke="auto"===o.stroke||U(o.stroke)?c:o.stroke}if(n.setVisual("style",o),n.setVisual("drawType",s),!e.isSeriesFiltered(t)&&u)return n.setVisual("colorFromPalette",!1),{dataEach:function(e,n){var i=t.getDataParams(n),r=A({},o);r[s]=u(i),e.setItemVisual(n,"style",r)}}}},Yg=new Sc,Ug={createOnAllSeries:!0,performRawSeries:!0,reset:function(t,e){if(!t.ignoreStyleOnData&&!e.isSeriesFiltered(t)){var n=t.getData(),i=t.visualStyleAccessPath||"itemStyle",r=Gg(t,i),o=n.getVisual("drawType");return{dataEach:n.hasItemOption?function(t,e){var n=t.getRawDataItem(e);if(n&&n[i]){Yg.option=n[i];var a=r(Yg);A(t.ensureUniqueItemVisual(e,"style"),a),Yg.option.decal&&(t.setItemVisual(e,"decal",Yg.option.decal),Yg.option.decal.dirty=!0),o in a&&t.setItemVisual(e,"colorFromPalette",!1)}}:null}}}},Xg={performRawSeries:!0,overallReset:function(t){var e=yt();t.eachSeries((function(t){var n=t.getColorBy();if(!t.isColorBySeries()){var i=t.type+"-"+n,r=e.get(i);r||(r={},e.set(i,r)),Vg(t).scope=r}})),t.eachSeries((function(e){if(!e.isColorBySeries()&&!t.isSeriesFiltered(e)){var n=e.getRawData(),i={},r=e.getData(),o=Vg(e).scope,a=e.visualStyleAccessPath||"itemStyle",s=Wg(e,a);r.each((function(t){var e=r.getRawIndex(t);i[e]=t})),n.each((function(t){var a=i[t];if(r.getItemVisual(a,"colorFromPalette")){var l=r.ensureUniqueItemVisual(a,"style"),u=n.getName(t)||t+"",h=n.count();l[s]=e.getColorFromPalette(u,o,h)}}))}}))}},Zg=Math.PI;var jg=function(){function t(t,e,n,i){this._stageTaskMap=yt(),this.ecInstance=t,this.api=e,n=this._dataProcessorHandlers=n.slice(),i=this._visualHandlers=i.slice(),this._allHandlers=n.concat(i)}return t.prototype.restoreData=function(t,e){t.restoreData(e),this._stageTaskMap.each((function(t){var e=t.overallTask;e&&e.dirty()}))},t.prototype.getPerformArgs=function(t,e){if(t.__pipeline){var n=this._pipelineMap.get(t.__pipeline.id),i=n.context,r=!e&&n.progressiveEnabled&&(!i||i.progressiveRender)&&t.__idxInPipeline>n.blockIndex?n.step:null,o=i&&i.modDataCount;return{step:r,modBy:null!=o?Math.ceil(o/r):null,modDataCount:o}}},t.prototype.getPipeline=function(t){return this._pipelineMap.get(t)},t.prototype.updateStreamModes=function(t,e){var n=this._pipelineMap.get(t.uid),i=t.getData().count(),r=n.progressiveEnabled&&e.incrementalPrepareRender&&i>=n.threshold,o=t.get("large")&&i>=t.get("largeThreshold"),a="mod"===t.get("progressiveChunkMode")?i:null;t.pipelineContext=n.context={progressiveRender:r,modDataCount:a,large:o}},t.prototype.restorePipelines=function(t){var e=this,n=e._pipelineMap=yt();t.eachSeries((function(t){var i=t.getProgressive(),r=t.uid;n.set(r,{id:r,head:null,tail:null,threshold:t.getProgressiveThreshold(),progressiveEnabled:i&&!(t.preventIncremental&&t.preventIncremental()),blockIndex:-1,step:Math.round(i||700),count:0}),e._pipe(t,t.dataTask)}))},t.prototype.prepareStageTasks=function(){var t=this._stageTaskMap,e=this.api.getModel(),n=this.api;E(this._allHandlers,(function(i){var r=t.get(i.uid)||t.set(i.uid,{}),o="";lt(!(i.reset&&i.overallReset),o),i.reset&&this._createSeriesStageTask(i,r,e,n),i.overallReset&&this._createOverallStageTask(i,r,e,n)}),this)},t.prototype.prepareView=function(t,e,n,i){var r=t.renderTask,o=r.context;o.model=e,o.ecModel=n,o.api=i,r.__block=!t.incrementalPrepareRender,this._pipe(e,r)},t.prototype.performDataProcessorTasks=function(t,e){this._performStageTasks(this._dataProcessorHandlers,t,e,{block:!0})},t.prototype.performVisualTasks=function(t,e,n){this._performStageTasks(this._visualHandlers,t,e,n)},t.prototype._performStageTasks=function(t,e,n,i){i=i||{};var r=!1,o=this;function a(t,e){return t.setDirty&&(!t.dirtyMap||t.dirtyMap.get(e.__pipeline.id))}E(t,(function(t,s){if(!i.visualType||i.visualType===t.visualType){var l=o._stageTaskMap.get(t.uid),u=l.seriesTaskMap,h=l.overallTask;if(h){var c,p=h.agentStubMap;p.each((function(t){a(i,t)&&(t.dirty(),c=!0)})),c&&h.dirty(),o.updatePayload(h,n);var d=o.getPerformArgs(h,i.block);p.each((function(t){t.perform(d)})),h.perform(d)&&(r=!0)}else u&&u.each((function(s,l){a(i,s)&&s.dirty();var u=o.getPerformArgs(s,i.block);u.skip=!t.performRawSeries&&e.isSeriesFiltered(s.context.model),o.updatePayload(s,n),s.perform(u)&&(r=!0)}))}})),this.unfinished=r||this.unfinished},t.prototype.performSeriesTasks=function(t){var e;t.eachSeries((function(t){e=t.dataTask.perform()||e})),this.unfinished=e||this.unfinished},t.prototype.plan=function(){this._pipelineMap.each((function(t){var e=t.tail;do{if(e.__block){t.blockIndex=e.__idxInPipeline;break}e=e.getUpstream()}while(e)}))},t.prototype.updatePayload=function(t,e){"remain"!==e&&(t.context.payload=e)},t.prototype._createSeriesStageTask=function(t,e,n,i){var r=this,o=e.seriesTaskMap,a=e.seriesTaskMap=yt(),s=t.seriesType,l=t.getTargetSeries;function u(e){var s=e.uid,l=a.set(s,o&&o.get(s)||vf({plan:Qg,reset:ty,count:iy}));l.context={model:e,ecModel:n,api:i,useClearVisual:t.isVisual&&!t.isLayout,plan:t.plan,reset:t.reset,scheduler:r},r._pipe(e,l)}t.createOnAllSeries?n.eachRawSeries(u):s?n.eachRawSeriesByType(s,u):l&&l(n,i).each(u)},t.prototype._createOverallStageTask=function(t,e,n,i){var r=this,o=e.overallTask=e.overallTask||vf({reset:qg});o.context={ecModel:n,api:i,overallReset:t.overallReset,scheduler:r};var a=o.agentStubMap,s=o.agentStubMap=yt(),l=t.seriesType,u=t.getTargetSeries,h=!0,c=!1,p="";function d(t){var e=t.uid,n=s.set(e,a&&a.get(e)||(c=!0,vf({reset:Kg,onDirty:Jg})));n.context={model:t,overallProgress:h},n.agent=o,n.__block=h,r._pipe(t,n)}lt(!t.createOnAllSeries,p),l?n.eachRawSeriesByType(l,d):u?u(n,i).each(d):(h=!1,E(n.getSeries(),d)),c&&o.dirty()},t.prototype._pipe=function(t,e){var n=t.uid,i=this._pipelineMap.get(n);!i.head&&(i.head=e),i.tail&&i.tail.pipe(e),i.tail=e,e.__idxInPipeline=i.count++,e.__pipeline=i},t.wrapStageHandler=function(t,e){return U(t)&&(t={overallReset:t,seriesType:ry(t)}),t.uid=Ic("stageHandler"),e&&(t.visualType=e),t},t}();function qg(t){t.overallReset(t.ecModel,t.api,t.payload)}function Kg(t){return t.overallProgress&&$g}function $g(){this.agent.dirty(),this.getDownstream().dirty()}function Jg(){this.agent&&this.agent.dirty()}function Qg(t){return t.plan?t.plan(t.model,t.ecModel,t.api,t.payload):null}function ty(t){t.useClearVisual&&t.data.clearAllVisual();var e=t.resetDefines=_o(t.reset(t.model,t.ecModel,t.api,t.payload));return e.length>1?z(e,(function(t,e){return ny(e)})):ey}var ey=ny(0);function ny(t){return function(e,n){var i=n.data,r=n.resetDefines[t];if(r&&r.dataEach)for(var o=e.start;o0&&h===r.length-u.length){var c=r.slice(0,h);"data"!==c&&(e.mainType=c,e[u.toLowerCase()]=t,s=!0)}}a.hasOwnProperty(r)&&(n[r]=t,s=!0),s||(i[r]=t)}))}return{cptQuery:e,dataQuery:n,otherQuery:i}},t.prototype.filter=function(t,e){var n=this.eventInfo;if(!n)return!0;var i=n.targetEl,r=n.packedEvent,o=n.model,a=n.view;if(!o||!a)return!0;var s=e.cptQuery,l=e.dataQuery;return u(s,o,"mainType")&&u(s,o,"subType")&&u(s,o,"index","componentIndex")&&u(s,o,"name")&&u(s,o,"id")&&u(l,r,"name")&&u(l,r,"dataIndex")&&u(l,r,"dataType")&&(!a.filterForExposedEvent||a.filterForExposedEvent(t,e.otherQuery,i,r));function u(t,e,n,i){return null==t[n]||e[i||n]===t[n]}},t.prototype.afterTrigger=function(){this.eventInfo=null},t}(),vy=["symbol","symbolSize","symbolRotate","symbolOffset"],my=vy.concat(["symbolKeepAspect"]),xy={createOnAllSeries:!0,performRawSeries:!0,reset:function(t,e){var n=t.getData();if(t.legendIcon&&n.setVisual("legendIcon",t.legendIcon),t.hasSymbolVisual){for(var i={},r={},o=!1,a=0;a=0&&Gy(l)?l:.5,t.createRadialGradient(a,s,0,a,s,l)}(t,e,n):function(t,e,n){var i=null==e.x?0:e.x,r=null==e.x2?1:e.x2,o=null==e.y?0:e.y,a=null==e.y2?0:e.y2;return e.global||(i=i*n.width+n.x,r=r*n.width+n.x,o=o*n.height+n.y,a=a*n.height+n.y),i=Gy(i)?i:0,r=Gy(r)?r:1,o=Gy(o)?o:0,a=Gy(a)?a:0,t.createLinearGradient(i,o,r,a)}(t,e,n),r=e.colorStops,o=0;o0&&(e=i.lineDash,n=i.lineWidth,e&&"solid"!==e&&n>0?"dashed"===e?[4*n,2*n]:"dotted"===e?[n]:j(e)?[e]:Y(e)?e:null:null),o=i.lineDashOffset;if(r){var a=i.strokeNoScale&&t.getLineScale?t.getLineScale():1;a&&1!==a&&(r=z(r,(function(t){return t/a})),o/=a)}return[r,o]}var Xy=new rs(!0);function Zy(t){var e=t.stroke;return!(null==e||"none"===e||!(t.lineWidth>0))}function jy(t){return"string"==typeof t&&"none"!==t}function qy(t){var e=t.fill;return null!=e&&"none"!==e}function Ky(t,e){if(null!=e.fillOpacity&&1!==e.fillOpacity){var n=t.globalAlpha;t.globalAlpha=e.fillOpacity*e.opacity,t.fill(),t.globalAlpha=n}else t.fill()}function $y(t,e){if(null!=e.strokeOpacity&&1!==e.strokeOpacity){var n=t.globalAlpha;t.globalAlpha=e.strokeOpacity*e.opacity,t.stroke(),t.globalAlpha=n}else t.stroke()}function Jy(t,e,n){var i=na(e.image,e.__image,n);if(ra(i)){var r=t.createPattern(i,e.repeat||"repeat");if("function"==typeof DOMMatrix&&r&&r.setTransform){var o=new DOMMatrix;o.translateSelf(e.x||0,e.y||0),o.rotateSelf(0,0,(e.rotation||0)*wt),o.scaleSelf(e.scaleX||1,e.scaleY||1),r.setTransform(o)}return r}}var Qy=["shadowBlur","shadowOffsetX","shadowOffsetY"],tv=[["lineCap","butt"],["lineJoin","miter"],["miterLimit",10]];function ev(t,e,n,i,r){var o=!1;if(!i&&e===(n=n||{}))return!1;if(i||e.opacity!==n.opacity){rv(t,r),o=!0;var a=Math.max(Math.min(e.opacity,1),0);t.globalAlpha=isNaN(a)?ma.opacity:a}(i||e.blend!==n.blend)&&(o||(rv(t,r),o=!0),t.globalCompositeOperation=e.blend||ma.blend);for(var s=0;s0&&t.unfinished);t.unfinished||this._zr.flush()}}},e.prototype.getDom=function(){return this._dom},e.prototype.getId=function(){return this.id},e.prototype.getZr=function(){return this._zr},e.prototype.isSSR=function(){return this._ssr},e.prototype.setOption=function(t,e,n){if(!this.__flagInMainProcess)if(this._disposed)qv(this.id);else{var i,r,o;if(q(e)&&(n=e.lazyUpdate,i=e.silent,r=e.replaceMerge,o=e.transition,e=e.notMerge),this.__flagInMainProcess=!0,!this._model||e){var a=new xd(this._api),s=this._theme,l=this._model=new hd;l.scheduler=this._scheduler,l.ssr=this._ssr,l.init(null,null,null,s,this._locale,a)}this._model.setOption(t,{replaceMerge:r},Qv);var u={seriesTransition:o,optionChanged:!0};if(n)this.__pendingUpdate={silent:i,updateParams:u},this.__flagInMainProcess=!1,this.getZr().wakeUp();else{try{Tv(this),Av.update.call(this,null,u)}catch(t){throw this.__pendingUpdate=null,this.__flagInMainProcess=!1,t}this._ssr||this._zr.flush(),this.__pendingUpdate=null,this.__flagInMainProcess=!1,Ov.call(this,i),Rv.call(this,i)}}},e.prototype.setTheme=function(){go()},e.prototype.getModel=function(){return this._model},e.prototype.getOption=function(){return this._model&&this._model.getOption()},e.prototype.getWidth=function(){return this._zr.getWidth()},e.prototype.getHeight=function(){return this._zr.getHeight()},e.prototype.getDevicePixelRatio=function(){return this._zr.painter.dpr||r.hasGlobalWindow&&window.devicePixelRatio||1},e.prototype.getRenderedCanvas=function(t){return this.renderToCanvas(t)},e.prototype.renderToCanvas=function(t){t=t||{};var e=this._zr.painter;return e.getRenderedCanvas({backgroundColor:t.backgroundColor||this._model.get("backgroundColor"),pixelRatio:t.pixelRatio||this.getDevicePixelRatio()})},e.prototype.renderToSVGString=function(t){t=t||{};var e=this._zr.painter;return e.renderToString({useViewBox:t.useViewBox})},e.prototype.getSvgDataURL=function(){if(r.svgSupported){var t=this._zr;return E(t.storage.getDisplayList(),(function(t){t.stopAnimation(null,!0)})),t.painter.toDataURL()}},e.prototype.getDataURL=function(t){if(!this._disposed){var e=(t=t||{}).excludeComponents,n=this._model,i=[],r=this;E(e,(function(t){n.eachComponent({mainType:t},(function(t){var e=r._componentsMap[t.__viewId];e.group.ignore||(i.push(e),e.group.ignore=!0)}))}));var o="svg"===this._zr.painter.getType()?this.getSvgDataURL():this.renderToCanvas(t).toDataURL("image/"+(t&&t.type||"png"));return E(i,(function(t){t.group.ignore=!1})),o}qv(this.id)},e.prototype.getConnectedDataURL=function(t){if(!this._disposed){var e="svg"===t.type,n=this.group,i=Math.min,r=Math.max,o=1/0;if(rm[n]){var a=o,s=o,l=-1/0,u=-1/0,c=[],p=t&&t.pixelRatio||this.getDevicePixelRatio();E(im,(function(o,h){if(o.group===n){var p=e?o.getZr().painter.getSvgDom().innerHTML:o.renderToCanvas(T(t)),d=o.getDom().getBoundingClientRect();a=i(d.left,a),s=i(d.top,s),l=r(d.right,l),u=r(d.bottom,u),c.push({dom:p,left:d.left,top:d.top})}}));var d=(l*=p)-(a*=p),f=(u*=p)-(s*=p),g=h.createCanvas(),y=Fr(g,{renderer:e?"svg":"canvas"});if(y.resize({width:d,height:f}),e){var v="";return E(c,(function(t){var e=t.left-a,n=t.top-s;v+=''+t.dom+""})),y.painter.getSvgRoot().innerHTML=v,t.connectedBackgroundColor&&y.painter.setBackgroundColor(t.connectedBackgroundColor),y.refreshImmediately(),y.painter.toDataURL()}return t.connectedBackgroundColor&&y.add(new Es({shape:{x:0,y:0,width:d,height:f},style:{fill:t.connectedBackgroundColor}})),E(c,(function(t){var e=new As({style:{x:t.left*p-a,y:t.top*p-s,image:t.dom}});y.add(e)})),y.refreshImmediately(),g.toDataURL("image/"+(t&&t.type||"png"))}return this.getDataURL(t)}qv(this.id)},e.prototype.convertToPixel=function(t,e){return kv(this,"convertToPixel",t,e)},e.prototype.convertFromPixel=function(t,e){return kv(this,"convertFromPixel",t,e)},e.prototype.containPixel=function(t,e){var n;if(!this._disposed)return E(Ro(this._model,t),(function(t,i){i.indexOf("Models")>=0&&E(t,(function(t){var r=t.coordinateSystem;if(r&&r.containPoint)n=n||!!r.containPoint(e);else if("seriesModels"===i){var o=this._chartsMap[t.__viewId];o&&o.containPoint&&(n=n||o.containPoint(e,t))}else 0}),this)}),this),!!n;qv(this.id)},e.prototype.getVisual=function(t,e){var n=Ro(this._model,t,{defaultMainType:"series"}),i=n.seriesModel;var r=i.getData(),o=n.hasOwnProperty("dataIndexInside")?n.dataIndexInside:n.hasOwnProperty("dataIndex")?r.indexOfRawIndex(n.dataIndex):null;return null!=o?by(r,o,e):wy(r,e)},e.prototype.getViewOfComponentModel=function(t){return this._componentsMap[t.__viewId]},e.prototype.getViewOfSeriesModel=function(t){return this._chartsMap[t.__viewId]},e.prototype._initEvents=function(){var t,e,n,i=this;E(jv,(function(t){var e=function(e){var n,r=i.getModel(),o=e.target,a="globalout"===t;if(a?n={}:o&&Ty(o,(function(t){var e=Js(t);if(e&&null!=e.dataIndex){var i=e.dataModel||r.getSeriesByIndex(e.seriesIndex);return n=i&&i.getDataParams(e.dataIndex,e.dataType)||{},!0}if(e.eventData)return n=A({},e.eventData),!0}),!0),n){var s=n.componentType,l=n.componentIndex;"markLine"!==s&&"markPoint"!==s&&"markArea"!==s||(s="series",l=n.seriesIndex);var u=s&&null!=l&&r.getComponent(s,l),h=u&&i["series"===u.mainType?"_chartsMap":"_componentsMap"][u.__viewId];0,n.event=e,n.type=t,i._$eventProcessor.eventInfo={targetEl:o,packedEvent:n,model:u,view:h},i.trigger(t,n)}};e.zrEventfulCallAtLast=!0,i._zr.on(t,e,i)})),E($v,(function(t,e){i._messageCenter.on(e,(function(t){this.trigger(e,t)}),i)})),E(["selectchanged"],(function(t){i._messageCenter.on(t,(function(e){this.trigger(t,e)}),i)})),t=this._messageCenter,e=this,n=this._api,t.on("selectchanged",(function(t){var i=n.getModel();t.isFromClick?(Iy("map","selectchanged",e,i,t),Iy("pie","selectchanged",e,i,t)):"select"===t.fromAction?(Iy("map","selected",e,i,t),Iy("pie","selected",e,i,t)):"unselect"===t.fromAction&&(Iy("map","unselected",e,i,t),Iy("pie","unselected",e,i,t))}))},e.prototype.isDisposed=function(){return this._disposed},e.prototype.clear=function(){this._disposed?qv(this.id):this.setOption({series:[]},!0)},e.prototype.dispose=function(){if(this._disposed)qv(this.id);else{this._disposed=!0,this.getDom()&&Bo(this.getDom(),sm,"");var t=this,e=t._api,n=t._model;E(t._componentsViews,(function(t){t.dispose(n,e)})),E(t._chartsViews,(function(t){t.dispose(n,e)})),t._zr.dispose(),t._dom=t._model=t._chartsMap=t._componentsMap=t._chartsViews=t._componentsViews=t._scheduler=t._api=t._zr=t._throttledZrFlush=t._theme=t._coordSysMgr=t._messageCenter=null,delete im[t.id]}},e.prototype.resize=function(t){if(!this.__flagInMainProcess)if(this._disposed)qv(this.id);else{this._zr.resize(t);var e=this._model;if(this._loadingFX&&this._loadingFX.resize(),e){var n=e.resetOption("media"),i=t&&t.silent;this.__pendingUpdate&&(null==i&&(i=this.__pendingUpdate.silent),n=!0,this.__pendingUpdate=null),this.__flagInMainProcess=!0;try{n&&Tv(this),Av.update.call(this,{type:"resize",animation:A({duration:0},t&&t.animation)})}catch(t){throw this.__flagInMainProcess=!1,t}this.__flagInMainProcess=!1,Ov.call(this,i),Rv.call(this,i)}}},e.prototype.showLoading=function(t,e){if(this._disposed)qv(this.id);else if(q(t)&&(e=t,t=""),t=t||"default",this.hideLoading(),nm[t]){var n=nm[t](this._api,e),i=this._zr;this._loadingFX=n,i.add(n)}},e.prototype.hideLoading=function(){this._disposed?qv(this.id):(this._loadingFX&&this._zr.remove(this._loadingFX),this._loadingFX=null)},e.prototype.makeActionFromEvent=function(t){var e=A({},t);return e.type=$v[t.type],e},e.prototype.dispatchAction=function(t,e){if(this._disposed)qv(this.id);else if(q(e)||(e={silent:!!e}),Kv[t.type]&&this._model)if(this.__flagInMainProcess)this._pendingActions.push(t);else{var n=e.silent;Pv.call(this,t,n);var i=e.flush;i?this._zr.flush():!1!==i&&r.browser.weChat&&this._throttledZrFlush(),Ov.call(this,n),Rv.call(this,n)}},e.prototype.updateLabelLayout=function(){gv.trigger("series:layoutlabels",this._model,this._api,{updatedSeries:[]})},e.prototype.appendData=function(t){if(this._disposed)qv(this.id);else{var e=t.seriesIndex,n=this.getModel().getSeriesByIndex(e);0,n.appendData(t),this._scheduler.unfinished=!0,this.getZr().wakeUp()}},e.internalField=function(){function t(t){t.clearColorPalette(),t.eachSeries((function(t){t.clearColorPalette()}))}function e(t){for(var e=[],n=t.currentStates,i=0;i0?{duration:o,delay:i.get("delay"),easing:i.get("easing")}:null;n.eachRendered((function(t){if(t.states&&t.states.emphasis){if(gh(t))return;if(t instanceof Ms&&function(t){var e=nl(t);e.normalFill=t.style.fill,e.normalStroke=t.style.stroke;var n=t.states.select||{};e.selectFill=n.style&&n.style.fill||null,e.selectStroke=n.style&&n.style.stroke||null}(t),t.__dirty){var n=t.prevStates;n&&t.useStates(n)}if(r){t.stateTransition=a;var i=t.getTextContent(),o=t.getTextGuideLine();i&&(i.stateTransition=a),o&&(o.stateTransition=a)}t.__dirty&&e(t)}}))}Tv=function(t){var e=t._scheduler;e.restorePipelines(t._model),e.prepareStageTasks(),Cv(t,!0),Cv(t,!1),e.plan()},Cv=function(t,e){for(var n=t._model,i=t._scheduler,r=e?t._componentsViews:t._chartsViews,o=e?t._componentsMap:t._chartsMap,a=t._zr,s=t._api,l=0;le.get("hoverLayerThreshold")&&!r.node&&!r.worker&&e.eachSeries((function(e){if(!e.preventUsingHoverLayer){var n=t._chartsMap[e.__viewId];n.__alive&&n.eachRendered((function(t){t.states.emphasis&&(t.states.emphasis.hoverLayer=!0)}))}}))}(t,e),gv.trigger("series:afterupdate",e,n,l)},Wv=function(t){t.__needsUpdateStatus=!0,t.getZr().wakeUp()},Hv=function(t){t.__needsUpdateStatus&&(t.getZr().storage.traverse((function(t){gh(t)||e(t)})),t.__needsUpdateStatus=!1)},Fv=function(t){return new(function(e){function i(){return null!==e&&e.apply(this,arguments)||this}return n(i,e),i.prototype.getCoordinateSystems=function(){return t._coordSysMgr.getCoordinateSystems()},i.prototype.getComponentByElement=function(e){for(;e;){var n=e.__ecComponentInfo;if(null!=n)return t._model.getComponent(n.mainType,n.index);e=e.parent}},i.prototype.enterEmphasis=function(e,n){Al(e,n),Wv(t)},i.prototype.leaveEmphasis=function(e,n){kl(e,n),Wv(t)},i.prototype.enterBlur=function(e){Ll(e),Wv(t)},i.prototype.leaveBlur=function(e){Pl(e),Wv(t)},i.prototype.enterSelect=function(e){Ol(e),Wv(t)},i.prototype.leaveSelect=function(e){Rl(e),Wv(t)},i.prototype.getModel=function(){return t.getModel()},i.prototype.getViewOfComponentModel=function(e){return t.getViewOfComponentModel(e)},i.prototype.getViewOfSeriesModel=function(e){return t.getViewOfSeriesModel(e)},i}(gd))(t)},Gv=function(t){function e(t,e){for(var n=0;n=0)){bm.push(n);var o=jg.wrapStageHandler(n,r);o.__prio=e,o.__raw=n,t.push(o)}}function Sm(t,e){nm[t]=e}function Mm(t,e,n){var i=vv("registerMap");i&&i(t,e,n)}var Im=function(t){var e=(t=T(t)).type,n="";e||yo(n);var i=e.split(":");2!==i.length&&yo(n);var r=!1;"echarts"===i[0]&&(e=i[1],r=!0),t.__isBuiltIn=r,Of.set(e,t)};_m(mv,Hg),_m(xv,Ug),_m(xv,Xg),_m(mv,xy),_m(xv,_y),_m(7e3,(function(t,e){t.eachRawSeries((function(n){if(!t.isSeriesFiltered(n)){var i=n.getData();i.hasItemVisual()&&i.each((function(t){var n=i.getItemVisual(t,"decal");n&&(i.ensureUniqueItemVisual(t,"style").decal=cv(n,e))}));var r=i.getVisual("decal");if(r)i.getVisual("style").decal=cv(r,e)}}))})),pm(Fd),dm(900,(function(t){var e=yt();t.eachSeries((function(t){var n=t.get("stack");if(n){var i=e.get(n)||e.set(n,[]),r=t.getData(),o={stackResultDimension:r.getCalculationInfo("stackResultDimension"),stackedOverDimension:r.getCalculationInfo("stackedOverDimension"),stackedDimension:r.getCalculationInfo("stackedDimension"),stackedByDimension:r.getCalculationInfo("stackedByDimension"),isStackedByIndex:r.getCalculationInfo("isStackedByIndex"),data:r,seriesModel:t};if(!o.stackedDimension||!o.isStackedByIndex&&!o.stackedByDimension)return;i.length&&r.setCalculationInfo("stackedOnSeries",i[i.length-1].seriesModel),i.push(o)}})),e.each(Gd)})),Sm("default",(function(t,e){k(e=e||{},{text:"loading",textColor:"#000",fontSize:12,fontWeight:"normal",fontStyle:"normal",fontFamily:"sans-serif",maskColor:"rgba(255, 255, 255, 0.8)",showSpinner:!0,color:"#5470c6",spinnerRadius:10,lineWidth:5,zlevel:0});var n=new Er,i=new Es({style:{fill:e.maskColor},zlevel:e.zlevel,z:1e4});n.add(i);var r,o=new Bs({style:{text:e.text,fill:e.textColor,fontSize:e.fontSize,fontWeight:e.fontWeight,fontStyle:e.fontStyle,fontFamily:e.fontFamily},zlevel:e.zlevel,z:10001}),a=new Es({style:{fill:"none"},textContent:o,textConfig:{position:"right",distance:10},zlevel:e.zlevel,z:10001});return n.add(a),e.showSpinner&&((r=new Ju({shape:{startAngle:-Zg/2,endAngle:-Zg/2+.1,r:e.spinnerRadius},style:{stroke:e.color,lineCap:"round",lineWidth:e.lineWidth},zlevel:e.zlevel,z:10001})).animateShape(!0).when(1e3,{endAngle:3*Zg/2}).start("circularInOut"),r.animateShape(!0).when(1e3,{startAngle:3*Zg/2}).delay(300).start("circularInOut"),n.add(r)),n.resize=function(){var n=o.getBoundingRect().width,s=e.showSpinner?e.spinnerRadius:0,l=(t.getWidth()-2*s-(e.showSpinner&&n?10:0)-n)/2-(e.showSpinner&&n?0:5+n/2)+(e.showSpinner?0:n/2)+(n?0:s),u=t.getHeight()/2;e.showSpinner&&r.setShape({cx:l,cy:u}),a.setShape({x:l-s,y:u-s,width:2*s,height:2*s}),i.setShape({x:0,y:0,width:t.getWidth(),height:t.getHeight()})},n.resize(),n})),vm({type:sl,event:sl,update:sl},bt),vm({type:ll,event:ll,update:ll},bt),vm({type:ul,event:ul,update:ul},bt),vm({type:hl,event:hl,update:hl},bt),vm({type:cl,event:cl,update:cl},bt),cm("light",hy),cm("dark",gy);var Tm=[],Cm={registerPreprocessor:pm,registerProcessor:dm,registerPostInit:fm,registerPostUpdate:gm,registerUpdateLifecycle:ym,registerAction:vm,registerCoordinateSystem:mm,registerLayout:xm,registerVisual:_m,registerTransform:Im,registerLoading:Sm,registerMap:Mm,registerImpl:function(t,e){yv[t]=e},PRIORITY:_v,ComponentModel:Op,ComponentView:wg,SeriesModel:fg,ChartView:Tg,registerComponentModel:function(t){Op.registerClass(t)},registerComponentView:function(t){wg.registerClass(t)},registerSeriesModel:function(t){fg.registerClass(t)},registerChartView:function(t){Tg.registerClass(t)},registerSubTypeDefaulter:function(t,e){Op.registerSubTypeDefaulter(t,e)},registerPainter:function(t,e){Gr(t,e)}};function Dm(t){Y(t)?E(t,(function(t){Dm(t)})):P(Tm,t)>=0||(Tm.push(t),U(t)&&(t={install:t}),t.install(Cm))}function Am(t){return null==t?0:t.length||1}function km(t){return t}var Lm=function(){function t(t,e,n,i,r,o){this._old=t,this._new=e,this._oldKeyGetter=n||km,this._newKeyGetter=i||km,this.context=r,this._diffModeMultiple="multiple"===o}return t.prototype.add=function(t){return this._add=t,this},t.prototype.update=function(t){return this._update=t,this},t.prototype.updateManyToOne=function(t){return this._updateManyToOne=t,this},t.prototype.updateOneToMany=function(t){return this._updateOneToMany=t,this},t.prototype.updateManyToMany=function(t){return this._updateManyToMany=t,this},t.prototype.remove=function(t){return this._remove=t,this},t.prototype.execute=function(){this[this._diffModeMultiple?"_executeMultiple":"_executeOneToOne"]()},t.prototype._executeOneToOne=function(){var t=this._old,e=this._new,n={},i=new Array(t.length),r=new Array(e.length);this._initIndexMap(t,null,i,"_oldKeyGetter"),this._initIndexMap(e,n,r,"_newKeyGetter");for(var o=0;o1){var u=s.shift();1===s.length&&(n[a]=s[0]),this._update&&this._update(u,o)}else 1===l?(n[a]=null,this._update&&this._update(s,o)):this._remove&&this._remove(o)}this._performRestAdd(r,n)},t.prototype._executeMultiple=function(){var t=this._old,e=this._new,n={},i={},r=[],o=[];this._initIndexMap(t,n,r,"_oldKeyGetter"),this._initIndexMap(e,i,o,"_newKeyGetter");for(var a=0;a1&&1===c)this._updateManyToOne&&this._updateManyToOne(u,l),i[s]=null;else if(1===h&&c>1)this._updateOneToMany&&this._updateOneToMany(u,l),i[s]=null;else if(1===h&&1===c)this._update&&this._update(u,l),i[s]=null;else if(h>1&&c>1)this._updateManyToMany&&this._updateManyToMany(u,l),i[s]=null;else if(h>1)for(var p=0;p1)for(var a=0;a30}var Hm,Ym,Um,Xm,Zm,jm,qm,Km=q,$m=z,Jm="undefined"==typeof Int32Array?Array:Int32Array,Qm=["hasItemOption","_nameList","_idList","_invertedIndicesMap","_dimSummary","userOutput","_rawData","_dimValueGetter","_nameDimIdx","_idDimIdx","_nameRepeatCount"],tx=["_approximateExtent"],ex=function(){function t(t,e){var n;this.type="list",this._dimOmitted=!1,this._nameList=[],this._idList=[],this._visual={},this._layout={},this._itemVisuals=[],this._itemLayouts=[],this._graphicEls=[],this._approximateExtent={},this._calculationInfo={},this.hasItemOption=!1,this.TRANSFERABLE_METHODS=["cloneShallow","downSample","lttbDownSample","map"],this.CHANGABLE_METHODS=["filterSelf","selectRange"],this.DOWNSAMPLE_METHODS=["downSample","lttbDownSample"];var i=!1;Bm(t)?(n=t.dimensions,this._dimOmitted=t.isDimensionOmitted(),this._schema=t):(i=!0,n=t),n=n||["x","y"];for(var r={},o=[],a={},s=!1,l={},u=0;u=e)){var n=this._store.getProvider();this._updateOrdinalMeta();var i=this._nameList,r=this._idList;if(n.getSource().sourceFormat===Vp&&!n.pure)for(var o=[],a=t;a0},t.prototype.ensureUniqueItemVisual=function(t,e){var n=this._itemVisuals,i=n[t];i||(i=n[t]={});var r=i[e];return null==r&&(Y(r=this.getVisual(e))?r=r.slice():Km(r)&&(r=A({},r)),i[e]=r),r},t.prototype.setItemVisual=function(t,e,n){var i=this._itemVisuals[t]||{};this._itemVisuals[t]=i,Km(e)?A(i,e):i[e]=n},t.prototype.clearAllVisual=function(){this._visual={},this._itemVisuals=[]},t.prototype.setLayout=function(t,e){Km(t)?A(this._layout,t):this._layout[t]=e},t.prototype.getLayout=function(t){return this._layout[t]},t.prototype.getItemLayout=function(t){return this._itemLayouts[t]},t.prototype.setItemLayout=function(t,e,n){this._itemLayouts[t]=n?A(this._itemLayouts[t]||{},e):e},t.prototype.clearItemLayouts=function(){this._itemLayouts.length=0},t.prototype.setItemGraphicEl=function(t,e){var n=this.hostModel&&this.hostModel.seriesIndex;Qs(n,this.dataType,t,e),this._graphicEls[t]=e},t.prototype.getItemGraphicEl=function(t){return this._graphicEls[t]},t.prototype.eachItemGraphicEl=function(t,e){E(this._graphicEls,(function(n,i){n&&t&&t.call(e,n,i)}))},t.prototype.cloneShallow=function(e){return e||(e=new t(this._schema?this._schema:$m(this.dimensions,this._getDimInfo,this),this.hostModel)),Zm(e,this),e._store=this._store,e},t.prototype.wrapMethod=function(t,e){var n=this[t];U(n)&&(this.__wrappedMethods=this.__wrappedMethods||[],this.__wrappedMethods.push(t),this[t]=function(){var t=n.apply(this,arguments);return e.apply(this,[t].concat(at(arguments)))})},t.internalField=(Hm=function(t){var e=t._invertedIndicesMap;E(e,(function(n,i){var r=t._dimInfos[i],o=r.ordinalMeta,a=t._store;if(o){n=e[i]=new Jm(o.categories.length);for(var s=0;s1&&(s+="__ec__"+u),i[e]=s}})),t}();function nx(t,e){jd(t)||(t=Kd(t));var n=(e=e||{}).coordDimensions||[],i=e.dimensionsDefine||t.dimensionsDefine||[],r=yt(),o=[],a=function(t,e,n,i){var r=Math.max(t.dimensionsDetectedCount||1,e.length,n.length,i||0);return E(e,(function(t){var e;q(t)&&(e=t.dimsDef)&&(r=Math.max(r,e.length))})),r}(t,n,i,e.dimensionsCount),s=e.canOmitUnusedDimensions&&Wm(a),l=i===t.dimensionsDefine,u=l?Gm(t):Fm(i),h=e.encodeDefine;!h&&e.encodeDefaulter&&(h=e.encodeDefaulter(t,a));for(var c=yt(h),p=new Ff(a),d=0;d0&&(i.name=r+(o-1)),o++,e.set(r,o)}}(o),new Vm({source:t,dimensions:o,fullDimensionCount:a,dimensionOmitted:s})}function ix(t,e,n){if(n||e.hasKey(t)){for(var i=0;e.hasKey(t+i);)i++;t+=i}return e.set(t,!0),t}var rx=function(t){this.coordSysDims=[],this.axisMap=yt(),this.categoryAxisMap=yt(),this.coordSysName=t};var ox={cartesian2d:function(t,e,n,i){var r=t.getReferringComponents("xAxis",Eo).models[0],o=t.getReferringComponents("yAxis",Eo).models[0];e.coordSysDims=["x","y"],n.set("x",r),n.set("y",o),ax(r)&&(i.set("x",r),e.firstCategoryDimIndex=0),ax(o)&&(i.set("y",o),null==e.firstCategoryDimIndex&&(e.firstCategoryDimIndex=1))},singleAxis:function(t,e,n,i){var r=t.getReferringComponents("singleAxis",Eo).models[0];e.coordSysDims=["single"],n.set("single",r),ax(r)&&(i.set("single",r),e.firstCategoryDimIndex=0)},polar:function(t,e,n,i){var r=t.getReferringComponents("polar",Eo).models[0],o=r.findAxisModel("radiusAxis"),a=r.findAxisModel("angleAxis");e.coordSysDims=["radius","angle"],n.set("radius",o),n.set("angle",a),ax(o)&&(i.set("radius",o),e.firstCategoryDimIndex=0),ax(a)&&(i.set("angle",a),null==e.firstCategoryDimIndex&&(e.firstCategoryDimIndex=1))},geo:function(t,e,n,i){e.coordSysDims=["lng","lat"]},parallel:function(t,e,n,i){var r=t.ecModel,o=r.getComponent("parallel",t.get("parallelIndex")),a=e.coordSysDims=o.dimensions.slice();E(o.parallelAxisIndex,(function(t,o){var s=r.getComponent("parallelAxis",t),l=a[o];n.set(l,s),ax(s)&&(i.set(l,s),null==e.firstCategoryDimIndex&&(e.firstCategoryDimIndex=o))}))}};function ax(t){return"category"===t.get("type")}function sx(t,e,n){var i,r,o,a=(n=n||{}).byIndex,s=n.stackedCoordDimension;!function(t){return!Bm(t.schema)}(e)?(r=e.schema,i=r.dimensions,o=e.store):i=e;var l,u,h,c,p=!(!t||!t.get("stack"));if(E(i,(function(t,e){X(t)&&(i[e]=t={name:t}),p&&!t.isExtraCoord&&(a||l||!t.ordinalMeta||(l=t),u||"ordinal"===t.type||"time"===t.type||s&&s!==t.coordDim||(u=t))})),!u||a||l||(a=!0),u){h="__\0ecstackresult_"+t.id,c="__\0ecstackedover_"+t.id,l&&(l.createInvertedIndices=!0);var d=u.coordDim,f=u.type,g=0;E(i,(function(t){t.coordDim===d&&g++}));var y={name:h,coordDim:d,coordDimIndex:g,type:f,isExtraCoord:!0,isCalculationCoord:!0,storeDimIndex:i.length},v={name:c,coordDim:c,coordDimIndex:g+1,type:f,isExtraCoord:!0,isCalculationCoord:!0,storeDimIndex:i.length+1};r?(o&&(y.storeDimIndex=o.ensureCalculationDimension(c,f),v.storeDimIndex=o.ensureCalculationDimension(h,f)),r.appendCalculationDimension(y),r.appendCalculationDimension(v)):(i.push(y),i.push(v))}return{stackedDimension:u&&u.name,stackedByDimension:l&&l.name,isStackedByIndex:a,stackedOverDimension:c,stackResultDimension:h}}function lx(t,e){return!!e&&e===t.getCalculationInfo("stackedDimension")}function ux(t,e){return lx(t,e)?t.getCalculationInfo("stackResultDimension"):e}function hx(t,e,n){n=n||{};var i,r=e.getSourceManager(),o=!1;t?(o=!0,i=Kd(t)):o=(i=r.getSource()).sourceFormat===Vp;var a=function(t){var e=t.get("coordinateSystem"),n=new rx(e),i=ox[e];if(i)return i(t,n,n.axisMap,n.categoryAxisMap),n}(e),s=function(t,e){var n,i=t.get("coordinateSystem"),r=vd.get(i);return e&&e.coordSysDims&&(n=z(e.coordSysDims,(function(t){var n={name:t},i=e.axisMap.get(t);if(i){var r=i.get("type");n.type=Rm(r)}return n}))),n||(n=r&&(r.getDimensionsInfo?r.getDimensionsInfo():r.dimensions.slice())||["x","y"]),n}(e,a),l=n.useEncodeDefaulter,u=U(l)?l:l?H(Kp,s,e):null,h=nx(i,{coordDimensions:s,generateCoord:n.generateCoord,encodeDefine:e.getEncode(),encodeDefaulter:u,canOmitUnusedDimensions:!o}),c=function(t,e,n){var i,r;return n&&E(t,(function(t,o){var a=t.coordDim,s=n.categoryAxisMap.get(a);s&&(null==i&&(i=o),t.ordinalMeta=s.getOrdinalMeta(),e&&(t.createInvertedIndices=!0)),null!=t.otherDims.itemName&&(r=!0)})),r||null==i||(t[i].otherDims.itemName=0),i}(h.dimensions,n.createInvertedIndices,a),p=o?null:r.getSharedDataStore(h),d=sx(e,{schema:h,store:p}),f=new ex(h,e);f.setCalculationInfo(d);var g=null!=c&&function(t){if(t.sourceFormat===Vp){var e=function(t){var e=0;for(;ee[1]&&(e[1]=t[1])},t.prototype.unionExtentFromData=function(t,e){this.unionExtent(t.getApproximateExtent(e))},t.prototype.getExtent=function(){return this._extent.slice()},t.prototype.setExtent=function(t,e){var n=this._extent;isNaN(t)||(n[0]=t),isNaN(e)||(n[1]=e)},t.prototype.isInExtentRange=function(t){return this._extent[0]<=t&&this._extent[1]>=t},t.prototype.isBlank=function(){return this._isBlank},t.prototype.setBlank=function(t){this._isBlank=t},t}();Ko(cx);var px=0,dx=function(){function t(t){this.categories=t.categories||[],this._needCollect=t.needCollect,this._deduplication=t.deduplication,this.uid=++px}return t.createByAxisModel=function(e){var n=e.option,i=n.data,r=i&&z(i,fx);return new t({categories:r,needCollect:!r,deduplication:!1!==n.dedplication})},t.prototype.getOrdinal=function(t){return this._getOrCreateMap().get(t)},t.prototype.parseAndCollect=function(t){var e,n=this._needCollect;if(!X(t)&&!n)return t;if(n&&!this._deduplication)return e=this.categories.length,this.categories[e]=t,e;var i=this._getOrCreateMap();return null==(e=i.get(t))&&(n?(e=this.categories.length,this.categories[e]=t,i.set(t,e)):e=NaN),e},t.prototype._getOrCreateMap=function(){return this._map||(this._map=yt(this.categories))},t}();function fx(t){return q(t)&&null!=t.value?t.value:t+""}function gx(t){return"interval"===t.type||"log"===t.type}function yx(t,e,n,i){var r={},o=t[1]-t[0],a=r.interval=ao(o/e,!0);null!=n&&ai&&(a=r.interval=i);var s=r.intervalPrecision=mx(a);return function(t,e){!isFinite(t[0])&&(t[0]=e[0]),!isFinite(t[1])&&(t[1]=e[1]),xx(t,0,e),xx(t,1,e),t[0]>t[1]&&(t[0]=t[1])}(r.niceTickExtent=[Xr(Math.ceil(t[0]/a)*a,s),Xr(Math.floor(t[1]/a)*a,s)],t),r}function vx(t){var e=Math.pow(10,oo(t)),n=t/e;return n?2===n?n=3:3===n?n=5:n*=2:n=1,Xr(n*e)}function mx(t){return jr(t)+2}function xx(t,e,n){t[e]=Math.max(Math.min(t[e],n[1]),n[0])}function _x(t,e){return t>=e[0]&&t<=e[1]}function bx(t,e){return e[1]===e[0]?.5:(t-e[0])/(e[1]-e[0])}function Sx(t,e){return t*(e[1]-e[0])+e[0]}var Mx=function(t){function e(e){var n=t.call(this,e)||this;n.type="ordinal";var i=n.getSetting("ordinalMeta");return i||(i=new dx({})),Y(i)&&(i=new dx({categories:z(i,(function(t){return q(t)?t.value:t}))})),n._ordinalMeta=i,n._extent=n.getSetting("extent")||[0,i.categories.length-1],n}return n(e,t),e.prototype.parse=function(t){return null==t?NaN:X(t)?this._ordinalMeta.getOrdinal(t):Math.round(t)},e.prototype.contain=function(t){return _x(t=this.parse(t),this._extent)&&null!=this._ordinalMeta.categories[t]},e.prototype.normalize=function(t){return bx(t=this._getTickNumber(this.parse(t)),this._extent)},e.prototype.scale=function(t){return t=Math.round(Sx(t,this._extent)),this.getRawOrdinalNumber(t)},e.prototype.getTicks=function(){for(var t=[],e=this._extent,n=e[0];n<=e[1];)t.push({value:n}),n++;return t},e.prototype.getMinorTicks=function(t){},e.prototype.setSortInfo=function(t){if(null!=t){for(var e=t.ordinalNumbers,n=this._ordinalNumbersByTick=[],i=this._ticksByOrdinalNumber=[],r=0,o=this._ordinalMeta.categories.length,a=Math.min(o,e.length);r=0&&t=0&&t=t},e.prototype.getOrdinalMeta=function(){return this._ordinalMeta},e.prototype.calcNiceTicks=function(){},e.prototype.calcNiceExtent=function(){},e.type="ordinal",e}(cx);cx.registerClass(Mx);var Ix=Xr,Tx=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.type="interval",e._interval=0,e._intervalPrecision=2,e}return n(e,t),e.prototype.parse=function(t){return t},e.prototype.contain=function(t){return _x(t,this._extent)},e.prototype.normalize=function(t){return bx(t,this._extent)},e.prototype.scale=function(t){return Sx(t,this._extent)},e.prototype.setExtent=function(t,e){var n=this._extent;isNaN(t)||(n[0]=parseFloat(t)),isNaN(e)||(n[1]=parseFloat(e))},e.prototype.unionExtent=function(t){var e=this._extent;t[0]e[1]&&(e[1]=t[1]),this.setExtent(e[0],e[1])},e.prototype.getInterval=function(){return this._interval},e.prototype.setInterval=function(t){this._interval=t,this._niceExtent=this._extent.slice(),this._intervalPrecision=mx(t)},e.prototype.getTicks=function(t){var e=this._interval,n=this._extent,i=this._niceExtent,r=this._intervalPrecision,o=[];if(!e)return o;n[0]1e4)return[];var s=o.length?o[o.length-1].value:i[1];return n[1]>s&&(t?o.push({value:Ix(s+e,r)}):o.push({value:n[1]})),o},e.prototype.getMinorTicks=function(t){for(var e=this.getTicks(!0),n=[],i=this.getExtent(),r=1;ri[0]&&h0&&(o=null===o?s:Math.min(o,s))}n[i]=o}}return n}(t),n=[];return E(t,(function(t){var i,r=t.coordinateSystem.getBaseAxis(),o=r.getExtent();if("category"===r.type)i=r.getBandWidth();else if("value"===r.type||"time"===r.type){var a=r.dim+"_"+r.index,s=e[a],l=Math.abs(o[1]-o[0]),u=r.scale.getExtent(),h=Math.abs(u[1]-u[0]);i=s?l/h*s:l}else{var c=t.getData();i=Math.abs(o[1]-o[0])/c.count()}var p=Ur(t.get("barWidth"),i),d=Ur(t.get("barMaxWidth"),i),f=Ur(t.get("barMinWidth")||(Bx(t)?.5:1),i),g=t.get("barGap"),y=t.get("barCategoryGap");n.push({bandWidth:i,barWidth:p,barMaxWidth:d,barMinWidth:f,barGap:g,barCategoryGap:y,axisKey:Px(r),stackId:Lx(t)})})),Nx(n)}function Nx(t){var e={};E(t,(function(t,n){var i=t.axisKey,r=t.bandWidth,o=e[i]||{bandWidth:r,remainedWidth:r,autoWidthCount:0,categoryGap:null,gap:"20%",stacks:{}},a=o.stacks;e[i]=o;var s=t.stackId;a[s]||o.autoWidthCount++,a[s]=a[s]||{width:0,maxWidth:0};var l=t.barWidth;l&&!a[s].width&&(a[s].width=l,l=Math.min(o.remainedWidth,l),o.remainedWidth-=l);var u=t.barMaxWidth;u&&(a[s].maxWidth=u);var h=t.barMinWidth;h&&(a[s].minWidth=h);var c=t.barGap;null!=c&&(o.gap=c);var p=t.barCategoryGap;null!=p&&(o.categoryGap=p)}));var n={};return E(e,(function(t,e){n[e]={};var i=t.stacks,r=t.bandWidth,o=t.categoryGap;if(null==o){var a=G(i).length;o=Math.max(35-4*a,15)+"%"}var s=Ur(o,r),l=Ur(t.gap,1),u=t.remainedWidth,h=t.autoWidthCount,c=(u-s)/(h+(h-1)*l);c=Math.max(c,0),E(i,(function(t){var e=t.maxWidth,n=t.minWidth;if(t.width){i=t.width;e&&(i=Math.min(i,e)),n&&(i=Math.max(i,n)),t.width=i,u-=i+l*i,h--}else{var i=c;e&&ei&&(i=n),i!==c&&(t.width=i,u-=i+l*i,h--)}})),c=(u-s)/(h+(h-1)*l),c=Math.max(c,0);var p,d=0;E(i,(function(t,e){t.width||(t.width=c),p=t,d+=t.width*(1+l)})),p&&(d-=p.width*l);var f=-d/2;E(i,(function(t,i){n[e][i]=n[e][i]||{bandWidth:r,offset:f,width:t.width},f+=t.width*(1+l)}))})),n}function Ex(t,e){var n=Ox(t,e),i=Rx(n);E(n,(function(t){var e=t.getData(),n=t.coordinateSystem.getBaseAxis(),r=Lx(t),o=i[Px(n)][r],a=o.offset,s=o.width;e.setLayout({bandWidth:o.bandWidth,offset:a,size:s})}))}function zx(t){return{seriesType:t,plan:Sg(),reset:function(t){if(Vx(t)){var e=t.getData(),n=t.coordinateSystem,i=n.getBaseAxis(),r=n.getOtherAxis(i),o=e.getDimensionIndex(e.mapDimension(r.dim)),a=e.getDimensionIndex(e.mapDimension(i.dim)),s=t.get("showBackground",!0),l=e.mapDimension(r.dim),u=e.getCalculationInfo("stackResultDimension"),h=lx(e,l)&&!!e.getCalculationInfo("stackedOnSeries"),c=r.isHorizontal(),p=function(t,e){return e.toGlobalCoord(e.dataToCoord("log"===e.type?1:0))}(0,r),d=Bx(t),f=t.get("barMinHeight")||0,g=u&&e.getDimensionIndex(u),y=e.getLayout("size"),v=e.getLayout("offset");return{progress:function(t,e){for(var i,r=t.count,l=d&&Ax(3*r),u=d&&s&&Ax(3*r),m=d&&Ax(r),x=n.master.getRect(),_=c?x.width:x.height,b=e.getStore(),w=0;null!=(i=t.next());){var S=b.get(h?g:o,i),M=b.get(a,i),I=p,T=void 0;h&&(T=+S-b.get(o,i));var C=void 0,D=void 0,A=void 0,k=void 0;if(c){var L=n.dataToPoint([S,M]);if(h)I=n.dataToPoint([T,M])[0];C=I,D=L[1]+v,A=L[0]-I,k=y,Math.abs(A)0)for(var s=0;s=0;--s)if(l[u]){o=l[u];break}o=o||a.none}if(Y(o)){var h=null==t.level?0:t.level>=0?t.level:o.length+t.level;o=o[h=Math.min(h,o.length-1)]}}return jc(new Date(t.value),o,r,i)}(t,e,n,this.getSetting("locale"),i)},e.prototype.getTicks=function(){var t=this._interval,e=this._extent,n=[];if(!t)return n;n.push({value:e[0],level:0});var i=this.getSetting("useUTC"),r=function(t,e,n,i){var r=1e4,o=Yc,a=0;function s(t,e,n,r,o,a,s){for(var l=new Date(e),u=e,h=l[r]();u1&&0===u&&o.unshift({value:o[0].value-p})}}for(u=0;u=i[0]&&v<=i[1]&&c++)}var m=(i[1]-i[0])/e;if(c>1.5*m&&p>m/1.5)break;if(u.push(g),c>m||t===o[d])break}h=[]}}0;var x=B(z(u,(function(t){return B(t,(function(t){return t.value>=i[0]&&t.value<=i[1]&&!t.notAdd}))})),(function(t){return t.length>0})),_=[],b=x.length-1;for(d=0;dn&&(this._approxInterval=n);var o=Gx.length,a=Math.min(function(t,e,n,i){for(;n>>1;t[r][1]16?16:t>7.5?7:t>3.5?4:t>1.5?2:1}function Hx(t){return(t/=2592e6)>6?6:t>3?3:t>2?2:1}function Yx(t){return(t/=zc)>12?12:t>6?6:t>3.5?4:t>2?2:1}function Ux(t,e){return(t/=e?Ec:Nc)>30?30:t>20?20:t>15?15:t>10?10:t>5?5:t>2?2:1}function Xx(t){return ao(t,!0)}function Zx(t,e,n){var i=new Date(t);switch(Xc(e)){case"year":case"month":i[op(n)](0);case"day":i[ap(n)](1);case"hour":i[sp(n)](0);case"minute":i[lp(n)](0);case"second":i[up(n)](0),i[hp(n)](0)}return i.getTime()}cx.registerClass(Fx);var jx=cx.prototype,qx=Tx.prototype,Kx=Xr,$x=Math.floor,Jx=Math.ceil,Qx=Math.pow,t_=Math.log,e_=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.type="log",e.base=10,e._originalScale=new Tx,e._interval=0,e}return n(e,t),e.prototype.getTicks=function(t){var e=this._originalScale,n=this._extent,i=e.getExtent();return z(qx.getTicks.call(this,t),(function(t){var e=t.value,r=Xr(Qx(this.base,e));return r=e===n[0]&&this._fixMin?i_(r,i[0]):r,{value:r=e===n[1]&&this._fixMax?i_(r,i[1]):r}}),this)},e.prototype.setExtent=function(t,e){var n=t_(this.base);t=t_(Math.max(0,t))/n,e=t_(Math.max(0,e))/n,qx.setExtent.call(this,t,e)},e.prototype.getExtent=function(){var t=this.base,e=jx.getExtent.call(this);e[0]=Qx(t,e[0]),e[1]=Qx(t,e[1]);var n=this._originalScale.getExtent();return this._fixMin&&(e[0]=i_(e[0],n[0])),this._fixMax&&(e[1]=i_(e[1],n[1])),e},e.prototype.unionExtent=function(t){this._originalScale.unionExtent(t);var e=this.base;t[0]=t_(t[0])/t_(e),t[1]=t_(t[1])/t_(e),jx.unionExtent.call(this,t)},e.prototype.unionExtentFromData=function(t,e){this.unionExtent(t.getApproximateExtent(e))},e.prototype.calcNiceTicks=function(t){t=t||10;var e=this._extent,n=e[1]-e[0];if(!(n===1/0||n<=0)){var i=ro(n);for(t/n*i<=.5&&(i*=10);!isNaN(i)&&Math.abs(i)<1&&Math.abs(i)>0;)i*=10;var r=[Xr(Jx(e[0]/i)*i),Xr($x(e[1]/i)*i)];this._interval=i,this._niceExtent=r}},e.prototype.calcNiceExtent=function(t){qx.calcNiceExtent.call(this,t),this._fixMin=t.fixMin,this._fixMax=t.fixMax},e.prototype.parse=function(t){return t},e.prototype.contain=function(t){return _x(t=t_(t)/t_(this.base),this._extent)},e.prototype.normalize=function(t){return bx(t=t_(t)/t_(this.base),this._extent)},e.prototype.scale=function(t){return t=Sx(t,this._extent),Qx(this.base,t)},e.type="log",e}(cx),n_=e_.prototype;function i_(t,e){return Kx(t,jr(e))}n_.getMinorTicks=qx.getMinorTicks,n_.getLabel=qx.getLabel,cx.registerClass(e_);var r_=function(){function t(t,e,n){this._prepareParams(t,e,n)}return t.prototype._prepareParams=function(t,e,n){n[1]0&&s>0&&!l&&(a=0),a<0&&s<0&&!u&&(s=0));var c=this._determinedMin,p=this._determinedMax;return null!=c&&(a=c,l=!0),null!=p&&(s=p,u=!0),{min:a,max:s,minFixed:l,maxFixed:u,isBlank:h}},t.prototype.modifyDataMinMax=function(t,e){this[a_[t]]=e},t.prototype.setDeterminedMinMax=function(t,e){var n=o_[t];this[n]=e},t.prototype.freeze=function(){this.frozen=!0},t}(),o_={min:"_determinedMin",max:"_determinedMax"},a_={min:"_dataMin",max:"_dataMax"};function s_(t,e,n){var i=t.rawExtentInfo;return i||(i=new r_(t,e,n),t.rawExtentInfo=i,i)}function l_(t,e){return null==e?null:nt(e)?NaN:t.parse(e)}function u_(t,e){var n=t.type,i=s_(t,e,t.getExtent()).calculate();t.setBlank(i.isBlank);var r=i.min,o=i.max,a=e.ecModel;if(a&&"time"===n){var s=Ox("bar",a),l=!1;if(E(s,(function(t){l=l||t.getBaseAxis()===e.axis})),l){var u=Rx(s),h=function(t,e,n,i){var r=n.axis.getExtent(),o=r[1]-r[0],a=function(t,e,n){if(t&&e){var i=t[Px(e)];return null!=i&&null!=n?i[Lx(n)]:i}}(i,n.axis);if(void 0===a)return{min:t,max:e};var s=1/0;E(a,(function(t){s=Math.min(t.offset,s)}));var l=-1/0;E(a,(function(t){l=Math.max(t.offset+t.width,l)})),s=Math.abs(s),l=Math.abs(l);var u=s+l,h=e-t,c=h/(1-(s+l)/o)-h;return{min:t-=c*(s/u),max:e+=c*(l/u)}}(r,o,e,u);r=h.min,o=h.max}}return{extent:[r,o],fixMin:i.minFixed,fixMax:i.maxFixed}}function h_(t,e){var n=e,i=u_(t,n),r=i.extent,o=n.get("splitNumber");t instanceof e_&&(t.base=n.get("logBase"));var a=t.type,s=n.get("interval"),l="interval"===a||"time"===a;t.setExtent(r[0],r[1]),t.calcNiceExtent({splitNumber:o,fixMin:i.fixMin,fixMax:i.fixMax,minInterval:l?n.get("minInterval"):null,maxInterval:l?n.get("maxInterval"):null}),null!=s&&t.setInterval&&t.setInterval(s)}function c_(t,e){if(e=e||t.get("type"))switch(e){case"category":return new Mx({ordinalMeta:t.getOrdinalMeta?t.getOrdinalMeta():t.getCategories(),extent:[1/0,-1/0]});case"time":return new Fx({locale:t.ecModel.getLocaleModel(),useUTC:t.ecModel.get("useUTC")});default:return new(cx.getClass(e)||Tx)}}function p_(t){var e,n,i=t.getLabelModel().get("formatter"),r="category"===t.type?t.scale.getExtent()[0]:null;return"time"===t.scale.type?(n=i,function(e,i){return t.scale.getFormattedLabel(e,i,n)}):X(i)?function(e){return function(n){var i=t.scale.getLabel(n);return e.replace("{value}",null!=i?i:"")}}(i):U(i)?(e=i,function(n,i){return null!=r&&(i=n.value-r),e(d_(t,n),i,null!=n.level?{level:n.level}:null)}):function(e){return t.scale.getLabel(e)}}function d_(t,e){return"category"===t.type?t.scale.getLabel(e):e.value}function f_(t,e){var n=e*Math.PI/180,i=t.width,r=t.height,o=i*Math.abs(Math.cos(n))+Math.abs(r*Math.sin(n)),a=i*Math.abs(Math.sin(n))+Math.abs(r*Math.cos(n));return new Ee(t.x,t.y,o,a)}function g_(t){var e=t.get("interval");return null==e?"auto":e}function y_(t){return"category"===t.type&&0===g_(t.getLabelModel())}function v_(t,e){var n={};return E(t.mapDimensionsAll(e),(function(e){n[ux(t,e)]=!0})),G(n)}var m_=function(){function t(){}return t.prototype.getNeedCrossZero=function(){return!this.option.scale},t.prototype.getCoordSysModel=function(){},t}();var x_={isDimensionStacked:lx,enableDataStack:sx,getStackedDimension:ux};var __=Object.freeze({__proto__:null,createList:function(t){return hx(null,t)},getLayoutRect:Tp,dataStack:x_,createScale:function(t,e){var n=e;e instanceof Sc||(n=new Sc(e));var i=c_(n);return i.setExtent(t[0],t[1]),h_(i,n),i},mixinAxisModelCommonMethods:function(t){R(t,m_)},getECData:Js,createTextStyle:function(t,e){return ec(t,null,null,"normal"!==(e=e||{}).state)},createDimensions:function(t,e){return nx(t,e).dimensions},createSymbol:Vy,enableHoverEmphasis:Wl});function b_(t,e){return Math.abs(t-e)<1e-8}function w_(t,e,n){var i=0,r=t[0];if(!r)return!1;for(var o=1;on&&(t=r,n=a)}if(t)return function(t){for(var e=0,n=0,i=0,r=t.length,o=t[r-1][0],a=t[r-1][1],s=0;s>1^-(1&s),l=l>>1^-(1&l),r=s+=r,o=l+=o,i.push([s/n,l/n])}return i}function O_(t,e){return z(B((t=function(t){if(!t.UTF8Encoding)return t;var e=t,n=e.UTF8Scale;return null==n&&(n=1024),E(e.features,(function(t){var e=t.geometry,i=e.encodeOffsets,r=e.coordinates;if(i)switch(e.type){case"LineString":e.coordinates=P_(r,i,n);break;case"Polygon":case"MultiLineString":L_(r,i,n);break;case"MultiPolygon":E(r,(function(t,e){return L_(t,i[e],n)}))}})),e.UTF8Encoding=!1,e}(t)).features,(function(t){return t.geometry&&t.properties&&t.geometry.coordinates.length>0})),(function(t){var n=t.properties,i=t.geometry,r=[];switch(i.type){case"Polygon":var o=i.coordinates;r.push(new C_(o[0],o.slice(1)));break;case"MultiPolygon":E(i.coordinates,(function(t){t[0]&&r.push(new C_(t[0],t.slice(1)))}));break;case"LineString":r.push(new D_([i.coordinates]));break;case"MultiLineString":r.push(new D_(i.coordinates))}var a=new A_(n[e||"name"],r,n.cp);return a.properties=n,a}))}var R_=Object.freeze({__proto__:null,linearMap:Yr,round:Xr,asc:Zr,getPrecision:jr,getPrecisionSafe:qr,getPixelPrecision:Kr,getPercentWithPrecision:function(t,e,n){return t[e]&&$r(t,n)[e]||0},MAX_SAFE_INTEGER:Qr,remRadian:to,isRadianAroundZero:eo,parseDate:io,quantity:ro,quantityExponent:oo,nice:ao,quantile:so,reformIntervals:lo,isNumeric:ho,numericToNumber:uo}),N_=Object.freeze({__proto__:null,parse:io,format:jc}),E_=Object.freeze({__proto__:null,extendShape:Sh,extendPath:Ih,makePath:Dh,makeImage:Ah,mergePath:Lh,resizePath:Ph,createIcon:Wh,updateProps:dh,initProps:fh,getTransform:Nh,clipPointsByRect:Fh,clipRectByRect:Gh,registerShape:Th,getShapeClass:Ch,Group:Er,Image:As,Text:Bs,Circle:xu,Ellipse:bu,Sector:Eu,Ring:Vu,Polygon:Gu,Polyline:Hu,Rect:Es,Line:Xu,BezierCurve:Ku,Arc:Ju,IncrementalDisplayable:uh,CompoundPath:Qu,LinearGradient:eh,RadialGradient:nh,BoundingRect:Ee}),z_=Object.freeze({__proto__:null,addCommas:cp,toCamelCase:pp,normalizeCssArray:dp,encodeHTML:ie,formatTpl:vp,getTooltipMarker:mp,formatTime:function(t,e,n){"week"!==t&&"month"!==t&&"quarter"!==t&&"half-year"!==t&&"year"!==t||(t="MM-dd\nyyyy");var i=io(e),r=n?"getUTC":"get",o=i[r+"FullYear"](),a=i[r+"Month"]()+1,s=i[r+"Date"](),l=i[r+"Hours"](),u=i[r+"Minutes"](),h=i[r+"Seconds"](),c=i[r+"Milliseconds"]();return t=t.replace("MM",Uc(a,2)).replace("M",a).replace("yyyy",o).replace("yy",Uc(o%100+"",2)).replace("dd",Uc(s,2)).replace("d",s).replace("hh",Uc(l,2)).replace("h",l).replace("mm",Uc(u,2)).replace("m",u).replace("ss",Uc(h,2)).replace("s",h).replace("SSS",Uc(c,3))},capitalFirst:function(t){return t?t.charAt(0).toUpperCase()+t.substr(1):t},truncateText:aa,getTextRect:function(t,e,n,i,r,o,a,s){return new Bs({style:{text:t,font:e,align:n,verticalAlign:i,padding:r,rich:o,overflow:a?"truncate":null,lineHeight:s}}).getBoundingRect()}}),V_=Object.freeze({__proto__:null,map:z,each:E,indexOf:P,inherits:O,reduce:V,filter:B,bind:W,curry:H,isArray:Y,isString:X,isObject:q,isFunction:U,extend:A,defaults:k,clone:T,merge:C}),B_=Po();function F_(t){return"category"===t.type?function(t){var e=t.getLabelModel(),n=W_(t,e);return!e.get("show")||t.scale.isBlank()?{labels:[],labelCategoryInterval:n.labelCategoryInterval}:n}(t):function(t){var e=t.scale.getTicks(),n=p_(t);return{labels:z(e,(function(e,i){return{level:e.level,formattedLabel:n(e,i),rawLabel:t.scale.getLabel(e),tickValue:e.value}}))}}(t)}function G_(t,e){return"category"===t.type?function(t,e){var n,i,r=H_(t,"ticks"),o=g_(e),a=Y_(r,o);if(a)return a;e.get("show")&&!t.scale.isBlank()||(n=[]);if(U(o))n=Z_(t,o,!0);else if("auto"===o){var s=W_(t,t.getLabelModel());i=s.labelCategoryInterval,n=z(s.labels,(function(t){return t.tickValue}))}else n=X_(t,i=o,!0);return U_(r,o,{ticks:n,tickCategoryInterval:i})}(t,e):{ticks:z(t.scale.getTicks(),(function(t){return t.value}))}}function W_(t,e){var n,i,r=H_(t,"labels"),o=g_(e),a=Y_(r,o);return a||(U(o)?n=Z_(t,o):(i="auto"===o?function(t){var e=B_(t).autoInterval;return null!=e?e:B_(t).autoInterval=t.calculateCategoryInterval()}(t):o,n=X_(t,i)),U_(r,o,{labels:n,labelCategoryInterval:i}))}function H_(t,e){return B_(t)[e]||(B_(t)[e]=[])}function Y_(t,e){for(var n=0;n1&&h/l>2&&(u=Math.round(Math.ceil(u/l)*l));var c=y_(t),p=a.get("showMinLabel")||c,d=a.get("showMaxLabel")||c;p&&u!==o[0]&&g(o[0]);for(var f=u;f<=o[1];f+=l)g(f);function g(t){var e={value:t};s.push(n?t:{formattedLabel:i(e),rawLabel:r.getLabel(e),tickValue:t})}return d&&f-l!==o[1]&&g(o[1]),s}function Z_(t,e,n){var i=t.scale,r=p_(t),o=[];return E(i.getTicks(),(function(t){var a=i.getLabel(t),s=t.value;e(t.value,a)&&o.push(n?s:{formattedLabel:r(t),rawLabel:a,tickValue:s})})),o}var j_=[0,1],q_=function(){function t(t,e,n){this.onBand=!1,this.inverse=!1,this.dim=t,this.scale=e,this._extent=n||[0,0]}return t.prototype.contain=function(t){var e=this._extent,n=Math.min(e[0],e[1]),i=Math.max(e[0],e[1]);return t>=n&&t<=i},t.prototype.containData=function(t){return this.scale.contain(t)},t.prototype.getExtent=function(){return this._extent.slice()},t.prototype.getPixelPrecision=function(t){return Kr(t||this.scale.getExtent(),this._extent)},t.prototype.setExtent=function(t,e){var n=this._extent;n[0]=t,n[1]=e},t.prototype.dataToCoord=function(t,e){var n=this._extent,i=this.scale;return t=i.normalize(t),this.onBand&&"ordinal"===i.type&&K_(n=n.slice(),i.count()),Yr(t,j_,n,e)},t.prototype.coordToData=function(t,e){var n=this._extent,i=this.scale;this.onBand&&"ordinal"===i.type&&K_(n=n.slice(),i.count());var r=Yr(t,n,j_,e);return this.scale.scale(r)},t.prototype.pointToData=function(t,e){},t.prototype.getTicksCoords=function(t){var e=(t=t||{}).tickModel||this.getTickModel(),n=z(G_(this,e).ticks,(function(t){return{coord:this.dataToCoord("ordinal"===this.scale.type?this.scale.getRawOrdinalNumber(t):t),tickValue:t}}),this);return function(t,e,n,i){var r=e.length;if(!t.onBand||n||!r)return;var o,a,s=t.getExtent();if(1===r)e[0].coord=s[0],o=e[1]={coord:s[0]};else{var l=e[r-1].tickValue-e[0].tickValue,u=(e[r-1].coord-e[0].coord)/l;E(e,(function(t){t.coord-=u/2})),a=1+t.scale.getExtent()[1]-e[r-1].tickValue,o={coord:e[r-1].coord+u*a},e.push(o)}var h=s[0]>s[1];c(e[0].coord,s[0])&&(i?e[0].coord=s[0]:e.shift());i&&c(s[0],e[0].coord)&&e.unshift({coord:s[0]});c(s[1],o.coord)&&(i?o.coord=s[1]:e.pop());i&&c(o.coord,s[1])&&e.push({coord:s[1]});function c(t,e){return t=Xr(t),e=Xr(e),h?t>e:t0&&t<100||(t=5),z(this.scale.getMinorTicks(t),(function(t){return z(t,(function(t){return{coord:this.dataToCoord(t),tickValue:t}}),this)}),this)},t.prototype.getViewLabels=function(){return F_(this).labels},t.prototype.getLabelModel=function(){return this.model.getModel("axisLabel")},t.prototype.getTickModel=function(){return this.model.getModel("axisTick")},t.prototype.getBandWidth=function(){var t=this._extent,e=this.scale.getExtent(),n=e[1]-e[0]+(this.onBand?1:0);0===n&&(n=1);var i=Math.abs(t[1]-t[0]);return Math.abs(i)/n},t.prototype.calculateCategoryInterval=function(){return function(t){var e=function(t){var e=t.getLabelModel();return{axisRotate:t.getRotate?t.getRotate():t.isHorizontal&&!t.isHorizontal()?90:0,labelRotate:e.get("rotate")||0,font:e.getFont()}}(t),n=p_(t),i=(e.axisRotate-e.labelRotate)/180*Math.PI,r=t.scale,o=r.getExtent(),a=r.count();if(o[1]-o[0]<1)return 0;var s=1;a>40&&(s=Math.max(1,Math.floor(a/40)));for(var l=o[0],u=t.dataToCoord(l+1)-t.dataToCoord(l),h=Math.abs(u*Math.cos(i)),c=Math.abs(u*Math.sin(i)),p=0,d=0;l<=o[1];l+=s){var f,g,y=_r(n({value:l}),e.font,"center","top");f=1.3*y.width,g=1.3*y.height,p=Math.max(p,f,7),d=Math.max(d,g,7)}var v=p/h,m=d/c;isNaN(v)&&(v=1/0),isNaN(m)&&(m=1/0);var x=Math.max(0,Math.floor(Math.min(v,m))),_=B_(t.model),b=t.getExtent(),w=_.lastAutoInterval,S=_.lastTickCount;return null!=w&&null!=S&&Math.abs(w-x)<=1&&Math.abs(S-a)<=1&&w>x&&_.axisExtent0===b[0]&&_.axisExtent1===b[1]?x=w:(_.lastTickCount=a,_.lastAutoInterval=x,_.axisExtent0=b[0],_.axisExtent1=b[1]),x}(this)},t}();function K_(t,e){var n=(t[1]-t[0])/e/2;t[0]+=n,t[1]-=n}var $_=2*Math.PI,J_=rs.CMD,Q_=["top","right","bottom","left"];function tb(t,e,n,i,r){var o=n.width,a=n.height;switch(t){case"top":i.set(n.x+o/2,n.y-e),r.set(0,-1);break;case"bottom":i.set(n.x+o/2,n.y+a+e),r.set(0,1);break;case"left":i.set(n.x-e,n.y+a/2),r.set(-1,0);break;case"right":i.set(n.x+o+e,n.y+a/2),r.set(1,0)}}function eb(t,e,n,i,r,o,a,s,l){a-=t,s-=e;var u=Math.sqrt(a*a+s*s),h=(a/=u)*n+t,c=(s/=u)*n+e;if(Math.abs(i-r)%$_<1e-4)return l[0]=h,l[1]=c,u-n;if(o){var p=i;i=us(r),r=us(p)}else i=us(i),r=us(r);i>r&&(r+=$_);var d=Math.atan2(s,a);if(d<0&&(d+=$_),d>=i&&d<=r||d+$_>=i&&d+$_<=r)return l[0]=h,l[1]=c,u-n;var f=n*Math.cos(i)+t,g=n*Math.sin(i)+e,y=n*Math.cos(r)+t,v=n*Math.sin(r)+e,m=(f-a)*(f-a)+(g-s)*(g-s),x=(y-a)*(y-a)+(v-s)*(v-s);return m0){e=e/180*Math.PI,sb.fromArray(t[0]),lb.fromArray(t[1]),ub.fromArray(t[2]),Ce.sub(hb,sb,lb),Ce.sub(cb,ub,lb);var n=hb.len(),i=cb.len();if(!(n<.001||i<.001)){hb.scale(1/n),cb.scale(1/i);var r=hb.dot(cb);if(Math.cos(e)1&&Ce.copy(fb,ub),fb.toArray(t[1])}}}}function yb(t,e,n){if(n<=180&&n>0){n=n/180*Math.PI,sb.fromArray(t[0]),lb.fromArray(t[1]),ub.fromArray(t[2]),Ce.sub(hb,lb,sb),Ce.sub(cb,ub,lb);var i=hb.len(),r=cb.len();if(!(i<.001||r<.001))if(hb.scale(1/i),cb.scale(1/r),hb.dot(e)=a)Ce.copy(fb,ub);else{fb.scaleAndAdd(cb,o/Math.tan(Math.PI/2-s));var l=ub.x!==lb.x?(fb.x-lb.x)/(ub.x-lb.x):(fb.y-lb.y)/(ub.y-lb.y);if(isNaN(l))return;l<0?Ce.copy(fb,lb):l>1&&Ce.copy(fb,ub)}fb.toArray(t[1])}}}function vb(t,e,n,i){var r="normal"===n,o=r?t:t.ensureState(n);o.ignore=e;var a=i.get("smooth");a&&!0===a&&(a=.3),o.shape=o.shape||{},a>0&&(o.shape.smooth=a);var s=i.getModel("lineStyle").getLineStyle();r?t.useStyle(s):o.style=s}function mb(t,e){var n=e.smooth,i=e.points;if(i)if(t.moveTo(i[0][0],i[0][1]),n>0&&i.length>=3){var r=Vt(i[0],i[1]),o=Vt(i[1],i[2]);if(!r||!o)return t.lineTo(i[1][0],i[1][1]),void t.lineTo(i[2][0],i[2][1]);var a=Math.min(r,o)*n,s=Gt([],i[1],i[0],a/r),l=Gt([],i[1],i[2],a/o),u=Gt([],s,l,.5);t.bezierCurveTo(s[0],s[1],s[0],s[1],u[0],u[1]),t.bezierCurveTo(l[0],l[1],l[0],l[1],i[2][0],i[2][1])}else for(var h=1;h0&&o&&_(-h/a,0,a);var f,g,y=t[0],v=t[a-1];return m(),f<0&&b(-f,.8),g<0&&b(g,.8),m(),x(f,g,1),x(g,f,-1),m(),f<0&&w(-f),g<0&&w(g),u}function m(){f=y.rect[e]-i,g=r-v.rect[e]-v.rect[n]}function x(t,e,n){if(t<0){var i=Math.min(e,-t);if(i>0){_(i*n,0,a);var r=i+t;r<0&&b(-r*n,1)}else b(-t*n,1)}}function _(n,i,r){0!==n&&(u=!0);for(var o=i;o0)for(l=0;l0;l--){_(-(o[l-1]*c),l,a)}}}function w(t){var e=t<0?-1:1;t=Math.abs(t);for(var n=Math.ceil(t/(a-1)),i=0;i0?_(n,0,i+1):_(-n,a-i-1,a),(t-=n)<=0)return}}function Sb(t,e,n,i){return wb(t,"y","height",e,n,i)}function Mb(t){var e=[];t.sort((function(t,e){return e.priority-t.priority}));var n=new Ee(0,0,0,0);function i(t){if(!t.ignore){var e=t.ensureState("emphasis");null==e.ignore&&(e.ignore=!1)}t.ignore=!0}for(var r=0;r=0&&n.attr(d.oldLayoutSelect),P(u,"emphasis")>=0&&n.attr(d.oldLayoutEmphasis)),dh(n,s,e,a)}else if(n.attr(s),!lc(n).valueAnimation){var h=rt(n.style.opacity,1);n.style.opacity=0,fh(n,{style:{opacity:h}},e,a)}if(d.oldLayout=s,n.states.select){var c=d.oldLayoutSelect={};Lb(c,s,Pb),Lb(c,n.states.select,Pb)}if(n.states.emphasis){var p=d.oldLayoutEmphasis={};Lb(p,s,Pb),Lb(p,n.states.emphasis,Pb)}hc(n,a,l,e,e)}if(i&&!i.ignore&&!i.invisible){r=(d=kb(i)).oldLayout;var d,f={points:i.shape.points};r?(i.attr({shape:r}),dh(i,{shape:f},e)):(i.setShape(f),i.style.strokePercent=0,fh(i,{style:{strokePercent:1}},e)),d.oldLayout=f}},t}(),Rb=Po();var Nb=Math.sin,Eb=Math.cos,zb=Math.PI,Vb=2*Math.PI,Bb=180/zb,Fb=function(){function t(){}return t.prototype.reset=function(t){this._start=!0,this._d=[],this._str="",this._p=Math.pow(10,t||4)},t.prototype.moveTo=function(t,e){this._add("M",t,e)},t.prototype.lineTo=function(t,e){this._add("L",t,e)},t.prototype.bezierCurveTo=function(t,e,n,i,r,o){this._add("C",t,e,n,i,r,o)},t.prototype.quadraticCurveTo=function(t,e,n,i){this._add("Q",t,e,n,i)},t.prototype.arc=function(t,e,n,i,r,o){this.ellipse(t,e,n,n,0,i,r,o)},t.prototype.ellipse=function(t,e,n,i,r,o,a,s){var l=a-o,u=!s,h=Math.abs(l),c=ui(h-Vb)||(u?l>=Vb:-l>=Vb),p=l>0?l%Vb:l%Vb+Vb,d=!1;d=!!c||!ui(h)&&p>=zb==!!u;var f=t+n*Eb(o),g=e+i*Nb(o);this._start&&this._add("M",f,g);var y=Math.round(r*Bb);if(c){var v=1/this._p,m=(u?1:-1)*(Vb-v);this._add("A",n,i,y,1,+u,t+n*Eb(o+m),e+i*Nb(o+m)),v>.01&&this._add("A",n,i,y,0,+u,f,g)}else{var x=t+n*Eb(a),_=e+i*Nb(a);this._add("A",n,i,y,+d,+u,x,_)}},t.prototype.rect=function(t,e,n,i){this._add("M",t,e),this._add("l",n,0),this._add("l",0,i),this._add("l",-n,0),this._add("Z")},t.prototype.closePath=function(){this._d.length>0&&this._add("Z")},t.prototype._add=function(t,e,n,i,r,o,a,s,l){for(var u=[],h=this._p,c=1;c"}(r,e.attrs)+ie(e.text)+(i?""+n+z(i,(function(e){return t(e)})).join(n)+n:"")+("")}(t)}function $b(t){return{zrId:t,shadowCache:{},patternCache:{},gradientCache:{},clipPathCache:{},defs:{},cssNodes:{},cssAnims:{},cssClassIdx:0,cssAnimIdx:0,shadowIdx:0,gradientIdx:0,patternIdx:0,clipPathIdx:0}}function Jb(t,e,n,i){return qb("svg","root",{width:t,height:e,xmlns:Xb,"xmlns:xlink":Zb,version:"1.1",baseProfile:"full",viewBox:!!i&&"0 0 "+t+" "+e},n)}var Qb={cubicIn:"0.32,0,0.67,0",cubicOut:"0.33,1,0.68,1",cubicInOut:"0.65,0,0.35,1",quadraticIn:"0.11,0,0.5,0",quadraticOut:"0.5,1,0.89,1",quadraticInOut:"0.45,0,0.55,1",quarticIn:"0.5,0,0.75,0",quarticOut:"0.25,1,0.5,1",quarticInOut:"0.76,0,0.24,1",quinticIn:"0.64,0,0.78,0",quinticOut:"0.22,1,0.36,1",quinticInOut:"0.83,0,0.17,1",sinusoidalIn:"0.12,0,0.39,0",sinusoidalOut:"0.61,1,0.88,1",sinusoidalInOut:"0.37,0,0.63,1",exponentialIn:"0.7,0,0.84,0",exponentialOut:"0.16,1,0.3,1",exponentialInOut:"0.87,0,0.13,1",circularIn:"0.55,0,1,0.45",circularOut:"0,0.55,0.45,1",circularInOut:"0.85,0,0.15,1"},tw="transform-origin";function ew(t,e,n){var i=A({},t.shape);A(i,e),t.buildPath(n,i);var r=new Fb;return r.reset(xi(t)),n.rebuildPath(r,1),r.generateStr(),r.getStr()}function nw(t,e){var n=e.originX,i=e.originY;(n||i)&&(t[tw]=n+"px "+i+"px")}var iw={fill:"fill",opacity:"opacity",lineWidth:"stroke-width",lineDashOffset:"stroke-dashoffset"};function rw(t,e){var n=e.zrId+"-ani-"+e.cssAnimIdx++;return e.cssAnims[n]=t,n}function ow(t){return X(t)?Qb[t]?"cubic-bezier("+Qb[t]+")":Ln(t)?t:"":""}function aw(t,e,n,i){var r=t.animators,o=r.length,a=[];if(t instanceof Qu){var s=function(t,e,n){var i,r,o=t.shape.paths,a={};if(E(o,(function(t){var e=$b(n.zrId);e.animation=!0,aw(t,{},e,!0);var o=e.cssAnims,s=e.cssNodes,l=G(o),u=l.length;if(u){var h=o[r=l[u-1]];for(var c in h){var p=h[c];a[c]=a[c]||{d:""},a[c].d+=p.d||""}for(var d in s){var f=s[d].animation;f.indexOf(r)>=0&&(i=f)}}})),i){e.d=!1;var s=rw(a,n);return i.replace(r,s)}}(t,e,n);if(s)a.push(s);else if(!o)return}else if(!o)return;for(var l={},u=0;u0})).length)return rw(h,n)+" "+r[0]+" both"}for(var y in l){(s=g(l[y]))&&a.push(s)}if(a.length){var v=n.zrId+"-cls-"+n.cssClassIdx++;n.cssNodes["."+v]={animation:a.join(",")},e.class=v}}var sw=Math.round;function lw(t){return t&&X(t.src)}function uw(t){return t&&U(t.toDataURL)}function hw(t,e,n,i){Ub((function(r,o){var a="fill"===r||"stroke"===r;a&&vi(o)?_w(e,t,r,i):a&&fi(o)?bw(n,t,r,i):t[r]=o}),e,n,!1),function(t,e,n){var i=t.style;if(function(t){return t&&(t.shadowBlur||t.shadowOffsetX||t.shadowOffsetY)}(i)){var r=function(t){var e=t.style,n=t.getGlobalScale();return[e.shadowColor,(e.shadowBlur||0).toFixed(2),(e.shadowOffsetX||0).toFixed(2),(e.shadowOffsetY||0).toFixed(2),n[0],n[1]].join(",")}(t),o=n.shadowCache,a=o[r];if(!a){var s=t.getGlobalScale(),l=s[0],u=s[1];if(!l||!u)return;var h=i.shadowOffsetX||0,c=i.shadowOffsetY||0,p=i.shadowBlur,d=si(i.shadowColor),f=d.opacity,g=d.color,y=p/2/l+" "+p/2/u;a=n.zrId+"-s"+n.shadowIdx++,n.defs[a]=qb("filter",a,{id:a,x:"-100%",y:"-100%",width:"300%",height:"300%"},[qb("feDropShadow","",{dx:h/l,dy:c/u,stdDeviation:y,"flood-color":g,"flood-opacity":f})]),o[r]=a}e.filter=mi(a)}}(n,t,i)}function cw(t){return ui(t[0]-1)&&ui(t[1])&&ui(t[2])&&ui(t[3]-1)}function pw(t,e,n){if(e&&(!function(t){return ui(t[4])&&ui(t[5])}(e)||!cw(e))){var i=n?10:1e4;t.transform=cw(e)?"translate("+sw(e[4]*i)/i+" "+sw(e[5]*i)/i+")":function(t){return"matrix("+hi(t[0])+","+hi(t[1])+","+hi(t[2])+","+hi(t[3])+","+ci(t[4])+","+ci(t[5])+")"}(e)}}function dw(t,e,n){for(var i=t.points,r=[],o=0;ol?Ew(t,null==n[c+1]?null:n[c+1].elm,n,s,c):zw(t,e,a,l))}(n,i,r):Pw(r)?(Pw(t.text)&&Aw(n,""),Ew(n,null,r,0,r.length-1)):Pw(i)?zw(n,i,0,i.length-1):Pw(t.text)&&Aw(n,""):t.text!==e.text&&(Pw(i)&&zw(n,i,0,i.length-1),Aw(n,e.text)))}var Fw=0,Gw=function(){function t(t,e,n){if(this.type="svg",this.refreshHover=Ww("refreshHover"),this.configLayer=Ww("configLayer"),this.storage=e,this._opts=n=A({},n),this.root=t,this._id="zr"+Fw++,this._oldVNode=Jb(n.width,n.height),t&&!n.ssr){var i=this._viewport=document.createElement("div");i.style.cssText="position:relative;overflow:hidden";var r=this._svgDom=this._oldVNode.elm=jb("svg");Vw(null,this._oldVNode),i.appendChild(r),t.appendChild(i)}this.resize(n.width,n.height)}return t.prototype.getType=function(){return this.type},t.prototype.getViewportRoot=function(){return this._viewport},t.prototype.getViewportRootOffset=function(){var t=this.getViewportRoot();if(t)return{offsetLeft:t.offsetLeft||0,offsetTop:t.offsetTop||0}},t.prototype.getSvgDom=function(){return this._svgDom},t.prototype.refresh=function(){if(this.root){var t=this.renderToVNode({willUpdate:!0});t.attrs.style="position:absolute;left:0;top:0;user-select:none",function(t,e){if(Rw(t,e))Bw(t,e);else{var n=t.elm,i=Cw(n);Nw(e),null!==i&&(Mw(i,e.elm,Dw(n)),zw(i,[t],0,0))}}(this._oldVNode,t),this._oldVNode=t}},t.prototype.renderOneToVNode=function(t){return xw(t,$b(this._id))},t.prototype.renderToVNode=function(t){t=t||{};var e=this.storage.getDisplayList(!0),n=this._width,i=this._height,r=$b(this._id);r.animation=t.animation,r.willUpdate=t.willUpdate,r.compress=t.compress;var o=[],a=this._bgVNode=function(t,e,n,i){var r;if(n&&"none"!==n)if(r=qb("rect","bg",{width:t,height:e,x:"0",y:"0",id:"0"}),vi(n))_w({fill:n},r.attrs,"fill",i);else if(fi(n))bw({style:{fill:n},dirty:bt,getBoundingRect:function(){return{width:t,height:e}}},r.attrs,"fill",i);else{var o=si(n),a=o.color,s=o.opacity;r.attrs.fill=a,s<1&&(r.attrs["fill-opacity"]=s)}return r}(n,i,this._backgroundColor,r);a&&o.push(a);var s=t.compress?null:this._mainVNode=qb("g","main",{},[]);this._paintList(e,r,s?s.children:o),s&&o.push(s);var l=z(G(r.defs),(function(t){return r.defs[t]}));if(l.length&&o.push(qb("defs","defs",{},l)),t.animation){var u=function(t,e,n){var i=(n=n||{}).newline?"\n":"",r=" {"+i,o=i+"}",a=z(G(t),(function(e){return e+r+z(G(t[e]),(function(n){return n+":"+t[e][n]+";"})).join(i)+o})).join(i),s=z(G(e),(function(t){return"@keyframes "+t+r+z(G(e[t]),(function(n){return n+r+z(G(e[t][n]),(function(i){var r=e[t][n][i];return"d"===i&&(r='path("'+r+'")'),i+":"+r+";"})).join(i)+o})).join(i)+o})).join(i);return a||s?[""].join(i):""}(r.cssNodes,r.cssAnims,{newline:!0});if(u){var h=qb("style","stl",{},[],u);o.push(h)}}return Jb(n,i,o,t.useViewBox)},t.prototype.renderToString=function(t){return t=t||{},Kb(this.renderToVNode({animation:rt(t.cssAnimation,!0),willUpdate:!1,compress:!0,useViewBox:rt(t.useViewBox,!0)}),{newline:!0})},t.prototype.setBackgroundColor=function(t){this._backgroundColor=t},t.prototype.getSvgRoot=function(){return this._mainVNode&&this._mainVNode.elm},t.prototype._paintList=function(t,e,n){for(var i,r,o=t.length,a=[],s=0,l=0,u=0;u=0&&(!c||!r||c[f]!==r[f]);f--);for(var g=d-1;g>f;g--)i=a[--s-1];for(var y=f+1;y=a)}}for(var h=this.__startIndex;h15)break}n.prevElClipPaths&&u.restore()};if(p)if(0===p.length)s=l.__endIndex;else for(var _=d.dpr,b=0;b0&&t>i[0]){for(s=0;st);s++);a=n[i[s]]}if(i.splice(s+1,0,t),n[t]=e,!e.virtual)if(a){var l=a.dom;l.nextSibling?o.insertBefore(e.dom,l.nextSibling):o.appendChild(e.dom)}else o.firstChild?o.insertBefore(e.dom,o.firstChild):o.appendChild(e.dom);e.__painter=this}},t.prototype.eachLayer=function(t,e){for(var n=this._zlevelList,i=0;i0?Zw:0),this._needsManuallyCompositing),u.__builtin__||I("ZLevel "+l+" has been used by unkown layer "+u.id),u!==o&&(u.__used=!0,u.__startIndex!==r&&(u.__dirty=!0),u.__startIndex=r,u.incremental?u.__drawIndex=-1:u.__drawIndex=r,e(r),o=u),1&s.__dirty&&!s.__inHover&&(u.__dirty=!0,u.incremental&&u.__drawIndex<0&&(u.__drawIndex=r))}e(r),this.eachBuiltinLayer((function(t,e){!t.__used&&t.getElementCount()>0&&(t.__dirty=!0,t.__startIndex=t.__endIndex=t.__drawIndex=0),t.__dirty&&t.__drawIndex<0&&(t.__drawIndex=t.__startIndex)}))},t.prototype.clear=function(){return this.eachBuiltinLayer(this._clearLayer),this},t.prototype._clearLayer=function(t){t.clear()},t.prototype.setBackgroundColor=function(t){this._backgroundColor=t,E(this._layers,(function(t){t.setUnpainted()}))},t.prototype.configLayer=function(t,e){if(e){var n=this._layerConfig;n[t]?C(n[t],e,!0):n[t]=e;for(var i=0;i-1&&(s.style.stroke=s.style.fill,s.style.fill="#fff",s.style.lineWidth=2),e},e.type="series.line",e.dependencies=["grid","polar"],e.defaultOption={z:3,coordinateSystem:"cartesian2d",legendHoverLink:!0,clip:!0,label:{position:"top"},endLabel:{show:!1,valueAnimation:!0,distance:8},lineStyle:{width:2,type:"solid"},emphasis:{scale:!0},step:!1,smooth:!1,smoothMonotone:null,symbol:"emptyCircle",symbolSize:4,symbolRotate:null,showSymbol:!0,showAllSymbol:"auto",connectNulls:!1,sampling:"none",animationEasing:"linear",progressive:0,hoverLayerThreshold:1/0,universalTransition:{divideShape:"clone"},triggerLineEvent:!1},e}(fg);function Kw(t,e){var n=t.mapDimensionsAll("defaultedLabel"),i=n.length;if(1===i){var r=df(t,e,n[0]);return null!=r?r+"":null}if(i){for(var o=[],a=0;a=0&&i.push(e[o])}return i.join(" ")}var Jw=function(t){function e(e,n,i,r){var o=t.call(this)||this;return o.updateData(e,n,i,r),o}return n(e,t),e.prototype._createSymbol=function(t,e,n,i,r){this.removeAll();var o=Vy(t,-1,-1,2,2,null,r);o.attr({z2:100,culling:!0,scaleX:i[0]/2,scaleY:i[1]/2}),o.drift=Qw,this._symbolType=t,this.add(o)},e.prototype.stopSymbolAnimation=function(t){this.childAt(0).stopAnimation(null,t)},e.prototype.getSymbolType=function(){return this._symbolType},e.prototype.getSymbolPath=function(){return this.childAt(0)},e.prototype.highlight=function(){Al(this.childAt(0))},e.prototype.downplay=function(){kl(this.childAt(0))},e.prototype.setZ=function(t,e){var n=this.childAt(0);n.zlevel=t,n.z=e},e.prototype.setDraggable=function(t,e){var n=this.childAt(0);n.draggable=t,n.cursor=!e&&t?"move":n.cursor},e.prototype.updateData=function(t,n,i,r){this.silent=!1;var o=t.getItemVisual(n,"symbol")||"circle",a=t.hostModel,s=e.getSymbolSize(t,n),l=o!==this._symbolType,u=r&&r.disableAnimation;if(l){var h=t.getItemVisual(n,"symbolKeepAspect");this._createSymbol(o,t,n,s,h)}else{(p=this.childAt(0)).silent=!1;var c={scaleX:s[0]/2,scaleY:s[1]/2};u?p.attr(c):dh(p,c,a,n),xh(p)}if(this._updateCommon(t,n,s,i,r),l){var p=this.childAt(0);if(!u){c={scaleX:this._sizeX,scaleY:this._sizeY,style:{opacity:p.style.opacity}};p.scaleX=p.scaleY=0,p.style.opacity=0,fh(p,c,a,n)}}u&&this.childAt(0).stopAnimation("leave")},e.prototype._updateCommon=function(t,e,n,i,r){var o,a,s,l,u,h,c,p,d,f=this.childAt(0),g=t.hostModel;if(i&&(o=i.emphasisItemStyle,a=i.blurItemStyle,s=i.selectItemStyle,l=i.focus,u=i.blurScope,c=i.labelStatesModels,p=i.hoverScale,d=i.cursorStyle,h=i.emphasisDisabled),!i||t.hasItemOption){var y=i&&i.itemModel?i.itemModel:t.getItemModel(e),v=y.getModel("emphasis");o=v.getModel("itemStyle").getItemStyle(),s=y.getModel(["select","itemStyle"]).getItemStyle(),a=y.getModel(["blur","itemStyle"]).getItemStyle(),l=v.get("focus"),u=v.get("blurScope"),h=v.get("disabled"),c=tc(y),p=v.getShallow("scale"),d=y.getShallow("cursor")}var m=t.getItemVisual(e,"symbolRotate");f.attr("rotation",(m||0)*Math.PI/180||0);var x=Fy(t.getItemVisual(e,"symbolOffset"),n);x&&(f.x=x[0],f.y=x[1]),d&&f.attr("cursor",d);var _=t.getItemVisual(e,"style"),b=_.fill;if(f instanceof As){var w=f.style;f.useStyle(A({image:w.image,x:w.x,y:w.y,width:w.width,height:w.height},_))}else f.__isEmptyBrush?f.useStyle(A({},_)):f.useStyle(_),f.style.decal=null,f.setColor(b,r&&r.symbolInnerColor),f.style.strokeNoScale=!0;var S=t.getItemVisual(e,"liftZ"),M=this._z2;null!=S?null==M&&(this._z2=f.z2,f.z2+=S):null!=M&&(f.z2=M,this._z2=null);var I=r&&r.useNameLabel;Qh(f,c,{labelFetcher:g,labelDataIndex:e,defaultText:function(e){return I?t.getName(e):Kw(t,e)},inheritColor:b,defaultOpacity:_.opacity}),this._sizeX=n[0]/2,this._sizeY=n[1]/2;var T=f.ensureState("emphasis");T.style=o,f.ensureState("select").style=s,f.ensureState("blur").style=a;var C=null==p||!0===p?Math.max(1.1,3/this._sizeY):isFinite(p)&&p>0?+p:1;T.scaleX=this._sizeX*C,T.scaleY=this._sizeY*C,this.setSymbolScale(1),Hl(this,l,u,h)},e.prototype.setSymbolScale=function(t){this.scaleX=this.scaleY=t},e.prototype.fadeOut=function(t,e,n){var i=this.childAt(0),r=Js(this).dataIndex,o=n&&n.animation;if(this.silent=i.silent=!0,n&&n.fadeLabel){var a=i.getTextContent();a&&yh(a,{style:{opacity:0}},e,{dataIndex:r,removeOpt:o,cb:function(){i.removeTextContent()}})}else i.removeTextContent();yh(i,{style:{opacity:0},scaleX:0,scaleY:0},e,{dataIndex:r,cb:t,removeOpt:o})},e.getSymbolSize=function(t,e){return By(t.getItemVisual(e,"symbolSize"))},e}(Er);function Qw(t,e){this.parent.drift(t,e)}function tS(t,e,n,i){return e&&!isNaN(e[0])&&!isNaN(e[1])&&!(i.isIgnore&&i.isIgnore(n))&&!(i.clipShape&&!i.clipShape.contain(e[0],e[1]))&&"none"!==t.getItemVisual(n,"symbol")}function eS(t){return null==t||q(t)||(t={isIgnore:t}),t||{}}function nS(t){var e=t.hostModel,n=e.getModel("emphasis");return{emphasisItemStyle:n.getModel("itemStyle").getItemStyle(),blurItemStyle:e.getModel(["blur","itemStyle"]).getItemStyle(),selectItemStyle:e.getModel(["select","itemStyle"]).getItemStyle(),focus:n.get("focus"),blurScope:n.get("blurScope"),emphasisDisabled:n.get("disabled"),hoverScale:n.get("scale"),labelStatesModels:tc(e),cursorStyle:e.get("cursor")}}var iS=function(){function t(t){this.group=new Er,this._SymbolCtor=t||Jw}return t.prototype.updateData=function(t,e){this._progressiveEls=null,e=eS(e);var n=this.group,i=t.hostModel,r=this._data,o=this._SymbolCtor,a=e.disableAnimation,s=nS(t),l={disableAnimation:a},u=e.getSymbolPoint||function(e){return t.getItemLayout(e)};r||n.removeAll(),t.diff(r).add((function(i){var r=u(i);if(tS(t,r,i,e)){var a=new o(t,i,s,l);a.setPosition(r),t.setItemGraphicEl(i,a),n.add(a)}})).update((function(h,c){var p=r.getItemGraphicEl(c),d=u(h);if(tS(t,d,h,e)){var f=t.getItemVisual(h,"symbol")||"circle",g=p&&p.getSymbolType&&p.getSymbolType();if(!p||g&&g!==f)n.remove(p),(p=new o(t,h,s,l)).setPosition(d);else{p.updateData(t,h,s,l);var y={x:d[0],y:d[1]};a?p.attr(y):dh(p,y,i)}n.add(p),t.setItemGraphicEl(h,p)}else n.remove(p)})).remove((function(t){var e=r.getItemGraphicEl(t);e&&e.fadeOut((function(){n.remove(e)}),i)})).execute(),this._getSymbolPoint=u,this._data=t},t.prototype.updateLayout=function(){var t=this,e=this._data;e&&e.eachItemGraphicEl((function(e,n){var i=t._getSymbolPoint(n);e.setPosition(i),e.markRedraw()}))},t.prototype.incrementalPrepareUpdate=function(t){this._seriesScope=nS(t),this._data=null,this.group.removeAll()},t.prototype.incrementalUpdate=function(t,e,n){function i(t){t.isGroup||(t.incremental=!0,t.ensureState("emphasis").hoverLayer=!0)}this._progressiveEls=[],n=eS(n);for(var r=t.start;r0?n=i[0]:i[1]<0&&(n=i[1]);return n}(r,n),a=i.dim,s=r.dim,l=e.mapDimension(s),u=e.mapDimension(a),h="x"===s||"radius"===s?1:0,c=z(t.dimensions,(function(t){return e.mapDimension(t)})),p=!1,d=e.getCalculationInfo("stackResultDimension");return lx(e,c[0])&&(p=!0,c[0]=d),lx(e,c[1])&&(p=!0,c[1]=d),{dataDimsForPoint:c,valueStart:o,valueAxisDim:s,baseAxisDim:a,stacked:!!p,valueDim:l,baseDim:u,baseDataOffset:h,stackedOverDimension:e.getCalculationInfo("stackedOverDimension")}}function oS(t,e,n,i){var r=NaN;t.stacked&&(r=n.get(n.getCalculationInfo("stackedOverDimension"),i)),isNaN(r)&&(r=t.valueStart);var o=t.baseDataOffset,a=[];return a[o]=n.get(t.baseDim,i),a[1-o]=r,e.dataToPoint(a)}var aS=Math.min,sS=Math.max;function lS(t,e){return isNaN(t)||isNaN(e)}function uS(t,e,n,i,r,o,a,s,l){for(var u,h,c,p,d,f,g=n,y=0;y=r||g<0)break;if(lS(v,m)){if(l){g+=o;continue}break}if(g===n)t[o>0?"moveTo":"lineTo"](v,m),c=v,p=m;else{var x=v-u,_=m-h;if(x*x+_*_<.5){g+=o;continue}if(a>0){for(var b=g+o,w=e[2*b],S=e[2*b+1];w===v&&S===m&&y=i||lS(w,S))d=v,f=m;else{T=w-u,C=S-h;var k=v-u,L=w-v,P=m-h,O=S-m,R=void 0,N=void 0;if("x"===s){var E=T>0?1:-1;d=v-E*(R=Math.abs(k))*a,f=m,D=v+E*(N=Math.abs(L))*a,A=m}else if("y"===s){var z=C>0?1:-1;d=v,f=m-z*(R=Math.abs(P))*a,D=v,A=m+z*(N=Math.abs(O))*a}else R=Math.sqrt(k*k+P*P),d=v-T*a*(1-(I=(N=Math.sqrt(L*L+O*O))/(N+R))),f=m-C*a*(1-I),A=m+C*a*I,D=aS(D=v+T*a*I,sS(w,v)),A=aS(A,sS(S,m)),D=sS(D,aS(w,v)),f=m-(C=(A=sS(A,aS(S,m)))-m)*R/N,d=aS(d=v-(T=D-v)*R/N,sS(u,v)),f=aS(f,sS(h,m)),D=v+(T=v-(d=sS(d,aS(u,v))))*N/R,A=m+(C=m-(f=sS(f,aS(h,m))))*N/R}t.bezierCurveTo(c,p,d,f,v,m),c=D,p=A}else t.lineTo(v,m)}u=v,h=m,g+=o}return y}var hS=function(){this.smooth=0,this.smoothConstraint=!0},cS=function(t){function e(e){var n=t.call(this,e)||this;return n.type="ec-polyline",n}return n(e,t),e.prototype.getDefaultStyle=function(){return{stroke:"#000",fill:null}},e.prototype.getDefaultShape=function(){return new hS},e.prototype.buildPath=function(t,e){var n=e.points,i=0,r=n.length/2;if(e.connectNulls){for(;r>0&&lS(n[2*r-2],n[2*r-1]);r--);for(;i=0){var y=a?(h-i)*g+i:(u-n)*g+n;return a?[t,y]:[y,t]}n=u,i=h;break;case o.C:u=r[l++],h=r[l++],c=r[l++],p=r[l++],d=r[l++],f=r[l++];var v=a?xn(n,u,c,d,t,s):xn(i,h,p,f,t,s);if(v>0)for(var m=0;m=0){y=a?vn(i,h,p,f,x):vn(n,u,c,d,x);return a?[t,y]:[y,t]}}n=d,i=f}}},e}(Ms),pS=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e}(hS),dS=function(t){function e(e){var n=t.call(this,e)||this;return n.type="ec-polygon",n}return n(e,t),e.prototype.getDefaultShape=function(){return new pS},e.prototype.buildPath=function(t,e){var n=e.points,i=e.stackedOnPoints,r=0,o=n.length/2,a=e.smoothMonotone;if(e.connectNulls){for(;o>0&&lS(n[2*o-2],n[2*o-1]);o--);for(;r=0;a--){var s=t.getDimensionInfo(i[a].dimension);if("x"===(r=s&&s.coordDim)||"y"===r){o=i[a];break}}if(o){var l=e.getAxis(r),u=z(o.stops,(function(t){return{coord:l.toGlobalCoord(l.dataToCoord(t.value)),color:t.color}})),h=u.length,c=o.outerColors.slice();h&&u[0].coord>u[h-1].coord&&(u.reverse(),c.reverse());var p=function(t,e){var n,i,r=[],o=t.length;function a(t,e,n){var i=t.coord;return{coord:n,color:Qn((n-i)/(e.coord-i),[t.color,e.color])}}for(var s=0;se){i?r.push(a(i,l,e)):n&&r.push(a(n,l,0),a(n,l,e));break}n&&(r.push(a(n,l,0)),n=null),r.push(l),i=l}}return r}(u,"x"===r?n.getWidth():n.getHeight()),d=p.length;if(!d&&h)return u[0].coord<0?c[1]?c[1]:u[h-1].color:c[0]?c[0]:u[0].color;var f=p[0].coord-10,g=p[d-1].coord+10,y=g-f;if(y<.001)return"transparent";E(p,(function(t){t.offset=(t.coord-f)/y})),p.push({offset:d?p[d-1].offset:.5,color:c[1]||"transparent"}),p.unshift({offset:d?p[0].offset:.5,color:c[0]||"transparent"});var v=new eh(0,0,0,0,p,!0);return v[r]=f,v[r+"2"]=g,v}}}function MS(t,e,n){var i=t.get("showAllSymbol"),r="auto"===i;if(!i||r){var o=n.getAxesByScale("ordinal")[0];if(o&&(!r||!function(t,e){var n=t.getExtent(),i=Math.abs(n[1]-n[0])/t.scale.count();isNaN(i)&&(i=0);for(var r=e.count(),o=Math.max(1,Math.round(r/5)),a=0;ai)return!1;return!0}(o,e))){var a=e.mapDimension(o.dim),s={};return E(o.getViewLabels(),(function(t){var e=o.scale.getRawOrdinalNumber(t.tickValue);s[e]=1})),function(t){return!s.hasOwnProperty(e.get(a,t))}}}}function IS(t,e){return[t[2*e],t[2*e+1]]}function TS(t){if(t.get(["endLabel","show"]))return!0;for(var e=0;e0&&"bolder"===t.get(["emphasis","lineStyle","width"]))&&(d.getState("emphasis").style.lineWidth=+d.style.lineWidth+1);Js(d).seriesIndex=t.seriesIndex,Hl(d,L,P,O);var R=bS(t.get("smooth")),N=t.get("smoothMonotone");if(d.setShape({smooth:R,smoothMonotone:N,connectNulls:w}),f){var E=a.getCalculationInfo("stackedOnSeries"),z=0;f.useStyle(k(l.getAreaStyle(),{fill:C,opacity:.7,lineJoin:"bevel",decal:a.getVisual("style").decal})),E&&(z=bS(E.get("smooth"))),f.setShape({smooth:R,stackedOnSmooth:z,smoothMonotone:N,connectNulls:w}),Zl(f,t,"areaStyle"),Js(f).seriesIndex=t.seriesIndex,Hl(f,L,P,O)}var V=function(t){i._changePolyState(t)};a.eachItemGraphicEl((function(t){t&&(t.onHoverStateChange=V)})),this._polyline.onHoverStateChange=V,this._data=a,this._coordSys=r,this._stackedOnPoints=_,this._points=u,this._step=T,this._valueOrigin=m,t.get("triggerLineEvent")&&(this.packEventData(t,d),f&&this.packEventData(t,f))},e.prototype.packEventData=function(t,e){Js(e).eventData={componentType:"series",componentSubType:"line",componentIndex:t.componentIndex,seriesIndex:t.seriesIndex,seriesName:t.name,seriesType:"line"}},e.prototype.highlight=function(t,e,n,i){var r=t.getData(),o=Lo(r,i);if(this._changePolyState("emphasis"),!(o instanceof Array)&&null!=o&&o>=0){var a=r.getLayout("points"),s=r.getItemGraphicEl(o);if(!s){var l=a[2*o],u=a[2*o+1];if(isNaN(l)||isNaN(u))return;if(this._clipShapeForSymbol&&!this._clipShapeForSymbol.contain(l,u))return;var h=t.get("zlevel")||0,c=t.get("z")||0;(s=new Jw(r,o)).x=l,s.y=u,s.setZ(h,c);var p=s.getSymbolPath().getTextContent();p&&(p.zlevel=h,p.z=c,p.z2=this._polyline.z2+1),s.__temp=!0,r.setItemGraphicEl(o,s),s.stopSymbolAnimation(!0),this.group.add(s)}s.highlight()}else Tg.prototype.highlight.call(this,t,e,n,i)},e.prototype.downplay=function(t,e,n,i){var r=t.getData(),o=Lo(r,i);if(this._changePolyState("normal"),null!=o&&o>=0){var a=r.getItemGraphicEl(o);a&&(a.__temp?(r.setItemGraphicEl(o,null),this.group.remove(a)):a.downplay())}else Tg.prototype.downplay.call(this,t,e,n,i)},e.prototype._changePolyState=function(t){var e=this._polygon;Ml(this._polyline,t),e&&Ml(e,t)},e.prototype._newPolyline=function(t){var e=this._polyline;return e&&this._lineGroup.remove(e),e=new cS({shape:{points:t},segmentIgnoreThreshold:2,z2:10}),this._lineGroup.add(e),this._polyline=e,e},e.prototype._newPolygon=function(t,e){var n=this._polygon;return n&&this._lineGroup.remove(n),n=new dS({shape:{points:t,stackedOnPoints:e},segmentIgnoreThreshold:2}),this._lineGroup.add(n),this._polygon=n,n},e.prototype._initSymbolLabelAnimation=function(t,e,n){var i,r,o=e.getBaseAxis(),a=o.inverse;"cartesian2d"===e.type?(i=o.isHorizontal(),r=!1):"polar"===e.type&&(i="angle"===o.dim,r=!0);var s=t.hostModel,l=s.get("animationDuration");U(l)&&(l=l(null));var u=s.get("animationDelay")||0,h=U(u)?u(null):u;t.eachItemGraphicEl((function(t,o){var s=t;if(s){var c=[t.x,t.y],p=void 0,d=void 0,f=void 0;if(n)if(r){var g=n,y=e.pointToCoord(c);i?(p=g.startAngle,d=g.endAngle,f=-y[1]/180*Math.PI):(p=g.r0,d=g.r,f=y[0])}else{var v=n;i?(p=v.x,d=v.x+v.width,f=t.x):(p=v.y+v.height,d=v.y,f=t.y)}var m=d===p?0:(f-p)/(d-p);a&&(m=1-m);var x=U(u)?u(o):l*m+h,_=s.getSymbolPath(),b=_.getTextContent();s.attr({scaleX:0,scaleY:0}),s.animateTo({scaleX:1,scaleY:1},{duration:200,setToFinal:!0,delay:x}),b&&b.animateFrom({style:{opacity:0}},{duration:300,delay:x}),_.disableLabelAnimation=!0}}))},e.prototype._initOrUpdateEndLabel=function(t,e,n){var i=t.getModel("endLabel");if(TS(t)){var r=t.getData(),o=this._polyline,a=r.getLayout("points");if(!a)return o.removeTextContent(),void(this._endLabel=null);var s=this._endLabel;s||((s=this._endLabel=new Bs({z2:200})).ignoreClip=!0,o.setTextContent(this._endLabel),o.disableLabelAnimation=!0);var l=function(t){for(var e,n,i=t.length/2;i>0&&(e=t[2*i-2],n=t[2*i-1],isNaN(e)||isNaN(n));i--);return i-1}(a);l>=0&&(Qh(o,tc(t,"endLabel"),{inheritColor:n,labelFetcher:t,labelDataIndex:l,defaultText:function(t,e,n){return null!=n?$w(r,n):Kw(r,t)},enableTextSetter:!0},function(t,e){var n=e.getBaseAxis(),i=n.isHorizontal(),r=n.inverse,o=i?r?"right":"left":"center",a=i?"middle":r?"top":"bottom";return{normal:{align:t.get("align")||o,verticalAlign:t.get("verticalAlign")||a}}}(i,e)),o.textConfig.position=null)}else this._endLabel&&(this._polyline.removeTextContent(),this._endLabel=null)},e.prototype._endLabelOnDuring=function(t,e,n,i,r,o,a){var s=this._endLabel,l=this._polyline;if(s){t<1&&null==i.originalX&&(i.originalX=s.x,i.originalY=s.y);var u=n.getLayout("points"),h=n.hostModel,c=h.get("connectNulls"),p=o.get("precision"),d=o.get("distance")||0,f=a.getBaseAxis(),g=f.isHorizontal(),y=f.inverse,v=e.shape,m=y?g?v.x:v.y+v.height:g?v.x+v.width:v.y,x=(g?d:0)*(y?-1:1),_=(g?0:-d)*(y?-1:1),b=g?"x":"y",w=function(t,e,n){for(var i,r,o=t.length/2,a="x"===n?0:1,s=0,l=-1,u=0;u=e||i>=e&&r<=e){l=u;break}s=u,i=r}else i=r;return{range:[s,l],t:(e-i)/(r-i)}}(u,m,b),S=w.range,M=S[1]-S[0],I=void 0;if(M>=1){if(M>1&&!c){var T=IS(u,S[0]);s.attr({x:T[0]+x,y:T[1]+_}),r&&(I=h.getRawValue(S[0]))}else{(T=l.getPointOn(m,b))&&s.attr({x:T[0]+x,y:T[1]+_});var C=h.getRawValue(S[0]),D=h.getRawValue(S[1]);r&&(I=Go(n,p,C,D,w.t))}i.lastFrameIndex=S[0]}else{var A=1===t||i.lastFrameIndex>0?S[0]:0;T=IS(u,A);r&&(I=h.getRawValue(A)),s.attr({x:T[0]+x,y:T[1]+_})}r&&lc(s).setLabelText(I)}},e.prototype._doUpdateAnimation=function(t,e,n,i,r,o,a){var s=this._polyline,l=this._polygon,u=t.hostModel,h=function(t,e,n,i,r,o,a,s){for(var l=function(t,e){var n=[];return e.diff(t).add((function(t){n.push({cmd:"+",idx:t})})).update((function(t,e){n.push({cmd:"=",idx:e,idx1:t})})).remove((function(t){n.push({cmd:"-",idx:t})})).execute(),n}(t,e),u=[],h=[],c=[],p=[],d=[],f=[],g=[],y=rS(r,e,a),v=t.getLayout("points")||[],m=e.getLayout("points")||[],x=0;x3e3||l&&_S(p,f)>3e3)return s.stopAnimation(),s.setShape({points:d}),void(l&&(l.stopAnimation(),l.setShape({points:d,stackedOnPoints:f})));s.shape.__points=h.current,s.shape.points=c;var g={shape:{points:d}};h.current!==c&&(g.shape.__points=h.next),s.stopAnimation(),dh(s,g,u),l&&(l.setShape({points:c,stackedOnPoints:p}),l.stopAnimation(),dh(l,{shape:{stackedOnPoints:f}},u),s.shape.points!==l.shape.points&&(l.shape.points=s.shape.points));for(var y=[],v=h.status,m=0;me&&(e=t[n]);return isFinite(e)?e:NaN},min:function(t){for(var e=1/0,n=0;n10&&"cartesian2d"===o.type&&r){var s=o.getBaseAxis(),l=o.getOtherAxis(s),u=s.getExtent(),h=n.getDevicePixelRatio(),c=Math.abs(u[1]-u[0])*(h||1),p=Math.round(a/c);if(isFinite(p)&&p>1){"lttb"===r&&t.setData(i.lttbDownSample(i.mapDimension(l.dim),1/p));var d=void 0;X(r)?d=kS[r]:U(r)&&(d=r),d&&t.setData(i.downSample(i.mapDimension(l.dim),1/p,d,LS))}}}}}var OS=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.getInitialData=function(t,e){return hx(null,this,{useEncodeDefaulter:!0})},e.prototype.getMarkerPosition=function(t,e,n){var i=this.coordinateSystem;if(i&&i.clampData){var r=i.dataToPoint(i.clampData(t));if(n)E(i.getAxes(),(function(n,o){if("category"===n.type){var a=n.getTicksCoords(),s=i.clampData(t)[o];!e||"x1"!==e[o]&&"y1"!==e[o]||(s+=1),s>a.length-1&&(s=a.length-1),s<0&&(s=0),a[s]&&(r[o]=n.toGlobalCoord(a[s].coord))}}));else{var o=this.getData(),a=o.getLayout("offset"),s=o.getLayout("size"),l=i.getBaseAxis().isHorizontal()?0:1;r[l]+=a+s/2}return r}return[NaN,NaN]},e.type="series.__base_bar__",e.defaultOption={z:2,coordinateSystem:"cartesian2d",legendHoverLink:!0,barMinHeight:0,barMinAngle:0,large:!1,largeThreshold:400,progressive:3e3,progressiveChunkMode:"mod"},e}(fg);fg.registerClass(OS);var RS=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.getInitialData=function(){return hx(null,this,{useEncodeDefaulter:!0,createInvertedIndices:!!this.get("realtimeSort",!0)||null})},e.prototype.getProgressive=function(){return!!this.get("large")&&this.get("progressive")},e.prototype.getProgressiveThreshold=function(){var t=this.get("progressiveThreshold"),e=this.get("largeThreshold");return e>t&&(t=e),t},e.prototype.brushSelector=function(t,e,n){return n.rect(e.getItemLayout(t))},e.type="series.bar",e.dependencies=["grid","polar"],e.defaultOption=Tc(OS.defaultOption,{clip:!0,roundCap:!1,showBackground:!1,backgroundStyle:{color:"rgba(180, 180, 180, 0.2)",borderColor:null,borderWidth:0,borderType:"solid",borderRadius:0,shadowBlur:0,shadowColor:null,shadowOffsetX:0,shadowOffsetY:0,opacity:1},select:{itemStyle:{borderColor:"#212121"}},realtimeSort:!1}),e}(OS),NS=function(){this.cx=0,this.cy=0,this.r0=0,this.r=0,this.startAngle=0,this.endAngle=2*Math.PI,this.clockwise=!0},ES=function(t){function e(e){var n=t.call(this,e)||this;return n.type="sausage",n}return n(e,t),e.prototype.getDefaultShape=function(){return new NS},e.prototype.buildPath=function(t,e){var n=e.cx,i=e.cy,r=Math.max(e.r0||0,0),o=Math.max(e.r,0),a=.5*(o-r),s=r+a,l=e.startAngle,u=e.endAngle,h=e.clockwise,c=2*Math.PI,p=h?u-lo)return!0;o=u}return!1},e.prototype._isOrderDifferentInView=function(t,e){for(var n=e.scale,i=n.getExtent(),r=Math.max(0,i[0]),o=Math.min(i[1],n.getOrdinalMeta().categories.length-1);r<=o;++r)if(t.ordinalNumbers[r]!==n.getRawOrdinalNumber(r))return!0},e.prototype._updateSortWithinSameData=function(t,e,n,i){if(this._isOrderChangedWithinSameData(t,e,n)){var r=this._dataSort(t,n,e);this._isOrderDifferentInView(r,n)&&(this._removeOnRenderedListener(i),i.dispatchAction({type:"changeAxisOrder",componentType:n.dim+"Axis",axisId:n.index,sortInfo:r}))}},e.prototype._dispatchInitSort=function(t,e,n){var i=e.baseAxis,r=this._dataSort(t,i,(function(n){return t.get(t.mapDimension(e.otherAxis.dim),n)}));n.dispatchAction({type:"changeAxisOrder",componentType:i.dim+"Axis",isInitSort:!0,axisId:i.index,sortInfo:r})},e.prototype.remove=function(t,e){this._clear(this._model),this._removeOnRenderedListener(e)},e.prototype.dispose=function(t,e){this._removeOnRenderedListener(e)},e.prototype._removeOnRenderedListener=function(t){this._onRendered&&(t.getZr().off("rendered",this._onRendered),this._onRendered=null)},e.prototype._clear=function(t){var e=this.group,n=this._data;t&&t.isAnimationEnabled()&&n&&!this._isLargeDraw?(this._removeBackground(),this._backgroundEls=[],n.eachItemGraphicEl((function(e){mh(e,t,Js(e).dataIndex)}))):e.removeAll(),this._data=null,this._isFirstFrame=!0},e.prototype._removeBackground=function(){this.group.remove(this._backgroundGroup),this._backgroundGroup=null},e.type="bar",e}(Tg),WS={cartesian2d:function(t,e){var n=e.width<0?-1:1,i=e.height<0?-1:1;n<0&&(e.x+=e.width,e.width=-e.width),i<0&&(e.y+=e.height,e.height=-e.height);var r=t.x+t.width,o=t.y+t.height,a=BS(e.x,t.x),s=FS(e.x+e.width,r),l=BS(e.y,t.y),u=FS(e.y+e.height,o),h=sr?s:a,e.y=c&&l>o?u:l,e.width=h?0:s-a,e.height=c?0:u-l,n<0&&(e.x+=e.width,e.width=-e.width),i<0&&(e.y+=e.height,e.height=-e.height),h||c},polar:function(t,e){var n=e.r0<=e.r?1:-1;if(n<0){var i=e.r;e.r=e.r0,e.r0=i}var r=FS(e.r,t.r),o=BS(e.r0,t.r0);e.r=r,e.r0=o;var a=r-o<0;if(n<0){i=e.r;e.r=e.r0,e.r0=i}return a}},HS={cartesian2d:function(t,e,n,i,r,o,a,s,l){var u=new Es({shape:A({},i),z2:1});(u.__dataIndex=n,u.name="item",o)&&(u.shape[r?"height":"width"]=0);return u},polar:function(t,e,n,i,r,o,a,s,l){var u=!r&&l?ES:Eu,h=new u({shape:i,z2:1});h.name="item";var c,p,d=KS(r);if(h.calculateTextPosition=(c=d,p=({isRoundCap:u===ES}||{}).isRoundCap,function(t,e,n){var i=e.position;if(!i||i instanceof Array)return Ir(t,e,n);var r=c(i),o=null!=e.distance?e.distance:5,a=this.shape,s=a.cx,l=a.cy,u=a.r,h=a.r0,d=(u+h)/2,f=a.startAngle,g=a.endAngle,y=(f+g)/2,v=p?Math.abs(u-h)/2:0,m=Math.cos,x=Math.sin,_=s+u*m(f),b=l+u*x(f),w="left",S="top";switch(r){case"startArc":_=s+(h-o)*m(y),b=l+(h-o)*x(y),w="center",S="top";break;case"insideStartArc":_=s+(h+o)*m(y),b=l+(h+o)*x(y),w="center",S="bottom";break;case"startAngle":_=s+d*m(f)+zS(f,o+v,!1),b=l+d*x(f)+VS(f,o+v,!1),w="right",S="middle";break;case"insideStartAngle":_=s+d*m(f)+zS(f,-o+v,!1),b=l+d*x(f)+VS(f,-o+v,!1),w="left",S="middle";break;case"middle":_=s+d*m(y),b=l+d*x(y),w="center",S="middle";break;case"endArc":_=s+(u+o)*m(y),b=l+(u+o)*x(y),w="center",S="bottom";break;case"insideEndArc":_=s+(u-o)*m(y),b=l+(u-o)*x(y),w="center",S="top";break;case"endAngle":_=s+d*m(g)+zS(g,o+v,!0),b=l+d*x(g)+VS(g,o+v,!0),w="left",S="middle";break;case"insideEndAngle":_=s+d*m(g)+zS(g,-o+v,!0),b=l+d*x(g)+VS(g,-o+v,!0),w="right",S="middle";break;default:return Ir(t,e,n)}return(t=t||{}).x=_,t.y=b,t.align=w,t.verticalAlign=S,t}),o){var f=r?"r":"endAngle",g={};h.shape[f]=r?0:i.startAngle,g[f]=i[f],(s?dh:fh)(h,{shape:g},o)}return h}};function YS(t,e,n,i,r,o,a,s){var l,u;o?(u={x:i.x,width:i.width},l={y:i.y,height:i.height}):(u={y:i.y,height:i.height},l={x:i.x,width:i.width}),s||(a?dh:fh)(n,{shape:l},e,r,null),(a?dh:fh)(n,{shape:u},e?t.baseAxis.model:null,r)}function US(t,e){for(var n=0;n0?1:-1,a=i.height>0?1:-1;return{x:i.x+o*r/2,y:i.y+a*r/2,width:i.width-o*r,height:i.height-a*r}},polar:function(t,e,n){var i=t.getItemLayout(e);return{cx:i.cx,cy:i.cy,r0:i.r0,r:i.r,startAngle:i.startAngle,endAngle:i.endAngle,clockwise:i.clockwise}}};function KS(t){return function(t){var e=t?"Arc":"Angle";return function(t){switch(t){case"start":case"insideStart":case"end":case"insideEnd":return t+e;default:return t}}}(t)}function $S(t,e,n,i,r,o,a,s){var l=e.getItemVisual(n,"style");s||t.setShape("r",i.get(["itemStyle","borderRadius"])||0),t.useStyle(l);var u=i.getShallow("cursor");u&&t.attr("cursor",u);var h=s?a?r.r>=r.r0?"endArc":"startArc":r.endAngle>=r.startAngle?"endAngle":"startAngle":a?r.height>=0?"bottom":"top":r.width>=0?"right":"left",c=tc(i);Qh(t,c,{labelFetcher:o,labelDataIndex:n,defaultText:Kw(o.getData(),n),inheritColor:l.fill,defaultOpacity:l.opacity,defaultOutsidePosition:h});var p=t.getTextContent();if(s&&p){var d=i.get(["label","position"]);t.textConfig.inside="middle"===d||null,function(t,e,n,i){if(j(i))t.setTextConfig({rotation:i});else if(Y(e))t.setTextConfig({rotation:0});else{var r,o=t.shape,a=o.clockwise?o.startAngle:o.endAngle,s=o.clockwise?o.endAngle:o.startAngle,l=(a+s)/2,u=n(e);switch(u){case"startArc":case"insideStartArc":case"middle":case"insideEndArc":case"endArc":r=l;break;case"startAngle":case"insideStartAngle":r=a;break;case"endAngle":case"insideEndAngle":r=s;break;default:return void t.setTextConfig({rotation:0})}var h=1.5*Math.PI-r;"middle"===u&&h>Math.PI/2&&h<1.5*Math.PI&&(h-=Math.PI),t.setTextConfig({rotation:h})}}(t,"outside"===d?h:d,KS(a),i.get(["label","rotate"]))}uc(p,c,o.getRawValue(n),(function(t){return $w(e,t)}));var f=i.getModel(["emphasis"]);Hl(t,f.get("focus"),f.get("blurScope"),f.get("disabled")),Zl(t,i),function(t){return null!=t.startAngle&&null!=t.endAngle&&t.startAngle===t.endAngle}(r)&&(t.style.fill="none",t.style.stroke="none",E(t.states,(function(t){t.style&&(t.style.fill=t.style.stroke="none")})))}var JS=function(){},QS=function(t){function e(e){var n=t.call(this,e)||this;return n.type="largeBar",n}return n(e,t),e.prototype.getDefaultShape=function(){return new JS},e.prototype.buildPath=function(t,e){for(var n=e.points,i=this.baseDimIdx,r=1-this.baseDimIdx,o=[],a=[],s=this.barWidth,l=0;l=s[0]&&e<=s[0]+l[0]&&n>=s[1]&&n<=s[1]+l[1])return a[h]}return-1}(this,t.offsetX,t.offsetY);Js(this).dataIndex=e>=0?e:null}),30,!1);function nM(t,e,n){if(vS(n,"cartesian2d")){var i=e,r=n.getArea();return{x:t?i.x:r.x,y:t?r.y:i.y,width:t?i.width:r.width,height:t?r.height:i.height}}var o=e;return{cx:(r=n.getArea()).cx,cy:r.cy,r0:t?r.r0:o.r0,r:t?r.r:o.r,startAngle:t?o.startAngle:0,endAngle:t?o.endAngle:2*Math.PI}}var iM=2*Math.PI,rM=Math.PI/180;function oM(t,e){return Tp(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()})}function aM(t,e){var n=oM(t,e),i=t.get("center"),r=t.get("radius");Y(r)||(r=[0,r]);var o,a,s=Ur(n.width,e.getWidth()),l=Ur(n.height,e.getHeight()),u=Math.min(s,l),h=Ur(r[0],u/2),c=Ur(r[1],u/2),p=t.coordinateSystem;if(p){var d=p.dataToPoint(i);o=d[0]||0,a=d[1]||0}else Y(i)||(i=[i,i]),o=Ur(i[0],s)+n.x,a=Ur(i[1],l)+n.y;return{cx:o,cy:a,r0:h,r:c}}function sM(t,e,n){e.eachSeriesByType(t,(function(t){var e=t.getData(),i=e.mapDimension("value"),r=oM(t,n),o=aM(t,n),a=o.cx,s=o.cy,l=o.r,u=o.r0,h=-t.get("startAngle")*rM,c=t.get("minAngle")*rM,p=0;e.each(i,(function(t){!isNaN(t)&&p++}));var d=e.getSum(i),f=Math.PI/(d||p)*2,g=t.get("clockwise"),y=t.get("roseType"),v=t.get("stillShowZeroSum"),m=e.getDataExtent(i);m[0]=0;var x=iM,_=0,b=h,w=g?1:-1;if(e.setLayout({viewRect:r,r:l}),e.each(i,(function(t,n){var i;if(isNaN(t))e.setItemLayout(n,{angle:NaN,startAngle:NaN,endAngle:NaN,clockwise:g,cx:a,cy:s,r0:u,r:y?NaN:l});else{(i="area"!==y?0===d&&v?f:t*f:iM/p)n?a:o,h=Math.abs(l.label.y-n);if(h>=u.maxY){var c=l.label.x-e-l.len2*r,p=i+l.len,f=Math.abs(c)t.unconstrainedWidth?null:d:null;i.setStyle("width",f)}var g=i.getBoundingRect();o.width=g.width;var y=(i.style.margin||0)+2.1;o.height=g.height+y,o.y-=(o.height-c)/2}}}function pM(t){return"center"===t.position}function dM(t){var e,n,i=t.getData(),r=[],o=!1,a=(t.get("minShowLabelAngle")||0)*uM,s=i.getLayout("viewRect"),l=i.getLayout("r"),u=s.width,h=s.x,c=s.y,p=s.height;function d(t){t.ignore=!0}i.each((function(t){var s=i.getItemGraphicEl(t),c=s.shape,p=s.getTextContent(),f=s.getTextGuideLine(),g=i.getItemModel(t),y=g.getModel("label"),v=y.get("position")||g.get(["emphasis","label","position"]),m=y.get("distanceToLabelLine"),x=y.get("alignTo"),_=Ur(y.get("edgeDistance"),u),b=y.get("bleedMargin"),w=g.getModel("labelLine"),S=w.get("length");S=Ur(S,u);var M=w.get("length2");if(M=Ur(M,u),Math.abs(c.endAngle-c.startAngle)0?"right":"left":k>0?"left":"right"}var B=Math.PI,F=0,G=y.get("rotate");if(j(G))F=G*(B/180);else if("center"===v)F=0;else if("radial"===G||!0===G){F=k<0?-A+B:-A}else if("tangential"===G&&"outside"!==v&&"outer"!==v){var W=Math.atan2(k,L);W<0&&(W=2*B+W),L>0&&(W=B+W),F=W-B}if(o=!!F,p.x=I,p.y=T,p.rotation=F,p.setStyle({verticalAlign:"middle"}),P){p.setStyle({align:D});var H=p.states.select;H&&(H.x+=p.x,H.y+=p.y)}else{var Y=p.getBoundingRect().clone();Y.applyTransform(p.getComputedTransform());var U=(p.style.margin||0)+2.1;Y.y-=U/2,Y.height+=U,r.push({label:p,labelLine:f,position:v,len:S,len2:M,minTurnAngle:w.get("minTurnAngle"),maxSurfaceAngle:w.get("maxSurfaceAngle"),surfaceNormal:new Ce(k,L),linePoints:C,textAlign:D,labelDistance:m,labelAlignTo:x,edgeDistance:_,bleedMargin:b,rect:Y,unconstrainedWidth:Y.width,labelStyleWidth:p.style.width})}s.setTextConfig({inside:P})}})),!o&&t.get("avoidLabelOverlap")&&function(t,e,n,i,r,o,a,s){for(var l=[],u=[],h=Number.MAX_VALUE,c=-Number.MAX_VALUE,p=0;p0){for(var l=o.getItemLayout(0),u=1;isNaN(l&&l.startAngle)&&u=n.r0}},e.type="pie",e}(Tg);function vM(t,e,n){e=Y(e)&&{coordDimensions:e}||A({encodeDefine:t.getEncode()},e);var i=t.getSource(),r=nx(i,e).dimensions,o=new ex(r,t);return o.initData(i,n),o}var mM=function(){function t(t,e){this._getDataWithEncodedVisual=t,this._getRawData=e}return t.prototype.getAllNames=function(){var t=this._getRawData();return t.mapArray(t.getName)},t.prototype.containName=function(t){return this._getRawData().indexOfName(t)>=0},t.prototype.indexOfName=function(t){return this._getDataWithEncodedVisual().indexOfName(t)},t.prototype.getItemVisual=function(t,e){return this._getDataWithEncodedVisual().getItemVisual(t,e)},t}(),xM=Po(),_M=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.init=function(e){t.prototype.init.apply(this,arguments),this.legendVisualProvider=new mM(W(this.getData,this),W(this.getRawData,this)),this._defaultLabelLine(e)},e.prototype.mergeOption=function(){t.prototype.mergeOption.apply(this,arguments)},e.prototype.getInitialData=function(){return vM(this,{coordDimensions:["value"],encodeDefaulter:H($p,this)})},e.prototype.getDataParams=function(e){var n=this.getData(),i=xM(n),r=i.seats;if(!r){var o=[];n.each(n.mapDimension("value"),(function(t){o.push(t)})),r=i.seats=$r(o,n.hostModel.get("percentPrecision"))}var a=t.prototype.getDataParams.call(this,e);return a.percent=r[e]||0,a.$vars.push("percent"),a},e.prototype._defaultLabelLine=function(t){bo(t,"labelLine",["show"]);var e=t.labelLine,n=t.emphasis.labelLine;e.show=e.show&&t.label.show,n.show=n.show&&t.emphasis.label.show},e.type="series.pie",e.defaultOption={z:2,legendHoverLink:!0,colorBy:"data",center:["50%","50%"],radius:[0,"75%"],clockwise:!0,startAngle:90,minAngle:0,minShowLabelAngle:0,selectedOffset:10,percentPrecision:2,stillShowZeroSum:!0,left:0,top:0,right:0,bottom:0,width:null,height:null,label:{rotate:0,show:!0,overflow:"truncate",position:"outer",alignTo:"none",edgeDistance:"25%",bleedMargin:10,distanceToLabelLine:5},labelLine:{show:!0,length:15,length2:15,smooth:!1,minTurnAngle:90,maxSurfaceAngle:90,lineStyle:{width:1,type:"solid"}},itemStyle:{borderWidth:1,borderJoin:"round"},showEmptyCircle:!0,emptyCircleStyle:{color:"lightgray",opacity:1},labelLayout:{hideOverlap:!0},emphasis:{scale:!0,scaleSize:5},avoidLabelOverlap:!0,animationType:"expansion",animationDuration:1e3,animationTypeUpdate:"transition",animationEasingUpdate:"cubicInOut",animationDurationUpdate:500,animationEasing:"cubicInOut"},e}(fg);var bM=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.hasSymbolVisual=!0,n}return n(e,t),e.prototype.getInitialData=function(t,e){return hx(null,this,{useEncodeDefaulter:!0})},e.prototype.getProgressive=function(){var t=this.option.progressive;return null==t?this.option.large?5e3:this.get("progressive"):t},e.prototype.getProgressiveThreshold=function(){var t=this.option.progressiveThreshold;return null==t?this.option.large?1e4:this.get("progressiveThreshold"):t},e.prototype.brushSelector=function(t,e,n){return n.point(e.getItemLayout(t))},e.prototype.getZLevelKey=function(){return this.getData().count()>this.getProgressiveThreshold()?this.id:""},e.type="series.scatter",e.dependencies=["grid","polar","geo","singleAxis","calendar"],e.defaultOption={coordinateSystem:"cartesian2d",z:2,legendHoverLink:!0,symbolSize:10,large:!1,largeThreshold:2e3,itemStyle:{opacity:.8},emphasis:{scale:!0},clip:!0,select:{itemStyle:{borderColor:"#212121"}},universalTransition:{divideShape:"clone"}},e}(fg),wM=function(){},SM=function(t){function e(e){var n=t.call(this,e)||this;return n._off=0,n.hoverDataIdx=-1,n}return n(e,t),e.prototype.getDefaultShape=function(){return new wM},e.prototype.reset=function(){this.notClear=!1,this._off=0},e.prototype.buildPath=function(t,e){var n,i=e.points,r=e.size,o=this.symbolProxy,a=o.shape,s=t.getContext?t.getContext():t,l=s&&r[0]<4,u=this.softClipShape;if(l)this._ctx=s;else{for(this._ctx=null,n=this._off;n=0;s--){var l=2*s,u=i[l]-o/2,h=i[l+1]-a/2;if(t>=u&&e>=h&&t<=u+o&&e<=h+a)return s}return-1},e.prototype.contain=function(t,e){var n=this.transformCoordToLocal(t,e),i=this.getBoundingRect();return t=n[0],e=n[1],i.contain(t,e)?(this.hoverDataIdx=this.findDataIndex(t,e))>=0:(this.hoverDataIdx=-1,!1)},e.prototype.getBoundingRect=function(){var t=this._rect;if(!t){for(var e=this.shape,n=e.points,i=e.size,r=i[0],o=i[1],a=1/0,s=1/0,l=-1/0,u=-1/0,h=0;h=0&&(l.dataIndex=n+(t.startIndex||0))}))},t.prototype.remove=function(){this._clear()},t.prototype._clear=function(){this._newAdded=[],this.group.removeAll()},t}(),IM=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){var i=t.getData();this._updateSymbolDraw(i,t).updateData(i,{clipShape:this._getClipShape(t)}),this._finished=!0},e.prototype.incrementalPrepareRender=function(t,e,n){var i=t.getData();this._updateSymbolDraw(i,t).incrementalPrepareUpdate(i),this._finished=!1},e.prototype.incrementalRender=function(t,e,n){this._symbolDraw.incrementalUpdate(t,e.getData(),{clipShape:this._getClipShape(e)}),this._finished=t.end===e.getData().count()},e.prototype.updateTransform=function(t,e,n){var i=t.getData();if(this.group.dirty(),!this._finished||i.count()>1e4)return{update:!0};var r=AS("").reset(t,e,n);r.progress&&r.progress({start:0,end:i.count(),count:i.count()},i),this._symbolDraw.updateLayout(i)},e.prototype.eachRendered=function(t){this._symbolDraw&&this._symbolDraw.eachRendered(t)},e.prototype._getClipShape=function(t){var e=t.coordinateSystem,n=e&&e.getArea&&e.getArea();return t.get("clip",!0)?n:null},e.prototype._updateSymbolDraw=function(t,e){var n=this._symbolDraw,i=e.pipelineContext.large;return n&&i===this._isLargeDraw||(n&&n.remove(),n=this._symbolDraw=i?new MM:new iS,this._isLargeDraw=i,this.group.removeAll()),this.group.add(n.group),n},e.prototype.remove=function(t,e){this._symbolDraw&&this._symbolDraw.remove(!0),this._symbolDraw=null},e.prototype.dispose=function(){},e.type="scatter",e}(Tg),TM=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.type="grid",e.dependencies=["xAxis","yAxis"],e.layoutMode="box",e.defaultOption={show:!1,z:0,left:"10%",top:60,right:"10%",bottom:70,containLabel:!1,backgroundColor:"rgba(0,0,0,0)",borderWidth:1,borderColor:"#ccc"},e}(Op),CM=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.getCoordSysModel=function(){return this.getReferringComponents("grid",Eo).models[0]},e.type="cartesian2dAxis",e}(Op);R(CM,m_);var DM={show:!0,z:0,inverse:!1,name:"",nameLocation:"end",nameRotate:null,nameTruncate:{maxWidth:null,ellipsis:"...",placeholder:"."},nameTextStyle:{},nameGap:15,silent:!1,triggerEvent:!1,tooltip:{show:!1},axisPointer:{},axisLine:{show:!0,onZero:!0,onZeroAxisIndex:null,lineStyle:{color:"#6E7079",width:1,type:"solid"},symbol:["none","none"],symbolSize:[10,15]},axisTick:{show:!0,inside:!1,length:5,lineStyle:{width:1}},axisLabel:{show:!0,inside:!1,rotate:0,showMinLabel:null,showMaxLabel:null,margin:8,fontSize:12},splitLine:{show:!0,lineStyle:{color:["#E0E6F1"],width:1,type:"solid"}},splitArea:{show:!1,areaStyle:{color:["rgba(250,250,250,0.2)","rgba(210,219,238,0.2)"]}}},AM=C({boundaryGap:!0,deduplication:null,splitLine:{show:!1},axisTick:{alignWithLabel:!1,interval:"auto"},axisLabel:{interval:"auto"}},DM),kM=C({boundaryGap:[0,0],axisLine:{show:"auto"},axisTick:{show:"auto"},splitNumber:5,minorTick:{show:!1,splitNumber:5,length:3,lineStyle:{}},minorSplitLine:{show:!1,lineStyle:{color:"#F4F7FD",width:1}}},DM),LM={category:AM,value:kM,time:C({splitNumber:6,axisLabel:{showMinLabel:!1,showMaxLabel:!1,rich:{primary:{fontWeight:"bold"}}},splitLine:{show:!1}},kM),log:k({logBase:10},kM)},PM={value:1,category:1,time:1,log:1};function OM(t,e,i,r){E(PM,(function(o,a){var s=C(C({},LM[a],!0),r,!0),l=function(t){function i(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e+"Axis."+a,n}return n(i,t),i.prototype.mergeDefaultAndTheme=function(t,e){var n=Dp(this),i=n?kp(t):{};C(t,e.getTheme().get(a+"Axis")),C(t,this.getDefaultOption()),t.type=RM(t),n&&Ap(t,i,n)},i.prototype.optionUpdated=function(){"category"===this.option.type&&(this.__ordinalMeta=dx.createByAxisModel(this))},i.prototype.getCategories=function(t){var e=this.option;if("category"===e.type)return t?e.data:this.__ordinalMeta.categories},i.prototype.getOrdinalMeta=function(){return this.__ordinalMeta},i.type=e+"Axis."+a,i.defaultOption=s,i}(i);t.registerComponentModel(l)})),t.registerSubTypeDefaulter(e+"Axis",RM)}function RM(t){return t.type||(t.data?"category":"value")}var NM=function(){function t(t){this.type="cartesian",this._dimList=[],this._axes={},this.name=t||""}return t.prototype.getAxis=function(t){return this._axes[t]},t.prototype.getAxes=function(){return z(this._dimList,(function(t){return this._axes[t]}),this)},t.prototype.getAxesByScale=function(t){return t=t.toLowerCase(),B(this.getAxes(),(function(e){return e.scale.type===t}))},t.prototype.addAxis=function(t){var e=t.dim;this._axes[e]=t,this._dimList.push(e)},t}(),EM=["x","y"];function zM(t){return"interval"===t.type||"time"===t.type}var VM=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.type="cartesian2d",e.dimensions=EM,e}return n(e,t),e.prototype.calcAffineTransform=function(){this._transform=this._invTransform=null;var t=this.getAxis("x").scale,e=this.getAxis("y").scale;if(zM(t)&&zM(e)){var n=t.getExtent(),i=e.getExtent(),r=this.dataToPoint([n[0],i[0]]),o=this.dataToPoint([n[1],i[1]]),a=n[1]-n[0],s=i[1]-i[0];if(a&&s){var l=(o[0]-r[0])/a,u=(o[1]-r[1])/s,h=r[0]-n[0]*l,c=r[1]-i[0]*u,p=this._transform=[l,0,0,u,h,c];this._invTransform=Me([],p)}}},e.prototype.getBaseAxis=function(){return this.getAxesByScale("ordinal")[0]||this.getAxesByScale("time")[0]||this.getAxis("x")},e.prototype.containPoint=function(t){var e=this.getAxis("x"),n=this.getAxis("y");return e.contain(e.toLocalCoord(t[0]))&&n.contain(n.toLocalCoord(t[1]))},e.prototype.containData=function(t){return this.getAxis("x").containData(t[0])&&this.getAxis("y").containData(t[1])},e.prototype.containZone=function(t,e){var n=this.dataToPoint(t),i=this.dataToPoint(e),r=this.getArea(),o=new Ee(n[0],n[1],i[0]-n[0],i[1]-n[1]);return r.intersect(o)},e.prototype.dataToPoint=function(t,e,n){n=n||[];var i=t[0],r=t[1];if(this._transform&&null!=i&&isFinite(i)&&null!=r&&isFinite(r))return Wt(n,t,this._transform);var o=this.getAxis("x"),a=this.getAxis("y");return n[0]=o.toGlobalCoord(o.dataToCoord(i,e)),n[1]=a.toGlobalCoord(a.dataToCoord(r,e)),n},e.prototype.clampData=function(t,e){var n=this.getAxis("x").scale,i=this.getAxis("y").scale,r=n.getExtent(),o=i.getExtent(),a=n.parse(t[0]),s=i.parse(t[1]);return(e=e||[])[0]=Math.min(Math.max(Math.min(r[0],r[1]),a),Math.max(r[0],r[1])),e[1]=Math.min(Math.max(Math.min(o[0],o[1]),s),Math.max(o[0],o[1])),e},e.prototype.pointToData=function(t,e){var n=[];if(this._invTransform)return Wt(n,t,this._invTransform);var i=this.getAxis("x"),r=this.getAxis("y");return n[0]=i.coordToData(i.toLocalCoord(t[0]),e),n[1]=r.coordToData(r.toLocalCoord(t[1]),e),n},e.prototype.getOtherAxis=function(t){return this.getAxis("x"===t.dim?"y":"x")},e.prototype.getArea=function(){var t=this.getAxis("x").getGlobalExtent(),e=this.getAxis("y").getGlobalExtent(),n=Math.min(t[0],t[1]),i=Math.min(e[0],e[1]),r=Math.max(t[0],t[1])-n,o=Math.max(e[0],e[1])-i;return new Ee(n,i,r,o)},e}(NM),BM=function(t){function e(e,n,i,r,o){var a=t.call(this,e,n,i)||this;return a.index=0,a.type=r||"value",a.position=o||"bottom",a}return n(e,t),e.prototype.isHorizontal=function(){var t=this.position;return"top"===t||"bottom"===t},e.prototype.getGlobalExtent=function(t){var e=this.getExtent();return e[0]=this.toGlobalCoord(e[0]),e[1]=this.toGlobalCoord(e[1]),t&&e[0]>e[1]&&e.reverse(),e},e.prototype.pointToData=function(t,e){return this.coordToData(this.toLocalCoord(t["x"===this.dim?0:1]),e)},e.prototype.setCategorySortInfo=function(t){if("category"!==this.type)return!1;this.model.option.categorySortInfo=t,this.scale.setSortInfo(t)},e}(q_);function FM(t,e,n){n=n||{};var i=t.coordinateSystem,r=e.axis,o={},a=r.getAxesOnZeroOf()[0],s=r.position,l=a?"onZero":s,u=r.dim,h=i.getRect(),c=[h.x,h.x+h.width,h.y,h.y+h.height],p={left:0,right:1,top:0,bottom:1,onZero:2},d=e.get("offset")||0,f="x"===u?[c[2]-d,c[3]+d]:[c[0]-d,c[1]+d];if(a){var g=a.toGlobalCoord(a.dataToCoord(0));f[p.onZero]=Math.max(Math.min(g,f[1]),f[0])}o.position=["y"===u?f[p[l]]:c[0],"x"===u?f[p[l]]:c[3]],o.rotation=Math.PI/2*("x"===u?0:1);o.labelDirection=o.tickDirection=o.nameDirection={top:-1,bottom:1,left:-1,right:1}[s],o.labelOffset=a?f[p[s]]-f[p.onZero]:0,e.get(["axisTick","inside"])&&(o.tickDirection=-o.tickDirection),it(n.labelInside,e.get(["axisLabel","inside"]))&&(o.labelDirection=-o.labelDirection);var y=e.get(["axisLabel","rotate"]);return o.labelRotate="top"===l?-y:y,o.z2=1,o}function GM(t){return"cartesian2d"===t.get("coordinateSystem")}function WM(t){var e={xAxisModel:null,yAxisModel:null};return E(e,(function(n,i){var r=i.replace(/Model$/,""),o=t.getReferringComponents(r,Eo).models[0];e[i]=o})),e}var HM=Math.log;function YM(t,e,n){var i=Tx.prototype,r=i.getTicks.call(n),o=i.getTicks.call(n,!0),a=r.length-1,s=i.getInterval.call(n),l=u_(t,e),u=l.extent,h=l.fixMin,c=l.fixMax;if("log"===t.type){var p=HM(t.base);u=[HM(u[0])/p,HM(u[1])/p]}t.setExtent(u[0],u[1]),t.calcNiceExtent({splitNumber:a,fixMin:h,fixMax:c});var d=i.getExtent.call(t);h&&(u[0]=d[0]),c&&(u[1]=d[1]);var f=i.getInterval.call(t),g=u[0],y=u[1];if(h&&c)f=(y-g)/a;else if(h)for(y=u[0]+f*a;yu[0]&&isFinite(g)&&isFinite(u[0]);)f=vx(f),g=u[1]-f*a;else{t.getTicks().length-1>a&&(f=vx(f));var v=f*a;(g=Xr((y=Math.ceil(u[1]/f)*f)-v))<0&&u[0]>=0?(g=0,y=Xr(v)):y>0&&u[1]<=0&&(y=0,g=-Xr(v))}var m=(r[0].value-o[0].value)/s,x=(r[a].value-o[a].value)/s;i.setExtent.call(t,g+f*m,y+f*x),i.setInterval.call(t,f),(m||x)&&i.setNiceExtent.call(t,g+f,y-f)}var UM=function(){function t(t,e,n){this.type="grid",this._coordsMap={},this._coordsList=[],this._axesMap={},this._axesList=[],this.axisPointerEnabled=!0,this.dimensions=EM,this._initCartesian(t,e,n),this.model=t}return t.prototype.getRect=function(){return this._rect},t.prototype.update=function(t,e){var n=this._axesMap;function i(t){var e,n=G(t),i=n.length;if(i){for(var r=[],o=i-1;o>=0;o--){var a=t[+n[o]],s=a.model,l=a.scale;gx(l)&&s.get("alignTicks")&&null==s.get("interval")?r.push(a):(h_(l,s),gx(l)&&(e=a))}r.length&&(e||h_((e=r.pop()).scale,e.model),E(r,(function(t){YM(t.scale,t.model,e.scale)})))}}this._updateScale(t,this.model),i(n.x),i(n.y);var r={};E(n.x,(function(t){ZM(n,"y",t,r)})),E(n.y,(function(t){ZM(n,"x",t,r)})),this.resize(this.model,e)},t.prototype.resize=function(t,e,n){var i=t.getBoxLayoutParams(),r=!n&&t.get("containLabel"),o=Tp(i,{width:e.getWidth(),height:e.getHeight()});this._rect=o;var a=this._axesList;function s(){E(a,(function(t){var e=t.isHorizontal(),n=e?[0,o.width]:[0,o.height],i=t.inverse?1:0;t.setExtent(n[i],n[1-i]),function(t,e){var n=t.getExtent(),i=n[0]+n[1];t.toGlobalCoord="x"===t.dim?function(t){return t+e}:function(t){return i-t+e},t.toLocalCoord="x"===t.dim?function(t){return t-e}:function(t){return i-t+e}}(t,e?o.x:o.y)}))}s(),r&&(E(a,(function(t){if(!t.model.get(["axisLabel","inside"])){var e=function(t){var e=t.model,n=t.scale;if(e.get(["axisLabel","show"])&&!n.isBlank()){var i,r,o=n.getExtent();r=n instanceof Mx?n.count():(i=n.getTicks()).length;var a,s=t.getLabelModel(),l=p_(t),u=1;r>40&&(u=Math.ceil(r/40));for(var h=0;h0&&i>0||n<0&&i<0)}(t)}var qM=Math.PI,KM=function(){function t(t,e){this.group=new Er,this.opt=e,this.axisModel=t,k(e,{labelOffset:0,nameDirection:1,tickDirection:1,labelDirection:1,silent:!0,handleAutoShown:function(){return!0}});var n=new Er({x:e.position[0],y:e.position[1],rotation:e.rotation});n.updateTransform(),this._transformGroup=n}return t.prototype.hasBuilder=function(t){return!!$M[t]},t.prototype.add=function(t){$M[t](this.opt,this.axisModel,this.group,this._transformGroup)},t.prototype.getGroup=function(){return this.group},t.innerTextLayout=function(t,e,n){var i,r,o=to(e-t);return eo(o)?(r=n>0?"top":"bottom",i="center"):eo(o-qM)?(r=n>0?"bottom":"top",i="center"):(r="middle",i=o>0&&o0?"right":"left":n>0?"left":"right"),{rotation:o,textAlign:i,textVerticalAlign:r}},t.makeAxisEventDataBase=function(t){var e={componentType:t.mainType,componentIndex:t.componentIndex};return e[t.mainType+"Index"]=t.componentIndex,e},t.isLabelSilent=function(t){var e=t.get("tooltip");return t.get("silent")||!(t.get("triggerEvent")||e&&e.show)},t}(),$M={axisLine:function(t,e,n,i){var r=e.get(["axisLine","show"]);if("auto"===r&&t.handleAutoShown&&(r=t.handleAutoShown("axisLine")),r){var o=e.axis.getExtent(),a=i.transform,s=[o[0],0],l=[o[1],0],u=s[0]>l[0];a&&(Wt(s,s,a),Wt(l,l,a));var h=A({lineCap:"round"},e.getModel(["axisLine","lineStyle"]).getLineStyle()),c=new Xu({shape:{x1:s[0],y1:s[1],x2:l[0],y2:l[1]},style:h,strokeContainThreshold:t.strokeContainThreshold||5,silent:!0,z2:1});Oh(c.shape,c.style.lineWidth),c.anid="line",n.add(c);var p=e.get(["axisLine","symbol"]);if(null!=p){var d=e.get(["axisLine","symbolSize"]);X(p)&&(p=[p,p]),(X(d)||j(d))&&(d=[d,d]);var f=Fy(e.get(["axisLine","symbolOffset"])||0,d),g=d[0],y=d[1];E([{rotate:t.rotation+Math.PI/2,offset:f[0],r:0},{rotate:t.rotation-Math.PI/2,offset:f[1],r:Math.sqrt((s[0]-l[0])*(s[0]-l[0])+(s[1]-l[1])*(s[1]-l[1]))}],(function(e,i){if("none"!==p[i]&&null!=p[i]){var r=Vy(p[i],-g/2,-y/2,g,y,h.stroke,!0),o=e.r+e.offset,a=u?l:s;r.attr({rotation:e.rotate,x:a[0]+o*Math.cos(t.rotation),y:a[1]-o*Math.sin(t.rotation),silent:!0,z2:11}),n.add(r)}}))}}},axisTickLabel:function(t,e,n,i){var r=function(t,e,n,i){var r=n.axis,o=n.getModel("axisTick"),a=o.get("show");"auto"===a&&i.handleAutoShown&&(a=i.handleAutoShown("axisTick"));if(!a||r.scale.isBlank())return;for(var s=o.getModel("lineStyle"),l=i.tickDirection*o.get("length"),u=eI(r.getTicksCoords(),e.transform,l,k(s.getLineStyle(),{stroke:n.get(["axisLine","lineStyle","color"])}),"ticks"),h=0;hc[1]?-1:1,d=["start"===s?c[0]-p*h:"end"===s?c[1]+p*h:(c[0]+c[1])/2,tI(s)?t.labelOffset+l*h:0],f=e.get("nameRotate");null!=f&&(f=f*qM/180),tI(s)?o=KM.innerTextLayout(t.rotation,null!=f?f:t.rotation,l):(o=function(t,e,n,i){var r,o,a=to(n-t),s=i[0]>i[1],l="start"===e&&!s||"start"!==e&&s;eo(a-qM/2)?(o=l?"bottom":"top",r="center"):eo(a-1.5*qM)?(o=l?"top":"bottom",r="center"):(o="middle",r=a<1.5*qM&&a>qM/2?l?"left":"right":l?"right":"left");return{rotation:a,textAlign:r,textVerticalAlign:o}}(t.rotation,s,f||0,c),null!=(a=t.axisNameAvailableWidth)&&(a=Math.abs(a/Math.sin(o.rotation)),!isFinite(a)&&(a=null)));var g=u.getFont(),y=e.get("nameTruncate",!0)||{},v=y.ellipsis,m=it(t.nameTruncateMaxWidth,y.maxWidth,a),x=new Bs({x:d[0],y:d[1],rotation:o.rotation,silent:KM.isLabelSilent(e),style:ec(u,{text:r,font:g,overflow:"truncate",width:m,ellipsis:v,fill:u.getTextColor()||e.get(["axisLine","lineStyle","color"]),align:u.get("align")||o.textAlign,verticalAlign:u.get("verticalAlign")||o.textVerticalAlign}),z2:1});if(Xh({el:x,componentModel:e,itemName:r}),x.__fullText=r,x.anid="name",e.get("triggerEvent")){var _=KM.makeAxisEventDataBase(e);_.targetType="axisName",_.name=r,Js(x).eventData=_}i.add(x),x.updateTransform(),n.add(x),x.decomposeTransform()}}};function JM(t){t&&(t.ignore=!0)}function QM(t,e){var n=t&&t.getBoundingRect().clone(),i=e&&e.getBoundingRect().clone();if(n&&i){var r=me([]);return we(r,r,-t.rotation),n.applyTransform(_e([],r,t.getLocalTransform())),i.applyTransform(_e([],r,e.getLocalTransform())),n.intersect(i)}}function tI(t){return"middle"===t||"center"===t}function eI(t,e,n,i,r){for(var o=[],a=[],s=[],l=0;l=0||t===e}function rI(t){var e=oI(t);if(e){var n=e.axisPointerModel,i=e.axis.scale,r=n.option,o=n.get("status"),a=n.get("value");null!=a&&(a=i.parse(a));var s=aI(n);null==o&&(r.status=s?"show":"hide");var l=i.getExtent().slice();l[0]>l[1]&&l.reverse(),(null==a||a>l[1])&&(a=l[1]),a0&&!c.min?c.min=0:null!=c.min&&c.min<0&&!c.max&&(c.max=0);var p=a;null!=c.color&&(p=k({color:c.color},a));var d=C(T(c),{boundaryGap:t,splitNumber:e,scale:n,axisLine:i,axisTick:r,axisLabel:o,name:c.text,showName:s,nameLocation:"end",nameGap:u,nameTextStyle:p,triggerEvent:h},!1);if(X(l)){var f=d.name;d.name=l.replace("{value}",null!=f?f:"")}else U(l)&&(d.name=l(d.name,d));var g=new Sc(d,null,this.ecModel);return R(g,m_.prototype),g.mainType="radar",g.componentIndex=this.componentIndex,g}),this);this._indicatorModels=c},e.prototype.getIndicatorModels=function(){return this._indicatorModels},e.type="radar",e.defaultOption={z:0,center:["50%","50%"],radius:"75%",startAngle:90,axisName:{show:!0},boundaryGap:[0,0],splitNumber:5,axisNameGap:15,scale:!1,shape:"polygon",axisLine:C({lineStyle:{color:"#bbb"}},DI.axisLine),axisLabel:AI(DI.axisLabel,!1),axisTick:AI(DI.axisTick,!1),splitLine:AI(DI.splitLine,!0),splitArea:AI(DI.splitArea,!0),indicator:[]},e}(Op),LI=["axisLine","axisTickLabel","axisName"],PI=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){this.group.removeAll(),this._buildAxes(t),this._buildSplitLineAndArea(t)},e.prototype._buildAxes=function(t){var e=t.coordinateSystem;E(z(e.getIndicatorAxes(),(function(t){var n=t.model.get("showName")?t.name:"";return new KM(t.model,{axisName:n,position:[e.cx,e.cy],rotation:t.angle,labelDirection:-1,tickDirection:-1,nameDirection:1})})),(function(t){E(LI,t.add,t),this.group.add(t.getGroup())}),this)},e.prototype._buildSplitLineAndArea=function(t){var e=t.coordinateSystem,n=e.getIndicatorAxes();if(n.length){var i=t.get("shape"),r=t.getModel("splitLine"),o=t.getModel("splitArea"),a=r.getModel("lineStyle"),s=o.getModel("areaStyle"),l=r.get("show"),u=o.get("show"),h=a.get("color"),c=s.get("color"),p=Y(h)?h:[h],d=Y(c)?c:[c],f=[],g=[];if("circle"===i)for(var y=n[0].getTicksCoords(),v=e.cx,m=e.cy,x=0;x3?1.4:r>1?1.2:1.1;FI(this,"zoom","zoomOnMouseWheel",t,{scale:i>0?s:1/s,originX:o,originY:a,isAvailableBehavior:null})}if(n){var l=Math.abs(i);FI(this,"scrollMove","moveOnMouseWheel",t,{scrollDelta:(i>0?1:-1)*(l>3?.4:l>1?.15:.05),originX:o,originY:a,isAvailableBehavior:null})}}},e.prototype._pinchHandler=function(t){zI(this._zr,"globalPan")||FI(this,"zoom",null,t,{scale:t.pinchScale>1?1.1:1/1.1,originX:t.pinchX,originY:t.pinchY,isAvailableBehavior:null})},e}(jt);function FI(t,e,n,i,r){t.pointerChecker&&t.pointerChecker(i,r.originX,r.originY)&&(pe(i.event),GI(t,e,n,i,r))}function GI(t,e,n,i,r){r.isAvailableBehavior=W(WI,null,n,i),t.trigger(e,r)}function WI(t,e,n){var i=n[t];return!t||i&&(!X(i)||e.event[i+"Key"])}function HI(t,e,n){var i=t.target;i.x+=e,i.y+=n,i.dirty()}function YI(t,e,n,i){var r=t.target,o=t.zoomLimit,a=t.zoom=t.zoom||1;if(a*=e,o){var s=o.min||0,l=o.max||1/0;a=Math.max(Math.min(l,a),s)}var u=a/t.zoom;t.zoom=a,r.x-=(n-r.x)*(u-1),r.y-=(i-r.y)*(u-1),r.scaleX*=u,r.scaleY*=u,r.dirty()}var UI,XI={axisPointer:1,tooltip:1,brush:1};function ZI(t,e,n){var i=e.getComponentByElement(t.topTarget),r=i&&i.coordinateSystem;return i&&i!==n&&!XI.hasOwnProperty(i.mainType)&&r&&r.model!==n}function jI(t){X(t)&&(t=(new DOMParser).parseFromString(t,"text/xml"));var e=t;for(9===e.nodeType&&(e=e.firstChild);"svg"!==e.nodeName.toLowerCase()||1!==e.nodeType;)e=e.nextSibling;return e}var qI={fill:"fill",stroke:"stroke","stroke-width":"lineWidth",opacity:"opacity","fill-opacity":"fillOpacity","stroke-opacity":"strokeOpacity","stroke-dasharray":"lineDash","stroke-dashoffset":"lineDashOffset","stroke-linecap":"lineCap","stroke-linejoin":"lineJoin","stroke-miterlimit":"miterLimit","font-family":"fontFamily","font-size":"fontSize","font-style":"fontStyle","font-weight":"fontWeight","text-anchor":"textAlign",visibility:"visibility",display:"display"},KI=G(qI),$I={"alignment-baseline":"textBaseline","stop-color":"stopColor"},JI=G($I),QI=function(){function t(){this._defs={},this._root=null}return t.prototype.parse=function(t,e){e=e||{};var n=jI(t);this._defsUsePending=[];var i=new Er;this._root=i;var r=[],o=n.getAttribute("viewBox")||"",a=parseFloat(n.getAttribute("width")||e.width),s=parseFloat(n.getAttribute("height")||e.height);isNaN(a)&&(a=null),isNaN(s)&&(s=null),oT(n,i,null,!0,!1);for(var l,u,h=n.firstChild;h;)this._parseNode(h,i,r,null,!1,!1),h=h.nextSibling;if(function(t,e){for(var n=0;n=4&&(l={x:parseFloat(c[0]||0),y:parseFloat(c[1]||0),width:parseFloat(c[2]),height:parseFloat(c[3])})}if(l&&null!=a&&null!=s&&(u=fT(l,{x:0,y:0,width:a,height:s}),!e.ignoreViewBox)){var p=i;(i=new Er).add(p),p.scaleX=p.scaleY=u.scale,p.x=u.x,p.y=u.y}return e.ignoreRootClip||null==a||null==s||i.setClipPath(new Es({shape:{x:0,y:0,width:a,height:s}})),{root:i,width:a,height:s,viewBoxRect:l,viewBoxTransform:u,named:r}},t.prototype._parseNode=function(t,e,n,i,r,o){var a,s=t.nodeName.toLowerCase(),l=i;if("defs"===s&&(r=!0),"text"===s&&(o=!0),"defs"===s||"switch"===s)a=e;else{if(!r){var u=UI[s];if(u&&_t(UI,s)){a=u.call(this,t,e);var h=t.getAttribute("name");if(h){var c={name:h,namedFrom:null,svgNodeTagLower:s,el:a};n.push(c),"g"===s&&(l=c)}else i&&n.push({name:i.name,namedFrom:i,svgNodeTagLower:s,el:a});e.add(a)}}var p=tT[s];if(p&&_t(tT,s)){var d=p.call(this,t),f=t.getAttribute("id");f&&(this._defs[f]=d)}}if(a&&a.isGroup)for(var g=t.firstChild;g;)1===g.nodeType?this._parseNode(g,a,n,l,r,o):3===g.nodeType&&o&&this._parseText(g,a),g=g.nextSibling},t.prototype._parseText=function(t,e){var n=new Ts({style:{text:t.textContent},silent:!0,x:this._textX||0,y:this._textY||0});iT(e,n),oT(t,n,this._defsUsePending,!1,!1),function(t,e){var n=e.__selfStyle;if(n){var i=n.textBaseline,r=i;i&&"auto"!==i?"baseline"===i?r="alphabetic":"before-edge"===i||"text-before-edge"===i?r="top":"after-edge"===i||"text-after-edge"===i?r="bottom":"central"!==i&&"mathematical"!==i||(r="middle"):r="alphabetic",t.style.textBaseline=r}var o=e.__inheritedStyle;if(o){var a=o.textAlign,s=a;a&&("middle"===a&&(s="center"),t.style.textAlign=s)}}(n,e);var i=n.style,r=i.fontSize;r&&r<9&&(i.fontSize=9,n.scaleX*=r/9,n.scaleY*=r/9);var o=(i.fontSize||i.fontFamily)&&[i.fontStyle,i.fontWeight,(i.fontSize||12)+"px",i.fontFamily||"sans-serif"].join(" ");i.font=o;var a=n.getBoundingRect();return this._textX+=a.width,e.add(n),n},t.internalField=void(UI={g:function(t,e){var n=new Er;return iT(e,n),oT(t,n,this._defsUsePending,!1,!1),n},rect:function(t,e){var n=new Es;return iT(e,n),oT(t,n,this._defsUsePending,!1,!1),n.setShape({x:parseFloat(t.getAttribute("x")||"0"),y:parseFloat(t.getAttribute("y")||"0"),width:parseFloat(t.getAttribute("width")||"0"),height:parseFloat(t.getAttribute("height")||"0")}),n.silent=!0,n},circle:function(t,e){var n=new xu;return iT(e,n),oT(t,n,this._defsUsePending,!1,!1),n.setShape({cx:parseFloat(t.getAttribute("cx")||"0"),cy:parseFloat(t.getAttribute("cy")||"0"),r:parseFloat(t.getAttribute("r")||"0")}),n.silent=!0,n},line:function(t,e){var n=new Xu;return iT(e,n),oT(t,n,this._defsUsePending,!1,!1),n.setShape({x1:parseFloat(t.getAttribute("x1")||"0"),y1:parseFloat(t.getAttribute("y1")||"0"),x2:parseFloat(t.getAttribute("x2")||"0"),y2:parseFloat(t.getAttribute("y2")||"0")}),n.silent=!0,n},ellipse:function(t,e){var n=new bu;return iT(e,n),oT(t,n,this._defsUsePending,!1,!1),n.setShape({cx:parseFloat(t.getAttribute("cx")||"0"),cy:parseFloat(t.getAttribute("cy")||"0"),rx:parseFloat(t.getAttribute("rx")||"0"),ry:parseFloat(t.getAttribute("ry")||"0")}),n.silent=!0,n},polygon:function(t,e){var n,i=t.getAttribute("points");i&&(n=rT(i));var r=new Gu({shape:{points:n||[]},silent:!0});return iT(e,r),oT(t,r,this._defsUsePending,!1,!1),r},polyline:function(t,e){var n,i=t.getAttribute("points");i&&(n=rT(i));var r=new Hu({shape:{points:n||[]},silent:!0});return iT(e,r),oT(t,r,this._defsUsePending,!1,!1),r},image:function(t,e){var n=new As;return iT(e,n),oT(t,n,this._defsUsePending,!1,!1),n.setStyle({image:t.getAttribute("xlink:href")||t.getAttribute("href"),x:+t.getAttribute("x"),y:+t.getAttribute("y"),width:+t.getAttribute("width"),height:+t.getAttribute("height")}),n.silent=!0,n},text:function(t,e){var n=t.getAttribute("x")||"0",i=t.getAttribute("y")||"0",r=t.getAttribute("dx")||"0",o=t.getAttribute("dy")||"0";this._textX=parseFloat(n)+parseFloat(r),this._textY=parseFloat(i)+parseFloat(o);var a=new Er;return iT(e,a),oT(t,a,this._defsUsePending,!1,!0),a},tspan:function(t,e){var n=t.getAttribute("x"),i=t.getAttribute("y");null!=n&&(this._textX=parseFloat(n)),null!=i&&(this._textY=parseFloat(i));var r=t.getAttribute("dx")||"0",o=t.getAttribute("dy")||"0",a=new Er;return iT(e,a),oT(t,a,this._defsUsePending,!1,!0),this._textX+=parseFloat(r),this._textY+=parseFloat(o),a},path:function(t,e){var n=yu(t.getAttribute("d")||"");return iT(e,n),oT(t,n,this._defsUsePending,!1,!1),n.silent=!0,n}}),t}(),tT={lineargradient:function(t){var e=parseInt(t.getAttribute("x1")||"0",10),n=parseInt(t.getAttribute("y1")||"0",10),i=parseInt(t.getAttribute("x2")||"10",10),r=parseInt(t.getAttribute("y2")||"0",10),o=new eh(e,n,i,r);return eT(t,o),nT(t,o),o},radialgradient:function(t){var e=parseInt(t.getAttribute("cx")||"0",10),n=parseInt(t.getAttribute("cy")||"0",10),i=parseInt(t.getAttribute("r")||"0",10),r=new nh(e,n,i);return eT(t,r),nT(t,r),r}};function eT(t,e){"userSpaceOnUse"===t.getAttribute("gradientUnits")&&(e.global=!0)}function nT(t,e){for(var n=t.firstChild;n;){if(1===n.nodeType&&"stop"===n.nodeName.toLocaleLowerCase()){var i=n.getAttribute("offset"),r=void 0;r=i&&i.indexOf("%")>0?parseInt(i,10)/100:i?parseFloat(i):0;var o={};dT(n,o,o);var a=o.stopColor||n.getAttribute("stop-color")||"#000000";e.colorStops.push({offset:r,color:a})}n=n.nextSibling}}function iT(t,e){t&&t.__inheritedStyle&&(e.__inheritedStyle||(e.__inheritedStyle={}),k(e.__inheritedStyle,t.__inheritedStyle))}function rT(t){for(var e=uT(t),n=[],i=0;i0;o-=2){var a=i[o],s=i[o-1],l=uT(a);switch(r=r||[1,0,0,1,0,0],s){case"translate":be(r,r,[parseFloat(l[0]),parseFloat(l[1]||"0")]);break;case"scale":Se(r,r,[parseFloat(l[0]),parseFloat(l[1]||l[0])]);break;case"rotate":we(r,r,-parseFloat(l[0])*cT);break;case"skewX":_e(r,[1,0,Math.tan(parseFloat(l[0])*cT),1,0,0],r);break;case"skewY":_e(r,[1,Math.tan(parseFloat(l[0])*cT),0,1,0,0],r);break;case"matrix":r[0]=parseFloat(l[0]),r[1]=parseFloat(l[1]),r[2]=parseFloat(l[2]),r[3]=parseFloat(l[3]),r[4]=parseFloat(l[4]),r[5]=parseFloat(l[5])}}e.setLocalTransform(r)}}(t,e),dT(t,a,s),i||function(t,e,n){for(var i=0;i0,f={api:n,geo:s,mapOrGeoModel:t,data:a,isVisualEncodedByVisualMap:d,isGeo:o,transformInfoRaw:c};"geoJSON"===s.resourceType?this._buildGeoJSON(f):"geoSVG"===s.resourceType&&this._buildSVG(f),this._updateController(t,e,n),this._updateMapSelectHandler(t,l,n,i)},t.prototype._buildGeoJSON=function(t){var e=this._regionsGroupByName=yt(),n=yt(),i=this._regionsGroup,r=t.transformInfoRaw,o=t.mapOrGeoModel,a=t.data,s=t.geo.projection,l=s&&s.stream;function u(t,e){return e&&(t=e(t)),t&&[t[0]*r.scaleX+r.x,t[1]*r.scaleY+r.y]}function h(t){for(var e=[],n=!l&&s&&s.project,i=0;i=0)&&(p=r);var d=a?{normal:{align:"center",verticalAlign:"middle"}}:null;Qh(e,tc(i),{labelFetcher:p,labelDataIndex:c,defaultText:n},d);var f=e.getTextContent();if(f&&(NT(f).ignore=f.ignore,e.textConfig&&a)){var g=e.getBoundingRect().clone();e.textConfig.layoutRect=g,e.textConfig.position=[(a[0]-g.x)/g.width*100+"%",(a[1]-g.y)/g.height*100+"%"]}e.disableLabelAnimation=!0}else e.removeTextContent(),e.removeTextConfig(),e.disableLabelAnimation=null}function GT(t,e,n,i,r,o){t.data?t.data.setItemGraphicEl(o,e):Js(e).eventData={componentType:"geo",componentIndex:r.componentIndex,geoIndex:r.componentIndex,name:n,region:i&&i.option||{}}}function WT(t,e,n,i,r){t.data||Xh({el:e,componentModel:r,itemName:n,itemTooltipOption:i.get("tooltip")})}function HT(t,e,n,i,r){e.highDownSilentOnTouch=!!r.get("selectedMode");var o=i.getModel("emphasis"),a=o.get("focus");return Hl(e,a,o.get("blurScope"),o.get("disabled")),t.isGeo&&function(t,e,n){var i=Js(t);i.componentMainType=e.mainType,i.componentIndex=e.componentIndex,i.componentHighDownName=n}(e,r,n),a}function YT(t,e,n){var i,r=[];function o(){i=[]}function a(){i.length&&(r.push(i),i=[])}var s=e({polygonStart:o,polygonEnd:a,lineStart:o,lineEnd:a,point:function(t,e){isFinite(t)&&isFinite(e)&&i.push([t,e])},sphere:function(){}});return!n&&s.polygonStart(),E(t,(function(t){s.lineStart();for(var e=0;e-1&&(n.style.stroke=n.style.fill,n.style.fill="#fff",n.style.lineWidth=2),n},e.type="series.map",e.dependencies=["geo"],e.layoutMode="box",e.defaultOption={z:2,coordinateSystem:"geo",map:"",left:"center",top:"center",aspectScale:null,showLegendSymbol:!0,boundingCoords:null,center:null,zoom:1,scaleLimit:null,selectedMode:!0,label:{show:!1,color:"#000"},itemStyle:{borderWidth:.5,borderColor:"#444",areaColor:"#eee"},emphasis:{label:{show:!0,color:"rgb(100,0,0)"},itemStyle:{areaColor:"rgba(255,215,0,0.8)"}},select:{label:{show:!0,color:"rgb(100,0,0)"},itemStyle:{color:"rgba(255,215,0,0.8)"}},nameProperty:"name"},e}(fg);function ZT(t){var e={};t.eachSeriesByType("map",(function(t){var n=t.getHostGeoModel(),i=n?"o"+n.id:"i"+t.getMapType();(e[i]=e[i]||[]).push(t)})),E(e,(function(t,e){for(var n,i,r,o=(n=z(t,(function(t){return t.getData()})),i=t[0].get("mapValueCalculation"),r={},E(n,(function(t){t.each(t.mapDimension("value"),(function(e,n){var i="ec-"+t.getName(n);r[i]=r[i]||[],isNaN(e)||r[i].push(e)}))})),n[0].map(n[0].mapDimension("value"),(function(t,e){for(var o="ec-"+n[0].getName(e),a=0,s=1/0,l=-1/0,u=r[o].length,h=0;h1?(d.width=p,d.height=p/x):(d.height=p,d.width=p*x),d.y=c[1]-d.height/2,d.x=c[0]-d.width/2;else{var b=t.getBoxLayoutParams();b.aspect=x,d=Tp(b,{width:v,height:m})}this.setViewRect(d.x,d.y,d.width,d.height),this.setCenter(t.get("center"),e),this.setZoom(t.get("zoom"))}R(tC,KT);var iC=function(){function t(){this.dimensions=QT}return t.prototype.create=function(t,e){var n=[];function i(t){return{nameProperty:t.get("nameProperty"),aspectScale:t.get("aspectScale"),projection:t.get("projection")}}t.eachComponent("geo",(function(t,r){var o=t.get("map"),a=new tC(o+r,o,A({nameMap:t.get("nameMap")},i(t)));a.zoomLimit=t.get("scaleLimit"),n.push(a),t.coordinateSystem=a,a.model=t,a.resize=nC,a.resize(t,e)})),t.eachSeries((function(t){if("geo"===t.get("coordinateSystem")){var e=t.get("geoIndex")||0;t.coordinateSystem=n[e]}}));var r={};return t.eachSeriesByType("map",(function(t){if(!t.getHostGeoModel()){var e=t.getMapType();r[e]=r[e]||[],r[e].push(t)}})),E(r,(function(t,r){var o=z(t,(function(t){return t.get("nameMap")})),a=new tC(r,r,A({nameMap:D(o)},i(t[0])));a.zoomLimit=it.apply(null,z(t,(function(t){return t.get("scaleLimit")}))),n.push(a),a.resize=nC,a.resize(t[0],e),E(t,(function(t){t.coordinateSystem=a,function(t,e){E(e.get("geoCoord"),(function(e,n){t.addGeoCoord(n,e)}))}(a,t)}))})),n},t.prototype.getFilledRegions=function(t,e,n,i){for(var r=(t||[]).slice(),o=yt(),a=0;a=0;){var o=e[n];o.hierNode.prelim+=i,o.hierNode.modifier+=i,r+=o.hierNode.change,i+=o.hierNode.shift+r}}(t);var o=(n[0].hierNode.prelim+n[n.length-1].hierNode.prelim)/2;r?(t.hierNode.prelim=r.hierNode.prelim+e(t,r),t.hierNode.modifier=t.hierNode.prelim-o):t.hierNode.prelim=o}else r&&(t.hierNode.prelim=r.hierNode.prelim+e(t,r));t.parentNode.hierNode.defaultAncestor=function(t,e,n,i){if(e){for(var r=t,o=t,a=o.parentNode.children[0],s=e,l=r.hierNode.modifier,u=o.hierNode.modifier,h=a.hierNode.modifier,c=s.hierNode.modifier;s=gC(s),o=yC(o),s&&o;){r=gC(r),a=yC(a),r.hierNode.ancestor=t;var p=s.hierNode.prelim+c-o.hierNode.prelim-u+i(s,o);p>0&&(mC(vC(s,t,n),t,p),u+=p,l+=p),c+=s.hierNode.modifier,u+=o.hierNode.modifier,l+=r.hierNode.modifier,h+=a.hierNode.modifier}s&&!gC(r)&&(r.hierNode.thread=s,r.hierNode.modifier+=c-l),o&&!yC(a)&&(a.hierNode.thread=o,a.hierNode.modifier+=u-h,n=t)}return n}(t,r,t.parentNode.hierNode.defaultAncestor||i[0],e)}function pC(t){var e=t.hierNode.prelim+t.parentNode.hierNode.modifier;t.setLayout({x:e},!0),t.hierNode.modifier+=t.parentNode.hierNode.modifier}function dC(t){return arguments.length?t:xC}function fC(t,e){return t-=Math.PI/2,{x:e*Math.cos(t),y:e*Math.sin(t)}}function gC(t){var e=t.children;return e.length&&t.isExpand?e[e.length-1]:t.hierNode.thread}function yC(t){var e=t.children;return e.length&&t.isExpand?e[0]:t.hierNode.thread}function vC(t,e,n){return t.hierNode.ancestor.parentNode===e.parentNode?t.hierNode.ancestor:n}function mC(t,e,n){var i=n/(e.hierNode.i-t.hierNode.i);e.hierNode.change-=i,e.hierNode.shift+=n,e.hierNode.modifier+=n,e.hierNode.prelim+=n,t.hierNode.change+=i}function xC(t,e){return t.parentNode===e.parentNode?1:2}var _C=function(){this.parentPoint=[],this.childPoints=[]},bC=function(t){function e(e){return t.call(this,e)||this}return n(e,t),e.prototype.getDefaultStyle=function(){return{stroke:"#000",fill:null}},e.prototype.getDefaultShape=function(){return new _C},e.prototype.buildPath=function(t,e){var n=e.childPoints,i=n.length,r=e.parentPoint,o=n[0],a=n[i-1];if(1===i)return t.moveTo(r[0],r[1]),void t.lineTo(o[0],o[1]);var s=e.orient,l="TB"===s||"BT"===s?0:1,u=1-l,h=Ur(e.forkPosition,1),c=[];c[l]=r[l],c[u]=r[u]+(a[u]-r[u])*h,t.moveTo(r[0],r[1]),t.lineTo(c[0],c[1]),t.moveTo(o[0],o[1]),c[l]=o[l],t.lineTo(c[0],c[1]),c[l]=a[l],t.lineTo(c[0],c[1]),t.lineTo(a[0],a[1]);for(var p=1;pm.x)||(_-=Math.PI);var S=b?"left":"right",M=s.getModel("label"),I=M.get("rotate"),T=I*(Math.PI/180),C=y.getTextContent();C&&(y.setTextConfig({position:M.get("position")||S,rotation:null==I?-_:T,origin:"center"}),C.setStyle("verticalAlign","middle"))}var D=s.get(["emphasis","focus"]),A="relative"===D?vt(a.getAncestorsIndices(),a.getDescendantIndices()):"ancestor"===D?a.getAncestorsIndices():"descendant"===D?a.getDescendantIndices():null;A&&(Js(n).focus=A),function(t,e,n,i,r,o,a,s){var l=e.getModel(),u=t.get("edgeShape"),h=t.get("layout"),c=t.getOrient(),p=t.get(["lineStyle","curveness"]),d=t.get("edgeForkPosition"),f=l.getModel("lineStyle").getLineStyle(),g=i.__edge;if("curve"===u)e.parentNode&&e.parentNode!==n&&(g||(g=i.__edge=new Ku({shape:DC(h,c,p,r,r)})),dh(g,{shape:DC(h,c,p,o,a)},t));else if("polyline"===u)if("orthogonal"===h){if(e!==n&&e.children&&0!==e.children.length&&!0===e.isExpand){for(var y=e.children,v=[],m=0;me&&(e=i.height)}this.height=e+1},t.prototype.getNodeById=function(t){if(this.getId()===t)return this;for(var e=0,n=this.children,i=n.length;e=0&&this.hostTree.data.setItemLayout(this.dataIndex,t,e)},t.prototype.getLayout=function(){return this.hostTree.data.getItemLayout(this.dataIndex)},t.prototype.getModel=function(t){if(!(this.dataIndex<0))return this.hostTree.data.getItemModel(this.dataIndex).getModel(t)},t.prototype.getLevelModel=function(){return(this.hostTree.levelModels||[])[this.depth]},t.prototype.setVisual=function(t,e){this.dataIndex>=0&&this.hostTree.data.setItemVisual(this.dataIndex,t,e)},t.prototype.getVisual=function(t){return this.hostTree.data.getItemVisual(this.dataIndex,t)},t.prototype.getRawIndex=function(){return this.hostTree.data.getRawIndex(this.dataIndex)},t.prototype.getId=function(){return this.hostTree.data.getId(this.dataIndex)},t.prototype.getChildIndex=function(){if(this.parentNode){for(var t=this.parentNode.children,e=0;e=0){var i=n.getData().tree.root,r=t.targetNode;if(X(r)&&(r=i.getNodeById(r)),r&&i.contains(r))return{node:r};var o=t.targetNodeId;if(null!=o&&(r=i.getNodeById(o)))return{node:r}}}function GC(t){for(var e=[];t;)(t=t.parentNode)&&e.push(t);return e.reverse()}function WC(t,e){return P(GC(t),e)>=0}function HC(t,e){for(var n=[];t;){var i=t.dataIndex;n.push({name:t.name,dataIndex:i,value:e.getRawValue(i)}),t=t.parentNode}return n.reverse(),n}var YC=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.hasSymbolVisual=!0,e.ignoreStyleOnData=!0,e}return n(e,t),e.prototype.getInitialData=function(t){var e={name:t.name,children:t.data},n=t.leaves||{},i=new Sc(n,this,this.ecModel),r=BC.createTree(e,this,(function(t){t.wrapMethod("getItemModel",(function(t,e){var n=r.getNodeByDataIndex(e);return n&&n.children.length&&n.isExpand||(t.parentModel=i),t}))}));var o=0;r.eachNode("preorder",(function(t){t.depth>o&&(o=t.depth)}));var a=t.expandAndCollapse&&t.initialTreeDepth>=0?t.initialTreeDepth:o;return r.root.eachNode("preorder",(function(t){var e=t.hostTree.data.getRawDataItem(t.dataIndex);t.isExpand=e&&null!=e.collapsed?!e.collapsed:t.depth<=a})),r.data},e.prototype.getOrient=function(){var t=this.get("orient");return"horizontal"===t?t="LR":"vertical"===t&&(t="TB"),t},e.prototype.setZoom=function(t){this.option.zoom=t},e.prototype.setCenter=function(t){this.option.center=t},e.prototype.formatTooltip=function(t,e,n){for(var i=this.getData().tree,r=i.root.children[0],o=i.getNodeByDataIndex(t),a=o.getValue(),s=o.name;o&&o!==r;)s=o.parentNode.name+"."+s,o=o.parentNode;return Qf("nameValue",{name:s,value:a,noValue:isNaN(a)||null==a})},e.prototype.getDataParams=function(e){var n=t.prototype.getDataParams.apply(this,arguments),i=this.getData().tree.getNodeByDataIndex(e);return n.treeAncestors=HC(i,this),n.collapsed=!i.isExpand,n},e.type="series.tree",e.layoutMode="box",e.defaultOption={z:2,coordinateSystem:"view",left:"12%",top:"12%",right:"12%",bottom:"12%",layout:"orthogonal",edgeShape:"curve",edgeForkPosition:"50%",roam:!1,nodeScaleRatio:.4,center:null,zoom:1,orient:"LR",symbol:"emptyCircle",symbolSize:7,expandAndCollapse:!0,initialTreeDepth:2,lineStyle:{color:"#ccc",width:1.5,curveness:.5},itemStyle:{color:"lightsteelblue",borderWidth:1.5},label:{show:!0},animationEasing:"linear",animationDuration:700,animationDurationUpdate:500},e}(fg);function UC(t,e){for(var n,i=[t];n=i.pop();)if(e(n),n.isExpand){var r=n.children;if(r.length)for(var o=r.length-1;o>=0;o--)i.push(r[o])}}function XC(t,e){t.eachSeriesByType("tree",(function(t){!function(t,e){var n=function(t,e){return Tp(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()})}(t,e);t.layoutInfo=n;var i=t.get("layout"),r=0,o=0,a=null;"radial"===i?(r=2*Math.PI,o=Math.min(n.height,n.width)/2,a=dC((function(t,e){return(t.parentNode===e.parentNode?1:2)/t.depth}))):(r=n.width,o=n.height,a=dC());var s=t.getData().tree.root,l=s.children[0];if(l){!function(t){var e=t;e.hierNode={defaultAncestor:null,ancestor:e,prelim:0,modifier:0,change:0,shift:0,i:0,thread:null};for(var n,i,r=[e];n=r.pop();)if(i=n.children,n.isExpand&&i.length)for(var o=i.length-1;o>=0;o--){var a=i[o];a.hierNode={defaultAncestor:null,ancestor:a,prelim:0,modifier:0,change:0,shift:0,i:o,thread:null},r.push(a)}}(s),function(t,e,n){for(var i,r=[t],o=[];i=r.pop();)if(o.push(i),i.isExpand){var a=i.children;if(a.length)for(var s=0;sh.getLayout().x&&(h=t),t.depth>c.depth&&(c=t)}));var p=u===h?1:a(u,h)/2,d=p-u.getLayout().x,f=0,g=0,y=0,v=0;if("radial"===i)f=r/(h.getLayout().x+p+d),g=o/(c.depth-1||1),UC(l,(function(t){y=(t.getLayout().x+d)*f,v=(t.depth-1)*g;var e=fC(y,v);t.setLayout({x:e.x,y:e.y,rawX:y,rawY:v},!0)}));else{var m=t.getOrient();"RL"===m||"LR"===m?(g=o/(h.getLayout().x+p+d),f=r/(c.depth-1||1),UC(l,(function(t){v=(t.getLayout().x+d)*g,y="LR"===m?(t.depth-1)*f:r-(t.depth-1)*f,t.setLayout({x:y,y:v},!0)}))):"TB"!==m&&"BT"!==m||(f=r/(h.getLayout().x+p+d),g=o/(c.depth-1||1),UC(l,(function(t){y=(t.getLayout().x+d)*f,v="TB"===m?(t.depth-1)*g:o-(t.depth-1)*g,t.setLayout({x:y,y:v},!0)})))}}}(t,e)}))}function ZC(t){t.eachSeriesByType("tree",(function(t){var e=t.getData();e.tree.eachNode((function(t){var n=t.getModel().getModel("itemStyle").getItemStyle();A(e.ensureUniqueItemVisual(t.dataIndex,"style"),n)}))}))}var jC=["treemapZoomToNode","treemapRender","treemapMove"];function qC(t){var e=t.getData().tree,n={};e.eachNode((function(e){for(var i=e;i&&i.depth>1;)i=i.parentNode;var r=ld(t.ecModel,i.name||i.dataIndex+"",n);e.setVisual("decal",r)}))}var KC=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.preventUsingHoverLayer=!0,n}return n(e,t),e.prototype.getInitialData=function(t,e){var n={name:t.name,children:t.data};$C(n);var i=t.levels||[],r=this.designatedVisualItemStyle={},o=new Sc({itemStyle:r},this,e);i=t.levels=function(t,e){var n,i,r=_o(e.get("color")),o=_o(e.get(["aria","decal","decals"]));if(!r)return;E(t=t||[],(function(t){var e=new Sc(t),r=e.get("color"),o=e.get("decal");(e.get(["itemStyle","color"])||r&&"none"!==r)&&(n=!0),(e.get(["itemStyle","decal"])||o&&"none"!==o)&&(i=!0)}));var a=t[0]||(t[0]={});n||(a.color=r.slice());!i&&o&&(a.decal=o.slice());return t}(i,e);var a=z(i||[],(function(t){return new Sc(t,o,e)}),this),s=BC.createTree(n,this,(function(t){t.wrapMethod("getItemModel",(function(t,e){var n=s.getNodeByDataIndex(e),i=n?a[n.depth]:null;return t.parentModel=i||o,t}))}));return s.data},e.prototype.optionUpdated=function(){this.resetViewRoot()},e.prototype.formatTooltip=function(t,e,n){var i=this.getData(),r=this.getRawValue(t);return Qf("nameValue",{name:i.getName(t),value:r})},e.prototype.getDataParams=function(e){var n=t.prototype.getDataParams.apply(this,arguments),i=this.getData().tree.getNodeByDataIndex(e);return n.treeAncestors=HC(i,this),n.treePathInfo=n.treeAncestors,n},e.prototype.setLayoutInfo=function(t){this.layoutInfo=this.layoutInfo||{},A(this.layoutInfo,t)},e.prototype.mapIdToIndex=function(t){var e=this._idIndexMap;e||(e=this._idIndexMap=yt(),this._idIndexMapCount=0);var n=e.get(t);return null==n&&e.set(t,n=this._idIndexMapCount++),n},e.prototype.getViewRoot=function(){return this._viewRoot},e.prototype.resetViewRoot=function(t){t?this._viewRoot=t:t=this._viewRoot;var e=this.getRawData().tree.root;t&&(t===e||e.contains(t))||(this._viewRoot=e)},e.prototype.enableAriaDecal=function(){qC(this)},e.type="series.treemap",e.layoutMode="box",e.defaultOption={progressive:0,left:"center",top:"middle",width:"80%",height:"80%",sort:!0,clipWindow:"origin",squareRatio:.5*(1+Math.sqrt(5)),leafDepth:null,drillDownIcon:"▶",zoomToNodeRatio:.1024,roam:!0,nodeClick:"zoomToNode",animation:!0,animationDurationUpdate:900,animationEasing:"quinticInOut",breadcrumb:{show:!0,height:22,left:"center",top:"bottom",emptyItemWidth:25,itemStyle:{color:"rgba(0,0,0,0.7)",textStyle:{color:"#fff"}},emphasis:{itemStyle:{color:"rgba(0,0,0,0.9)"}}},label:{show:!0,distance:0,padding:5,position:"inside",color:"#fff",overflow:"truncate"},upperLabel:{show:!1,position:[0,"50%"],height:20,overflow:"truncate",verticalAlign:"middle"},itemStyle:{color:null,colorAlpha:null,colorSaturation:null,borderWidth:0,gapWidth:0,borderColor:"#fff",borderColorSaturation:null},emphasis:{upperLabel:{show:!0,position:[0,"50%"],overflow:"truncate",verticalAlign:"middle"}},visualDimension:0,visualMin:null,visualMax:null,color:[],colorAlpha:null,colorSaturation:null,colorMappingBy:"index",visibleMin:10,childrenVisibleMin:null,levels:[]},e}(fg);function $C(t){var e=0;E(t.children,(function(t){$C(t);var n=t.value;Y(n)&&(n=n[0]),e+=n}));var n=t.value;Y(n)&&(n=n[0]),(null==n||isNaN(n))&&(n=e),n<0&&(n=0),Y(t.value)?t.value[0]=n:t.value=n}var JC=function(){function t(t){this.group=new Er,t.add(this.group)}return t.prototype.render=function(t,e,n,i){var r=t.getModel("breadcrumb"),o=this.group;if(o.removeAll(),r.get("show")&&n){var a=r.getModel("itemStyle"),s=r.getModel("emphasis"),l=a.getModel("textStyle"),u=s.getModel(["itemStyle","textStyle"]),h={pos:{left:r.get("left"),right:r.get("right"),top:r.get("top"),bottom:r.get("bottom")},box:{width:e.getWidth(),height:e.getHeight()},emptyItemWidth:r.get("emptyItemWidth"),totalWidth:0,renderList:[]};this._prepare(n,h,l),this._renderContent(t,h,a,s,l,u,i),Cp(o,h.pos,h.box)}},t.prototype._prepare=function(t,e,n){for(var i=t;i;i=i.parentNode){var r=Do(i.getModel().get("name"),""),o=n.getTextRect(r),a=Math.max(o.width+16,e.emptyItemWidth);e.totalWidth+=a+8,e.renderList.push({node:i,text:r,width:a})}},t.prototype._renderContent=function(t,e,n,i,r,o,a){for(var s,l,u,h,c,p,d,f,g,y=0,v=e.emptyItemWidth,m=t.get(["breadcrumb","height"]),x=(s=e.pos,l=e.box,h=l.width,c=l.height,p=Ur(s.left,h),d=Ur(s.top,c),f=Ur(s.right,h),g=Ur(s.bottom,c),(isNaN(p)||isNaN(parseFloat(s.left)))&&(p=0),(isNaN(f)||isNaN(parseFloat(s.right)))&&(f=h),(isNaN(d)||isNaN(parseFloat(s.top)))&&(d=0),(isNaN(g)||isNaN(parseFloat(s.bottom)))&&(g=c),u=dp(u||0),{width:Math.max(f-p-u[1]-u[3],0),height:Math.max(g-d-u[0]-u[2],0)}),_=e.totalWidth,b=e.renderList,w=i.getModel("itemStyle").getItemStyle(),S=b.length-1;S>=0;S--){var M=b[S],I=M.node,T=M.width,C=M.text;_>x.width&&(_-=T-v,T=v,C=null);var D=new Gu({shape:{points:QC(y,0,T,m,S===b.length-1,0===S)},style:k(n.getItemStyle(),{lineJoin:"bevel"}),textContent:new Bs({style:ec(r,{text:C})}),textConfig:{position:"inside"},z2:1e5,onclick:H(a,I)});D.disableLabelAnimation=!0,D.getTextContent().ensureState("emphasis").style=ec(o,{text:C}),D.ensureState("emphasis").style=w,Hl(D,i.get("focus"),i.get("blurScope"),i.get("disabled")),this.group.add(D),tD(D,t,I),y+=T+8}},t.prototype.remove=function(){this.group.removeAll()},t}();function QC(t,e,n,i,r,o){var a=[[r?t:t-5,e],[t+n,e],[t+n,e+i],[r?t:t-5,e+i]];return!o&&a.splice(2,0,[t+n+5,e+i/2]),!r&&a.push([t,e+i/2]),a}function tD(t,e,n){Js(t).eventData={componentType:"series",componentSubType:"treemap",componentIndex:e.componentIndex,seriesIndex:e.seriesIndex,seriesName:e.name,seriesType:"treemap",selfType:"breadcrumb",nodeData:{dataIndex:n&&n.dataIndex,name:n&&n.name},treePathInfo:n&&HC(n,e)}}var eD=function(){function t(){this._storage=[],this._elExistsMap={}}return t.prototype.add=function(t,e,n,i,r){return!this._elExistsMap[t.id]&&(this._elExistsMap[t.id]=!0,this._storage.push({el:t,target:e,duration:n,delay:i,easing:r}),!0)},t.prototype.finished=function(t){return this._finishedCallback=t,this},t.prototype.start=function(){for(var t=this,e=this._storage.length,n=function(){--e<=0&&(t._storage.length=0,t._elExistsMap={},t._finishedCallback&&t._finishedCallback())},i=0,r=this._storage.length;i3||Math.abs(t.dy)>3)){var e=this.seriesModel.getData().tree.root;if(!e)return;var n=e.getLayout();if(!n)return;this.api.dispatchAction({type:"treemapMove",from:this.uid,seriesId:this.seriesModel.id,rootRect:{x:n.x+t.dx,y:n.y+t.dy,width:n.width,height:n.height}})}},e.prototype._onZoom=function(t){var e=t.originX,n=t.originY;if("animating"!==this._state){var i=this.seriesModel.getData().tree.root;if(!i)return;var r=i.getLayout();if(!r)return;var o=new Ee(r.x,r.y,r.width,r.height),a=this.seriesModel.layoutInfo,s=[1,0,0,1,0,0];be(s,s,[-(e-=a.x),-(n-=a.y)]),Se(s,s,[t.scale,t.scale]),be(s,s,[e,n]),o.applyTransform(s),this.api.dispatchAction({type:"treemapRender",from:this.uid,seriesId:this.seriesModel.id,rootRect:{x:o.x,y:o.y,width:o.width,height:o.height}})}},e.prototype._initEvents=function(t){var e=this;t.on("click",(function(t){if("ready"===e._state){var n=e.seriesModel.get("nodeClick",!0);if(n){var i=e.findTarget(t.offsetX,t.offsetY);if(i){var r=i.node;if(r.getLayout().isLeafRoot)e._rootToNode(i);else if("zoomToNode"===n)e._zoomToNode(i);else if("link"===n){var o=r.hostTree.data.getItemModel(r.dataIndex),a=o.get("link",!0),s=o.get("target",!0)||"blank";a&&_p(a,s)}}}}}),this)},e.prototype._renderBreadcrumb=function(t,e,n){var i=this;n||(n=null!=t.get("leafDepth",!0)?{node:t.getViewRoot()}:this.findTarget(e.getWidth()/2,e.getHeight()/2))||(n={node:t.getData().tree.root}),(this._breadcrumb||(this._breadcrumb=new JC(this.group))).render(t,e,n.node,(function(e){"animating"!==i._state&&(WC(t.getViewRoot(),e)?i._rootToNode({node:e}):i._zoomToNode({node:e}))}))},e.prototype.remove=function(){this._clearController(),this._containerGroup&&this._containerGroup.removeAll(),this._storage={nodeGroup:[],background:[],content:[]},this._state="ready",this._breadcrumb&&this._breadcrumb.remove()},e.prototype.dispose=function(){this._clearController()},e.prototype._zoomToNode=function(t){this.api.dispatchAction({type:"treemapZoomToNode",from:this.uid,seriesId:this.seriesModel.id,targetNode:t.node})},e.prototype._rootToNode=function(t){this.api.dispatchAction({type:"treemapRootToNode",from:this.uid,seriesId:this.seriesModel.id,targetNode:t.node})},e.prototype.findTarget=function(t,e){var n;return this.seriesModel.getViewRoot().eachNode({attr:"viewChildren",order:"preorder"},(function(i){var r=this._storage.background[i.getRawIndex()];if(r){var o=r.transformCoordToLocal(t,e),a=r.shape;if(!(a.x<=o[0]&&o[0]<=a.x+a.width&&a.y<=o[1]&&o[1]<=a.y+a.height))return!1;n={node:i,offsetX:o[0],offsetY:o[1]}}}),this),n},e.type="treemap",e}(Tg);var hD=E,cD=q,pD=-1,dD=function(){function t(e){var n=e.mappingMethod,i=e.type,r=this.option=T(e);this.type=i,this.mappingMethod=n,this._normalizeData=SD[n];var o=t.visualHandlers[i];this.applyVisual=o.applyVisual,this.getColorMapper=o.getColorMapper,this._normalizedToVisual=o._normalizedToVisual[n],"piecewise"===n?(fD(r),function(t){var e=t.pieceList;t.hasSpecialVisual=!1,E(e,(function(e,n){e.originIndex=n,null!=e.visual&&(t.hasSpecialVisual=!0)}))}(r)):"category"===n?r.categories?function(t){var e=t.categories,n=t.categoryMap={},i=t.visual;if(hD(e,(function(t,e){n[t]=e})),!Y(i)){var r=[];q(i)?hD(i,(function(t,e){var i=n[e];r[null!=i?i:pD]=t})):r[-1]=i,i=wD(t,r)}for(var o=e.length-1;o>=0;o--)null==i[o]&&(delete n[e[o]],e.pop())}(r):fD(r,!0):(lt("linear"!==n||r.dataExtent),fD(r))}return t.prototype.mapValueToVisual=function(t){var e=this._normalizeData(t);return this._normalizedToVisual(e,t)},t.prototype.getNormalizer=function(){return W(this._normalizeData,this)},t.listVisualTypes=function(){return G(t.visualHandlers)},t.isValidType=function(e){return t.visualHandlers.hasOwnProperty(e)},t.eachVisual=function(t,e,n){q(t)?E(t,e,n):e.call(n,t)},t.mapVisual=function(e,n,i){var r,o=Y(e)?[]:q(e)?{}:(r=!0,null);return t.eachVisual(e,(function(t,e){var a=n.call(i,t,e);r?o=a:o[e]=a})),o},t.retrieveVisuals=function(e){var n,i={};return e&&hD(t.visualHandlers,(function(t,r){e.hasOwnProperty(r)&&(i[r]=e[r],n=!0)})),n?i:null},t.prepareVisualTypes=function(t){if(Y(t))t=t.slice();else{if(!cD(t))return[];var e=[];hD(t,(function(t,n){e.push(n)})),t=e}return t.sort((function(t,e){return"color"===e&&"color"!==t&&0===t.indexOf("color")?1:-1})),t},t.dependsOn=function(t,e){return"color"===e?!(!t||0!==t.indexOf(e)):t===e},t.findPieceIndex=function(t,e,n){for(var i,r=1/0,o=0,a=e.length;ou[1]&&(u[1]=l);var h=e.get("colorMappingBy"),c={type:a.name,dataExtent:u,visual:a.range};"color"!==c.type||"index"!==h&&"id"!==h?c.mappingMethod="linear":(c.mappingMethod="category",c.loop=!0);var p=new dD(c);return ID(p).drColorMappingBy=h,p}(0,r,o,0,u,d);E(d,(function(t,e){if(t.depth>=n.length||t===n[t.depth]){var o=function(t,e,n,i,r,o){var a=A({},e);if(r){var s=r.type,l="color"===s&&ID(r).drColorMappingBy,u="index"===l?i:"id"===l?o.mapIdToIndex(n.getId()):n.getValue(t.get("visualDimension"));a[s]=r.mapValueToVisual(u)}return a}(r,u,t,e,f,i);CD(t,o,n,i)}}))}else s=DD(u),h.fill=s}}function DD(t){var e=AD(t,"color");if(e){var n=AD(t,"colorAlpha"),i=AD(t,"colorSaturation");return i&&(e=ei(e,null,null,i)),n&&(e=ni(e,n)),e}}function AD(t,e){var n=t[e];if(null!=n&&"none"!==n)return n}function kD(t,e){var n=t.get(e);return Y(n)&&n.length?{name:e,range:n}:null}var LD=Math.max,PD=Math.min,OD=it,RD=E,ND=["itemStyle","borderWidth"],ED=["itemStyle","gapWidth"],zD=["upperLabel","show"],VD=["upperLabel","height"],BD={seriesType:"treemap",reset:function(t,e,n,i){var r=n.getWidth(),o=n.getHeight(),a=t.option,s=Tp(t.getBoxLayoutParams(),{width:n.getWidth(),height:n.getHeight()}),l=a.size||[],u=Ur(OD(s.width,l[0]),r),h=Ur(OD(s.height,l[1]),o),c=i&&i.type,p=FC(i,["treemapZoomToNode","treemapRootToNode"],t),d="treemapRender"===c||"treemapMove"===c?i.rootRect:null,f=t.getViewRoot(),g=GC(f);if("treemapMove"!==c){var y="treemapZoomToNode"===c?function(t,e,n,i,r){var o,a=(e||{}).node,s=[i,r];if(!a||a===n)return s;var l=i*r,u=l*t.option.zoomToNodeRatio;for(;o=a.parentNode;){for(var h=0,c=o.children,p=0,d=c.length;pQr&&(u=Qr),a=o}ua[1]&&(a[1]=e)}))):a=[NaN,NaN];return{sum:i,dataExtent:a}}(e,a,s);if(0===u.sum)return t.viewChildren=[];if(u.sum=function(t,e,n,i,r){if(!i)return n;for(var o=t.get("visibleMin"),a=r.length,s=a,l=a-1;l>=0;l--){var u=r["asc"===i?a-l-1:l].getValue();u/n*ei&&(i=a));var l=t.area*t.area,u=e*e*n;return l?LD(u*i/l,l/(u*r)):1/0}function WD(t,e,n,i,r){var o=e===n.width?0:1,a=1-o,s=["x","y"],l=["width","height"],u=n[s[o]],h=e?t.area/e:0;(r||h>n[l[a]])&&(h=n[l[a]]);for(var c=0,p=t.length;ci&&(i=e);var o=i%2?i+2:i+3;r=[];for(var a=0;a0&&(m[0]=-m[0],m[1]=-m[1]);var _=v[0]<0?-1:1;if("start"!==i.__position&&"end"!==i.__position){var b=-Math.atan2(v[1],v[0]);u[0].8?"left":h[0]<-.8?"right":"center",p=h[1]>.8?"top":h[1]<-.8?"bottom":"middle";break;case"start":i.x=-h[0]*f+l[0],i.y=-h[1]*g+l[1],c=h[0]>.8?"right":h[0]<-.8?"left":"center",p=h[1]>.8?"bottom":h[1]<-.8?"top":"middle";break;case"insideStartTop":case"insideStart":case"insideStartBottom":i.x=f*_+l[0],i.y=l[1]+w,c=v[0]<0?"right":"left",i.originX=-f*_,i.originY=-w;break;case"insideMiddleTop":case"insideMiddle":case"insideMiddleBottom":case"middle":i.x=x[0],i.y=x[1]+w,c="center",i.originY=-w;break;case"insideEndTop":case"insideEnd":case"insideEndBottom":i.x=-f*_+u[0],i.y=u[1]+w,c=v[0]>=0?"right":"left",i.originX=f*_,i.originY=-w}i.scaleX=i.scaleY=r,i.setStyle({verticalAlign:i.__verticalAlign||p,align:i.__align||c})}}}function S(t,e){var n=t.__specifiedRotation;if(null==n){var i=a.tangentAt(e);t.attr("rotation",(1===e?-1:1)*Math.PI/2-Math.atan2(i[1],i[0]))}else t.attr("rotation",n)}},e}(Er),TA=function(){function t(t){this.group=new Er,this._LineCtor=t||IA}return t.prototype.updateData=function(t){var e=this;this._progressiveEls=null;var n=this,i=n.group,r=n._lineData;n._lineData=t,r||i.removeAll();var o=CA(t);t.diff(r).add((function(n){e._doAdd(t,n,o)})).update((function(n,i){e._doUpdate(r,t,i,n,o)})).remove((function(t){i.remove(r.getItemGraphicEl(t))})).execute()},t.prototype.updateLayout=function(){var t=this._lineData;t&&t.eachItemGraphicEl((function(e,n){e.updateLayout(t,n)}),this)},t.prototype.incrementalPrepareUpdate=function(t){this._seriesScope=CA(t),this._lineData=null,this.group.removeAll()},t.prototype.incrementalUpdate=function(t,e){function n(t){t.isGroup||function(t){return t.animators&&t.animators.length>0}(t)||(t.incremental=!0,t.ensureState("emphasis").hoverLayer=!0)}this._progressiveEls=[];for(var i=t.start;i=0?i+=u:i-=u:f>=0?i-=u:i+=u}return i}function zA(t,e){var n=[],i=Cn,r=[[],[],[]],o=[[],[]],a=[];e/=2,t.eachEdge((function(t,s){var l=t.getLayout(),u=t.getVisual("fromSymbol"),h=t.getVisual("toSymbol");l.__original||(l.__original=[Tt(l[0]),Tt(l[1])],l[2]&&l.__original.push(Tt(l[2])));var c=l.__original;if(null!=l[2]){if(It(r[0],c[0]),It(r[1],c[2]),It(r[2],c[1]),u&&"none"!==u){var p=aA(t.node1),d=EA(r,c[0],p*e);i(r[0][0],r[1][0],r[2][0],d,n),r[0][0]=n[3],r[1][0]=n[4],i(r[0][1],r[1][1],r[2][1],d,n),r[0][1]=n[3],r[1][1]=n[4]}if(h&&"none"!==h){p=aA(t.node2),d=EA(r,c[1],p*e);i(r[0][0],r[1][0],r[2][0],d,n),r[1][0]=n[1],r[2][0]=n[2],i(r[0][1],r[1][1],r[2][1],d,n),r[1][1]=n[1],r[2][1]=n[2]}It(l[0],r[0]),It(l[1],r[2]),It(l[2],r[1])}else{if(It(o[0],c[0]),It(o[1],c[1]),kt(a,o[1],o[0]),Et(a,a),u&&"none"!==u){p=aA(t.node1);At(o[0],o[0],a,p*e)}if(h&&"none"!==h){p=aA(t.node2);At(o[1],o[1],a,-p*e)}It(l[0],o[0]),It(l[1],o[1])}}))}function VA(t){return"view"===t.type}var BA=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.init=function(t,e){var n=new iS,i=new TA,r=this.group;this._controller=new BI(e.getZr()),this._controllerHost={target:r},r.add(n.group),r.add(i.group),this._symbolDraw=n,this._lineDraw=i,this._firstRender=!0},e.prototype.render=function(t,e,n){var i=this,r=t.coordinateSystem;this._model=t;var o=this._symbolDraw,a=this._lineDraw,s=this.group;if(VA(r)){var l={x:r.x,y:r.y,scaleX:r.scaleX,scaleY:r.scaleY};this._firstRender?s.attr(l):dh(s,l,t)}zA(t.getGraph(),oA(t));var u=t.getData();o.updateData(u);var h=t.getEdgeData();a.updateData(h),this._updateNodeAndLinkScale(),this._updateController(t,e,n),clearTimeout(this._layoutTimeout);var c=t.forceLayout,p=t.get(["force","layoutAnimation"]);c&&this._startForceLayoutIteration(c,p);var d=t.get("layout");u.graph.eachNode((function(e){var n=e.dataIndex,r=e.getGraphicEl(),o=e.getModel();if(r){r.off("drag").off("dragend");var a=o.get("draggable");a&&r.on("drag",(function(o){switch(d){case"force":c.warmUp(),!i._layouting&&i._startForceLayoutIteration(c,p),c.setFixed(n),u.setItemLayout(n,[r.x,r.y]);break;case"circular":u.setItemLayout(n,[r.x,r.y]),e.setLayout({fixed:!0},!0),uA(t,"symbolSize",e,[o.offsetX,o.offsetY]),i.updateLayout(t);break;default:u.setItemLayout(n,[r.x,r.y]),iA(t.getGraph(),t),i.updateLayout(t)}})).on("dragend",(function(){c&&c.setUnfixed(n)})),r.setDraggable(a,!!o.get("cursor")),"adjacency"===o.get(["emphasis","focus"])&&(Js(r).focus=e.getAdjacentDataIndices())}})),u.graph.eachEdge((function(t){var e=t.getGraphicEl(),n=t.getModel().get(["emphasis","focus"]);e&&"adjacency"===n&&(Js(e).focus={edge:[t.dataIndex],node:[t.node1.dataIndex,t.node2.dataIndex]})}));var f="circular"===t.get("layout")&&t.get(["circular","rotateLabel"]),g=u.getLayout("cx"),y=u.getLayout("cy");u.graph.eachNode((function(t){cA(t,f,g,y)})),this._firstRender=!1},e.prototype.dispose=function(){this._controller&&this._controller.dispose(),this._controllerHost=null},e.prototype._startForceLayoutIteration=function(t,e){var n=this;!function i(){t.step((function(t){n.updateLayout(n._model),(n._layouting=!t)&&(e?n._layoutTimeout=setTimeout(i,16):i())}))}()},e.prototype._updateController=function(t,e,n){var i=this,r=this._controller,o=this._controllerHost,a=this.group;r.setPointerChecker((function(e,i,r){var o=a.getBoundingRect();return o.applyTransform(a.transform),o.contain(i,r)&&!ZI(e,n,t)})),VA(t.coordinateSystem)?(r.enable(t.get("roam")),o.zoomLimit=t.get("scaleLimit"),o.zoom=t.coordinateSystem.getZoom(),r.off("pan").off("zoom").on("pan",(function(e){HI(o,e.dx,e.dy),n.dispatchAction({seriesId:t.id,type:"graphRoam",dx:e.dx,dy:e.dy})})).on("zoom",(function(e){YI(o,e.scale,e.originX,e.originY),n.dispatchAction({seriesId:t.id,type:"graphRoam",zoom:e.scale,originX:e.originX,originY:e.originY}),i._updateNodeAndLinkScale(),zA(t.getGraph(),oA(t)),i._lineDraw.updateLayout(),n.updateLabelLayout()}))):r.disable()},e.prototype._updateNodeAndLinkScale=function(){var t=this._model,e=t.getData(),n=oA(t);e.eachItemGraphicEl((function(t,e){t&&t.setSymbolScale(n)}))},e.prototype.updateLayout=function(t){zA(t.getGraph(),oA(t)),this._symbolDraw.updateLayout(),this._lineDraw.updateLayout()},e.prototype.remove=function(t,e){this._symbolDraw&&this._symbolDraw.remove(),this._lineDraw&&this._lineDraw.remove()},e.type="graph",e}(Tg);function FA(t){return"_EC_"+t}var GA=function(){function t(t){this.type="graph",this.nodes=[],this.edges=[],this._nodesMap={},this._edgesMap={},this._directed=t||!1}return t.prototype.isDirected=function(){return this._directed},t.prototype.addNode=function(t,e){t=null==t?""+e:""+t;var n=this._nodesMap;if(!n[FA(t)]){var i=new WA(t,e);return i.hostGraph=this,this.nodes.push(i),n[FA(t)]=i,i}},t.prototype.getNodeByIndex=function(t){var e=this.data.getRawIndex(t);return this.nodes[e]},t.prototype.getNodeById=function(t){return this._nodesMap[FA(t)]},t.prototype.addEdge=function(t,e,n){var i=this._nodesMap,r=this._edgesMap;if(j(t)&&(t=this.nodes[t]),j(e)&&(e=this.nodes[e]),t instanceof WA||(t=i[FA(t)]),e instanceof WA||(e=i[FA(e)]),t&&e){var o=t.id+"-"+e.id,a=new HA(t,e,n);return a.hostGraph=this,this._directed&&(t.outEdges.push(a),e.inEdges.push(a)),t.edges.push(a),t!==e&&e.edges.push(a),this.edges.push(a),r[o]=a,a}},t.prototype.getEdgeByIndex=function(t){var e=this.edgeData.getRawIndex(t);return this.edges[e]},t.prototype.getEdge=function(t,e){t instanceof WA&&(t=t.id),e instanceof WA&&(e=e.id);var n=this._edgesMap;return this._directed?n[t+"-"+e]:n[t+"-"+e]||n[e+"-"+t]},t.prototype.eachNode=function(t,e){for(var n=this.nodes,i=n.length,r=0;r=0&&t.call(e,n[r],r)},t.prototype.eachEdge=function(t,e){for(var n=this.edges,i=n.length,r=0;r=0&&n[r].node1.dataIndex>=0&&n[r].node2.dataIndex>=0&&t.call(e,n[r],r)},t.prototype.breadthFirstTraverse=function(t,e,n,i){if(e instanceof WA||(e=this._nodesMap[FA(e)]),e){for(var r="out"===n?"outEdges":"in"===n?"inEdges":"edges",o=0;o=0&&n.node2.dataIndex>=0}));for(r=0,o=i.length;r=0&&this[t][e].setItemVisual(this.dataIndex,n,i)},getVisual:function(n){return this[t][e].getItemVisual(this.dataIndex,n)},setLayout:function(n,i){this.dataIndex>=0&&this[t][e].setItemLayout(this.dataIndex,n,i)},getLayout:function(){return this[t][e].getItemLayout(this.dataIndex)},getGraphicEl:function(){return this[t][e].getItemGraphicEl(this.dataIndex)},getRawIndex:function(){return this[t][e].getRawIndex(this.dataIndex)}}}function UA(t,e,n,i,r){for(var o=new GA(i),a=0;a "+p)),u++)}var d,f=n.get("coordinateSystem");if("cartesian2d"===f||"polar"===f)d=hx(t,n);else{var g=vd.get(f),y=g&&g.dimensions||[];P(y,"value")<0&&y.concat(["value"]);var v=nx(t,{coordDimensions:y,encodeDefine:n.getEncode()}).dimensions;(d=new ex(v,n)).initData(t)}var m=new ex(["value"],n);return m.initData(l,s),r&&r(d,m),kC({mainData:d,struct:o,structAttr:"graph",datas:{node:d,edge:m},datasAttr:{node:"data",edge:"edgeData"}}),o.update(),o}R(WA,YA("hostGraph","data")),R(HA,YA("hostGraph","edgeData"));var XA=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.hasSymbolVisual=!0,n}return n(e,t),e.prototype.init=function(e){t.prototype.init.apply(this,arguments);var n=this;function i(){return n._categoriesData}this.legendVisualProvider=new mM(i,i),this.fillDataTextStyle(e.edges||e.links),this._updateCategoriesData()},e.prototype.mergeOption=function(e){t.prototype.mergeOption.apply(this,arguments),this.fillDataTextStyle(e.edges||e.links),this._updateCategoriesData()},e.prototype.mergeDefaultAndTheme=function(e){t.prototype.mergeDefaultAndTheme.apply(this,arguments),bo(e,"edgeLabel",["show"])},e.prototype.getInitialData=function(t,e){var n,i=t.edges||t.links||[],r=t.data||t.nodes||[],o=this;if(r&&i){KD(n=this)&&(n.__curvenessList=[],n.__edgeMap={},$D(n));var a=UA(r,i,this,!0,(function(t,e){t.wrapMethod("getItemModel",(function(t){var e=o._categoriesModels[t.getShallow("category")];return e&&(e.parentModel=t.parentModel,t.parentModel=e),t}));var n=Sc.prototype.getModel;function i(t,e){var i=n.call(this,t,e);return i.resolveParentPath=r,i}function r(t){if(t&&("label"===t[0]||"label"===t[1])){var e=t.slice();return"label"===t[0]?e[0]="edgeLabel":"label"===t[1]&&(e[1]="edgeLabel"),e}return t}e.wrapMethod("getItemModel",(function(t){return t.resolveParentPath=r,t.getModel=i,t}))}));return E(a.edges,(function(t){!function(t,e,n,i){if(KD(n)){var r=JD(t,e,n),o=n.__edgeMap,a=o[QD(r)];o[r]&&!a?o[r].isForward=!0:a&&o[r]&&(a.isForward=!0,o[r].isForward=!1),o[r]=o[r]||[],o[r].push(i)}}(t.node1,t.node2,this,t.dataIndex)}),this),a.data}},e.prototype.getGraph=function(){return this.getData().graph},e.prototype.getEdgeData=function(){return this.getGraph().edgeData},e.prototype.getCategoriesData=function(){return this._categoriesData},e.prototype.formatTooltip=function(t,e,n){if("edge"===n){var i=this.getData(),r=this.getDataParams(t,n),o=i.graph.getEdgeByIndex(t),a=i.getName(o.node1.dataIndex),s=i.getName(o.node2.dataIndex),l=[];return null!=a&&l.push(a),null!=s&&l.push(s),Qf("nameValue",{name:l.join(" > "),value:r.value,noValue:null==r.value})}return cg({series:this,dataIndex:t,multipleSeries:e})},e.prototype._updateCategoriesData=function(){var t=z(this.option.categories||[],(function(t){return null!=t.value?t:A({value:0},t)})),e=new ex(["value"],this);e.initData(t),this._categoriesData=e,this._categoriesModels=e.mapArray((function(t){return e.getItemModel(t)}))},e.prototype.setZoom=function(t){this.option.zoom=t},e.prototype.setCenter=function(t){this.option.center=t},e.prototype.isAnimationEnabled=function(){return t.prototype.isAnimationEnabled.call(this)&&!("force"===this.get("layout")&&this.get(["force","layoutAnimation"]))},e.type="series.graph",e.dependencies=["grid","polar","geo","singleAxis","calendar"],e.defaultOption={z:2,coordinateSystem:"view",legendHoverLink:!0,layout:null,circular:{rotateLabel:!1},force:{initLayout:null,repulsion:[0,50],gravity:.1,friction:.6,edgeLength:30,layoutAnimation:!0},left:"center",top:"center",symbol:"circle",symbolSize:10,edgeSymbol:["none","none"],edgeSymbolSize:10,edgeLabel:{position:"middle",distance:5},draggable:!1,roam:!1,center:null,zoom:1,nodeScaleRatio:.6,label:{show:!1,formatter:"{b}"},itemStyle:{},lineStyle:{color:"#aaa",width:1,opacity:.5},emphasis:{scale:!0,label:{show:!0}},select:{itemStyle:{borderColor:"#212121"}}},e}(fg),ZA={type:"graphRoam",event:"graphRoam",update:"none"};var jA=function(){this.angle=0,this.width=10,this.r=10,this.x=0,this.y=0},qA=function(t){function e(e){var n=t.call(this,e)||this;return n.type="pointer",n}return n(e,t),e.prototype.getDefaultShape=function(){return new jA},e.prototype.buildPath=function(t,e){var n=Math.cos,i=Math.sin,r=e.r,o=e.width,a=e.angle,s=e.x-n(a)*o*(o>=r/3?1:2),l=e.y-i(a)*o*(o>=r/3?1:2);a=e.angle-Math.PI/2,t.moveTo(s,l),t.lineTo(e.x+n(a)*o,e.y+i(a)*o),t.lineTo(e.x+n(e.angle)*r,e.y+i(e.angle)*r),t.lineTo(e.x-n(a)*o,e.y-i(a)*o),t.lineTo(s,l)},e}(Ms);function KA(t,e){var n=null==t?"":t+"";return e&&(X(e)?n=e.replace("{value}",n):U(e)&&(n=e(t))),n}var $A=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){this.group.removeAll();var i=t.get(["axisLine","lineStyle","color"]),r=function(t,e){var n=t.get("center"),i=e.getWidth(),r=e.getHeight(),o=Math.min(i,r);return{cx:Ur(n[0],e.getWidth()),cy:Ur(n[1],e.getHeight()),r:Ur(t.get("radius"),o/2)}}(t,n);this._renderMain(t,e,n,i,r),this._data=t.getData()},e.prototype.dispose=function(){},e.prototype._renderMain=function(t,e,n,i,r){var o=this.group,a=t.get("clockwise"),s=-t.get("startAngle")/180*Math.PI,l=-t.get("endAngle")/180*Math.PI,u=t.getModel("axisLine"),h=u.get("roundCap")?ES:Eu,c=u.get("show"),p=u.getModel("lineStyle"),d=p.get("width"),f=[s,l];is(f,!a);for(var g=(l=f[1])-(s=f[0]),y=s,v=[],m=0;c&&m=t&&(0===e?0:i[e-1][0])Math.PI/2&&(V+=Math.PI):"tangential"===z?V=-M-Math.PI/2:j(z)&&(V=z*Math.PI/180),0===V?c.add(new Bs({style:ec(x,{text:O,x:N,y:E,verticalAlign:h<-.8?"top":h>.8?"bottom":"middle",align:u<-.4?"left":u>.4?"right":"center"},{inheritColor:R}),silent:!0})):c.add(new Bs({style:ec(x,{text:O,x:N,y:E,verticalAlign:"middle",align:"center"},{inheritColor:R}),silent:!0,originX:N,originY:E,rotation:V}))}if(m.get("show")&&k!==_){P=(P=m.get("distance"))?P+l:l;for(var B=0;B<=b;B++){u=Math.cos(M),h=Math.sin(M);var F=new Xu({shape:{x1:u*(f-P)+p,y1:h*(f-P)+d,x2:u*(f-S-P)+p,y2:h*(f-S-P)+d},silent:!0,style:D});"auto"===D.stroke&&F.setStyle({stroke:i((k+B/b)/_)}),c.add(F),M+=T}M-=T}else M+=I}},e.prototype._renderPointer=function(t,e,n,i,r,o,a,s,l){var u=this.group,h=this._data,c=this._progressEls,p=[],d=t.get(["pointer","show"]),f=t.getModel("progress"),g=f.get("show"),y=t.getData(),v=y.mapDimension("value"),m=+t.get("min"),x=+t.get("max"),_=[m,x],b=[o,a];function w(e,n){var i,o=y.getItemModel(e).getModel("pointer"),a=Ur(o.get("width"),r.r),s=Ur(o.get("length"),r.r),l=t.get(["pointer","icon"]),u=o.get("offsetCenter"),h=Ur(u[0],r.r),c=Ur(u[1],r.r),p=o.get("keepAspect");return(i=l?Vy(l,h-a/2,c-s,a,s,null,p):new qA({shape:{angle:-Math.PI/2,width:a,r:s,x:h,y:c}})).rotation=-(n+Math.PI/2),i.x=r.cx,i.y=r.cy,i}function S(t,e){var n=f.get("roundCap")?ES:Eu,i=f.get("overlap"),a=i?f.get("width"):l/y.count(),u=i?r.r-a:r.r-(t+1)*a,h=i?r.r:r.r-t*a,c=new n({shape:{startAngle:o,endAngle:e,cx:r.cx,cy:r.cy,clockwise:s,r0:u,r:h}});return i&&(c.z2=x-y.get(v,t)%x),c}(g||d)&&(y.diff(h).add((function(e){var n=y.get(v,e);if(d){var i=w(e,o);fh(i,{rotation:-((isNaN(+n)?b[0]:Yr(n,_,b,!0))+Math.PI/2)},t),u.add(i),y.setItemGraphicEl(e,i)}if(g){var r=S(e,o),a=f.get("clip");fh(r,{shape:{endAngle:Yr(n,_,b,a)}},t),u.add(r),Qs(t.seriesIndex,y.dataType,e,r),p[e]=r}})).update((function(e,n){var i=y.get(v,e);if(d){var r=h.getItemGraphicEl(n),a=r?r.rotation:o,s=w(e,a);s.rotation=a,dh(s,{rotation:-((isNaN(+i)?b[0]:Yr(i,_,b,!0))+Math.PI/2)},t),u.add(s),y.setItemGraphicEl(e,s)}if(g){var l=c[n],m=S(e,l?l.shape.endAngle:o),x=f.get("clip");dh(m,{shape:{endAngle:Yr(i,_,b,x)}},t),u.add(m),Qs(t.seriesIndex,y.dataType,e,m),p[e]=m}})).execute(),y.each((function(t){var e=y.getItemModel(t),n=e.getModel("emphasis"),r=n.get("focus"),o=n.get("blurScope"),a=n.get("disabled");if(d){var s=y.getItemGraphicEl(t),l=y.getItemVisual(t,"style"),u=l.fill;if(s instanceof As){var h=s.style;s.useStyle(A({image:h.image,x:h.x,y:h.y,width:h.width,height:h.height},l))}else s.useStyle(l),"pointer"!==s.type&&s.setColor(u);s.setStyle(e.getModel(["pointer","itemStyle"]).getItemStyle()),"auto"===s.style.fill&&s.setStyle("fill",i(Yr(y.get(v,t),_,[0,1],!0))),s.z2EmphasisLift=0,Zl(s,e),Hl(s,r,o,a)}if(g){var c=p[t];c.useStyle(y.getItemVisual(t,"style")),c.setStyle(e.getModel(["progress","itemStyle"]).getItemStyle()),c.z2EmphasisLift=0,Zl(c,e),Hl(c,r,o,a)}})),this._progressEls=p)},e.prototype._renderAnchor=function(t,e){var n=t.getModel("anchor");if(n.get("show")){var i=n.get("size"),r=n.get("icon"),o=n.get("offsetCenter"),a=n.get("keepAspect"),s=Vy(r,e.cx-i/2+Ur(o[0],e.r),e.cy-i/2+Ur(o[1],e.r),i,i,null,a);s.z2=n.get("showAbove")?1:0,s.setStyle(n.getModel("itemStyle").getItemStyle()),this.group.add(s)}},e.prototype._renderTitleAndDetail=function(t,e,n,i,r){var o=this,a=t.getData(),s=a.mapDimension("value"),l=+t.get("min"),u=+t.get("max"),h=new Er,c=[],p=[],d=t.isAnimationEnabled(),f=t.get(["pointer","showAbove"]);a.diff(this._data).add((function(t){c[t]=new Bs({silent:!0}),p[t]=new Bs({silent:!0})})).update((function(t,e){c[t]=o._titleEls[e],p[t]=o._detailEls[e]})).execute(),a.each((function(e){var n=a.getItemModel(e),o=a.get(s,e),g=new Er,y=i(Yr(o,[l,u],[0,1],!0)),v=n.getModel("title");if(v.get("show")){var m=v.get("offsetCenter"),x=r.cx+Ur(m[0],r.r),_=r.cy+Ur(m[1],r.r);(D=c[e]).attr({z2:f?0:2,style:ec(v,{x:x,y:_,text:a.getName(e),align:"center",verticalAlign:"middle"},{inheritColor:y})}),g.add(D)}var b=n.getModel("detail");if(b.get("show")){var w=b.get("offsetCenter"),S=r.cx+Ur(w[0],r.r),M=r.cy+Ur(w[1],r.r),I=Ur(b.get("width"),r.r),T=Ur(b.get("height"),r.r),C=t.get(["progress","show"])?a.getItemVisual(e,"style").fill:y,D=p[e],A=b.get("formatter");D.attr({z2:f?0:2,style:ec(b,{x:S,y:M,text:KA(o,A),width:isNaN(I)?null:I,height:isNaN(T)?null:T,align:"center",verticalAlign:"middle"},{inheritColor:C})}),uc(D,{normal:b},o,(function(t){return KA(t,A)})),d&&hc(D,e,a,t,{getFormattedLabel:function(t,e,n,i,r,a){return KA(a?a.interpolatedValue:o,A)}}),g.add(D)}h.add(g)})),this.group.add(h),this._titleEls=c,this._detailEls=p},e.type="gauge",e}(Tg),JA=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.visualStyleAccessPath="itemStyle",n}return n(e,t),e.prototype.getInitialData=function(t,e){return vM(this,["value"])},e.type="series.gauge",e.defaultOption={z:2,colorBy:"data",center:["50%","50%"],legendHoverLink:!0,radius:"75%",startAngle:225,endAngle:-45,clockwise:!0,min:0,max:100,splitNumber:10,axisLine:{show:!0,roundCap:!1,lineStyle:{color:[[1,"#E6EBF8"]],width:10}},progress:{show:!1,overlap:!0,width:10,roundCap:!1,clip:!0},splitLine:{show:!0,length:10,distance:10,lineStyle:{color:"#63677A",width:3,type:"solid"}},axisTick:{show:!0,splitNumber:5,length:6,distance:10,lineStyle:{color:"#63677A",width:1,type:"solid"}},axisLabel:{show:!0,distance:15,color:"#464646",fontSize:12,rotate:0},pointer:{icon:null,offsetCenter:[0,0],show:!0,showAbove:!0,length:"60%",width:6,keepAspect:!1},anchor:{show:!1,showAbove:!1,size:6,icon:"circle",offsetCenter:[0,0],keepAspect:!1,itemStyle:{color:"#fff",borderWidth:0,borderColor:"#5470c6"}},title:{show:!0,offsetCenter:[0,"20%"],color:"#464646",fontSize:16,valueAnimation:!1},detail:{show:!0,backgroundColor:"rgba(0,0,0,0)",borderWidth:0,borderColor:"#ccc",width:100,height:null,padding:[5,10],offsetCenter:[0,"40%"],color:"#464646",fontSize:30,fontWeight:"bold",lineHeight:30,valueAnimation:!1}},e}(fg);var QA=["itemStyle","opacity"],tk=function(t){function e(e,n){var i=t.call(this)||this,r=i,o=new Hu,a=new Bs;return r.setTextContent(a),i.setTextGuideLine(o),i.updateData(e,n,!0),i}return n(e,t),e.prototype.updateData=function(t,e,n){var i=this,r=t.hostModel,o=t.getItemModel(e),a=t.getItemLayout(e),s=o.getModel("emphasis"),l=o.get(QA);l=null==l?1:l,n||xh(i),i.useStyle(t.getItemVisual(e,"style")),i.style.lineJoin="round",n?(i.setShape({points:a.points}),i.style.opacity=0,fh(i,{style:{opacity:l}},r,e)):dh(i,{style:{opacity:l},shape:{points:a.points}},r,e),Zl(i,o),this._updateLabel(t,e),Hl(this,s.get("focus"),s.get("blurScope"),s.get("disabled"))},e.prototype._updateLabel=function(t,e){var n=this,i=this.getTextGuideLine(),r=n.getTextContent(),o=t.hostModel,a=t.getItemModel(e),s=t.getItemLayout(e).label,l=t.getItemVisual(e,"style"),u=l.fill;Qh(r,tc(a),{labelFetcher:t.hostModel,labelDataIndex:e,defaultOpacity:l.opacity,defaultText:t.getName(e)},{normal:{align:s.textAlign,verticalAlign:s.verticalAlign}}),n.setTextConfig({local:!0,inside:!!s.inside,insideStroke:u,outsideFill:u});var h=s.linePoints;i.setShape({points:h}),n.textGuideLineConfig={anchor:h?new Ce(h[0][0],h[0][1]):null},dh(r,{style:{x:s.x,y:s.y}},o,e),r.attr({rotation:s.rotation,originX:s.x,originY:s.y,z2:10}),xb(n,_b(a),{stroke:u})},e}(Gu),ek=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.ignoreLabelLineUpdate=!0,n}return n(e,t),e.prototype.render=function(t,e,n){var i=t.getData(),r=this._data,o=this.group;i.diff(r).add((function(t){var e=new tk(i,t);i.setItemGraphicEl(t,e),o.add(e)})).update((function(t,e){var n=r.getItemGraphicEl(e);n.updateData(i,t),o.add(n),i.setItemGraphicEl(t,n)})).remove((function(e){mh(r.getItemGraphicEl(e),t,e)})).execute(),this._data=i},e.prototype.remove=function(){this.group.removeAll(),this._data=null},e.prototype.dispose=function(){},e.type="funnel",e}(Tg),nk=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.init=function(e){t.prototype.init.apply(this,arguments),this.legendVisualProvider=new mM(W(this.getData,this),W(this.getRawData,this)),this._defaultLabelLine(e)},e.prototype.getInitialData=function(t,e){return vM(this,{coordDimensions:["value"],encodeDefaulter:H($p,this)})},e.prototype._defaultLabelLine=function(t){bo(t,"labelLine",["show"]);var e=t.labelLine,n=t.emphasis.labelLine;e.show=e.show&&t.label.show,n.show=n.show&&t.emphasis.label.show},e.prototype.getDataParams=function(e){var n=this.getData(),i=t.prototype.getDataParams.call(this,e),r=n.mapDimension("value"),o=n.getSum(r);return i.percent=o?+(n.get(r,e)/o*100).toFixed(2):0,i.$vars.push("percent"),i},e.type="series.funnel",e.defaultOption={z:2,legendHoverLink:!0,colorBy:"data",left:80,top:60,right:80,bottom:60,minSize:"0%",maxSize:"100%",sort:"descending",orient:"vertical",gap:0,funnelAlign:"center",label:{show:!0,position:"outer"},labelLine:{show:!0,length:20,lineStyle:{width:1}},itemStyle:{borderColor:"#fff",borderWidth:1},emphasis:{label:{show:!0}},select:{itemStyle:{borderColor:"#212121"}}},e}(fg);function ik(t,e){t.eachSeriesByType("funnel",(function(t){var n=t.getData(),i=n.mapDimension("value"),r=t.get("sort"),o=function(t,e){return Tp(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()})}(t,e),a=t.get("orient"),s=o.width,l=o.height,u=function(t,e){for(var n=t.mapDimension("value"),i=t.mapArray(n,(function(t){return t})),r=[],o="ascending"===e,a=0,s=t.count();a5)return;var i=this._model.coordinateSystem.getSlidedAxisExpandWindow([t.offsetX,t.offsetY]);"none"!==i.behavior&&this._dispatchExpand({axisExpandWindow:i.axisExpandWindow})}this._mouseDownPoint=null},mousemove:function(t){if(!this._mouseDownPoint&&yk(this,"mousemove")){var e=this._model,n=e.coordinateSystem.getSlidedAxisExpandWindow([t.offsetX,t.offsetY]),i=n.behavior;"jump"===i&&this._throttledDispatchExpand.debounceNextCall(e.get("axisExpandDebounce")),this._throttledDispatchExpand("none"===i?null:{axisExpandWindow:n.axisExpandWindow,animation:"jump"===i?null:{duration:0}})}}};function yk(t,e){var n=t._model;return n.get("axisExpandable")&&n.get("axisExpandTriggerOn")===e}var vk=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.init=function(){t.prototype.init.apply(this,arguments),this.mergeOption({})},e.prototype.mergeOption=function(t){var e=this.option;t&&C(e,t,!0),this._initDimensions()},e.prototype.contains=function(t,e){var n=t.get("parallelIndex");return null!=n&&e.getComponent("parallel",n)===this},e.prototype.setAxisExpand=function(t){E(["axisExpandable","axisExpandCenter","axisExpandCount","axisExpandWidth","axisExpandWindow"],(function(e){t.hasOwnProperty(e)&&(this.option[e]=t[e])}),this)},e.prototype._initDimensions=function(){var t=this.dimensions=[],e=this.parallelAxisIndex=[];E(B(this.ecModel.queryComponents({mainType:"parallelAxis"}),(function(t){return(t.get("parallelIndex")||0)===this.componentIndex}),this),(function(n){t.push("dim"+n.get("dim")),e.push(n.componentIndex)}))},e.type="parallel",e.dependencies=["parallelAxis"],e.layoutMode="box",e.defaultOption={z:0,left:80,top:60,right:80,bottom:60,layout:"horizontal",axisExpandable:!1,axisExpandCenter:null,axisExpandCount:0,axisExpandWidth:50,axisExpandRate:17,axisExpandDebounce:50,axisExpandSlideTriggerArea:[-.15,.05,.4],axisExpandTriggerOn:"click",parallelAxisDefault:null},e}(Op),mk=function(t){function e(e,n,i,r,o){var a=t.call(this,e,n,i)||this;return a.type=r||"value",a.axisIndex=o,a}return n(e,t),e.prototype.isHorizontal=function(){return"horizontal"!==this.coordinateSystem.getModel().get("layout")},e}(q_);function xk(t,e,n,i,r,o){t=t||0;var a=n[1]-n[0];if(null!=r&&(r=bk(r,[0,a])),null!=o&&(o=Math.max(o,null!=r?r:0)),"all"===i){var s=Math.abs(e[1]-e[0]);s=bk(s,[0,a]),r=o=bk(s,[r,o]),i=0}e[0]=bk(e[0],n),e[1]=bk(e[1],n);var l=_k(e,i);e[i]+=t;var u,h=r||0,c=n.slice();return l.sign<0?c[0]+=h:c[1]-=h,e[i]=bk(e[i],c),u=_k(e,i),null!=r&&(u.sign!==l.sign||u.spano&&(e[1-i]=e[i]+u.sign*o),e}function _k(t,e){var n=t[e]-t[1-e];return{span:Math.abs(n),sign:n>0?-1:n<0?1:e?-1:1}}function bk(t,e){return Math.min(null!=e[1]?e[1]:1/0,Math.max(null!=e[0]?e[0]:-1/0,t))}var wk=E,Sk=Math.min,Mk=Math.max,Ik=Math.floor,Tk=Math.ceil,Ck=Xr,Dk=Math.PI,Ak=function(){function t(t,e,n){this.type="parallel",this._axesMap=yt(),this._axesLayout={},this.dimensions=t.dimensions,this._model=t,this._init(t,e,n)}return t.prototype._init=function(t,e,n){var i=t.dimensions,r=t.parallelAxisIndex;wk(i,(function(t,n){var i=r[n],o=e.getComponent("parallelAxis",i),a=this._axesMap.set(t,new mk(t,c_(o),[0,0],o.get("type"),i)),s="category"===a.type;a.onBand=s&&o.get("boundaryGap"),a.inverse=o.get("inverse"),o.axis=a,a.model=o,a.coordinateSystem=o.coordinateSystem=this}),this)},t.prototype.update=function(t,e){this._updateAxesFromSeries(this._model,t)},t.prototype.containPoint=function(t){var e=this._makeLayoutInfo(),n=e.axisBase,i=e.layoutBase,r=e.pixelDimIndex,o=t[1-r],a=t[r];return o>=n&&o<=n+e.axisLength&&a>=i&&a<=i+e.layoutLength},t.prototype.getModel=function(){return this._model},t.prototype._updateAxesFromSeries=function(t,e){e.eachSeries((function(n){if(t.contains(n,e)){var i=n.getData();wk(this.dimensions,(function(t){var e=this._axesMap.get(t);e.scale.unionExtentFromData(i,i.mapDimension(t)),h_(e.scale,e.model)}),this)}}),this)},t.prototype.resize=function(t,e){this._rect=Tp(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()}),this._layoutAxes()},t.prototype.getRect=function(){return this._rect},t.prototype._makeLayoutInfo=function(){var t,e=this._model,n=this._rect,i=["x","y"],r=["width","height"],o=e.get("layout"),a="horizontal"===o?0:1,s=n[r[a]],l=[0,s],u=this.dimensions.length,h=kk(e.get("axisExpandWidth"),l),c=kk(e.get("axisExpandCount")||0,[0,u]),p=e.get("axisExpandable")&&u>3&&u>c&&c>1&&h>0&&s>0,d=e.get("axisExpandWindow");d?(t=kk(d[1]-d[0],l),d[1]=d[0]+t):(t=kk(h*(c-1),l),(d=[h*(e.get("axisExpandCenter")||Ik(u/2))-t/2])[1]=d[0]+t);var f=(s-t)/(u-c);f<3&&(f=0);var g=[Ik(Ck(d[0]/h,1))+1,Tk(Ck(d[1]/h,1))-1],y=f/h*d[0];return{layout:o,pixelDimIndex:a,layoutBase:n[i[a]],layoutLength:s,axisBase:n[i[1-a]],axisLength:n[r[1-a]],axisExpandable:p,axisExpandWidth:h,axisCollapseWidth:f,axisExpandWindow:d,axisCount:u,winInnerIndices:g,axisExpandWindow0Pos:y}},t.prototype._layoutAxes=function(){var t=this._rect,e=this._axesMap,n=this.dimensions,i=this._makeLayoutInfo(),r=i.layout;e.each((function(t){var e=[0,i.axisLength],n=t.inverse?1:0;t.setExtent(e[n],e[1-n])})),wk(n,(function(e,n){var o=(i.axisExpandable?Pk:Lk)(n,i),a={horizontal:{x:o.position,y:i.axisLength},vertical:{x:0,y:o.position}},s={horizontal:Dk/2,vertical:0},l=[a[r].x+t.x,a[r].y+t.y],u=s[r],h=[1,0,0,1,0,0];we(h,h,u),be(h,h,l),this._axesLayout[e]={position:l,rotation:u,transform:h,axisNameAvailableWidth:o.axisNameAvailableWidth,axisLabelShow:o.axisLabelShow,nameTruncateMaxWidth:o.nameTruncateMaxWidth,tickDirection:1,labelDirection:1}}),this)},t.prototype.getAxis=function(t){return this._axesMap.get(t)},t.prototype.dataToPoint=function(t,e){return this.axisCoordToPoint(this._axesMap.get(e).dataToCoord(t),e)},t.prototype.eachActiveState=function(t,e,n,i){null==n&&(n=0),null==i&&(i=t.count());var r=this._axesMap,o=this.dimensions,a=[],s=[];E(o,(function(e){a.push(t.mapDimension(e)),s.push(r.get(e).model)}));for(var l=this.hasAxisBrushed(),u=n;ur*(1-h[0])?(l="jump",a=s-r*(1-h[2])):(a=s-r*h[1])>=0&&(a=s-r*(1-h[1]))<=0&&(a=0),(a*=e.axisExpandWidth/u)?xk(a,i,o,"all"):l="none";else{var p=i[1]-i[0];(i=[Mk(0,o[1]*s/p-p/2)])[1]=Sk(o[1],i[0]+p),i[0]=i[1]-p}return{axisExpandWindow:i,behavior:l}},t}();function kk(t,e){return Sk(Mk(t,e[0]),e[1])}function Lk(t,e){var n=e.layoutLength/(e.axisCount-1);return{position:n*t,axisNameAvailableWidth:n,axisLabelShow:!0}}function Pk(t,e){var n,i,r=e.layoutLength,o=e.axisExpandWidth,a=e.axisCount,s=e.axisCollapseWidth,l=e.winInnerIndices,u=s,h=!1;return t=0;n--)Zr(e[n])},e.prototype.getActiveState=function(t){var e=this.activeIntervals;if(!e.length)return"normal";if(null==t||isNaN(+t))return"inactive";if(1===e.length){var n=e[0];if(n[0]<=t&&t<=n[1])return"active"}else for(var i=0,r=e.length;i6}(t)||o){if(a&&!o){"single"===s.brushMode&&Qk(t);var l=T(s);l.brushType=yL(l.brushType,a),l.panelId=a===Nk?null:a.panelId,o=t._creatingCover=Uk(t,l),t._covers.push(o)}if(o){var u=xL[yL(t._brushType,a)];o.__brushOption.range=u.getCreatingRange(pL(t,o,t._track)),i&&(Xk(t,o),u.updateCommon(t,o)),Zk(t,o),r={isEnd:i}}}else i&&"single"===s.brushMode&&s.removeOnClick&&$k(t,e,n)&&Qk(t)&&(r={isEnd:i,removeOnClick:!0});return r}function yL(t,e){return"auto"===t?e.defaultBrushType:t}var vL={mousedown:function(t){if(this._dragging)mL(this,t);else if(!t.target||!t.target.draggable){dL(t);var e=this.group.transformCoordToLocal(t.offsetX,t.offsetY);this._creatingCover=null,(this._creatingPanel=$k(this,t,e))&&(this._dragging=!0,this._track=[e.slice()])}},mousemove:function(t){var e=t.offsetX,n=t.offsetY,i=this.group.transformCoordToLocal(e,n);if(function(t,e,n){if(t._brushType&&!function(t,e,n){var i=t._zr;return e<0||e>i.getWidth()||n<0||n>i.getHeight()}(t,e.offsetX,e.offsetY)){var i=t._zr,r=t._covers,o=$k(t,e,n);if(!t._dragging)for(var a=0;a=0&&(o[r[a].depth]=new Sc(r[a],this,e));if(i&&n){var s=UA(i,n,this,!0,(function(t,e){t.wrapMethod("getItemModel",(function(t,e){var n=t.parentModel,i=n.getData().getItemLayout(e);if(i){var r=i.depth,o=n.levelModels[r];o&&(t.parentModel=o)}return t})),e.wrapMethod("getItemModel",(function(t,e){var n=t.parentModel,i=n.getGraph().getEdgeByIndex(e).node1.getLayout();if(i){var r=i.depth,o=n.levelModels[r];o&&(t.parentModel=o)}return t}))}));return s.data}},e.prototype.setNodePosition=function(t,e){var n=(this.option.data||this.option.nodes)[t];n.localX=e[0],n.localY=e[1]},e.prototype.getGraph=function(){return this.getData().graph},e.prototype.getEdgeData=function(){return this.getGraph().edgeData},e.prototype.formatTooltip=function(t,e,n){function i(t){return isNaN(t)||null==t}if("edge"===n){var r=this.getDataParams(t,n),o=r.data,a=r.value;return Qf("nameValue",{name:o.source+" -- "+o.target,value:a,noValue:i(a)})}var s=this.getGraph().getNodeByIndex(t).getLayout().value,l=this.getDataParams(t,n).data.name;return Qf("nameValue",{name:null!=l?l+"":null,value:s,noValue:i(s)})},e.prototype.optionUpdated=function(){},e.prototype.getDataParams=function(e,n){var i=t.prototype.getDataParams.call(this,e,n);if(null==i.value&&"node"===n){var r=this.getGraph().getNodeByIndex(e).getLayout().value;i.value=r}return i},e.type="series.sankey",e.defaultOption={z:2,coordinateSystem:"view",left:"5%",top:"5%",right:"20%",bottom:"5%",orient:"horizontal",nodeWidth:20,nodeGap:8,draggable:!0,layoutIterations:32,label:{show:!0,position:"right",fontSize:12},edgeLabel:{show:!1,fontSize:12},levels:[],nodeAlign:"justify",lineStyle:{color:"#314656",opacity:.2,curveness:.5},emphasis:{label:{show:!0},lineStyle:{opacity:.5}},select:{itemStyle:{borderColor:"#212121"}},animationEasing:"linear",animationDuration:1e3},e}(fg);function RL(t,e){t.eachSeriesByType("sankey",(function(t){var n=t.get("nodeWidth"),i=t.get("nodeGap"),r=function(t,e){return Tp(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()})}(t,e);t.layoutInfo=r;var o=r.width,a=r.height,s=t.getGraph(),l=s.nodes,u=s.edges;!function(t){E(t,(function(t){var e=YL(t.outEdges,HL),n=YL(t.inEdges,HL),i=t.getValue()||0,r=Math.max(e,n,i);t.setLayout({value:r},!0)}))}(l),function(t,e,n,i,r,o,a,s,l){(function(t,e,n,i,r,o,a){for(var s=[],l=[],u=[],h=[],c=0,p=0;p=0;v&&y.depth>d&&(d=y.depth),g.setLayout({depth:v?y.depth:c},!0),"vertical"===o?g.setLayout({dy:n},!0):g.setLayout({dx:n},!0);for(var m=0;mc-1?d:c-1;a&&"left"!==a&&function(t,e,n,i){if("right"===e){for(var r=[],o=t,a=0;o.length;){for(var s=0;s0;o--)zL(s,l*=.99,a),EL(s,r,n,i,a),UL(s,l,a),EL(s,r,n,i,a)}(t,e,o,r,i,a,s),function(t,e){var n="vertical"===e?"x":"y";E(t,(function(t){t.outEdges.sort((function(t,e){return t.node2.getLayout()[n]-e.node2.getLayout()[n]})),t.inEdges.sort((function(t,e){return t.node1.getLayout()[n]-e.node1.getLayout()[n]}))})),E(t,(function(t){var e=0,n=0;E(t.outEdges,(function(t){t.setLayout({sy:e},!0),e+=t.getLayout().dy})),E(t.inEdges,(function(t){t.setLayout({ty:n},!0),n+=t.getLayout().dy}))}))}(t,s)}(l,u,n,i,o,a,0!==B(l,(function(t){return 0===t.getLayout().value})).length?0:t.get("layoutIterations"),t.get("orient"),t.get("nodeAlign"))}))}function NL(t){var e=t.hostGraph.data.getRawDataItem(t.dataIndex);return null!=e.depth&&e.depth>=0}function EL(t,e,n,i,r){var o="vertical"===r?"x":"y";E(t,(function(t){var a,s,l;t.sort((function(t,e){return t.getLayout()[o]-e.getLayout()[o]}));for(var u=0,h=t.length,c="vertical"===r?"dx":"dy",p=0;p0&&(a=s.getLayout()[o]+l,"vertical"===r?s.setLayout({x:a},!0):s.setLayout({y:a},!0)),u=s.getLayout()[o]+s.getLayout()[c]+e;if((l=u-e-("vertical"===r?i:n))>0){a=s.getLayout()[o]-l,"vertical"===r?s.setLayout({x:a},!0):s.setLayout({y:a},!0),u=a;for(p=h-2;p>=0;--p)(l=(s=t[p]).getLayout()[o]+s.getLayout()[c]+e-u)>0&&(a=s.getLayout()[o]-l,"vertical"===r?s.setLayout({x:a},!0):s.setLayout({y:a},!0)),u=s.getLayout()[o]}}))}function zL(t,e,n){E(t.slice().reverse(),(function(t){E(t,(function(t){if(t.outEdges.length){var i=YL(t.outEdges,VL,n)/YL(t.outEdges,HL);if(isNaN(i)){var r=t.outEdges.length;i=r?YL(t.outEdges,BL,n)/r:0}if("vertical"===n){var o=t.getLayout().x+(i-WL(t,n))*e;t.setLayout({x:o},!0)}else{var a=t.getLayout().y+(i-WL(t,n))*e;t.setLayout({y:a},!0)}}}))}))}function VL(t,e){return WL(t.node2,e)*t.getValue()}function BL(t,e){return WL(t.node2,e)}function FL(t,e){return WL(t.node1,e)*t.getValue()}function GL(t,e){return WL(t.node1,e)}function WL(t,e){return"vertical"===e?t.getLayout().x+t.getLayout().dx/2:t.getLayout().y+t.getLayout().dy/2}function HL(t){return t.getValue()}function YL(t,e,n){for(var i=0,r=t.length,o=-1;++oo&&(o=e)})),E(n,(function(e){var n=new dD({type:"color",mappingMethod:"linear",dataExtent:[r,o],visual:t.get("color")}).mapValueToVisual(e.getLayout().value),i=e.getModel().get(["itemStyle","color"]);null!=i?(e.setVisual("color",i),e.setVisual("style",{fill:i})):(e.setVisual("color",n),e.setVisual("style",{fill:n}))}))}i.length&&E(i,(function(t){var e=t.getModel().get("lineStyle");t.setVisual("style",e)}))}))}var ZL=function(){function t(){}return t.prototype.getInitialData=function(t,e){var n,i,r=e.getComponent("xAxis",this.get("xAxisIndex")),o=e.getComponent("yAxis",this.get("yAxisIndex")),a=r.get("type"),s=o.get("type");"category"===a?(t.layout="horizontal",n=r.getOrdinalMeta(),i=!0):"category"===s?(t.layout="vertical",n=o.getOrdinalMeta(),i=!0):t.layout=t.layout||"horizontal";var l=["x","y"],u="horizontal"===t.layout?0:1,h=this._baseAxisDim=l[u],c=l[1-u],p=[r,o],d=p[u].get("type"),f=p[1-u].get("type"),g=t.data;if(g&&i){var y=[];E(g,(function(t,e){var n;Y(t)?(n=t.slice(),t.unshift(e)):Y(t.value)?((n=A({},t)).value=n.value.slice(),t.value.unshift(e)):n=t,y.push(n)})),t.data=y}var v=this.defaultValueDimensions,m=[{name:h,type:Rm(d),ordinalMeta:n,otherDims:{tooltip:!1,itemName:0},dimsDef:["base"]},{name:c,type:Rm(f),dimsDef:v.slice()}];return vM(this,{coordDimensions:m,dimensionsCount:v.length+1,encodeDefaulter:H(Kp,m,this)})},t.prototype.getBaseAxis=function(){var t=this._baseAxisDim;return this.ecModel.getComponent(t+"Axis",this.get(t+"AxisIndex")).axis},t}(),jL=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.defaultValueDimensions=[{name:"min",defaultTooltip:!0},{name:"Q1",defaultTooltip:!0},{name:"median",defaultTooltip:!0},{name:"Q3",defaultTooltip:!0},{name:"max",defaultTooltip:!0}],n.visualDrawType="stroke",n}return n(e,t),e.type="series.boxplot",e.dependencies=["xAxis","yAxis","grid"],e.defaultOption={z:2,coordinateSystem:"cartesian2d",legendHoverLink:!0,layout:null,boxWidth:[7,50],itemStyle:{color:"#fff",borderWidth:1},emphasis:{scale:!0,itemStyle:{borderWidth:2,shadowBlur:5,shadowOffsetX:1,shadowOffsetY:1,shadowColor:"rgba(0,0,0,0.2)"}},animationDuration:800},e}(fg);R(jL,ZL,!0);var qL=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){var i=t.getData(),r=this.group,o=this._data;this._data||r.removeAll();var a="horizontal"===t.get("layout")?1:0;i.diff(o).add((function(t){if(i.hasValue(t)){var e=JL(i.getItemLayout(t),i,t,a,!0);i.setItemGraphicEl(t,e),r.add(e)}})).update((function(t,e){var n=o.getItemGraphicEl(e);if(i.hasValue(t)){var s=i.getItemLayout(t);n?(xh(n),QL(s,n,i,t)):n=JL(s,i,t,a),r.add(n),i.setItemGraphicEl(t,n)}else r.remove(n)})).remove((function(t){var e=o.getItemGraphicEl(t);e&&r.remove(e)})).execute(),this._data=i},e.prototype.remove=function(t){var e=this.group,n=this._data;this._data=null,n&&n.eachItemGraphicEl((function(t){t&&e.remove(t)}))},e.type="boxplot",e}(Tg),KL=function(){},$L=function(t){function e(e){var n=t.call(this,e)||this;return n.type="boxplotBoxPath",n}return n(e,t),e.prototype.getDefaultShape=function(){return new KL},e.prototype.buildPath=function(t,e){var n=e.points,i=0;for(t.moveTo(n[i][0],n[i][1]),i++;i<4;i++)t.lineTo(n[i][0],n[i][1]);for(t.closePath();ig){var _=[v,x];i.push(_)}}}return{boxData:n,outliers:i}}(e.getRawData(),t.config);return[{dimensions:["ItemName","Low","Q1","Q2","Q3","High"],data:i.boxData},{data:i.outliers}]}};var rP=["color","borderColor"],oP=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){this.group.removeClipPath(),this._progressiveEls=null,this._updateDrawMode(t),this._isLargeDraw?this._renderLarge(t):this._renderNormal(t)},e.prototype.incrementalPrepareRender=function(t,e,n){this._clear(),this._updateDrawMode(t)},e.prototype.incrementalRender=function(t,e,n,i){this._progressiveEls=[],this._isLargeDraw?this._incrementalRenderLarge(t,e):this._incrementalRenderNormal(t,e)},e.prototype.eachRendered=function(t){jh(this._progressiveEls||this.group,t)},e.prototype._updateDrawMode=function(t){var e=t.pipelineContext.large;null!=this._isLargeDraw&&e===this._isLargeDraw||(this._isLargeDraw=e,this._clear())},e.prototype._renderNormal=function(t){var e=t.getData(),n=this._data,i=this.group,r=e.getLayout("isSimpleBox"),o=t.get("clip",!0),a=t.coordinateSystem,s=a.getArea&&a.getArea();this._data||i.removeAll(),e.diff(n).add((function(n){if(e.hasValue(n)){var a=e.getItemLayout(n);if(o&&uP(s,a))return;var l=lP(a,n,!0);fh(l,{shape:{points:a.ends}},t,n),hP(l,e,n,r),i.add(l),e.setItemGraphicEl(n,l)}})).update((function(a,l){var u=n.getItemGraphicEl(l);if(e.hasValue(a)){var h=e.getItemLayout(a);o&&uP(s,h)?i.remove(u):(u?(dh(u,{shape:{points:h.ends}},t,a),xh(u)):u=lP(h),hP(u,e,a,r),i.add(u),e.setItemGraphicEl(a,u))}else i.remove(u)})).remove((function(t){var e=n.getItemGraphicEl(t);e&&i.remove(e)})).execute(),this._data=e},e.prototype._renderLarge=function(t){this._clear(),fP(t,this.group);var e=t.get("clip",!0)?yS(t.coordinateSystem,!1,t):null;e?this.group.setClipPath(e):this.group.removeClipPath()},e.prototype._incrementalRenderNormal=function(t,e){for(var n,i=e.getData(),r=i.getLayout("isSimpleBox");null!=(n=t.next());){var o=lP(i.getItemLayout(n));hP(o,i,n,r),o.incremental=!0,this.group.add(o),this._progressiveEls.push(o)}},e.prototype._incrementalRenderLarge=function(t,e){fP(e,this.group,this._progressiveEls,!0)},e.prototype.remove=function(t){this._clear()},e.prototype._clear=function(){this.group.removeAll(),this._data=null},e.type="candlestick",e}(Tg),aP=function(){},sP=function(t){function e(e){var n=t.call(this,e)||this;return n.type="normalCandlestickBox",n}return n(e,t),e.prototype.getDefaultShape=function(){return new aP},e.prototype.buildPath=function(t,e){var n=e.points;this.__simpleBox?(t.moveTo(n[4][0],n[4][1]),t.lineTo(n[6][0],n[6][1])):(t.moveTo(n[0][0],n[0][1]),t.lineTo(n[1][0],n[1][1]),t.lineTo(n[2][0],n[2][1]),t.lineTo(n[3][0],n[3][1]),t.closePath(),t.moveTo(n[4][0],n[4][1]),t.lineTo(n[5][0],n[5][1]),t.moveTo(n[6][0],n[6][1]),t.lineTo(n[7][0],n[7][1]))},e}(Ms);function lP(t,e,n){var i=t.ends;return new sP({shape:{points:n?cP(i,t):i},z2:100})}function uP(t,e){for(var n=!0,i=0;i0?"borderColor":"borderColor0"])||n.get(["itemStyle",t>0?"color":"color0"]);0===t&&(r=n.get(["itemStyle","borderColorDoji"]));var o=n.getModel("itemStyle").getItemStyle(rP);e.useStyle(o),e.style.fill=null,e.style.stroke=r}var yP=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.defaultValueDimensions=[{name:"open",defaultTooltip:!0},{name:"close",defaultTooltip:!0},{name:"lowest",defaultTooltip:!0},{name:"highest",defaultTooltip:!0}],n}return n(e,t),e.prototype.getShadowDim=function(){return"open"},e.prototype.brushSelector=function(t,e,n){var i=e.getItemLayout(t);return i&&n.rect(i.brushRect)},e.type="series.candlestick",e.dependencies=["xAxis","yAxis","grid"],e.defaultOption={z:2,coordinateSystem:"cartesian2d",legendHoverLink:!0,layout:null,clip:!0,itemStyle:{color:"#eb5454",color0:"#47b262",borderColor:"#eb5454",borderColor0:"#47b262",borderColorDoji:null,borderWidth:1},emphasis:{scale:!0,itemStyle:{borderWidth:2}},barMaxWidth:null,barMinWidth:null,barWidth:null,large:!0,largeThreshold:600,progressive:3e3,progressiveThreshold:1e4,progressiveChunkMode:"mod",animationEasing:"linear",animationDuration:300},e}(fg);function vP(t){t&&Y(t.series)&&E(t.series,(function(t){q(t)&&"k"===t.type&&(t.type="candlestick")}))}R(yP,ZL,!0);var mP=["itemStyle","borderColor"],xP=["itemStyle","borderColor0"],_P=["itemStyle","borderColorDoji"],bP=["itemStyle","color"],wP=["itemStyle","color0"],SP={seriesType:"candlestick",plan:Sg(),performRawSeries:!0,reset:function(t,e){function n(t,e){return e.get(t>0?bP:wP)}function i(t,e){return e.get(0===t?_P:t>0?mP:xP)}if(!e.isSeriesFiltered(t))return!t.pipelineContext.large&&{progress:function(t,e){for(var r;null!=(r=t.next());){var o=e.getItemModel(r),a=e.getItemLayout(r).sign,s=o.getItemStyle();s.fill=n(a,o),s.stroke=i(a,o)||s.fill,A(e.ensureUniqueItemVisual(r,"style"),s)}}}}},MP={seriesType:"candlestick",plan:Sg(),reset:function(t){var e=t.coordinateSystem,n=t.getData(),i=function(t,e){var n,i=t.getBaseAxis(),r="category"===i.type?i.getBandWidth():(n=i.getExtent(),Math.abs(n[1]-n[0])/e.count()),o=Ur(rt(t.get("barMaxWidth"),r),r),a=Ur(rt(t.get("barMinWidth"),1),r),s=t.get("barWidth");return null!=s?Ur(s,r):Math.max(Math.min(r/2,o),a)}(t,n),r=["x","y"],o=n.getDimensionIndex(n.mapDimension(r[0])),a=z(n.mapDimensionsAll(r[1]),n.getDimensionIndex,n),s=a[0],l=a[1],u=a[2],h=a[3];if(n.setLayout({candleWidth:i,isSimpleBox:i<=1.3}),!(o<0||a.length<4))return{progress:t.pipelineContext.large?function(n,i){var r,a,c=Ax(4*n.count),p=0,d=[],f=[],g=i.getStore(),y=!!t.get(["itemStyle","borderColorDoji"]);for(;null!=(a=n.next());){var v=g.get(o,a),m=g.get(s,a),x=g.get(l,a),_=g.get(u,a),b=g.get(h,a);isNaN(v)||isNaN(_)||isNaN(b)?(c[p++]=NaN,p+=3):(c[p++]=IP(g,a,m,x,l,y),d[0]=v,d[1]=_,r=e.dataToPoint(d,null,f),c[p++]=r?r[0]:NaN,c[p++]=r?r[1]:NaN,d[1]=b,r=e.dataToPoint(d,null,f),c[p++]=r?r[1]:NaN)}i.setLayout("largePoints",c)}:function(t,n){var r,a=n.getStore();for(;null!=(r=t.next());){var c=a.get(o,r),p=a.get(s,r),d=a.get(l,r),f=a.get(u,r),g=a.get(h,r),y=Math.min(p,d),v=Math.max(p,d),m=M(y,c),x=M(v,c),_=M(f,c),b=M(g,c),w=[];I(w,x,0),I(w,m,1),w.push(C(b),C(x),C(_),C(m));var S=!!n.getItemModel(r).get(["itemStyle","borderColorDoji"]);n.setItemLayout(r,{sign:IP(a,r,p,d,l,S),initBaseline:p>d?x[1]:m[1],ends:w,brushRect:T(f,g,c)})}function M(t,n){var i=[];return i[0]=n,i[1]=t,isNaN(n)||isNaN(t)?[NaN,NaN]:e.dataToPoint(i)}function I(t,e,n){var r=e.slice(),o=e.slice();r[0]=Rh(r[0]+i/2,1,!1),o[0]=Rh(o[0]-i/2,1,!0),n?t.push(r,o):t.push(o,r)}function T(t,e,n){var r=M(t,n),o=M(e,n);return r[0]-=i/2,o[0]-=i/2,{x:r[0],y:r[1],width:i,height:o[1]-r[1]}}function C(t){return t[0]=Rh(t[0],1),t}}}}};function IP(t,e,n,i,r,o){return n>i?-1:n0?t.get(r,e-1)<=i?1:-1:1}function TP(t,e){var n=e.rippleEffectColor||e.color;t.eachChild((function(t){t.attr({z:e.z,zlevel:e.zlevel,style:{stroke:"stroke"===e.brushType?n:null,fill:"fill"===e.brushType?n:null}})}))}var CP=function(t){function e(e,n){var i=t.call(this)||this,r=new Jw(e,n),o=new Er;return i.add(r),i.add(o),i.updateData(e,n),i}return n(e,t),e.prototype.stopEffectAnimation=function(){this.childAt(1).removeAll()},e.prototype.startEffectAnimation=function(t){for(var e=t.symbolType,n=t.color,i=t.rippleNumber,r=this.childAt(1),o=0;o0&&(o=this._getLineLength(i)/l*1e3),o!==this._period||a!==this._loop||s!==this._roundTrip){i.stopAnimation();var h=void 0;h=U(u)?u(n):u,i.__t>0&&(h=-o*i.__t),this._animateSymbol(i,o,h,a,s)}this._period=o,this._loop=a,this._roundTrip=s}},e.prototype._animateSymbol=function(t,e,n,i,r){if(e>0){t.__t=0;var o=this,a=t.animate("",i).when(r?2*e:e,{__t:r?2:1}).delay(n).during((function(){o._updateSymbolPosition(t)}));i||a.done((function(){o.remove(t)})),a.start()}},e.prototype._getLineLength=function(t){return Vt(t.__p1,t.__cp1)+Vt(t.__cp1,t.__p2)},e.prototype._updateAnimationPoints=function(t,e){t.__p1=e[0],t.__p2=e[1],t.__cp1=e[2]||[(e[0][0]+e[1][0])/2,(e[0][1]+e[1][1])/2]},e.prototype.updateData=function(t,e,n){this.childAt(0).updateData(t,e,n),this._updateEffectSymbol(t,e)},e.prototype._updateSymbolPosition=function(t){var e=t.__p1,n=t.__p2,i=t.__cp1,r=t.__t<1?t.__t:2-t.__t,o=[t.x,t.y],a=o.slice(),s=Mn,l=In;o[0]=s(e[0],i[0],n[0],r),o[1]=s(e[1],i[1],n[1],r);var u=t.__t<1?l(e[0],i[0],n[0],r):l(n[0],i[0],e[0],1-r),h=t.__t<1?l(e[1],i[1],n[1],r):l(n[1],i[1],e[1],1-r);t.rotation=-Math.atan2(h,u)-Math.PI/2,"line"!==this._symbolType&&"rect"!==this._symbolType&&"roundRect"!==this._symbolType||(void 0!==t.__lastT&&t.__lastT=0&&!(i[o]<=e);o--);o=Math.min(o,r-2)}else{for(o=a;oe);o++);o=Math.min(o-1,r-2)}var s=(e-i[o])/(i[o+1]-i[o]),l=n[o],u=n[o+1];t.x=l[0]*(1-s)+s*u[0],t.y=l[1]*(1-s)+s*u[1];var h=t.__t<1?u[0]-l[0]:l[0]-u[0],c=t.__t<1?u[1]-l[1]:l[1]-u[1];t.rotation=-Math.atan2(c,h)-Math.PI/2,this._lastFrame=o,this._lastFramePercent=e,t.ignore=!1}},e}(kP),OP=function(){this.polyline=!1,this.curveness=0,this.segs=[]},RP=function(t){function e(e){var n=t.call(this,e)||this;return n._off=0,n.hoverDataIdx=-1,n}return n(e,t),e.prototype.reset=function(){this.notClear=!1,this._off=0},e.prototype.getDefaultStyle=function(){return{stroke:"#000",fill:null}},e.prototype.getDefaultShape=function(){return new OP},e.prototype.buildPath=function(t,e){var n,i=e.segs,r=e.curveness;if(e.polyline)for(n=this._off;n0){t.moveTo(i[n++],i[n++]);for(var a=1;a0){var c=(s+u)/2-(l-h)*r,p=(l+h)/2-(u-s)*r;t.quadraticCurveTo(c,p,u,h)}else t.lineTo(u,h)}this.incremental&&(this._off=n,this.notClear=!0)},e.prototype.findDataIndex=function(t,e){var n=this.shape,i=n.segs,r=n.curveness,o=this.style.lineWidth;if(n.polyline)for(var a=0,s=0;s0)for(var u=i[s++],h=i[s++],c=1;c0){if(ss(u,h,(u+p)/2-(h-d)*r,(h+d)/2-(p-u)*r,p,d,o,t,e))return a}else if(os(u,h,p,d,o,t,e))return a;a++}return-1},e.prototype.contain=function(t,e){var n=this.transformCoordToLocal(t,e),i=this.getBoundingRect();return t=n[0],e=n[1],i.contain(t,e)?(this.hoverDataIdx=this.findDataIndex(t,e))>=0:(this.hoverDataIdx=-1,!1)},e.prototype.getBoundingRect=function(){var t=this._rect;if(!t){for(var e=this.shape.segs,n=1/0,i=1/0,r=-1/0,o=-1/0,a=0;a0&&(o.dataIndex=n+t.__startIndex)}))},t.prototype._clear=function(){this._newAdded=[],this.group.removeAll()},t}(),EP={seriesType:"lines",plan:Sg(),reset:function(t){var e=t.coordinateSystem;if(e){var n=t.get("polyline"),i=t.pipelineContext.large;return{progress:function(r,o){var a=[];if(i){var s=void 0,l=r.end-r.start;if(n){for(var u=0,h=r.start;h0&&(l||s.configLayer(o,{motionBlur:!0,lastFrameAlpha:Math.max(Math.min(a/10+.9,1),0)})),r.updateData(i);var u=t.get("clip",!0)&&yS(t.coordinateSystem,!1,t);u?this.group.setClipPath(u):this.group.removeClipPath(),this._lastZlevel=o,this._finished=!0},e.prototype.incrementalPrepareRender=function(t,e,n){var i=t.getData();this._updateLineDraw(i,t).incrementalPrepareUpdate(i),this._clearLayer(n),this._finished=!1},e.prototype.incrementalRender=function(t,e,n){this._lineDraw.incrementalUpdate(t,e.getData()),this._finished=t.end===e.getData().count()},e.prototype.eachRendered=function(t){this._lineDraw&&this._lineDraw.eachRendered(t)},e.prototype.updateTransform=function(t,e,n){var i=t.getData(),r=t.pipelineContext;if(!this._finished||r.large||r.progressiveRender)return{update:!0};var o=EP.reset(t,e,n);o.progress&&o.progress({start:0,end:i.count(),count:i.count()},i),this._lineDraw.updateLayout(),this._clearLayer(n)},e.prototype._updateLineDraw=function(t,e){var n=this._lineDraw,i=this._showEffect(e),r=!!e.get("polyline"),o=e.pipelineContext.large;return n&&i===this._hasEffet&&r===this._isPolyline&&o===this._isLargeDraw||(n&&n.remove(),n=this._lineDraw=o?new NP:new TA(r?i?PP:LP:i?kP:IA),this._hasEffet=i,this._isPolyline=r,this._isLargeDraw=o),this.group.add(n.group),n},e.prototype._showEffect=function(t){return!!t.get(["effect","show"])},e.prototype._clearLayer=function(t){var e=t.getZr();"svg"===e.painter.getType()||null==this._lastZlevel||e.painter.getLayer(this._lastZlevel).clear(!0)},e.prototype.remove=function(t,e){this._lineDraw&&this._lineDraw.remove(),this._lineDraw=null,this._clearLayer(e)},e.prototype.dispose=function(t,e){this.remove(t,e)},e.type="lines",e}(Tg),VP="undefined"==typeof Uint32Array?Array:Uint32Array,BP="undefined"==typeof Float64Array?Array:Float64Array;function FP(t){var e=t.data;e&&e[0]&&e[0][0]&&e[0][0].coord&&(t.data=z(e,(function(t){var e={coords:[t[0].coord,t[1].coord]};return t[0].name&&(e.fromName=t[0].name),t[1].name&&(e.toName=t[1].name),D([e,t[0],t[1]])})))}var GP=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.visualStyleAccessPath="lineStyle",n.visualDrawType="stroke",n}return n(e,t),e.prototype.init=function(e){e.data=e.data||[],FP(e);var n=this._processFlatCoordsArray(e.data);this._flatCoords=n.flatCoords,this._flatCoordsOffset=n.flatCoordsOffset,n.flatCoords&&(e.data=new Float32Array(n.count)),t.prototype.init.apply(this,arguments)},e.prototype.mergeOption=function(e){if(FP(e),e.data){var n=this._processFlatCoordsArray(e.data);this._flatCoords=n.flatCoords,this._flatCoordsOffset=n.flatCoordsOffset,n.flatCoords&&(e.data=new Float32Array(n.count))}t.prototype.mergeOption.apply(this,arguments)},e.prototype.appendData=function(t){var e=this._processFlatCoordsArray(t.data);e.flatCoords&&(this._flatCoords?(this._flatCoords=vt(this._flatCoords,e.flatCoords),this._flatCoordsOffset=vt(this._flatCoordsOffset,e.flatCoordsOffset)):(this._flatCoords=e.flatCoords,this._flatCoordsOffset=e.flatCoordsOffset),t.data=new Float32Array(e.count)),this.getRawData().appendData(t.data)},e.prototype._getCoordsFromItemModel=function(t){var e=this.getData().getItemModel(t),n=e.option instanceof Array?e.option:e.getShallow("coords");return n},e.prototype.getLineCoordsCount=function(t){return this._flatCoordsOffset?this._flatCoordsOffset[2*t+1]:this._getCoordsFromItemModel(t).length},e.prototype.getLineCoords=function(t,e){if(this._flatCoordsOffset){for(var n=this._flatCoordsOffset[2*t],i=this._flatCoordsOffset[2*t+1],r=0;r ")})},e.prototype.preventIncremental=function(){return!!this.get(["effect","show"])},e.prototype.getProgressive=function(){var t=this.option.progressive;return null==t?this.option.large?1e4:this.get("progressive"):t},e.prototype.getProgressiveThreshold=function(){var t=this.option.progressiveThreshold;return null==t?this.option.large?2e4:this.get("progressiveThreshold"):t},e.prototype.getZLevelKey=function(){var t=this.getModel("effect"),e=t.get("trailLength");return this.getData().count()>this.getProgressiveThreshold()?this.id:t.get("show")&&e>0?e+"":""},e.type="series.lines",e.dependencies=["grid","polar","geo","calendar"],e.defaultOption={coordinateSystem:"geo",z:2,legendHoverLink:!0,xAxisIndex:0,yAxisIndex:0,symbol:["none","none"],symbolSize:[10,10],geoIndex:0,effect:{show:!1,period:4,constantSpeed:0,symbol:"circle",symbolSize:3,loop:!0,trailLength:.2},large:!1,largeThreshold:2e3,polyline:!1,clip:!0,label:{show:!1,position:"end"},lineStyle:{opacity:.5}},e}(fg);function WP(t){return t instanceof Array||(t=[t,t]),t}var HP={seriesType:"lines",reset:function(t){var e=WP(t.get("symbol")),n=WP(t.get("symbolSize")),i=t.getData();return i.setVisual("fromSymbol",e&&e[0]),i.setVisual("toSymbol",e&&e[1]),i.setVisual("fromSymbolSize",n&&n[0]),i.setVisual("toSymbolSize",n&&n[1]),{dataEach:i.hasItemOption?function(t,e){var n=t.getItemModel(e),i=WP(n.getShallow("symbol",!0)),r=WP(n.getShallow("symbolSize",!0));i[0]&&t.setItemVisual(e,"fromSymbol",i[0]),i[1]&&t.setItemVisual(e,"toSymbol",i[1]),r[0]&&t.setItemVisual(e,"fromSymbolSize",r[0]),r[1]&&t.setItemVisual(e,"toSymbolSize",r[1])}:null}}};var YP=function(){function t(){this.blurSize=30,this.pointSize=20,this.maxOpacity=1,this.minOpacity=0,this._gradientPixels={inRange:null,outOfRange:null};var t=h.createCanvas();this.canvas=t}return t.prototype.update=function(t,e,n,i,r,o){var a=this._getBrush(),s=this._getGradient(r,"inRange"),l=this._getGradient(r,"outOfRange"),u=this.pointSize+this.blurSize,h=this.canvas,c=h.getContext("2d"),p=t.length;h.width=e,h.height=n;for(var d=0;d0){var I=o(v)?s:l;v>0&&(v=v*S+w),x[_++]=I[M],x[_++]=I[M+1],x[_++]=I[M+2],x[_++]=I[M+3]*v*256}else _+=4}return c.putImageData(m,0,0),h},t.prototype._getBrush=function(){var t=this._brushCanvas||(this._brushCanvas=h.createCanvas()),e=this.pointSize+this.blurSize,n=2*e;t.width=n,t.height=n;var i=t.getContext("2d");return i.clearRect(0,0,n,n),i.shadowOffsetX=n,i.shadowBlur=this.blurSize,i.shadowColor="#000",i.beginPath(),i.arc(-e,e,this.pointSize,0,2*Math.PI,!0),i.closePath(),i.fill(),t},t.prototype._getGradient=function(t,e){for(var n=this._gradientPixels,i=n[e]||(n[e]=new Uint8ClampedArray(1024)),r=[0,0,0,0],o=0,a=0;a<256;a++)t[e](a/255,!0,r),i[o++]=r[0],i[o++]=r[1],i[o++]=r[2],i[o++]=r[3];return i},t}();function UP(t){var e=t.dimensions;return"lng"===e[0]&&"lat"===e[1]}var XP=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){var i;e.eachComponent("visualMap",(function(e){e.eachTargetSeries((function(n){n===t&&(i=e)}))})),this._progressiveEls=null,this.group.removeAll();var r=t.coordinateSystem;"cartesian2d"===r.type||"calendar"===r.type?this._renderOnCartesianAndCalendar(t,n,0,t.getData().count()):UP(r)&&this._renderOnGeo(r,t,i,n)},e.prototype.incrementalPrepareRender=function(t,e,n){this.group.removeAll()},e.prototype.incrementalRender=function(t,e,n,i){var r=e.coordinateSystem;r&&(UP(r)?this.render(e,n,i):(this._progressiveEls=[],this._renderOnCartesianAndCalendar(e,i,t.start,t.end,!0)))},e.prototype.eachRendered=function(t){jh(this._progressiveEls||this.group,t)},e.prototype._renderOnCartesianAndCalendar=function(t,e,n,i,r){var o,a,s,l,u=t.coordinateSystem,h=vS(u,"cartesian2d");if(h){var c=u.getAxis("x"),p=u.getAxis("y");0,o=c.getBandWidth()+.5,a=p.getBandWidth()+.5,s=c.scale.getExtent(),l=p.scale.getExtent()}for(var d=this.group,f=t.getData(),g=t.getModel(["emphasis","itemStyle"]).getItemStyle(),y=t.getModel(["blur","itemStyle"]).getItemStyle(),v=t.getModel(["select","itemStyle"]).getItemStyle(),m=t.get(["itemStyle","borderRadius"]),x=tc(t),_=t.getModel("emphasis"),b=_.get("focus"),w=_.get("blurScope"),S=_.get("disabled"),M=h?[f.mapDimension("x"),f.mapDimension("y"),f.mapDimension("value")]:[f.mapDimension("time"),f.mapDimension("value")],I=n;Is[1]||Al[1])continue;var k=u.dataToPoint([D,A]);T=new Es({shape:{x:k[0]-o/2,y:k[1]-a/2,width:o,height:a},style:C})}else{if(isNaN(f.get(M[1],I)))continue;T=new Es({z2:1,shape:u.dataToRect([f.get(M[0],I)]).contentShape,style:C})}if(f.hasItemOption){var L=f.getItemModel(I),P=L.getModel("emphasis");g=P.getModel("itemStyle").getItemStyle(),y=L.getModel(["blur","itemStyle"]).getItemStyle(),v=L.getModel(["select","itemStyle"]).getItemStyle(),m=L.get(["itemStyle","borderRadius"]),b=P.get("focus"),w=P.get("blurScope"),S=P.get("disabled"),x=tc(L)}T.shape.r=m;var O=t.getRawValue(I),R="-";O&&null!=O[2]&&(R=O[2]+""),Qh(T,x,{labelFetcher:t,labelDataIndex:I,defaultOpacity:C.opacity,defaultText:R}),T.ensureState("emphasis").style=g,T.ensureState("blur").style=y,T.ensureState("select").style=v,Hl(T,b,w,S),T.incremental=r,r&&(T.states.emphasis.hoverLayer=!0),d.add(T),f.setItemGraphicEl(I,T),this._progressiveEls&&this._progressiveEls.push(T)}},e.prototype._renderOnGeo=function(t,e,n,i){var r=n.targetVisuals.inRange,o=n.targetVisuals.outOfRange,a=e.getData(),s=this._hmLayer||this._hmLayer||new YP;s.blurSize=e.get("blurSize"),s.pointSize=e.get("pointSize"),s.minOpacity=e.get("minOpacity"),s.maxOpacity=e.get("maxOpacity");var l=t.getViewRect().clone(),u=t.getRoamTransform();l.applyTransform(u);var h=Math.max(l.x,0),c=Math.max(l.y,0),p=Math.min(l.width+l.x,i.getWidth()),d=Math.min(l.height+l.y,i.getHeight()),f=p-h,g=d-c,y=[a.mapDimension("lng"),a.mapDimension("lat"),a.mapDimension("value")],v=a.mapArray(y,(function(e,n,i){var r=t.dataToPoint([e,n]);return r[0]-=h,r[1]-=c,r.push(i),r})),m=n.getExtent(),x="visualMap.continuous"===n.type?function(t,e){var n=t[1]-t[0];return e=[(e[0]-t[0])/n,(e[1]-t[0])/n],function(t){return t>=e[0]&&t<=e[1]}}(m,n.option.range):function(t,e,n){var i=t[1]-t[0],r=(e=z(e,(function(e){return{interval:[(e.interval[0]-t[0])/i,(e.interval[1]-t[0])/i]}}))).length,o=0;return function(t){var i;for(i=o;i=0;i--){var a;if((a=e[i].interval)[0]<=t&&t<=a[1]){o=i;break}}return i>=0&&i0?1:-1}(n,o,r,i,c),function(t,e,n,i,r,o,a,s,l,u){var h,c=l.valueDim,p=l.categoryDim,d=Math.abs(n[p.wh]),f=t.getItemVisual(e,"symbolSize");h=Y(f)?f.slice():null==f?["100%","100%"]:[f,f];h[p.index]=Ur(h[p.index],d),h[c.index]=Ur(h[c.index],i?d:Math.abs(o)),u.symbolSize=h,(u.symbolScale=[h[0]/s,h[1]/s])[c.index]*=(l.isHorizontal?-1:1)*a}(t,e,r,o,0,c.boundingLength,c.pxSign,u,i,c),function(t,e,n,i,r){var o=t.get(jP)||0;o&&(KP.attr({scaleX:e[0],scaleY:e[1],rotation:n}),KP.updateTransform(),o/=KP.getLineScale(),o*=e[i.valueDim.index]);r.valueLineWidth=o||0}(n,c.symbolScale,l,i,c);var p=c.symbolSize,d=Fy(n.get("symbolOffset"),p);return function(t,e,n,i,r,o,a,s,l,u,h,c){var p=h.categoryDim,d=h.valueDim,f=c.pxSign,g=Math.max(e[d.index]+s,0),y=g;if(i){var v=Math.abs(l),m=it(t.get("symbolMargin"),"15%")+"",x=!1;m.lastIndexOf("!")===m.length-1&&(x=!0,m=m.slice(0,m.length-1));var _=Ur(m,e[d.index]),b=Math.max(g+2*_,0),w=x?0:2*_,S=ho(i),M=S?i:fO((v+w)/b);b=g+2*(_=(v-M*g)/2/(x?M:Math.max(M-1,1))),w=x?0:2*_,S||"fixed"===i||(M=u?fO((Math.abs(u)+w)/b):0),y=M*b-w,c.repeatTimes=M,c.symbolMargin=_}var I=f*(y/2),T=c.pathPosition=[];T[p.index]=n[p.wh]/2,T[d.index]="start"===a?I:"end"===a?l-I:l/2,o&&(T[0]+=o[0],T[1]+=o[1]);var C=c.bundlePosition=[];C[p.index]=n[p.xy],C[d.index]=n[d.xy];var D=c.barRectShape=A({},n);D[d.wh]=f*Math.max(Math.abs(n[d.wh]),Math.abs(T[d.index]+I)),D[p.wh]=n[p.wh];var k=c.clipShape={};k[p.xy]=-n[p.xy],k[p.wh]=h.ecSize[p.wh],k[d.xy]=0,k[d.wh]=n[d.wh]}(n,p,r,o,0,d,s,c.valueLineWidth,c.boundingLength,c.repeatCutLength,i,c),c}function QP(t,e){return t.toGlobalCoord(t.dataToCoord(t.scale.parse(e)))}function tO(t){var e=t.symbolPatternSize,n=Vy(t.symbolType,-e/2,-e/2,e,e);return n.attr({culling:!0}),"image"!==n.type&&n.setStyle({strokeNoScale:!0}),n}function eO(t,e,n,i){var r=t.__pictorialBundle,o=n.symbolSize,a=n.valueLineWidth,s=n.pathPosition,l=e.valueDim,u=n.repeatTimes||0,h=0,c=o[e.valueDim.index]+a+2*n.symbolMargin;for(cO(t,(function(t){t.__pictorialAnimationIndex=h,t.__pictorialRepeatTimes=u,h0:i<0)&&(r=u-1-t),e[l.index]=c*(r-u/2+.5)+s[l.index],{x:e[0],y:e[1],scaleX:n.symbolScale[0],scaleY:n.symbolScale[1],rotation:n.rotation}}}function nO(t,e,n,i){var r=t.__pictorialBundle,o=t.__pictorialMainPath;o?pO(o,null,{x:n.pathPosition[0],y:n.pathPosition[1],scaleX:n.symbolScale[0],scaleY:n.symbolScale[1],rotation:n.rotation},n,i):(o=t.__pictorialMainPath=tO(n),r.add(o),pO(o,{x:n.pathPosition[0],y:n.pathPosition[1],scaleX:0,scaleY:0,rotation:n.rotation},{scaleX:n.symbolScale[0],scaleY:n.symbolScale[1]},n,i))}function iO(t,e,n){var i=A({},e.barRectShape),r=t.__pictorialBarRect;r?pO(r,null,{shape:i},e,n):((r=t.__pictorialBarRect=new Es({z2:2,shape:i,silent:!0,style:{stroke:"transparent",fill:"transparent",lineWidth:0}})).disableMorphing=!0,t.add(r))}function rO(t,e,n,i){if(n.symbolClip){var r=t.__pictorialClipPath,o=A({},n.clipShape),a=e.valueDim,s=n.animationModel,l=n.dataIndex;if(r)dh(r,{shape:o},s,l);else{o[a.wh]=0,r=new Es({shape:o}),t.__pictorialBundle.setClipPath(r),t.__pictorialClipPath=r;var u={};u[a.wh]=n.clipShape[a.wh],qh[i?"updateProps":"initProps"](r,{shape:u},s,l)}}}function oO(t,e){var n=t.getItemModel(e);return n.getAnimationDelayParams=aO,n.isAnimationEnabled=sO,n}function aO(t){return{index:t.__pictorialAnimationIndex,count:t.__pictorialRepeatTimes}}function sO(){return this.parentModel.isAnimationEnabled()&&!!this.getShallow("animation")}function lO(t,e,n,i){var r=new Er,o=new Er;return r.add(o),r.__pictorialBundle=o,o.x=n.bundlePosition[0],o.y=n.bundlePosition[1],n.symbolRepeat?eO(r,e,n):nO(r,0,n),iO(r,n,i),rO(r,e,n,i),r.__pictorialShapeStr=hO(t,n),r.__pictorialSymbolMeta=n,r}function uO(t,e,n,i){var r=i.__pictorialBarRect;r&&r.removeTextContent();var o=[];cO(i,(function(t){o.push(t)})),i.__pictorialMainPath&&o.push(i.__pictorialMainPath),i.__pictorialClipPath&&(n=null),E(o,(function(t){yh(t,{scaleX:0,scaleY:0},n,e,(function(){i.parent&&i.parent.remove(i)}))})),t.setItemGraphicEl(e,null)}function hO(t,e){return[t.getItemVisual(e.dataIndex,"symbol")||"none",!!e.symbolRepeat,!!e.symbolClip].join(":")}function cO(t,e,n){E(t.__pictorialBundle.children(),(function(i){i!==t.__pictorialBarRect&&e.call(n,i)}))}function pO(t,e,n,i,r,o){e&&t.attr(e),i.symbolClip&&!r?n&&t.attr(n):n&&qh[r?"updateProps":"initProps"](t,n,i.animationModel,i.dataIndex,o)}function dO(t,e,n){var i=n.dataIndex,r=n.itemModel,o=r.getModel("emphasis"),a=o.getModel("itemStyle").getItemStyle(),s=r.getModel(["blur","itemStyle"]).getItemStyle(),l=r.getModel(["select","itemStyle"]).getItemStyle(),u=r.getShallow("cursor"),h=o.get("focus"),c=o.get("blurScope"),p=o.get("scale");cO(t,(function(t){if(t instanceof As){var e=t.style;t.useStyle(A({image:e.image,x:e.x,y:e.y,width:e.width,height:e.height},n.style))}else t.useStyle(n.style);var i=t.ensureState("emphasis");i.style=a,p&&(i.scaleX=1.1*t.scaleX,i.scaleY=1.1*t.scaleY),t.ensureState("blur").style=s,t.ensureState("select").style=l,u&&(t.cursor=u),t.z2=n.z2}));var d=e.valueDim.posDesc[+(n.boundingLength>0)];Qh(t.__pictorialBarRect,tc(r),{labelFetcher:e.seriesModel,labelDataIndex:i,defaultText:Kw(e.seriesModel.getData(),i),inheritColor:n.style.fill,defaultOpacity:n.style.opacity,defaultOutsidePosition:d}),Hl(t,h,c,o.get("disabled"))}function fO(t){var e=Math.round(t);return Math.abs(t-e)<1e-4?e:Math.ceil(t)}var gO=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.hasSymbolVisual=!0,n.defaultSymbol="roundRect",n}return n(e,t),e.prototype.getInitialData=function(e){return e.stack=null,t.prototype.getInitialData.apply(this,arguments)},e.type="series.pictorialBar",e.dependencies=["grid"],e.defaultOption=Tc(OS.defaultOption,{symbol:"circle",symbolSize:null,symbolRotate:null,symbolPosition:null,symbolOffset:null,symbolMargin:null,symbolRepeat:!1,symbolRepeatDirection:"end",symbolClip:!1,symbolBoundingData:null,symbolPatternSize:400,barGap:"-100%",progressive:0,emphasis:{scale:!1},select:{itemStyle:{borderColor:"#212121"}}}),e}(OS);var yO=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n._layers=[],n}return n(e,t),e.prototype.render=function(t,e,n){var i=t.getData(),r=this,o=this.group,a=t.getLayerSeries(),s=i.getLayout("layoutInfo"),l=s.rect,u=s.boundaryGap;function h(t){return t.name}o.x=0,o.y=l.y+u[0];var c=new Lm(this._layersSeries||[],a,h,h),p=[];function d(e,n,s){var l=r._layers;if("remove"!==e){for(var u,h,c=[],d=[],f=a[n].indices,g=0;go&&(o=s),i.push(s)}for(var u=0;uo&&(o=c)}return{y0:r,max:o}}(l),h=u.y0,c=n/u.max,p=o.length,d=o[0].indices.length,f=0;fMath.PI/2?"right":"left"):S&&"center"!==S?"left"===S?(m=r.r0+w,a>Math.PI/2&&(S="right")):"right"===S&&(m=r.r-w,a>Math.PI/2&&(S="left")):(m=o===2*Math.PI&&0===r.r0?0:(r.r+r.r0)/2,S="center"),g.style.align=S,g.style.verticalAlign=f(p,"verticalAlign")||"middle",g.x=m*s+r.cx,g.y=m*l+r.cy;var M=f(p,"rotate"),I=0;"radial"===M?(I=-a)<-Math.PI/2&&(I+=Math.PI):"tangential"===M?(I=Math.PI/2-a)>Math.PI/2?I-=Math.PI:I<-Math.PI/2&&(I+=Math.PI):j(M)&&(I=M*Math.PI/180),g.rotation=I})),h.dirtyStyle()},e}(Eu),bO="sunburstRootToNode",wO="sunburstHighlight";var SO=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n,i){var r=this;this.seriesModel=t,this.api=n,this.ecModel=e;var o=t.getData(),a=o.tree.root,s=t.getViewRoot(),l=this.group,u=t.get("renderLabelForZeroData"),h=[];s.eachNode((function(t){h.push(t)}));var c=this._oldChildren||[];!function(i,r){if(0===i.length&&0===r.length)return;function s(t){return t.getId()}function h(s,h){!function(i,r){u||!i||i.getValue()||(i=null);if(i!==a&&r!==a)if(r&&r.piece)i?(r.piece.updateData(!1,i,t,e,n),o.setItemGraphicEl(i.dataIndex,r.piece)):function(t){if(!t)return;t.piece&&(l.remove(t.piece),t.piece=null)}(r);else if(i){var s=new _O(i,t,e,n);l.add(s),o.setItemGraphicEl(i.dataIndex,s)}}(null==s?null:i[s],null==h?null:r[h])}new Lm(r,i,s,s).add(h).update(h).remove(H(h,null)).execute()}(h,c),function(i,o){o.depth>0?(r.virtualPiece?r.virtualPiece.updateData(!1,i,t,e,n):(r.virtualPiece=new _O(i,t,e,n),l.add(r.virtualPiece)),o.piece.off("click"),r.virtualPiece.on("click",(function(t){r._rootToNode(o.parentNode)}))):r.virtualPiece&&(l.remove(r.virtualPiece),r.virtualPiece=null)}(a,s),this._initEvents(),this._oldChildren=h},e.prototype._initEvents=function(){var t=this;this.group.off("click"),this.group.on("click",(function(e){var n=!1;t.seriesModel.getViewRoot().eachNode((function(i){if(!n&&i.piece&&i.piece===e.target){var r=i.getModel().get("nodeClick");if("rootToNode"===r)t._rootToNode(i);else if("link"===r){var o=i.getModel(),a=o.get("link");if(a)_p(a,o.get("target",!0)||"_blank")}n=!0}}))}))},e.prototype._rootToNode=function(t){t!==this.seriesModel.getViewRoot()&&this.api.dispatchAction({type:bO,from:this.uid,seriesId:this.seriesModel.id,targetNode:t})},e.prototype.containPoint=function(t,e){var n=e.getData().getItemLayout(0);if(n){var i=t[0]-n.cx,r=t[1]-n.cy,o=Math.sqrt(i*i+r*r);return o<=n.r&&o>=n.r0}},e.type="sunburst",e}(Tg),MO=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.ignoreStyleOnData=!0,n}return n(e,t),e.prototype.getInitialData=function(t,e){var n={name:t.name,children:t.data};IO(n);var i=this._levelModels=z(t.levels||[],(function(t){return new Sc(t,this,e)}),this),r=BC.createTree(n,this,(function(t){t.wrapMethod("getItemModel",(function(t,e){var n=r.getNodeByDataIndex(e),o=i[n.depth];return o&&(t.parentModel=o),t}))}));return r.data},e.prototype.optionUpdated=function(){this.resetViewRoot()},e.prototype.getDataParams=function(e){var n=t.prototype.getDataParams.apply(this,arguments),i=this.getData().tree.getNodeByDataIndex(e);return n.treePathInfo=HC(i,this),n},e.prototype.getLevelModel=function(t){return this._levelModels&&this._levelModels[t.depth]},e.prototype.getViewRoot=function(){return this._viewRoot},e.prototype.resetViewRoot=function(t){t?this._viewRoot=t:t=this._viewRoot;var e=this.getRawData().tree.root;t&&(t===e||e.contains(t))||(this._viewRoot=e)},e.prototype.enableAriaDecal=function(){qC(this)},e.type="series.sunburst",e.defaultOption={z:2,center:["50%","50%"],radius:[0,"75%"],clockwise:!0,startAngle:90,minAngle:0,stillShowZeroSum:!0,nodeClick:"rootToNode",renderLabelForZeroData:!1,label:{rotate:"radial",show:!0,opacity:1,align:"center",position:"inside",distance:5,silent:!0},itemStyle:{borderWidth:1,borderColor:"white",borderType:"solid",shadowBlur:0,shadowColor:"rgba(0, 0, 0, 0.2)",shadowOffsetX:0,shadowOffsetY:0,opacity:1},emphasis:{focus:"descendant"},blur:{itemStyle:{opacity:.2},label:{opacity:.1}},animationType:"expansion",animationDuration:1e3,animationDurationUpdate:500,data:[],sort:"desc"},e}(fg);function IO(t){var e=0;E(t.children,(function(t){IO(t);var n=t.value;Y(n)&&(n=n[0]),e+=n}));var n=t.value;Y(n)&&(n=n[0]),(null==n||isNaN(n))&&(n=e),n<0&&(n=0),Y(t.value)?t.value[0]=n:t.value=n}var TO=Math.PI/180;function CO(t,e,n){e.eachSeriesByType(t,(function(t){var e=t.get("center"),i=t.get("radius");Y(i)||(i=[0,i]),Y(e)||(e=[e,e]);var r=n.getWidth(),o=n.getHeight(),a=Math.min(r,o),s=Ur(e[0],r),l=Ur(e[1],o),u=Ur(i[0],a/2),h=Ur(i[1],a/2),c=-t.get("startAngle")*TO,p=t.get("minAngle")*TO,d=t.getData().tree.root,f=t.getViewRoot(),g=f.depth,y=t.get("sort");null!=y&&DO(f,y);var v=0;E(f.children,(function(t){!isNaN(t.getValue())&&v++}));var m=f.getValue(),x=Math.PI/(m||v)*2,_=f.depth>0,b=f.height-(_?-1:1),w=(h-u)/(b||1),S=t.get("clockwise"),M=t.get("stillShowZeroSum"),I=S?1:-1,T=function(e,n){if(e){var i=n;if(e!==d){var r=e.getValue(),o=0===m&&M?x:r*x;o1;)r=r.parentNode;var o=n.getColorFromPalette(r.name||r.dataIndex+"",e);return t.depth>1&&X(o)&&(o=Kn(o,(t.depth-1)/(i-1)*.5)),o}(r,t,i.root.height)),A(n.ensureUniqueItemVisual(r.dataIndex,"style"),o)}))}))}var kO={color:"fill",borderColor:"stroke"},LO={symbol:1,symbolSize:1,symbolKeepAspect:1,legendIcon:1,visualMeta:1,liftZ:1,decal:1},PO=Po(),OO=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.optionUpdated=function(){this.currentZLevel=this.get("zlevel",!0),this.currentZ=this.get("z",!0)},e.prototype.getInitialData=function(t,e){return hx(null,this)},e.prototype.getDataParams=function(e,n,i){var r=t.prototype.getDataParams.call(this,e,n);return i&&(r.info=PO(i).info),r},e.type="series.custom",e.dependencies=["grid","polar","geo","singleAxis","calendar"],e.defaultOption={coordinateSystem:"cartesian2d",z:2,legendHoverLink:!0,clip:!1},e}(fg);function RO(t,e){return e=e||[0,0],z(["x","y"],(function(n,i){var r=this.getAxis(n),o=e[i],a=t[i]/2;return"category"===r.type?r.getBandWidth():Math.abs(r.dataToCoord(o-a)-r.dataToCoord(o+a))}),this)}function NO(t,e){return e=e||[0,0],z([0,1],(function(n){var i=e[n],r=t[n]/2,o=[],a=[];return o[n]=i-r,a[n]=i+r,o[1-n]=a[1-n]=e[1-n],Math.abs(this.dataToPoint(o)[n]-this.dataToPoint(a)[n])}),this)}function EO(t,e){var n=this.getAxis(),i=e instanceof Array?e[0]:e,r=(t instanceof Array?t[0]:t)/2;return"category"===n.type?n.getBandWidth():Math.abs(n.dataToCoord(i-r)-n.dataToCoord(i+r))}function zO(t,e){return e=e||[0,0],z(["Radius","Angle"],(function(n,i){var r=this["get"+n+"Axis"](),o=e[i],a=t[i]/2,s="category"===r.type?r.getBandWidth():Math.abs(r.dataToCoord(o-a)-r.dataToCoord(o+a));return"Angle"===n&&(s=s*Math.PI/180),s}),this)}function VO(t,e,n,i){return t&&(t.legacy||!1!==t.legacy&&!n&&!i&&"tspan"!==e&&("text"===e||_t(t,"text")))}function BO(t,e,n){var i,r,o,a=t;if("text"===e)o=a;else{o={},_t(a,"text")&&(o.text=a.text),_t(a,"rich")&&(o.rich=a.rich),_t(a,"textFill")&&(o.fill=a.textFill),_t(a,"textStroke")&&(o.stroke=a.textStroke),_t(a,"fontFamily")&&(o.fontFamily=a.fontFamily),_t(a,"fontSize")&&(o.fontSize=a.fontSize),_t(a,"fontStyle")&&(o.fontStyle=a.fontStyle),_t(a,"fontWeight")&&(o.fontWeight=a.fontWeight),r={type:"text",style:o,silent:!0},i={};var s=_t(a,"textPosition");n?i.position=s?a.textPosition:"inside":s&&(i.position=a.textPosition),_t(a,"textPosition")&&(i.position=a.textPosition),_t(a,"textOffset")&&(i.offset=a.textOffset),_t(a,"textRotation")&&(i.rotation=a.textRotation),_t(a,"textDistance")&&(i.distance=a.textDistance)}return FO(o,t),E(o.rich,(function(t){FO(t,t)})),{textConfig:i,textContent:r}}function FO(t,e){e&&(e.font=e.textFont||e.font,_t(e,"textStrokeWidth")&&(t.lineWidth=e.textStrokeWidth),_t(e,"textAlign")&&(t.align=e.textAlign),_t(e,"textVerticalAlign")&&(t.verticalAlign=e.textVerticalAlign),_t(e,"textLineHeight")&&(t.lineHeight=e.textLineHeight),_t(e,"textWidth")&&(t.width=e.textWidth),_t(e,"textHeight")&&(t.height=e.textHeight),_t(e,"textBackgroundColor")&&(t.backgroundColor=e.textBackgroundColor),_t(e,"textPadding")&&(t.padding=e.textPadding),_t(e,"textBorderColor")&&(t.borderColor=e.textBorderColor),_t(e,"textBorderWidth")&&(t.borderWidth=e.textBorderWidth),_t(e,"textBorderRadius")&&(t.borderRadius=e.textBorderRadius),_t(e,"textBoxShadowColor")&&(t.shadowColor=e.textBoxShadowColor),_t(e,"textBoxShadowBlur")&&(t.shadowBlur=e.textBoxShadowBlur),_t(e,"textBoxShadowOffsetX")&&(t.shadowOffsetX=e.textBoxShadowOffsetX),_t(e,"textBoxShadowOffsetY")&&(t.shadowOffsetY=e.textBoxShadowOffsetY))}function GO(t,e,n){var i=t;i.textPosition=i.textPosition||n.position||"inside",null!=n.offset&&(i.textOffset=n.offset),null!=n.rotation&&(i.textRotation=n.rotation),null!=n.distance&&(i.textDistance=n.distance);var r=i.textPosition.indexOf("inside")>=0,o=t.fill||"#000";WO(i,e);var a=null==i.textFill;return r?a&&(i.textFill=n.insideFill||"#fff",!i.textStroke&&n.insideStroke&&(i.textStroke=n.insideStroke),!i.textStroke&&(i.textStroke=o),null==i.textStrokeWidth&&(i.textStrokeWidth=2)):(a&&(i.textFill=t.fill||n.outsideFill||"#000"),!i.textStroke&&n.outsideStroke&&(i.textStroke=n.outsideStroke)),i.text=e.text,i.rich=e.rich,E(e.rich,(function(t){WO(t,t)})),i}function WO(t,e){e&&(_t(e,"fill")&&(t.textFill=e.fill),_t(e,"stroke")&&(t.textStroke=e.fill),_t(e,"lineWidth")&&(t.textStrokeWidth=e.lineWidth),_t(e,"font")&&(t.font=e.font),_t(e,"fontStyle")&&(t.fontStyle=e.fontStyle),_t(e,"fontWeight")&&(t.fontWeight=e.fontWeight),_t(e,"fontSize")&&(t.fontSize=e.fontSize),_t(e,"fontFamily")&&(t.fontFamily=e.fontFamily),_t(e,"align")&&(t.textAlign=e.align),_t(e,"verticalAlign")&&(t.textVerticalAlign=e.verticalAlign),_t(e,"lineHeight")&&(t.textLineHeight=e.lineHeight),_t(e,"width")&&(t.textWidth=e.width),_t(e,"height")&&(t.textHeight=e.height),_t(e,"backgroundColor")&&(t.textBackgroundColor=e.backgroundColor),_t(e,"padding")&&(t.textPadding=e.padding),_t(e,"borderColor")&&(t.textBorderColor=e.borderColor),_t(e,"borderWidth")&&(t.textBorderWidth=e.borderWidth),_t(e,"borderRadius")&&(t.textBorderRadius=e.borderRadius),_t(e,"shadowColor")&&(t.textBoxShadowColor=e.shadowColor),_t(e,"shadowBlur")&&(t.textBoxShadowBlur=e.shadowBlur),_t(e,"shadowOffsetX")&&(t.textBoxShadowOffsetX=e.shadowOffsetX),_t(e,"shadowOffsetY")&&(t.textBoxShadowOffsetY=e.shadowOffsetY),_t(e,"textShadowColor")&&(t.textShadowColor=e.textShadowColor),_t(e,"textShadowBlur")&&(t.textShadowBlur=e.textShadowBlur),_t(e,"textShadowOffsetX")&&(t.textShadowOffsetX=e.textShadowOffsetX),_t(e,"textShadowOffsetY")&&(t.textShadowOffsetY=e.textShadowOffsetY))}var HO={position:["x","y"],scale:["scaleX","scaleY"],origin:["originX","originY"]},YO=G(HO),UO=(V(gr,(function(t,e){return t[e]=1,t}),{}),gr.join(", "),["","style","shape","extra"]),XO=Po();function ZO(t,e,n,i,r){var o=t+"Animation",a=ch(t,i,r)||{},s=XO(e).userDuring;return a.duration>0&&(a.during=s?W(tR,{el:e,userDuring:s}):null,a.setToFinal=!0,a.scope=t),A(a,n[o]),a}function jO(t,e,n,i){var r=(i=i||{}).dataIndex,o=i.isInit,a=i.clearStyle,s=n.isAnimationEnabled(),l=XO(t),u=e.style;l.userDuring=e.during;var h={},c={};if(function(t,e,n){for(var i=0;i=0)){var c=t.getAnimationStyleProps(),p=c?c.style:null;if(p){!r&&(r=i.style={});var d=G(n);for(u=0;u0&&t.animateFrom(p,d)}else!function(t,e,n,i,r){if(r){var o=ZO("update",t,e,i,n);o.duration>0&&t.animateFrom(r,o)}}(t,e,r||0,n,h);qO(t,e),u?t.dirty():t.markRedraw()}function qO(t,e){for(var n=XO(t).leaveToProps,i=0;i=0){!o&&(o=i[t]={});var p=G(a);for(h=0;hi[1]&&i.reverse(),{coordSys:{type:"polar",cx:t.cx,cy:t.cy,r:i[1],r0:i[0]},api:{coord:function(i){var r=e.dataToRadius(i[0]),o=n.dataToAngle(i[1]),a=t.coordToPoint([r,o]);return a.push(r,o*Math.PI/180),a},size:W(zO,t)}}},calendar:function(t){var e=t.getRect(),n=t.getRangeInfo();return{coordSys:{type:"calendar",x:e.x,y:e.y,width:e.width,height:e.height,cellWidth:t.getCellWidth(),cellHeight:t.getCellHeight(),rangeInfo:{start:n.start,end:n.end,weeks:n.weeks,dayCount:n.allDay}},api:{coord:function(e,n){return t.dataToPoint(e,n)}}}}};function mR(t){return t instanceof Ms}function xR(t){return t instanceof wa}var _R=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n,i){this._progressiveEls=null;var r=this._data,o=t.getData(),a=this.group,s=IR(t,o,e,n);r||a.removeAll(),o.diff(r).add((function(e){CR(n,null,e,s(e,i),t,a,o)})).remove((function(e){var n=r.getItemGraphicEl(e);n&&KO(n,PO(n).option,t)})).update((function(e,l){var u=r.getItemGraphicEl(l);CR(n,u,e,s(e,i),t,a,o)})).execute();var l=t.get("clip",!0)?yS(t.coordinateSystem,!1,t):null;l?a.setClipPath(l):a.removeClipPath(),this._data=o},e.prototype.incrementalPrepareRender=function(t,e,n){this.group.removeAll(),this._data=null},e.prototype.incrementalRender=function(t,e,n,i,r){var o=e.getData(),a=IR(e,o,n,i),s=this._progressiveEls=[];function l(t){t.isGroup||(t.incremental=!0,t.ensureState("emphasis").hoverLayer=!0)}for(var u=t.start;u=0?e.getStore().get(r,n):void 0}var o=e.get(i.name,n),a=i&&i.ordinalMeta;return a?a.categories[o]:o},styleEmphasis:function(n,i){0;null==i&&(i=s);var r=m(i,lR).getItemStyle(),o=x(i,lR),a=ec(o,null,null,!0,!0);a.text=o.getShallow("show")?ot(t.getFormattedLabel(i,lR),t.getFormattedLabel(i,uR),Kw(e,i)):null;var l=nc(o,null,!0);return b(n,r),r=GO(r,a,l),n&&_(r,n),r.legacy=!0,r},visual:function(t,n){if(null==n&&(n=s),_t(kO,t)){var i=e.getItemVisual(n,"style");return i?i[kO[t]]:null}if(_t(LO,t))return e.getItemVisual(n,t)},barLayout:function(t){if("cartesian2d"===o.type){return function(t){var e=[],n=t.axis,i="axis0";if("category"===n.type){for(var r=n.getBandWidth(),o=0;o=c;f--){var g=e.childAt(f);OR(e,g,r)}}(t,c,n,i,r),a>=0?o.replaceAt(c,a):o.add(c),c}function AR(t,e,n){var i,r=PO(t),o=e.type,a=e.shape,s=e.style;return n.isUniversalTransitionEnabled()||null!=o&&o!==r.customGraphicType||"path"===o&&((i=a)&&(_t(i,"pathData")||_t(i,"d")))&&zR(a)!==r.customPathData||"image"===o&&_t(s,"image")&&s.image!==r.customImagePath}function kR(t,e,n){var i=e?LR(t,e):t,r=e?PR(t,i,lR):t.style,o=t.type,a=i?i.textConfig:null,s=t.textContent,l=s?e?LR(s,e):s:null;if(r&&(n.isLegacy||VO(r,o,!!a,!!l))){n.isLegacy=!0;var u=BO(r,o,!e);!a&&u.textConfig&&(a=u.textConfig),!l&&u.textContent&&(l=u.textContent)}if(!e&&l){var h=l;!h.type&&(h.type="text")}var c=e?n[e]:n.normal;c.cfg=a,c.conOpt=l}function LR(t,e){return e?t?t[e]:null:t}function PR(t,e,n){var i=e&&e.style;return null==i&&n===lR&&t&&(i=t.styleEmphasis),i}function OR(t,e,n){e&&KO(e,PO(t).option,n)}function RR(t,e){var n=t&&t.name;return null!=n?n:"e\0\0"+e}function NR(t,e){var n=this.context,i=null!=t?n.newChildren[t]:null,r=null!=e?n.oldChildren[e]:null;DR(n.api,r,n.dataIndex,i,n.seriesModel,n.group)}function ER(t){var e=this.context,n=e.oldChildren[t];n&&KO(n,PO(n).option,e.seriesModel)}function zR(t){return t&&(t.pathData||t.d)}var VR=Po(),BR=T,FR=W,GR=function(){function t(){this._dragging=!1,this.animationThreshold=15}return t.prototype.render=function(t,e,n,i){var r=e.get("value"),o=e.get("status");if(this._axisModel=t,this._axisPointerModel=e,this._api=n,i||this._lastValue!==r||this._lastStatus!==o){this._lastValue=r,this._lastStatus=o;var a=this._group,s=this._handle;if(!o||"hide"===o)return a&&a.hide(),void(s&&s.hide());a&&a.show(),s&&s.show();var l={};this.makeElOption(l,r,t,e,n);var u=l.graphicKey;u!==this._lastGraphicKey&&this.clear(n),this._lastGraphicKey=u;var h=this._moveAnimation=this.determineAnimation(t,e);if(a){var c=H(WR,e,h);this.updatePointerEl(a,l,c),this.updateLabelEl(a,l,c,e)}else a=this._group=new Er,this.createPointerEl(a,l,t,e),this.createLabelEl(a,l,t,e),n.getZr().add(a);XR(a,e,!0),this._renderHandle(r)}},t.prototype.remove=function(t){this.clear(t)},t.prototype.dispose=function(t){this.clear(t)},t.prototype.determineAnimation=function(t,e){var n=e.get("animation"),i=t.axis,r="category"===i.type,o=e.get("snap");if(!o&&!r)return!1;if("auto"===n||null==n){var a=this.animationThreshold;if(r&&i.getBandWidth()>a)return!0;if(o){var s=oI(t).seriesDataCount,l=i.getExtent();return Math.abs(l[0]-l[1])/s>a}return!1}return!0===n},t.prototype.makeElOption=function(t,e,n,i,r){},t.prototype.createPointerEl=function(t,e,n,i){var r=e.pointer;if(r){var o=VR(t).pointerEl=new qh[r.type](BR(e.pointer));t.add(o)}},t.prototype.createLabelEl=function(t,e,n,i){if(e.label){var r=VR(t).labelEl=new Bs(BR(e.label));t.add(r),YR(r,i)}},t.prototype.updatePointerEl=function(t,e,n){var i=VR(t).pointerEl;i&&e.pointer&&(i.setStyle(e.pointer.style),n(i,{shape:e.pointer.shape}))},t.prototype.updateLabelEl=function(t,e,n,i){var r=VR(t).labelEl;r&&(r.setStyle(e.label.style),n(r,{x:e.label.x,y:e.label.y}),YR(r,i))},t.prototype._renderHandle=function(t){if(!this._dragging&&this.updateHandleTransform){var e,n=this._axisPointerModel,i=this._api.getZr(),r=this._handle,o=n.getModel("handle"),a=n.get("status");if(!o.get("show")||!a||"hide"===a)return r&&i.remove(r),void(this._handle=null);this._handle||(e=!0,r=this._handle=Wh(o.get("icon"),{cursor:"move",draggable:!0,onmousemove:function(t){pe(t.event)},onmousedown:FR(this._onHandleDragMove,this,0,0),drift:FR(this._onHandleDragMove,this),ondragend:FR(this._onHandleDragEnd,this)}),i.add(r)),XR(r,n,!1),r.setStyle(o.getItemStyle(null,["color","borderColor","borderWidth","opacity","shadowColor","shadowBlur","shadowOffsetX","shadowOffsetY"]));var s=o.get("size");Y(s)||(s=[s,s]),r.scaleX=s[0]/2,r.scaleY=s[1]/2,Eg(this,"_doDispatchAxisPointer",o.get("throttle")||0,"fixRate"),this._moveHandleToValue(t,e)}},t.prototype._moveHandleToValue=function(t,e){WR(this._axisPointerModel,!e&&this._moveAnimation,this._handle,UR(this.getHandleTransform(t,this._axisModel,this._axisPointerModel)))},t.prototype._onHandleDragMove=function(t,e){var n=this._handle;if(n){this._dragging=!0;var i=this.updateHandleTransform(UR(n),[t,e],this._axisModel,this._axisPointerModel);this._payloadInfo=i,n.stopAnimation(),n.attr(UR(i)),VR(n).lastProp=null,this._doDispatchAxisPointer()}},t.prototype._doDispatchAxisPointer=function(){if(this._handle){var t=this._payloadInfo,e=this._axisModel;this._api.dispatchAction({type:"updateAxisPointer",x:t.cursorPoint[0],y:t.cursorPoint[1],tooltipOption:t.tooltipOption,axesInfo:[{axisDim:e.axis.dim,axisIndex:e.componentIndex}]})}},t.prototype._onHandleDragEnd=function(){if(this._dragging=!1,this._handle){var t=this._axisPointerModel.get("value");this._moveHandleToValue(t),this._api.dispatchAction({type:"hideTip"})}},t.prototype.clear=function(t){this._lastValue=null,this._lastStatus=null;var e=t.getZr(),n=this._group,i=this._handle;e&&n&&(this._lastGraphicKey=null,n&&e.remove(n),i&&e.remove(i),this._group=null,this._handle=null,this._payloadInfo=null),zg(this,"_doDispatchAxisPointer")},t.prototype.doClear=function(){},t.prototype.buildLabel=function(t,e,n){return{x:t[n=n||0],y:t[1-n],width:e[n],height:e[1-n]}},t}();function WR(t,e,n,i){HR(VR(n).lastProp,i)||(VR(n).lastProp=i,e?dh(n,i,t):(n.stopAnimation(),n.attr(i)))}function HR(t,e){if(q(t)&&q(e)){var n=!0;return E(e,(function(e,i){n=n&&HR(t[i],e)})),!!n}return t===e}function YR(t,e){t[e.get(["label","show"])?"show":"hide"]()}function UR(t){return{x:t.x||0,y:t.y||0,rotation:t.rotation||0}}function XR(t,e,n){var i=e.get("z"),r=e.get("zlevel");t&&t.traverse((function(t){"group"!==t.type&&(null!=i&&(t.z=i),null!=r&&(t.zlevel=r),t.silent=n)}))}function ZR(t){var e,n=t.get("type"),i=t.getModel(n+"Style");return"line"===n?(e=i.getLineStyle()).fill=null:"shadow"===n&&((e=i.getAreaStyle()).stroke=null),e}function jR(t,e,n,i,r){var o=qR(n.get("value"),e.axis,e.ecModel,n.get("seriesDataIndices"),{precision:n.get(["label","precision"]),formatter:n.get(["label","formatter"])}),a=n.getModel("label"),s=dp(a.get("padding")||0),l=a.getFont(),u=_r(o,l),h=r.position,c=u.width+s[1]+s[3],p=u.height+s[0]+s[2],d=r.align;"right"===d&&(h[0]-=c),"center"===d&&(h[0]-=c/2);var f=r.verticalAlign;"bottom"===f&&(h[1]-=p),"middle"===f&&(h[1]-=p/2),function(t,e,n,i){var r=i.getWidth(),o=i.getHeight();t[0]=Math.min(t[0]+e,r)-e,t[1]=Math.min(t[1]+n,o)-n,t[0]=Math.max(t[0],0),t[1]=Math.max(t[1],0)}(h,c,p,i);var g=a.get("backgroundColor");g&&"auto"!==g||(g=e.get(["axisLine","lineStyle","color"])),t.label={x:h[0],y:h[1],style:ec(a,{text:o,font:l,fill:a.getTextColor(),padding:s,backgroundColor:g}),z2:10}}function qR(t,e,n,i,r){t=e.scale.parse(t);var o=e.scale.getLabel({value:t},{precision:r.precision}),a=r.formatter;if(a){var s={value:d_(e,{value:t}),axisDimension:e.dim,axisIndex:e.index,seriesData:[]};E(i,(function(t){var e=n.getSeriesByIndex(t.seriesIndex),i=t.dataIndexInside,r=e&&e.getDataParams(i);r&&s.seriesData.push(r)})),X(a)?o=a.replace("{value}",o):U(a)&&(o=a(s))}return o}function KR(t,e,n){var i=[1,0,0,1,0,0];return we(i,i,n.rotation),be(i,i,n.position),Eh([t.dataToCoord(e),(n.labelOffset||0)+(n.labelDirection||1)*(n.labelMargin||0)],i)}function $R(t,e,n,i,r,o){var a=KM.innerTextLayout(n.rotation,0,n.labelDirection);n.labelMargin=r.get(["label","margin"]),jR(e,i,r,o,{position:KR(i.axis,t,n),align:a.textAlign,verticalAlign:a.textVerticalAlign})}function JR(t,e,n){return{x1:t[n=n||0],y1:t[1-n],x2:e[n],y2:e[1-n]}}function QR(t,e,n){return{x:t[n=n||0],y:t[1-n],width:e[n],height:e[1-n]}}function tN(t,e,n,i,r,o){return{cx:t,cy:e,r0:n,r:i,startAngle:r,endAngle:o,clockwise:!0}}var eN=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.makeElOption=function(t,e,n,i,r){var o=n.axis,a=o.grid,s=i.get("type"),l=nN(a,o).getOtherAxis(o).getGlobalExtent(),u=o.toGlobalCoord(o.dataToCoord(e,!0));if(s&&"none"!==s){var h=ZR(i),c=iN[s](o,u,l);c.style=h,t.graphicKey=c.type,t.pointer=c}$R(e,t,FM(a.model,n),n,i,r)},e.prototype.getHandleTransform=function(t,e,n){var i=FM(e.axis.grid.model,e,{labelInside:!1});i.labelMargin=n.get(["handle","margin"]);var r=KR(e.axis,t,i);return{x:r[0],y:r[1],rotation:i.rotation+(i.labelDirection<0?Math.PI:0)}},e.prototype.updateHandleTransform=function(t,e,n,i){var r=n.axis,o=r.grid,a=r.getGlobalExtent(!0),s=nN(o,r).getOtherAxis(r).getGlobalExtent(),l="x"===r.dim?0:1,u=[t.x,t.y];u[l]+=e[l],u[l]=Math.min(a[1],u[l]),u[l]=Math.max(a[0],u[l]);var h=(s[1]+s[0])/2,c=[h,h];c[l]=u[l];return{x:u[0],y:u[1],rotation:t.rotation,cursorPoint:c,tooltipOption:[{verticalAlign:"middle"},{align:"center"}][l]}},e}(GR);function nN(t,e){var n={};return n[e.dim+"AxisIndex"]=e.index,t.getCartesian(n)}var iN={line:function(t,e,n){return{type:"Line",subPixelOptimize:!0,shape:JR([e,n[0]],[e,n[1]],rN(t))}},shadow:function(t,e,n){var i=Math.max(1,t.getBandWidth()),r=n[1]-n[0];return{type:"Rect",shape:QR([e-i/2,n[0]],[i,r],rN(t))}}};function rN(t){return"x"===t.dim?0:1}var oN=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="axisPointer",e.defaultOption={show:"auto",z:50,type:"line",snap:!1,triggerTooltip:!0,value:null,status:null,link:[],animation:null,animationDurationUpdate:200,lineStyle:{color:"#B9BEC9",width:1,type:"dashed"},shadowStyle:{color:"rgba(210,219,238,0.2)"},label:{show:!0,formatter:null,precision:"auto",margin:3,color:"#fff",padding:[5,7,5,7],backgroundColor:"auto",borderColor:null,borderWidth:0,borderRadius:3},handle:{show:!1,icon:"M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z",size:45,margin:50,color:"#333",shadowBlur:3,shadowColor:"#aaa",shadowOffsetX:0,shadowOffsetY:2,throttle:40}},e}(Op),aN=Po(),sN=E;function lN(t,e,n){if(!r.node){var i=e.getZr();aN(i).records||(aN(i).records={}),function(t,e){if(aN(t).initialized)return;function n(n,i){t.on(n,(function(n){var r=function(t){var e={showTip:[],hideTip:[]},n=function(i){var r=e[i.type];r?r.push(i):(i.dispatchAction=n,t.dispatchAction(i))};return{dispatchAction:n,pendings:e}}(e);sN(aN(t).records,(function(t){t&&i(t,n,r.dispatchAction)})),function(t,e){var n,i=t.showTip.length,r=t.hideTip.length;i?n=t.showTip[i-1]:r&&(n=t.hideTip[r-1]);n&&(n.dispatchAction=null,e.dispatchAction(n))}(r.pendings,e)}))}aN(t).initialized=!0,n("click",H(hN,"click")),n("mousemove",H(hN,"mousemove")),n("globalout",uN)}(i,e),(aN(i).records[t]||(aN(i).records[t]={})).handler=n}}function uN(t,e,n){t.handler("leave",null,n)}function hN(t,e,n,i){e.handler(t,n,i)}function cN(t,e){if(!r.node){var n=e.getZr();(aN(n).records||{})[t]&&(aN(n).records[t]=null)}}var pN=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){var i=e.getComponent("tooltip"),r=t.get("triggerOn")||i&&i.get("triggerOn")||"mousemove|click";lN("axisPointer",n,(function(t,e,n){"none"!==r&&("leave"===t||r.indexOf(t)>=0)&&n({type:"updateAxisPointer",currTrigger:t,x:e&&e.offsetX,y:e&&e.offsetY})}))},e.prototype.remove=function(t,e){cN("axisPointer",e)},e.prototype.dispose=function(t,e){cN("axisPointer",e)},e.type="axisPointer",e}(wg);function dN(t,e){var n,i=[],r=t.seriesIndex;if(null==r||!(n=e.getSeriesByIndex(r)))return{point:[]};var o=n.getData(),a=Lo(o,t);if(null==a||a<0||Y(a))return{point:[]};var s=o.getItemGraphicEl(a),l=n.coordinateSystem;if(n.getTooltipPosition)i=n.getTooltipPosition(a)||[];else if(l&&l.dataToPoint)if(t.isStacked){var u=l.getBaseAxis(),h=l.getOtherAxis(u).dim,c=u.dim,p="x"===h||"radius"===h?1:0,d=o.mapDimension(c),f=[];f[p]=o.get(d,a),f[1-p]=o.get(o.getCalculationInfo("stackResultDimension"),a),i=l.dataToPoint(f)||[]}else i=l.dataToPoint(o.getValues(z(l.dimensions,(function(t){return o.mapDimension(t)})),a))||[];else if(s){var g=s.getBoundingRect().clone();g.applyTransform(s.transform),i=[g.x+g.width/2,g.y+g.height/2]}return{point:i,el:s}}var fN=Po();function gN(t,e,n){var i=t.currTrigger,r=[t.x,t.y],o=t,a=t.dispatchAction||W(n.dispatchAction,n),s=e.getComponent("axisPointer").coordSysAxesInfo;if(s){_N(r)&&(r=dN({seriesIndex:o.seriesIndex,dataIndex:o.dataIndex},e).point);var l=_N(r),u=o.axesInfo,h=s.axesInfo,c="leave"===i||_N(r),p={},d={},f={list:[],map:{}},g={showPointer:H(vN,d),showTooltip:H(mN,f)};E(s.coordSysMap,(function(t,e){var n=l||t.containPoint(r);E(s.coordSysAxesInfo[e],(function(t,e){var i=t.axis,o=function(t,e){for(var n=0;n<(t||[]).length;n++){var i=t[n];if(e.axis.dim===i.axisDim&&e.axis.model.componentIndex===i.axisIndex)return i}}(u,t);if(!c&&n&&(!u||o)){var a=o&&o.value;null!=a||l||(a=i.pointToData(r)),null!=a&&yN(t,a,g,!1,p)}}))}));var y={};return E(h,(function(t,e){var n=t.linkGroup;n&&!d[e]&&E(n.axesInfo,(function(e,i){var r=d[i];if(e!==t&&r){var o=r.value;n.mapper&&(o=t.axis.scale.parse(n.mapper(o,xN(e),xN(t)))),y[t.key]=o}}))})),E(y,(function(t,e){yN(h[e],t,g,!0,p)})),function(t,e,n){var i=n.axesInfo=[];E(e,(function(e,n){var r=e.axisPointerModel.option,o=t[n];o?(!e.useHandle&&(r.status="show"),r.value=o.value,r.seriesDataIndices=(o.payloadBatch||[]).slice()):!e.useHandle&&(r.status="hide"),"show"===r.status&&i.push({axisDim:e.axis.dim,axisIndex:e.axis.model.componentIndex,value:r.value})}))}(d,h,p),function(t,e,n,i){if(_N(e)||!t.list.length)return void i({type:"hideTip"});var r=((t.list[0].dataByAxis[0]||{}).seriesDataIndices||[])[0]||{};i({type:"showTip",escapeConnect:!0,x:e[0],y:e[1],tooltipOption:n.tooltipOption,position:n.position,dataIndexInside:r.dataIndexInside,dataIndex:r.dataIndex,seriesIndex:r.seriesIndex,dataByCoordSys:t.list})}(f,r,t,a),function(t,e,n){var i=n.getZr(),r="axisPointerLastHighlights",o=fN(i)[r]||{},a=fN(i)[r]={};E(t,(function(t,e){var n=t.axisPointerModel.option;"show"===n.status&&E(n.seriesDataIndices,(function(t){var e=t.seriesIndex+" | "+t.dataIndex;a[e]=t}))}));var s=[],l=[];E(o,(function(t,e){!a[e]&&l.push(t)})),E(a,(function(t,e){!o[e]&&s.push(t)})),l.length&&n.dispatchAction({type:"downplay",escapeConnect:!0,notBlur:!0,batch:l}),s.length&&n.dispatchAction({type:"highlight",escapeConnect:!0,notBlur:!0,batch:s})}(h,0,n),p}}function yN(t,e,n,i,r){var o=t.axis;if(!o.scale.isBlank()&&o.containData(e))if(t.involveSeries){var a=function(t,e){var n=e.axis,i=n.dim,r=t,o=[],a=Number.MAX_VALUE,s=-1;return E(e.seriesModels,(function(e,l){var u,h,c=e.getData().mapDimensionsAll(i);if(e.getAxisTooltipData){var p=e.getAxisTooltipData(c,t,n);h=p.dataIndices,u=p.nestestValue}else{if(!(h=e.getData().indicesOfNearest(c[0],t,"category"===n.type?.5:null)).length)return;u=e.getData().get(c[0],h[0])}if(null!=u&&isFinite(u)){var d=t-u,f=Math.abs(d);f<=a&&((f=0&&s<0)&&(a=f,s=d,r=u,o.length=0),E(h,(function(t){o.push({seriesIndex:e.seriesIndex,dataIndexInside:t,dataIndex:e.getData().getRawIndex(t)})})))}})),{payloadBatch:o,snapToValue:r}}(e,t),s=a.payloadBatch,l=a.snapToValue;s[0]&&null==r.seriesIndex&&A(r,s[0]),!i&&t.snap&&o.containData(l)&&null!=l&&(e=l),n.showPointer(t,e,s),n.showTooltip(t,a,l)}else n.showPointer(t,e)}function vN(t,e,n,i){t[e.key]={value:n,payloadBatch:i}}function mN(t,e,n,i){var r=n.payloadBatch,o=e.axis,a=o.model,s=e.axisPointerModel;if(e.triggerTooltip&&r.length){var l=e.coordSys.model,u=sI(l),h=t.map[u];h||(h=t.map[u]={coordSysId:l.id,coordSysIndex:l.componentIndex,coordSysType:l.type,coordSysMainType:l.mainType,dataByAxis:[]},t.list.push(h)),h.dataByAxis.push({axisDim:o.dim,axisIndex:a.componentIndex,axisType:a.type,axisId:a.id,value:i,valueLabelOpt:{precision:s.get(["label","precision"]),formatter:s.get(["label","formatter"])},seriesDataIndices:r.slice()})}}function xN(t){var e=t.axis.model,n={},i=n.axisDim=t.axis.dim;return n.axisIndex=n[i+"AxisIndex"]=e.componentIndex,n.axisName=n[i+"AxisName"]=e.name,n.axisId=n[i+"AxisId"]=e.id,n}function _N(t){return!t||null==t[0]||isNaN(t[0])||null==t[1]||isNaN(t[1])}function bN(t){uI.registerAxisPointerClass("CartesianAxisPointer",eN),t.registerComponentModel(oN),t.registerComponentView(pN),t.registerPreprocessor((function(t){if(t){(!t.axisPointer||0===t.axisPointer.length)&&(t.axisPointer={});var e=t.axisPointer.link;e&&!Y(e)&&(t.axisPointer.link=[e])}})),t.registerProcessor(t.PRIORITY.PROCESSOR.STATISTIC,(function(t,e){t.getComponent("axisPointer").coordSysAxesInfo=nI(t,e)})),t.registerAction({type:"updateAxisPointer",event:"updateAxisPointer",update:":updateAxisPointer"},gN)}var wN=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.makeElOption=function(t,e,n,i,r){var o=n.axis;"angle"===o.dim&&(this.animationThreshold=Math.PI/18);var a=o.polar,s=a.getOtherAxis(o).getExtent(),l=o.dataToCoord(e),u=i.get("type");if(u&&"none"!==u){var h=ZR(i),c=SN[u](o,a,l,s);c.style=h,t.graphicKey=c.type,t.pointer=c}var p=function(t,e,n,i,r){var o=e.axis,a=o.dataToCoord(t),s=i.getAngleAxis().getExtent()[0];s=s/180*Math.PI;var l,u,h,c=i.getRadiusAxis().getExtent();if("radius"===o.dim){var p=[1,0,0,1,0,0];we(p,p,s),be(p,p,[i.cx,i.cy]),l=Eh([a,-r],p);var d=e.getModel("axisLabel").get("rotate")||0,f=KM.innerTextLayout(s,d*Math.PI/180,-1);u=f.textAlign,h=f.textVerticalAlign}else{var g=c[1];l=i.coordToPoint([g+r,a]);var y=i.cx,v=i.cy;u=Math.abs(l[0]-y)/g<.3?"center":l[0]>y?"left":"right",h=Math.abs(l[1]-v)/g<.3?"middle":l[1]>v?"top":"bottom"}return{position:l,align:u,verticalAlign:h}}(e,n,0,a,i.get(["label","margin"]));jR(t,n,i,r,p)},e}(GR);var SN={line:function(t,e,n,i){return"angle"===t.dim?{type:"Line",shape:JR(e.coordToPoint([i[0],n]),e.coordToPoint([i[1],n]))}:{type:"Circle",shape:{cx:e.cx,cy:e.cy,r:n}}},shadow:function(t,e,n,i){var r=Math.max(1,t.getBandWidth()),o=Math.PI/180;return"angle"===t.dim?{type:"Sector",shape:tN(e.cx,e.cy,i[0],i[1],(-n-r/2)*o,(r/2-n)*o)}:{type:"Sector",shape:tN(e.cx,e.cy,n-r/2,n+r/2,0,2*Math.PI)}}},MN=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.findAxisModel=function(t){var e;return this.ecModel.eachComponent(t,(function(t){t.getCoordSysModel()===this&&(e=t)}),this),e},e.type="polar",e.dependencies=["radiusAxis","angleAxis"],e.defaultOption={z:0,center:["50%","50%"],radius:"80%"},e}(Op),IN=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.getCoordSysModel=function(){return this.getReferringComponents("polar",Eo).models[0]},e.type="polarAxis",e}(Op);R(IN,m_);var TN=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="angleAxis",e}(IN),CN=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="radiusAxis",e}(IN),DN=function(t){function e(e,n){return t.call(this,"radius",e,n)||this}return n(e,t),e.prototype.pointToData=function(t,e){return this.polar.pointToData(t,e)["radius"===this.dim?0:1]},e}(q_);DN.prototype.dataToRadius=q_.prototype.dataToCoord,DN.prototype.radiusToData=q_.prototype.coordToData;var AN=Po(),kN=function(t){function e(e,n){return t.call(this,"angle",e,n||[0,360])||this}return n(e,t),e.prototype.pointToData=function(t,e){return this.polar.pointToData(t,e)["radius"===this.dim?0:1]},e.prototype.calculateCategoryInterval=function(){var t=this,e=t.getLabelModel(),n=t.scale,i=n.getExtent(),r=n.count();if(i[1]-i[0]<1)return 0;var o=i[0],a=t.dataToCoord(o+1)-t.dataToCoord(o),s=Math.abs(a),l=_r(null==o?"":o+"",e.getFont(),"center","top"),u=Math.max(l.height,7)/s;isNaN(u)&&(u=1/0);var h=Math.max(0,Math.floor(u)),c=AN(t.model),p=c.lastAutoInterval,d=c.lastTickCount;return null!=p&&null!=d&&Math.abs(p-h)<=1&&Math.abs(d-r)<=1&&p>h?h=p:(c.lastTickCount=r,c.lastAutoInterval=h),h},e}(q_);kN.prototype.dataToAngle=q_.prototype.dataToCoord,kN.prototype.angleToData=q_.prototype.coordToData;var LN=["radius","angle"],PN=function(){function t(t){this.dimensions=LN,this.type="polar",this.cx=0,this.cy=0,this._radiusAxis=new DN,this._angleAxis=new kN,this.axisPointerEnabled=!0,this.name=t||"",this._radiusAxis.polar=this._angleAxis.polar=this}return t.prototype.containPoint=function(t){var e=this.pointToCoord(t);return this._radiusAxis.contain(e[0])&&this._angleAxis.contain(e[1])},t.prototype.containData=function(t){return this._radiusAxis.containData(t[0])&&this._angleAxis.containData(t[1])},t.prototype.getAxis=function(t){return this["_"+t+"Axis"]},t.prototype.getAxes=function(){return[this._radiusAxis,this._angleAxis]},t.prototype.getAxesByScale=function(t){var e=[],n=this._angleAxis,i=this._radiusAxis;return n.scale.type===t&&e.push(n),i.scale.type===t&&e.push(i),e},t.prototype.getAngleAxis=function(){return this._angleAxis},t.prototype.getRadiusAxis=function(){return this._radiusAxis},t.prototype.getOtherAxis=function(t){var e=this._angleAxis;return t===e?this._radiusAxis:e},t.prototype.getBaseAxis=function(){return this.getAxesByScale("ordinal")[0]||this.getAxesByScale("time")[0]||this.getAngleAxis()},t.prototype.getTooltipAxes=function(t){var e=null!=t&&"auto"!==t?this.getAxis(t):this.getBaseAxis();return{baseAxes:[e],otherAxes:[this.getOtherAxis(e)]}},t.prototype.dataToPoint=function(t,e){return this.coordToPoint([this._radiusAxis.dataToRadius(t[0],e),this._angleAxis.dataToAngle(t[1],e)])},t.prototype.pointToData=function(t,e){var n=this.pointToCoord(t);return[this._radiusAxis.radiusToData(n[0],e),this._angleAxis.angleToData(n[1],e)]},t.prototype.pointToCoord=function(t){var e=t[0]-this.cx,n=t[1]-this.cy,i=this.getAngleAxis(),r=i.getExtent(),o=Math.min(r[0],r[1]),a=Math.max(r[0],r[1]);i.inverse?o=a-360:a=o+360;var s=Math.sqrt(e*e+n*n);e/=s,n/=s;for(var l=Math.atan2(-n,e)/Math.PI*180,u=la;)l+=360*u;return[s,l]},t.prototype.coordToPoint=function(t){var e=t[0],n=t[1]/180*Math.PI;return[Math.cos(n)*e+this.cx,-Math.sin(n)*e+this.cy]},t.prototype.getArea=function(){var t=this.getAngleAxis(),e=this.getRadiusAxis().getExtent().slice();e[0]>e[1]&&e.reverse();var n=t.getExtent(),i=Math.PI/180;return{cx:this.cx,cy:this.cy,r0:e[0],r:e[1],startAngle:-n[0]*i,endAngle:-n[1]*i,clockwise:t.inverse,contain:function(t,e){var n=t-this.cx,i=e-this.cy,r=n*n+i*i-1e-4,o=this.r,a=this.r0;return r<=o*o&&r>=a*a}}},t.prototype.convertToPixel=function(t,e,n){return ON(e)===this?this.dataToPoint(n):null},t.prototype.convertFromPixel=function(t,e,n){return ON(e)===this?this.pointToData(n):null},t}();function ON(t){var e=t.seriesModel,n=t.polarModel;return n&&n.coordinateSystem||e&&e.coordinateSystem}function RN(t,e){var n=this,i=n.getAngleAxis(),r=n.getRadiusAxis();if(i.scale.setExtent(1/0,-1/0),r.scale.setExtent(1/0,-1/0),t.eachSeries((function(t){if(t.coordinateSystem===n){var e=t.getData();E(v_(e,"radius"),(function(t){r.scale.unionExtentFromData(e,t)})),E(v_(e,"angle"),(function(t){i.scale.unionExtentFromData(e,t)}))}})),h_(i.scale,i.model),h_(r.scale,r.model),"category"===i.type&&!i.onBand){var o=i.getExtent(),a=360/i.scale.count();i.inverse?o[1]+=a:o[1]-=a,i.setExtent(o[0],o[1])}}function NN(t,e){if(t.type=e.get("type"),t.scale=c_(e),t.onBand=e.get("boundaryGap")&&"category"===t.type,t.inverse=e.get("inverse"),function(t){return"angleAxis"===t.mainType}(e)){t.inverse=t.inverse!==e.get("clockwise");var n=e.get("startAngle");t.setExtent(n,n+(t.inverse?-360:360))}e.axis=t,t.model=e}var EN={dimensions:LN,create:function(t,e){var n=[];return t.eachComponent("polar",(function(t,i){var r=new PN(i+"");r.update=RN;var o=r.getRadiusAxis(),a=r.getAngleAxis(),s=t.findAxisModel("radiusAxis"),l=t.findAxisModel("angleAxis");NN(o,s),NN(a,l),function(t,e,n){var i=e.get("center"),r=n.getWidth(),o=n.getHeight();t.cx=Ur(i[0],r),t.cy=Ur(i[1],o);var a=t.getRadiusAxis(),s=Math.min(r,o)/2,l=e.get("radius");null==l?l=[0,"100%"]:Y(l)||(l=[0,l]);var u=[Ur(l[0],s),Ur(l[1],s)];a.inverse?a.setExtent(u[1],u[0]):a.setExtent(u[0],u[1])}(r,t,e),n.push(r),t.coordinateSystem=r,r.model=t})),t.eachSeries((function(t){if("polar"===t.get("coordinateSystem")){var e=t.getReferringComponents("polar",Eo).models[0];0,t.coordinateSystem=e.coordinateSystem}})),n}},zN=["axisLine","axisLabel","axisTick","minorTick","splitLine","minorSplitLine","splitArea"];function VN(t,e,n){e[1]>e[0]&&(e=e.slice().reverse());var i=t.coordToPoint([e[0],n]),r=t.coordToPoint([e[1],n]);return{x1:i[0],y1:i[1],x2:r[0],y2:r[1]}}function BN(t){return t.getRadiusAxis().inverse?0:1}function FN(t){var e=t[0],n=t[t.length-1];e&&n&&Math.abs(Math.abs(e.coord-n.coord)-360)<1e-4&&t.pop()}var GN=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.axisPointerClass="PolarAxisPointer",n}return n(e,t),e.prototype.render=function(t,e){if(this.group.removeAll(),t.get("show")){var n=t.axis,i=n.polar,r=i.getRadiusAxis().getExtent(),o=n.getTicksCoords(),a=n.getMinorTicksCoords(),s=z(n.getViewLabels(),(function(t){t=T(t);var e=n.scale,i="ordinal"===e.type?e.getRawOrdinalNumber(t.tickValue):t.tickValue;return t.coord=n.dataToCoord(i),t}));FN(s),FN(o),E(zN,(function(e){!t.get([e,"show"])||n.scale.isBlank()&&"axisLine"!==e||WN[e](this.group,t,i,o,a,r,s)}),this)}},e.type="angleAxis",e}(uI),WN={axisLine:function(t,e,n,i,r,o){var a,s=e.getModel(["axisLine","lineStyle"]),l=BN(n),u=l?0:1;(a=0===o[u]?new xu({shape:{cx:n.cx,cy:n.cy,r:o[l]},style:s.getLineStyle(),z2:1,silent:!0}):new Vu({shape:{cx:n.cx,cy:n.cy,r:o[l],r0:o[u]},style:s.getLineStyle(),z2:1,silent:!0})).style.fill=null,t.add(a)},axisTick:function(t,e,n,i,r,o){var a=e.getModel("axisTick"),s=(a.get("inside")?-1:1)*a.get("length"),l=o[BN(n)],u=z(i,(function(t){return new Xu({shape:VN(n,[l,l+s],t.coord)})}));t.add(Lh(u,{style:k(a.getModel("lineStyle").getLineStyle(),{stroke:e.get(["axisLine","lineStyle","color"])})}))},minorTick:function(t,e,n,i,r,o){if(r.length){for(var a=e.getModel("axisTick"),s=e.getModel("minorTick"),l=(a.get("inside")?-1:1)*s.get("length"),u=o[BN(n)],h=[],c=0;cf?"left":"right",v=Math.abs(d[1]-g)/p<.3?"middle":d[1]>g?"top":"bottom";if(s&&s[c]){var m=s[c];q(m)&&m.textStyle&&(a=new Sc(m.textStyle,l,l.ecModel))}var x=new Bs({silent:KM.isLabelSilent(e),style:ec(a,{x:d[0],y:d[1],fill:a.getTextColor()||e.get(["axisLine","lineStyle","color"]),text:i.formattedLabel,align:y,verticalAlign:v})});if(t.add(x),h){var _=KM.makeAxisEventDataBase(e);_.targetType="axisLabel",_.value=i.rawLabel,Js(x).eventData=_}}),this)},splitLine:function(t,e,n,i,r,o){var a=e.getModel("splitLine").getModel("lineStyle"),s=a.get("color"),l=0;s=s instanceof Array?s:[s];for(var u=[],h=0;h=0?"p":"n",T=_;m&&(i[s][M]||(i[s][M]={p:_,n:_}),T=i[s][M][I]);var C=void 0,D=void 0,A=void 0,k=void 0;if("radius"===c.dim){var L=c.dataToCoord(S)-_,P=o.dataToCoord(M);Math.abs(L)=k})}}}))}var KN={startAngle:90,clockwise:!0,splitNumber:12,axisLabel:{rotate:0}},$N={splitNumber:5},JN=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="polar",e}(wg);function QN(t,e){e=e||{};var n=t.coordinateSystem,i=t.axis,r={},o=i.position,a=i.orient,s=n.getRect(),l=[s.x,s.x+s.width,s.y,s.y+s.height],u={horizontal:{top:l[2],bottom:l[3]},vertical:{left:l[0],right:l[1]}};r.position=["vertical"===a?u.vertical[o]:l[0],"horizontal"===a?u.horizontal[o]:l[3]];r.rotation=Math.PI/2*{horizontal:0,vertical:1}[a];r.labelDirection=r.tickDirection=r.nameDirection={top:-1,bottom:1,right:1,left:-1}[o],t.get(["axisTick","inside"])&&(r.tickDirection=-r.tickDirection),it(e.labelInside,t.get(["axisLabel","inside"]))&&(r.labelDirection=-r.labelDirection);var h=e.rotate;return null==h&&(h=t.get(["axisLabel","rotate"])),r.labelRotation="top"===o?-h:h,r.z2=1,r}var tE=["axisLine","axisTickLabel","axisName"],eE=["splitArea","splitLine"],nE=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.axisPointerClass="SingleAxisPointer",n}return n(e,t),e.prototype.render=function(e,n,i,r){var o=this.group;o.removeAll();var a=this._axisGroup;this._axisGroup=new Er;var s=QN(e),l=new KM(e,s);E(tE,l.add,l),o.add(this._axisGroup),o.add(l.getGroup()),E(eE,(function(t){e.get([t,"show"])&&iE[t](this,this.group,this._axisGroup,e)}),this),Bh(a,this._axisGroup,e),t.prototype.render.call(this,e,n,i,r)},e.prototype.remove=function(){pI(this)},e.type="singleAxis",e}(uI),iE={splitLine:function(t,e,n,i){var r=i.axis;if(!r.scale.isBlank()){var o=i.getModel("splitLine"),a=o.getModel("lineStyle"),s=a.get("color");s=s instanceof Array?s:[s];for(var l=a.get("width"),u=i.coordinateSystem.getRect(),h=r.isHorizontal(),c=[],p=0,d=r.getTicksCoords({tickModel:o}),f=[],g=[],y=0;y=e.y&&t[1]<=e.y+e.height:n.contain(n.toLocalCoord(t[1]))&&t[0]>=e.y&&t[0]<=e.y+e.height},t.prototype.pointToData=function(t){var e=this.getAxis();return[e.coordToData(e.toLocalCoord(t["horizontal"===e.orient?0:1]))]},t.prototype.dataToPoint=function(t){var e=this.getAxis(),n=this.getRect(),i=[],r="horizontal"===e.orient?0:1;return t instanceof Array&&(t=t[0]),i[r]=e.toGlobalCoord(e.dataToCoord(+t)),i[1-r]=0===r?n.y+n.height/2:n.x+n.width/2,i},t.prototype.convertToPixel=function(t,e,n){return lE(e)===this?this.dataToPoint(n):null},t.prototype.convertFromPixel=function(t,e,n){return lE(e)===this?this.pointToData(n):null},t}();function lE(t){var e=t.seriesModel,n=t.singleAxisModel;return n&&n.coordinateSystem||e&&e.coordinateSystem}var uE={create:function(t,e){var n=[];return t.eachComponent("singleAxis",(function(i,r){var o=new sE(i,t,e);o.name="single_"+r,o.resize(i,e),i.coordinateSystem=o,n.push(o)})),t.eachSeries((function(t){if("singleAxis"===t.get("coordinateSystem")){var e=t.getReferringComponents("singleAxis",Eo).models[0];t.coordinateSystem=e&&e.coordinateSystem}})),n},dimensions:aE},hE=["x","y"],cE=["width","height"],pE=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.makeElOption=function(t,e,n,i,r){var o=n.axis,a=o.coordinateSystem,s=gE(a,1-fE(o)),l=a.dataToPoint(e)[0],u=i.get("type");if(u&&"none"!==u){var h=ZR(i),c=dE[u](o,l,s);c.style=h,t.graphicKey=c.type,t.pointer=c}$R(e,t,QN(n),n,i,r)},e.prototype.getHandleTransform=function(t,e,n){var i=QN(e,{labelInside:!1});i.labelMargin=n.get(["handle","margin"]);var r=KR(e.axis,t,i);return{x:r[0],y:r[1],rotation:i.rotation+(i.labelDirection<0?Math.PI:0)}},e.prototype.updateHandleTransform=function(t,e,n,i){var r=n.axis,o=r.coordinateSystem,a=fE(r),s=gE(o,a),l=[t.x,t.y];l[a]+=e[a],l[a]=Math.min(s[1],l[a]),l[a]=Math.max(s[0],l[a]);var u=gE(o,1-a),h=(u[1]+u[0])/2,c=[h,h];return c[a]=l[a],{x:l[0],y:l[1],rotation:t.rotation,cursorPoint:c,tooltipOption:{verticalAlign:"middle"}}},e}(GR),dE={line:function(t,e,n){return{type:"Line",subPixelOptimize:!0,shape:JR([e,n[0]],[e,n[1]],fE(t))}},shadow:function(t,e,n){var i=t.getBandWidth(),r=n[1]-n[0];return{type:"Rect",shape:QR([e-i/2,n[0]],[i,r],fE(t))}}};function fE(t){return t.isHorizontal()?0:1}function gE(t,e){var n=t.getRect();return[n[hE[e]],n[hE[e]]+n[cE[e]]]}var yE=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="single",e}(wg);var vE=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.init=function(e,n,i){var r=kp(e);t.prototype.init.apply(this,arguments),mE(e,r)},e.prototype.mergeOption=function(e){t.prototype.mergeOption.apply(this,arguments),mE(this.option,e)},e.prototype.getCellSize=function(){return this.option.cellSize},e.type="calendar",e.defaultOption={z:2,left:80,top:60,cellSize:20,orient:"horizontal",splitLine:{show:!0,lineStyle:{color:"#000",width:1,type:"solid"}},itemStyle:{color:"#fff",borderWidth:1,borderColor:"#ccc"},dayLabel:{show:!0,firstDay:0,position:"start",margin:"50%",color:"#000"},monthLabel:{show:!0,position:"start",margin:5,align:"center",formatter:null,color:"#000"},yearLabel:{show:!0,position:null,margin:30,formatter:null,color:"#ccc",fontFamily:"sans-serif",fontWeight:"bolder",fontSize:20}},e}(Op);function mE(t,e){var n,i=t.cellSize;1===(n=Y(i)?i:t.cellSize=[i,i]).length&&(n[1]=n[0]);var r=z([0,1],(function(t){return function(t,e){return null!=t[Sp[e][0]]||null!=t[Sp[e][1]]&&null!=t[Sp[e][2]]}(e,t)&&(n[t]="auto"),null!=n[t]&&"auto"!==n[t]}));Ap(t,e,{type:"box",ignoreSize:r})}var xE=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){var i=this.group;i.removeAll();var r=t.coordinateSystem,o=r.getRangeInfo(),a=r.getOrient(),s=e.getLocaleModel();this._renderDayRect(t,o,i),this._renderLines(t,o,a,i),this._renderYearText(t,o,a,i),this._renderMonthText(t,s,a,i),this._renderWeekText(t,s,o,a,i)},e.prototype._renderDayRect=function(t,e,n){for(var i=t.coordinateSystem,r=t.getModel("itemStyle").getItemStyle(),o=i.getCellWidth(),a=i.getCellHeight(),s=e.start.time;s<=e.end.time;s=i.getNextNDay(s,1).time){var l=i.dataToRect([s],!1).tl,u=new Es({shape:{x:l[0],y:l[1],width:o,height:a},cursor:"default",style:r});n.add(u)}},e.prototype._renderLines=function(t,e,n,i){var r=this,o=t.coordinateSystem,a=t.getModel(["splitLine","lineStyle"]).getLineStyle(),s=t.get(["splitLine","show"]),l=a.lineWidth;this._tlpoints=[],this._blpoints=[],this._firstDayOfMonth=[],this._firstDayPoints=[];for(var u=e.start,h=0;u.time<=e.end.time;h++){p(u.formatedDate),0===h&&(u=o.getDateInfo(e.start.y+"-"+e.start.m));var c=u.date;c.setMonth(c.getMonth()+1),u=o.getDateInfo(c)}function p(e){r._firstDayOfMonth.push(o.getDateInfo(e)),r._firstDayPoints.push(o.dataToRect([e],!1).tl);var l=r._getLinePointsOfOneWeek(t,e,n);r._tlpoints.push(l[0]),r._blpoints.push(l[l.length-1]),s&&r._drawSplitline(l,a,i)}p(o.getNextNDay(e.end.time,1).formatedDate),s&&this._drawSplitline(r._getEdgesPoints(r._tlpoints,l,n),a,i),s&&this._drawSplitline(r._getEdgesPoints(r._blpoints,l,n),a,i)},e.prototype._getEdgesPoints=function(t,e,n){var i=[t[0].slice(),t[t.length-1].slice()],r="horizontal"===n?0:1;return i[0][r]=i[0][r]-e/2,i[1][r]=i[1][r]+e/2,i},e.prototype._drawSplitline=function(t,e,n){var i=new Hu({z2:20,shape:{points:t},style:e});n.add(i)},e.prototype._getLinePointsOfOneWeek=function(t,e,n){for(var i=t.coordinateSystem,r=i.getDateInfo(e),o=[],a=0;a<7;a++){var s=i.getNextNDay(r.time,a),l=i.dataToRect([s.time],!1);o[2*s.day]=l.tl,o[2*s.day+1]=l["horizontal"===n?"bl":"tr"]}return o},e.prototype._formatterLabel=function(t,e){return X(t)&&t?(n=t,E(e,(function(t,e){n=n.replace("{"+e+"}",i?ie(t):t)})),n):U(t)?t(e):e.nameMap;var n,i},e.prototype._yearTextPositionControl=function(t,e,n,i,r){var o=e[0],a=e[1],s=["center","bottom"];"bottom"===i?(a+=r,s=["center","top"]):"left"===i?o-=r:"right"===i?(o+=r,s=["center","top"]):a-=r;var l=0;return"left"!==i&&"right"!==i||(l=Math.PI/2),{rotation:l,x:o,y:a,style:{align:s[0],verticalAlign:s[1]}}},e.prototype._renderYearText=function(t,e,n,i){var r=t.getModel("yearLabel");if(r.get("show")){var o=r.get("margin"),a=r.get("position");a||(a="horizontal"!==n?"top":"left");var s=[this._tlpoints[this._tlpoints.length-1],this._blpoints[0]],l=(s[0][0]+s[1][0])/2,u=(s[0][1]+s[1][1])/2,h="horizontal"===n?0:1,c={top:[l,s[h][1]],bottom:[l,s[1-h][1]],left:[s[1-h][0],u],right:[s[h][0],u]},p=e.start.y;+e.end.y>+e.start.y&&(p=p+"-"+e.end.y);var d=r.get("formatter"),f={start:e.start.y,end:e.end.y,nameMap:p},g=this._formatterLabel(d,f),y=new Bs({z2:30,style:ec(r,{text:g})});y.attr(this._yearTextPositionControl(y,c[a],n,a,o)),i.add(y)}},e.prototype._monthTextPositionControl=function(t,e,n,i,r){var o="left",a="top",s=t[0],l=t[1];return"horizontal"===n?(l+=r,e&&(o="center"),"start"===i&&(a="bottom")):(s+=r,e&&(a="middle"),"start"===i&&(o="right")),{x:s,y:l,align:o,verticalAlign:a}},e.prototype._renderMonthText=function(t,e,n,i){var r=t.getModel("monthLabel");if(r.get("show")){var o=r.get("nameMap"),a=r.get("margin"),s=r.get("position"),l=r.get("align"),u=[this._tlpoints,this._blpoints];o&&!X(o)||(o&&(e=Rc(o)||e),o=e.get(["time","monthAbbr"])||[]);var h="start"===s?0:1,c="horizontal"===n?0:1;a="start"===s?-a:a;for(var p="center"===l,d=0;d=i.start.time&&n.timea.end.time&&t.reverse(),t},t.prototype._getRangeInfo=function(t){var e,n=[this.getDateInfo(t[0]),this.getDateInfo(t[1])];n[0].time>n[1].time&&(e=!0,n.reverse());var i=Math.floor(n[1].time/_E)-Math.floor(n[0].time/_E)+1,r=new Date(n[0].time),o=r.getDate(),a=n[1].date.getDate();r.setDate(o+i-1);var s=r.getDate();if(s!==a)for(var l=r.getTime()-n[1].time>0?1:-1;(s=r.getDate())!==a&&(r.getTime()-n[1].time)*l>0;)i-=l,r.setDate(s-l);var u=Math.floor((i+n[0].day+6)/7),h=e?1-u:u-1;return e&&n.reverse(),{range:[n[0].formatedDate,n[1].formatedDate],start:n[0],end:n[1],allDay:i,weeks:u,nthWeek:h,fweek:n[0].day,lweek:n[1].day}},t.prototype._getDateByWeeksAndDay=function(t,e,n){var i=this._getRangeInfo(n);if(t>i.weeks||0===t&&ei.lweek)return null;var r=7*(t-1)-i.fweek+e,o=new Date(i.start.time);return o.setDate(+i.start.d+r),this.getDateInfo(o)},t.create=function(e,n){var i=[];return e.eachComponent("calendar",(function(r){var o=new t(r,e,n);i.push(o),r.coordinateSystem=o})),e.eachSeries((function(t){"calendar"===t.get("coordinateSystem")&&(t.coordinateSystem=i[t.get("calendarIndex")||0])})),i},t.dimensions=["time","value"],t}();function wE(t){var e=t.calendarModel,n=t.seriesModel;return e?e.coordinateSystem:n?n.coordinateSystem:null}function SE(t,e){var n;return E(e,(function(e){null!=t[e]&&"auto"!==t[e]&&(n=!0)})),n}var ME=["transition","enterFrom","leaveTo"],IE=ME.concat(["enterAnimation","updateAnimation","leaveAnimation"]);function TE(t,e,n){if(n&&(!t[n]&&e[n]&&(t[n]={}),t=t[n],e=e[n]),t&&e)for(var i=n?ME:IE,r=0;r=0;l--){var p,d,f;if(f=null!=(d=Do((p=n[l]).id,null))?r.get(d):null){var g=f.parent,y=(c=AE(g),{}),v=Cp(f,p,g===i?{width:o,height:a}:{width:c.width,height:c.height},null,{hv:p.hv,boundingMode:p.bounding},y);if(!AE(f).isNew&&v){for(var m=p.transition,x={},_=0;_=0)?x[b]=w:f[b]=w}dh(f,x,t,0)}else f.attr(y)}}},e.prototype._clear=function(){var t=this,e=this._elMap;e.each((function(n){OE(n,AE(n).option,e,t._lastGraphicModel)})),this._elMap=yt()},e.prototype.dispose=function(){this._clear()},e.type="graphic",e}(wg);function LE(t){var e=_t(DE,t)?DE[t]:Ch(t);var n=new e({});return AE(n).type=t,n}function PE(t,e,n,i){var r=LE(n);return e.add(r),i.set(t,r),AE(r).id=t,AE(r).isNew=!0,r}function OE(t,e,n,i){t&&t.parent&&("group"===t.type&&t.traverse((function(t){OE(t,e,n,i)})),KO(t,e,i),n.removeKey(AE(t).id))}function RE(t,e,n,i){t.isGroup||E([["cursor",wa.prototype.cursor],["zlevel",i||0],["z",n||0],["z2",0]],(function(n){var i=n[0];_t(e,i)?t[i]=rt(e[i],n[1]):null==t[i]&&(t[i]=n[1])})),E(G(e),(function(n){if(0===n.indexOf("on")){var i=e[n];t[n]=U(i)?i:null}})),_t(e,"draggable")&&(t.draggable=e.draggable),null!=e.name&&(t.name=e.name),null!=e.id&&(t.id=e.id)}var NE=["x","y","radius","angle","single"],EE=["cartesian2d","polar","singleAxis"];function zE(t){return t+"Axis"}function VE(t,e){var n,i=yt(),r=[],o=yt();t.eachComponent({mainType:"dataZoom",query:e},(function(t){o.get(t.uid)||s(t)}));do{n=!1,t.eachComponent("dataZoom",a)}while(n);function a(t){!o.get(t.uid)&&function(t){var e=!1;return t.eachTargetAxis((function(t,n){var r=i.get(t);r&&r[n]&&(e=!0)})),e}(t)&&(s(t),n=!0)}function s(t){o.set(t.uid,!0),r.push(t),t.eachTargetAxis((function(t,e){(i.get(t)||i.set(t,[]))[e]=!0}))}return r}function BE(t){var e=t.ecModel,n={infoList:[],infoMap:yt()};return t.eachTargetAxis((function(t,i){var r=e.getComponent(zE(t),i);if(r){var o=r.getCoordSysModel();if(o){var a=o.uid,s=n.infoMap.get(a);s||(s={model:o,axisModels:[]},n.infoList.push(s),n.infoMap.set(a,s)),s.axisModels.push(r)}}})),n}var FE=function(){function t(){this.indexList=[],this.indexMap=[]}return t.prototype.add=function(t){this.indexMap[t]||(this.indexList.push(t),this.indexMap[t]=!0)},t}(),GE=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n._autoThrottle=!0,n._noTarget=!0,n._rangePropMode=["percent","percent"],n}return n(e,t),e.prototype.init=function(t,e,n){var i=WE(t);this.settledOption=i,this.mergeDefaultAndTheme(t,n),this._doInit(i)},e.prototype.mergeOption=function(t){var e=WE(t);C(this.option,t,!0),C(this.settledOption,e,!0),this._doInit(e)},e.prototype._doInit=function(t){var e=this.option;this._setDefaultThrottle(t),this._updateRangeUse(t);var n=this.settledOption;E([["start","startValue"],["end","endValue"]],(function(t,i){"value"===this._rangePropMode[i]&&(e[t[0]]=n[t[0]]=null)}),this),this._resetTarget()},e.prototype._resetTarget=function(){var t=this.get("orient",!0),e=this._targetAxisInfoMap=yt();this._fillSpecifiedTargetAxis(e)?this._orient=t||this._makeAutoOrientByTargetAxis():(this._orient=t||"horizontal",this._fillAutoTargetAxisByOrient(e,this._orient)),this._noTarget=!0,e.each((function(t){t.indexList.length&&(this._noTarget=!1)}),this)},e.prototype._fillSpecifiedTargetAxis=function(t){var e=!1;return E(NE,(function(n){var i=this.getReferringComponents(zE(n),zo);if(i.specified){e=!0;var r=new FE;E(i.models,(function(t){r.add(t.componentIndex)})),t.set(n,r)}}),this),e},e.prototype._fillAutoTargetAxisByOrient=function(t,e){var n=this.ecModel,i=!0;if(i){var r="vertical"===e?"y":"x";o(n.findComponents({mainType:r+"Axis"}),r)}i&&o(n.findComponents({mainType:"singleAxis",filter:function(t){return t.get("orient",!0)===e}}),"single");function o(e,n){var r=e[0];if(r){var o=new FE;if(o.add(r.componentIndex),t.set(n,o),i=!1,"x"===n||"y"===n){var a=r.getReferringComponents("grid",Eo).models[0];a&&E(e,(function(t){r.componentIndex!==t.componentIndex&&a===t.getReferringComponents("grid",Eo).models[0]&&o.add(t.componentIndex)}))}}}i&&E(NE,(function(e){if(i){var r=n.findComponents({mainType:zE(e),filter:function(t){return"category"===t.get("type",!0)}});if(r[0]){var o=new FE;o.add(r[0].componentIndex),t.set(e,o),i=!1}}}),this)},e.prototype._makeAutoOrientByTargetAxis=function(){var t;return this.eachTargetAxis((function(e){!t&&(t=e)}),this),"y"===t?"vertical":"horizontal"},e.prototype._setDefaultThrottle=function(t){if(t.hasOwnProperty("throttle")&&(this._autoThrottle=!1),this._autoThrottle){var e=this.ecModel.option;this.option.throttle=e.animation&&e.animationDurationUpdate>0?100:20}},e.prototype._updateRangeUse=function(t){var e=this._rangePropMode,n=this.get("rangeMode");E([["start","startValue"],["end","endValue"]],(function(i,r){var o=null!=t[i[0]],a=null!=t[i[1]];o&&!a?e[r]="percent":!o&&a?e[r]="value":n?e[r]=n[r]:o&&(e[r]="percent")}))},e.prototype.noTarget=function(){return this._noTarget},e.prototype.getFirstTargetAxisModel=function(){var t;return this.eachTargetAxis((function(e,n){null==t&&(t=this.ecModel.getComponent(zE(e),n))}),this),t},e.prototype.eachTargetAxis=function(t,e){this._targetAxisInfoMap.each((function(n,i){E(n.indexList,(function(n){t.call(e,i,n)}))}))},e.prototype.getAxisProxy=function(t,e){var n=this.getAxisModel(t,e);if(n)return n.__dzAxisProxy},e.prototype.getAxisModel=function(t,e){var n=this._targetAxisInfoMap.get(t);if(n&&n.indexMap[e])return this.ecModel.getComponent(zE(t),e)},e.prototype.setRawRange=function(t){var e=this.option,n=this.settledOption;E([["start","startValue"],["end","endValue"]],(function(i){null==t[i[0]]&&null==t[i[1]]||(e[i[0]]=n[i[0]]=t[i[0]],e[i[1]]=n[i[1]]=t[i[1]])}),this),this._updateRangeUse(t)},e.prototype.setCalculatedRange=function(t){var e=this.option;E(["start","startValue","end","endValue"],(function(n){e[n]=t[n]}))},e.prototype.getPercentRange=function(){var t=this.findRepresentativeAxisProxy();if(t)return t.getDataPercentWindow()},e.prototype.getValueRange=function(t,e){if(null!=t||null!=e)return this.getAxisProxy(t,e).getDataValueWindow();var n=this.findRepresentativeAxisProxy();return n?n.getDataValueWindow():void 0},e.prototype.findRepresentativeAxisProxy=function(t){if(t)return t.__dzAxisProxy;for(var e,n=this._targetAxisInfoMap.keys(),i=0;i=0}(e)){var n=zE(this._dimName),i=e.getReferringComponents(n,Eo).models[0];i&&this._axisIndex===i.componentIndex&&t.push(e)}}),this),t},t.prototype.getAxisModel=function(){return this.ecModel.getComponent(this._dimName+"Axis",this._axisIndex)},t.prototype.getMinMaxSpan=function(){return T(this._minMaxSpan)},t.prototype.calculateDataWindow=function(t){var e,n=this._dataExtent,i=this.getAxisModel().axis.scale,r=this._dataZoomModel.getRangePropMode(),o=[0,100],a=[],s=[];XE(["start","end"],(function(l,u){var h=t[l],c=t[l+"Value"];"percent"===r[u]?(null==h&&(h=o[u]),c=i.parse(Yr(h,o,n))):(e=!0,h=Yr(c=null==c?n[u]:i.parse(c),n,o)),s[u]=null==c||isNaN(c)?n[u]:c,a[u]=null==h||isNaN(h)?o[u]:h})),ZE(s),ZE(a);var l=this._minMaxSpan;function u(t,e,n,r,o){var a=o?"Span":"ValueSpan";xk(0,t,n,"all",l["min"+a],l["max"+a]);for(var s=0;s<2;s++)e[s]=Yr(t[s],n,r,!0),o&&(e[s]=i.parse(e[s]))}return e?u(s,a,n,o,!1):u(a,s,o,n,!0),{valueWindow:s,percentWindow:a}},t.prototype.reset=function(t){if(t===this._dataZoomModel){var e=this.getTargetSeriesModels();this._dataExtent=function(t,e,n){var i=[1/0,-1/0];XE(n,(function(t){!function(t,e,n){e&&E(v_(e,n),(function(n){var i=e.getApproximateExtent(n);i[0]t[1]&&(t[1]=i[1])}))}(i,t.getData(),e)}));var r=t.getAxisModel(),o=s_(r.axis.scale,r,i).calculate();return[o.min,o.max]}(this,this._dimName,e),this._updateMinMaxSpan();var n=this.calculateDataWindow(t.settledOption);this._valueWindow=n.valueWindow,this._percentWindow=n.percentWindow,this._setAxisModel()}},t.prototype.filterData=function(t,e){if(t===this._dataZoomModel){var n=this._dimName,i=this.getTargetSeriesModels(),r=t.get("filterMode"),o=this._valueWindow;"none"!==r&&XE(i,(function(t){var e=t.getData(),i=e.mapDimensionsAll(n);if(i.length){if("weakFilter"===r){var a=e.getStore(),s=z(i,(function(t){return e.getDimensionIndex(t)}),e);e.filterSelf((function(t){for(var e,n,r,l=0;lo[1];if(h&&!c&&!p)return!0;h&&(r=!0),c&&(e=!0),p&&(n=!0)}return r&&e&&n}))}else XE(i,(function(n){if("empty"===r)t.setData(e=e.map(n,(function(t){return function(t){return t>=o[0]&&t<=o[1]}(t)?t:NaN})));else{var i={};i[n]=o,e.selectRange(i)}}));XE(i,(function(t){e.setApproximateExtent(o,t)}))}}))}},t.prototype._updateMinMaxSpan=function(){var t=this._minMaxSpan={},e=this._dataZoomModel,n=this._dataExtent;XE(["min","max"],(function(i){var r=e.get(i+"Span"),o=e.get(i+"ValueSpan");null!=o&&(o=this.getAxisModel().axis.scale.parse(o)),null!=o?r=Yr(n[0]+o,n,[0,100],!0):null!=r&&(o=Yr(r,[0,100],n,!0)-n[0]),t[i+"Span"]=r,t[i+"ValueSpan"]=o}),this)},t.prototype._setAxisModel=function(){var t=this.getAxisModel(),e=this._percentWindow,n=this._valueWindow;if(e){var i=Kr(n,[0,500]);i=Math.min(i,20);var r=t.axis.scale.rawExtentInfo;0!==e[0]&&r.setDeterminedMinMax("min",+n[0].toFixed(i)),100!==e[1]&&r.setDeterminedMinMax("max",+n[1].toFixed(i)),r.freeze()}},t}();var qE={getTargetSeries:function(t){function e(e){t.eachComponent("dataZoom",(function(n){n.eachTargetAxis((function(i,r){var o=t.getComponent(zE(i),r);e(i,r,o,n)}))}))}e((function(t,e,n,i){n.__dzAxisProxy=null}));var n=[];e((function(e,i,r,o){r.__dzAxisProxy||(r.__dzAxisProxy=new jE(e,i,o,t),n.push(r.__dzAxisProxy))}));var i=yt();return E(n,(function(t){E(t.getTargetSeriesModels(),(function(t){i.set(t.uid,t)}))})),i},overallReset:function(t,e){t.eachComponent("dataZoom",(function(t){t.eachTargetAxis((function(e,n){t.getAxisProxy(e,n).reset(t)})),t.eachTargetAxis((function(n,i){t.getAxisProxy(n,i).filterData(t,e)}))})),t.eachComponent("dataZoom",(function(t){var e=t.findRepresentativeAxisProxy();if(e){var n=e.getDataPercentWindow(),i=e.getDataValueWindow();t.setCalculatedRange({start:n[0],end:n[1],startValue:i[0],endValue:i[1]})}}))}};var KE=!1;function $E(t){KE||(KE=!0,t.registerProcessor(t.PRIORITY.PROCESSOR.FILTER,qE),function(t){t.registerAction("dataZoom",(function(t,e){E(VE(e,t),(function(e){e.setRawRange({start:t.start,end:t.end,startValue:t.startValue,endValue:t.endValue})}))}))}(t),t.registerSubTypeDefaulter("dataZoom",(function(){return"slider"})))}function JE(t){t.registerComponentModel(HE),t.registerComponentView(UE),$E(t)}var QE=function(){},tz={};function ez(t,e){tz[t]=e}function nz(t){return tz[t]}var iz=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.optionUpdated=function(){t.prototype.optionUpdated.apply(this,arguments);var e=this.ecModel;E(this.option.feature,(function(t,n){var i=nz(n);i&&(i.getDefaultOption&&(i.defaultOption=i.getDefaultOption(e)),C(t,i.defaultOption))}))},e.type="toolbox",e.layoutMode={type:"box",ignoreSize:!0},e.defaultOption={show:!0,z:6,orient:"horizontal",left:"right",top:"top",backgroundColor:"transparent",borderColor:"#ccc",borderRadius:0,borderWidth:0,padding:5,itemSize:15,itemGap:8,showTitle:!0,iconStyle:{borderColor:"#666",color:"none"},emphasis:{iconStyle:{borderColor:"#3E98C5"}},tooltip:{show:!1,position:"bottom"}},e}(Op);function rz(t,e){var n=dp(e.get("padding")),i=e.getItemStyle(["color","opacity"]);return i.fill=e.get("backgroundColor"),t=new Es({shape:{x:t.x-n[3],y:t.y-n[0],width:t.width+n[1]+n[3],height:t.height+n[0]+n[2],r:e.get("borderRadius")},style:i,silent:!0,z2:-1})}var oz=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.render=function(t,e,n,i){var r=this.group;if(r.removeAll(),t.get("show")){var o=+t.get("itemSize"),a="vertical"===t.get("orient"),s=t.get("feature")||{},l=this._features||(this._features={}),u=[];E(s,(function(t,e){u.push(e)})),new Lm(this._featureNames||[],u).add(h).update(h).remove(H(h,null)).execute(),this._featureNames=u,function(t,e,n){var i=e.getBoxLayoutParams(),r=e.get("padding"),o={width:n.getWidth(),height:n.getHeight()},a=Tp(i,o,r);Ip(e.get("orient"),t,e.get("itemGap"),a.width,a.height),Cp(t,i,o,r)}(r,t,n),r.add(rz(r.getBoundingRect(),t)),a||r.eachChild((function(t){var e=t.__title,i=t.ensureState("emphasis"),a=i.textConfig||(i.textConfig={}),s=t.getTextContent(),l=s&&s.ensureState("emphasis");if(l&&!U(l)&&e){var u=l.style||(l.style={}),h=_r(e,Bs.makeFont(u)),c=t.x+r.x,p=!1;t.y+r.y+o+h.height>n.getHeight()&&(a.position="top",p=!0);var d=p?-5-h.height:o+10;c+h.width/2>n.getWidth()?(a.position=["100%",d],u.align="right"):c-h.width/2<0&&(a.position=[0,d],u.align="left")}}))}function h(h,c){var p,d=u[h],f=u[c],g=s[d],y=new Sc(g,t,t.ecModel);if(i&&null!=i.newTitle&&i.featureName===d&&(g.title=i.newTitle),d&&!f){if(function(t){return 0===t.indexOf("my")}(d))p={onclick:y.option.onclick,featureName:d};else{var v=nz(d);if(!v)return;p=new v}l[d]=p}else if(!(p=l[f]))return;p.uid=Ic("toolbox-feature"),p.model=y,p.ecModel=e,p.api=n;var m=p instanceof QE;d||!f?!y.get("show")||m&&p.unusable?m&&p.remove&&p.remove(e,n):(!function(i,s,l){var u,h,c=i.getModel("iconStyle"),p=i.getModel(["emphasis","iconStyle"]),d=s instanceof QE&&s.getIcons?s.getIcons():i.get("icon"),f=i.get("title")||{};X(d)?(u={})[l]=d:u=d;X(f)?(h={})[l]=f:h=f;var g=i.iconPaths={};E(u,(function(l,u){var d=Wh(l,{},{x:-o/2,y:-o/2,width:o,height:o});d.setStyle(c.getItemStyle()),d.ensureState("emphasis").style=p.getItemStyle();var f=new Bs({style:{text:h[u],align:p.get("textAlign"),borderRadius:p.get("textBorderRadius"),padding:p.get("textPadding"),fill:null},ignore:!0});d.setTextContent(f),Xh({el:d,componentModel:t,itemName:u,formatterParamsExtra:{title:h[u]}}),d.__title=h[u],d.on("mouseover",(function(){var e=p.getItemStyle(),i=a?null==t.get("right")&&"right"!==t.get("left")?"right":"left":null==t.get("bottom")&&"bottom"!==t.get("top")?"bottom":"top";f.setStyle({fill:p.get("textFill")||e.fill||e.stroke||"#000",backgroundColor:p.get("textBackgroundColor")}),d.setTextConfig({position:p.get("textPosition")||i}),f.ignore=!t.get("showTitle"),n.enterEmphasis(this)})).on("mouseout",(function(){"emphasis"!==i.get(["iconStatus",u])&&n.leaveEmphasis(this),f.hide()})),("emphasis"===i.get(["iconStatus",u])?Al:kl)(d),r.add(d),d.on("click",W(s.onclick,s,e,n,u)),g[u]=d}))}(y,p,d),y.setIconStatus=function(t,e){var n=this.option,i=this.iconPaths;n.iconStatus=n.iconStatus||{},n.iconStatus[t]=e,i[t]&&("emphasis"===e?Al:kl)(i[t])},p instanceof QE&&p.render&&p.render(y,e,n,i)):m&&p.dispose&&p.dispose(e,n)}},e.prototype.updateView=function(t,e,n,i){E(this._features,(function(t){t instanceof QE&&t.updateView&&t.updateView(t.model,e,n,i)}))},e.prototype.remove=function(t,e){E(this._features,(function(n){n instanceof QE&&n.remove&&n.remove(t,e)})),this.group.removeAll()},e.prototype.dispose=function(t,e){E(this._features,(function(n){n instanceof QE&&n.dispose&&n.dispose(t,e)}))},e.type="toolbox",e}(wg);var az=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.onclick=function(t,e){var n=this.model,i=n.get("name")||t.get("title.0.text")||"echarts",o="svg"===e.getZr().painter.getType(),a=o?"svg":n.get("type",!0)||"png",s=e.getConnectedDataURL({type:a,backgroundColor:n.get("backgroundColor",!0)||t.get("backgroundColor")||"#fff",connectedBackgroundColor:n.get("connectedBackgroundColor"),excludeComponents:n.get("excludeComponents"),pixelRatio:n.get("pixelRatio")}),l=r.browser;if(U(MouseEvent)&&(l.newEdge||!l.ie&&!l.edge)){var u=document.createElement("a");u.download=i+"."+a,u.target="_blank",u.href=s;var h=new MouseEvent("click",{view:document.defaultView,bubbles:!0,cancelable:!1});u.dispatchEvent(h)}else if(window.navigator.msSaveOrOpenBlob||o){var c=s.split(","),p=c[0].indexOf("base64")>-1,d=o?decodeURIComponent(c[1]):c[1];p&&(d=window.atob(d));var f=i+"."+a;if(window.navigator.msSaveOrOpenBlob){for(var g=d.length,y=new Uint8Array(g);g--;)y[g]=d.charCodeAt(g);var v=new Blob([y]);window.navigator.msSaveOrOpenBlob(v,f)}else{var m=document.createElement("iframe");document.body.appendChild(m);var x=m.contentWindow,_=x.document;_.open("image/svg+xml","replace"),_.write(d),_.close(),x.focus(),_.execCommand("SaveAs",!0,f),document.body.removeChild(m)}}else{var b=n.get("lang"),w='',S=window.open();S.document.write(w),S.document.title=i}},e.getDefaultOption=function(t){return{show:!0,icon:"M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0",title:t.getLocaleModel().get(["toolbox","saveAsImage","title"]),type:"png",connectedBackgroundColor:"#fff",name:"",excludeComponents:["toolbox"],lang:t.getLocaleModel().get(["toolbox","saveAsImage","lang"])}},e}(QE),sz="__ec_magicType_stack__",lz=[["line","bar"],["stack"]],uz=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.getIcons=function(){var t=this.model,e=t.get("icon"),n={};return E(t.get("type"),(function(t){e[t]&&(n[t]=e[t])})),n},e.getDefaultOption=function(t){return{show:!0,type:[],icon:{line:"M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4",bar:"M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7",stack:"M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z"},title:t.getLocaleModel().get(["toolbox","magicType","title"]),option:{},seriesIndex:{}}},e.prototype.onclick=function(t,e,n){var i=this.model,r=i.get(["seriesIndex",n]);if(hz[n]){var o,a={series:[]};E(lz,(function(t){P(t,n)>=0&&E(t,(function(t){i.setIconStatus(t,"normal")}))})),i.setIconStatus(n,"emphasis"),t.eachComponent({mainType:"series",query:null==r?null:{seriesIndex:r}},(function(t){var e=t.subType,r=t.id,o=hz[n](e,r,t,i);o&&(k(o,t.option),a.series.push(o));var s=t.coordinateSystem;if(s&&"cartesian2d"===s.type&&("line"===n||"bar"===n)){var l=s.getAxesByScale("ordinal")[0];if(l){var u=l.dim+"Axis",h=t.getReferringComponents(u,Eo).models[0].componentIndex;a[u]=a[u]||[];for(var c=0;c<=h;c++)a[u][h]=a[u][h]||{};a[u][h].boundaryGap="bar"===n}}}));var s=n;"stack"===n&&(o=C({stack:i.option.title.tiled,tiled:i.option.title.stack},i.option.title),"emphasis"!==i.get(["iconStatus",n])&&(s="tiled")),e.dispatchAction({type:"changeMagicType",currentType:s,newOption:a,newTitle:o,featureName:"magicType"})}},e}(QE),hz={line:function(t,e,n,i){if("bar"===t)return C({id:e,type:"line",data:n.get("data"),stack:n.get("stack"),markPoint:n.get("markPoint"),markLine:n.get("markLine")},i.get(["option","line"])||{},!0)},bar:function(t,e,n,i){if("line"===t)return C({id:e,type:"bar",data:n.get("data"),stack:n.get("stack"),markPoint:n.get("markPoint"),markLine:n.get("markLine")},i.get(["option","bar"])||{},!0)},stack:function(t,e,n,i){var r=n.get("stack")===sz;if("line"===t||"bar"===t)return i.setIconStatus("stack",r?"normal":"emphasis"),C({id:e,stack:r?"":sz},i.get(["option","stack"])||{},!0)}};vm({type:"changeMagicType",event:"magicTypeChanged",update:"prepareAndUpdate"},(function(t,e){e.mergeOption(t.newOption)}));var cz=new Array(60).join("-"),pz="\t";function dz(t){return t.replace(/^\s\s*/,"").replace(/\s\s*$/,"")}var fz=new RegExp("[\t]+","g");function gz(t,e){var n=t.split(new RegExp("\n*"+cz+"\n*","g")),i={series:[]};return E(n,(function(t,n){if(function(t){if(t.slice(0,t.indexOf("\n")).indexOf(pz)>=0)return!0}(t)){var r=function(t){for(var e=t.split(/\n+/g),n=[],i=z(dz(e.shift()).split(fz),(function(t){return{name:t,data:[]}})),r=0;r=0)&&t(r,i._targetInfoList)}))}return t.prototype.setOutputRanges=function(t,e){return this.matchOutputRanges(t,e,(function(t,e,n){if((t.coordRanges||(t.coordRanges=[])).push(e),!t.coordRange){t.coordRange=e;var i=Az[t.brushType](0,n,e);t.__rangeOffset={offset:Lz[t.brushType](i.values,t.range,[1,1]),xyMinMax:i.xyMinMax}}})),t},t.prototype.matchOutputRanges=function(t,e,n){E(t,(function(t){var i=this.findTargetInfo(t,e);i&&!0!==i&&E(i.coordSyses,(function(i){var r=Az[t.brushType](1,i,t.range,!0);n(t,r.values,i,e)}))}),this)},t.prototype.setInputRanges=function(t,e){E(t,(function(t){var n,i,r,o,a,s=this.findTargetInfo(t,e);if(t.range=t.range||[],s&&!0!==s){t.panelId=s.panelId;var l=Az[t.brushType](0,s.coordSys,t.coordRange),u=t.__rangeOffset;t.range=u?Lz[t.brushType](l.values,u.offset,(n=l.xyMinMax,i=u.xyMinMax,r=Oz(n),o=Oz(i),a=[r[0]/o[0],r[1]/o[1]],isNaN(a[0])&&(a[0]=1),isNaN(a[1])&&(a[1]=1),a)):l.values}}),this)},t.prototype.makePanelOpts=function(t,e){return z(this._targetInfoList,(function(n){var i=n.getPanelRect();return{panelId:n.panelId,defaultBrushType:e?e(n):null,clipPath:bL(i),isTargetByCursor:SL(i,t,n.coordSysModel),getLinearBrushOtherExtent:wL(i)}}))},t.prototype.controlSeries=function(t,e,n){var i=this.findTargetInfo(t,n);return!0===i||i&&P(i.coordSyses,e.coordinateSystem)>=0},t.prototype.findTargetInfo=function(t,e){for(var n=this._targetInfoList,i=Iz(e,t),r=0;rt[1]&&t.reverse(),t}function Iz(t,e){return Ro(t,e,{includeMainTypes:wz})}var Tz={grid:function(t,e){var n=t.xAxisModels,i=t.yAxisModels,r=t.gridModels,o=yt(),a={},s={};(n||i||r)&&(E(n,(function(t){var e=t.axis.grid.model;o.set(e.id,e),a[e.id]=!0})),E(i,(function(t){var e=t.axis.grid.model;o.set(e.id,e),s[e.id]=!0})),E(r,(function(t){o.set(t.id,t),a[t.id]=!0,s[t.id]=!0})),o.each((function(t){var r=t.coordinateSystem,o=[];E(r.getCartesians(),(function(t,e){(P(n,t.getAxis("x").model)>=0||P(i,t.getAxis("y").model)>=0)&&o.push(t)})),e.push({panelId:"grid--"+t.id,gridModel:t,coordSysModel:t,coordSys:o[0],coordSyses:o,getPanelRect:Dz.grid,xAxisDeclared:a[t.id],yAxisDeclared:s[t.id]})})))},geo:function(t,e){E(t.geoModels,(function(t){var n=t.coordinateSystem;e.push({panelId:"geo--"+t.id,geoModel:t,coordSysModel:t,coordSys:n,coordSyses:[n],getPanelRect:Dz.geo})}))}},Cz=[function(t,e){var n=t.xAxisModel,i=t.yAxisModel,r=t.gridModel;return!r&&n&&(r=n.axis.grid.model),!r&&i&&(r=i.axis.grid.model),r&&r===e.gridModel},function(t,e){var n=t.geoModel;return n&&n===e.geoModel}],Dz={grid:function(){return this.coordSys.master.getRect().clone()},geo:function(){var t=this.coordSys,e=t.getBoundingRect().clone();return e.applyTransform(Nh(t)),e}},Az={lineX:H(kz,0),lineY:H(kz,1),rect:function(t,e,n,i){var r=t?e.pointToData([n[0][0],n[1][0]],i):e.dataToPoint([n[0][0],n[1][0]],i),o=t?e.pointToData([n[0][1],n[1][1]],i):e.dataToPoint([n[0][1],n[1][1]],i),a=[Mz([r[0],o[0]]),Mz([r[1],o[1]])];return{values:a,xyMinMax:a}},polygon:function(t,e,n,i){var r=[[1/0,-1/0],[1/0,-1/0]];return{values:z(n,(function(n){var o=t?e.pointToData(n,i):e.dataToPoint(n,i);return r[0][0]=Math.min(r[0][0],o[0]),r[1][0]=Math.min(r[1][0],o[1]),r[0][1]=Math.max(r[0][1],o[0]),r[1][1]=Math.max(r[1][1],o[1]),o})),xyMinMax:r}}};function kz(t,e,n,i){var r=n.getAxis(["x","y"][t]),o=Mz(z([0,1],(function(t){return e?r.coordToData(r.toLocalCoord(i[t]),!0):r.toGlobalCoord(r.dataToCoord(i[t]))}))),a=[];return a[t]=o,a[1-t]=[NaN,NaN],{values:o,xyMinMax:a}}var Lz={lineX:H(Pz,0),lineY:H(Pz,1),rect:function(t,e,n){return[[t[0][0]-n[0]*e[0][0],t[0][1]-n[0]*e[0][1]],[t[1][0]-n[1]*e[1][0],t[1][1]-n[1]*e[1][1]]]},polygon:function(t,e,n){return z(t,(function(t,i){return[t[0]-n[0]*e[i][0],t[1]-n[1]*e[i][1]]}))}};function Pz(t,e,n,i){return[e[0]-i[t]*n[0],e[1]-i[t]*n[1]]}function Oz(t){return t?[t[0][1]-t[0][0],t[1][1]-t[1][0]]:[NaN,NaN]}var Rz,Nz,Ez=E,zz=xo+"toolbox-dataZoom_",Vz=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.render=function(t,e,n,i){this._brushController||(this._brushController=new Yk(n.getZr()),this._brushController.on("brush",W(this._onBrush,this)).mount()),function(t,e,n,i,r){var o=n._isZoomActive;i&&"takeGlobalCursor"===i.type&&(o="dataZoomSelect"===i.key&&i.dataZoomSelectActive);n._isZoomActive=o,t.setIconStatus("zoom",o?"emphasis":"normal");var a=new Sz(Fz(t),e,{include:["grid"]}).makePanelOpts(r,(function(t){return t.xAxisDeclared&&!t.yAxisDeclared?"lineX":!t.xAxisDeclared&&t.yAxisDeclared?"lineY":"rect"}));n._brushController.setPanels(a).enableBrush(!(!o||!a.length)&&{brushType:"auto",brushStyle:t.getModel("brushStyle").getItemStyle()})}(t,e,this,i,n),function(t,e){t.setIconStatus("back",function(t){return _z(t).length}(e)>1?"emphasis":"normal")}(t,e)},e.prototype.onclick=function(t,e,n){Bz[n].call(this)},e.prototype.remove=function(t,e){this._brushController&&this._brushController.unmount()},e.prototype.dispose=function(t,e){this._brushController&&this._brushController.dispose()},e.prototype._onBrush=function(t){var e=t.areas;if(t.isEnd&&e.length){var n={},i=this.ecModel;this._brushController.updateCovers([]),new Sz(Fz(this.model),i,{include:["grid"]}).matchOutputRanges(e,i,(function(t,e,n){if("cartesian2d"===n.type){var i=t.brushType;"rect"===i?(r("x",n,e[0]),r("y",n,e[1])):r({lineX:"x",lineY:"y"}[i],n,e)}})),function(t,e){var n=_z(t);mz(e,(function(e,i){for(var r=n.length-1;r>=0&&!n[r][i];r--);if(r<0){var o=t.queryComponents({mainType:"dataZoom",subType:"select",id:i})[0];if(o){var a=o.getPercentRange();n[0][i]={dataZoomId:i,start:a[0],end:a[1]}}}})),n.push(e)}(i,n),this._dispatchZoomAction(n)}function r(t,e,r){var o=e.getAxis(t),a=o.model,s=function(t,e,n){var i;return n.eachComponent({mainType:"dataZoom",subType:"select"},(function(n){n.getAxisModel(t,e.componentIndex)&&(i=n)})),i}(t,a,i),l=s.findRepresentativeAxisProxy(a).getMinMaxSpan();null==l.minValueSpan&&null==l.maxValueSpan||(r=xk(0,r.slice(),o.scale.getExtent(),0,l.minValueSpan,l.maxValueSpan)),s&&(n[s.id]={dataZoomId:s.id,startValue:r[0],endValue:r[1]})}},e.prototype._dispatchZoomAction=function(t){var e=[];Ez(t,(function(t,n){e.push(T(t))})),e.length&&this.api.dispatchAction({type:"dataZoom",from:this.uid,batch:e})},e.getDefaultOption=function(t){return{show:!0,filterMode:"filter",icon:{zoom:"M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1",back:"M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26"},title:t.getLocaleModel().get(["toolbox","dataZoom","title"]),brushStyle:{borderWidth:0,color:"rgba(210,219,238,0.2)"}}},e}(QE),Bz={zoom:function(){var t=!this._isZoomActive;this.api.dispatchAction({type:"takeGlobalCursor",key:"dataZoomSelect",dataZoomSelectActive:t})},back:function(){this._dispatchZoomAction(function(t){var e=_z(t),n=e[e.length-1];e.length>1&&e.pop();var i={};return mz(n,(function(t,n){for(var r=e.length-1;r>=0;r--)if(t=e[r][n]){i[n]=t;break}})),i}(this.ecModel))}};function Fz(t){var e={xAxisIndex:t.get("xAxisIndex",!0),yAxisIndex:t.get("yAxisIndex",!0),xAxisId:t.get("xAxisId",!0),yAxisId:t.get("yAxisId",!0)};return null==e.xAxisIndex&&null==e.xAxisId&&(e.xAxisIndex="all"),null==e.yAxisIndex&&null==e.yAxisId&&(e.yAxisIndex="all"),e}Rz="dataZoom",Nz=function(t){var e=t.getComponent("toolbox",0),n=["feature","dataZoom"];if(e&&null!=e.get(n)){var i=e.getModel(n),r=[],o=Ro(t,Fz(i));return Ez(o.xAxisModels,(function(t){return a(t,"xAxis","xAxisIndex")})),Ez(o.yAxisModels,(function(t){return a(t,"yAxis","yAxisIndex")})),r}function a(t,e,n){var o=t.componentIndex,a={type:"select",$fromToolbox:!0,filterMode:i.get("filterMode",!0)||"filter",id:zz+e+o};a[n]=o,r.push(a)}},lt(null==ed.get(Rz)&&Nz),ed.set(Rz,Nz);var Gz=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="tooltip",e.dependencies=["axisPointer"],e.defaultOption={z:60,show:!0,showContent:!0,trigger:"item",triggerOn:"mousemove|click",alwaysShowContent:!1,displayMode:"single",renderMode:"auto",confine:null,showDelay:0,hideDelay:100,transitionDuration:.4,enterable:!1,backgroundColor:"#fff",shadowBlur:10,shadowColor:"rgba(0, 0, 0, .2)",shadowOffsetX:1,shadowOffsetY:2,borderRadius:4,borderWidth:1,padding:null,extraCssText:"",axisPointer:{type:"line",axis:"auto",animation:"auto",animationDurationUpdate:200,animationEasingUpdate:"exponentialOut",crossStyle:{color:"#999",width:1,type:"dashed",textStyle:{}}},textStyle:{color:"#666",fontSize:14}},e}(Op);function Wz(t){var e=t.get("confine");return null!=e?!!e:"richText"===t.get("renderMode")}function Hz(t){if(r.domSupported)for(var e=document.documentElement.style,n=0,i=t.length;n-1?(u+="top:50%",h+="translateY(-50%) rotate("+(a="left"===s?-225:-45)+"deg)"):(u+="left:50%",h+="translateX(-50%) rotate("+(a="top"===s?225:45)+"deg)");var c=a*Math.PI/180,p=l+r,d=p*Math.abs(Math.cos(c))+p*Math.abs(Math.sin(c)),f=e+" solid "+r+"px;";return'
                '}(n,i,r)),X(t))o.innerHTML=t+a;else if(t){o.innerHTML="",Y(t)||(t=[t]);for(var s=0;s=0?this._tryShow(n,i):"leave"===e&&this._hide(i))}),this))},e.prototype._keepShow=function(){var t=this._tooltipModel,e=this._ecModel,n=this._api,i=t.get("triggerOn");if(null!=this._lastX&&null!=this._lastY&&"none"!==i&&"click"!==i){var r=this;clearTimeout(this._refreshUpdateTimeout),this._refreshUpdateTimeout=setTimeout((function(){!n.isDisposed()&&r.manuallyShowTip(t,e,n,{x:r._lastX,y:r._lastY,dataByCoordSys:r._lastDataByCoordSys})}))}},e.prototype.manuallyShowTip=function(t,e,n,i){if(i.from!==this.uid&&!r.node&&n.getDom()){var o=aV(i,n);this._ticket="";var a=i.dataByCoordSys,s=function(t,e,n){var i=No(t).queryOptionMap,r=i.keys()[0];if(!r||"series"===r)return;var o,a=Vo(e,r,i.get(r),{useDefault:!1,enableAll:!1,enableNone:!1}).models[0];if(!a)return;if(n.getViewOfComponentModel(a).group.traverse((function(e){var n=Js(e).tooltipConfig;if(n&&n.name===t.name)return o=e,!0})),o)return{componentMainType:r,componentIndex:a.componentIndex,el:o}}(i,e,n);if(s){var l=s.el.getBoundingRect().clone();l.applyTransform(s.el.transform),this._tryShow({offsetX:l.x+l.width/2,offsetY:l.y+l.height/2,target:s.el,position:i.position,positionDefault:"bottom"},o)}else if(i.tooltip&&null!=i.x&&null!=i.y){var u=iV;u.x=i.x,u.y=i.y,u.update(),Js(u).tooltipConfig={name:null,option:i.tooltip},this._tryShow({offsetX:i.x,offsetY:i.y,target:u},o)}else if(a)this._tryShow({offsetX:i.x,offsetY:i.y,position:i.position,dataByCoordSys:a,tooltipOption:i.tooltipOption},o);else if(null!=i.seriesIndex){if(this._manuallyAxisShowTip(t,e,n,i))return;var h=dN(i,e),c=h.point[0],p=h.point[1];null!=c&&null!=p&&this._tryShow({offsetX:c,offsetY:p,target:h.el,position:i.position,positionDefault:"bottom"},o)}else null!=i.x&&null!=i.y&&(n.dispatchAction({type:"updateAxisPointer",x:i.x,y:i.y}),this._tryShow({offsetX:i.x,offsetY:i.y,position:i.position,target:n.getZr().findHover(i.x,i.y).target},o))}},e.prototype.manuallyHideTip=function(t,e,n,i){var r=this._tooltipContent;!this._alwaysShowContent&&this._tooltipModel&&r.hideLater(this._tooltipModel.get("hideDelay")),this._lastX=this._lastY=this._lastDataByCoordSys=null,i.from!==this.uid&&this._hide(aV(i,n))},e.prototype._manuallyAxisShowTip=function(t,e,n,i){var r=i.seriesIndex,o=i.dataIndex,a=e.getComponent("axisPointer").coordSysAxesInfo;if(null!=r&&null!=o&&null!=a){var s=e.getSeriesByIndex(r);if(s)if("axis"===oV([s.getData().getItemModel(o),s,(s.coordinateSystem||{}).model],this._tooltipModel).get("trigger"))return n.dispatchAction({type:"updateAxisPointer",seriesIndex:r,dataIndex:o,position:i.position}),!0}},e.prototype._tryShow=function(t,e){var n=t.target;if(this._tooltipModel){this._lastX=t.offsetX,this._lastY=t.offsetY;var i=t.dataByCoordSys;if(i&&i.length)this._showAxisTooltip(i,t);else if(n){var r,o;this._lastDataByCoordSys=null,Ty(n,(function(t){return null!=Js(t).dataIndex?(r=t,!0):null!=Js(t).tooltipConfig?(o=t,!0):void 0}),!0),r?this._showSeriesItemTooltip(t,r,e):o?this._showComponentItemTooltip(t,o,e):this._hide(e)}else this._lastDataByCoordSys=null,this._hide(e)}},e.prototype._showOrMove=function(t,e){var n=t.get("showDelay");e=W(e,this),clearTimeout(this._showTimout),n>0?this._showTimout=setTimeout(e,n):e()},e.prototype._showAxisTooltip=function(t,e){var n=this._ecModel,i=this._tooltipModel,r=[e.offsetX,e.offsetY],o=oV([e.tooltipOption],i),a=this._renderMode,s=[],l=Qf("section",{blocks:[],noHeader:!0}),u=[],h=new hg;E(t,(function(t){E(t.dataByAxis,(function(t){var e=n.getComponent(t.axisDim+"Axis",t.axisIndex),r=t.value;if(e&&null!=r){var o=qR(r,e.axis,n,t.seriesDataIndices,t.valueLabelOpt),c=Qf("section",{header:o,noHeader:!ut(o),sortBlocks:!0,blocks:[]});l.blocks.push(c),E(t.seriesDataIndices,(function(l){var p=n.getSeriesByIndex(l.seriesIndex),d=l.dataIndexInside,f=p.getDataParams(d);if(!(f.dataIndex<0)){f.axisDim=t.axisDim,f.axisIndex=t.axisIndex,f.axisType=t.axisType,f.axisId=t.axisId,f.axisValue=d_(e.axis,{value:r}),f.axisValueLabel=o,f.marker=h.makeTooltipMarker("item",xp(f.color),a);var g=yf(p.formatTooltip(d,!0,null)),y=g.frag;if(y){var v=oV([p],i).get("valueFormatter");c.blocks.push(v?A({valueFormatter:v},y):y)}g.text&&u.push(g.text),s.push(f)}}))}}))})),l.blocks.reverse(),u.reverse();var c=e.position,p=o.get("order"),d=og(l,h,a,p,n.get("useUTC"),o.get("textStyle"));d&&u.unshift(d);var f="richText"===a?"\n\n":"
                ",g=u.join(f);this._showOrMove(o,(function(){this._updateContentNotChangedOnAxis(t,s)?this._updatePosition(o,c,r[0],r[1],this._tooltipContent,s):this._showTooltipContent(o,g,s,Math.random()+"",r[0],r[1],c,null,h)}))},e.prototype._showSeriesItemTooltip=function(t,e,n){var i=this._ecModel,r=Js(e),o=r.seriesIndex,a=i.getSeriesByIndex(o),s=r.dataModel||a,l=r.dataIndex,u=r.dataType,h=s.getData(u),c=this._renderMode,p=t.positionDefault,d=oV([h.getItemModel(l),s,a&&(a.coordinateSystem||{}).model],this._tooltipModel,p?{position:p}:null),f=d.get("trigger");if(null==f||"item"===f){var g=s.getDataParams(l,u),y=new hg;g.marker=y.makeTooltipMarker("item",xp(g.color),c);var v=yf(s.formatTooltip(l,!1,u)),m=d.get("order"),x=d.get("valueFormatter"),_=v.frag,b=_?og(x?A({valueFormatter:x},_):_,y,c,m,i.get("useUTC"),d.get("textStyle")):v.text,w="item_"+s.name+"_"+l;this._showOrMove(d,(function(){this._showTooltipContent(d,b,g,w,t.offsetX,t.offsetY,t.position,t.target,y)})),n({type:"showTip",dataIndexInside:l,dataIndex:h.getRawIndex(l),seriesIndex:o,from:this.uid})}},e.prototype._showComponentItemTooltip=function(t,e,n){var i=Js(e),r=i.tooltipConfig.option||{};if(X(r)){r={content:r,formatter:r}}var o=[r],a=this._ecModel.getComponent(i.componentMainType,i.componentIndex);a&&o.push(a),o.push({formatter:r.content});var s=t.positionDefault,l=oV(o,this._tooltipModel,s?{position:s}:null),u=l.get("content"),h=Math.random()+"",c=new hg;this._showOrMove(l,(function(){var n=T(l.get("formatterParams")||{});this._showTooltipContent(l,u,n,h,t.offsetX,t.offsetY,t.position,e,c)})),n({type:"showTip",from:this.uid})},e.prototype._showTooltipContent=function(t,e,n,i,r,o,a,s,l){if(this._ticket="",t.get("showContent")&&t.get("show")){var u=this._tooltipContent;u.setEnterable(t.get("enterable"));var h=t.get("formatter");a=a||t.get("position");var c=e,p=this._getNearestPoint([r,o],n,t.get("trigger"),t.get("borderColor")).color;if(h)if(X(h)){var d=t.ecModel.get("useUTC"),f=Y(n)?n[0]:n;c=h,f&&f.axisType&&f.axisType.indexOf("time")>=0&&(c=jc(f.axisValue,c,d)),c=vp(c,n,!0)}else if(U(h)){var g=W((function(e,i){e===this._ticket&&(u.setContent(i,l,t,p,a),this._updatePosition(t,a,r,o,u,n,s))}),this);this._ticket=i,c=h(n,i,g)}else c=h;u.setContent(c,l,t,p,a),u.show(t,p),this._updatePosition(t,a,r,o,u,n,s)}},e.prototype._getNearestPoint=function(t,e,n,i){return"axis"===n||Y(e)?{color:i||("html"===this._renderMode?"#fff":"none")}:Y(e)?void 0:{color:i||e.color||e.borderColor}},e.prototype._updatePosition=function(t,e,n,i,r,o,a){var s=this._api.getWidth(),l=this._api.getHeight();e=e||t.get("position");var u=r.getSize(),h=t.get("align"),c=t.get("verticalAlign"),p=a&&a.getBoundingRect().clone();if(a&&p.applyTransform(a.transform),U(e)&&(e=e([n,i],o,r.el,p,{viewSize:[s,l],contentSize:u.slice()})),Y(e))n=Ur(e[0],s),i=Ur(e[1],l);else if(q(e)){var d=e;d.width=u[0],d.height=u[1];var f=Tp(d,{width:s,height:l});n=f.x,i=f.y,h=null,c=null}else if(X(e)&&a){var g=function(t,e,n,i){var r=n[0],o=n[1],a=Math.ceil(Math.SQRT2*i)+8,s=0,l=0,u=e.width,h=e.height;switch(t){case"inside":s=e.x+u/2-r/2,l=e.y+h/2-o/2;break;case"top":s=e.x+u/2-r/2,l=e.y-o-a;break;case"bottom":s=e.x+u/2-r/2,l=e.y+h+a;break;case"left":s=e.x-r-a,l=e.y+h/2-o/2;break;case"right":s=e.x+u+a,l=e.y+h/2-o/2}return[s,l]}(e,p,u,t.get("borderWidth"));n=g[0],i=g[1]}else{g=function(t,e,n,i,r,o,a){var s=n.getSize(),l=s[0],u=s[1];null!=o&&(t+l+o+2>i?t-=l+o:t+=o);null!=a&&(e+u+a>r?e-=u+a:e+=a);return[t,e]}(n,i,r,s,l,h?null:20,c?null:20);n=g[0],i=g[1]}if(h&&(n-=sV(h)?u[0]/2:"right"===h?u[0]:0),c&&(i-=sV(c)?u[1]/2:"bottom"===c?u[1]:0),Wz(t)){g=function(t,e,n,i,r){var o=n.getSize(),a=o[0],s=o[1];return t=Math.min(t+a,i)-a,e=Math.min(e+s,r)-s,t=Math.max(t,0),e=Math.max(e,0),[t,e]}(n,i,r,s,l);n=g[0],i=g[1]}r.moveTo(n,i)},e.prototype._updateContentNotChangedOnAxis=function(t,e){var n=this._lastDataByCoordSys,i=this._cbParamsList,r=!!n&&n.length===t.length;return r&&E(n,(function(n,o){var a=n.dataByAxis||[],s=(t[o]||{}).dataByAxis||[];(r=r&&a.length===s.length)&&E(a,(function(t,n){var o=s[n]||{},a=t.seriesDataIndices||[],l=o.seriesDataIndices||[];(r=r&&t.value===o.value&&t.axisType===o.axisType&&t.axisId===o.axisId&&a.length===l.length)&&E(a,(function(t,e){var n=l[e];r=r&&t.seriesIndex===n.seriesIndex&&t.dataIndex===n.dataIndex})),i&&E(t.seriesDataIndices,(function(t){var n=t.seriesIndex,o=e[n],a=i[n];o&&a&&a.data!==o.data&&(r=!1)}))}))})),this._lastDataByCoordSys=t,this._cbParamsList=e,!!r},e.prototype._hide=function(t){this._lastDataByCoordSys=null,t({type:"hideTip",from:this.uid})},e.prototype.dispose=function(t,e){!r.node&&e.getDom()&&(zg(this,"_updatePosition"),this._tooltipContent.dispose(),cN("itemTooltip",e))},e.type="tooltip",e}(wg);function oV(t,e,n){var i,r=e.ecModel;n?(i=new Sc(n,r,r),i=new Sc(e.option,i,r)):i=e;for(var o=t.length-1;o>=0;o--){var a=t[o];a&&(a instanceof Sc&&(a=a.get("tooltip",!0)),X(a)&&(a={formatter:a}),a&&(i=new Sc(a,i,r)))}return i}function aV(t,e){return t.dispatchAction||W(e.dispatchAction,e)}function sV(t){return"center"===t||"middle"===t}var lV=["rect","polygon","keep","clear"];function uV(t,e){var n=_o(t?t.brush:[]);if(n.length){var i=[];E(n,(function(t){var e=t.hasOwnProperty("toolbox")?t.toolbox:[];e instanceof Array&&(i=i.concat(e))}));var r=t&&t.toolbox;Y(r)&&(r=r[0]),r||(r={feature:{}},t.toolbox=[r]);var o=r.feature||(r.feature={}),a=o.brush||(o.brush={}),s=a.type||(a.type=[]);s.push.apply(s,i),function(t){var e={};E(t,(function(t){e[t]=1})),t.length=0,E(e,(function(e,n){t.push(n)}))}(s),e&&!s.length&&s.push.apply(s,lV)}}var hV=E;function cV(t){if(t)for(var e in t)if(t.hasOwnProperty(e))return!0}function pV(t,e,n){var i={};return hV(e,(function(e){var r,o=i[e]=((r=function(){}).prototype.__hidden=r.prototype,new r);hV(t[e],(function(t,i){if(dD.isValidType(i)){var r={type:i,visual:t};n&&n(r,e),o[i]=new dD(r),"opacity"===i&&((r=T(r)).type="colorAlpha",o.__hidden.__alphaForOpacity=new dD(r))}}))})),i}function dV(t,e,n){var i;E(n,(function(t){e.hasOwnProperty(t)&&cV(e[t])&&(i=!0)})),i&&E(n,(function(n){e.hasOwnProperty(n)&&cV(e[n])?t[n]=T(e[n]):delete t[n]}))}var fV={lineX:gV(0),lineY:gV(1),rect:{point:function(t,e,n){return t&&n.boundingRect.contain(t[0],t[1])},rect:function(t,e,n){return t&&n.boundingRect.intersect(t)}},polygon:{point:function(t,e,n){return t&&n.boundingRect.contain(t[0],t[1])&&w_(n.range,t[0],t[1])},rect:function(t,e,n){var i=n.range;if(!t||i.length<=1)return!1;var r=t.x,o=t.y,a=t.width,s=t.height,l=i[0];return!!(w_(i,r,o)||w_(i,r+a,o)||w_(i,r,o+s)||w_(i,r+a,o+s)||Ee.create(t).contain(l[0],l[1])||Hh(r,o,r+a,o,i)||Hh(r,o,r,o+s,i)||Hh(r+a,o,r+a,o+s,i)||Hh(r,o+s,r+a,o+s,i))||void 0}}};function gV(t){var e=["x","y"],n=["width","height"];return{point:function(e,n,i){if(e){var r=i.range;return yV(e[t],r)}},rect:function(i,r,o){if(i){var a=o.range,s=[i[e[t]],i[e[t]]+i[n[t]]];return s[1]e[0][1]&&(e[0][1]=o[0]),o[1]e[1][1]&&(e[1][1]=o[1])}return e&&IV(e)}};function IV(t){return new Ee(t[0][0],t[1][0],t[0][1]-t[0][0],t[1][1]-t[1][0])}var TV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.init=function(t,e){this.ecModel=t,this.api=e,this.model,(this._brushController=new Yk(e.getZr())).on("brush",W(this._onBrush,this)).mount()},e.prototype.render=function(t,e,n,i){this.model=t,this._updateController(t,e,n,i)},e.prototype.updateTransform=function(t,e,n,i){_V(e),this._updateController(t,e,n,i)},e.prototype.updateVisual=function(t,e,n,i){this.updateTransform(t,e,n,i)},e.prototype.updateView=function(t,e,n,i){this._updateController(t,e,n,i)},e.prototype._updateController=function(t,e,n,i){(!i||i.$from!==t.id)&&this._brushController.setPanels(t.brushTargetManager.makePanelOpts(n)).enableBrush(t.brushOption).updateCovers(t.areas.slice())},e.prototype.dispose=function(){this._brushController.dispose()},e.prototype._onBrush=function(t){var e=this.model.id,n=this.model.brushTargetManager.setOutputRanges(t.areas,this.ecModel);(!t.isEnd||t.removeOnClick)&&this.api.dispatchAction({type:"brush",brushId:e,areas:T(n),$from:e}),t.isEnd&&this.api.dispatchAction({type:"brushEnd",brushId:e,areas:T(n),$from:e})},e.type="brush",e}(wg),CV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.areas=[],n.brushOption={},n}return n(e,t),e.prototype.optionUpdated=function(t,e){var n=this.option;!e&&dV(n,t,["inBrush","outOfBrush"]);var i=n.inBrush=n.inBrush||{};n.outOfBrush=n.outOfBrush||{color:"#ddd"},i.hasOwnProperty("liftZ")||(i.liftZ=5)},e.prototype.setAreas=function(t){t&&(this.areas=z(t,(function(t){return DV(this.option,t)}),this))},e.prototype.setBrushOption=function(t){this.brushOption=DV(this.option,t),this.brushType=this.brushOption.brushType},e.type="brush",e.dependencies=["geo","grid","xAxis","yAxis","parallel","series"],e.defaultOption={seriesIndex:"all",brushType:"rect",brushMode:"single",transformable:!0,brushStyle:{borderWidth:1,color:"rgba(210,219,238,0.3)",borderColor:"#D2DBEE"},throttleType:"fixRate",throttleDelay:0,removeOnClick:!0,z:1e4},e}(Op);function DV(t,e){return C({brushType:t.brushType,brushMode:t.brushMode,transformable:t.transformable,brushStyle:new Sc(t.brushStyle).getItemStyle(),removeOnClick:t.removeOnClick,z:t.z},e,!0)}var AV=["rect","polygon","lineX","lineY","keep","clear"],kV=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.render=function(t,e,n){var i,r,o;e.eachComponent({mainType:"brush"},(function(t){i=t.brushType,r=t.brushOption.brushMode||"single",o=o||!!t.areas.length})),this._brushType=i,this._brushMode=r,E(t.get("type",!0),(function(e){t.setIconStatus(e,("keep"===e?"multiple"===r:"clear"===e?o:e===i)?"emphasis":"normal")}))},e.prototype.updateView=function(t,e,n){this.render(t,e,n)},e.prototype.getIcons=function(){var t=this.model,e=t.get("icon",!0),n={};return E(t.get("type",!0),(function(t){e[t]&&(n[t]=e[t])})),n},e.prototype.onclick=function(t,e,n){var i=this._brushType,r=this._brushMode;"clear"===n?(e.dispatchAction({type:"axisAreaSelect",intervals:[]}),e.dispatchAction({type:"brush",command:"clear",areas:[]})):e.dispatchAction({type:"takeGlobalCursor",key:"brush",brushOption:{brushType:"keep"===n?i:i!==n&&n,brushMode:"keep"===n?"multiple"===r?"single":"multiple":r}})},e.getDefaultOption=function(t){return{show:!0,type:AV.slice(),icon:{rect:"M7.3,34.7 M0.4,10V-0.2h9.8 M89.6,10V-0.2h-9.8 M0.4,60v10.2h9.8 M89.6,60v10.2h-9.8 M12.3,22.4V10.5h13.1 M33.6,10.5h7.8 M49.1,10.5h7.8 M77.5,22.4V10.5h-13 M12.3,31.1v8.2 M77.7,31.1v8.2 M12.3,47.6v11.9h13.1 M33.6,59.5h7.6 M49.1,59.5 h7.7 M77.5,47.6v11.9h-13",polygon:"M55.2,34.9c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1 s-3.1-1.4-3.1-3.1S53.5,34.9,55.2,34.9z M50.4,51c1.7,0,3.1,1.4,3.1,3.1c0,1.7-1.4,3.1-3.1,3.1c-1.7,0-3.1-1.4-3.1-3.1 C47.3,52.4,48.7,51,50.4,51z M55.6,37.1l1.5-7.8 M60.1,13.5l1.6-8.7l-7.8,4 M59,19l-1,5.3 M24,16.1l6.4,4.9l6.4-3.3 M48.5,11.6 l-5.9,3.1 M19.1,12.8L9.7,5.1l1.1,7.7 M13.4,29.8l1,7.3l6.6,1.6 M11.6,18.4l1,6.1 M32.8,41.9 M26.6,40.4 M27.3,40.2l6.1,1.6 M49.9,52.1l-5.6-7.6l-4.9-1.2",lineX:"M15.2,30 M19.7,15.6V1.9H29 M34.8,1.9H40.4 M55.3,15.6V1.9H45.9 M19.7,44.4V58.1H29 M34.8,58.1H40.4 M55.3,44.4 V58.1H45.9 M12.5,20.3l-9.4,9.6l9.6,9.8 M3.1,29.9h16.5 M62.5,20.3l9.4,9.6L62.3,39.7 M71.9,29.9H55.4",lineY:"M38.8,7.7 M52.7,12h13.2v9 M65.9,26.6V32 M52.7,46.3h13.2v-9 M24.9,12H11.8v9 M11.8,26.6V32 M24.9,46.3H11.8v-9 M48.2,5.1l-9.3-9l-9.4,9.2 M38.9-3.9V12 M48.2,53.3l-9.3,9l-9.4-9.2 M38.9,62.3V46.4",keep:"M4,10.5V1h10.3 M20.7,1h6.1 M33,1h6.1 M55.4,10.5V1H45.2 M4,17.3v6.6 M55.6,17.3v6.6 M4,30.5V40h10.3 M20.7,40 h6.1 M33,40h6.1 M55.4,30.5V40H45.2 M21,18.9h62.9v48.6H21V18.9z",clear:"M22,14.7l30.9,31 M52.9,14.7L22,45.7 M4.7,16.8V4.2h13.1 M26,4.2h7.8 M41.6,4.2h7.8 M70.3,16.8V4.2H57.2 M4.7,25.9v8.6 M70.3,25.9v8.6 M4.7,43.2v12.6h13.1 M26,55.8h7.8 M41.6,55.8h7.8 M70.3,43.2v12.6H57.2"},title:t.getLocaleModel().get(["toolbox","brush","title"])}},e}(QE);var LV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.layoutMode={type:"box",ignoreSize:!0},n}return n(e,t),e.type="title",e.defaultOption={z:6,show:!0,text:"",target:"blank",subtext:"",subtarget:"blank",left:0,top:0,backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderWidth:0,padding:5,itemGap:10,textStyle:{fontSize:18,fontWeight:"bold",color:"#464646"},subtextStyle:{fontSize:12,color:"#6E7079"}},e}(Op),PV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.render=function(t,e,n){if(this.group.removeAll(),t.get("show")){var i=this.group,r=t.getModel("textStyle"),o=t.getModel("subtextStyle"),a=t.get("textAlign"),s=rt(t.get("textBaseline"),t.get("textVerticalAlign")),l=new Bs({style:ec(r,{text:t.get("text"),fill:r.getTextColor()},{disableBox:!0}),z2:10}),u=l.getBoundingRect(),h=t.get("subtext"),c=new Bs({style:ec(o,{text:h,fill:o.getTextColor(),y:u.height+t.get("itemGap"),verticalAlign:"top"},{disableBox:!0}),z2:10}),p=t.get("link"),d=t.get("sublink"),f=t.get("triggerEvent",!0);l.silent=!p&&!f,c.silent=!d&&!f,p&&l.on("click",(function(){_p(p,"_"+t.get("target"))})),d&&c.on("click",(function(){_p(d,"_"+t.get("subtarget"))})),Js(l).eventData=Js(c).eventData=f?{componentType:"title",componentIndex:t.componentIndex}:null,i.add(l),h&&i.add(c);var g=i.getBoundingRect(),y=t.getBoxLayoutParams();y.width=g.width,y.height=g.height;var v=Tp(y,{width:n.getWidth(),height:n.getHeight()},t.get("padding"));a||("middle"===(a=t.get("left")||t.get("right"))&&(a="center"),"right"===a?v.x+=v.width:"center"===a&&(v.x+=v.width/2)),s||("center"===(s=t.get("top")||t.get("bottom"))&&(s="middle"),"bottom"===s?v.y+=v.height:"middle"===s&&(v.y+=v.height/2),s=s||"top"),i.x=v.x,i.y=v.y,i.markRedraw();var m={align:a,verticalAlign:s};l.setStyle(m),c.setStyle(m),g=i.getBoundingRect();var x=v.margin,_=t.getItemStyle(["color","opacity"]);_.fill=t.get("backgroundColor");var b=new Es({shape:{x:g.x-x[3],y:g.y-x[0],width:g.width+x[1]+x[3],height:g.height+x[0]+x[2],r:t.get("borderRadius")},style:_,subPixelOptimize:!0,silent:!0});i.add(b)}},e.type="title",e}(wg);var OV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.layoutMode="box",n}return n(e,t),e.prototype.init=function(t,e,n){this.mergeDefaultAndTheme(t,n),this._initData()},e.prototype.mergeOption=function(e){t.prototype.mergeOption.apply(this,arguments),this._initData()},e.prototype.setCurrentIndex=function(t){null==t&&(t=this.option.currentIndex);var e=this._data.count();this.option.loop?t=(t%e+e)%e:(t>=e&&(t=e-1),t<0&&(t=0)),this.option.currentIndex=t},e.prototype.getCurrentIndex=function(){return this.option.currentIndex},e.prototype.isIndexMax=function(){return this.getCurrentIndex()>=this._data.count()-1},e.prototype.setPlayState=function(t){this.option.autoPlay=!!t},e.prototype.getPlayState=function(){return!!this.option.autoPlay},e.prototype._initData=function(){var t,e=this.option,n=e.data||[],i=e.axisType,r=this._names=[];"category"===i?(t=[],E(n,(function(e,n){var i,o=Do(So(e),"");q(e)?(i=T(e)).value=n:i=n,t.push(i),r.push(o)}))):t=n;var o={category:"ordinal",time:"time",value:"number"}[i]||"number";(this._data=new ex([{name:"value",type:o}],this)).initData(t,r)},e.prototype.getData=function(){return this._data},e.prototype.getCategories=function(){if("category"===this.get("axisType"))return this._names.slice()},e.type="timeline",e.defaultOption={z:4,show:!0,axisType:"time",realtime:!0,left:"20%",top:null,right:"20%",bottom:0,width:null,height:40,padding:5,controlPosition:"left",autoPlay:!1,rewind:!1,loop:!0,playInterval:2e3,currentIndex:0,itemStyle:{},label:{color:"#000"},data:[]},e}(Op),RV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="timeline.slider",e.defaultOption=Tc(OV.defaultOption,{backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderWidth:0,orient:"horizontal",inverse:!1,tooltip:{trigger:"item"},symbol:"circle",symbolSize:12,lineStyle:{show:!0,width:2,color:"#DAE1F5"},label:{position:"auto",show:!0,interval:"auto",rotate:0,color:"#A4B1D7"},itemStyle:{color:"#A4B1D7",borderWidth:1},checkpointStyle:{symbol:"circle",symbolSize:15,color:"#316bf3",borderColor:"#fff",borderWidth:2,shadowBlur:2,shadowOffsetX:1,shadowOffsetY:1,shadowColor:"rgba(0, 0, 0, 0.3)",animation:!0,animationDuration:300,animationEasing:"quinticInOut"},controlStyle:{show:!0,showPlayBtn:!0,showPrevBtn:!0,showNextBtn:!0,itemSize:24,itemGap:12,position:"left",playIcon:"path://M31.6,53C17.5,53,6,41.5,6,27.4S17.5,1.8,31.6,1.8C45.7,1.8,57.2,13.3,57.2,27.4S45.7,53,31.6,53z M31.6,3.3 C18.4,3.3,7.5,14.1,7.5,27.4c0,13.3,10.8,24.1,24.1,24.1C44.9,51.5,55.7,40.7,55.7,27.4C55.7,14.1,44.9,3.3,31.6,3.3z M24.9,21.3 c0-2.2,1.6-3.1,3.5-2l10.5,6.1c1.899,1.1,1.899,2.9,0,4l-10.5,6.1c-1.9,1.1-3.5,0.2-3.5-2V21.3z",stopIcon:"path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z",nextIcon:"M2,18.5A1.52,1.52,0,0,1,.92,18a1.49,1.49,0,0,1,0-2.12L7.81,9.36,1,3.11A1.5,1.5,0,1,1,3,.89l8,7.34a1.48,1.48,0,0,1,.49,1.09,1.51,1.51,0,0,1-.46,1.1L3,18.08A1.5,1.5,0,0,1,2,18.5Z",prevIcon:"M10,.5A1.52,1.52,0,0,1,11.08,1a1.49,1.49,0,0,1,0,2.12L4.19,9.64,11,15.89a1.5,1.5,0,1,1-2,2.22L1,10.77A1.48,1.48,0,0,1,.5,9.68,1.51,1.51,0,0,1,1,8.58L9,.92A1.5,1.5,0,0,1,10,.5Z",prevBtnSize:18,nextBtnSize:18,color:"#A4B1D7",borderColor:"#A4B1D7",borderWidth:1},emphasis:{label:{show:!0,color:"#6f778d"},itemStyle:{color:"#316BF3"},controlStyle:{color:"#316BF3",borderColor:"#316BF3",borderWidth:2}},progress:{lineStyle:{color:"#316BF3"},itemStyle:{color:"#316BF3"},label:{color:"#6f778d"}},data:[]}),e}(OV);R(RV,gf.prototype);var NV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="timeline",e}(wg),EV=function(t){function e(e,n,i,r){var o=t.call(this,e,n,i)||this;return o.type=r||"value",o}return n(e,t),e.prototype.getLabelModel=function(){return this.model.getModel("label")},e.prototype.isHorizontal=function(){return"horizontal"===this.model.get("orient")},e}(q_),zV=Math.PI,VV=Po(),BV=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.init=function(t,e){this.api=e},e.prototype.render=function(t,e,n){if(this.model=t,this.api=n,this.ecModel=e,this.group.removeAll(),t.get("show",!0)){var i=this._layout(t,n),r=this._createGroup("_mainGroup"),o=this._createGroup("_labelGroup"),a=this._axis=this._createAxis(i,t);t.formatTooltip=function(t){return Qf("nameValue",{noName:!0,value:a.scale.getLabel({value:t})})},E(["AxisLine","AxisTick","Control","CurrentPointer"],(function(e){this["_render"+e](i,r,a,t)}),this),this._renderAxisLabel(i,o,a,t),this._position(i,t)}this._doPlayStop(),this._updateTicksStatus()},e.prototype.remove=function(){this._clearTimer(),this.group.removeAll()},e.prototype.dispose=function(){this._clearTimer()},e.prototype._layout=function(t,e){var n,i,r,o,a=t.get(["label","position"]),s=t.get("orient"),l=function(t,e){return Tp(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()},t.get("padding"))}(t,e),u={horizontal:"center",vertical:(n=null==a||"auto"===a?"horizontal"===s?l.y+l.height/2=0||"+"===n?"left":"right"},h={horizontal:n>=0||"+"===n?"top":"bottom",vertical:"middle"},c={horizontal:0,vertical:zV/2},p="vertical"===s?l.height:l.width,d=t.getModel("controlStyle"),f=d.get("show",!0),g=f?d.get("itemSize"):0,y=f?d.get("itemGap"):0,v=g+y,m=t.get(["label","rotate"])||0;m=m*zV/180;var x=d.get("position",!0),_=f&&d.get("showPlayBtn",!0),b=f&&d.get("showPrevBtn",!0),w=f&&d.get("showNextBtn",!0),S=0,M=p;"left"===x||"bottom"===x?(_&&(i=[0,0],S+=v),b&&(r=[S,0],S+=v),w&&(o=[M-g,0],M-=v)):(_&&(i=[M-g,0],M-=v),b&&(r=[0,0],S+=v),w&&(o=[M-g,0],M-=v));var I=[S,M];return t.get("inverse")&&I.reverse(),{viewRect:l,mainLength:p,orient:s,rotation:c[s],labelRotation:m,labelPosOpt:n,labelAlign:t.get(["label","align"])||u[s],labelBaseline:t.get(["label","verticalAlign"])||t.get(["label","baseline"])||h[s],playPosition:i,prevBtnPosition:r,nextBtnPosition:o,axisExtent:I,controlSize:g,controlGap:y}},e.prototype._position=function(t,e){var n=this._mainGroup,i=this._labelGroup,r=t.viewRect;if("vertical"===t.orient){var o=[1,0,0,1,0,0],a=r.x,s=r.y+r.height;be(o,o,[-a,-s]),we(o,o,-zV/2),be(o,o,[a,s]),(r=r.clone()).applyTransform(o)}var l=y(r),u=y(n.getBoundingRect()),h=y(i.getBoundingRect()),c=[n.x,n.y],p=[i.x,i.y];p[0]=c[0]=l[0][0];var d,f=t.labelPosOpt;null==f||X(f)?(v(c,u,l,1,d="+"===f?0:1),v(p,h,l,1,1-d)):(v(c,u,l,1,d=f>=0?0:1),p[1]=c[1]+f);function g(t){t.originX=l[0][0]-t.x,t.originY=l[1][0]-t.y}function y(t){return[[t.x,t.x+t.width],[t.y,t.y+t.height]]}function v(t,e,n,i,r){t[i]+=n[i][r]-e[i][r]}n.setPosition(c),i.setPosition(p),n.rotation=i.rotation=t.rotation,g(n),g(i)},e.prototype._createAxis=function(t,e){var n=e.getData(),i=e.get("axisType"),r=function(t,e){if(e=e||t.get("type"))switch(e){case"category":return new Mx({ordinalMeta:t.getCategories(),extent:[1/0,-1/0]});case"time":return new Fx({locale:t.ecModel.getLocaleModel(),useUTC:t.ecModel.get("useUTC")});default:return new Tx}}(e,i);r.getTicks=function(){return n.mapArray(["value"],(function(t){return{value:t}}))};var o=n.getDataExtent("value");r.setExtent(o[0],o[1]),r.calcNiceTicks();var a=new EV("value",r,t.axisExtent,i);return a.model=e,a},e.prototype._createGroup=function(t){var e=this[t]=new Er;return this.group.add(e),e},e.prototype._renderAxisLine=function(t,e,n,i){var r=n.getExtent();if(i.get(["lineStyle","show"])){var o=new Xu({shape:{x1:r[0],y1:0,x2:r[1],y2:0},style:A({lineCap:"round"},i.getModel("lineStyle").getLineStyle()),silent:!0,z2:1});e.add(o);var a=this._progressLine=new Xu({shape:{x1:r[0],x2:this._currentPointer?this._currentPointer.x:r[0],y1:0,y2:0},style:k({lineCap:"round",lineWidth:o.style.lineWidth},i.getModel(["progress","lineStyle"]).getLineStyle()),silent:!0,z2:1});e.add(a)}},e.prototype._renderAxisTick=function(t,e,n,i){var r=this,o=i.getData(),a=n.scale.getTicks();this._tickSymbols=[],E(a,(function(t){var a=n.dataToCoord(t.value),s=o.getItemModel(t.value),l=s.getModel("itemStyle"),u=s.getModel(["emphasis","itemStyle"]),h=s.getModel(["progress","itemStyle"]),c={x:a,y:0,onclick:W(r._changeTimeline,r,t.value)},p=FV(s,l,e,c);p.ensureState("emphasis").style=u.getItemStyle(),p.ensureState("progress").style=h.getItemStyle(),Wl(p);var d=Js(p);s.get("tooltip")?(d.dataIndex=t.value,d.dataModel=i):d.dataIndex=d.dataModel=null,r._tickSymbols.push(p)}))},e.prototype._renderAxisLabel=function(t,e,n,i){var r=this;if(n.getLabelModel().get("show")){var o=i.getData(),a=n.getViewLabels();this._tickLabels=[],E(a,(function(i){var a=i.tickValue,s=o.getItemModel(a),l=s.getModel("label"),u=s.getModel(["emphasis","label"]),h=s.getModel(["progress","label"]),c=n.dataToCoord(i.tickValue),p=new Bs({x:c,y:0,rotation:t.labelRotation-t.rotation,onclick:W(r._changeTimeline,r,a),silent:!1,style:ec(l,{text:i.formattedLabel,align:t.labelAlign,verticalAlign:t.labelBaseline})});p.ensureState("emphasis").style=ec(u),p.ensureState("progress").style=ec(h),e.add(p),Wl(p),VV(p).dataIndex=a,r._tickLabels.push(p)}))}},e.prototype._renderControl=function(t,e,n,i){var r=t.controlSize,o=t.rotation,a=i.getModel("controlStyle").getItemStyle(),s=i.getModel(["emphasis","controlStyle"]).getItemStyle(),l=i.getPlayState(),u=i.get("inverse",!0);function h(t,n,l,u){if(t){var h=Mr(rt(i.get(["controlStyle",n+"BtnSize"]),r),r),c=function(t,e,n,i){var r=i.style,o=Wh(t.get(["controlStyle",e]),i||{},new Ee(n[0],n[1],n[2],n[3]));r&&o.setStyle(r);return o}(i,n+"Icon",[0,-h/2,h,h],{x:t[0],y:t[1],originX:r/2,originY:0,rotation:u?-o:0,rectHover:!0,style:a,onclick:l});c.ensureState("emphasis").style=s,e.add(c),Wl(c)}}h(t.nextBtnPosition,"next",W(this._changeTimeline,this,u?"-":"+")),h(t.prevBtnPosition,"prev",W(this._changeTimeline,this,u?"+":"-")),h(t.playPosition,l?"stop":"play",W(this._handlePlayClick,this,!l),!0)},e.prototype._renderCurrentPointer=function(t,e,n,i){var r=i.getData(),o=i.getCurrentIndex(),a=r.getItemModel(o).getModel("checkpointStyle"),s=this,l={onCreate:function(t){t.draggable=!0,t.drift=W(s._handlePointerDrag,s),t.ondragend=W(s._handlePointerDragend,s),GV(t,s._progressLine,o,n,i,!0)},onUpdate:function(t){GV(t,s._progressLine,o,n,i)}};this._currentPointer=FV(a,a,this._mainGroup,{},this._currentPointer,l)},e.prototype._handlePlayClick=function(t){this._clearTimer(),this.api.dispatchAction({type:"timelinePlayChange",playState:t,from:this.uid})},e.prototype._handlePointerDrag=function(t,e,n){this._clearTimer(),this._pointerChangeTimeline([n.offsetX,n.offsetY])},e.prototype._handlePointerDragend=function(t){this._pointerChangeTimeline([t.offsetX,t.offsetY],!0)},e.prototype._pointerChangeTimeline=function(t,e){var n=this._toAxisCoord(t)[0],i=Zr(this._axis.getExtent().slice());n>i[1]&&(n=i[1]),n=0&&(a[o]=+a[o].toFixed(c)),[a,h]}var JV={min:H($V,"min"),max:H($V,"max"),average:H($V,"average"),median:H($V,"median")};function QV(t,e){if(e){var n=t.getData(),i=t.coordinateSystem,r=i.dimensions;if(!function(t){return!isNaN(parseFloat(t.x))&&!isNaN(parseFloat(t.y))}(e)&&!Y(e.coord)&&i){var o=tB(e,n,i,t);if((e=T(e)).type&&JV[e.type]&&o.baseAxis&&o.valueAxis){var a=P(r,o.baseAxis.dim),s=P(r,o.valueAxis.dim),l=JV[e.type](n,o.baseDataDim,o.valueDataDim,a,s);e.coord=l[0],e.value=l[1]}else e.coord=[null!=e.xAxis?e.xAxis:e.radiusAxis,null!=e.yAxis?e.yAxis:e.angleAxis]}if(null==e.coord)e.coord=[];else for(var u=e.coord,h=0;h<2;h++)JV[u[h]]&&(u[h]=iB(n,n.mapDimension(r[h]),u[h]));return e}}function tB(t,e,n,i){var r={};return null!=t.valueIndex||null!=t.valueDim?(r.valueDataDim=null!=t.valueIndex?e.getDimension(t.valueIndex):t.valueDim,r.valueAxis=n.getAxis(function(t,e){var n=t.getData().getDimensionInfo(e);return n&&n.coordDim}(i,r.valueDataDim)),r.baseAxis=n.getOtherAxis(r.valueAxis),r.baseDataDim=e.mapDimension(r.baseAxis.dim)):(r.baseAxis=i.getBaseAxis(),r.valueAxis=n.getOtherAxis(r.baseAxis),r.baseDataDim=e.mapDimension(r.baseAxis.dim),r.valueDataDim=e.mapDimension(r.valueAxis.dim)),r}function eB(t,e){return!(t&&t.containData&&e.coord&&!KV(e))||t.containData(e.coord)}function nB(t,e){return t?function(t,n,i,r){return _f(r<2?t.coord&&t.coord[r]:t.value,e[r])}:function(t,n,i,r){return _f(t.value,e[r])}}function iB(t,e,n){if("average"===n){var i=0,r=0;return t.each(e,(function(t,e){isNaN(t)||(i+=t,r++)})),i/r}return"median"===n?t.getMedian(e):t.getDataExtent(e)["max"===n?1:0]}var rB=Po(),oB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.init=function(){this.markerGroupMap=yt()},e.prototype.render=function(t,e,n){var i=this,r=this.markerGroupMap;r.each((function(t){rB(t).keep=!1})),e.eachSeries((function(t){var r=jV.getMarkerModelFromSeries(t,i.type);r&&i.renderSeries(t,r,e,n)})),r.each((function(t){!rB(t).keep&&i.group.remove(t.group)}))},e.prototype.markKeep=function(t){rB(t).keep=!0},e.prototype.toggleBlurSeries=function(t,e){var n=this;E(t,(function(t){var i=jV.getMarkerModelFromSeries(t,n.type);i&&i.getData().eachItemGraphicEl((function(t){t&&(e?Ll(t):Pl(t))}))}))},e.type="marker",e}(wg);function aB(t,e,n){var i=e.coordinateSystem;t.each((function(r){var o,a=t.getItemModel(r),s=Ur(a.get("x"),n.getWidth()),l=Ur(a.get("y"),n.getHeight());if(isNaN(s)||isNaN(l)){if(e.getMarkerPosition)o=e.getMarkerPosition(t.getValues(t.dimensions,r));else if(i){var u=t.get(i.dimensions[0],r),h=t.get(i.dimensions[1],r);o=i.dataToPoint([u,h])}}else o=[s,l];isNaN(s)||(o[0]=s),isNaN(l)||(o[1]=l),t.setItemLayout(r,o)}))}var sB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.updateTransform=function(t,e,n){e.eachSeries((function(t){var e=jV.getMarkerModelFromSeries(t,"markPoint");e&&(aB(e.getData(),t,n),this.markerGroupMap.get(t.id).updateLayout())}),this)},e.prototype.renderSeries=function(t,e,n,i){var r=t.coordinateSystem,o=t.id,a=t.getData(),s=this.markerGroupMap,l=s.get(o)||s.set(o,new iS),u=function(t,e,n){var i;i=t?z(t&&t.dimensions,(function(t){return A(A({},e.getData().getDimensionInfo(e.getData().mapDimension(t))||{}),{name:t,ordinalMeta:null})})):[{name:"value",type:"float"}];var r=new ex(i,n),o=z(n.get("data"),H(QV,e));t&&(o=B(o,H(eB,t)));var a=nB(!!t,i);return r.initData(o,null,a),r}(r,t,e);e.setData(u),aB(e.getData(),t,i),u.each((function(t){var n=u.getItemModel(t),i=n.getShallow("symbol"),r=n.getShallow("symbolSize"),o=n.getShallow("symbolRotate"),s=n.getShallow("symbolOffset"),l=n.getShallow("symbolKeepAspect");if(U(i)||U(r)||U(o)||U(s)){var h=e.getRawValue(t),c=e.getDataParams(t);U(i)&&(i=i(h,c)),U(r)&&(r=r(h,c)),U(o)&&(o=o(h,c)),U(s)&&(s=s(h,c))}var p=n.getModel("itemStyle").getItemStyle(),d=wy(a,"color");p.fill||(p.fill=d),u.setItemVisual(t,{symbol:i,symbolSize:r,symbolRotate:o,symbolOffset:s,symbolKeepAspect:l,style:p})})),l.updateData(u),this.group.add(l.group),u.eachItemGraphicEl((function(t){t.traverse((function(t){Js(t).dataModel=e}))})),this.markKeep(l),l.group.silent=e.get("silent")||t.get("silent")},e.type="markPoint",e}(oB);var lB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.createMarkerModelFromSeries=function(t,n,i){return new e(t,n,i)},e.type="markLine",e.defaultOption={z:5,symbol:["circle","arrow"],symbolSize:[8,16],symbolOffset:0,precision:2,tooltip:{trigger:"item"},label:{show:!0,position:"end",distance:5},lineStyle:{type:"dashed"},emphasis:{label:{show:!0},lineStyle:{width:3}},animationEasing:"linear"},e}(jV),uB=Po(),hB=function(t,e,n,i){var r,o=t.getData();if(Y(i))r=i;else{var a=i.type;if("min"===a||"max"===a||"average"===a||"median"===a||null!=i.xAxis||null!=i.yAxis){var s=void 0,l=void 0;if(null!=i.yAxis||null!=i.xAxis)s=e.getAxis(null!=i.yAxis?"y":"x"),l=it(i.yAxis,i.xAxis);else{var u=tB(i,o,e,t);s=u.valueAxis,l=iB(o,ux(o,u.valueDataDim),a)}var h="x"===s.dim?0:1,c=1-h,p=T(i),d={coord:[]};p.type=null,p.coord=[],p.coord[c]=-1/0,d.coord[c]=1/0;var f=n.get("precision");f>=0&&j(l)&&(l=+l.toFixed(Math.min(f,20))),p.coord[h]=d.coord[h]=l,r=[p,d,{type:a,valueIndex:i.valueIndex,value:l}]}else r=[]}var g=[QV(t,r[0]),QV(t,r[1]),A({},r[2])];return g[2].type=g[2].type||null,C(g[2],g[0]),C(g[2],g[1]),g};function cB(t){return!isNaN(t)&&!isFinite(t)}function pB(t,e,n,i){var r=1-t,o=i.dimensions[t];return cB(e[r])&&cB(n[r])&&e[t]===n[t]&&i.getAxis(o).containData(e[t])}function dB(t,e){if("cartesian2d"===t.type){var n=e[0].coord,i=e[1].coord;if(n&&i&&(pB(1,n,i,t)||pB(0,n,i,t)))return!0}return eB(t,e[0])&&eB(t,e[1])}function fB(t,e,n,i,r){var o,a=i.coordinateSystem,s=t.getItemModel(e),l=Ur(s.get("x"),r.getWidth()),u=Ur(s.get("y"),r.getHeight());if(isNaN(l)||isNaN(u)){if(i.getMarkerPosition)o=i.getMarkerPosition(t.getValues(t.dimensions,e));else{var h=a.dimensions,c=t.get(h[0],e),p=t.get(h[1],e);o=a.dataToPoint([c,p])}if(vS(a,"cartesian2d")){var d=a.getAxis("x"),f=a.getAxis("y");h=a.dimensions;cB(t.get(h[0],e))?o[0]=d.toGlobalCoord(d.getExtent()[n?0:1]):cB(t.get(h[1],e))&&(o[1]=f.toGlobalCoord(f.getExtent()[n?0:1]))}isNaN(l)||(o[0]=l),isNaN(u)||(o[1]=u)}else o=[l,u];t.setItemLayout(e,o)}var gB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.updateTransform=function(t,e,n){e.eachSeries((function(t){var e=jV.getMarkerModelFromSeries(t,"markLine");if(e){var i=e.getData(),r=uB(e).from,o=uB(e).to;r.each((function(e){fB(r,e,!0,t,n),fB(o,e,!1,t,n)})),i.each((function(t){i.setItemLayout(t,[r.getItemLayout(t),o.getItemLayout(t)])})),this.markerGroupMap.get(t.id).updateLayout()}}),this)},e.prototype.renderSeries=function(t,e,n,i){var r=t.coordinateSystem,o=t.id,a=t.getData(),s=this.markerGroupMap,l=s.get(o)||s.set(o,new TA);this.group.add(l.group);var u=function(t,e,n){var i;i=t?z(t&&t.dimensions,(function(t){return A(A({},e.getData().getDimensionInfo(e.getData().mapDimension(t))||{}),{name:t,ordinalMeta:null})})):[{name:"value",type:"float"}];var r=new ex(i,n),o=new ex(i,n),a=new ex([],n),s=z(n.get("data"),H(hB,e,t,n));t&&(s=B(s,H(dB,t)));var l=nB(!!t,i);return r.initData(z(s,(function(t){return t[0]})),null,l),o.initData(z(s,(function(t){return t[1]})),null,l),a.initData(z(s,(function(t){return t[2]}))),a.hasItemOption=!0,{from:r,to:o,line:a}}(r,t,e),h=u.from,c=u.to,p=u.line;uB(e).from=h,uB(e).to=c,e.setData(p);var d=e.get("symbol"),f=e.get("symbolSize"),g=e.get("symbolRotate"),y=e.get("symbolOffset");function v(e,n,r){var o=e.getItemModel(n);fB(e,n,r,t,i);var s=o.getModel("itemStyle").getItemStyle();null==s.fill&&(s.fill=wy(a,"color")),e.setItemVisual(n,{symbolKeepAspect:o.get("symbolKeepAspect"),symbolOffset:rt(o.get("symbolOffset",!0),y[r?0:1]),symbolRotate:rt(o.get("symbolRotate",!0),g[r?0:1]),symbolSize:rt(o.get("symbolSize"),f[r?0:1]),symbol:rt(o.get("symbol",!0),d[r?0:1]),style:s})}Y(d)||(d=[d,d]),Y(f)||(f=[f,f]),Y(g)||(g=[g,g]),Y(y)||(y=[y,y]),u.from.each((function(t){v(h,t,!0),v(c,t,!1)})),p.each((function(t){var e=p.getItemModel(t).getModel("lineStyle").getLineStyle();p.setItemLayout(t,[h.getItemLayout(t),c.getItemLayout(t)]),null==e.stroke&&(e.stroke=h.getItemVisual(t,"style").fill),p.setItemVisual(t,{fromSymbolKeepAspect:h.getItemVisual(t,"symbolKeepAspect"),fromSymbolOffset:h.getItemVisual(t,"symbolOffset"),fromSymbolRotate:h.getItemVisual(t,"symbolRotate"),fromSymbolSize:h.getItemVisual(t,"symbolSize"),fromSymbol:h.getItemVisual(t,"symbol"),toSymbolKeepAspect:c.getItemVisual(t,"symbolKeepAspect"),toSymbolOffset:c.getItemVisual(t,"symbolOffset"),toSymbolRotate:c.getItemVisual(t,"symbolRotate"),toSymbolSize:c.getItemVisual(t,"symbolSize"),toSymbol:c.getItemVisual(t,"symbol"),style:e})})),l.updateData(p),u.line.eachItemGraphicEl((function(t){Js(t).dataModel=e,t.traverse((function(t){Js(t).dataModel=e}))})),this.markKeep(l),l.group.silent=e.get("silent")||t.get("silent")},e.type="markLine",e}(oB);var yB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.createMarkerModelFromSeries=function(t,n,i){return new e(t,n,i)},e.type="markArea",e.defaultOption={z:1,tooltip:{trigger:"item"},animation:!1,label:{show:!0,position:"top"},itemStyle:{borderWidth:0},emphasis:{label:{show:!0,position:"top"}}},e}(jV),vB=Po(),mB=function(t,e,n,i){var r=i[0],o=i[1];if(r&&o){var a=QV(t,r),s=QV(t,o),l=a.coord,u=s.coord;l[0]=it(l[0],-1/0),l[1]=it(l[1],-1/0),u[0]=it(u[0],1/0),u[1]=it(u[1],1/0);var h=D([{},a,s]);return h.coord=[a.coord,s.coord],h.x0=a.x,h.y0=a.y,h.x1=s.x,h.y1=s.y,h}};function xB(t){return!isNaN(t)&&!isFinite(t)}function _B(t,e,n,i){var r=1-t;return xB(e[r])&&xB(n[r])}function bB(t,e){var n=e.coord[0],i=e.coord[1],r={coord:n,x:e.x0,y:e.y0},o={coord:i,x:e.x1,y:e.y1};return vS(t,"cartesian2d")?!(!n||!i||!_B(1,n,i)&&!_B(0,n,i))||function(t,e,n){return!(t&&t.containZone&&e.coord&&n.coord&&!KV(e)&&!KV(n))||t.containZone(e.coord,n.coord)}(t,r,o):eB(t,r)||eB(t,o)}function wB(t,e,n,i,r){var o,a=i.coordinateSystem,s=t.getItemModel(e),l=Ur(s.get(n[0]),r.getWidth()),u=Ur(s.get(n[1]),r.getHeight());if(isNaN(l)||isNaN(u)){if(i.getMarkerPosition){var h=t.getValues(["x0","y0"],e),c=t.getValues(["x1","y1"],e),p=a.clampData(h),d=a.clampData(c),f=[];"x0"===n[0]?f[0]=p[0]>d[0]?c[0]:h[0]:f[0]=p[0]>d[0]?h[0]:c[0],"y0"===n[1]?f[1]=p[1]>d[1]?c[1]:h[1]:f[1]=p[1]>d[1]?h[1]:c[1],o=i.getMarkerPosition(f,n,!0)}else{var g=[m=t.get(n[0],e),x=t.get(n[1],e)];a.clampData&&a.clampData(g,g),o=a.dataToPoint(g,!0)}if(vS(a,"cartesian2d")){var y=a.getAxis("x"),v=a.getAxis("y"),m=t.get(n[0],e),x=t.get(n[1],e);xB(m)?o[0]=y.toGlobalCoord(y.getExtent()["x0"===n[0]?0:1]):xB(x)&&(o[1]=v.toGlobalCoord(v.getExtent()["y0"===n[1]?0:1]))}isNaN(l)||(o[0]=l),isNaN(u)||(o[1]=u)}else o=[l,u];return o}var SB=[["x0","y0"],["x1","y0"],["x1","y1"],["x0","y1"]],MB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.updateTransform=function(t,e,n){e.eachSeries((function(t){var e=jV.getMarkerModelFromSeries(t,"markArea");if(e){var i=e.getData();i.each((function(e){var r=z(SB,(function(r){return wB(i,e,r,t,n)}));i.setItemLayout(e,r),i.getItemGraphicEl(e).setShape("points",r)}))}}),this)},e.prototype.renderSeries=function(t,e,n,i){var r=t.coordinateSystem,o=t.id,a=t.getData(),s=this.markerGroupMap,l=s.get(o)||s.set(o,{group:new Er});this.group.add(l.group),this.markKeep(l);var u=function(t,e,n){var i,r,o=["x0","y0","x1","y1"];if(t){var a=z(t&&t.dimensions,(function(t){var n=e.getData();return A(A({},n.getDimensionInfo(n.mapDimension(t))||{}),{name:t,ordinalMeta:null})}));r=z(o,(function(t,e){return{name:t,type:a[e%2].type}})),i=new ex(r,n)}else i=new ex(r=[{name:"value",type:"float"}],n);var s=z(n.get("data"),H(mB,e,t,n));t&&(s=B(s,H(bB,t)));var l=t?function(t,e,n,i){return _f(t.coord[Math.floor(i/2)][i%2],r[i])}:function(t,e,n,i){return _f(t.value,r[i])};return i.initData(s,null,l),i.hasItemOption=!0,i}(r,t,e);e.setData(u),u.each((function(e){var n=z(SB,(function(n){return wB(u,e,n,t,i)})),o=r.getAxis("x").scale,s=r.getAxis("y").scale,l=o.getExtent(),h=s.getExtent(),c=[o.parse(u.get("x0",e)),o.parse(u.get("x1",e))],p=[s.parse(u.get("y0",e)),s.parse(u.get("y1",e))];Zr(c),Zr(p);var d=!!(l[0]>c[1]||l[1]p[1]||h[1]=0},e.prototype.getOrient=function(){return"vertical"===this.get("orient")?{index:1,name:"vertical"}:{index:0,name:"horizontal"}},e.type="legend.plain",e.dependencies=["series"],e.defaultOption={z:4,show:!0,orient:"horizontal",left:"center",top:0,align:"auto",backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderRadius:0,borderWidth:0,padding:5,itemGap:10,itemWidth:25,itemHeight:14,symbolRotate:"inherit",symbolKeepAspect:!0,inactiveColor:"#ccc",inactiveBorderColor:"#ccc",inactiveBorderWidth:"auto",itemStyle:{color:"inherit",opacity:"inherit",borderColor:"inherit",borderWidth:"auto",borderCap:"inherit",borderJoin:"inherit",borderDashOffset:"inherit",borderMiterLimit:"inherit"},lineStyle:{width:"auto",color:"inherit",inactiveColor:"#ccc",inactiveWidth:2,opacity:"inherit",type:"inherit",cap:"inherit",join:"inherit",dashOffset:"inherit",miterLimit:"inherit"},textStyle:{color:"#333"},selectedMode:!0,selector:!1,selectorLabel:{show:!0,borderRadius:10,padding:[3,5,3,5],fontSize:12,fontFamily:"sans-serif",color:"#666",borderWidth:1,borderColor:"#666"},emphasis:{selectorLabel:{show:!0,color:"#eee",backgroundColor:"#666"}},selectorPosition:"auto",selectorItemGap:7,selectorButtonGap:10,tooltip:{show:!1}},e}(Op),TB=H,CB=E,DB=Er,AB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.newlineDisabled=!1,n}return n(e,t),e.prototype.init=function(){this.group.add(this._contentGroup=new DB),this.group.add(this._selectorGroup=new DB),this._isFirstRender=!0},e.prototype.getContentGroup=function(){return this._contentGroup},e.prototype.getSelectorGroup=function(){return this._selectorGroup},e.prototype.render=function(t,e,n){var i=this._isFirstRender;if(this._isFirstRender=!1,this.resetInner(),t.get("show",!0)){var r=t.get("align"),o=t.get("orient");r&&"auto"!==r||(r="right"===t.get("left")&&"vertical"===o?"right":"left");var a=t.get("selector",!0),s=t.get("selectorPosition",!0);!a||s&&"auto"!==s||(s="horizontal"===o?"end":"start"),this.renderInner(r,t,e,n,a,o,s);var l=t.getBoxLayoutParams(),u={width:n.getWidth(),height:n.getHeight()},h=t.get("padding"),c=Tp(l,u,h),p=this.layoutInner(t,r,c,i,a,s),d=Tp(k({width:p.width,height:p.height},l),u,h);this.group.x=d.x-p.x,this.group.y=d.y-p.y,this.group.markRedraw(),this.group.add(this._backgroundEl=rz(p,t))}},e.prototype.resetInner=function(){this.getContentGroup().removeAll(),this._backgroundEl&&this.group.remove(this._backgroundEl),this.getSelectorGroup().removeAll()},e.prototype.renderInner=function(t,e,n,i,r,o,a){var s=this.getContentGroup(),l=yt(),u=e.get("selectedMode"),h=[];n.eachRawSeries((function(t){!t.get("legendHoverLink")&&h.push(t.id)})),CB(e.getData(),(function(r,o){var a=r.get("name");if(!this.newlineDisabled&&(""===a||"\n"===a)){var c=new DB;return c.newline=!0,void s.add(c)}var p=n.getSeriesByName(a)[0];if(!l.get(a)){if(p){var d=p.getData(),f=d.getVisual("legendLineStyle")||{},g=d.getVisual("legendIcon"),y=d.getVisual("style");this._createItem(p,a,o,r,e,t,f,y,g,u,i).on("click",TB(kB,a,null,i,h)).on("mouseover",TB(PB,p.name,null,i,h)).on("mouseout",TB(OB,p.name,null,i,h)),l.set(a,!0)}else n.eachRawSeries((function(n){if(!l.get(a)&&n.legendVisualProvider){var s=n.legendVisualProvider;if(!s.containName(a))return;var c=s.indexOfName(a),p=s.getItemVisual(c,"style"),d=s.getItemVisual(c,"legendIcon"),f=jn(p.fill);f&&0===f[3]&&(f[3]=.2,p=A(A({},p),{fill:ii(f,"rgba")})),this._createItem(n,a,o,r,e,t,{},p,d,u,i).on("click",TB(kB,null,a,i,h)).on("mouseover",TB(PB,null,a,i,h)).on("mouseout",TB(OB,null,a,i,h)),l.set(a,!0)}}),this);0}}),this),r&&this._createSelector(r,e,i,o,a)},e.prototype._createSelector=function(t,e,n,i,r){var o=this.getSelectorGroup();CB(t,(function(t){var i=t.type,r=new Bs({style:{x:0,y:0,align:"center",verticalAlign:"middle"},onclick:function(){n.dispatchAction({type:"all"===i?"legendAllSelect":"legendInverseSelect"})}});o.add(r),Qh(r,{normal:e.getModel("selectorLabel"),emphasis:e.getModel(["emphasis","selectorLabel"])},{defaultText:t.title}),Wl(r)}))},e.prototype._createItem=function(t,e,n,i,r,o,a,s,l,u,h){var c=t.visualDrawType,p=r.get("itemWidth"),d=r.get("itemHeight"),f=r.isSelected(e),g=i.get("symbolRotate"),y=i.get("symbolKeepAspect"),v=i.get("icon"),m=function(t,e,n,i,r,o,a){function s(t,e){"auto"===t.lineWidth&&(t.lineWidth=e.lineWidth>0?2:0),CB(t,(function(n,i){"inherit"===t[i]&&(t[i]=e[i])}))}var l=e.getModel("itemStyle"),u=l.getItemStyle(),h=0===t.lastIndexOf("empty",0)?"fill":"stroke",c=l.getShallow("decal");u.decal=c&&"inherit"!==c?cv(c,a):i.decal,"inherit"===u.fill&&(u.fill=i[r]);"inherit"===u.stroke&&(u.stroke=i[h]);"inherit"===u.opacity&&(u.opacity=("fill"===r?i:n).opacity);s(u,i);var p=e.getModel("lineStyle"),d=p.getLineStyle();if(s(d,n),"auto"===u.fill&&(u.fill=i.fill),"auto"===u.stroke&&(u.stroke=i.fill),"auto"===d.stroke&&(d.stroke=i.fill),!o){var f=e.get("inactiveBorderWidth"),g=u[h];u.lineWidth="auto"===f?i.lineWidth>0&&g?2:0:u.lineWidth,u.fill=e.get("inactiveColor"),u.stroke=e.get("inactiveBorderColor"),d.stroke=p.get("inactiveColor"),d.lineWidth=p.get("inactiveWidth")}return{itemStyle:u,lineStyle:d}}(l=v||l||"roundRect",i,a,s,c,f,h),x=new DB,_=i.getModel("textStyle");if(!U(t.getLegendIcon)||v&&"inherit"!==v){var b="inherit"===v&&t.getData().getVisual("symbol")?"inherit"===g?t.getData().getVisual("symbolRotate"):g:0;x.add(function(t){var e=t.icon||"roundRect",n=Vy(e,0,0,t.itemWidth,t.itemHeight,t.itemStyle.fill,t.symbolKeepAspect);n.setStyle(t.itemStyle),n.rotation=(t.iconRotate||0)*Math.PI/180,n.setOrigin([t.itemWidth/2,t.itemHeight/2]),e.indexOf("empty")>-1&&(n.style.stroke=n.style.fill,n.style.fill="#fff",n.style.lineWidth=2);return n}({itemWidth:p,itemHeight:d,icon:l,iconRotate:b,itemStyle:m.itemStyle,lineStyle:m.lineStyle,symbolKeepAspect:y}))}else x.add(t.getLegendIcon({itemWidth:p,itemHeight:d,icon:l,iconRotate:g,itemStyle:m.itemStyle,lineStyle:m.lineStyle,symbolKeepAspect:y}));var w="left"===o?p+5:-5,S=o,M=r.get("formatter"),I=e;X(M)&&M?I=M.replace("{name}",null!=e?e:""):U(M)&&(I=M(e));var T=i.get("inactiveColor");x.add(new Bs({style:ec(_,{text:I,x:w,y:d/2,fill:f?_.getTextColor():T,align:S,verticalAlign:"middle"})}));var C=new Es({shape:x.getBoundingRect(),invisible:!0}),D=i.getModel("tooltip");return D.get("show")&&Xh({el:C,componentModel:r,itemName:e,itemTooltipOption:D.option}),x.add(C),x.eachChild((function(t){t.silent=!0})),C.silent=!u,this.getContentGroup().add(x),Wl(x),x.__legendDataIndex=n,x},e.prototype.layoutInner=function(t,e,n,i,r,o){var a=this.getContentGroup(),s=this.getSelectorGroup();Ip(t.get("orient"),a,t.get("itemGap"),n.width,n.height);var l=a.getBoundingRect(),u=[-l.x,-l.y];if(s.markRedraw(),a.markRedraw(),r){Ip("horizontal",s,t.get("selectorItemGap",!0));var h=s.getBoundingRect(),c=[-h.x,-h.y],p=t.get("selectorButtonGap",!0),d=t.getOrient().index,f=0===d?"width":"height",g=0===d?"height":"width",y=0===d?"y":"x";"end"===o?c[d]+=l[f]+p:u[d]+=h[f]+p,c[1-d]+=l[g]/2-h[g]/2,s.x=c[0],s.y=c[1],a.x=u[0],a.y=u[1];var v={x:0,y:0};return v[f]=l[f]+p+h[f],v[g]=Math.max(l[g],h[g]),v[y]=Math.min(0,h[y]+c[1-d]),v}return a.x=u[0],a.y=u[1],this.group.getBoundingRect()},e.prototype.remove=function(){this.getContentGroup().removeAll(),this._isFirstRender=!0},e.type="legend.plain",e}(wg);function kB(t,e,n,i){OB(t,e,n,i),n.dispatchAction({type:"legendToggleSelect",name:null!=t?t:e}),PB(t,e,n,i)}function LB(t){for(var e,n=t.getZr().storage.getDisplayList(),i=0,r=n.length;in[r],f=[-c.x,-c.y];e||(f[i]=l[s]);var g=[0,0],y=[-p.x,-p.y],v=rt(t.get("pageButtonGap",!0),t.get("itemGap",!0));d&&("end"===t.get("pageButtonPosition",!0)?y[i]+=n[r]-p[r]:g[i]+=p[r]+v);y[1-i]+=c[o]/2-p[o]/2,l.setPosition(f),u.setPosition(g),h.setPosition(y);var m={x:0,y:0};if(m[r]=d?n[r]:c[r],m[o]=Math.max(c[o],p[o]),m[a]=Math.min(0,p[a]+y[1-i]),u.__rectSize=n[r],d){var x={x:0,y:0};x[r]=Math.max(n[r]-p[r]-v,0),x[o]=m[o],u.setClipPath(new Es({shape:x})),u.__rectSize=x[r]}else h.eachChild((function(t){t.attr({invisible:!0,silent:!0})}));var _=this._getPageInfo(t);return null!=_.pageIndex&&dh(l,{x:_.contentPosition[0],y:_.contentPosition[1]},d?t:null),this._updatePageInfoView(t,_),m},e.prototype._pageGo=function(t,e,n){var i=this._getPageInfo(e)[t];null!=i&&n.dispatchAction({type:"legendScroll",scrollDataIndex:i,legendId:e.id})},e.prototype._updatePageInfoView=function(t,e){var n=this._controllerGroup;E(["pagePrev","pageNext"],(function(i){var r=null!=e[i+"DataIndex"],o=n.childOfName(i);o&&(o.setStyle("fill",r?t.get("pageIconColor",!0):t.get("pageIconInactiveColor",!0)),o.cursor=r?"pointer":"default")}));var i=n.childOfName("pageText"),r=t.get("pageFormatter"),o=e.pageIndex,a=null!=o?o+1:0,s=e.pageCount;i&&r&&i.setStyle("text",X(r)?r.replace("{current}",null==a?"":a+"").replace("{total}",null==s?"":s+""):r({current:a,total:s}))},e.prototype._getPageInfo=function(t){var e=t.get("scrollDataIndex",!0),n=this.getContentGroup(),i=this._containerGroup.__rectSize,r=t.getOrient().index,o=FB[r],a=GB[r],s=this._findTargetItemIndex(e),l=n.children(),u=l[s],h=l.length,c=h?1:0,p={contentPosition:[n.x,n.y],pageCount:c,pageIndex:c-1,pagePrevDataIndex:null,pageNextDataIndex:null};if(!u)return p;var d=m(u);p.contentPosition[r]=-d.s;for(var f=s+1,g=d,y=d,v=null;f<=h;++f)(!(v=m(l[f]))&&y.e>g.s+i||v&&!x(v,g.s))&&(g=y.i>g.i?y:v)&&(null==p.pageNextDataIndex&&(p.pageNextDataIndex=g.i),++p.pageCount),y=v;for(f=s-1,g=d,y=d,v=null;f>=-1;--f)(v=m(l[f]))&&x(y,v.s)||!(g.i=e&&t.s<=e+i}},e.prototype._findTargetItemIndex=function(t){return this._showController?(this.getContentGroup().eachChild((function(i,r){var o=i.__legendDataIndex;null==n&&null!=o&&(n=r),o===t&&(e=r)})),null!=e?e:n):0;var e,n},e.type="legend.scroll",e}(AB);function HB(t){Dm(EB),t.registerComponentModel(zB),t.registerComponentView(WB),function(t){t.registerAction("legendScroll","legendscroll",(function(t,e){var n=t.scrollDataIndex;null!=n&&e.eachComponent({mainType:"legend",subType:"scroll",query:t},(function(t){t.setScrollDataIndex(n)}))}))}(t)}var YB=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="dataZoom.inside",e.defaultOption=Tc(GE.defaultOption,{disabled:!1,zoomLock:!1,zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!1,preventDefaultMouseMove:!0}),e}(GE),UB=Po();function XB(t,e,n){UB(t).coordSysRecordMap.each((function(t){var i=t.dataZoomInfoMap.get(e.uid);i&&(i.getRange=n)}))}function ZB(t,e){if(e){t.removeKey(e.model.uid);var n=e.controller;n&&n.dispose()}}function jB(t,e){t.isDisposed()||t.dispatchAction({type:"dataZoom",animation:{easing:"cubicOut",duration:100},batch:e})}function qB(t,e,n,i){return t.coordinateSystem.containPoint([n,i])}function KB(t){t.registerProcessor(t.PRIORITY.PROCESSOR.FILTER,(function(t,e){var n=UB(e),i=n.coordSysRecordMap||(n.coordSysRecordMap=yt());i.each((function(t){t.dataZoomInfoMap=null})),t.eachComponent({mainType:"dataZoom",subType:"inside"},(function(t){E(BE(t).infoList,(function(n){var r=n.model.uid,o=i.get(r)||i.set(r,function(t,e){var n={model:e,containsPoint:H(qB,e),dispatchAction:H(jB,t),dataZoomInfoMap:null,controller:null},i=n.controller=new BI(t.getZr());return E(["pan","zoom","scrollMove"],(function(t){i.on(t,(function(e){var i=[];n.dataZoomInfoMap.each((function(r){if(e.isAvailableBehavior(r.model.option)){var o=(r.getRange||{})[t],a=o&&o(r.dzReferCoordSysInfo,n.model.mainType,n.controller,e);!r.model.get("disabled",!0)&&a&&i.push({dataZoomId:r.model.id,start:a[0],end:a[1]})}})),i.length&&n.dispatchAction(i)}))})),n}(e,n.model));(o.dataZoomInfoMap||(o.dataZoomInfoMap=yt())).set(t.uid,{dzReferCoordSysInfo:n,model:t,getRange:null})}))})),i.each((function(t){var e,n=t.controller,r=t.dataZoomInfoMap;if(r){var o=r.keys()[0];null!=o&&(e=r.get(o))}if(e){var a=function(t){var e,n="type_",i={type_true:2,type_move:1,type_false:0,type_undefined:-1},r=!0;return t.each((function(t){var o=t.model,a=!o.get("disabled",!0)&&(!o.get("zoomLock",!0)||"move");i[n+a]>i[n+e]&&(e=a),r=r&&o.get("preventDefaultMouseMove",!0)})),{controlType:e,opt:{zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!0,preventDefaultMouseMove:!!r}}}(r);n.enable(a.controlType,a.opt),n.setPointerChecker(t.containsPoint),Eg(t,"dispatchAction",e.model.get("throttle",!0),"fixRate")}else ZB(i,t)}))}))}var $B=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.type="dataZoom.inside",e}return n(e,t),e.prototype.render=function(e,n,i){t.prototype.render.apply(this,arguments),e.noTarget()?this._clear():(this.range=e.getPercentRange(),XB(i,e,{pan:W(JB.pan,this),zoom:W(JB.zoom,this),scrollMove:W(JB.scrollMove,this)}))},e.prototype.dispose=function(){this._clear(),t.prototype.dispose.apply(this,arguments)},e.prototype._clear=function(){!function(t,e){for(var n=UB(t).coordSysRecordMap,i=n.keys(),r=0;r0?s.pixelStart+s.pixelLength-s.pixel:s.pixel-s.pixelStart)/s.pixelLength*(o[1]-o[0])+o[0],u=Math.max(1/i.scale,0);o[0]=(o[0]-l)*u+l,o[1]=(o[1]-l)*u+l;var h=this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();return xk(0,o,[0,100],0,h.minSpan,h.maxSpan),this.range=o,r[0]!==o[0]||r[1]!==o[1]?o:void 0}},pan:QB((function(t,e,n,i,r,o){var a=tF[i]([o.oldX,o.oldY],[o.newX,o.newY],e,r,n);return a.signal*(t[1]-t[0])*a.pixel/a.pixelLength})),scrollMove:QB((function(t,e,n,i,r,o){return tF[i]([0,0],[o.scrollDelta,o.scrollDelta],e,r,n).signal*(t[1]-t[0])*o.scrollDelta}))};function QB(t){return function(e,n,i,r){var o=this.range,a=o.slice(),s=e.axisModels[0];if(s)return xk(t(a,s,e,n,i,r),a,[0,100],"all"),this.range=a,o[0]!==a[0]||o[1]!==a[1]?a:void 0}}var tF={grid:function(t,e,n,i,r){var o=n.axis,a={},s=r.model.coordinateSystem.getRect();return t=t||[0,0],"x"===o.dim?(a.pixel=e[0]-t[0],a.pixelLength=s.width,a.pixelStart=s.x,a.signal=o.inverse?1:-1):(a.pixel=e[1]-t[1],a.pixelLength=s.height,a.pixelStart=s.y,a.signal=o.inverse?-1:1),a},polar:function(t,e,n,i,r){var o=n.axis,a={},s=r.model.coordinateSystem,l=s.getRadiusAxis().getExtent(),u=s.getAngleAxis().getExtent();return t=t?s.pointToCoord(t):[0,0],e=s.pointToCoord(e),"radiusAxis"===n.mainType?(a.pixel=e[0]-t[0],a.pixelLength=l[1]-l[0],a.pixelStart=l[0],a.signal=o.inverse?1:-1):(a.pixel=e[1]-t[1],a.pixelLength=u[1]-u[0],a.pixelStart=u[0],a.signal=o.inverse?-1:1),a},singleAxis:function(t,e,n,i,r){var o=n.axis,a=r.model.coordinateSystem.getRect(),s={};return t=t||[0,0],"horizontal"===o.orient?(s.pixel=e[0]-t[0],s.pixelLength=a.width,s.pixelStart=a.x,s.signal=o.inverse?1:-1):(s.pixel=e[1]-t[1],s.pixelLength=a.height,s.pixelStart=a.y,s.signal=o.inverse?-1:1),s}};function eF(t){$E(t),t.registerComponentModel(YB),t.registerComponentView($B),KB(t)}var nF=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.type="dataZoom.slider",e.layoutMode="box",e.defaultOption=Tc(GE.defaultOption,{show:!0,right:"ph",top:"ph",width:"ph",height:"ph",left:null,bottom:null,borderColor:"#d2dbee",borderRadius:3,backgroundColor:"rgba(47,69,84,0)",dataBackground:{lineStyle:{color:"#d2dbee",width:.5},areaStyle:{color:"#d2dbee",opacity:.2}},selectedDataBackground:{lineStyle:{color:"#8fb0f7",width:.5},areaStyle:{color:"#8fb0f7",opacity:.2}},fillerColor:"rgba(135,175,274,0.2)",handleIcon:"path://M-9.35,34.56V42m0-40V9.5m-2,0h4a2,2,0,0,1,2,2v21a2,2,0,0,1-2,2h-4a2,2,0,0,1-2-2v-21A2,2,0,0,1-11.35,9.5Z",handleSize:"100%",handleStyle:{color:"#fff",borderColor:"#ACB8D1"},moveHandleSize:7,moveHandleIcon:"path://M-320.9-50L-320.9-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-348-41-339-50-320.9-50z M-212.3-50L-212.3-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-239.4-41-230.4-50-212.3-50z M-103.7-50L-103.7-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-130.9-41-121.8-50-103.7-50z",moveHandleStyle:{color:"#D2DBEE",opacity:.7},showDetail:!0,showDataShadow:"auto",realtime:!0,zoomLock:!1,textStyle:{color:"#6E7079"},brushSelect:!0,brushStyle:{color:"rgba(135,175,274,0.15)"},emphasis:{handleStyle:{borderColor:"#8FB0F7"},moveHandleStyle:{color:"#8FB0F7"}}}),e}(GE),iF=Es,rF="horizontal",oF="vertical",aF=["line","bar","candlestick","scatter"],sF={easing:"cubicOut",duration:100,delay:0},lF=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n._displayables={},n}return n(e,t),e.prototype.init=function(t,e){this.api=e,this._onBrush=W(this._onBrush,this),this._onBrushEnd=W(this._onBrushEnd,this)},e.prototype.render=function(e,n,i,r){if(t.prototype.render.apply(this,arguments),Eg(this,"_dispatchZoomAction",e.get("throttle"),"fixRate"),this._orient=e.getOrient(),!1!==e.get("show")){if(e.noTarget())return this._clear(),void this.group.removeAll();r&&"dataZoom"===r.type&&r.from===this.uid||this._buildView(),this._updateView()}else this.group.removeAll()},e.prototype.dispose=function(){this._clear(),t.prototype.dispose.apply(this,arguments)},e.prototype._clear=function(){zg(this,"_dispatchZoomAction");var t=this.api.getZr();t.off("mousemove",this._onBrush),t.off("mouseup",this._onBrushEnd)},e.prototype._buildView=function(){var t=this.group;t.removeAll(),this._brushing=!1,this._displayables.brushRect=null,this._resetLocation(),this._resetInterval();var e=this._displayables.sliderGroup=new Er;this._renderBackground(),this._renderHandle(),this._renderDataShadow(),t.add(e),this._positionGroup()},e.prototype._resetLocation=function(){var t=this.dataZoomModel,e=this.api,n=t.get("brushSelect")?7:0,i=this._findCoordRect(),r={width:e.getWidth(),height:e.getHeight()},o=this._orient===rF?{right:r.width-i.x-i.width,top:r.height-30-7-n,width:i.width,height:30}:{right:7,top:i.y,width:30,height:i.height},a=kp(t.option);E(["right","top","width","height"],(function(t){"ph"===a[t]&&(a[t]=o[t])}));var s=Tp(a,r);this._location={x:s.x,y:s.y},this._size=[s.width,s.height],this._orient===oF&&this._size.reverse()},e.prototype._positionGroup=function(){var t=this.group,e=this._location,n=this._orient,i=this.dataZoomModel.getFirstTargetAxisModel(),r=i&&i.get("inverse"),o=this._displayables.sliderGroup,a=(this._dataShadowInfo||{}).otherAxisInverse;o.attr(n!==rF||r?n===rF&&r?{scaleY:a?1:-1,scaleX:-1}:n!==oF||r?{scaleY:a?-1:1,scaleX:-1,rotation:Math.PI/2}:{scaleY:a?-1:1,scaleX:1,rotation:Math.PI/2}:{scaleY:a?1:-1,scaleX:1});var s=t.getBoundingRect([o]);t.x=e.x-s.x,t.y=e.y-s.y,t.markRedraw()},e.prototype._getViewExtent=function(){return[0,this._size[0]]},e.prototype._renderBackground=function(){var t=this.dataZoomModel,e=this._size,n=this._displayables.sliderGroup,i=t.get("brushSelect");n.add(new iF({silent:!0,shape:{x:0,y:0,width:e[0],height:e[1]},style:{fill:t.get("backgroundColor")},z2:-40}));var r=new iF({shape:{x:0,y:0,width:e[0],height:e[1]},style:{fill:"transparent"},z2:0,onclick:W(this._onClickPanel,this)}),o=this.api.getZr();i?(r.on("mousedown",this._onBrushStart,this),r.cursor="crosshair",o.on("mousemove",this._onBrush),o.on("mouseup",this._onBrushEnd)):(o.off("mousemove",this._onBrush),o.off("mouseup",this._onBrushEnd)),n.add(r)},e.prototype._renderDataShadow=function(){var t=this._dataShadowInfo=this._prepareDataShadowInfo();if(this._displayables.dataShadowSegs=[],t){var e=this._size,n=this._shadowSize||[],i=t.series,r=i.getRawData(),o=i.getShadowDim&&i.getShadowDim(),a=o&&r.getDimensionInfo(o)?i.getShadowDim():t.otherDim;if(null!=a){var s=this._shadowPolygonPts,l=this._shadowPolylinePts;if(r!==this._shadowData||a!==this._shadowDim||e[0]!==n[0]||e[1]!==n[1]){var u=r.getDataExtent(a),h=.3*(u[1]-u[0]);u=[u[0]-h,u[1]+h];var c,p=[0,e[1]],d=[0,e[0]],f=[[e[0],0],[0,0]],g=[],y=d[1]/(r.count()-1),v=0,m=Math.round(r.count()/e[0]);r.each([a],(function(t,e){if(m>0&&e%m)v+=y;else{var n=null==t||isNaN(t)||""===t,i=n?0:Yr(t,u,p,!0);n&&!c&&e?(f.push([f[f.length-1][0],0]),g.push([g[g.length-1][0],0])):!n&&c&&(f.push([v,0]),g.push([v,0])),f.push([v,i]),g.push([v,i]),v+=y,c=n}})),s=this._shadowPolygonPts=f,l=this._shadowPolylinePts=g}this._shadowData=r,this._shadowDim=a,this._shadowSize=[e[0],e[1]];for(var x=this.dataZoomModel,_=0;_<3;_++){var b=w(1===_);this._displayables.sliderGroup.add(b),this._displayables.dataShadowSegs.push(b)}}}function w(t){var e=x.getModel(t?"selectedDataBackground":"dataBackground"),n=new Er,i=new Gu({shape:{points:s},segmentIgnoreThreshold:1,style:e.getModel("areaStyle").getAreaStyle(),silent:!0,z2:-20}),r=new Hu({shape:{points:l},segmentIgnoreThreshold:1,style:e.getModel("lineStyle").getLineStyle(),silent:!0,z2:-19});return n.add(i),n.add(r),n}},e.prototype._prepareDataShadowInfo=function(){var t=this.dataZoomModel,e=t.get("showDataShadow");if(!1!==e){var n,i=this.ecModel;return t.eachTargetAxis((function(r,o){E(t.getAxisProxy(r,o).getTargetSeriesModels(),(function(t){if(!(n||!0!==e&&P(aF,t.get("type"))<0)){var a,s=i.getComponent(zE(r),o).axis,l={x:"y",y:"x",radius:"angle",angle:"radius"}[r],u=t.coordinateSystem;null!=l&&u.getOtherAxis&&(a=u.getOtherAxis(s).inverse),l=t.getData().mapDimension(l),n={thisAxis:s,series:t,thisDim:r,otherDim:l,otherAxisInverse:a}}}),this)}),this),n}},e.prototype._renderHandle=function(){var t=this.group,e=this._displayables,n=e.handles=[null,null],i=e.handleLabels=[null,null],r=this._displayables.sliderGroup,o=this._size,a=this.dataZoomModel,s=this.api,l=a.get("borderRadius")||0,u=a.get("brushSelect"),h=e.filler=new iF({silent:u,style:{fill:a.get("fillerColor")},textConfig:{position:"inside"}});r.add(h),r.add(new iF({silent:!0,subPixelOptimize:!0,shape:{x:0,y:0,width:o[0],height:o[1],r:l},style:{stroke:a.get("dataBackgroundColor")||a.get("borderColor"),lineWidth:1,fill:"rgba(0,0,0,0)"}})),E([0,1],(function(e){var o=a.get("handleIcon");!Ny[o]&&o.indexOf("path://")<0&&o.indexOf("image://")<0&&(o="path://"+o);var s=Vy(o,-1,0,2,2,null,!0);s.attr({cursor:uF(this._orient),draggable:!0,drift:W(this._onDragMove,this,e),ondragend:W(this._onDragEnd,this),onmouseover:W(this._showDataInfo,this,!0),onmouseout:W(this._showDataInfo,this,!1),z2:5});var l=s.getBoundingRect(),u=a.get("handleSize");this._handleHeight=Ur(u,this._size[1]),this._handleWidth=l.width/l.height*this._handleHeight,s.setStyle(a.getModel("handleStyle").getItemStyle()),s.style.strokeNoScale=!0,s.rectHover=!0,s.ensureState("emphasis").style=a.getModel(["emphasis","handleStyle"]).getItemStyle(),Wl(s);var h=a.get("handleColor");null!=h&&(s.style.fill=h),r.add(n[e]=s);var c=a.getModel("textStyle");t.add(i[e]=new Bs({silent:!0,invisible:!0,style:ec(c,{x:0,y:0,text:"",verticalAlign:"middle",align:"center",fill:c.getTextColor(),font:c.getFont()}),z2:10}))}),this);var c=h;if(u){var p=Ur(a.get("moveHandleSize"),o[1]),d=e.moveHandle=new Es({style:a.getModel("moveHandleStyle").getItemStyle(),silent:!0,shape:{r:[0,0,2,2],y:o[1]-.5,height:p}}),f=.8*p,g=e.moveHandleIcon=Vy(a.get("moveHandleIcon"),-f/2,-f/2,f,f,"#fff",!0);g.silent=!0,g.y=o[1]+p/2-.5,d.ensureState("emphasis").style=a.getModel(["emphasis","moveHandleStyle"]).getItemStyle();var y=Math.min(o[1]/2,Math.max(p,10));(c=e.moveZone=new Es({invisible:!0,shape:{y:o[1]-y,height:p+y}})).on("mouseover",(function(){s.enterEmphasis(d)})).on("mouseout",(function(){s.leaveEmphasis(d)})),r.add(d),r.add(g),r.add(c)}c.attr({draggable:!0,cursor:uF(this._orient),drift:W(this._onDragMove,this,"all"),ondragstart:W(this._showDataInfo,this,!0),ondragend:W(this._onDragEnd,this),onmouseover:W(this._showDataInfo,this,!0),onmouseout:W(this._showDataInfo,this,!1)})},e.prototype._resetInterval=function(){var t=this._range=this.dataZoomModel.getPercentRange(),e=this._getViewExtent();this._handleEnds=[Yr(t[0],[0,100],e,!0),Yr(t[1],[0,100],e,!0)]},e.prototype._updateInterval=function(t,e){var n=this.dataZoomModel,i=this._handleEnds,r=this._getViewExtent(),o=n.findRepresentativeAxisProxy().getMinMaxSpan(),a=[0,100];xk(e,i,r,n.get("zoomLock")?"all":t,null!=o.minSpan?Yr(o.minSpan,a,r,!0):null,null!=o.maxSpan?Yr(o.maxSpan,a,r,!0):null);var s=this._range,l=this._range=Zr([Yr(i[0],r,a,!0),Yr(i[1],r,a,!0)]);return!s||s[0]!==l[0]||s[1]!==l[1]},e.prototype._updateView=function(t){var e=this._displayables,n=this._handleEnds,i=Zr(n.slice()),r=this._size;E([0,1],(function(t){var i=e.handles[t],o=this._handleHeight;i.attr({scaleX:o/2,scaleY:o/2,x:n[t]+(t?-1:1),y:r[1]/2-o/2})}),this),e.filler.setShape({x:i[0],y:0,width:i[1]-i[0],height:r[1]});var o={x:i[0],width:i[1]-i[0]};e.moveHandle&&(e.moveHandle.setShape(o),e.moveZone.setShape(o),e.moveZone.getBoundingRect(),e.moveHandleIcon&&e.moveHandleIcon.attr("x",o.x+o.width/2));for(var a=e.dataShadowSegs,s=[0,i[0],i[1],r[0]],l=0;le[0]||n[1]<0||n[1]>e[1])){var i=this._handleEnds,r=(i[0]+i[1])/2,o=this._updateInterval("all",n[0]-r);this._updateView(),o&&this._dispatchZoomAction(!1)}},e.prototype._onBrushStart=function(t){var e=t.offsetX,n=t.offsetY;this._brushStart=new Ce(e,n),this._brushing=!0,this._brushStartTime=+new Date},e.prototype._onBrushEnd=function(t){if(this._brushing){var e=this._displayables.brushRect;if(this._brushing=!1,e){e.attr("ignore",!0);var n=e.shape;if(!(+new Date-this._brushStartTime<200&&Math.abs(n.width)<5)){var i=this._getViewExtent(),r=[0,100];this._range=Zr([Yr(n.x,i,r,!0),Yr(n.x+n.width,i,r,!0)]),this._handleEnds=[n.x,n.x+n.width],this._updateView(),this._dispatchZoomAction(!1)}}}},e.prototype._onBrush=function(t){this._brushing&&(pe(t.event),this._updateBrushRect(t.offsetX,t.offsetY))},e.prototype._updateBrushRect=function(t,e){var n=this._displayables,i=this.dataZoomModel,r=n.brushRect;r||(r=n.brushRect=new iF({silent:!0,style:i.getModel("brushStyle").getItemStyle()}),n.sliderGroup.add(r)),r.attr("ignore",!1);var o=this._brushStart,a=this._displayables.sliderGroup,s=a.transformCoordToLocal(t,e),l=a.transformCoordToLocal(o.x,o.y),u=this._size;s[0]=Math.max(Math.min(u[0],s[0]),0),r.setShape({x:l[0],y:0,width:s[0]-l[0],height:u[1]})},e.prototype._dispatchZoomAction=function(t){var e=this._range;this.api.dispatchAction({type:"dataZoom",from:this.uid,dataZoomId:this.dataZoomModel.id,animation:t?sF:null,start:e[0],end:e[1]})},e.prototype._findCoordRect=function(){var t,e=BE(this.dataZoomModel).infoList;if(!t&&e.length){var n=e[0].model.coordinateSystem;t=n.getRect&&n.getRect()}if(!t){var i=this.api.getWidth(),r=this.api.getHeight();t={x:.2*i,y:.2*r,width:.6*i,height:.6*r}}return t},e.type="dataZoom.slider",e}(YE);function uF(t){return"vertical"===t?"ns-resize":"ew-resize"}function hF(t){t.registerComponentModel(nF),t.registerComponentView(lF),$E(t)}var cF=function(t,e,n){var i=T((pF[t]||{})[e]);return n&&Y(i)?i[i.length-1]:i},pF={color:{active:["#006edd","#e0ffff"],inactive:["rgba(0,0,0,0)"]},colorHue:{active:[0,360],inactive:[0,0]},colorSaturation:{active:[.3,1],inactive:[0,0]},colorLightness:{active:[.9,.5],inactive:[0,0]},colorAlpha:{active:[.3,1],inactive:[0,0]},opacity:{active:[.3,1],inactive:[0,0]},symbol:{active:["circle","roundRect","diamond"],inactive:["none"]},symbolSize:{active:[10,50],inactive:[0,0]}},dF=dD.mapVisual,fF=dD.eachVisual,gF=Y,yF=E,vF=Zr,mF=Yr,xF=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n.stateList=["inRange","outOfRange"],n.replacableOptionKeys=["inRange","outOfRange","target","controller","color"],n.layoutMode={type:"box",ignoreSize:!0},n.dataBound=[-1/0,1/0],n.targetVisuals={},n.controllerVisuals={},n}return n(e,t),e.prototype.init=function(t,e,n){this.mergeDefaultAndTheme(t,n)},e.prototype.optionUpdated=function(t,e){var n=this.option;!e&&dV(n,t,this.replacableOptionKeys),this.textStyleModel=this.getModel("textStyle"),this.resetItemSize(),this.completeVisualOption()},e.prototype.resetVisual=function(t){var e=this.stateList;t=W(t,this),this.controllerVisuals=pV(this.option.controller,e,t),this.targetVisuals=pV(this.option.target,e,t)},e.prototype.getItemSymbol=function(){return null},e.prototype.getTargetSeriesIndices=function(){var t=this.option.seriesIndex,e=[];return null==t||"all"===t?this.ecModel.eachSeries((function(t,n){e.push(n)})):e=_o(t),e},e.prototype.eachTargetSeries=function(t,e){E(this.getTargetSeriesIndices(),(function(n){var i=this.ecModel.getSeriesByIndex(n);i&&t.call(e,i)}),this)},e.prototype.isTargetSeries=function(t){var e=!1;return this.eachTargetSeries((function(n){n===t&&(e=!0)})),e},e.prototype.formatValueText=function(t,e,n){var i,r=this.option,o=r.precision,a=this.dataBound,s=r.formatter;n=n||["<",">"],Y(t)&&(t=t.slice(),i=!0);var l=e?t:i?[u(t[0]),u(t[1])]:u(t);return X(s)?s.replace("{value}",i?l[0]:l).replace("{value2}",i?l[1]:l):U(s)?i?s(t[0],t[1]):s(t):i?t[0]===a[0]?n[0]+" "+l[1]:t[1]===a[1]?n[1]+" "+l[0]:l[0]+" - "+l[1]:l;function u(t){return t===a[0]?"min":t===a[1]?"max":(+t).toFixed(Math.min(o,20))}},e.prototype.resetExtent=function(){var t=this.option,e=vF([t.min,t.max]);this._dataExtent=e},e.prototype.getDataDimensionIndex=function(t){var e=this.option.dimension;if(null!=e)return t.getDimensionIndex(e);for(var n=t.dimensions,i=n.length-1;i>=0;i--){var r=n[i],o=t.getDimensionInfo(r);if(!o.isCalculationCoord)return o.storeDimIndex}},e.prototype.getExtent=function(){return this._dataExtent.slice()},e.prototype.completeVisualOption=function(){var t=this.ecModel,e=this.option,n={inRange:e.inRange,outOfRange:e.outOfRange},i=e.target||(e.target={}),r=e.controller||(e.controller={});C(i,n),C(r,n);var o=this.isCategory();function a(n){gF(e.color)&&!n.inRange&&(n.inRange={color:e.color.slice().reverse()}),n.inRange=n.inRange||{color:t.get("gradientColor")}}a.call(this,i),a.call(this,r),function(t,e,n){var i=t[e],r=t[n];i&&!r&&(r=t[n]={},yF(i,(function(t,e){if(dD.isValidType(e)){var n=cF(e,"inactive",o);null!=n&&(r[e]=n,"color"!==e||r.hasOwnProperty("opacity")||r.hasOwnProperty("colorAlpha")||(r.opacity=[0,0]))}})))}.call(this,i,"inRange","outOfRange"),function(t){var e=(t.inRange||{}).symbol||(t.outOfRange||{}).symbol,n=(t.inRange||{}).symbolSize||(t.outOfRange||{}).symbolSize,i=this.get("inactiveColor"),r=this.getItemSymbol()||"roundRect";yF(this.stateList,(function(a){var s=this.itemSize,l=t[a];l||(l=t[a]={color:o?i:[i]}),null==l.symbol&&(l.symbol=e&&T(e)||(o?r:[r])),null==l.symbolSize&&(l.symbolSize=n&&T(n)||(o?s[0]:[s[0],s[0]])),l.symbol=dF(l.symbol,(function(t){return"none"===t?r:t}));var u=l.symbolSize;if(null!=u){var h=-1/0;fF(u,(function(t){t>h&&(h=t)})),l.symbolSize=dF(u,(function(t){return mF(t,[0,h],[0,s[0]],!0)}))}}),this)}.call(this,r)},e.prototype.resetItemSize=function(){this.itemSize=[parseFloat(this.get("itemWidth")),parseFloat(this.get("itemHeight"))]},e.prototype.isCategory=function(){return!!this.option.categories},e.prototype.setSelected=function(t){},e.prototype.getSelected=function(){return null},e.prototype.getValueState=function(t){return null},e.prototype.getVisualMeta=function(t){return null},e.type="visualMap",e.dependencies=["series"],e.defaultOption={show:!0,z:4,seriesIndex:"all",min:0,max:200,left:0,right:null,top:null,bottom:0,itemWidth:null,itemHeight:null,inverse:!1,orient:"vertical",backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",contentColor:"#5793f3",inactiveColor:"#aaa",borderWidth:0,padding:5,textGap:10,precision:0,textStyle:{color:"#333"}},e}(Op),_F=[20,140],bF=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.optionUpdated=function(e,n){t.prototype.optionUpdated.apply(this,arguments),this.resetExtent(),this.resetVisual((function(t){t.mappingMethod="linear",t.dataExtent=this.getExtent()})),this._resetRange()},e.prototype.resetItemSize=function(){t.prototype.resetItemSize.apply(this,arguments);var e=this.itemSize;(null==e[0]||isNaN(e[0]))&&(e[0]=_F[0]),(null==e[1]||isNaN(e[1]))&&(e[1]=_F[1])},e.prototype._resetRange=function(){var t=this.getExtent(),e=this.option.range;!e||e.auto?(t.auto=1,this.option.range=t):Y(e)&&(e[0]>e[1]&&e.reverse(),e[0]=Math.max(e[0],t[0]),e[1]=Math.min(e[1],t[1]))},e.prototype.completeVisualOption=function(){t.prototype.completeVisualOption.apply(this,arguments),E(this.stateList,(function(t){var e=this.option.controller[t].symbolSize;e&&e[0]!==e[1]&&(e[0]=e[1]/3)}),this)},e.prototype.setSelected=function(t){this.option.range=t.slice(),this._resetRange()},e.prototype.getSelected=function(){var t=this.getExtent(),e=Zr((this.get("range")||[]).slice());return e[0]>t[1]&&(e[0]=t[1]),e[1]>t[1]&&(e[1]=t[1]),e[0]=n[1]||t<=e[1])?"inRange":"outOfRange"},e.prototype.findTargetDataIndices=function(t){var e=[];return this.eachTargetSeries((function(n){var i=[],r=n.getData();r.each(this.getDataDimensionIndex(r),(function(e,n){t[0]<=e&&e<=t[1]&&i.push(n)}),this),e.push({seriesId:n.id,dataIndex:i})}),this),e},e.prototype.getVisualMeta=function(t){var e=wF(this,"outOfRange",this.getExtent()),n=wF(this,"inRange",this.option.range.slice()),i=[];function r(e,n){i.push({value:e,color:t(e,n)})}for(var o=0,a=0,s=n.length,l=e.length;at[1])break;n.push({color:this.getControllerVisual(o,"color",e),offset:r/100})}return n.push({color:this.getControllerVisual(t[1],"color",e),offset:1}),n},e.prototype._createBarPoints=function(t,e){var n=this.visualMapModel.itemSize;return[[n[0]-e[0],t[0]],[n[0],t[0]],[n[0],t[1]],[n[0]-e[1],t[1]]]},e.prototype._createBarGroup=function(t){var e=this._orient,n=this.visualMapModel.get("inverse");return new Er("horizontal"!==e||n?"horizontal"===e&&n?{scaleX:"bottom"===t?-1:1,rotation:-Math.PI/2}:"vertical"!==e||n?{scaleX:"left"===t?1:-1}:{scaleX:"left"===t?1:-1,scaleY:-1}:{scaleX:"bottom"===t?1:-1,rotation:Math.PI/2})},e.prototype._updateHandle=function(t,e){if(this._useHandle){var n=this._shapes,i=this.visualMapModel,r=n.handleThumbs,o=n.handleLabels,a=i.itemSize,s=i.getExtent();DF([0,1],(function(l){var u=r[l];u.setStyle("fill",e.handlesColor[l]),u.y=t[l];var h=CF(t[l],[0,a[1]],s,!0),c=this.getControllerVisual(h,"symbolSize");u.scaleX=u.scaleY=c/a[0],u.x=a[0]-c/2;var p=Eh(n.handleLabelPoints[l],Nh(u,this.group));o[l].setStyle({x:p[0],y:p[1],text:i.formatValueText(this._dataInterval[l]),verticalAlign:"middle",align:"vertical"===this._orient?this._applyTransform("left",n.mainGroup):"center"})}),this)}},e.prototype._showIndicator=function(t,e,n,i){var r=this.visualMapModel,o=r.getExtent(),a=r.itemSize,s=[0,a[1]],l=this._shapes,u=l.indicator;if(u){u.attr("invisible",!1);var h=this.getControllerVisual(t,"color",{convertOpacityToAlpha:!0}),c=this.getControllerVisual(t,"symbolSize"),p=CF(t,o,s,!0),d=a[0]-c/2,f={x:u.x,y:u.y};u.y=p,u.x=d;var g=Eh(l.indicatorLabelPoint,Nh(u,this.group)),y=l.indicatorLabel;y.attr("invisible",!1);var v=this._applyTransform("left",l.mainGroup),m="horizontal"===this._orient;y.setStyle({text:(n||"")+r.formatValueText(e),verticalAlign:m?v:"middle",align:m?"center":v});var x={x:d,y:p,style:{fill:h}},_={style:{x:g[0],y:g[1]}};if(r.ecModel.isAnimationEnabled()&&!this._firstShowIndicator){var b={duration:100,easing:"cubicInOut",additive:!0};u.x=f.x,u.y=f.y,u.animateTo(x,b),y.animateTo(_,b)}else u.attr(x),y.attr(_);this._firstShowIndicator=!1;var w=this._shapes.handleLabels;if(w)for(var S=0;Sr[1]&&(u[1]=1/0),e&&(u[0]===-1/0?this._showIndicator(l,u[1],"< ",a):u[1]===1/0?this._showIndicator(l,u[0],"> ",a):this._showIndicator(l,l,"≈ ",a));var h=this._hoverLinkDataIndices,c=[];(e||OF(n))&&(c=this._hoverLinkDataIndices=n.findTargetDataIndices(u));var p=function(t,e){var n={},i={};return r(t||[],n),r(e||[],i,n),[o(n),o(i)];function r(t,e,n){for(var i=0,r=t.length;i=0&&(r.dimension=o,i.push(r))}})),t.getData().setVisual("visualMeta",i)}}];function VF(t,e,n,i){for(var r=e.targetVisuals[i],o=dD.prepareVisualTypes(r),a={color:wy(t.getData(),"color")},s=0,l=o.length;s0:t.splitNumber>0)&&!t.calculable?"piecewise":"continuous"})),t.registerAction(NF,EF),E(zF,(function(e){t.registerVisual(t.PRIORITY.VISUAL.COMPONENT,e)})),t.registerPreprocessor(FF))}function YF(t){t.registerComponentModel(bF),t.registerComponentView(LF),HF(t)}var UF=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n._pieceList=[],n}return n(e,t),e.prototype.optionUpdated=function(e,n){t.prototype.optionUpdated.apply(this,arguments),this.resetExtent();var i=this._mode=this._determineMode();this._pieceList=[],XF[this._mode].call(this,this._pieceList),this._resetSelected(e,n);var r=this.option.categories;this.resetVisual((function(t,e){"categories"===i?(t.mappingMethod="category",t.categories=T(r)):(t.dataExtent=this.getExtent(),t.mappingMethod="piecewise",t.pieceList=z(this._pieceList,(function(t){return t=T(t),"inRange"!==e&&(t.visual=null),t})))}))},e.prototype.completeVisualOption=function(){var e=this.option,n={},i=dD.listVisualTypes(),r=this.isCategory();function o(t,e,n){return t&&t[e]&&t[e].hasOwnProperty(n)}E(e.pieces,(function(t){E(i,(function(e){t.hasOwnProperty(e)&&(n[e]=1)}))})),E(n,(function(t,n){var i=!1;E(this.stateList,(function(t){i=i||o(e,t,n)||o(e.target,t,n)}),this),!i&&E(this.stateList,(function(t){(e[t]||(e[t]={}))[n]=cF(n,"inRange"===t?"active":"inactive",r)}))}),this),t.prototype.completeVisualOption.apply(this,arguments)},e.prototype._resetSelected=function(t,e){var n=this.option,i=this._pieceList,r=(e?n:t).selected||{};if(n.selected=r,E(i,(function(t,e){var n=this.getSelectedMapKey(t);r.hasOwnProperty(n)||(r[n]=!0)}),this),"single"===n.selectedMode){var o=!1;E(i,(function(t,e){var n=this.getSelectedMapKey(t);r[n]&&(o?r[n]=!1:o=!0)}),this)}},e.prototype.getItemSymbol=function(){return this.get("itemSymbol")},e.prototype.getSelectedMapKey=function(t){return"categories"===this._mode?t.value+"":t.index+""},e.prototype.getPieceList=function(){return this._pieceList},e.prototype._determineMode=function(){var t=this.option;return t.pieces&&t.pieces.length>0?"pieces":this.option.categories?"categories":"splitNumber"},e.prototype.setSelected=function(t){this.option.selected=T(t)},e.prototype.getValueState=function(t){var e=dD.findPieceIndex(t,this._pieceList);return null!=e&&this.option.selected[this.getSelectedMapKey(this._pieceList[e])]?"inRange":"outOfRange"},e.prototype.findTargetDataIndices=function(t){var e=[],n=this._pieceList;return this.eachTargetSeries((function(i){var r=[],o=i.getData();o.each(this.getDataDimensionIndex(o),(function(e,i){dD.findPieceIndex(e,n)===t&&r.push(i)}),this),e.push({seriesId:i.id,dataIndex:r})}),this),e},e.prototype.getRepresentValue=function(t){var e;if(this.isCategory())e=t.value;else if(null!=t.value)e=t.value;else{var n=t.interval||[];e=n[0]===-1/0&&n[1]===1/0?0:(n[0]+n[1])/2}return e},e.prototype.getVisualMeta=function(t){if(!this.isCategory()){var e=[],n=["",""],i=this,r=this._pieceList.slice();if(r.length){var o=r[0].interval[0];o!==-1/0&&r.unshift({interval:[-1/0,o]}),(o=r[r.length-1].interval[1])!==1/0&&r.push({interval:[o,1/0]})}else r.push({interval:[-1/0,1/0]});var a=-1/0;return E(r,(function(t){var e=t.interval;e&&(e[0]>a&&s([a,e[0]],"outOfRange"),s(e.slice()),a=e[1])}),this),{stops:e,outerColors:n}}function s(r,o){var a=i.getRepresentValue({interval:r});o||(o=i.getValueState(a));var s=t(a,o);r[0]===-1/0?n[0]=s:r[1]===1/0?n[1]=s:e.push({value:r[0],color:s},{value:r[1],color:s})}},e.type="visualMap.piecewise",e.defaultOption=Tc(xF.defaultOption,{selected:null,minOpen:!1,maxOpen:!1,align:"auto",itemWidth:20,itemHeight:14,itemSymbol:"roundRect",pieces:null,categories:null,splitNumber:5,selectedMode:"multiple",itemGap:10,hoverLink:!0}),e}(xF),XF={splitNumber:function(t){var e=this.option,n=Math.min(e.precision,20),i=this.getExtent(),r=e.splitNumber;r=Math.max(parseInt(r,10),1),e.splitNumber=r;for(var o=(i[1]-i[0])/r;+o.toFixed(n)!==o&&n<5;)n++;e.precision=n,o=+o.toFixed(n),e.minOpen&&t.push({interval:[-1/0,i[0]],close:[0,0]});for(var a=0,s=i[0];a","≥"][e[0]]];t.text=t.text||this.formatValueText(null!=t.value?t.value:t.interval,!1,n)}),this)}};function ZF(t,e){var n=t.inverse;("vertical"===t.orient?!n:n)&&e.reverse()}var jF=function(t){function e(){var n=null!==t&&t.apply(this,arguments)||this;return n.type=e.type,n}return n(e,t),e.prototype.doRender=function(){var t=this.group;t.removeAll();var e=this.visualMapModel,n=e.get("textGap"),i=e.textStyleModel,r=i.getFont(),o=i.getTextColor(),a=this._getItemAlign(),s=e.itemSize,l=this._getViewData(),u=l.endsText,h=it(e.get("showLabel",!0),!u);u&&this._renderEndsText(t,u[0],s,h,a),E(l.viewPieceList,(function(i){var l=i.piece,u=new Er;u.onclick=W(this._onItemClick,this,l),this._enableHoverLink(u,i.indexInModelPieceList);var c=e.getRepresentValue(l);if(this._createItemSymbol(u,c,[0,0,s[0],s[1]]),h){var p=this.visualMapModel.getValueState(c);u.add(new Bs({style:{x:"right"===a?-n:s[0]+n,y:s[1]/2,text:l.text,verticalAlign:"middle",align:a,font:r,fill:o,opacity:"outOfRange"===p?.5:1}}))}t.add(u)}),this),u&&this._renderEndsText(t,u[1],s,h,a),Ip(e.get("orient"),t,e.get("itemGap")),this.renderBackground(t),this.positionGroup(t)},e.prototype._enableHoverLink=function(t,e){var n=this;t.on("mouseover",(function(){return i("highlight")})).on("mouseout",(function(){return i("downplay")}));var i=function(t){var i=n.visualMapModel;i.option.hoverLink&&n.api.dispatchAction({type:t,batch:TF(i.findTargetDataIndices(e),i)})}},e.prototype._getItemAlign=function(){var t=this.visualMapModel,e=t.option;if("vertical"===e.orient)return IF(t,this.api,t.itemSize);var n=e.align;return n&&"auto"!==n||(n="left"),n},e.prototype._renderEndsText=function(t,e,n,i,r){if(e){var o=new Er,a=this.visualMapModel.textStyleModel;o.add(new Bs({style:ec(a,{x:i?"right"===r?n[0]:0:n[0]/2,y:n[1]/2,verticalAlign:"middle",align:i?r:"center",text:e})})),t.add(o)}},e.prototype._getViewData=function(){var t=this.visualMapModel,e=z(t.getPieceList(),(function(t,e){return{piece:t,indexInModelPieceList:e}})),n=t.get("text"),i=t.get("orient"),r=t.get("inverse");return("horizontal"===i?r:!r)?e.reverse():n&&(n=n.slice().reverse()),{viewPieceList:e,endsText:n}},e.prototype._createItemSymbol=function(t,e,n){t.add(Vy(this.getControllerVisual(e,"symbol"),n[0],n[1],n[2],n[3],this.getControllerVisual(e,"color")))},e.prototype._onItemClick=function(t){var e=this.visualMapModel,n=e.option,i=n.selectedMode;if(i){var r=T(n.selected),o=e.getSelectedMapKey(t);"single"===i||!0===i?(r[o]=!0,E(r,(function(t,e){r[e]=e===o}))):r[o]=!r[o],this.api.dispatchAction({type:"selectDataRange",from:this.uid,visualMapId:this.visualMapModel.id,selected:r})}},e.type="visualMap.piecewise",e}(SF);function qF(t){t.registerComponentModel(UF),t.registerComponentView(jF),HF(t)}var KF={label:{enabled:!0},decal:{show:!1}},$F=Po(),JF={};function QF(t,e){var n=t.getModel("aria");if(n.get("enabled")){var i=T(KF);C(i.label,t.getLocaleModel().get("aria"),!1),C(n.option,i,!1),function(){if(n.getModel("decal").get("show")){var e=yt();t.eachSeries((function(t){if(!t.isColorBySeries()){var n=e.get(t.type);n||(n={},e.set(t.type,n)),$F(t).scope=n}})),t.eachRawSeries((function(e){if(!t.isSeriesFiltered(e))if(U(e.enableAriaDecal))e.enableAriaDecal();else{var n=e.getData();if(e.isColorBySeries()){var i=ld(e.ecModel,e.name,JF,t.getSeriesCount()),r=n.getVisual("decal");n.setVisual("decal",u(r,i))}else{var o=e.getRawData(),a={},s=$F(e).scope;n.each((function(t){var e=n.getRawIndex(t);a[e]=t}));var l=o.count();o.each((function(t){var i=a[t],r=o.getName(t)||t+"",h=ld(e.ecModel,r,s,l),c=n.getItemVisual(i,"decal");n.setItemVisual(i,"decal",u(c,h))}))}}function u(t,e){var n=t?A(A({},e),t):e;return n.dirty=!0,n}}))}}(),function(){var i=t.getLocaleModel().get("aria"),o=n.getModel("label");if(o.option=k(o.option,i),!o.get("enabled"))return;var a=e.getZr().dom;if(o.get("description"))return void a.setAttribute("aria-label",o.get("description"));var s,l=t.getSeriesCount(),u=o.get(["data","maxCount"])||10,h=o.get(["series","maxCount"])||10,c=Math.min(l,h);if(l<1)return;var p=function(){var e=t.get("title");e&&e.length&&(e=e[0]);return e&&e.text}();if(p){var d=o.get(["general","withTitle"]);s=r(d,{title:p})}else s=o.get(["general","withoutTitle"]);var f=[],g=l>1?o.get(["series","multiple","prefix"]):o.get(["series","single","prefix"]);s+=r(g,{seriesCount:l}),t.eachSeries((function(e,n){if(n1?o.get(["series","multiple",a]):o.get(["series","single",a]),{seriesId:e.seriesIndex,seriesName:e.get("name"),seriesType:(x=e.subType,t.getLocaleModel().get(["series","typeNames"])[x]||"自定义图")});var s=e.getData();if(s.count()>u)i+=r(o.get(["data","partialData"]),{displayCnt:u});else i+=o.get(["data","allData"]);for(var h=o.get(["data","separator","middle"]),p=o.get(["data","separator","end"]),d=[],g=0;g":"gt",">=":"gte","=":"eq","!=":"ne","<>":"ne"},nG=function(){function t(t){if(null==(this._condVal=X(t)?new RegExp(t):et(t)?t:null)){var e="";0,yo(e)}}return t.prototype.evaluate=function(t){var e=typeof t;return X(e)?this._condVal.test(t):!!j(e)&&this._condVal.test(t+"")},t}(),iG=function(){function t(){}return t.prototype.evaluate=function(){return this.value},t}(),rG=function(){function t(){}return t.prototype.evaluate=function(){for(var t=this.children,e=0;e2&&l.push(e),e=[t,n]}function f(t,n,i,r){vG(t,i)&&vG(n,r)||e.push(t,n,i,r,i,r)}function g(t,n,i,r,o,a){var s=Math.abs(n-t),l=4*Math.tan(s/4)/3,u=nM:C2&&l.push(e),l}function xG(t,e,n,i,r,o,a,s,l,u){if(vG(t,n)&&vG(e,i)&&vG(r,a)&&vG(o,s))l.push(a,s);else{var h=2/u,c=h*h,p=a-t,d=s-e,f=Math.sqrt(p*p+d*d);p/=f,d/=f;var g=n-t,y=i-e,v=r-a,m=o-s,x=g*g+y*y,_=v*v+m*m;if(x=0&&_-w*w=0)l.push(a,s);else{var S=[],M=[];bn(t,n,r,a,.5,S),bn(e,i,o,s,.5,M),xG(S[0],M[0],S[1],M[1],S[2],M[2],S[3],M[3],l,u),xG(S[4],M[4],S[5],M[5],S[6],M[6],S[7],M[7],l,u)}}}}function _G(t,e,n){var i=t[e],r=t[1-e],o=Math.abs(i/r),a=Math.ceil(Math.sqrt(o*n)),s=Math.floor(n/a);0===s&&(s=1,a=n);for(var l=[],u=0;u0)for(u=0;uMath.abs(u),c=_G([l,u],h?0:1,e),p=(h?s:u)/c.length,d=0;d1?null:new Ce(d*l+t,d*u+e)}function MG(t,e,n){var i=new Ce;Ce.sub(i,n,e),i.normalize();var r=new Ce;return Ce.sub(r,t,e),r.dot(i)}function IG(t,e){var n=t[t.length-1];n&&n[0]===e[0]&&n[1]===e[1]||t.push(e)}function TG(t){var e=t.points,n=[],i=[];Oa(e,n,i);var r=new Ee(n[0],n[1],i[0]-n[0],i[1]-n[1]),o=r.width,a=r.height,s=r.x,l=r.y,u=new Ce,h=new Ce;return o>a?(u.x=h.x=s+o/2,u.y=l,h.y=l+a):(u.y=h.y=l+a/2,u.x=s,h.x=s+o),function(t,e,n){for(var i=t.length,r=[],o=0;or,a=_G([i,r],o?0:1,e),s=o?"width":"height",l=o?"height":"width",u=o?"x":"y",h=o?"y":"x",c=t[s]/a.length,p=0;p0)for(var b=i/n,w=-i/2;w<=i/2;w+=b){var S=Math.sin(w),M=Math.cos(w),I=0;for(x=0;x0;l/=2){var u=0,h=0;(t&l)>0&&(u=1),(e&l)>0&&(h=1),s+=l*l*(3*u^h),0===h&&(1===u&&(t=l-1-t,e=l-1-e),a=t,t=e,e=a)}return s}function HG(t){var e=1/0,n=1/0,i=-1/0,r=-1/0,o=z(t,(function(t){var o=t.getBoundingRect(),a=t.getComputedTransform(),s=o.x+o.width/2+(a?a[4]:0),l=o.y+o.height/2+(a?a[5]:0);return e=Math.min(s,e),n=Math.min(l,n),i=Math.max(s,i),r=Math.max(l,r),[s,l]}));return z(o,(function(o,a){return{cp:o,z:WG(o[0],o[1],e,n,i,r),path:t[a]}})).sort((function(t,e){return t.z-e.z})).map((function(t){return t.path}))}function YG(t){return AG(t.path,t.count)}function UG(t){return Y(t[0])}function XG(t,e){for(var n=[],i=t.length,r=0;r=0;r--)if(!n[r].many.length){var l=n[s].many;if(l.length<=1){if(!s)return n;s=0}o=l.length;var u=Math.ceil(o/2);n[r].many=l.slice(u,o),n[s].many=l.slice(0,u),s++}return n}var ZG={clone:function(t){for(var e=[],n=1-Math.pow(1-t.path.style.opacity,1/t.count),i=0;i0){var s,l,u=i.getModel("universalTransition").get("delay"),h=Object.assign({setToFinal:!0},a);UG(t)&&(s=t,l=e),UG(e)&&(s=e,l=t);for(var c=s?s===t:t.length>e.length,p=s?XG(l,s):XG(c?e:t,[c?t:e]),d=0,f=0;f1e4))for(var i=n.getIndices(),r=function(t){for(var e=t.dimensions,n=0;n0&&i.group.traverse((function(t){t instanceof Ms&&!t.animators.length&&t.animateFrom({style:{opacity:0}},r)}))}))}function iW(t){var e=t.getModel("universalTransition").get("seriesKey");return e||t.id}function rW(t){return Y(t)?t.sort().join(","):t}function oW(t){if(t.hostModel)return t.hostModel.getModel("universalTransition").get("divideShape")}function aW(t,e){for(var n=0;n=0&&r.push({dataGroupId:e.oldDataGroupIds[n],data:e.oldData[n],divide:oW(e.oldData[n]),dim:t.dimension})})),E(_o(t.to),(function(t){var i=aW(n.updatedSeries,t);if(i>=0){var r=n.updatedSeries[i].getData();o.push({dataGroupId:e.oldDataGroupIds[i],data:r,divide:oW(r),dim:t.dimension})}})),r.length>0&&o.length>0&&nW(r,o,i)}(t,i,n,e)}));else{var o=function(t,e){var n=yt(),i=yt(),r=yt();return E(t.oldSeries,(function(e,n){var o=t.oldDataGroupIds[n],a=t.oldData[n],s=iW(e),l=rW(s);i.set(l,{dataGroupId:o,data:a}),Y(s)&&E(s,(function(t){r.set(t,{key:l,dataGroupId:o,data:a})}))})),E(e.updatedSeries,(function(t){if(t.isUniversalTransitionEnabled()&&t.isAnimationEnabled()){var e=t.get("dataGroupId"),o=t.getData(),a=iW(t),s=rW(a),l=i.get(s);if(l)n.set(s,{oldSeries:[{dataGroupId:l.dataGroupId,divide:oW(l.data),data:l.data}],newSeries:[{dataGroupId:e,divide:oW(o),data:o}]});else if(Y(a)){var u=[];E(a,(function(t){var e=i.get(t);e.data&&u.push({dataGroupId:e.dataGroupId,divide:oW(e.data),data:e.data})})),u.length&&n.set(s,{oldSeries:u,newSeries:[{dataGroupId:e,data:o,divide:oW(o)}]})}else{var h=r.get(a);if(h){var c=n.get(h.key);c||(c={oldSeries:[{dataGroupId:h.dataGroupId,data:h.data,divide:oW(h.data)}],newSeries:[]},n.set(h.key,c)),c.newSeries.push({dataGroupId:e,data:o,divide:oW(o)})}}}})),n}(i,n);E(o.keys(),(function(t){var n=o.get(t);nW(n.oldSeries,n.newSeries,e)}))}E(n.updatedSeries,(function(t){t.__universalTransitionEnabled&&(t.__universalTransitionEnabled=!1)}))}for(var a=t.getSeries(),s=i.oldSeries=[],l=i.oldDataGroupIds=[],u=i.oldData=[],h=0;h)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="
                ",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
                a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
                t
                ",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
                ",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t -}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
                ","
                "],area:[1,"",""],param:[1,"",""],thead:[1,"","
                "],tr:[2,"","
                "],col:[2,"","
                "],td:[3,"","
                "],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
                ","
                "]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); -u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+'',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}e.vessel(f,function(n,r,d){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){l.parents("."+s[0])[0]||(l.data("display",l.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+s[0]+a).find("."+s[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=d),e.layero=i("#"+s[0]+a),t.scrollbar||s.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",l[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),s.anim[t.anim]&&e.layero.addClass(s.anim[t.anim]).data("anim",!0)}},l.pt.auto=function(e){function t(e){e=l.find(e),e.height(f[1]-c-d-2*(0|parseFloat(e.css("padding"))))}var a=this,o=a.config,l=i("#"+s[0]+e);""===o.area[0]&&o.maxWidth>0&&(r.ie&&r.ie<8&&o.btn&&l.width(l.innerWidth()),l.outerWidth()>o.maxWidth&&l.width(o.maxWidth));var f=[l.innerWidth(),l.innerHeight()],c=l.find(s[1]).outerHeight()||0,d=l.find("."+s[6]).outerHeight()||0;switch(o.type){case 2:t("iframe");break;default:""===o.area[1]?o.fixed&&f[1]>=n.height()&&(f[1]=n.height(),t("."+s[5])):t("."+s[5])}return a},l.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(s[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},l.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var l={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),l.autoLeft=function(){l.left+o[0]-n.width()>0?(l.tipLeft=l.left+l.width-o[0],f.css({right:12,left:"auto"})):l.tipLeft=l.left},l.where=[function(){l.autoLeft(),l.tipTop=l.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){l.tipLeft=l.left+l.width+10,l.tipTop=l.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){l.autoLeft(),l.tipTop=l.top+l.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){l.tipLeft=l.left-o[0]-10,l.tipTop=l.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],l.where[c-1](),1===c?l.top-(n.scrollTop()+o[1]+16)<0&&l.where[2]():2===c?n.width()-(l.left+l.width+o[0]+16)>0||l.where[3]():3===c?l.top-n.scrollTop()+l.height+o[1]+16-n.height()>0&&l.where[0]():4===c&&o[0]+16-l.left>0&&l.where[1](),a.find("."+s[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:l.tipLeft-(t.fixed?n.scrollLeft():0),top:l.tipTop-(t.fixed?n.scrollTop():0)})},l.pt.move=function(){var e=this,t=e.config,a=i(document),l=e.layero,s=l.find(t.move),f=l.find(".layui-layer-resize"),c={};return t.move&&s.css("cursor","move"),s.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(l.css("left")),e.clientY-parseFloat(l.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[l.outerWidth(),l.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],s="fixed"===l.css("position");if(i.preventDefault(),c.stX=s?0:n.scrollLeft(),c.stY=s?0:n.scrollTop(),!t.moveOut){var f=n.width()-l.outerWidth()+c.stX,d=n.height()-l.outerHeight()+c.stY;af&&(a=f),od&&(o=d)}l.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd()),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},l.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+s[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+s[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+s[0])[0]||1==n.attr("layer")&&i("."+s[0]).length<1&&n.removeAttr("layer").show(),n=null})},l.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+s[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},l.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){s.html.attr("layer-full")==e&&(s.html[0].style.removeProperty?s.html[0].style.removeProperty("overflow"):s.html[0].style.removeAttribute("overflow"),s.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+s[4]).attr("times"),i("#"+s[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+s[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+s[0]+e),a=n.find(s[1]).outerHeight()||0,o=n.find("."+s[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+s[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+s[0]+e),r=a.find(".layui-layer-content"),l=a.attr("type"),f=a.find(s[1]).outerHeight()||0,c=a.find("."+s[6]).outerHeight()||0;a.attr("minLeft");l!==o.type[3]&&l!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+s[6]).outerHeight(),l===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+s[0]+e),l=a.find(s[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:l,left:f,top:n.height()-l,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(s[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+s[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(s[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+s[0]+e);o.record(a),s.html.attr("layer-full")||s.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+s[0]+(t||r.index)).find(s[1]);n.html(e)},r.close=function(e){var t=i("#"+s[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var l="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+s[5]+")").remove();for(var a=t.find("."+l),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(l)}else{if(n===o.type[2])try{var f=i("#"+s[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+s[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("anim")&&t.addClass(a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),setTimeout(function(){f()},r.ie&&r.ie<10||!t.data("anim")?0:200)}},r.closeAll=function(e){i.each(i("."+s[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var l,s=2==e.formType?'":function(){return''}();return r.open(i.extend({type:1,btn:["确定","取消"],content:s,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(e){l=e.find(".layui-layer-input"),l.focus()},resize:!1,yes:function(i){var n=l.val();""===n?l.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",l,{tips:1}):t&&t(n,i,l)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{};return r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,n="";if(e>0)for(n=''+t[0].title+"";i"+t[i].title+"";return n}(),content:'
                  '+function(){var e=t.length,i=1,n="";if(e>0)for(n='
                • '+(t[0].content||"no content")+"
                • ";i'+(t[i].content||"no content")+"";return n}()+"
                ",success:function(t){var n=t.find(".layui-layer-title").children(),a=t.find(".layui-layer-tabmain").children();n.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var n=i(this),o=n.index();n.addClass("layui-layer-tabnow").siblings().removeClass("layui-layer-tabnow"),a.eq(o).show().siblings().hide(),"function"==typeof e.change&&e.change(o)})}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var l={};if(t=t||{},t.photos){var s=t.photos.constructor===Object,f=s?t.photos:{},d=f.data||[],u=f.start||0;if(l.imgIndex=(0|u)+1,t.img=t.img||"img",s){if(0===d.length)return r.msg("没有图片")}else{var y=i(t.photos),p=function(){d=[],y.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),d.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(p(),0===d.length)return;if(n||y.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:d,tab:t.tab},full:t.full}),!0),p()}),!n)return}l.imgprev=function(e){l.imgIndex--,l.imgIndex<1&&(l.imgIndex=d.length),l.tabimg(e)},l.imgnext=function(e,t){l.imgIndex++,l.imgIndex>d.length&&(l.imgIndex=1,t)||l.tabimg(e)},l.keyup=function(e){if(!l.end){var t=e.keyCode;e.preventDefault(),37===t?l.imgprev(!0):39===t?l.imgnext(!0):27===t&&r.close(l.index)}},l.tabimg=function(e){d.length<=1||(f.start=l.imgIndex-1,r.close(l.index),r.photos(t,!0,e))},l.event=function(){l.bigimg.hover(function(){l.imgsee.show()},function(){l.imgsee.hide()}),l.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),l.imgprev()}),l.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),l.imgnext()}),i(document).on("keyup",l.keyup)},l.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(d[u].src,function(n){r.close(l.loadi),l.index=r.open(i.extend({type:1,area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0]'+(d[u].alt||
                '+(d.length>1?'':"")+'
                '+(d[u].alt||"")+""+l.imgIndex+"/"+d.length+"
                ",success:function(e,i){l.bigimg=e.find(".layui-layer-phimg"),l.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),l.event(e),t.tab&&t.tab(d[u],e)},end:function(){l.end=!0,i(document).off("keyup",l.keyup)}},t))},function(){r.close(l.loadi),r.msg("当前图片地址异常
                是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){d.length>1&&l.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),s.html=i("html"),r.open=function(e){var t=new l(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.jquery),e.layer=r,t("layer",r)})):"function"==typeof define?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window); \ No newline at end of file diff --git a/route/static/layer/mobile/index.html b/route/static/layer/mobile/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/layer/mobile/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/layer/mobile/layer.js b/route/static/layer/mobile/layer.js deleted file mode 100755 index f9cf69313..000000000 --- a/route/static/layer/mobile/layer.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! layer mobile-v2.0.0 Web弹层组件 MIT License http://layer.layui.com/mobile By 贤心 */ - ;!function(e){"use strict";var t=document,n="querySelectorAll",i="getElementsByClassName",a=function(e){return t[n](e)},s={type:0,shade:!0,shadeClose:!0,fixed:!0,anim:"scale"},l={extend:function(e){var t=JSON.parse(JSON.stringify(s));for(var n in e)t[n]=e[n];return t},timer:{},end:{}};l.touch=function(e,t){e.addEventListener("click",function(e){t.call(this,e)},!1)};var r=0,o=["layui-m-layer"],c=function(e){var t=this;t.config=l.extend(e),t.view()};c.prototype.view=function(){var e=this,n=e.config,s=t.createElement("div");e.id=s.id=o[0]+r,s.setAttribute("class",o[0]+" "+o[0]+(n.type||0)),s.setAttribute("index",r);var l=function(){var e="object"==typeof n.title;return n.title?'

                '+(e?n.title[0]:n.title)+"

                ":""}(),c=function(){"string"==typeof n.btn&&(n.btn=[n.btn]);var e,t=(n.btn||[]).length;return 0!==t&&n.btn?(e=''+n.btn[0]+"",2===t&&(e=''+n.btn[1]+""+e),'
                '+e+"
                "):""}();if(n.fixed||(n.top=n.hasOwnProperty("top")?n.top:100,n.style=n.style||"",n.style+=" top:"+(t.body.scrollTop+n.top)+"px"),2===n.type&&(n.content='

                '+(n.content||"")+"

                "),n.skin&&(n.anim="up"),"msg"===n.skin&&(n.shade=!1),s.innerHTML=(n.shade?"
                ':"")+'
                "+l+'
                '+n.content+"
                "+c+"
                ",!n.type||2===n.type){var d=t[i](o[0]+n.type),y=d.length;y>=1&&layer.close(d[0].getAttribute("index"))}document.body.appendChild(s);var u=e.elem=a("#"+e.id)[0];n.success&&n.success(u),e.index=r++,e.action(n,u)},c.prototype.action=function(e,t){var n=this;e.time&&(l.timer[n.index]=setTimeout(function(){layer.close(n.index)},1e3*e.time));var a=function(){var t=this.getAttribute("type");0==t?(e.no&&e.no(),layer.close(n.index)):e.yes?e.yes(n.index):layer.close(n.index)};if(e.btn)for(var s=t[i]("layui-m-layerbtn")[0].children,r=s.length,o=0;o -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                - \ No newline at end of file diff --git a/route/static/layer/mobile/need/layer.css b/route/static/layer/mobile/need/layer.css deleted file mode 100755 index b9dbf2010..000000000 --- a/route/static/layer/mobile/need/layer.css +++ /dev/null @@ -1 +0,0 @@ -.layui-m-layer{position:relative;z-index:19891014}.layui-m-layer *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.layui-m-layermain,.layui-m-layershade{position:fixed;left:0;top:0;width:100%;height:100%}.layui-m-layershade{background-color:rgba(0,0,0,.7);pointer-events:auto}.layui-m-layermain{display:table;font-family:Helvetica,arial,sans-serif;pointer-events:none}.layui-m-layermain .layui-m-layersection{display:table-cell;vertical-align:middle;text-align:center}.layui-m-layerchild{position:relative;display:inline-block;text-align:left;background-color:#fff;font-size:14px;border-radius:5px;box-shadow:0 0 8px rgba(0,0,0,.1);pointer-events:auto;-webkit-overflow-scrolling:touch;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}@-webkit-keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}.layui-m-anim-scale{animation-name:layui-m-anim-scale;-webkit-animation-name:layui-m-anim-scale}@-webkit-keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}.layui-m-anim-up{-webkit-animation-name:layui-m-anim-up;animation-name:layui-m-anim-up}.layui-m-layer0 .layui-m-layerchild{width:90%;max-width:640px}.layui-m-layer1 .layui-m-layerchild{border:none;border-radius:0}.layui-m-layer2 .layui-m-layerchild{width:auto;max-width:260px;min-width:40px;border:none;background:0 0;box-shadow:none;color:#fff}.layui-m-layerchild h3{padding:0 10px;height:60px;line-height:60px;font-size:16px;font-weight:400;border-radius:5px 5px 0 0;text-align:center}.layui-m-layerbtn span,.layui-m-layerchild h3{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-m-layercont{padding:50px 30px;line-height:22px;text-align:center}.layui-m-layer1 .layui-m-layercont{padding:0;text-align:left}.layui-m-layer2 .layui-m-layercont{text-align:center;padding:0;line-height:0}.layui-m-layer2 .layui-m-layercont i{width:25px;height:25px;margin-left:8px;display:inline-block;background-color:#fff;border-radius:100%;-webkit-animation:layui-m-anim-loading 1.4s infinite ease-in-out;animation:layui-m-anim-loading 1.4s infinite ease-in-out;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-m-layerbtn,.layui-m-layerbtn span{position:relative;text-align:center;border-radius:0 0 5px 5px}.layui-m-layer2 .layui-m-layercont p{margin-top:20px}@-webkit-keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}@keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}.layui-m-layer2 .layui-m-layercont i:first-child{margin-left:0;-webkit-animation-delay:-.32s;animation-delay:-.32s}.layui-m-layer2 .layui-m-layercont i.layui-m-layerload{-webkit-animation-delay:-.16s;animation-delay:-.16s}.layui-m-layer2 .layui-m-layercont>div{line-height:22px;padding-top:7px;margin-bottom:20px;font-size:14px}.layui-m-layerbtn{display:box;display:-moz-box;display:-webkit-box;width:100%;height:50px;line-height:50px;font-size:0;border-top:1px solid #D0D0D0;background-color:#F2F2F2}.layui-m-layerbtn span{display:block;-moz-box-flex:1;box-flex:1;-webkit-box-flex:1;font-size:14px;cursor:pointer}.layui-m-layerbtn span[yes]{color:#40AFFE}.layui-m-layerbtn span[no]{border-right:1px solid #D0D0D0;border-radius:0 0 0 5px}.layui-m-layerbtn span:active{background-color:#F6F6F6}.layui-m-layerend{position:absolute;right:7px;top:10px;width:30px;height:30px;border:0;font-weight:400;background:0 0;cursor:pointer;-webkit-appearance:none;font-size:30px}.layui-m-layerend::after,.layui-m-layerend::before{position:absolute;left:5px;top:15px;content:'';width:18px;height:1px;background-color:#999;transform:rotate(45deg);-webkit-transform:rotate(45deg);border-radius:3px}.layui-m-layerend::after{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}body .layui-m-layer .layui-m-layer-footer{position:fixed;width:95%;max-width:100%;margin:0 auto;left:0;right:0;bottom:10px;background:0 0}.layui-m-layer-footer .layui-m-layercont{padding:20px;border-radius:5px 5px 0 0;background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn{display:block;height:auto;background:0 0;border-top:none}.layui-m-layer-footer .layui-m-layerbtn span{background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn span[no]{color:#FD482C;border-top:1px solid #c2c2c2;border-radius:0 0 5px 5px}.layui-m-layer-footer .layui-m-layerbtn span[yes]{margin-top:10px;border-radius:5px}body .layui-m-layer .layui-m-layer-msg{width:auto;max-width:90%;margin:0 auto;bottom:-150px;background-color:rgba(0,0,0,.7);color:#fff}.layui-m-layer-msg .layui-m-layercont{padding:10px 20px} \ No newline at end of file diff --git a/route/static/layer/skin/default/close.png b/route/static/layer/skin/default/close.png deleted file mode 100755 index c1162cba9..000000000 Binary files a/route/static/layer/skin/default/close.png and /dev/null differ diff --git a/route/static/layer/skin/default/close_hover.png b/route/static/layer/skin/default/close_hover.png deleted file mode 100755 index 5b9151dbd..000000000 Binary files a/route/static/layer/skin/default/close_hover.png and /dev/null differ diff --git a/route/static/layer/skin/default/icon-ext.png b/route/static/layer/skin/default/icon-ext.png deleted file mode 100755 index 15bc30025..000000000 Binary files a/route/static/layer/skin/default/icon-ext.png and /dev/null differ diff --git a/route/static/layer/skin/default/icon.png b/route/static/layer/skin/default/icon.png deleted file mode 100755 index 9afc7fda8..000000000 Binary files a/route/static/layer/skin/default/icon.png and /dev/null differ diff --git a/route/static/layer/skin/default/index.html b/route/static/layer/skin/default/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/layer/skin/default/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/layer/skin/default/layer.css b/route/static/layer/skin/default/layer.css deleted file mode 100755 index 08f3a0c9a..000000000 --- a/route/static/layer/skin/default/layer.css +++ /dev/null @@ -1 +0,0 @@ -.layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span,.layui-layer-title{text-overflow:ellipsis;white-space:nowrap}*html{background-image:url(about:blank);background-attachment:fixed}html #layuicss-skinlayercss{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+"px")}.layui-layer{-webkit-overflow-scrolling:touch;top:150px;left:0;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;box-shadow:1px 1px 50px rgba(0,0,0,.3)}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #b2b2b2;border:1px solid rgba(0,0,0,.1);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-load{background:url(loading-1.gif) center center no-repeat #eee}.layui-layer-ico{background:url(icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-move{display:none;position:fixed;*position:absolute;left:0;top:0;width:100%;height:100%;cursor:move;opacity:0;filter:alpha(opacity=0);background-color:#fff;z-index:2147483647}.layui-layer-resize{position:absolute;width:15px;height:15px;right:0;bottom:0;display:none;cursor:se-resize}.layui-layer{border-radius:2px;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}@-webkit-keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim{-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-01{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layer-anim-02{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-03{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layer-anim-04{-webkit-animation-name:rollIn;animation-name:rollIn}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layer-anim-05{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layer-anim-06{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);-ms-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-close{-webkit-animation-name:bounceOut;animation-name:bounceOut;-webkit-animation-duration:.2s;animation-duration:.2s}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;background-color:#f8f8f8;border-radius:2px 2px 0 0}.layui-layer-setwin{position:absolute;right:15px;*right:0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2e2d3c;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2d93ca}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:0 -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{background-image:url(close.png);position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;transition:All .4s ease-in-out;*right:-18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-image:url(close_hover.png);transform:rotate(180deg)}.layui-layer-btn{background-color:#f6f8f8;border-top:1px solid #edf1f2;float:left;padding:9px 18px 10px;pointer-events:auto;text-align:right;width:100%;user-select:none;-webkit-user-select:none}.layui-layer-btn a{background-color:#20a53a;border-radius:3px;color:#fff;cursor:pointer;float:right;font-size:12px;font-weight:700;height:30px;line-height:28px;margin-left:8px;padding:0 12px;text-decoration:none}.layui-layer-btn a:hover{background-color:#10952a;border-color:#398439;text-decoration:none}.layui-layer-btn a:active{opacity:.8}.layui-layer-btn .layui-layer-btn1{border-color:#cbcbcb;background-color:#cbcbcb;color:#fff}.layui-layer-btn .layui-layer-btn1:hover{background-color:#c9302c}.layui-layer-btn-l{text-align:left}.layui-layer-btn-c{text-align:center}.layui-layer-dialog{min-width:350px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;overflow:hidden;font-size:14px;overflow-x:hidden;overflow-y:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;_left:-40px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8d8d8d;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #d3d4d3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:0}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0;box-shadow:none;border:0}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(loading-2.gif) no-repeat}.layui-layer-tips{background:0;box-shadow:none;border:0}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:5px 10px;font-size:12px;_float:left;border-radius:2px;box-shadow:1px 1px 3px rgba(0,0,0,.2);background-color:#000;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#000}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:1px;border-bottom-style:solid;border-bottom-color:#000}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476a7;color:#fff;border:0}.layui-layer-lan .layui-layer-btn{padding:10px;text-align:right;border-top:1px solid #e9e7e7}.layui-layer-lan .layui-layer-btn a{background:#bbb5b5;border:0}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#c9c5c5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:0}.layui-layer-molv .layui-layer-btn a{background:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92b8b1}.layui-layer-iconext{background:url(icon-ext.png) no-repeat}.layui-layer-prompt .layui-layer-input{display:block;width:220px;height:30px;margin:0 auto;line-height:30px;padding:0 5px;border:1px solid #ccc;box-shadow:1px 1px 5px rgba(0,0,0,.1) inset;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px}.layui-layer-prompt .layui-layer-content{padding:20px}.layui-layer-prompt .layui-layer-btn{padding-top:0}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;border-bottom:1px solid #ccc;background-color:#eee;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;cursor:default;overflow:hidden}.layui-layer-tab .layui-layer-title span.layui-layer-tabnow{height:43px;border-left:1px solid #ccc;border-right:1px solid #ccc;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.xubox_tab_layer{display:block}.xubox_tabclose{position:absolute;right:10px;top:5px;cursor:pointer}.layui-layer-photos{-webkit-animation-duration:1s;animation-duration:1s}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}@media screen and (max-width:1100px){.layui-layer-iframe{overflow-y:auto;-webkit-overflow-scrolling:touch}} \ No newline at end of file diff --git a/route/static/layer/skin/default/loading-0.gif b/route/static/layer/skin/default/loading-0.gif deleted file mode 100755 index fe1097f72..000000000 Binary files a/route/static/layer/skin/default/loading-0.gif and /dev/null differ diff --git a/route/static/layer/skin/default/loading-1.gif b/route/static/layer/skin/default/loading-1.gif deleted file mode 100755 index a6abc9e57..000000000 Binary files a/route/static/layer/skin/default/loading-1.gif and /dev/null differ diff --git a/route/static/layer/skin/default/loading-2.gif b/route/static/layer/skin/default/loading-2.gif deleted file mode 100755 index e1691ac98..000000000 Binary files a/route/static/layer/skin/default/loading-2.gif and /dev/null differ diff --git a/route/static/layer/skin/index.html b/route/static/layer/skin/index.html deleted file mode 100755 index 35c63e8b1..000000000 --- a/route/static/layer/skin/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
                -

                目标URL

                -

                发送域名

                -

                内容替换

                -
                -
                  -
                • 目标Url必需是可以访问的,否则将直接502
                • -
                • 默认本站点所有域名访问将被传递到目标服务器,请确保目标服务器已绑定域名
                • -
                • 若您是被动代理,请在发送域名处填写上目标站点的域名
                • -
                • 若您不需要内容替换功能,请直接留空
                • -
                • 可通过purge清理指定URL的缓存,示例:http://test.com/purge/test.png
                • -
                -
                \ No newline at end of file diff --git a/route/static/layer/skin/layer.css b/route/static/layer/skin/layer.css deleted file mode 100755 index 6985801d3..000000000 --- a/route/static/layer/skin/layer.css +++ /dev/null @@ -1,7 +0,0 @@ -/*! - - @Name: layer's style - @Author: 贤心 - @Blog: sentsin.com - - */html{background-image:url(about:blank);background-attachment:fixed}html #layui_layer_skinlayercss{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+"px")}.layui-layer{top:150px;left:50%;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;box-shadow:1px 1px 50px rgba(0,0,0,.3);-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #b2b2b2;border:1px solid rgba(0,0,0,.3);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-moves{position:absolute;border:3px solid #666;border:3px solid rgba(0,0,0,.5);cursor:move;background-color:#fff;background-color:rgba(255,255,255,.3);filter:alpha(opacity=50)}.layui-layer-load{background:url(default/loading-0.gif) center center no-repeat #fff}.layui-layer-ico{background:url(default/icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}@-webkit-keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layui-anim{-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.03);transform:scale(1.03)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.03);-ms-transform:scale(1.03);transform:scale(1.03)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layui-anim-close{-webkit-animation-name:bounceOut;animation-name:bounceOut;-webkit-animation-duration:.2s;animation-duration:.2s}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layui-anim-01{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layui-anim-02{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layui-anim-03{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layui-anim-04{-webkit-animation-name:rollIn;animation-name:rollIn}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-anim-05{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layui-anim-06{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-layer-title{padding:0 80px 0 10px;height:40px;line-height:40px;border-bottom:1px solid #edf1f2;font-size:14px;color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background-color:#f6f8f8}.layui-layer-setwin{position:absolute;right:10px;*right:0;top:10px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:14px;height:14px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#919191;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2d93ca}.layui-layer-setwin .layui-layer-max{background-position:-45px -55px}.layui-layer-setwin .layui-layer-max:hover{background-position:-95px -55px}.layui-layer-setwin .layui-layer-maxmin{background-position:-68px -55px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-118px -55px}.layui-layer-setwin .layui-layer-close1{width:14px;height:14px;background-position:-5px -55px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{background-position:-25px -55px}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-23px;top:-23px;width:30px;height:30px;margin-left:0;background-position:-149px -44px;*right:-18px;_right:-15px;_top:-23px;_width:14px;_height:14px;_background-position:-5px -55px}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -44px;_background-position:-25px -55px}.layui-layer-btn{background-color:#f6f8f8;border-top:1px solid #edf1f2;float:left;padding:9px 20px 10px;pointer-events:auto;text-align:right;width:100%}.layui-layer-btn a{height:30px;line-height:30px;margin:0 8px;padding:0 20px;background:#5fbfe7;color:#fff;font-size:14px;font-weight:700;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.7}.layui-layer-btn .layui-layer-btn1{background:#a6bbce}.layui-layer-dialog{min-width:240px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;font-size:14px;overflow:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:15px;left:15px;_left:-50px;width:39px;height:39px}.layui-layer-ico1{background-position:-46px 0}.layui-layer-ico2{background-position:-93px 0}.layui-layer-ico3{background-position:-145px 0}.layui-layer-ico4{background-position:-191px 0}.layui-layer-ico5{background-position:-239px 0}.layui-layer-ico6{background-position:-287px 0}.layui-layer-rim{border:6px solid #8d8d8d;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:170px;border-radius:3px;border:5px solid #8d8d8d;border:5px solid rgba(0,0,0,.4)}.layui-layer-msg .layui-layer-content .layui-layer-ico{top:10px}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:0}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:22px 20px 22px 65px;text-align:left}.layui-layer-msg .layui-layer-padding{padding:17px 20px 17px 65px}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0}.layui-layer-iframe .layui-layer-content{overflow:hidden}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0;box-shadow:none;border:0}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(default/loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(default/loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(default/loading-2.gif) no-repeat}.layui-layer-tips{background:0;box-shadow:none;border:0}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:5px 10px;font-size:12px;_float:left;border-radius:3px;box-shadow:1px 1px 3px rgba(0,0,0,.3);background-color:#F90;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#F90}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:1px;border-bottom-style:solid;border-bottom-color:#F90}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476a7;color:#fff;border:0}.layui-layer-lan .layui-layer-btn{padding:10px;text-align:right;border-top:1px solid #e9e7e7}.layui-layer-lan .layui-layer-btn a{background:#bbb5b5}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#c9c5c5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:0}.layui-layer-molv .layui-layer-btn a{background:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92b8b1} \ No newline at end of file diff --git a/route/static/layer/skin/layer.ext.css b/route/static/layer/skin/layer.ext.css deleted file mode 100755 index 45a32a80e..000000000 --- a/route/static/layer/skin/layer.ext.css +++ /dev/null @@ -1,8 +0,0 @@ -/*! - - @Name: layer拓展样式 - @Date: 2012.12.13 - @Author: 贤心 - @blog: sentsin.com - - */.layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span{text-overflow:ellipsis;white-space:nowrap}.layui-layer-iconext{background:url(default/icon-ext.png) no-repeat}html #layui_layer_skinlayerextcss{display:none;position:absolute;width:1989px}.layui-layer-prompt .layui-layer-input{display:block;width:220px;height:30px;margin:0 auto;line-height:30px;padding:0 5px;border:1px solid #ccc;box-shadow:1px 1px 5px rgba(0,0,0,.1) inset;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;border-bottom:1px solid #ccc;background-color:#eee;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;cursor:default;overflow:hidden}.layui-layer-tab .layui-layer-title span.layui-layer-tabnow{height:36px;border-left:1px solid #ccc;border-right:1px solid #ccc;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.xubox_tab_layer{display:block}.xubox_tabclose{position:absolute;right:10px;top:5px;cursor:pointer}.layui-layer-photos{-webkit-animation-duration:1s;animation-duration:1s;background:url(default/xubox_loading1.gif) center center no-repeat #000}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal} \ No newline at end of file diff --git a/route/static/logo.png b/route/static/logo.png deleted file mode 100644 index 34548a078..000000000 Binary files a/route/static/logo.png and /dev/null differ diff --git a/route/static/mdw.jpg b/route/static/mdw.jpg deleted file mode 100644 index f21a10aca..000000000 Binary files a/route/static/mdw.jpg and /dev/null differ diff --git a/route/templates/default/close.html b/route/templates/default/close.html deleted file mode 100755 index 94abce6fb..000000000 --- a/route/templates/default/close.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - 抱歉,管理员已关闭面板 - - -

                开启命令

                -

                mw open

                -
                - - - \ No newline at end of file diff --git a/route/templates/default/config.html b/route/templates/default/config.html deleted file mode 100755 index 655630e3c..000000000 --- a/route/templates/default/config.html +++ /dev/null @@ -1,196 +0,0 @@ -{% extends "layout.html" %} -{% block content %} -
                -
                -
                -
                首页/面板设置
                -
                -
                -
                -
                - 关闭面板 -
                - - -
                -
                -
                - 开发模式 -
                - - -
                -
                - -
                - 监听IPv6 -
                - - -
                -
                -
                -
                -
                -

                设置

                -
                -

                为了提高安全,修改面板密码!

                -
                -
                -
                -

                - 别名 - - - 面板名称 -

                - -

                - 服务器IP - - - 默认为外网IP,若您在本地虚拟机测试,请填写虚拟机内网IP! -

                - -

                - 面板端口 - - - 建议端口范围7200 - 65535 -

                - -

                - 安全入口 - - - 面板管理入口,设置后只能通过指定安全入口登录面板,如: /abc -

                - -

                - 默认建站目录 - - - 新创建的站点,默认将保存到该目录的下级目录! -

                - -

                - 默认备份目录 - - - 网站和数据库的备份目录! -

                - -

                - 服务器时间 - - - 设置/同步当前服务器时间 -

                -

                - 面板用户 - - - 设置面板账号 -

                -

                - 面板密码 - - - 设置面板密码 -

                - -
                - -

                安全

                -
                - -

                - 绑定域名 - - - 为面板绑定一个访问域名,注意:一旦绑定域名,只能通过域名访问面板 -

                - -

                - 面板SSL - - - - 为面板设置https协议访问,提升面板访问安全性 -

                - -

                - BasicAuth认证 - - - 为面板增加一道基于BasicAuth的认证服务,有效防止面板被扫描 -

                - -

                - API接口 - - - - 提供面板API接口访问的支持 -

                - -

                - 未认证响应状态 - - - 用于在未登录且未正确输入安全入口时的响应,可用于隐藏面板特征 -

                - -

                - 临时访问授权 - - 为非管理员临时提供面板访问权限 -

                - -

                - 二次验证 - - - - 二次验证,加强安全登录 -

                -
                - - -

                通知

                - -
                - -

                - TG机器人通知 - - - - Telegram Bot机器人通知【国内可能无法使用】 -

                - -

                - 邮件通知 - - - - 邮件通知 -

                - -
                - - -
                -
                -
                - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/control.html b/route/templates/default/control.html deleted file mode 100755 index e6fb4da21..000000000 --- a/route/templates/default/control.html +++ /dev/null @@ -1,176 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -
                -
                -
                -
                - 首页监控/系统监控 -
                -
                -
                -
                -
                - 开启监控 -
                -
                -
                -
                -
                - 保存天数: - -
                -
                - -
                -
                -
                -
                -
                - 仅统计外网 -
                -
                -
                - - -
                -
                -
                -
                -
                -
                -
                -

                平均负载

                -
                - 区间检索: - 昨天 - 今天 - 最近7天 - 最近30天 -
                - 自定义时间 -
                - 时间范围 - - -
                提交
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -

                CPU

                -
                - 区间检索: - 昨天 - 今天 - 最近7天 - 最近30天 -
                - 自定义时间 -
                - 时间范围 - - -
                提交
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -

                内存

                -
                - 区间检索: - 昨天 - 今天、 - 最近7天 - 最近30天 -
                - 自定义时间 -
                - 时间范围 - - -
                提交
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -

                磁盘IO

                -
                - $data['lan']['S1'] - 昨天 - 今天 - 最近7天 - 最近30天 -
                - 自定义时间 -
                - 时间范围 - - -
                提交
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -

                网络IO

                -
                - $data['lan']['S1'] - 昨天 - 今天 - 最近7天 - 最近30天 -
                - 自定义时间 -
                - 时间范围 - - -
                提交
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -
                -
                - - - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/crontab.html b/route/templates/default/crontab.html deleted file mode 100755 index 7284ec8ee..000000000 --- a/route/templates/default/crontab.html +++ /dev/null @@ -1,155 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -
                -
                -
                -
                - 首页/计划任务 -
                -
                -
                -
                -

                添加计划任务

                -
                -
                -
                - 任务类型 - - *任务类型包含以下部分:Shell脚本、备份网站、备份数据库、备份目录、日志切割、释放内存、访问URL -
                -
                - 任务名称 -
                -
                -
                - 执行周期 - -
                - -
                - - 小时 -
                -
                - - 分钟 -
                -
                -
                -
                - 脚本内容 -
                -
                -
                -
                添加任务
                -
                -
                  -
                • 当添加完备份任务,应该手动运行一次,并检查备份包是否完整
                • -
                • 磁盘容量不够、数据库密码错误、网络不稳定等原因,可能导致数据备份不完整
                • -
                -
                - - - - - - - - - - - - - - - - -
                -
                -
                -

                任务列表

                - -
                -
                -
                - - - - - - - - - - - - - - -
                任务名称状态周期执行时机保存数量备份到添加时间操作
                - - - - - - - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/files.html b/route/templates/default/files.html deleted file mode 100755 index e074d3004..000000000 --- a/route/templates/default/files.html +++ /dev/null @@ -1,113 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} -
                -
                -
                -
                -
                -
                -
                - - - - -
                -
                - -
                -
                -
                - - - - - -
                - - -
                -
                -
                - -
                - -
                -
                -
                - - - - - - - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/firewall.html b/route/templates/default/firewall.html deleted file mode 100755 index e0b1e5224..000000000 --- a/route/templates/default/firewall.html +++ /dev/null @@ -1,117 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - - -
                -
                -
                -
                - 首页/系统安全 -
                - -
                -
                -
                - -
                -
                - SSH管理 -
                -
                -
                -
                - -
                - SSH端口: - -
                -
                - -
                -
                -
                -
                - 启用禁ping -
                - -
                -
                -
                - -
                -
                -
                - 启用防火墙 -
                - -
                -
                -
                -
                - -
                -
                - -
                -
                -
                -
                -

                防火墙

                -
                - -
                -
                - - - - - - 说明: 支持放行端口范围,如: 3000:3500 -
                -
                -
                -
                -
                -
                -
                -
                -
                - -
                -
                - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/index.html b/route/templates/default/index.html deleted file mode 100755 index dd74bd55d..000000000 --- a/route/templates/default/index.html +++ /dev/null @@ -1,171 +0,0 @@ -{% extends "layout.html" %} {% block content %} -
                - -
                -
                - 系统:正在获取中...   -
                - - {{config.version}} - 更新 | - - 重启 - -
                - -
                - -
                - -
                - -
                -
                -

                状态

                -
                -
                -
                  -
                • -

                  负载状态?

                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  0%
                  -
                  -

                  获取中...

                  -
                • -
                • -

                  CPU使用率

                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  0%
                  -
                  -

                  获取中...

                  -
                • -
                • -

                  内存使用率

                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  0%
                  - -
                  -
                  -

                  获取中...

                  -
                • -
                -
                -
                - - -
                -
                概览
                -
                - -
                -
                - -
                -
                -
                -
                软件
                -
                -
                - -
                -
                -
                -
                -
                -
                - -
                -
                -
                -
                -
                - 流量 - 磁盘IO -
                - -
                - - -
                -
                - -
                -
                -
                -

                上行

                0
                -

                下行

                0
                -

                总发送

                0
                -

                总接收

                0
                -
                -
                -
                - -
                -
                -

                读取

                0
                -

                写入

                0
                -

                每秒读写

                0
                -

                读写IO延迟

                0
                -
                -
                -
                -
                - -
                -
                -
                -
                -
                - - - - - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/layout.html b/route/templates/default/layout.html deleted file mode 100755 index f86b59826..000000000 --- a/route/templates/default/layout.html +++ /dev/null @@ -1,208 +0,0 @@ - - - - - - - -{{data['title']}} - - - - - - - - -{% for menu in data['hook_menu'] %} -{% if menu['css_path'] and data['hook_tag'] == menu['name'] %} - - -{% endif %} -{% endfor %} - - -{% for menu in data['hook_global_static'] %} -{% if menu['css_path'] %} - -{% endif %} -{% endfor %} - - - -
                -
                - - - - - - - - - - - - - - - - - - - - - - - - - - - - {% block content %}{% endblock %} - -
                -
                - - - - - - - - - - - -{% for menu in data['hook_menu'] %} -{% if menu['js_path'] and data['hook_tag'] == menu['name'] %} - -{% endif %} -{% endfor %} - - -{% for menu in data['hook_global_static'] %} -{% if menu['js_path'] %} - -{% endif %} -{% endfor %} - - - - - - - - - \ No newline at end of file diff --git a/route/templates/default/login.html b/route/templates/default/login.html deleted file mode 100755 index aaf5f544a..000000000 --- a/route/templates/default/login.html +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - - - - -{{data['title']}} - - - - - - -
                - -
                - - - - - - - - \ No newline at end of file diff --git a/route/templates/default/logs.html b/route/templates/default/logs.html deleted file mode 100644 index ac3e0b876..000000000 --- a/route/templates/default/logs.html +++ /dev/null @@ -1,189 +0,0 @@ -{% extends "layout.html" %} -{% block content %} - - - -
                -
                -
                -
                -
                面板日志
                -
                日志审计
                -
                -
                - - -
                - - -
                -
                -
                -
                - - -
                -
                -
                - - - - - - - - -
                编号操作类型详情操作时间
                -
                -
                -
                -
                1共1条
                -
                -
                - - - -
                -
                -
                - -
                -
                -
                -
                -
                - -
                -
                - -
                -
                -
                - - - - - - - - - - - - -
                用户来源端口时间
                -
                -
                -
                -
                -
                第 1 页
                -
                -
                -
                -
                - -
                -
                -
                -
                -
                -
                - - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/path.html b/route/templates/default/path.html deleted file mode 100755 index 7998d53fb..000000000 --- a/route/templates/default/path.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - 安全入口校验失败 - - -

                请使用正确的入口登录面板

                -

                错误原因:当前新安装的已经开启了安全入口登录,新装机器都会随机一个8位字符的安全入口名称,亦可以在面板设置处修改,如您没记录或不记得了,可以使用以下方式解决

                -

                解决方法:在SSH终端输入以下一种命令来解决

                -

                1.查看面板入口:mw default

                -

                2.关闭安全入口:mw close_admin_path

                -

                注意:【关闭安全入口】将使您的面板登录地址被直接暴露在互联网上,非常危险,请谨慎操作

                - - \ No newline at end of file diff --git a/route/templates/default/plugin_menu.html b/route/templates/default/plugin_menu.html deleted file mode 100644 index 647c28616..000000000 --- a/route/templates/default/plugin_menu.html +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "layout.html" %} -{% block content %} -{{data['plugin_content']|safe }} -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/site.html b/route/templates/default/site.html deleted file mode 100755 index aa91828b4..000000000 --- a/route/templates/default/site.html +++ /dev/null @@ -1,75 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} -
                -
                -
                -
                - 首页/网站 -
                - -
                -
                -
                -

                创建站点时会自动创建权限配置,统一使用www用户。

                -
                -   - - - - - - - -
                - -
                - -
                -
                - - - - - - - - - - - - - - -
                域名网站状态备份网站目录到期日期备注操作
                -
                - - -
                - 站点分类: - -
                -
                -
                -
                -
                - - - - - -{% endblock %} \ No newline at end of file diff --git a/route/templates/default/soft.html b/route/templates/default/soft.html deleted file mode 100755 index 21f31853f..000000000 --- a/route/templates/default/soft.html +++ /dev/null @@ -1,58 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -
                -
                -
                -
                - 首页/软件管理 -
                - -
                -
                -
                -
                - - - - - -
                - - - - - - - - - - - - -
                软件名称说明位置状态首页显示操作
                - -
                -
                -
                -
                -
                - - - - - - - -{% endblock %} \ No newline at end of file diff --git a/route/templates/index.html b/route/templates/index.html deleted file mode 100644 index b2d525b29..000000000 --- a/route/templates/index.html +++ /dev/null @@ -1 +0,0 @@ -index \ No newline at end of file diff --git a/scripts/backup.py b/scripts/backup.py index 5ecf5811e..fbec67091 100755 --- a/scripts/backup.py +++ b/scripts/backup.py @@ -6,21 +6,18 @@ import sys import os import re +import time if sys.platform != 'darwin': os.chdir('/www/server/mdserver-web') +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) -chdir = os.getcwd() -sys.path.append(chdir + '/class/core') - -# reload(sys) -# sys.setdefaultencoding('utf-8') - - -import mw -import db -import time +import core.mw as mw +import core.db as db class backupTools: diff --git a/scripts/init.d/mw.service.tpl b/scripts/init.d/mw.service.tpl index aa21242ac..8c92d1214 100755 --- a/scripts/init.d/mw.service.tpl +++ b/scripts/init.d/mw.service.tpl @@ -6,7 +6,7 @@ After=network.target Type=simple WorkingDirectory={$SERVER_PATH} EnvironmentFile={$SERVER_PATH}/scripts/init.d/service.sh -ExecStart=gunicorn -c setting.py app:app +ExecStart=cd web && gunicorn -c setting.py app:app ExecStop=kill -HUP $MAINID ExecReload=kill -HUP $MAINID KillMode=process diff --git a/scripts/init.d/mw.tpl b/scripts/init.d/mw.tpl index ee4c491d9..72e8f9183 100755 --- a/scripts/init.d/mw.tpl +++ b/scripts/init.d/mw.tpl @@ -28,13 +28,13 @@ WORKING='[\033[34m*\033[0m]' PATH=/usr/local/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export LANG=en_US.UTF-8 -mw_path={$SERVER_PATH} -ROOT_PATH=$(dirname "$mw_path") -PATH=$PATH:$mw_path/bin +PANEL_DIR={$SERVER_PATH} +ROOT_PATH=$(dirname "$PANEL_DIR") +PATH=$PATH:${PANEL_DIR}/bin -if [ -f $mw_path/bin/activate ];then - source $mw_path/bin/activate +if [ -f ${PANEL_DIR}/bin/activate ];then + source ${PANEL_DIR}/bin/activate if [ "$?" != "0" ];then echo "load local python env fail!" fi @@ -45,8 +45,8 @@ mw_start_panel() isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'` if [ "$isStart" == '' ];then echo -e "starting mw-panel... \c" - cd $mw_path && gunicorn -c setting.py app:app - port=$(cat ${mw_path}/data/port.pl) + cd ${PANEL_DIR}/web && gunicorn -c setting.py app:app + port=$(cat ${PANEL_DIR}/data/port.pl) isStart="" while [[ "$isStart" == "" ]]; do @@ -61,7 +61,7 @@ mw_start_panel() if [ "$isStart" == '' ];then echo -e "\033[31mfailed\033[0m" echo '------------------------------------------------------' - tail -n 20 ${mw_path}/logs/error.log + tail -n 20 ${PANEL_DIR}/logs/panel_error.log echo '------------------------------------------------------' echo -e "\033[31mError: mw-panel service startup failed.\033[0m" return; @@ -75,16 +75,16 @@ mw_start_panel() mw_start_task() { - isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}') + isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}') if [ "$isStart" == '' ];then echo -e "starting mw-tasks... \c" - cd $mw_path && python3 task.py >> ${mw_path}/logs/task.log 2>&1 & + cd ${PANEL_DIR} && python3 panel_task.py >> ${PANEL_DIR}/logs/panel_task.log 2>&1 & sleep 0.3 - isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}') + isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}') if [ "$isStart" == '' ];then echo -e "\033[31mfailed\033[0m" echo '------------------------------------------------------' - tail -n 20 $mw_path/logs/task.log + tail -n 20 ${PANEL_DIR}/logs/panel_task.log echo '------------------------------------------------------' echo -e "\033[31mError: mw-tasks service startup failed.\033[0m" return; @@ -101,21 +101,28 @@ mw_start() mw_start_panel } -# /www/server/mdserver-web/tmp/panelTask.pl && service mw restart_task +# /www/server/mdserver-web/logs/panel_task.lock && service mw restart_task mw_stop_task() { - if [ -f $mw_path/tmp/panelTask.pl ];then + if [ -f ${PANEL_DIR}/logs/panel_task.lock ];then echo -e "\033[32mthe task is running and cannot be stopped\033[0m" - exit 0 + return 0 fi echo -e "stopping mw-tasks... \c"; - pids=$(ps aux | grep 'task.py'|grep -v grep|awk '{print $2}') + pids=$(ps aux | grep 'panel_task.py'|grep -v grep|awk '{print $2}') arr=($pids) for p in ${arr[@]} do kill -9 $p > /dev/null 2>&1 done + + zzpids=$(ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]' | awk '{print $2}') + arr=($zzpids) + for p in ${arr[@]} + do + kill -9 $p > /dev/null 2>&1 + done echo -e "\033[32mdone\033[0m" } @@ -123,18 +130,25 @@ mw_stop_panel() { echo -e "stopping mw-panel... \c"; - pidfile=${mw_path}/logs/mw.pid + pidfile=${PANEL_DIR}/logs/mw.pid if [ -f $pidfile ];then pid=`cat $pidfile` kill -9 $pid > /dev/null 2>&1 rm -f $pidfile fi - arr=`ps aux|grep 'gunicorn -c setting.py app:app'|grep -v grep|awk '{print $2}'` - for p in ${arr[@]} + ARR=`ps aux|grep 'gunicorn -c setting.py app:app'|grep -v grep|awk '{print $2}'` + for p in ${ARR[@]} do kill -9 $p > /dev/null 2>&1 done + + PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'` + ARR=($PLIST) + for i in ${ARR[@]} + do + kill -9 $i > /dev/null 2>&1 + done echo -e "\033[32mdone\033[0m" } @@ -154,7 +168,7 @@ mw_status() echo -e "\033[31mmw not running\033[0m" fi - isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}') + isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}') if [ "$isStart" != '' ];then echo -e "\033[32mmw-task (pid $isStart) already running\033[0m" else @@ -174,7 +188,7 @@ mw_reload() do kill -9 $p done - cd $mw_path && gunicorn -c setting.py app:app + cd ${PANEL_DIR}/web && gunicorn -c setting.py app:app isStart=`ps aux|grep 'gunicorn -c setting.py app:app'|grep -v grep|awk '{print $2}'` if [ "$isStart" == '' ];then echo -e "\033[31mfailed\033[0m" @@ -192,27 +206,25 @@ mw_reload() } mw_close(){ - echo 'True' > $mw_path/data/close.pl + cd ${PANEL_DIR} && python3 panel_tools.py cli 14 } mw_open() { - if [ -f $mw_path/data/close.pl ];then - rm -rf $mw_path/data/close.pl - fi + cd ${PANEL_DIR} && python3 panel_tools.py cli 15 } mw_unbind_domain() { - if [ -f $mw_path/data/bind_domain.pl ];then - rm -rf $mw_path/data/bind_domain.pl + if [ -f ${PANEL_DIR}/data/bind_domain.pl ];then + rm -rf ${PANEL_DIR}/data/bind_domain.pl fi } mw_unbind_ssl() { - if [ -f $mw_path/local ];then - rm -rf $mw_path/local + if [ -f ${PANEL_DIR}/local ];then + rm -rf ${PANEL_DIR}/local fi if [ -f $mw_path/nginx ];then @@ -226,12 +238,12 @@ mw_unbind_ssl() error_logs() { - tail -n 100 $mw_path/logs/error.log + tail -n 100 ${PANEL_DIR}/logs/panel_error.log } mw_update() { - if [ -f $mw_path/task.py ];then + if [ -f ${PANEL_DIR}/task.py ];then echo "与后续版本差异太大,不再提供更新" exit 0 fi @@ -251,7 +263,7 @@ mw_update() mw_update_dev() { - if [ -f $mw_path/task.py ];then + if [ -f ${PANEL_DIR}/task.py ];then echo "与后续版本差异太大,不再提供更新" exit 0 fi @@ -267,14 +279,14 @@ mw_update_dev() else curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh | bash fi - cd /www/server/mdserver-web + cd ${PANEL_DIR} } mw_update_venv() { - rm -rf /www/server/mdserver-web/bin - rm -rf /www/server/mdserver-web/lib64 - rm -rf /www/server/mdserver-web/lib + rm -rf ${PANEL_DIR}/bin + rm -rf ${PANEL_DIR}/lib64 + rm -rf ${PANEL_DIR}/lib LOCAL_ADDR=common cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"") @@ -287,7 +299,7 @@ mw_update_venv() else curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh | bash fi - cd /www/server/mdserver-web + cd ${PANEL_DIR} } mw_mirror() @@ -303,7 +315,7 @@ mw_mirror() else bash <(curl --insecure -sSL https://gitee.com/SuperManito/LinuxMirrors/raw/main/ChangeMirrors.sh) fi - cd ${ROOT_PATH}/mdserver-web + cd ${ROOT_PATH} } mw_install_app() @@ -338,14 +350,15 @@ mw_debug(){ mw_force_kill port=7200 - if [ -f $mw_path/data/port.pl ];then - port=$(cat $mw_path/data/port.pl) + if [ -f ${PANEL_DIR}/data/port.pl ];then + port=$(cat ${PANEL_DIR}/data/port.pl) fi - if [ -d ${ROOT_PATH}/mdserver-web ];then - cd ${ROOT_PATH}/mdserver-web + if [ -d ${PANEL_DIR}/web ];then + cd ${PANEL_DIR}/web fi - gunicorn -b :$port -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + # gunicorn -b :$port -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app + gunicorn -b :$port -k eventlet -w 1 app:app } @@ -445,7 +458,7 @@ mw_connect_pgdb(){ fi - pwd=$(cd ${ROOT_PATH}/mdserver-web && python3 ${ROOT_PATH}/mdserver-web/plugins/postgresql/index.py root_pwd) + pwd=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/plugins/postgresql/index.py root_pwd) export PGPASSWORD=${pwd} echo "${ROOT_PATH}/postgresql/bin/psql -U postgres -W" ${ROOT_PATH}/postgresql/bin/psql -U postgres -W @@ -464,7 +477,7 @@ mw_mongodb(){ AUTH_STR="" if [[ "$MGDB_AUTH" == "enabled" ]];then - pwd=$(cd ${ROOT_PATH}/mdserver-web && python3 ${ROOT_PATH}/mdserver-web/plugins/mongodb/index.py root_pwd) + pwd=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/plugins/mongodb/index.py root_pwd) AUTH_STR="-u root -p ${pwd}" fi @@ -493,14 +506,81 @@ mw_redis(){ } mw_venv(){ - cd ${ROOT_PATH}/mdserver-web && source bin/activate + cd ${PANEL_DIR} && source bin/activate } mw_clean_lib(){ - cd ${ROOT_PATH}/mdserver-web && rm -rf lib - cd ${ROOT_PATH}/mdserver-web && rm -rf lib64 - cd ${ROOT_PATH}/mdserver-web && rm -rf bin - cd ${ROOT_PATH}/mdserver-web && rm -rf include + cd ${PANEL_DIR} && rm -rf lib + cd ${PANEL_DIR} && rm -rf lib64 + cd ${PANEL_DIR} && rm -rf bin + cd ${PANEL_DIR} && rm -rf include +} + +mw_default(){ + cd ${PANEL_DIR} + port=7200 + scheme=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py panel_ssl_type) + + if [ -f ${PANEL_DIR}/data/port.pl ];then + port=$(cat ${PANEL_DIR}/data/port.pl) + fi + + if [ ! -f ${PANEL_DIR}/data/default.pl ];then + echo -e "\033[33mInstall Failed\033[0m" + exit 1 + fi + + password=$(cat ${PANEL_DIR}/data/default.pl) + + admin_path=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py admin_path) + if [ "$address" == "" ];then + v4=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py getServerIp 4) + v6=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py getServerIp 6) + + if [ "$v4" != "" ] && [ "$v6" != "" ]; then + + if [ ! -f ${PANEL_DIR}/data/ipv6.pl ];then + echo 'True' > ${PANEL_DIR}/data/ipv6.pl + mw_stop + mw_start + fi + + address="MW-PANEL-URL-IPV4: ${scheme}://$v4:$port$admin_path \nMW-PANEL-URL-IPV6: ${scheme}://[$v6]:$port$admin_path" + elif [ "$v4" != "" ]; then + address="MW-PANEL-URL: ${scheme}://$v4:$port$admin_path" + elif [ "$v6" != "" ]; then + + if [ ! -f ${PANEL_DIR}/data/ipv6.pl ];then + # Need to restart ipv6 to take effect + echo 'True' > ${PANEL_DIR}/data/ipv6.pl + mw_stop + mw_start + fi + address="MW-PANEL-URL: ${scheme}://[$v6]:$port$admin_path" + else + address="MW-PANEL-URL: ${scheme}://you-network-ip:$port$admin_path" + fi + else + address="MW-PANEL-URL: ${scheme}://$address:$port$admin_path" + fi + + # bind domain check + panel_bind_domain=$(cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py panel_bind_domain) + if [ "$panel_bind_domain" != "" ];then + address="MW-PANEL-URL: ${scheme}://$panel_bind_domain:$port$admin_path\n${address}" + fi + + show_panel_ip="$port|" + echo -e "==================================================================" + echo -e "\033[32mMW-PANEL DEFAULT INFO!\033[0m" + echo -e "==================================================================" + echo -e "$address" + echo -e `cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py username` + echo -e `cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py password` + echo -e "\033[33mWarning:\033[0m" + echo -e "\033[33mIf you cannot access the panel. \033[0m" + echo -e "\033[33mrelease the following port (${show_panel_ip}80|443|22) in the security group.\033[0m" + echo -e "==================================================================" } case "$1" in @@ -535,77 +615,8 @@ case "$1" in 'mongodb') mw_mongodb;; 'venv') mw_update_venv;; 'clean_lib') mw_clean_lib;; - 'default') - cd $mw_path - port=7200 - scheme=http - - if [ -f $mw_path/ssl/choose.pl ];then - scheme=https - fi - - if [ -f $mw_path/data/port.pl ];then - port=$(cat $mw_path/data/port.pl) - fi - - if [ ! -f $mw_path/data/default.pl ];then - echo -e "\033[33mInstall Failed\033[0m" - exit 1 - fi - - password=$(cat $mw_path/data/default.pl) - if [ -f $mw_path/data/domain.conf ];then - address=$(cat $mw_path/data/domain.conf) - fi - if [ -f $mw_path/data/admin_path.pl ];then - auth_path=$(cat $mw_path/data/admin_path.pl) - fi - - if [ "$address" == "" ];then - v4=$(python3 $mw_path/tools.py getServerIp 4) - v6=$(python3 $mw_path/tools.py getServerIp 6) - - if [ "$v4" != "" ] && [ "$v6" != "" ]; then - - if [ ! -f $mw_path/data/ipv6.pl ];then - echo 'True' > $mw_path/data/ipv6.pl - mw_stop - mw_start - fi - - address="MW-Panel-Url-Ipv4: ${scheme}://$v4:$port$auth_path \nMW-Panel-Url-Ipv6: ${scheme}://[$v6]:$port$auth_path" - elif [ "$v4" != "" ]; then - address="MW-Panel-Url: ${scheme}://$v4:$port$auth_path" - elif [ "$v6" != "" ]; then - - if [ ! -f $mw_path/data/ipv6.pl ];then - # Need to restart ipv6 to take effect - echo 'True' > $mw_path/data/ipv6.pl - mw_stop - mw_start - fi - address="MW-Panel-Url: ${scheme}://[$v6]:$port$auth_path" - else - address="MW-Panel-Url: ${scheme}://you-network-ip:$port$auth_path" - fi - else - address="MW-Panel-Url: ${scheme}://$address:$port$auth_path" - fi - - show_panel_ip="$port|" - echo -e "==================================================================" - echo -e "\033[32mMW-Panel default info!\033[0m" - echo -e "==================================================================" - echo -e "$address" - echo -e `python3 $mw_path/tools.py username` - echo -e `python3 $mw_path/tools.py password` - # echo -e "password: $password" - echo -e "\033[33mWarning:\033[0m" - echo -e "\033[33mIf you cannot access the panel. \033[0m" - echo -e "\033[33mrelease the following port (${show_panel_ip}80|443|22) in the security group.\033[0m" - echo -e "==================================================================" - ;; + 'default') mw_default;; *) - cd $mw_path && python3 $mw_path/tools.py cli $1 + cd ${PANEL_DIR} && python3 ${PANEL_DIR}/panel_tools.py cli $1 ;; esac diff --git a/scripts/install/debian.sh b/scripts/install/debian.sh index ae0ac0a46..37e4d97ba 100644 --- a/scripts/install/debian.sh +++ b/scripts/install/debian.sh @@ -74,7 +74,9 @@ apt install -y rar apt install -y unrar apt install -y pv apt install -y bc -apt install -y python3-pip python3-dev python3-venv +apt install -y python3-pip +apt install -y python3-dev +apt install -y python3-venv apt install -y libncurses5 apt install -y libncurses5-dev apt install -y bzip2 diff --git a/scripts/install/ubuntu.sh b/scripts/install/ubuntu.sh index 8af0c274f..37f8ef8bf 100644 --- a/scripts/install/ubuntu.sh +++ b/scripts/install/ubuntu.sh @@ -70,7 +70,7 @@ echo "SSH PORT:${SSH_PORT}" if [ -f /usr/sbin/ufw ];then # look # ufw status - ufw enable + echo 'y' | ufw enable if [ "$SSH_PORT" != "" ];then ufw allow $SSH_PORT/tcp diff --git a/scripts/install_dev.sh b/scripts/install_dev.sh index a44795a5c..cde638325 100755 --- a/scripts/install_dev.sh +++ b/scripts/install_dev.sh @@ -11,7 +11,6 @@ if [ -f /www/server/mdserver-web/tools.py ];then exit 0 fi - echo -e "您正在安装的是\033[31mmdserver-web测试版\033[0m,非开发测试用途请使用正式版 install.sh !" echo -e "You are installing\033[31m mdserver-web dev version\033[0m, normally use install.sh for production.\n" sleep 1 diff --git a/scripts/logs_backup.py b/scripts/logs_backup.py index 7c5877402..18203c08e 100755 --- a/scripts/logs_backup.py +++ b/scripts/logs_backup.py @@ -13,13 +13,13 @@ if sys.platform != 'darwin': os.chdir('/www/server/mdserver-web') -chdir = os.getcwd() -sys.path.append(chdir + '/class/core') +web_dir = os.getcwd() + "/web" +if os.path.exists(web_dir): + sys.path.append(web_dir) + os.chdir(web_dir) -# importlib.reload(sys) -# sys.setdefaultencoding('utf-8') +import core.mw as mw -import mw print('==================================================================') print('★[' + time.strftime("%Y/%m/%d %H:%M:%S") + '],切割日志') print('==================================================================') diff --git a/scripts/install_new.sh b/scripts/old/install.sh old mode 100755 new mode 100644 similarity index 71% rename from scripts/install_new.sh rename to scripts/old/install.sh index 71ca2fb51..6bd55eaa9 --- a/scripts/install_new.sh +++ b/scripts/old/install.sh @@ -4,9 +4,7 @@ export PATH # LANG=en_US.UTF-8 is64bit=`getconf LONG_BIT` -echo -e "您正在安装的是\033[31mmdserver-web测试版\033[0m,非开发测试用途请使用正式版 install.sh !" -echo -e "You are installing\033[31m mdserver-web dev version\033[0m, normally use install.sh for production.\n" -sleep 1 +VERSION=0.17.3 LOG_FILE=/var/log/mw-install.log @@ -21,14 +19,13 @@ startTime=`date +%s` _os=`uname` echo "use system: ${_os}" - if [ ${_os} == "Darwin" ]; then OSNAME='macos' -elif grep -Eq "openSUSE" /etc/*-release; then +elif grep -Eqi "openSUSE" /etc/*-release; then OSNAME='opensuse' zypper refresh - zypper install -y wget curl zip unzip unrar rar -elif grep -Eq "FreeBSD" /etc/*-release; then + zypper install cron wget curl zip unzip +elif grep -Eqi "FreeBSD" /etc/*-release; then OSNAME='freebsd' pkg install -y wget curl zip unzip unrar rar elif grep -Eqi "EulerOS" /etc/*-release || grep -Eqi "openEuler" /etc/*-release; then @@ -36,27 +33,27 @@ elif grep -Eqi "EulerOS" /etc/*-release || grep -Eqi "openEuler" /etc/*-release; yum install -y wget curl zip unzip tar crontabs elif grep -Eqi "CentOS" /etc/issue || grep -Eqi "CentOS" /etc/*-release; then OSNAME='rhel' - yum install -y wget zip unzip tar + yum install -y wget curl zip unzip tar crontabs elif grep -Eqi "Fedora" /etc/issue || grep -Eqi "Fedora" /etc/*-release; then OSNAME='rhel' - yum install -y wget zip unzip tar + yum install -y wget curl zip unzip tar crontabs elif grep -Eqi "Rocky" /etc/issue || grep -Eqi "Rocky" /etc/*-release; then OSNAME='rhel' - yum install -y wget zip unzip + yum install -y wget curl zip unzip tar crontabs elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eqi "AlmaLinux" /etc/*-release; then OSNAME='rhel' - yum install -y wget zip unzip tar + yum install -y wget curl zip unzip tar crontabs elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eqi "Amazon Linux" /etc/*-release; then OSNAME='amazon' - yum install -y wget zip unzip tar -elif grep -Eqi "Ubuntu" /etc/issue || grep -Eqi "Ubuntu" /etc/*-release; then - OSNAME='ubuntu' - apt install -y wget zip unzip tar -elif grep -Eqi "Debian" /etc/issue || grep -Eqi "Debian" /etc/*-release; then + yum install -y wget curl zip unzip tar crontabs +elif grep -Eqi "Debian" /etc/issue || grep -Eqi "Debian" /etc/os-release; then OSNAME='debian' apt update -y - apt install -y devscripts - apt install -y wget zip unzip tar + apt install -y wget curl zip unzip tar cron +elif grep -Eqi "Ubuntu" /etc/issue || grep -Eqi "Ubuntu" /etc/os-release; then + OSNAME='ubuntu' + apt update -y + apt install -y wget curl zip unzip tar cron else OSNAME='unknow' fi @@ -66,6 +63,7 @@ if [ "$EUID" -ne 0 ] && [ "$OSNAME" != "macos" ];then exit fi + # HTTP_PREFIX="https://" # LOCAL_ADDR=common # ping -c 1 github.com > /dev/null 2>&1 @@ -83,45 +81,44 @@ if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then fi echo "local:${LOCAL_ADDR}" -echo "OSNAME:${OSNAME}" if [ $OSNAME != "macos" ];then - if id www &> /dev/null ;then echo "" else groupadd www useradd -g www -s /usr/sbin/nologin www fi - + mkdir -p /www/server mkdir -p /www/wwwroot mkdir -p /www/wwwlogs mkdir -p /www/backup/database mkdir -p /www/backup/site + # https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh if [ ! -d /www/server/mdserver-web ];then - if [ "$LOCAL_ADDR" == "common" ];then - curl --insecure -sSLo /tmp/dev.zip ${HTTP_PREFIX}github.com/midoks/mdserver-web/archive/refs/heads/dev.zip - cd /tmp && unzip /tmp/dev.zip - mv -f /tmp/mdserver-web-dev /www/server/mdserver-web - rm -rf /tmp/dev.zip - rm -rf /tmp/mdserver-web-dev + curl --insecure -sSLo /tmp/master.zip ${HTTP_PREFIX}github.com/midoks/mdserver-web/archive/refs/tags/${VERSION}.zip + cd /tmp && unzip /tmp/master.zip + mv -f /tmp/mdserver-web-master /www/server/mdserver-web + rm -rf /tmp/master.zip + rm -rf /tmp/mdserver-web-master else - # curl --insecure -sSLo /tmp/dev.zip https://code.midoks.icu/midoks/mdserver-web/archive/dev.zip - wget --no-check-certificate -O /tmp/dev.zip https://code.midoks.icu/midoks/mdserver-web/archive/dev.zip - cd /tmp && unzip /tmp/dev.zip + # curl --insecure -sSLo /tmp/master.zip https://code.midoks.icu/midoks/mdserver-web/archive/master.zip + wget --no-check-certificate -O /tmp/master.zip https://code.midoks.icu/midoks/mdserver-web/archive/master.zip + cd /tmp && unzip /tmp/master.zip mv -f /tmp/mdserver-web /www/server/mdserver-web - rm -rf /tmp/dev.zip + rm -rf /tmp/master.zip rm -rf /tmp/mdserver-web - fi + fi + + fi # install acme.sh if [ ! -d /root/.acme.sh ];then if [ "$LOCAL_ADDR" != "common" ];then - # curl -sSL -o /tmp/acme.tar.gz ${HTTP_PREFIX}github.com/acmesh-official/acme.sh/archive/master.tar.gz curl --insecure -sSLo /tmp/acme.tar.gz https://gitee.com/neilpang/acme.sh/repository/archive/master.tar.gz tar xvzf /tmp/acme.tar.gz -C /tmp cd /tmp/acme.sh-master @@ -135,7 +132,6 @@ if [ $OSNAME != "macos" ];then fi echo "use system version: ${OSNAME}" - if [ "${OSNAME}" == "macos" ];then curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/master/scripts/install/macos.sh | bash else @@ -147,7 +143,7 @@ if [ "${OSNAME}" == "macos" ];then exit 0 fi -cd /www/server/mdserver-web && bash cli_new.sh start +cd /www/server/mdserver-web && bash cli.sh start isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'` n=0 while [ ! -f /etc/rc.d/init.d/mw ]; @@ -157,7 +153,7 @@ do let n+=1 if [ $n -gt 20 ];then echo -e "start mw fail" - exit 1 + exit 1 fi done diff --git a/scripts/old/update.sh b/scripts/old/update.sh new file mode 100644 index 000000000..079aa5924 --- /dev/null +++ b/scripts/old/update.sh @@ -0,0 +1,117 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin +export PATH +# LANG=en_US.UTF-8 +is64bit=`getconf LONG_BIT` + +startTime=`date +%s` + +VERSION=0.17.3 + +_os=`uname` +echo "use system: ${_os}" + +if [ "$EUID" -ne 0 ] + then echo "Please run as root!" + exit +fi + +if [ ${_os} != "Darwin" ] && [ ! -d /www/server/mdserver-web/logs ]; then + mkdir -p /www/server/mdserver-web/logs +fi + +LOG_FILE=/var/log/mw-update.log + +{ + +if [ ${_os} == "Darwin" ]; then + OSNAME='macos' +elif grep -Eqi "openSUSE" /etc/*-release; then + OSNAME='opensuse' + zypper refresh +elif grep -Eqi "EulerOS" /etc/*-release || grep -Eqi "openEuler" /etc/*-release; then + OSNAME='euler' +elif grep -Eqi "FreeBSD" /etc/*-release; then + OSNAME='freebsd' +elif grep -Eqi "CentOS" /etc/issue || grep -Eqi "CentOS" /etc/*-release; then + OSNAME='rhel' + yum install -y wget zip unzip +elif grep -Eqi "Fedora" /etc/issue || grep -Eqi "Fedora" /etc/*-release; then + OSNAME='rhel' + yum install -y wget zip unzip +elif grep -Eqi "Rocky" /etc/issue || grep -Eqi "Rocky" /etc/*-release; then + OSNAME='rhel' + yum install -y wget zip unzip +elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eqi "AlmaLinux" /etc/*-release; then + OSNAME='rhel' + yum install -y wget zip unzip +elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eqi "Amazon Linux" /etc/*-release; then + OSNAME='amazon' + yum install -y wget zip unzip +elif grep -Eqi "Debian" /etc/issue || grep -Eqi "Debian" /etc/*-release; then + OSNAME='debian' + apt install -y wget zip unzip +elif grep -Eqi "Ubuntu" /etc/issue || grep -Eqi "Ubuntu" /etc/*-release; then + OSNAME='ubuntu' + apt install -y wget zip unzip +elif grep -Eqi "Raspbian" /etc/issue || grep -Eqi "Raspbian" /etc/*-release; then + OSNAME='raspbian' +else + OSNAME='unknow' +fi + + +HTTP_PREFIX="https://" +LOCAL_ADDR=common +cn=$(curl -fsSL -m 10 -s http://ipinfo.io/json | grep "\"country\": \"CN\"") +if [ ! -z "$cn" ] || [ "$?" == "0" ] ;then + LOCAL_ADDR=cn + HTTP_PREFIX="https://mirror.ghproxy.com/" +fi +echo "local:${LOCAL_ADDR}" + +CP_CMD=/usr/bin/cp +if [ -f /bin/cp ];then + CP_CMD=/bin/cp +fi + +if [ "$LOCAL_ADDR" != "common" ];then + # curl --insecure -sSLo /tmp/master.zip https://code.midoks.icu/midoks/mdserver-web/archive/master.zip + wget --no-check-certificate -O /tmp/master.zip github.com/midoks/mdserver-web/archive/refs/tags/${VERSION}.zip + cd /tmp && unzip /tmp/master.zip + + $CP_CMD -rf /tmp/mdserver-web/* /www/server/mdserver-web + rm -rf /tmp/master.zip + rm -rf /tmp/mdserver-web +else + curl --insecure -sSLo /tmp/master.zip https://codeload.github.com/midoks/mdserver-web/zip/master + + cd /tmp && unzip /tmp/master.zip + $CP_CMD -rf /tmp/mdserver-web-master/* /www/server/mdserver-web + rm -rf /tmp/master.zip + rm -rf /tmp/mdserver-web-master +fi + + +#pip uninstall public +echo "use system version: ${OSNAME}" +cd /www/server/mdserver-web && bash scripts/update/${OSNAME}.sh + +bash /etc/rc.d/init.d/mw restart +bash /etc/rc.d/init.d/mw default + +if [ -f /usr/bin/mw ];then + rm -rf /usr/bin/mw +fi + +if [ ! -e /usr/bin/mw ]; then + if [ ! -f /usr/bin/mw ];then + ln -s /etc/rc.d/init.d/mw /usr/bin/mw + fi +fi + +endTime=`date +%s` +((outTime=($endTime-$startTime)/60)) +echo -e "Time consumed:\033[32m $outTime \033[0mMinute!" + +} 1> >(tee $LOG_FILE) 2>&1 \ No newline at end of file diff --git a/scripts/rememory.sh b/scripts/rememory.sh index 933f7eff2..5311e7aa6 100755 --- a/scripts/rememory.sh +++ b/scripts/rememory.sh @@ -32,7 +32,7 @@ fi echo "OpenResty -- END" -PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81) +PHP_VER_LIST=(53 54 55 56 70 71 72 73 74 80 81 82 83 84) for PHP_VER in ${PHP_VER_LIST[@]}; do echo "PHP${PHP_VER} -- START" if [ -f /usr/lib/systemd/system/php${PHP_VER}.service ];then diff --git a/scripts/update/debian.sh b/scripts/update/debian.sh index 060e92635..dcc663cac 100644 --- a/scripts/update/debian.sh +++ b/scripts/update/debian.sh @@ -37,7 +37,11 @@ fi cd /www/server/mdserver-web/scripts && bash lib.sh chmod 755 /www/server/mdserver-web/data + if [ -f /etc/rc.d/init.d/mw ];then + if [ -f /usr/bin/mw ];then + rm -rf /usr/bin/mw + fi bash /etc/rc.d/init.d/mw stop && rm -rf /www/server/mdserver-web/scripts/init.d/mw && rm -rf /etc/rc.d/init.d/mw fi diff --git a/scripts/update/ubuntu.sh b/scripts/update/ubuntu.sh index 74da1d9d9..1ee4def96 100644 --- a/scripts/update/ubuntu.sh +++ b/scripts/update/ubuntu.sh @@ -17,6 +17,9 @@ chmod 755 /www/server/mdserver-web/data if [ -f /etc/rc.d/init.d/mw ];then + if [ -f /usr/bin/mw ];then + rm -rf /usr/bin/mw + fi bash /etc/rc.d/init.d/mw stop && rm -rf /www/server/mdserver-web/scripts/init.d/mw && rm -rf /etc/rc.d/init.d/mw fi diff --git a/setting.py b/setting.py deleted file mode 100755 index eac374df0..000000000 --- a/setting.py +++ /dev/null @@ -1,95 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 配置文件 -# --------------------------------------------------------------------------------- - - -import time -import sys -import random -import os - -pwd = os.getcwd() -sys.path.append(pwd + '/class/core') - -import mw - -# cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' -# info = mw.execShell(cmd) -# p = "/usr/local/lib/" + info[0].strip() + "/site-packages" -# p_debain = "/usr/local/lib/" + info[0].strip() + "/dist-packages" - -# sys.path.append(p) -# sys.path.append(p_debain) - -import system_api -cpu_info = system_api.system_api().getCpuInfo() -workers = cpu_info[1] - - -log_dir = os.getcwd() + '/logs' -if not os.path.exists(log_dir): - os.mkdir(log_dir) - -# default port -mw_port = "7200" -if os.path.exists("data/port.pl"): - mw_port = mw.readFile('data/port.pl') - mw_port.strip() -else: - import firewall_api - import common - common.initDB() - mw_port = str(random.randint(10000, 65530)) - firewall_api.firewall_api().addAcceptPortArgs(mw_port, 'WEB面板', 'port') - mw.writeFile('data/port.pl', mw_port) - -bind = [] -if os.path.exists('data/ipv6.pl'): - bind.append('[0:0:0:0:0:0:0:0]:%s' % mw_port) -else: - bind.append('0.0.0.0:%s' % mw_port) - -ssl_choose_file = 'ssl/choose.pl' -if os.path.exists(ssl_choose_file): - ssl_choose = mw.readFile(ssl_choose_file).strip() - if mw.inArray(['local','nginx'],ssl_choose): - tmp_cert = 'ssl/'+ssl_choose+'/cert.pem' - tmp_private = 'ssl/'+ssl_choose+'/private.pem' - if os.path.exists(tmp_cert) and os.path.exists(tmp_private): - certfile = tmp_cert - keyfile = tmp_private - ciphers = 'TLSv1 TLSv1.1 TLSv1.2 TLSv1.3' - ssl_version = 2 - -# 初始安装时,自动生成安全路径 -if not os.path.exists('data/admin_path.pl'): - admin_path = mw.getRandomString(8) - mw.writeFile('data/admin_path.pl', '/' + admin_path.lower()) - -if workers > 2: - workers = 1 - -threads = workers * 1 -backlog = 512 -reload = False -daemon = True -worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' -timeout = 7200 -keepalive = 60 -preload_app = True -capture_output = True -access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"' -loglevel = 'info' -errorlog = log_dir + '/error.log' -accesslog = log_dir + '/access.log' -pidfile = log_dir + '/mw.pid' diff --git a/ssl/README.md b/ssl/README.md index bc8dc9fc5..39c380ce8 100644 --- a/ssl/README.md +++ b/ssl/README.md @@ -1,4 +1 @@ -# 自动生成 - -certificate.pem - 证书信息 -privateKey.pem - 私钥 \ No newline at end of file +# 数据目录 \ No newline at end of file diff --git a/task.py b/task.py deleted file mode 100755 index 71691f088..000000000 --- a/task.py +++ /dev/null @@ -1,633 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 计划任务 -# --------------------------------------------------------------------------------- - -import sys -import os -import json -import time -import threading -import psutil - -if sys.version_info[0] == 2: - reload(sys) - sys.setdefaultencoding('utf-8') - - -sys.path.append(os.getcwd() + "/class/core") -import mw -import db - -# print sys.path - -# cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' -# info = mw.execShell(cmd) -# p = "/usr/local/lib/" + info[0].strip() + "/site-packages" -# sys.path.append(p) - - -global pre, timeoutCount, logPath, isTask, oldEdate, isCheck -pre = 0 -timeoutCount = 0 -isCheck = 0 -oldEdate = None - -logPath = os.getcwd() + '/tmp/panelExec.log' -isTask = os.getcwd() + '/tmp/panelTask.pl' - -if not os.path.exists(os.getcwd() + "/tmp"): - os.system('mkdir -p ' + os.getcwd() + "/tmp") - -if not os.path.exists(logPath): - os.system("touch " + logPath) - - -def service_cmd(method): - cmd = '/etc/init.d/mw' - if os.path.exists(cmd): - execShell(cmd + ' ' + method) - return - - cmd = mw.getRunDir() + '/scripts/init.d/mw' - if os.path.exists(cmd): - execShell(cmd + ' ' + method) - return - - -def openresty_cmd(method = 'reload'): - # 检查是否安装 - odir = mw.getServerDir() + '/openresty' - if not os.path.exists(odir): - return False - - # systemd - systemd = mw.systemdCfgDir()+'/openresty.service' - if os.path.exists(systemd): - execShell('systemctl ' + method + ' openresty') - return True - - sys_initd = '/etc/init.d/openresty' - if os.path.exists(sys_initd): - os.system(sys_initd + ' ' + method) - return True - - install_initd = mw.getServerDir()+'/openresty/init.d/openresty' - if os.path.exists(install_initd): - os.system(install_initd + ' ' + method) - return True - return False - -def mw_async(f): - def wrapper(*args, **kwargs): - thr = threading.Thread(target=f, args=args, kwargs=kwargs) - thr.start() - return wrapper - - -@mw_async -def restartMw(): - time.sleep(1) - cmd = mw.getRunDir() + '/scripts/init.d/mw reload &' - mw.execShell(cmd) - - -def execShell(cmdstring, cwd=None, timeout=None, shell=True): - try: - global logPath - import shlex - import datetime - import subprocess - - if timeout: - end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout) - - cmd = cmdstring + ' > ' + logPath + ' 2>&1' - sub = subprocess.Popen( - cmd, cwd=cwd, stdin=subprocess.PIPE, shell=shell, bufsize=4096) - while sub.poll() is None: - time.sleep(0.1) - - data = sub.communicate() - # python3 fix 返回byte数据 - if isinstance(data[0], bytes): - t1 = str(data[0], encoding='utf-8') - - if isinstance(data[1], bytes): - t2 = str(data[1], encoding='utf-8') - return (t1, t2) - except Exception as e: - return (None, None) - - -def downloadFile(url, filename): - # 下载文件 - try: - import urllib - import socket - socket.setdefaulttimeout(300) - - headers = ( - 'User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36') - opener = urllib.request.build_opener() - opener.addheaders = [headers] - urllib.request.install_opener(opener) - - urllib.request.urlretrieve( - url, filename=filename, reporthook=downloadHook) - - if not mw.isAppleSystem(): - os.system('chown www.www ' + filename) - - writeLogs('done') - except Exception as e: - writeLogs(str(e)) - - -def downloadHook(count, blockSize, totalSize): - # 下载文件进度回调 - global pre - used = count * blockSize - pre1 = int((100.0 * used / totalSize)) - if pre == (100 - pre1): - return - speed = {'total': totalSize, 'used': used, 'pre': pre1} - writeLogs(json.dumps(speed)) - - -def writeLogs(logMsg): - # 写输出日志 - try: - global logPath - fp = open(logPath, 'w+') - fp.write(logMsg) - fp.close() - except: - pass - - -def runTask(): - global isTask - try: - if os.path.exists(isTask): - sql = db.Sql() - sql.table('tasks').where( - "status=?", ('-1',)).setField('status', '0') - taskArr = sql.table('tasks').where("status=?", ('0',)).field( - 'id,type,execstr').order("id asc").select() - for value in taskArr: - start = int(time.time()) - if not sql.table('tasks').where("id=?", (value['id'],)).count(): - continue - sql.table('tasks').where("id=?", (value['id'],)).save( - 'status,start', ('-1', start)) - if value['type'] == 'download': - argv = value['execstr'].split('|mw|') - downloadFile(argv[0], argv[1]) - elif value['type'] == 'execshell': - execShell(value['execstr']) - end = int(time.time()) - sql.table('tasks').where("id=?", (value['id'],)).save( - 'status,end', ('1', end)) - - if(sql.table('tasks').where("status=?", ('0')).count() < 1): - os.system('rm -f ' + isTask) - - sql.close() - except Exception as e: - print(str(e)) - - # 站点过期检查 - siteEdate() - - -def startTask(): - # 任务队列 - try: - while True: - runTask() - time.sleep(2) - except Exception as e: - time.sleep(60) - startTask() - - -def siteEdate(): - # 网站到期处理 - global oldEdate - try: - if not oldEdate: - oldEdate = mw.readFile('data/edate.pl') - if not oldEdate: - oldEdate = '0000-00-00' - mEdate = time.strftime('%Y-%m-%d', time.localtime()) - if oldEdate == mEdate: - return False - edateSites = mw.M('sites').where('edate>? AND edate 80: - panel_title = mw.getConfig('title') - ip = mw.getHostAddr() - now_time = mw.getDateFromNow() - msg = now_time + '|节点[' + panel_title + ':' + ip + \ - ']处于高负载[' + str(tmp['used']) + '],请排查原因!' - mw.notifyMessage(msg, '面板监控', 600) - - if not cpuInfo: - tmp['mem'] = sm.getMemUsed() - cpuInfo = tmp - - if cpuInfo['used'] < tmp['used']: - tmp['mem'] = sm.getMemUsed() - cpuInfo = tmp - - # 取当前网络Io - networkIo = sm.psutilNetIoCounters() - if not network_up: - network_up = networkIo[0] - network_down = networkIo[1] - tmp = {} - tmp['upTotal'] = networkIo[0] - tmp['downTotal'] = networkIo[1] - tmp['up'] = round(float((networkIo[0] - network_up) / 1024), 2) - tmp['down'] = round(float((networkIo[1] - network_down) / 1024), 2) - tmp['downPackets'] = networkIo[3] - tmp['upPackets'] = networkIo[2] - - network_up = networkIo[0] - network_down = networkIo[1] - - if not networkInfo: - networkInfo = tmp - if (tmp['up'] + tmp['down']) > (networkInfo['up'] + networkInfo['down']): - networkInfo = tmp - # 取磁盘Io - # if os.path.exists('/proc/diskstats'): - diskio_2 = psutil.disk_io_counters() - if not diskio_1: - diskio_1 = diskio_2 - tmp = {} - tmp['read_count'] = diskio_2.read_count - diskio_1.read_count - tmp['write_count'] = diskio_2.write_count - diskio_1.write_count - tmp['read_bytes'] = diskio_2.read_bytes - diskio_1.read_bytes - tmp['write_bytes'] = diskio_2.write_bytes - diskio_1.write_bytes - tmp['read_time'] = diskio_2.read_time - diskio_1.read_time - tmp['write_time'] = diskio_2.write_time - diskio_1.write_time - - if not diskInfo: - diskInfo = tmp - else: - diskInfo['read_count'] += tmp['read_count'] - diskInfo['write_count'] += tmp['write_count'] - diskInfo['read_bytes'] += tmp['read_bytes'] - diskInfo['write_bytes'] += tmp['write_bytes'] - diskInfo['read_time'] += tmp['read_time'] - diskInfo['write_time'] += tmp['write_time'] - diskio_1 = diskio_2 - - # print diskInfo - if count >= 12: - try: - addtime = int(time.time()) - deltime = addtime - (day * 86400) - - data = (cpuInfo['used'], cpuInfo['mem'], addtime) - sql.table('cpuio').add('pro,mem,addtime', data) - sql.table('cpuio').where("addtime 100: - lpro = 100 - sql.table('load_average').add('pro,one,five,fifteen,addtime', (lpro, load_average[ - 'one'], load_average['five'], load_average['fifteen'], addtime)) - - lpro = None - load_average = None - cpuInfo = None - networkInfo = None - diskInfo = None - count = 0 - reloadNum += 1 - if reloadNum > 1440: - reloadNum = 0 - mw.writeFile('logs/sys_interrupt.pl', - "reload num:" + str(reloadNum)) - restartMw() - except Exception as ex: - print(str(ex)) - mw.writeFile('logs/sys_interrupt.pl', str(ex)) - - del(tmp) - time.sleep(5) - count += 1 - except Exception as ex: - print(str(ex)) - mw.writeFile('logs/sys_interrupt.pl', str(ex)) - - restartMw() - - time.sleep(30) - systemTask() - - -# -------------------------------------- PHP监控 start --------------------------------------------- # -# 502错误检查线程 -def check502Task(): - try: - while True: - if os.path.exists(mw.getRunDir() + '/data/502Task.pl'): - check502() - time.sleep(30) - except: - time.sleep(30) - check502Task() - - -def check502(): - try: - verlist = [ - '52', '53', '54', '55', '56', '70', - '71', '72', '73', '74', '80', '81', - '82', '83' - ] - for ver in verlist: - sdir = mw.getServerDir() - php_path = sdir + '/php/' + ver + '/sbin/php-fpm' - if not os.path.exists(php_path): - continue - if checkPHPVersion(ver): - continue - if startPHPVersion(ver): - print('检测到PHP-' + ver + '处理异常,已自动修复!') - mw.writeLog('PHP守护程序', '检测到PHP-' + ver + '处理异常,已自动修复!') - except Exception as e: - print(str(e)) - - -# 处理指定PHP版本 -def startPHPVersion(version): - sdir = mw.getServerDir() - try: - # system - phpService = mw.systemdCfgDir() + '/php' + version + '.service' - if os.path.exists(phpService): - mw.execShell("systemctl restart php" + version) - if checkPHPVersion(version): - return True - - # initd - fpm = sdir + '/php/init.d/php' + version - php_path = sdir + '/php/' + version + '/sbin/php-fpm' - if not os.path.exists(php_path): - if os.path.exists(fpm): - os.remove(fpm) - return False - - if not os.path.exists(fpm): - return False - - # 尝试重载服务 - os.system(fpm + ' reload') - if checkPHPVersion(version): - return True - - # 尝试重启服务 - cgi = '/tmp/php-cgi-' + version + '.sock' - pid = sdir + '/php/' + version + '/var/run/php-fpm.pid' - data = mw.execShell("ps -ef | grep php/" + version +" | grep -v grep|grep -v python |awk '{print $2}'") - if data[0] != '': - os.system("ps -ef | grep php/" + version + " | grep -v grep|grep -v python |awk '{print $2}' | xargs kill ") - time.sleep(0.5) - if not os.path.exists(cgi): - os.system('rm -f ' + cgi) - if not os.path.exists(pid): - os.system('rm -f ' + pid) - os.system(fpm + ' start') - if checkPHPVersion(version): - return True - - # 检查是否正确启动 - if os.path.exists(cgi): - return True - except Exception as e: - print(str(e)) - return True - - -def getFpmConfFile(version): - return mw.getServerDir() + '/php/' + version + '/etc/php-fpm.d/www.conf' - - -def getFpmAddress(version): - fpm_address = '/tmp/php-cgi-{}.sock'.format(version) - php_fpm_file = getFpmConfFile(version) - try: - content = readFile(php_fpm_file) - tmp = re.findall(r"listen\s*=\s*(.+)", content) - if not tmp: - return fpm_address - if tmp[0].find('sock') != -1: - return fpm_address - if tmp[0].find(':') != -1: - listen_tmp = tmp[0].split(':') - if bind: - fpm_address = (listen_tmp[0], int(listen_tmp[1])) - else: - fpm_address = ('127.0.0.1', int(listen_tmp[1])) - else: - fpm_address = ('127.0.0.1', int(tmp[0])) - return fpm_address - except: - return fpm_address - - -def checkPHPVersion(version): - # 检查指定PHP版本 - try: - sock = getFpmAddress(version) - data = mw.requestFcgiPHP(sock, '/phpfpm_status_' + version + '?json') - result = str(data, encoding='utf-8') - except Exception as e: - result = 'Bad Gateway' - - # print(version,result) - # 检查openresty - if result.find('Bad Gateway') != -1: - return False - if result.find('HTTP Error 404: Not Found') != -1: - return False - - # 检查Web服务是否启动 - if result.find('Connection refused') != -1: - return False - # global isTask - # if os.path.exists(isTask): - # isStatus = mw.readFile(isTask) - # if isStatus == 'True': - # return True - - # # systemd - # systemd = mw.systemdCfgDir() + '/openresty.service' - # if os.path.exists(systemd): - # execShell('systemctl reload openresty') - # return True - # # initd - # initd = '/etc/init.d/openresty' - # if os.path.exists(initd): - # os.system(initd + ' reload') - return True - -# --------------------------------------PHP监控 end--------------------------------------------- # - - -# --------------------------------------OpenResty Auto Restart Start --------------------------------------------- # -# 解决acme.sh续签后,未起效。 -def openrestyAutoRestart(): - try: - while True: - # 检查是否安装 - odir = mw.getServerDir() + '/openresty' - if not os.path.exists(odir): - time.sleep(86400) - continue - - openresty_cmd('reload') - time.sleep(86400) - except Exception as e: - print(str(e)) - time.sleep(86400) - -# --------------------------------------OpenResty Auto Restart End --------------------------------------------- # - -# ------------------------------------ OpenResty Restart At Once Start ------------------------------------------ # - - -def openrestyRestartAtOnce(): - restart_nginx_tip = 'data/restart_nginx.pl' - while True: - if os.path.exists(restart_nginx_tip): - os.remove(restart_nginx_tip) - openresty_cmd('reload') - time.sleep(1) -# ----------------------------------- OpenResty Restart At Once End ------------------------------------------ # - - -# --------------------------------------Panel Restart Start --------------------------------------------- # -def restartPanelService(): - restartTip = 'data/restart.pl' - while True: - if os.path.exists(restartTip): - os.remove(restartTip) - service_cmd('restart_panel') - time.sleep(1) -# --------------------------------------Panel Restart End --------------------------------------------- # - - -def setDaemon(t): - if sys.version_info.major == 3 and sys.version_info.minor >= 10: - t.daemon = True - else: - t.setDaemon(True) - return t - -if __name__ == "__main__": - - # 系统监控 - sysTask = threading.Thread(target=systemTask) - sysTask = setDaemon(sysTask) - sysTask.start() - - # PHP 502错误检查线程 - php502 = threading.Thread(target=check502Task) - php502 = setDaemon(php502) - php502.start() - - # OpenResty Restart At Once Start - oraos = threading.Thread(target=openrestyRestartAtOnce) - oraos = setDaemon(oraos) - oraos.start() - - - # OpenResty Auto Restart Start - oar = threading.Thread(target=openrestyAutoRestart) - oar = setDaemon(oar) - oar.start() - - - # Panel Restart Start - rps = threading.Thread(target=restartPanelService) - rps = setDaemon(rps) - rps.start() - - startTask() diff --git a/tools.py b/tools.py deleted file mode 100755 index 6a050f9d9..000000000 --- a/tools.py +++ /dev/null @@ -1,303 +0,0 @@ -# coding: utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------- -# 工具箱 -# --------------------------------------------------------------------------------- - - -import sys -import os -import json -import time -import re - -sys.path.append(os.getcwd() + "/class/core") -import mw -import db - -# cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' -# info = mw.execShell(cmd) -# p = "/usr/local/lib/" + info[0].strip() + "/site-packages" -# sys.path.append(p) - -INIT_DIR = "/etc/rc.d/init.d" -if mw.isAppleSystem(): - INIT_DIR = mw.getRunDir() + "/scripts/init.d" - -INIT_CMD = INIT_DIR + "/mw" - - -def mw_input_cmd(msg): - if sys.version_info[0] == 2: - in_val = raw_input(msg) - else: - in_val = input(msg) - return in_val - - -def mwcli(mw_input=0): - raw_tip = "======================================================" - if not mw_input: - print("===============mdserver-web cli tools=================") - print("(1) 重启面板服务") - print("(2) 停止面板服务") - print("(3) 启动面板服务") - print("(4) 重载面板服务") - print("(5) 修改面板端口") - print("(10) 查看面板默认信息") - print("(11) 修改面板密码") - print("(12) 修改面板用户名") - print("(13) 显示面板错误日志") - print("(20) 关闭BasicAuth认证") - print("(21) 解除域名绑定") - print("(22) 解除面板SSL绑定") - print("(23) 开启IPV6支持") - print("(24) 关闭IPV6支持") - print("(25) 开启防火墙SSH端口") - print("(26) 关闭二次验证") - print("(27) 查看防火墙信息") - print("(100) 开启PHP52显示") - print("(101) 关闭PHP52显示") - print("(200) 切换Linux系统软件源") - print("(201) 简单速度测试") - print("(0) 取消") - print(raw_tip) - try: - mw_input = input("请输入命令编号:") - if sys.version_info[0] == 3: - mw_input = int(mw_input) - except: - mw_input = 0 - - nums = [ - 1, 2, 3, 4, 5, 10, 11, 12, 13, - 20, 21, 22, 23, 24, 25, 26, 27, - 100, 101, - 200, 201 - ] - if not mw_input in nums: - print(raw_tip) - print("已取消!") - exit() - - if mw_input == 1: - os.system(INIT_CMD + " restart") - elif mw_input == 2: - os.system(INIT_CMD + " stop") - elif mw_input == 3: - os.system(INIT_CMD + " start") - elif mw_input == 4: - os.system(INIT_CMD + " reload") - elif mw_input == 5: - in_port = mw_input_cmd("请输入新的面板端口:") - in_port_int = int(in_port.strip()) - if in_port_int < 65536 and in_port_int > 0: - import firewall_api - firewall_api.firewall_api().addAcceptPortArgs( - in_port, 'WEB面板[TOOLS修改]', 'port') - mw.writeFile('data/port.pl', in_port) - os.system(INIT_CMD + " restart_panel") - os.system(INIT_CMD + " default") - else: - print("|-端口范围在0-65536之间") - return - elif mw_input == 10: - os.system(INIT_CMD + " default") - elif mw_input == 11: - input_pwd = mw_input_cmd("请输入新的面板密码:") - if len(input_pwd.strip()) < 5: - print("|-错误,密码长度不能小于5位") - return - set_panel_pwd(input_pwd.strip(), True) - elif mw_input == 12: - input_user = mw_input_cmd("请输入新的面板用户名(>=5位):") - set_panel_username(input_user.strip()) - elif mw_input == 13: - os.system('tail -100 ' + mw.getRunDir() + '/logs/error.log') - elif mw_input == 20: - basic_auth = 'data/basic_auth.json' - if os.path.exists(basic_auth): - os.remove(basic_auth) - os.system(INIT_CMD + " restart") - print("|-关闭basic_auth成功") - elif mw_input == 21: - bind_domain = 'data/bind_domain.pl' - if os.path.exists(bind_domain): - os.remove(bind_domain) - os.system(INIT_CMD + " unbind_domain") - print("|-解除域名绑定成功") - elif mw_input == 22: - ssl_choose = 'ssl/choose.pl' - if os.path.exists(ssl_choose): - os.remove(ssl_choose) - os.system(INIT_CMD + " unbind_ssl") - print("|-解除面板SSL绑定成功") - elif mw_input == 23: - listen_ipv6 = 'data/ipv6.pl' - if not os.path.exists(listen_ipv6): - mw.writeFile(listen_ipv6,'True') - os.system(INIT_CMD + " restart") - print("|-开启IPv6支持了") - else: - print("|-已开启IPv6支持!") - elif mw_input == 24: - listen_ipv6 = 'data/ipv6.pl' - if not os.path.exists(listen_ipv6): - print("|-已关闭IPv6支持!") - else: - os.remove(listen_ipv6) - os.system(INIT_CMD + " restart") - print("|-关闭IPv6支持了") - elif mw_input == 25: - open_ssh_port() - print("|-已开启!") - elif mw_input == 26: - auth_secret = 'data/auth_secret.pl' - if os.path.exists(auth_secret): - os.remove(auth_secret) - print("|-关闭二次验证成功!") - else: - print("|-二次验证已关闭!") - elif mw_input == 27: - cmd = 'which ufw' - run_cmd = False - find_cmd = mw.execShell(cmd) - if find_cmd[0].strip() != '': - run_cmd = True - os.system('ufw status') - - cmd = 'which firewall-cmd' - find_cmd = mw.execShell(cmd) - if find_cmd[0].strip() != '': - run_cmd = True - os.system('firewall-cmd --list-all') - if not run_cmd: - mw.echoInfo("未检测到防火墙!") - elif mw_input == 100: - php_conf = 'plugins/php/info.json' - if os.path.exists(php_conf): - cont = mw.readFile(php_conf) - cont = re.sub("\"53\"", "\"52\",\"53\"", cont) - cont = re.sub("\"5.3.29\"", "\"5.2.17\",\"5.3.29\"", cont) - mw.writeFile(php_conf, cont) - print("|-执行PHP52显示成功!") - elif mw_input == 101: - php_conf = 'plugins/php/info.json' - if os.path.exists(php_conf): - cont = mw.readFile(php_conf) - cont = re.sub("\"52\",", "", cont) - cont = re.sub("\"5.2.17\",", cont) - mw.writeFile(php_conf, cont) - print("|-执行PHP52隐藏成功!") - elif mw_input == 200: - os.system(INIT_CMD + " mirror") - elif mw_input == 201: - os.system('curl -Lso- bench.sh | bash') - - -def open_ssh_port(): - import firewall_api - find_ssh_port_cmd = "cat /etc/ssh/sshd_config | grep '^Port \\d*' | tail -1" - cmd_data = mw.execShell(find_ssh_port_cmd) - ssh_port = cmd_data[0].replace("Port ", '').strip() - if ssh_port == '': - ssh_port = '22' - - print("|-SSH端口: "+ str(ssh_port)) - firewall_api.firewall_api().addAcceptPortArgs(ssh_port, 'SSH远程管理服务', 'port') - return True - - -def set_panel_pwd(password, ncli=False): - # 设置面板密码 - import db - sql = db.Sql() - result = sql.table('users').where('id=?', (1,)).setField( - 'password', mw.md5(password)) - username = sql.table('users').where('id=?', (1,)).getField('username') - if ncli: - print("|-用户名: " + username) - print("|-新密码: " + password) - else: - print(username) - - -def show_panel_pwd(): - # 设置面板密码 - import db - sql = db.Sql() - password = sql.table('users').where('id=?', (1,)).getField('password') - - file_pwd = '' - if os.path.exists('data/default.pl'): - file_pwd = mw.readFile('data/default.pl').strip() - - if mw.md5(file_pwd) == password: - print('password: ' + file_pwd) - return - print("password has been changed!") - - -def set_panel_username(username=None): - # 随机面板用户名 - import db - sql = db.Sql() - if username: - if len(username) < 5: - print("|-错误,用户名长度不能少于5位") - return - if username in ['admin', 'root']: - print("|-错误,不能使用过于简单的用户名") - return - - sql.table('users').where('id=?', (1,)).setField('username', username) - print("|-新用户名: %s" % username) - return - - username = sql.table('users').where('id=?', (1,)).getField('username') - if username == 'admin': - username = mw.getRandomString(8).lower() - sql.table('users').where('id=?', (1,)).setField('username', username) - print('username: ' + username) - - -def getServerIp(): - version = sys.argv[2] - # ip = mw.execShell( - # "curl --insecure -{} -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text".format(version)) - ip = mw.execShell( - "curl --insecure -{} -sS --connect-timeout 5 -m 60 https://ip.cachecha.com/?format=text".format(version)) - print(ip[0]) - - -if __name__ == "__main__": - method = sys.argv[1] - if method == 'panel': - set_panel_pwd(sys.argv[2]) - elif method == 'username': - if len(sys.argv) > 2: - set_panel_username(sys.argv[2]) - else: - set_panel_username() - elif method == 'password': - show_panel_pwd() - elif method == 'getServerIp': - getServerIp() - elif method == "cli": - clinum = 0 - try: - if len(sys.argv) > 2: - clinum = int(sys.argv[2]) if sys.argv[2][:6] else sys.argv[2] - except: - clinum = sys.argv[2] - mwcli(clinum) - else: - print('ERROR: Parameter error') diff --git a/version/api/mw_api.php b/version/api/mw_api.php deleted file mode 100755 index 4096d8fdf..000000000 --- a/version/api/mw_api.php +++ /dev/null @@ -1,94 +0,0 @@ -MW_PANEL = $mw_panel; - } - - if ($mw_key) { - $this->MW_KEY = $mw_key; - } - } - - /** - * 发起POST请求 - * @param String $url 目标网填,带http:// - * @param Array|String $data 欲提交的数据 - * @return string - */ - private function httpPostCookie($url, $data, $timeout = 60) { - //定义cookie保存位置 - $cookie_file = './' . md5($this->MW_PANEL) . '.cookie'; - if (!file_exists($cookie_file)) { - $fp = fopen($cookie_file, 'w+'); - fclose($fp); - } - - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, $data); - curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file); - curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_HEADER, 0); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - $output = curl_exec($ch); - curl_close($ch); - return $output; - } - - /** - * 构造带有签名的关联数组 - */ - private function getKeyData() { - $now_time = time(); - $ready_data = array( - 'request_token' => md5($now_time . '' . md5($this->MW_KEY)), - 'request_time' => $now_time, - ); - return $ready_data; - } - - //示例取面板日志 - public function getLogsList() { - //拼接URL地址 - $url = $this->MW_PANEL . '/api/firewall/get_log_list'; - - //准备POST数据 - $post_data = $this->getKeyData(); //取签名 - $post_data['p'] = '1'; - $post_data['limit'] = 10; - - //请求面板接口 - $result = $this->httpPostCookie($url, $post_data); - - //解析JSON数据 - $data = json_decode($result, true); - return $data; - } - -} - -//实例化对象 -$api = new mwApi(); -//获取面板日志 -$rdata = $api->getLogsList(); - -// var_dump($rdata); -//输出JSON数据到浏览器 -echo json_encode($rdata); - -?> \ No newline at end of file diff --git a/version/api/mw_api.py b/version/api/mw_api.py deleted file mode 100755 index 3748e1a8b..000000000 --- a/version/api/mw_api.py +++ /dev/null @@ -1,125 +0,0 @@ -# coding: utf-8 -# +----------------------------------------------------------------------------------- -# | MW Linux面板 -# +----------------------------------------------------------------------------------- -# | Copyright (c) 2015-2099 MW(http://github.com/midoks/mdserver) All rights reserved. -# +----------------------------------------------------------------------------------- -# | Author: midoks -# +----------------------------------------------------------------------------------- - -#------------------------------ -# API-Demo of Python -#------------------------------ -import time -import hashlib -import sys -import os -import json - - -class mwApi: - __MW_KEY = 'uATE5NrKDWIlZuDcYpvLVhoUo1c7A1Pk' - __MW_PANEL = 'http://127.0.0.1:7200' - - # 如果希望多台面板,可以在实例化对象时,将面板地址与密钥传入 - def __init__(self, mw_panel=None, mw_key=None): - if mw_panel: - self.__MW_PANEL = mw_panel - self.__MW_KEY = mw_key - - # 计算MD5 - def __get_md5(self, s): - m = hashlib.md5() - m.update(s.encode('utf-8')) - return m.hexdigest() - - # 构造带有签名的关联数组 - def __get_key_data(self): - now_time = int(time.time()) - ready_data = { - 'request_token': self.__get_md5(str(now_time) + '' + self.__get_md5(self.__MW_KEY)), - 'request_time': now_time - } - return ready_data - - # 发送POST请求并保存Cookie - #@url 被请求的URL地址(必需) - #@data POST参数,可以是字符串或字典(必需) - #@timeout 超时时间默认1800秒 - # return string - def __http_post_cookie(self, url, p_data, timeout=1800): - cookie_file = '/tmp/' + self.__get_md5(self.__MW_PANEL) + '.cookie' - if sys.version_info[0] == 2: - # Python2 - import urllib - import urllib2 - import ssl - import cookielib - - # 创建cookie对象 - cookie_obj = cookielib.MozillaCookieJar(cookie_file) - - # 加载已保存的cookie - if os.path.exists(cookie_file): - cookie_obj.load(cookie_file, ignore_discard=True, - ignore_expires=True) - - ssl._create_default_https_context = ssl._create_unverified_context - - data = urllib.urlencode(p_data) - req = urllib2.Request(url, data) - opener = urllib2.build_opener( - urllib2.HTTPCookieProcessor(cookie_obj)) - response = opener.open(req, timeout=timeout) - - # 保存cookie - cookie_obj.save(ignore_discard=True, ignore_expires=True) - return response.read() - else: - # Python3 - import urllib.request - import ssl - import http.cookiejar - cookie_obj = http.cookiejar.MozillaCookieJar(cookie_file) - # 加载已保存的cookie - if os.path.exists(cookie_file): - cookie_obj.load(cookie_file, ignore_discard=True, - ignore_expires=True) - - handler = urllib.request.HTTPCookieProcessor(cookie_obj) - data = urllib.parse.urlencode(p_data).encode('utf-8') - req = urllib.request.Request(url, data) - opener = urllib.request.build_opener(handler) - response = opener.open(req, timeout=timeout) - cookie_obj.save(ignore_discard=True, ignore_expires=True) - result = response.read() - if type(result) == bytes: - result = result.decode('utf-8') - return result - - # 取面板日志 - def getLogs(self): - # 拼接URL地址 - url = self.__MW_PANEL + '/api/logs/get_log_list' - - # 准备POST数据 - post_data = self.__get_key_data() # 取签名 - post_data['limit'] = 10 - post_data['p'] = '1' - - # 请求面板接口 - result = self.__http_post_cookie(url, post_data) - - # 解析JSON数据 - return json.loads(result) - - -if __name__ == '__main__': - # 实例化MW-API对象 - api = mwApi() - - # 调用get_logs方法 - rdata = api.getLogs() - - # 打印响应数据 - print(rdata) diff --git a/version/info.json b/version/info.json deleted file mode 100644 index f56145cde..000000000 --- a/version/info.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "version": "0.9.9", - "content": "* 目标版本。", - }, - { - "version": "0.9.2", - "content": "* 批量域名添加修复。
                * 常用命令说明。", - "path": "https://github.com/midoks/mdserver-web/releases/download/0.9.2/mdserver-web.zip" - }, - { - "version": "0.9.1", - "content": "* 菜单修复。
                * 更新修复", - "path": "https://github.com/midoks/mdserver-web/releases/download/0.9.1/mdserver-web.zip", - "purge":"https://purge.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/update.sh" - }, - { - "version": "0.9.0", - "content": "* 主流系统支持。
                * 插件支持更多参数。
                * MySQL主从支持GTID和经典模式。
                * MariaDB主从支持GTID和经典模式。
                * Rsyncd更新。
                * 添加网站统计的插件。
                * 添加varnish插件。", - "path": "https://github.com/midoks/mdserver-web/releases/download/0.9.0/mdserver-web.zip" - } -] \ No newline at end of file diff --git a/web/admin/__init__.py b/web/admin/__init__.py index d8f3ff159..2277d5ca4 100644 --- a/web/admin/__init__.py +++ b/web/admin/__init__.py @@ -14,39 +14,33 @@ import json import time import uuid import logging + from datetime import timedelta from flask import Flask - -from flask_socketio import SocketIO, emit, send +from flask import request +from flask import redirect +from flask import Response from flask import Flask, abort, current_app, session, url_for from flask import Blueprint, render_template from flask import render_template_string -from flask import request -from flask import Response +from flask_socketio import SocketIO, emit, send - -from flask_migrate import Migrate from flask_caching import Cache from werkzeug.local import LocalProxy -from admin import model -from admin import setup - -from admin.model import db as sys_db +from admin.common import isLogined import core.mw as mw import config import utils.config as utils_config +import thisdb -root_dir = mw.getRunDir() - -socketio = SocketIO(manage_session=False, async_mode='threading', - logger=False, engineio_logger=False, debug=False, - ping_interval=25, ping_timeout=120) - +# 初始化db +from admin import setup +setup.init() app = Flask(__name__, template_folder='templates/default') @@ -57,9 +51,12 @@ cache.init_app(app, config={'CACHE_TYPE': 'simple'}) # 静态文件配置 from whitenoise import WhiteNoise app.wsgi_app = WhiteNoise(app.wsgi_app, root="../web/static/", prefix="static/", max_age=604800) +app.jinja_env.trim_blocks = True # session配置 -app.secret_key = uuid.UUID(int=uuid.getnode()).hex[-12:] +# app.secret_key = uuid.UUID(int=uuid.getnode()).hex[-12:] +app.config['SECRET_KEY'] = uuid.UUID(int=uuid.getnode()).hex[-12:] + # app.config['sessions'] = dict() app.config['SESSION_PERMANENT'] = True app.config['SESSION_USE_SIGNER'] = True @@ -68,38 +65,17 @@ app.config['SESSION_COOKIE_NAME'] = "MW_VER_1" app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=31) # db的配置 -app.config['SQLALCHEMY_DATABASE_URI'] = mw.getSqitePrefix()+config.SQLITE_PATH+"?timeout=20" # 使用 SQLite 数据库 +# app.config['SQLALCHEMY_DATABASE_URI'] = mw.getSqitePrefix()+config.SQLITE_PATH+"?timeout=20" # 使用 SQLite 数据库 app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True -# 初始化db -sys_db.init_app(app) -Migrate(app, sys_db) - - -# 检查数据库是否存在。如果没有就创建它。 -setup_db_required = False -if not os.path.isfile(config.SQLITE_PATH): - setup_db_required = True - -# with app.app_context(): -# sys_db.create_all() - -with app.app_context(): - if setup_db_required: - sys_db.create_all() - -# 初始化用户信息 -with app.app_context(): - if setup_db_required: - setup.init_admin_user() - setup.init_option() - +# BASIC AUTH app.config['BASIC_AUTH_OPEN'] = False -with app.app_context(): - basic_auth = model.getOptionByJson('basic_auth', default={'open':False}) +try: + basic_auth = thisdb.getOptionByJson('basic_auth', default={'open':False}) if basic_auth['open']: app.config['BASIC_AUTH_OPEN'] = True - +except Exception as e: + pass # 加载模块 from .submodules import get_submodules @@ -118,9 +94,16 @@ def sendAuthenticated(): @app.before_request def requestCheck(): + + admin_close = thisdb.getOption('admin_close') + if admin_close == 'yes': + if not request.path.startswith('/close'): + return redirect('/close') + + config.APP_START_TIME=time.time() # 自定义basic auth认证 if app.config['BASIC_AUTH_OPEN']: - basic_auth = model.getOptionByJson('basic_auth', default={'open':False}) + basic_auth = thisdb.getOptionByJson('basic_auth', default={'open':False}) if not basic_auth['open']: return @@ -130,12 +113,15 @@ def requestCheck(): if not auth: return sendAuthenticated() - # print(auth.username.strip(),auth.password.strip()) salt = basic_auth['salt'] basic_user = mw.md5(auth.username.strip() + salt) basic_pwd = mw.md5(auth.password.strip() + salt) if basic_user != basic_auth['basic_user'] or basic_pwd != basic_auth['basic_pwd']: return sendAuthenticated() + + domain_check = mw.checkDomainPanel() + if domain_check: + return domain_check @@ -143,94 +129,85 @@ def requestCheck(): def requestAfter(response): response.headers['soft'] = config.APP_NAME response.headers['mw-version'] = config.APP_VERSION + if mw.isDebugMode(): + response.headers['mw-debug-cos'] = time.time() - config.APP_START_TIME return response @app.errorhandler(404) def page_unauthorized(error): - return render_template_string('404 not found', error_info=error), 404 + from flask import redirect + return redirect('/', code=302) + # return render_template_string('404 not found', error_info=error), 404 # 设置模板全局变量 @app.context_processor def inject_global_variables(): - ver = config.APP_VERSION; + app_ver = config.APP_VERSION if mw.isDebugMode(): - ver = ver + str(time.time()) + app_ver = app_ver + str(time.time()) data = utils_config.getGlobalVar() g_config = { - 'version': ver, + 'version': app_ver, 'title' : 'MW面板', 'ip' : '127.0.0.1' } return dict(config=g_config, data=data) +# webssh +# socketio = SocketIO(manage_session=False, async_mode='threading', +# logger=False, engineio_logger=False, debug=False, +# ping_interval=25, ping_timeout=120) +socketio = SocketIO(logger=False, + engineio_logger=False, + cors_allowed_origins="*") +socketio.init_app(app) -# from flasgger import Swagger -# api = Api(app, version='1.0', title='API', description='API 文档') -# Swagger(app) - -# @app.route('/colors//') -# def colors(palette): -# """ -# 根据调色板名称返回颜色列表 -# --- -# parameters: -# - name: palette -# in: path -# type: string -# enum: ['all', 'rgb', 'cmyk'] -# required: true -# default: all -# definitions: -# Palette: -# type: object -# properties: -# palette_name: -# type: array -# items: -# $ref: '#/definitions/Color' -# Color: -# type: string -# responses: -# 200: -# description: 返回的颜色列表,可按调色板过滤 -# schema: -# $ref: '#/definitions/Palette' -# examples: -# rgb: ['red', 'green', 'blue'] -# """ -# all_colors = { -# 'cmyk': ['cyan', 'magenta', 'yellow', 'black'], -# 'rgb': ['red', 'green', 'blue'] -# } -# if palette == 'all': -# result = all_colorselse -# result = {palette: all_colors.get(palette)} -# return jsonify(result) +@socketio.on('webssh_websocketio') +def webssh_websocketio(data): + if not isLogined(): + emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) + return + import utils.ssh.ssh_terminal as ssh_terminal + shell_client = ssh_terminal.ssh_terminal.instance() + shell_client.run(request.sid, data) + return +@socketio.on('webssh') +def webssh(data): + if not isLogined(): + emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'}) + return None + import utils.ssh.ssh_local as ssh_local + shell = ssh_local.ssh_local.instance() + shell.run(data) + return + + +# File logging +logger = logging.getLogger('werkzeug') +logger.setLevel(config.CONSOLE_LOG_LEVEL) + +from utils.enhanced_log_rotation import EnhancedRotatingFileHandler +fh = EnhancedRotatingFileHandler(config.LOG_FILE, + config.LOG_ROTATION_SIZE, + config.LOG_ROTATION_AGE, + config.LOG_ROTATION_MAX_LOG_FILES) +fh.setLevel(config.FILE_LOG_LEVEL) +app.logger.addHandler(fh) +logger.addHandler(fh) + +# Console logging +ch = logging.StreamHandler() +ch.setLevel(config.CONSOLE_LOG_LEVEL) +ch.setFormatter(logging.Formatter(config.CONSOLE_LOG_FORMAT)) # Log the startup app.logger.info('########################################################') app.logger.info('Starting %s v%s...', config.APP_NAME, config.APP_VERSION) app.logger.info('########################################################') -app.logger.debug("Python syspath: %s", sys.path) - - - -# OK -socketio.init_app(app, cors_allowed_origins="*") - -# def create_app(app_name = None): -# -# if not app_name: -# app_name = config.APP_NAME - -# # Check if app is created for CLI operations or Web -# cli_mode = False -# if app_name.endswith('-cli'): -# cli_mode = True -# return app \ No newline at end of file +app.logger.debug("Python syspath: %s", sys.path) \ No newline at end of file diff --git a/web/admin/common.py b/web/admin/common.py index c1692c081..235bbbdac 100644 --- a/web/admin/common.py +++ b/web/admin/common.py @@ -10,13 +10,13 @@ import time -from admin import model from admin import session +import thisdb def isLogined(): if 'login' in session and session['login'] == True and 'username' in session: username = session['username'] - info = model.getUserByName(username) + info = thisdb.getUserByName(username) if info is None: return False diff --git a/web/admin/crontab/__init__.py b/web/admin/crontab/__init__.py index 6162e7272..64adb51cf 100644 --- a/web/admin/crontab/__init__.py +++ b/web/admin/crontab/__init__.py @@ -13,22 +13,116 @@ from flask import Blueprint, render_template from flask import request from admin.user_login_check import panel_login_required -from admin.model import Crontab -blueprint = Blueprint('crontab', __name__, url_prefix='/crontab', template_folder='../../templates/default') +from utils.crontab import crontab as MwCrontab +import core.mw as mw +import thisdb + +blueprint = Blueprint('crontab', __name__, url_prefix='/crontab', template_folder='../../templates') @blueprint.route('/index', endpoint='index') def index(): - return render_template('crontab.html') + name = thisdb.getOption('template', default='default') + return render_template('%s/crontab.html' % name) -# 插件列表 -@blueprint.route('/list', endpoint='list', methods=['GET','POST']) +# 计划任务列表 +@blueprint.route('/list', endpoint='list', methods=['POST']) @panel_login_required def list(): - page = request.args.get('p', 1) - size = 10 - count = Crontab.query.count() - print(count) - clist = Crontab.query.paginate(page=int(page), per_page=size) - print(clist) + page = request.args.get('p', '1').strip() + limit = request.args.get('limit', '10').strip() + return MwCrontab.instance().getCrontabList(page=int(page),size=int(limit)) + +# 计划任务日志 +@blueprint.route('/logs', endpoint='logs', methods=['POST']) +def logs(): + cron_id = request.form.get('id', '') + return MwCrontab.instance().cronLog(cron_id) + +# 删除计划任务 +@blueprint.route('/del', endpoint='del', methods=['POST']) +def crontab_del(): + cron_id = request.form.get('id', '') + return MwCrontab.instance().delete(cron_id) + +# 删除计划任务日志 +@blueprint.route('/del_logs', endpoint='del_logs', methods=['POST']) +def del_logs(): + cron_id = request.form.get('id', '') + return MwCrontab.instance().delLogs(cron_id) + + +# 设置计划任务状态 +@blueprint.route('/set_cron_status', endpoint='set_cron_status', methods=['POST']) +def set_cron_status(): + cron_id = request.form.get('id', '') + return MwCrontab.instance().setCronStatus(cron_id) + +# 设置计划任务状态 +@blueprint.route('/get_data_list', endpoint='get_data_list', methods=['POST']) +def get_data_list(): + stype = request.form.get('type', '') + return MwCrontab.instance().getDataList(stype) + + +# 获取计划任务 +@blueprint.route('/get_crond_find', endpoint='get_crond_find', methods=['POST']) +def get_crond_find(): + cron_id = request.form.get('id', '') + data = MwCrontab.instance().getCrondFind(cron_id) + return data + +# 修改计划任务 +@blueprint.route('/modify_crond', endpoint='modify_crond', methods=['POST']) +def modify_crond(): + request_data = {} + + request_data['name'] = request.form.get('name', '') + request_data['type'] = request.form.get('type', '') + request_data['week'] = request.form.get('week', '') + request_data['where1'] = request.form.get('where1', '') + request_data['hour'] = request.form.get('hour', '') + request_data['minute'] = request.form.get('minute', '') + request_data['save'] = request.form.get('save', '') + request_data['backup_to'] = request.form.get('backup_to', '') + request_data['stype'] = request.form.get('stype', '') + request_data['sname'] = request.form.get('sname', '') + request_data['sbody'] = request.form.get('sbody', '') + request_data['url_address'] = request.form.get('urladdress', '') + cron_id = request.form.get('id', '') + data = MwCrontab.instance().modifyCrond(cron_id,request_data) + return data + +# 执行计划任务 +@blueprint.route('/start_task', endpoint='start_task', methods=['POST']) +def start_task(): + cron_id = request.form.get('id', '') + return MwCrontab.instance().startTask(cron_id) + +# 添加计划任务 +@blueprint.route('/add', endpoint='add', methods=['POST']) +@panel_login_required +def add(): + request_data = {} + request_data['name'] = request.form.get('name', '') + request_data['type'] = request.form.get('type', '') + request_data['week'] = request.form.get('week', '') + request_data['where1'] = request.form.get('where1', '') + request_data['hour'] = request.form.get('hour', '') + request_data['minute'] = request.form.get('minute', '') + request_data['save'] = request.form.get('save', '') + request_data['backup_to'] = request.form.get('backup_to', '') + request_data['stype'] = request.form.get('stype', '') + request_data['sname'] = request.form.get('sname', '') + request_data['sbody'] = request.form.get('sbody', '') + request_data['url_address'] = request.form.get('urladdress', '') + + info = thisdb.getCronByName(request_data['name']) + if info is not None: + return mw.returnData(False, '任务名称重复') + + cron_id = MwCrontab.instance().add(request_data) + if cron_id > 0: + return mw.returnData(True, '添加成功') + return mw.returnData(False, '添加失败') + - return [] \ No newline at end of file diff --git a/web/admin/dashboard/__init__.py b/web/admin/dashboard/__init__.py index 0bf9fdee3..77a962a50 100644 --- a/web/admin/dashboard/__init__.py +++ b/web/admin/dashboard/__init__.py @@ -8,233 +8,6 @@ # Author: midoks # --------------------------------------------------------------------------------- -import io -import time -from flask import Blueprint, render_template -from flask import make_response -from flask import redirect -from flask import Response -from flask import request,g - -from admin.common import isLogined -from admin.user_login_check import panel_login_required -from admin import cache,session -from admin import model -from admin.model import db,TempLogin,Users - -import core.mw as mw - - -blueprint = Blueprint('dashboard', __name__, url_prefix='/', template_folder='../../templates') -@blueprint.route('/') -@panel_login_required -def index(): - return render_template('default/index.html') - -# 仅针对webhook插件 -@blueprint.route("/hook", methods=['POST', 'GET']) -def webhook(): - # 兼容获取关键数据 - access_key = request.args.get('access_key', '').strip() - if access_key == '': - access_key = request.form.get('access_key', '').strip() - - params = request.args.get('params', '').strip() - if params == '': - params = request.form.get('params', '').strip() - - input_args = { - 'access_key': access_key, - 'params': params, - } - - wh_install_path = mw.getServerDir() + '/webhook' - if not os.path.exists(wh_install_path): - return mw.returnJson(False, '请先安装WebHook插件!') - - package = mw.getPanelDir() + "/plugins/webhook" - if not package in sys.path: - sys.path.append(package) - - try: - import webhook_index - return webhook_index.runShellArgs(input_args) - except Exception as e: - return str(e) - -# 安全路径 -@blueprint.route('/',endpoint='admin_safe_path',methods=['GET']) -def admin_safe_path(path): - db_path = model.getOption('admin_path') - if db_path == path: - return render_template('default/login.html') - - unauthorized_status = model.getOption('unauthorized_status') - if unauthorized_status == '0': - return render_template('default/path.html') - return Response(status=int(unauthorized_status)) - - -# --------------------------------------------------------------------------------- -# 定义登录入口相关方法 -# --------------------------------------------------------------------------------- - -def getErrorNum(key, limit=None): - key = mw.md5(key) - num = cache.get(key) - if not num: - num = 0 - if not limit: - return num - if limit > num: - return True - return False - - -def setErrorNum(key, empty=False, expire=3600): - key = mw.md5(key) - num = cache.get(key) - if not num: - num = 0 - else: - if empty: - cache.delete(key) - return True - cache.set(key, num + 1, expire) - return True - -def login_temp_user(token): - if len(token) != 32: - return '错误的参数!' - - skey = mw.getClientIp() + '_temp_login' - if not getErrorNum(skey, 10): - return '连续10次验证失败,禁止1小时' - - stime = int(time.time()) - - tmp_data = model.getTempLoginByToken(token) - if not tmp_data: - setErrorNum(skey) - return '验证失败!' - - if stime > int(tmp_data['expire']): - setErrorNum(skey) - return "过期" - - user_data = model.getUserById(1) - - login_addr = mw.getClientIp() + ":" + str(request.environ.get('REMOTE_PORT')) - mw.writeLog('用户临时登录', "登录成功,帐号:{1},登录IP:{2}",(user_data['name'], login_addr)) - - TempLogin.query.filter(TempLogin.id==tmp_data['id']).update({"login_time": stime, 'state': 1, 'login_addr': login_addr}) - db.session.commit() - - session['login'] = True - session['username'] = user_data['name'] - session['tmp_login'] = True - session['tmp_login_id'] = str(tmp_data['id']) - session['tmp_login_expire'] = int(tmp_data['expire']) - session['uid'] = user_data['id'] - - return redirect('/') - -# 登录页: 当设置了安全路径,本页失效。 -@blueprint.route('/login') -def login(): - - # 临时登录功能 - token = request.args.get('tmp_token', '').strip() - if token != '': - return login_temp_user(token) - - # 注销登录 - signout = request.args.get('signout', '') - if signout == 'True': - session.clear() - session['login'] = False - session['overdue'] = 0 - - db_path = model.getOption('admin_path') - if db_path == '': - return render_template('default/login.html') - else: - unauthorized_status = model.getOption('unauthorized_status') - if unauthorized_status == '0': - return render_template('default/path.html') - return Response(status=int(unauthorized_status)) - -# 验证码 -@blueprint.route('/code') -def code(): - import utils.vilidate as vilidate - vie = vilidate.vieCode() - codeImage = vie.GetCodeImage(80, 4) - out = io.BytesIO() - codeImage[0].save(out, "png") - session['code'] = mw.md5(''.join(codeImage[1]).lower()) - - img = Response(out.getvalue(), headers={'Content-Type': 'image/png'}) - return make_response(img) - -# 检查是否登录 -@blueprint.route('/check_login',methods=['GET','POST']) -def check_login(): - if isLogined(): - return mw.returnData(True,'已登录') - return mw.returnData(False,'未登录') - -# 执行登录操作 -@blueprint.route('/do_login', endpoint='do_login', methods=['POST']) -def do_login(): - username = request.form.get('username', '').strip() - password = request.form.get('password', '').strip() - code = request.form.get('code', '').strip() - - login_cache_count = 5 - login_cache_limit = cache.get('login_cache_limit') - - if 'code' in session: - if session['code'] != mw.md5(code): - if login_cache_limit == None: - login_cache_limit = 1 - else: - login_cache_limit = int(login_cache_limit) + 1 - - if login_cache_limit >= login_cache_count: - model.setOption('admin_close', 'yes') - return mw.returnJson(False, '面板已经关闭!') - - cache.set('login_cache_limit', login_cache_limit, timeout=10000) - login_cache_limit = cache.get('login_cache_limit') - login_err_msg = mw.getInfo("验证码错误,您还可以尝试[{1}]次!", (str(login_cache_count - login_cache_limit))) - mw.writeLog('用户登录', login_err_msg) - return mw.returnData(False, login_err_msg) - - info = model.getUserByName(username) - password = mw.md5(password) - - if info['name'] != username or info['password'] != password: - msg = "密码错误,帐号:{1},密码:{2},登录IP:{3}", (('****', '******', request.remote_addr)) - - if login_cache_limit == None: - login_cache_limit = 1 - else: - login_cache_limit = int(login_cache_limit) + 1 - - if login_cache_limit >= login_cache_count: - model.setOption('admin_close', 'yes') - return mw.returnData(False, '面板已经关闭!') - - cache.set('login_cache_limit', login_cache_limit, timeout=10000) - login_cache_limit = cache.get('login_cache_limit') - mw.writeLog('用户登录', mw.getInfo(msg)) - return mw.returnData(-1, mw.getInfo("用户名或密码错误,您还可以尝试[{1}]次!", (str(login_cache_count - login_cache_limit)))) - - - session['login'] = True - session['username'] = info['name'] - session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60 - - return mw.returnJson(1, '登录成功,正在跳转...') +from .dashboard import * +from .login import * diff --git a/web/admin/dashboard/dashboard.py b/web/admin/dashboard/dashboard.py new file mode 100644 index 000000000..a769c1e0c --- /dev/null +++ b/web/admin/dashboard/dashboard.py @@ -0,0 +1,113 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import io +import time +import base64 +import json + +from flask import Blueprint, render_template +from flask import make_response +from flask import redirect +from flask import Response +from flask import request,g + +from admin.common import isLogined +from admin.user_login_check import panel_login_required +from admin import cache,session + +import core.mw as mw +import thisdb + + +blueprint = Blueprint('dashboard', __name__, url_prefix='/', template_folder='../../templates') +@blueprint.route('/') +@panel_login_required +def index(): + name = thisdb.getOption('template', default='default') + return render_template('%s/index.html' % name) + +# 安全路径 +@blueprint.route('/',endpoint='admin_safe_path',methods=['GET']) +def admin_safe_path(path): + login = request.args.get('login', '') + if login != '': + try: + # print(login) + login_str = base64.b64decode(login) + login_str = login_str.decode('utf-8') + data = json.loads(login_str) + + time_now = time.time() * 1000 + time_diff = time_now - data['time'] + + if time_diff > 2000: + return redirect('/') + + + info = thisdb.getUserByName(data['username']) + if info is None: + return redirect('/') + + if info['password'] != mw.md5(data['password']): + return redirect('/') + + session['login'] = True + session['username'] = info['name'] + session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60 + + thisdb.updateUserLoginTime() + return redirect('/') + except Exception as e: + pass + + + db_path = thisdb.getOption('admin_path') + name = thisdb.getOption('template', default='default') + if isLogined(): + return redirect('/') + if db_path == path: + return render_template('%s/login.html' % name) + + unauthorized_status = thisdb.getOption('unauthorized_status') + if unauthorized_status == '0': + return render_template('%s/path.html' % name) + return Response(status=int(unauthorized_status)) + +# 仅针对webhook插件 +@blueprint.route("/hook", methods=['POST', 'GET']) +def webhook(): + # 兼容获取关键数据 + access_key = request.args.get('access_key', '').strip() + if access_key == '': + access_key = request.form.get('access_key', '').strip() + + params = request.args.get('params', '').strip() + if params == '': + params = request.form.get('params', '').strip() + + input_args = { + 'access_key': access_key, + 'params': params, + } + + wh_install_path = mw.getServerDir() + '/webhook' + if not os.path.exists(wh_install_path): + return mw.returnData(False, '请先安装WebHook插件!') + + package = mw.getPanelDir() + "/plugins/webhook" + if not package in sys.path: + sys.path.append(package) + + try: + import webhook_index + return webhook_index.runShellArgs(input_args) + except Exception as e: + return str(e) diff --git a/web/admin/dashboard/login.py b/web/admin/dashboard/login.py new file mode 100644 index 000000000..ef9cc9e5e --- /dev/null +++ b/web/admin/dashboard/login.py @@ -0,0 +1,231 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import io +import time + +from flask import Blueprint, render_template +from flask import make_response +from flask import redirect +from flask import Response +from flask import request,g + +from admin.common import isLogined +from admin.user_login_check import panel_login_required +from admin import cache,session + +import core.mw as mw +import thisdb + +from .dashboard import blueprint + + +def getErrorNum(key, limit=None): + key = mw.md5(key) + num = cache.get(key) + if not num: + num = 0 + if not limit: + return num + if limit > num: + return True + return False + + +def setErrorNum(key, empty=False, expire=3600): + key = mw.md5(key) + num = cache.get(key) + if not num: + num = 0 + else: + if empty: + cache.delete(key) + return True + cache.set(key, num + 1, expire) + return True + +def login_temp_user(token): + if len(token) != 32: + return '错误的参数!' + + skey = mw.getClientIp() + '_temp_login' + if not getErrorNum(skey, 10): + return '连续10次验证失败,禁止1小时' + + stime = int(time.time()) + + tmp_data = thisdb.getTempLoginByToken(token) + if not tmp_data: + setErrorNum(skey) + return '验证失败!' + + if stime > int(tmp_data['expire']): + setErrorNum(skey) + return "过期" + + user_data = thisdb.getUserById(1) + login_addr = mw.getClientIp() + ":" + str(request.environ.get('REMOTE_PORT')) + mw.writeLog('用户临时登录', "登录成功,帐号:{1},登录IP:{2}",(user_data['name'], login_addr)) + + mw.M('temp_login').where('id=?',(tmp_data['id'],)).update({"login_time": stime, 'state': 1, 'login_addr': login_addr}) + + session['login'] = True + session['username'] = user_data['name'] + session['tmp_login'] = True + session['tmp_login_id'] = str(tmp_data['id']) + session['tmp_login_expire'] = int(tmp_data['expire']) + session['uid'] = user_data['id'] + + return redirect('/') + +# 登录页: 当设置了安全路径,本页失效。 +@blueprint.route('/login') +def login(): + name = thisdb.getOption('template', default='default') + + # 临时登录功能 + token = request.args.get('tmp_token', '').strip() + if token != '': + return login_temp_user(token) + + # 注销登录 + signout = request.args.get('signout', '') + if signout == 'True': + session.clear() + session['login'] = False + session['overdue'] = 0 + + db_path = thisdb.getOption('admin_path') + if db_path == '': + return render_template('%s/login.html' % name) + else: + unauthorized_status = thisdb.getOption('unauthorized_status') + if unauthorized_status == '0': + return render_template('%s/path.html' % name) + return Response(status=int(unauthorized_status)) + +@blueprint.route('/close') +def close(): + name = thisdb.getOption('template', default='default') + admin_close = thisdb.getOption('admin_close') + if admin_close == 'no': + return redirect('/', code=302) + return render_template('%s/close.html' % name) + + +# 验证码 +@blueprint.route('/code') +def code(): + import utils.vilidate as vilidate + vie = vilidate.vieCode() + codeImage = vie.GetCodeImage(80, 4) + out = io.BytesIO() + codeImage[0].save(out, "png") + session['code'] = mw.md5(''.join(codeImage[1]).lower()) + + img = Response(out.getvalue(), headers={'Content-Type': 'image/png'}) + return make_response(img) + +# 检查是否登录 +@blueprint.route('/check_login',methods=['GET','POST']) +def check_login(): + if isLogined(): + return mw.returnData(True,'已登录') + return mw.returnData(False,'未登录') + +@blueprint.route("/verify_login", methods=['POST']) +def verifyLogin(): + import pyotp + + username = request.form.get('username', '').strip() + password = request.form.get('password', '').strip() + password = mw.md5(password) + + info = thisdb.getUserByRoot() + if info['name'] != username or info['password'] != password: + return mw.returnJson(-1, "密码错误?") + + auth = request.form.get('auth', '').strip() + two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False}) + if two_step_verification['open']: + sec = mw.deDoubleCrypt('mdserver-web', two_step_verification['secret']) + totp = pyotp.TOTP(sec) + if totp.verify(auth): + session['login'] = True + session['username'] = info['name'] + session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60 + + thisdb.updateUserLoginTime() + return mw.returnData(1, '二次验证成功!') + return mw.returnData(-1, '二次验证失败!') + +# 执行登录操作 +@blueprint.route('/do_login', endpoint='do_login', methods=['POST']) +def do_login(): + admin_close = thisdb.getOption('admin_close') + if admin_close == 'yes': + return mw.returnData(False, '面板已经关闭!') + + username = request.form.get('username', '').strip() + password = request.form.get('password', '').strip() + code = request.form.get('code', '').strip() + + login_cache_count = 5 + login_cache_limit = cache.get('login_cache_limit') + + if 'code' in session: + if session['code'] != mw.md5(code): + if login_cache_limit == None: + login_cache_limit = 1 + else: + login_cache_limit = int(login_cache_limit) + 1 + + if login_cache_limit >= login_cache_count: + thisdb.setOption('admin_close', 'yes') + return mw.returnData(False, '面板已经关闭!') + + cache.set('login_cache_limit', login_cache_limit, timeout=10000) + login_cache_limit = cache.get('login_cache_limit') + login_err_msg = mw.getInfo("验证码错误,您还可以尝试[{1}]次!", (str(login_cache_count - login_cache_limit))) + mw.writeLog('用户登录', login_err_msg) + return mw.returnData(False, login_err_msg) + + info = thisdb.getUserByName(username) + password = mw.md5(password) + + if info['name'] != username or info['password'] != password: + msg = "密码错误,帐号:{1},密码:{2},登录IP:{3}", (('****', '******', request.remote_addr)) + + if login_cache_limit == None: + login_cache_limit = 1 + else: + login_cache_limit = int(login_cache_limit) + 1 + + if login_cache_limit >= login_cache_count: + thisdb.setOption('admin_close', 'yes') + return mw.returnData(False, '面板已经关闭!') + + cache.set('login_cache_limit', login_cache_limit, timeout=10000) + login_cache_limit = cache.get('login_cache_limit') + mw.writeLog('用户登录', mw.getInfo(msg)) + return mw.returnData(-1, mw.getInfo("用户名或密码错误,您还可以尝试[{1}]次!", (str(login_cache_count - login_cache_limit)))) + + cache.delete('login_cache_limit') + # 二步验证密钥 + two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False}) + if two_step_verification['open']: + return mw.returnData(2, '需要两步验证!') + + session['login'] = True + session['username'] = info['name'] + session['overdue'] = int(time.time()) + 7 * 24 * 60 * 60 + + thisdb.updateUserLoginTime() + return mw.returnData(1, '登录成功,正在跳转...') diff --git a/web/admin/files/__init__.py b/web/admin/files/__init__.py index bd14375c8..450673456 100644 --- a/web/admin/files/__init__.py +++ b/web/admin/files/__init__.py @@ -8,149 +8,5 @@ # Author: midoks # --------------------------------------------------------------------------------- -import os - -from flask import Blueprint, render_template -from flask import request - - -from admin import model -from admin.user_login_check import panel_login_required -import core.mw as mw -import utils.file as file - -blueprint = Blueprint('files', __name__, url_prefix='/files', template_folder='../../templates/default') -@blueprint.route('/index', endpoint='index') -@panel_login_required -def index(): - return render_template('files.html') - -# 获取文件内容 -@blueprint.route('/get_body', endpoint='get_file_body', methods=['POST']) -@panel_login_required -def get_file_body(): - path = request.form.get('path', '') - return file.getFileBody(path) - -# 获取文件内容 -@blueprint.route('/save_body', endpoint='save_body', methods=['POST']) -@panel_login_required -def save_body(): - path = request.form.get('path', '') - data = request.form.get('data', '') - encoding = request.form.get('encoding', '') - if not os.path.exists(path): - return mw.returnData(False, '文件不存在') - try: - if encoding == 'ascii': - encoding = 'utf-8' - - data = data.encode(encoding, errors='ignore').decode(encoding) - fp = open(path, 'w+', encoding=encoding) - fp.write(data) - fp.close() - - if path.find("web_conf") > 0: - mw.restartWeb() - mw.writeLog('文件管理', '文件[{1}]保存成功', (path,)) - return mw.returnData(True, '文件保存成功') - except Exception as ex: - return mw.returnData(False, '文件保存错误:' + str(ex)) - -# 获取文件内容(最新行数) -@blueprint.route('/get_last_body', endpoint='get_file_last_body', methods=['POST']) -@panel_login_required -def get_file_last_body(): - path = request.form.get('path', '') - line = request.form.get('line', '100') - - if not os.path.exists(path): - return mw.returnData(False, '文件不存在', (path,)) - - try: - data = mw.getLastLine(path, int(line)) - return mw.returnData(True, 'OK', data) - except Exception as ex: - return mw.returnData(False, '无法正确读取文件!' + str(ex)) - - -# 获取文件列表 -@blueprint.route('/get_dir', endpoint='get_dir', methods=['POST']) -@panel_login_required -def get_dir(): - path = request.form.get('path', '') - if not os.path.exists(path): - path = mw.getFatherDir() + '/wwwroot' - search = request.args.get('search', '').strip().lower() - search_all = request.args.get('all', '').strip().lower() - page = request.args.get('p', '1').strip().lower() - row = request.args.get('row', '10') - order = request.form.get('order', '') - - if search_all == 'yes' and search != '': - dir_list = file.getAllDirList(path, int(page), int(row),order, search) - else: - dir_list = file.getDirList(path, int(page), int(row),order, search) - - dir_list['page'] = mw.getPage({'p':page, 'row': row, 'tojs':'getFiles', 'count': dir_list['count']}, '1,2,3,4,5,6,7,8') - return dir_list - -# 获取站点日志目录 -@blueprint.route('/get_dir_size', endpoint='get_dir_size', methods=['POST']) -@panel_login_required -def get_dir_size(): - path = request.form.get('path', '') - size = file.getDirSize(path) - return mw.returnData(True, mw.toSize(size)) - - -# 删除文件 -@blueprint.route('/delete', endpoint='delete', methods=['POST']) -@panel_login_required -def delete(): - path = request.form.get('path', '') - return file.fileDelete(path) - - -# 删除文件 -@blueprint.route('/delete_dir', endpoint='delete_dir', methods=['POST']) -@panel_login_required -def delete_dir(): - path = request.form.get('path', '') - return file.dirDelete(path) - - -# 回收站文件 -@blueprint.route('/get_recycle_bin', endpoint='get_recycle_bin', methods=['POST']) -@panel_login_required -def get_recycle_bin(): - return file.getRecycleBin() - -# 回收站文件恢复 -@blueprint.route('/re_recycle_bin', endpoint='re_recycle_bin', methods=['POST']) -@panel_login_required -def re_recycle_bin(): - path = request.form.get('path', '') - return file.reRecycleBin(path) - -# 回收站文件 -@blueprint.route('/recycle_bin', endpoint='recycle_bin', methods=['POST']) -@panel_login_required -def recycle_bin(): - return file.toggleRecycleBin() - - - - - - - - - - - - - - - - +from .files import * +from .recycle import * diff --git a/web/admin/files/files.py b/web/admin/files/files.py new file mode 100644 index 000000000..e114061d6 --- /dev/null +++ b/web/admin/files/files.py @@ -0,0 +1,322 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import time +import json + +from flask import Blueprint, render_template +from flask import request +from flask import make_response +from flask import send_file +from flask import send_from_directory + +from werkzeug.utils import secure_filename + +from admin.user_login_check import panel_login_required +from admin import session + +import core.mw as mw +import utils.file as file +import thisdb + +blueprint = Blueprint('files', __name__, url_prefix='/files', template_folder='../../templates') +@blueprint.route('/index', endpoint='index') +@panel_login_required +def index(): + name = thisdb.getOption('template', default='default') + return render_template('%s/files.html' % name) + +# 获取文件内容 +@blueprint.route('/check_exists_files', endpoint='check_exists_files', methods=['POST']) +@panel_login_required +def check_exists_files(): + dfile = request.form.get('dfile', '') + filename = request.form.get('filename', '') + data = [] + filesx = [] + if filename == '': + filesx = json.loads(session['selected']['data']) + else: + filesx.append(filename) + + for fn in filesx: + if fn == '.': + continue + filename = dfile + '/' + fn + if os.path.exists(filename): + tmp = {} + stat = os.stat(filename) + tmp['filename'] = fn + tmp['size'] = os.path.getsize(filename) + tmp['mtime'] = str(int(stat.st_mtime)) + data.append(tmp) + return mw.returnData(True, 'ok', data) + + +# 粘贴内容 +@blueprint.route('/batch_paste', endpoint='batch_paste', methods=['POST']) +@panel_login_required +def batch_paste(): + path = request.form.get('path', '') + stype = request.form.get('type', '') + return file.batchPaste(path, stype) + +# 压缩文件 +@blueprint.route('/zip', endpoint='zip', methods=['POST']) +@panel_login_required +def zip(): + sfile = request.form.get('sfile', '') + dfile = request.form.get('dfile', '') + stype = request.form.get('type', '') + path = request.form.get('path', '') + return file.zip(sfile, dfile, stype, path) + +# 文件权限查看 +@blueprint.route('/file_access', endpoint='file_access', methods=['POST']) +@panel_login_required +def file_access(): + filename = request.form.get('filename', '') + data = file.getAccess(filename) + data['sys_users'] = file.getSysUserList() + return data + +# 设置权限 +@blueprint.route('/set_file_access', endpoint='set_file_access', methods=['POST']) +@panel_login_required +def set_file_access(): + if mw.isAppleSystem(): + return mw.returnData(True, '开发机不设置!') + + filename = request.form.get('filename', '') + user = request.form.get('user', '') + access = request.form.get('access', '755') + return file.setFileAccess(filename, user, access) + + +# 复制文件内容 +@blueprint.route('/copy_file', endpoint='copy_file', methods=['POST']) +@panel_login_required +def copy_file(): + sfile = request.form.get('sfile', '') + dfile = request.form.get('dfile', '') + return file.copyFile(sfile, dfile) + +# 获取文件内容 +@blueprint.route('/get_body', endpoint='get_body', methods=['POST']) +@panel_login_required +def get_body(): + path = request.form.get('path', '') + return file.getFileBody(path) + +# 获取文件内容 +@blueprint.route('/save_body', endpoint='save_body', methods=['POST']) +@panel_login_required +def save_body(): + path = request.form.get('path', '') + data = request.form.get('data', '') + encoding = request.form.get('encoding', '') + return file.saveBody(path,data,encoding) + +# 获取文件内容(最新行数) +@blueprint.route('/get_last_body', endpoint='get_file_last_body', methods=['POST']) +@panel_login_required +def get_file_last_body(): + path = request.form.get('path', '') + line = request.form.get('line', '100') + + if not os.path.exists(path): + return mw.returnData(False, '文件不存在', (path,)) + + try: + data = mw.getLastLine(path, int(line)) + return mw.returnData(True, 'OK', data) + except Exception as ex: + return mw.returnData(False, '无法正确读取文件!' + str(ex)) + + +# 获取文件列表 +@blueprint.route('/get_dir', endpoint='get_dir', methods=['POST']) +@panel_login_required +def get_dir(): + path = request.form.get('path', '') + if not os.path.exists(path): + path = mw.getFatherDir() + '/wwwroot' + search = request.args.get('search', '').strip().lower() + search_all = request.args.get('all', '').strip().lower() + page = request.args.get('p', '1').strip().lower() + row = request.args.get('row', '10') + order = request.form.get('order', '') + + if search_all == 'yes' and search != '': + dir_list = file.getAllDirList(path, int(page), int(row),order, search) + else: + dir_list = file.getDirList(path, int(page), int(row),order, search) + + dir_list['page'] = mw.getPage({'p':page, 'row': row, 'tojs':'getFiles', 'count': dir_list['count']}, '1,2,3,4,5,6,7,8') + return dir_list + +# 解压 +@blueprint.route('/uncompress', endpoint='uncompress', methods=['POST']) +@panel_login_required +def uncompress(): + sfile = request.form.get('sfile', '') + dfile = request.form.get('dfile', '') + path = request.form.get('path', '') + return file.uncompress(sfile, dfile, path) + +# 批量操作 +@blueprint.route('/set_batch_data', endpoint='set_batch_data', methods=['POST']) +@panel_login_required +def set_batch_data(): + path = request.form.get('path', '') + stype = request.form.get('type', '') + access = request.form.get('access', '') + user = request.form.get('user', '') + data = request.form.get('data') + return file.setBatchData(path, stype, access, user, data) + +# 上传文件 +@blueprint.route('/upload_file', endpoint='upload_file', methods=['POST']) +@panel_login_required +def upload_file(): + path = request.args.get('path', '') + if not os.path.exists(path): + os.makedirs(path) + f = request.files['zunfile'] + filename = os.path.join(path, f.filename) + + s_path = path + if os.path.exists(filename): + s_path = filename + p_stat = os.stat(s_path) + + # print(filename) + f.save(filename) + os.chown(filename, p_stat.st_uid, p_stat.st_gid) + os.chmod(filename, p_stat.st_mode) + + msg = mw.getInfo('上传文件[{1}] 到 [{2}]成功!', (filename, path)) + mw.writeLog('文件管理', msg) + return mw.returnData(True, '上传成功!') + + +# 上传文件 +@blueprint.route('/upload_segment', endpoint='upload_segment', methods=['POST']) +@panel_login_required +def upload_segment(): + path = request.form.get('path', '') + name = request.form.get('name', '') + size = request.form.get('size') + start = request.form.get('start') + dir_mode = request.form.get('dir_mode', '') + file_mode = request.form.get('file_mode', '') + b64_data = request.form.get('b64_data', '0') + upload_files = request.files.getlist("blob") + return file.uploadSegment(path,name,size,start,dir_mode,file_mode,b64_data,upload_files) + + +# 修改文件名 +@blueprint.route('/mv_file', endpoint='mv_file', methods=['POST']) +@panel_login_required +def mv_file(): + sfile = request.form.get('sfile', '') + dfile = request.form.get('dfile', '') + return file.mvFile(sfile, dfile) + +# 创建文件 +@blueprint.route('/create_file', endpoint='create_file', methods=['POST']) +@panel_login_required +def create_file(): + path = request.form.get('path', '') + return file.createFile(path) + + +# 创建目录 +@blueprint.route('/create_dir', endpoint='create_dir', methods=['POST']) +@panel_login_required +def create_dir(): + path = request.form.get('path', '') + return file.createDir(path) + + +# 获取站点日志目录 +@blueprint.route('/get_dir_size', endpoint='get_dir_size', methods=['POST']) +@panel_login_required +def get_dir_size(): + path = request.form.get('path', '') + size = file.getDirSize(path) + return mw.returnData(True, mw.toSize(size)) + + +# 删除文件 +@blueprint.route('/delete', endpoint='delete', methods=['POST']) +@panel_login_required +def delete(): + path = request.form.get('path', '') + return file.fileDelete(path) + + +# 删除文件 +@blueprint.route('/delete_dir', endpoint='delete_dir', methods=['POST']) +@panel_login_required +def delete_dir(): + path = request.form.get('path', '') + return file.dirDelete(path) + +# 下载文件 +@blueprint.route('/download', endpoint='download', methods=['GET']) +@panel_login_required +def download(): + filename = request.args.get('filename', '') + if not os.path.exists(filename): + return '' + is_attachment = True + if filename.endswith(".svg"): + is_attachment = False + + response = make_response(send_from_directory(os.path.dirname(filename), os.path.basename(filename), as_attachment=is_attachment)) + return response + +# 远程下载 +@blueprint.route('/download_file', endpoint='download_file', methods=['POST']) +@panel_login_required +def download_file(): + url = request.form.get('url', '') + path = request.form.get('path', '') + filename = request.form.get('filename', '') + + execstr = url + '|mw|' + path + '/' + filename + execstr = execstr.strip() + + title = '下载文件[' + filename + ']' + thisdb.addTaskByDownload(name=title, cmd=execstr) + # self.setFileAccept(path + '/' + filename) + mw.triggerTask() + return mw.returnData(True, '已将下载任务添加到队列!') + +# 日志清空 +@blueprint.route('/close_logs', endpoint='close_logs', methods=['POST']) +@panel_login_required +def close_logs(): + return file.closeLogs() + + + + + + + + + + + + + diff --git a/web/admin/files/recycle.py b/web/admin/files/recycle.py new file mode 100644 index 000000000..5cc69fb67 --- /dev/null +++ b/web/admin/files/recycle.py @@ -0,0 +1,61 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os + +from flask import Blueprint, render_template +from flask import request +from flask import make_response +from flask import send_file +from flask import send_from_directory + +from admin.user_login_check import panel_login_required +import core.mw as mw +import utils.file as file + +from .files import blueprint +# 回收站文件 +@blueprint.route('/get_recycle_bin', endpoint='get_recycle_bin', methods=['POST']) +@panel_login_required +def get_recycle_bin(): + return file.getRecycleBin() + +# 回收站文件恢复 +@blueprint.route('/re_recycle_bin', endpoint='re_recycle_bin', methods=['POST']) +@panel_login_required +def re_recycle_bin(): + path = request.form.get('path', '') + return file.reRecycleBin(path) + +# 回收站文件 +@blueprint.route('/recycle_bin', endpoint='recycle_bin', methods=['POST']) +@panel_login_required +def recycle_bin(): + return file.toggleRecycleBin() + +# 回收站文件 +@blueprint.route('/close_recycle_bin', endpoint='close_recycle_bin', methods=['POST']) +@panel_login_required +def close_recycle_bin(): + return file.closeRecycleBin() + + + + + + + + + + + + + + diff --git a/web/admin/firewall/__init__.py b/web/admin/firewall/__init__.py index 182390ce4..d2eace702 100644 --- a/web/admin/firewall/__init__.py +++ b/web/admin/firewall/__init__.py @@ -13,18 +13,18 @@ from flask import Blueprint, render_template from flask import request from admin.user_login_check import panel_login_required -from admin.model import db, Firewall - from utils.firewall import Firewall as MwFirewall import core.mw as mw +import thisdb -blueprint = Blueprint('firewall', __name__, url_prefix='/firewall', template_folder='../../templates/default') +blueprint = Blueprint('firewall', __name__, url_prefix='/firewall', template_folder='../../templates') @blueprint.route('/index', endpoint='index') @panel_login_required def index(): - return render_template('firewall.html') + name = thisdb.getOption('template', default='default') + return render_template('%s/firewall.html' % name) # 防火墙列表 @@ -33,25 +33,7 @@ def index(): def get_list(): p = request.form.get('p', '1').strip() limit = request.form.get('limit', '10').strip() - - count = Firewall.query.filter_by().count() - pagination = Firewall.query.filter_by().paginate(page=int(p), per_page=int(limit)) - - rows = [] - for item in pagination.items: - t = {} - t['id'] = item.id - t['port'] = item.port - t['protocol'] = item.protocol - t['ps'] = item.ps - t['add_time'] = item.add_time - t['update_time'] = item.update_time - rows.append(t) - - data = {} - data['data'] = rows - data['page'] = mw.getPage({'count':count,'tojs':'getLogs','p':p,'row':limit}) - return data + return MwFirewall.instance().getList(p,limit) # 获取站点日志目录 @blueprint.route('/get_www_path', endpoint='get_www_path', methods=['POST']) @@ -64,17 +46,68 @@ def get_www_path(): @blueprint.route('/get_ssh_info', endpoint='get_ssh_info', methods=['POST']) @panel_login_required def get_ssh_info(): - mf = MwFirewall.instance() - return mf.getSshInfo() + return MwFirewall.instance().getSshInfo() # 切换ping开关 @blueprint.route('/set_ping', endpoint='set_ping', methods=['POST']) @panel_login_required def set_ping(): - mf = MwFirewall.instance() - return mf.setPing() - + status = request.form.get('status') + return MwFirewall.instance().setPing(status) + +# 修改ssh端口 +@blueprint.route('/set_ssh_port', endpoint='set_ssh_port', methods=['POST']) +@panel_login_required +def set_ssh_port(): + port = request.form.get('port', '1').strip() + return MwFirewall.instance().setSshPort(port) + +# 添加放行端口 +@blueprint.route('/add_accept_port', endpoint='add_accept_port', methods=['POST']) +@panel_login_required +def add_accept_port(): + port = request.form.get('port', '').strip() + ps = request.form.get('ps', '').strip() + protocol = request.form.get('protocol', '').strip() + stype = request.form.get('type', '').strip() + + return MwFirewall.instance().addAcceptPort(port, ps, stype, protocol=protocol) + +# 删除放行端口 +@blueprint.route('/del_accept_port', endpoint='del_accept_port', methods=['POST']) +@panel_login_required +def del_accept_port(): + port = request.form.get('port', '').strip() + firewall_id = request.form.get('id', '').strip() + protocol = request.form.get('protocol', '').strip() + return MwFirewall.instance().delAcceptPort(firewall_id, port, protocol=protocol) + + +# 设置防火墙状态 +@blueprint.route('/set_fw', endpoint='set_fw', methods=['POST']) +@panel_login_required +def set_fw(): + if mw.isAppleSystem(): + return mw.returnData(True, '开发机不能设置!') + status = request.form.get('status', '1') + return MwFirewall.instance().setFw(status) + +@blueprint.route('/set_ssh_pass_status', endpoint='set_ssh_pass_status', methods=['POST']) +@panel_login_required +def set_ssh_pass_status(): + if mw.isAppleSystem(): + return mw.returnData(True, '开发机不能设置!') + status = request.form.get('status', '1') + return MwFirewall.instance().setSshPassStatus(status) + +@blueprint.route('/set_ssh_pubkey_status', endpoint='set_ssh_pubkey_status', methods=['POST']) +@panel_login_required +def set_ssh_pubkey_status(): + if mw.isAppleSystem(): + return mw.returnData(True, '开发机不能设置!') + status = request.form.get('status', '1') + return MwFirewall.instance().setSshPubkeyStatus(status) diff --git a/web/admin/logs/__init__.py b/web/admin/logs/__init__.py index eb988b6e4..2d2218616 100644 --- a/web/admin/logs/__init__.py +++ b/web/admin/logs/__init__.py @@ -14,19 +14,17 @@ from flask import request from admin.user_login_check import panel_login_required -from admin import model -from admin.model import db, Logs - import core.mw as mw import utils.adult_log as adult_log +import thisdb # 日志页面 blueprint = Blueprint('logs', __name__, url_prefix='/logs', template_folder='../../templates') @blueprint.route('/index', endpoint='index') @panel_login_required def index(): - return render_template('default/logs.html') - + name = thisdb.getOption('template', default='default') + return render_template('%s/logs.html' % name) # 日志列表 @blueprint.route('/get_log_list', endpoint='get_log_list', methods=['POST']) @@ -36,33 +34,18 @@ def get_log_list(): size = request.form.get('limit', '10').strip() search = request.form.get('search', '').strip() - count = Logs.query.filter_by().count() - if search != '': - pagination = Logs.query.filter_by(Logs.type.like(search) or Logs.log.like(search)).paginate(page=int(p), per_page=int(size)) - else: - pagination = Logs.query.filter_by().order_by(Logs.id.desc()).paginate(page=int(p), per_page=int(size)) - - rows = [] - for item in pagination.items: - t = {} - t['id'] = item.id - t['type'] = item.type - t['log'] = item.log - t['uid'] = item.uid - t['add_time'] = item.add_time - rows.append(t) + info = thisdb.getLogsList(page=int(p),size=int(size), search=search) data = {} - data['data'] = rows - data['page'] = mw.getPage({'count':count,'tojs':'getLogs','p':p}) + data['data'] = info['list'] + data['page'] = mw.getPage({'count':info['count'],'tojs':'getLogs','p':p,'row':size}) return data # 日志清空 @blueprint.route('/del_panel_logs', endpoint='del_panel_logs', methods=['POST']) @panel_login_required def del_panel_logs(): - model.clearLog() - # mw.M('logs').dbPos(mw.getPanelDataDir(),'panel').where('id>?', (0,)).delete() + thisdb.clearLog() mw.writeLog('面板设置', '面板操作日志已清空!') return mw.returnData(True, '面板操作日志已清空!') @@ -71,7 +54,7 @@ def del_panel_logs(): @panel_login_required def get_audit_logs_files(): logs_file = adult_log.getAuditLogsFiles() - return logs_file + return mw.returnData(True, 'ok', logs_file) # 系统审计日志列表 @blueprint.route('/get_audit_file', endpoint='get_audit_file', methods=['POST']) diff --git a/web/admin/model/initdb.py b/web/admin/model/initdb.py deleted file mode 100644 index 8923feebc..000000000 --- a/web/admin/model/initdb.py +++ /dev/null @@ -1,194 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - - -from flask_sqlalchemy import SQLAlchemy -# from flask_security import UserMixin, RoleMixin -import config - -SCHEMA_VERSION = 1 - - -db = SQLAlchemy( - engine_options={ - 'pool_size': config.CONFIG_DATABASE_CONNECTION_POOL_SIZE, - 'max_overflow': config.CONFIG_DATABASE_CONNECTION_MAX_OVERFLOW, - } -) - - -class Version(db.Model): - """用于参考/升级的版本号""" - __tablename__ = 'version' - name = db.Column(db.String(32), primary_key=True) - value = db.Column(db.Integer(), nullable=False) - -class Role(db.Model): - """定义安全角色""" - __tablename__ = 'role' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - name = db.Column(db.String(128), unique=True, nullable=False) - description = db.Column(db.String(256), nullable=False) - -class Option(db.Model): - """定义类型""" - __tablename__ = 'option' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - name = db.Column(db.String(128), unique=True, nullable=False, comment="配置名称") - type = db.Column(db.String(50), unique=False, nullable=False, default='common',comment="计划类型:common/hook/web") - value = db.Column(db.TEXT, unique=False, nullable=False, comment="内容") - -class Users(db.Model): - """定义登录用户""" - __tablename__ = 'users' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - name = db.Column(db.String(128), unique=True, nullable=False, comment="用户名") - password = db.Column(db.String(128), unique=False, nullable=False, comment="密码") - login_ip = db.Column(db.String(128), unique=False, nullable=True, comment="登录IP") - login_time = db.Column(db.String(128), unique=False, nullable=True, comment="登录时间") - phone = db.Column(db.String(20), unique=False, nullable=False,comment="手机") - email = db.Column(db.String(320), nullable=False, comment="邮件") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - update_time = db.Column(db.TEXT, nullable=False, comment="更新时间") - -class Crontab(db.Model): - """定义计划任务""" - __tablename__ = 'crontab' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - name = db.Column(db.TEXT, unique=True, nullable=False, comment="任务名称") - type = db.Column(db.String(128), unique=False, nullable=False, comment="计划类型") - where1 = db.Column(db.TEXT, unique=False, nullable=True, comment="执行条件") - where_hour = db.Column(db.TEXT, unique=False, nullable=True, comment="执行条件/小时") - where_minute = db.Column(db.TEXT, unique=False, nullable=True, comment="执行条件/分钟") - echo = db.Column(db.TEXT, unique=False, nullable=True, comment="脚本保存名称") - sname = db.Column(db.TEXT, unique=False, nullable=True, default='',comment="名称") - sbody = db.Column(db.TEXT, unique=False, nullable=True, default='',comment="脚本") - stype = db.Column(db.TEXT, unique=False, nullable=True, default='',comment="脚本类型") - url_address = db.Column(db.TEXT, unique=False, nullable=True, default='',comment="URL访问地址") - backup_to = db.Column(db.TEXT, unique=False, nullable=True, comment="备份地址") - save = db.Column(db.Integer, unique=False, nullable=True, default=3,comment="备份数量") - status = db.Column(db.Integer, unique=False, nullable=True, default=1, comment="状态") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - update_time = db.Column(db.TEXT, nullable=False, comment="更新时间") - -class Logs(db.Model): - """定义日志""" - __tablename__ = 'logs' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - uid = db.Column(db.Integer(), unique=False, nullable=False, comment="用户ID") - type = db.Column(db.String(128), unique=False, nullable=False, comment="日志类型") - log = db.Column(db.TEXT, unique=False, nullable=True, comment="日志内容") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - -class Firewall(db.Model): - """定义防火墙""" - __tablename__ = 'firewall' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - port = db.Column(db.Integer(), unique=False, nullable=False, comment="端口") - protocol = db.Column(db.Integer(), unique=False, nullable=False, comment="协议/tcp/udp") - ps = db.Column(db.TEXT, unique=False, nullable=False, comment="备注") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - update_time = db.Column(db.TEXT, nullable=False, comment="更新时间") - - -class Backup(db.Model): - """定义备份""" - __tablename__ = 'backup' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - pid = db.Column(db.Integer(), unique=False, nullable=False, comment="父级ID") - type = db.Column(db.String(128), unique=False, nullable=False, comment="备份类型") - name = db.Column(db.TEXT, unique=False, nullable=False, comment="名称") - filename = db.Column(db.TEXT, unique=False, nullable=False, comment="文件绝对位置") - size = db.Column(db.Integer(), unique=False, nullable=False, comment="大小") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - -class Sites(db.Model): - """定义计划任务""" - __tablename__ = 'sites' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - name = db.Column(db.TEXT, unique=False, nullable=False, comment="网站名称") - path = db.Column(db.TEXT, unique=False, nullable=False, comment="网站路径") - index = db.Column(db.TEXT, unique=False, nullable=False, comment="") - ps = db.Column(db.TEXT, unique=False, nullable=False, comment="备注") - edate = db.Column(db.TEXT, unique=False, nullable=False, comment="到期时间") - type_id = db.Column(db.TEXT, unique=False, nullable=False, comment="分类ID") - status = db.Column(db.Integer(), unique=False, nullable=True, default=1, comment="状态") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - update_time = db.Column(db.TEXT, nullable=False, comment="更新时间") - -class SiteType(db.Model): - """定义站点类型""" - __tablename__ = 'site_type' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - name = db.Column(db.TEXT, unique=False, nullable=False, comment="类型名称") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - update_time = db.Column(db.TEXT, nullable=False, comment="更新时间") - -class Domain(db.Model): - """定义站点域名""" - __tablename__ = 'domain' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - pid = db.Column(db.Integer(), unique=False, nullable=False, comment="站点ID") - name = db.Column(db.TEXT, unique=False, nullable=False, comment="域名") - port = db.Column(db.Integer(), unique=False, nullable=False, comment="端口") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - -class Binding(db.Model): - """定义站点绑定""" - __tablename__ = 'binding' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - pid = db.Column(db.Integer(), unique=False, nullable=False, comment="站点ID") - domain = db.Column(db.TEXT, unique=False, nullable=False, comment="域名") - path = db.Column(db.TEXT, unique=False, nullable=False, comment="路径") - port = db.Column(db.Integer(), unique=False, nullable=False, comment="端口") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - - -class Tasks(db.Model): - """定义任务""" - __tablename__ = 'tasks' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - name = db.Column(db.TEXT, unique=False, nullable=False, comment="任务名称") - type = db.Column(db.TEXT, unique=False, nullable=False, comment="类型") - cmd = db.Column(db.TEXT, unique=False, nullable=False, comment="执行命令") - start = db.Column(db.Integer(), unique=False, nullable=True, comment="开始执行时间") - end = db.Column(db.Integer(), unique=False, nullable=True, comment="结束执行时间") - status = db.Column(db.Integer(), unique=False, nullable=True, default=1, comment="状态") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - - -class TempLogin(db.Model): - """定义临时登录""" - __tablename__ = 'temp_login' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - token = db.Column(db.TEXT, unique=False, nullable=False, comment="token") - salt = db.Column(db.TEXT, unique=False, nullable=False, comment="salt") - state = db.Column(db.Integer(), unique=False, nullable=False, comment="状态") - login_time = db.Column(db.Integer(), unique=False, nullable=True, comment="登录时间") - login_addr = db.Column(db.Integer(), unique=False, nullable=True, comment="登录地址") - logout_time = db.Column(db.Integer(), unique=False, nullable=True, comment="登出时间") - expire = db.Column(db.Integer(), unique=False, nullable=True, comment="过期时间") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - -class Panel(db.Model): - """定义其他面板""" - __tablename__ = 'panel' - id = db.Column(db.Integer(), primary_key=True,autoincrement=True, comment="ID") - title = db.Column(db.TEXT, unique=False, nullable=False, comment="标题") - url = db.Column(db.TEXT, unique=False, nullable=False, comment="地址") - username = db.Column(db.Integer(), unique=False, nullable=False, comment="用户名") - password = db.Column(db.Integer(), unique=False, nullable=True, comment="密码") - click = db.Column(db.Integer(), unique=False, nullable=True, comment="点击次数") - add_time = db.Column(db.TEXT, nullable=False, comment="添加时间") - - - - - diff --git a/web/admin/model/logs.py b/web/admin/model/logs.py deleted file mode 100644 index 94f911966..000000000 --- a/web/admin/model/logs.py +++ /dev/null @@ -1,54 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - - -import json -import time - -from admin.model import db, Logs - -def formatDate(format="%Y-%m-%d %H:%M:%S", times=None): - # 格式化指定时间戳 - if not times: - times = int(time.time()) - time_local = time.localtime(times) - return time.strftime(format, time_local) - -def clearLog(): - try: - # from sqlalchemy import text - # db.session.execute(text("DELETE FROM logs where id>1")) - db.session.query(Logs).filter(Logs.id > 0).delete(synchronize_session=False) - db.session.commit() - except Exception as e: - db.session.commit() - finally: - db.session.close() - return True - -def addLog(type, log, - uid: int | None = 1 -) -> str: - ''' - 添加日志 - :type -> str 类型 (必填) - :log -> str 日志内容 (必填) - :uid -> int 用户ID - ''' - add_time = formatDate() - add_logs = Logs( - uid=uid, - log=log, - type=type, - add_time=add_time) - db.session.add(add_logs) - db.session.commit() - db.session.close() - return True \ No newline at end of file diff --git a/web/admin/model/task.py b/web/admin/model/task.py deleted file mode 100644 index 66edfd33c..000000000 --- a/web/admin/model/task.py +++ /dev/null @@ -1,115 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -from admin.model import db, Tasks - -import core.mw as mw - -def getTaskCount( - status: int | None = -1 - ) -> int: - return Tasks.query.filter(Tasks.status==status).count() - -# 未执行任务总数 -def getTaskUnexecutedCount() -> int: - return Tasks.query.filter(Tasks.status!=1).count() - - -def addTask( - name: str | None = '常用任务', - cmd: str | None = None, - type: str | None = 'execshell', - status: int | None = 0, -): - ''' - 添加后台任务 - :name -> str 类型 - :cmd -> str 日志内容 (必填) - :type -> str 用户ID - ''' - if cmd is None: - return False - - add_time = mw.formatDate() - add_data = Tasks( - name=name, - cmd=cmd, - type=type, - start=0, - end=0, - status=status, - add_time=add_time) - db.session.add(add_data) - db.session.commit() - db.session.close() - return True - -def getTaskFirstByRun() -> None: - item = Tasks.query.filter(Tasks.status==-1).order_by(Tasks.id.asc()).first() - - if item is None: - return None - row = {} - row['id'] = item.id - row['name'] = item.name - row['type'] = item.type - row['cmd'] = item.cmd - row['start'] = item.start - row['end'] = item.end - row['status'] = item.status - row['add_time'] = item.add_time - return row - -def getTaskList( - status: int | None = 1, - page: int | None = 1, - size: int | None = 10, -): - pagination = Tasks.query.filter(Tasks.status==status).order_by(Tasks.id.asc()).paginate(page=int(page), per_page=int(size)) - - rows = [] - for item in pagination.items: - t = {} - t['id'] = item.id - t['name'] = item.name - t['type'] = item.type - t['cmd'] = item.cmd - t['start'] = item.start - t['end'] = item.end - t['status'] = item.status - t['add_time'] = item.add_time - rows.append(t) - return rows - - - - -def setTaskStatus(id, - status: int | None = 0 -): - Tasks.query.filter(Tasks.id==id).update({'status':status}) - db.session.commit() - return True - -def setTaskData(id, - start: int | None = None, - end: int | None = None, -): - update_data = {} - - if start is not None: - update_data['start'] = start - if end is not None: - update_data['end'] = end - - Tasks.query.filter(Tasks.id==id).update(update_data) - db.session.commit() - return True - diff --git a/web/admin/model/temp_login.py b/web/admin/model/temp_login.py deleted file mode 100644 index ac60f45c4..000000000 --- a/web/admin/model/temp_login.py +++ /dev/null @@ -1,44 +0,0 @@ -# coding:utf-8 - -# --------------------------------------------------------------------------------- -# MW-Linux面板 -# --------------------------------------------------------------------------------- -# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. -# --------------------------------------------------------------------------------- -# Author: midoks -# --------------------------------------------------------------------------------- - -import time - -from admin.model import db, TempLogin - -import core.mw as mw - -def getTempLoginByToken(token, -) -> None: - ''' - 获取用户信息通过用户名 - ''' - item = TempLogin.query.filter(TempLogin.token==token).first() - if item is None: - return None - row = {} - row['id'] = item.id - row['token'] = item.token - row['salt'] = item.salt - row['state'] = item.state - row['login_time'] = item.login_time - row['login_addr'] = item.login_addr - row['logout_time'] = item.logout_time - row['expire'] = item.expire - row['add_time'] = item.add_time - return row - -def clearTempLogin()->bool: - ''' - 清空过期数据 - ''' - now_time = int(time.time()) - TempLogin.query.filter(TempLogin.expire -# --------------------------------------------------------------------------------- - -from admin.model import db, Users - -import core.mw as mw - -def getUserByName(name, -) -> None: - ''' - 获取用户信息通过用户名 - ''' - item = Users.query.filter(Users.name==name).first() - if item is None: - return None - row = {} - row['id'] = item.id - row['name'] = item.name - row['password'] = item.password - row['login_ip'] = item.login_ip - row['login_time'] = item.login_time - row['phone'] = item.phone - row['email'] = item.email - row['add_time'] = item.add_time - row['update_time'] = item.update_time - return row - -def isLoginCheck(username, password) -> bool: - info = getUserByName(data['username']) - if info is None: - return False - - if info['password'] == mw.md5(password): - return True - return False - - -def getUserById(id, -) -> None: - ''' - 获取用户信息通过用户名 - ''' - item = Users.query.filter(Users.id==id).first() - if item is None: - return None - row = {} - row['id'] = item.id - row['name'] = item.name - row['password'] = item.password - row['login_ip'] = item.login_ip - row['login_time'] = item.login_time - row['phone'] = item.phone - row['email'] = item.email - row['add_time'] = item.add_time - row['update_time'] = item.update_time - return row - -def isLoginCheck(username, password) -> bool: - info = getUserByName(data['username']) - if info is None: - return False - - if info['password'] == mw.md5(password): - return True - return False diff --git a/web/admin/monitor/__init__.py b/web/admin/monitor/__init__.py index 0a540268a..29fcb5e0e 100644 --- a/web/admin/monitor/__init__.py +++ b/web/admin/monitor/__init__.py @@ -13,8 +13,11 @@ from flask import Blueprint, render_template from admin.user_login_check import panel_login_required -blueprint = Blueprint('monitor', __name__, url_prefix='/monitor', template_folder='../../templates/default') +import thisdb + +blueprint = Blueprint('monitor', __name__, url_prefix='/monitor', template_folder='../../templates') @blueprint.route('/index', endpoint='index') @panel_login_required def index(): - return render_template('monitor.html') \ No newline at end of file + name = thisdb.getOption('template', default='default') + return render_template('%s/monitor.html' % name) \ No newline at end of file diff --git a/web/admin/plugins/__init__.py b/web/admin/plugins/__init__.py index 8e8a1a1e2..d152ae3e3 100644 --- a/web/admin/plugins/__init__.py +++ b/web/admin/plugins/__init__.py @@ -14,54 +14,34 @@ import json from flask import Blueprint, render_template from flask import request -from utils.mwplugin import MwPlugin +from utils.plugin import plugin as MwPlugin from admin.user_login_check import panel_login_required -from admin import model import core.mw as mw import utils.config as utils_config +import thisdb -blueprint = Blueprint('plugins', __name__, url_prefix='/plugins', template_folder='../../templates/default') +blueprint = Blueprint('plugins', __name__, url_prefix='/plugins', template_folder='../../templates') @blueprint.route('/index', endpoint='index') @panel_login_required def index(): - return render_template('plugins.html') + name = thisdb.getOption('template', default='default') + return render_template('%s/plugins.html' % name) # 初始化检查,首页提示选择安装 @blueprint.route('/init', endpoint='init', methods=['POST']) @panel_login_required def init(): - plugin_names = { - 'openresty': '1.25.3', - 'php': '56', - 'swap': '1.1', - 'mysql': '5.7', - 'phpmyadmin': '4.4.15', - } + return MwPlugin.instance().init() - pn_dir = mw.getPluginDir() - pn_server_dir = mw.getServerDir() - pn_list = [] - for pn in plugin_names: - info = {} - pn_json = pn_dir + '/' + pn + '/info.json' - pn_server = pn_server_dir + '/' + pn - if not os.path.exists(pn_server): - - tmp = mw.readFile(pn_json) - tmp = json.loads(tmp) - - info['title'] = tmp['title'] - info['name'] = tmp['name'] - info['versions'] = tmp['versions'] - info['default_ver'] = plugin_names[pn] - pn_list.append(info) - else: - return mw.returnData(False, 'ok') - - return mw.returnData(True, 'ok', pn_list) +# 初始化安装 +@blueprint.route('/init_install', endpoint='init_install', methods=['POST']) +@panel_login_required +def init_install(): + plugin_list = request.form.get('list', '') + return MwPlugin.instance().initInstall(plugin_list) # 首页软件展示 @blueprint.route('/index_list', endpoint='index_list', methods=['GET','POST']) @@ -131,7 +111,7 @@ def menu(): pg = MwPlugin.instance() tag = request.args.get('tag', '') - hook_menu = model.getOptionByJson('hook_menu',type='hook',default=[]) + hook_menu = thisdb.getOptionByJson('hook_menu',type='hook',default=[]) content = '' for menu_data in hook_menu: if tag == menu_data['name'] and 'path' in menu_data: @@ -168,6 +148,23 @@ def file(): content = open(file, 'rb').read() return content + +# 插件上传 +@blueprint.route('/update_zip', endpoint='update_zip', methods=['POST']) +@panel_login_required +def update_zip(): + request_zip = request.files['plugin_zip'] + return MwPlugin.instance().updateZip(request_zip) + + +@blueprint.route('/input_zip', endpoint='input_zip', methods=['POST']) +@panel_login_required +def input_zip(): + plugin_name = request.form.get('plugin_name', '') + tmp_path = request.form.get('tmp_path', '') + return MwPlugin.instance().inputZipApi(plugin_name,tmp_path) + + # 插件设置页 @blueprint.route('/setting', endpoint='setting', methods=['GET']) @panel_login_required diff --git a/web/admin/setting/__init__.py b/web/admin/setting/__init__.py index adfb25a3e..1dfb93200 100644 --- a/web/admin/setting/__init__.py +++ b/web/admin/setting/__init__.py @@ -9,13 +9,15 @@ # --------------------------------------------------------------------------------- from .setting import * + from .temp_login import * from .timezone import * from .secondary_verifiy import * +from .notify_email import * +from .notify_tgbot import * +from .app import * +from .panel_ssl import * +from .panel_bookmark import * - - - - \ No newline at end of file diff --git a/web/admin/setting/app.py b/web/admin/setting/app.py new file mode 100644 index 000000000..273e19ce8 --- /dev/null +++ b/web/admin/setting/app.py @@ -0,0 +1,92 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import re +import json +import os +import time + +from flask import Blueprint, render_template +from flask import request + +from admin import session +from admin.user_login_check import panel_login_required + +import core.mw as mw +import utils.config as utils_config + +from .setting import blueprint +import thisdb + +# 设置API +@blueprint.route('/set_panel_api', endpoint='set_panel_api', methods=['POST']) +@panel_login_required +def set_panel_api(): + panel_api = thisdb.getOptionByJson('panel_api', default={'open':False}) + if not panel_api['open']: + panel_api['open'] = True + thisdb.setOption('panel_api', json.dumps(panel_api)) + return mw.returnData(True, '开启API成功!') + else: + panel_api['open'] = False + thisdb.setOption('panel_api', json.dumps(panel_api)) + return mw.returnData(True, '关闭API成功!') + + +# 获取APP列表 +@blueprint.route('/get_app_list', endpoint='get_app_list', methods=['POST']) +@panel_login_required +def get_app_list(): + limit = request.form.get('limit', '5').strip() + page = request.form.get('page', '1').strip() + tojs = request.form.get('tojs', 'getAppList').strip() + + info = thisdb.getAppList(page=int(page),size=int(limit)) + data = {} + data['data'] = info['list'] + data['page'] = mw.getPage({'count':info['count'],'tojs':tojs,'p':page,'row':limit}) + return data + + +# 添加APP列表 +@blueprint.route('/add_app', endpoint='add_app', methods=['POST']) +@panel_login_required +def add_app(): + app_id = request.form.get('app_id', '').strip() + app_secret = request.form.get('app_secret', '1').strip() + limit_addr = request.form.get('limit_addr', '').strip() + if limit_addr == '': + return mw.returnData(False, 'IP限制不能为空!') + + rid = thisdb.addApp(app_id,app_secret,limit_addr) + if rid > 0: + return mw.returnData(True, '添加成功!') + return mw.returnData(False, '添加失败!') + +# 添加APP列表 +@blueprint.route('/toggle_app_status', endpoint='toggle_app_status', methods=['POST']) +@panel_login_required +def toggle_app_status(): + aid = request.form.get('id', '').strip() + rid = thisdb.toggleAppStatus(aid) + if rid > 0: + return mw.returnData(True, '切换成功!') + return mw.returnData(False, '切换失败!') + + +# 获取APP列表 +@blueprint.route('/delete_app', endpoint='delete_app', methods=['POST']) +@panel_login_required +def delete_app(): + aid = request.form.get('id', '').strip() + rid = thisdb.deleteAppById(aid) + if rid > 0: + return mw.returnData(True, '删除成功!') + return mw.returnData(False, '删除失败!') diff --git a/web/admin/setting/notify_email.py b/web/admin/setting/notify_email.py new file mode 100644 index 000000000..9bd8b3f2b --- /dev/null +++ b/web/admin/setting/notify_email.py @@ -0,0 +1,89 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import re +import json +import os +import time + +from flask import Blueprint, render_template +from flask import request + +from admin import session +from admin.user_login_check import panel_login_required + +import core.mw as mw +import utils.config as utils_config + +from .setting import blueprint +import thisdb + +# 获取邮件信息 +@blueprint.route('/get_notify_email', endpoint='get_notify_email', methods=['POST']) +@panel_login_required +def get_notify_email(): + notify_email = thisdb.getOptionByJson('notify_email', default={'open':False}, type='notify') + + if 'cfg' in notify_email: + decrypt_data = mw.deDoubleCrypt('email', notify_email['cfg']) + notify_email['email'] = json.loads(decrypt_data) + else: + notify_email['email'] = {'smtp_host':'','smtp_port':'','smtp_ssl':'','to_mail_addr':'','username':'','password':''} + + return mw.returnData(True,'ok',notify_email) + + +# 设置邮件信息 +@blueprint.route('/set_notify_email', endpoint='set_notify_email', methods=['POST']) +@panel_login_required +def set_notify_email(): + tag = request.form.get('tag', '').strip() + data = request.form.get('data', '').strip() + + crypt_data = mw.enDoubleCrypt(tag, data) + + notify_email = thisdb.getOptionByJson('notify_email', default={'open':False}, type='notify') + notify_email['cfg'] = crypt_data + + thisdb.setOption('notify_email', json.dumps(notify_email), type='notify') + return mw.returnData(True,'设置成功') + + +# 设置邮件测试 +@blueprint.route('/set_notify_email_test', endpoint='set_notify_email_test', methods=['POST']) +@panel_login_required +def set_notify_email_test(): + tag = request.form.get('tag', '').strip() + tag_data = request.form.get('data', '').strip() + + data = json.loads(tag_data) + test_pass = mw.emailNotifyTest(data) + if test_pass == True: + return mw.returnData(True, '验证成功') + return mw.returnData(False, '验证失败:'+test_pass) + +# 切换邮件开关 +@blueprint.route('/set_notify_email_enable', endpoint='set_notify_email_enable', methods=['POST']) +@panel_login_required +def set_notify_email_enable(): + tag = request.form.get('tag', '').strip() + data = request.form.get('data', '').strip() + + notify_email = thisdb.getOptionByJson('notify_email', default={'open':False}, type='notify') + + if notify_email['open']: + op_action = '关闭' + notify_email['open'] = False + else: + op_action = '开启' + notify_email['open'] = True + + thisdb.setOption('notify_email', json.dumps(notify_email), type='notify') + return mw.returnData(True, op_action+'成功') \ No newline at end of file diff --git a/web/admin/setting/notify_tgbot.py b/web/admin/setting/notify_tgbot.py new file mode 100644 index 000000000..f3bc11a5d --- /dev/null +++ b/web/admin/setting/notify_tgbot.py @@ -0,0 +1,85 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import re +import json +import os +import time + +from flask import Blueprint, render_template +from flask import request + +from admin import session +from admin.user_login_check import panel_login_required + +import core.mw as mw +import utils.config as utils_config + +from .setting import blueprint +import thisdb + +# 获取邮件信息 +@blueprint.route('/get_notify_tgbot', endpoint='get_notify_tgbot', methods=['POST']) +@panel_login_required +def get_notify_tgbot(): + notify_tgbot = thisdb.getOptionByJson('notify_tgbot', default={'open':False}, type='notify') + if 'cfg' in notify_tgbot: + decrypt_data = mw.deDoubleCrypt('tgbot', notify_tgbot['cfg']) + notify_tgbot['tgbot'] = json.loads(decrypt_data) + else: + notify_tgbot['tgbot'] = [] + return mw.returnData(True,'ok',notify_tgbot) + + +# 设置邮件信息 +@blueprint.route('/set_notify_tgbot', endpoint='set_notify_tgbot', methods=['POST']) +@panel_login_required +def set_notify_tgbot(): + data = request.form.get('data', '').strip() + + crypt_data = mw.enDoubleCrypt('tgbot', data) + + notify_tgbot = thisdb.getOptionByJson('notify_tgbot', default={'open':False}, type='notify') + notify_tgbot['cfg'] = crypt_data + + thisdb.setOption('notify_tgbot', json.dumps(notify_tgbot), type='notify') + return mw.returnData(True,'设置成功') + + +# 设置邮件测试 +@blueprint.route('/set_notify_tgbot_test', endpoint='set_notify_tgbot_test', methods=['POST']) +@panel_login_required +def set_notify_tgbot_test(): + tag_data = request.form.get('data', '').strip() + + tmp = json.loads(tag_data) + test_pass = mw.tgbotNotifyTest(tmp['app_token'], tmp['chat_id']) + if test_pass == True: + return mw.returnData(True, '验证成功') + return mw.returnData(False, '验证失败:'+test_pass) + +# 切换邮件开关 +@blueprint.route('/set_notify_tgbot_enable', endpoint='set_notify_tgbot_enable', methods=['POST']) +@panel_login_required +def set_notify_tgbot_enable(): + tag = request.form.get('tag', '').strip() + data = request.form.get('data', '').strip() + + notify_tgbot = thisdb.getOptionByJson('notify_tgbot', default={'open':False}, type='notify') + + if notify_tgbot['open']: + op_action = '关闭' + notify_tgbot['open'] = False + else: + op_action = '开启' + notify_tgbot['open'] = True + + thisdb.setOption('notify_tgbot', json.dumps(notify_tgbot), type='notify') + return mw.returnData(True, op_action+'成功') \ No newline at end of file diff --git a/web/admin/setting/panel_bookmark.py b/web/admin/setting/panel_bookmark.py new file mode 100644 index 000000000..922269e8f --- /dev/null +++ b/web/admin/setting/panel_bookmark.py @@ -0,0 +1,88 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import re +import json +import os +import time + +from flask import Blueprint, render_template +from flask import request + +from admin import session +from admin.user_login_check import panel_login_required + +import core.mw as mw +import utils.config as utils_config + +from utils.setting import setting as MwSetting + +from .setting import blueprint +import thisdb + +# 添加面板书签 +@blueprint.route('/add_panel_info', endpoint='add_panel_info', methods=['POST']) +@panel_login_required +def add_panel_info(): + title = request.form.get('title', '') + url = request.form.get('url', '') + username = request.form.get('username', '') + password = request.form.get('password', '') + + # 校验是还是重复 + isAdd = mw.M('panel').where('title=? OR url=?', (title, url)).count() + if isAdd: + return mw.returnData(False, '备注或面板地址重复!') + isRe = mw.M('panel').add('title,url,username,password,click,add_time', + (title, url, username, password, 0, int(time.time()))) + if isRe: + return mw.returnData(True, '添加成功!') + return mw.returnData(False, '添加失败!') + +# 取面板书签列表 +@blueprint.route('/get_panel_list', endpoint='get_panel_list', methods=['GET','POST']) +@panel_login_required +def get_panel_list(): + data = mw.M('panel').field('id,title,url,username,password,click,add_time').order('click desc').select() + return mw.returnData(True, 'ok!', data) + + +# 删除面板书签 +@blueprint.route('/del_panel_info', endpoint='del_panel_info', methods=['GET','POST']) +@panel_login_required +def del_panel_info(): + panel_id = request.form.get('id', '') + isExists = mw.M('panel').where('id=?', (panel_id,)).count() + if not isExists: + return mw.returnData(False, '指定面板资料不存在!') + mw.M('panel').where('id=?', (panel_id,)).delete() + return mw.returnData(True, '删除成功!') + + +# 设置面板域名 +@blueprint.route('/set_panel_info', endpoint='set_panel_info', methods=['POST']) +@panel_login_required +def set_panel_info(): + title = request.form.get('title', '') + url = request.form.get('url', '') + username = request.form.get('username', '') + password = request.form.get('password', '') + panel_id = request.form.get('id', '') + # 校验是还是重复 + isSave = mw.M('panel').where('(title=? OR url=?) AND id!=?', (title, url, panel_id)).count() + if isSave: + return mw.returnData(False, '备注或面板地址重复!') + + # 更新到数据库 + isRe = mw.M('panel').where('id=?', (panel_id,)).save('title,url,username,password', (title, url, username, password)) + if isRe: + return mw.returnData(True, '修改成功!') + return mw.returnData(False, '修改失败!') + diff --git a/web/admin/setting/panel_ssl.py b/web/admin/setting/panel_ssl.py new file mode 100644 index 000000000..19f904dcd --- /dev/null +++ b/web/admin/setting/panel_ssl.py @@ -0,0 +1,80 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import re +import json +import os +import time + +from flask import Blueprint, render_template +from flask import request + +from admin import session +from admin.user_login_check import panel_login_required + +import core.mw as mw +import utils.config as utils_config + +from utils.setting import setting as MwSetting + +from .setting import blueprint +import thisdb + +# 获取面板证书信息 +@blueprint.route('/get_panel_ssl', endpoint='get_panel_ssl', methods=['POST']) +@panel_login_required +def get_panel_ssl(): + return MwSetting.instance().getPanelSsl() + + +# 获取面板证书信息 +@blueprint.route('/save_panel_ssl', endpoint='save_panel_ssl', methods=['POST']) +@panel_login_required +def save_panel_ssl(): + choose = request.form.get('choose', '').strip() + certPem = request.form.get('certPem', '').strip() + privateKey = request.form.get('privateKey', '').strip() + return MwSetting.instance().savePanelSsl(choose,certPem,privateKey) + +# 获取面板证书信息 +@blueprint.route('/del_panel_ssl', endpoint='del_panel_ssl', methods=['POST']) +@panel_login_required +def del_panel_ssl(): + choose = request.form.get('choose', '').strip() + return MwSetting.instance().delPanelSsl(choose) + +# 开启面板证书 +@blueprint.route('/set_panel_local_ssl', endpoint='set_panel_local_ssl', methods=['POST']) +@panel_login_required +def set_panel_local_ssl(): + cert_type = request.form.get('cert_type', '').strip() + return MwSetting.instance().setPanelLocalSsl(cert_type) + +# 关闭面板证书 +@blueprint.route('/close_panel_ssl', endpoint='close_panel_ssl', methods=['POST']) +@panel_login_required +def close_panel_ssl(): + return MwSetting.instance().closePanelSsl() + +# 设置面板域名 +@blueprint.route('/set_panel_domain', endpoint='set_panel_domain', methods=['POST']) +@panel_login_required +def set_panel_domain(): + domain = request.form.get('domain', '').strip() + return MwSetting.instance().setPanelDomain(domain) + + + + + + + + + diff --git a/web/admin/setting/secondary_verifiy.py b/web/admin/setting/secondary_verifiy.py index 562383706..d7bf70feb 100644 --- a/web/admin/setting/secondary_verifiy.py +++ b/web/admin/setting/secondary_verifiy.py @@ -16,39 +16,55 @@ import time from flask import Blueprint, render_template from flask import request -from admin import model from admin import session -from admin.model import db,TempLogin from admin.user_login_check import panel_login_required import core.mw as mw import utils.config as utils_config from .setting import blueprint +import thisdb - +# 获取二次验证信息 @blueprint.route('/get_auth_secret', endpoint='get_auth_secret', methods=['POST']) @panel_login_required def get_auth_secret(): - reset = request.form.get('reset', '') - __file = mw.getCommonFile() - import pyotp - auth = __file['auth_secret'] + + reset = request.form.get('reset', '') tag = 'mdserver-web' - if os.path.exists(auth) and reset != '1': - content = mw.readFile(auth) - sec = mw.deDoubleCrypt(tag,content) + two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False}) + + if 'secret' in two_step_verification and reset != '1': + secret = mw.deDoubleCrypt(tag, two_step_verification['secret']) else: - sec = pyotp.random_base32() - crypt_data = mw.enDoubleCrypt(tag, sec) - mw.writeFile(auth, crypt_data) + secret = pyotp.random_base32() + crypt_data = mw.enDoubleCrypt(tag, secret) + two_step_verification['secret'] = crypt_data + thisdb.setOption('two_step_verification', json.dumps(two_step_verification)) ip = mw.getHostAddr() - url = pyotp.totp.TOTP(sec).provisioning_uri(name=ip, issuer_name=tag) + url = pyotp.totp.TOTP(secret).provisioning_uri(name=ip, issuer_name=tag) rdata = {} - rdata['secret'] = sec + rdata['secret'] = secret rdata['url'] = url return mw.returnData(True, '设置成功!', rdata) + + +# 设置二次验证,加强安全登录 +@blueprint.route('/set_auth_secret', endpoint='set_auth_secret', methods=['POST']) +@panel_login_required +def set_auth_secret(): + two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False}) + if two_step_verification['open']: + two_step_verification['open'] = False + thisdb.setOption('two_step_verification', json.dumps(two_step_verification)) + return mw.returnData(True, '关闭成功!', 0) + else: + two_step_verification['open'] = True + thisdb.setOption('two_step_verification', json.dumps(two_step_verification)) + return mw.returnData(True, '开启成功!', 1) + + \ No newline at end of file diff --git a/web/admin/setting/setting.py b/web/admin/setting/setting.py index 0e3b1a26b..b0d3cca6d 100644 --- a/web/admin/setting/setting.py +++ b/web/admin/setting/setting.py @@ -11,37 +11,36 @@ import re import json import os +import time from flask import Blueprint, render_template from flask import request -from admin import model +from admin import session from admin.user_login_check import panel_login_required + import core.mw as mw +import thisdb import utils.config as utils_config + # 默认页面 blueprint = Blueprint('setting', __name__, url_prefix='/setting', template_folder='../../templates') @blueprint.route('/index', endpoint='index') @panel_login_required def index(): - return render_template('default/setting.html') - -@blueprint.route('/get_panel_list', endpoint='get_panel_list', methods=['POST']) -@panel_login_required -def get_panel_list(): - return [] - + name = thisdb.getOption('template', default='default') + return render_template('%s/setting.html' % name) # 设置面板名称 @blueprint.route('/set_webname', endpoint='set_webname', methods=['POST']) @panel_login_required def set_webname(): webname = request.form.get('webname', '') - src_webname = model.getOption('title') + src_webname = thisdb.getOption('title') if webname != src_webname: - model.setOption('title', webname) + thisdb.setOption('title', webname) return mw.returnData(True, '面板别名保存成功!') # 设置服务器IP @@ -49,9 +48,9 @@ def set_webname(): @panel_login_required def set_ip(): host_ip = request.form.get('host_ip', '') - src_host_ip = model.getOption('server_ip') + src_host_ip = thisdb.getOption('server_ip') if host_ip != src_host_ip: - model.setOption('server_ip', host_ip) + thisdb.setOption('server_ip', host_ip) return mw.returnData(True, 'IP保存成功!') # 默认备份目录 @@ -59,9 +58,9 @@ def set_ip(): @panel_login_required def set_backup_dir(): backup_path = request.form.get('backup_path', '') - src_backup_path = model.getOption('backup_path') + src_backup_path = thisdb.getOption('backup_path') if backup_path != src_backup_path: - model.setOption('backup_path', backup_path) + thisdb.setOption('backup_path', backup_path) return mw.returnData(True, '修改默认备份目录成功!') # 默认站点目录 @@ -69,9 +68,9 @@ def set_backup_dir(): @panel_login_required def set_www_dir(): sites_path = request.form.get('sites_path', '') - src_sites_path = model.getOption('sites_path') + src_sites_path = thisdb.getOption('sites_path') if sites_path != src_sites_path: - model.setOption('sites_path', sites_path) + thisdb.setOption('sites_path', sites_path) return mw.returnData(True, '修改默认建站目录成功!') @@ -100,9 +99,9 @@ def set_admin_path(): if not re.match(r"^/[\w]+$", admin_path): return mw.returnData(False, '入口地址格式不正确,示例: /mw_rand') - src_admin_path = model.getOption('admin_path') + src_admin_path = thisdb.getOption('admin_path') if admin_path != src_admin_path: - model.setOption('admin_path', admin_path[1:]) + thisdb.setOption('admin_path', admin_path[1:]) return mw.returnData(True, '修改成功!') @@ -123,7 +122,7 @@ def set_basic_auth(): is_open = False if basic_open == 'false': - model.setOption('basic_auth', json.dumps({'open':False})) + thisdb.setOption('basic_auth', json.dumps({'open':False})) mw.writeLog('面板设置', '设置BasicAuth状态为: %s' % is_open) return mw.returnData(True, '删除BasicAuth成功!') @@ -137,12 +136,12 @@ def set_basic_auth(): data['basic_pwd'] = mw.md5(basic_pwd + salt) data['open'] = is_open - model.setOption('basic_auth', json.dumps(data)) + thisdb.setOption('basic_auth', json.dumps(data)) mw.writeLog('面板设置', '设置BasicAuth状态为: %s' % is_open) return mw.returnData(True, '设置成功!') -# 设置站点状态 +# 设置面板未登录状态 @blueprint.route('/set_status_code', endpoint='set_status_code', methods=['POST']) @panel_login_required def set_status_code(): @@ -156,10 +155,96 @@ def set_status_code(): return mw.returnData(False, '状态码范围错误!') info = utils_config.getUnauthStatus(code=str(status_code)) - model.setOption('unauthorized_status', str(status_code)) + thisdb.setOption('unauthorized_status', str(status_code)) mw.writeLog('面板设置', '将未授权响应状态码设置为:{0}:{1}'.format(status_code,info['text'])) return mw.returnData(True, '设置成功!') - + +# 设置面板调式模式 +@blueprint.route('/open_debug', endpoint='open_debug', methods=['POST']) +@panel_login_required +def open_debug(): + debug = thisdb.getOption('debug',default='close') + if debug == 'open': + thisdb.setOption('debug','close') + return mw.returnData(True, '开发模式关闭!') + thisdb.setOption('debug','open') + return mw.returnData(True, '开发模式开启!') +# 设置面板开关 +@blueprint.route('/close_panel', endpoint='close_panel', methods=['POST']) +@panel_login_required +def close_panel(): + admin_close = thisdb.getOption('admin_close',default='no') + if admin_close == 'no': + thisdb.setOption('admin_close','yes') + return mw.returnData(True, '关闭面板成功!') + thisdb.setOption('admin_close','no') + return mw.returnData(True, '开启面板成功!') + +# 设置IPV6状态 +@blueprint.route('/set_ipv6_status', endpoint='set_ipv6_status', methods=['POST']) +@panel_login_required +def set_ipv6_status(): + __file = mw.getCommonFile() + ipv6_file = __file['ipv6'] + if os.path.exists(ipv6_file): + os.remove(ipv6_file) + mw.writeLog('面板设置', '关闭面板IPv6兼容!') + mw.returnData('面板设置', '关闭面板IPv6兼容!') + else: + mw.writeFile(ipv6_file, 'True') + mw.writeLog('面板设置', '开启面板IPv6兼容!') + mw.restartMw() + return mw.returnData(True, '设置成功!') + +# 设置面板用户 +@blueprint.route('/set_name', endpoint='set_name', methods=['POST']) +@panel_login_required +def set_name(): + name1 = request.form.get('name1', '') + name2 = request.form.get('name2', '') + if name1 != name2: + return mw.returnData(False, '两次输入的用户名不一致,请重新输入!') + if len(name1) < 3: + return mw.returnData(False, '用户名长度不能少于3位') + thisdb.setUserByName(session['username'], name1) + session['username'] = name1 + return mw.returnData(True, '用户修改成功!') + +# 设置面板密码 +@blueprint.route('/set_password', endpoint='set_password', methods=['POST']) +@panel_login_required +def set_password(): + password1 = request.form.get('password1', '') + password2 = request.form.get('password2', '') + if password1 != password2: + return mw.returnData(False, '两次输入的密码不一致,请重新输入!') + if len(password1) < 5: + return mw.returnData(False, '用户密码不能小于5位!') + + thisdb.setUserPwdByName(session['username'], password1) + return mw.returnData(True, '密码修改成功!') + +# 设置面板端口 +@blueprint.route('/set_port', endpoint='set_port', methods=['POST']) +@panel_login_required +def set_port(): + port = request.form.get('port', '') + if port != mw.getHostPort(): + from utils.firewall import Firewall as MwFirewall + + sysCfgDir = mw.systemdCfgDir() + if os.path.exists(sysCfgDir + "/firewalld.service"): + if not MwFirewall.instance().getFwStatus(): + return mw.returnData(False, 'firewalld必须先启动!') + + mw.setHostPort(port) + msg = mw.getInfo('放行端口[{1}]成功', (port,)) + mw.writeLog("防火墙管理", msg) + + MwFirewall.instance().addAcceptPort(port, 'PANEL端口-配置修改', 'port') + mw.restartMw() + + return mw.returnData(True, '端口保存成功!') \ No newline at end of file diff --git a/web/admin/setting/temp_login.py b/web/admin/setting/temp_login.py index 51f12a268..6aa7633af 100644 --- a/web/admin/setting/temp_login.py +++ b/web/admin/setting/temp_login.py @@ -16,13 +16,12 @@ import time from flask import Blueprint, render_template from flask import request -from admin import model from admin import session -from admin.model import db,TempLogin from admin.user_login_check import panel_login_required import core.mw as mw import utils.config as utils_config +import thisdb from .setting import blueprint @@ -34,24 +33,11 @@ def get_temp_login(): p = request.form.get('page', '1').strip() tojs = request.form.get('tojs', '').strip() - count = TempLogin.query.filter_by().count() - pagination = TempLogin.query.filter_by().order_by(TempLogin.expire.desc()).paginate(page=int(p), per_page=int(limit)) - - rows = [] - for item in pagination.items: - t = {} - t['id'] = item.id - t['salt'] = item.salt - t['state'] = item.state - t['expire'] = item.expire - t['login_time'] = item.login_time - t['login_addr'] = item.login_addr - t['add_time'] = item.add_time - rows.append(t) + info = thisdb.getTempLoginPage(int(p), int(limit)) data = {} - data['data'] = rows - data['page'] = mw.getPage({'count':count,'tojs':'setTempAccessReq','p':p,'row':limit}) + data['data'] = info['list'] + data['page'] = mw.getPage({'count':info['count'],'tojs':'setTempAccessReq','p':p,'row':limit}) return data @blueprint.route('/set_temp_login', endpoint='set_temp_login', methods=['POST']) @@ -59,38 +45,27 @@ def get_temp_login(): def set_temp_login(): # if 'tmp_login_expire' in session: # return mw.returnData(False, '没有权限') + + + thisdb.clearTempLogin() + start_time = int(time.time()) - model.clearTempLogin() + expire = start_time+3600 + + rand_str = mw.getRandomString(48) + token = mw.md5(rand_str) - - token = mw.getRandomString(48) - salt = mw.getRandomString(12) - - expire=start_time+3600 - make_token = mw.md5(token + salt) - add_tl = TempLogin( - token=make_token, - salt=salt, - state=0, - login_time=0, - login_addr='', - expire=expire, - add_time=start_time) - r = db.session.add(add_tl) - db.session.commit() - - if r is None: + r = thisdb.addTempLogin(token, expire) + if r > 0: mw.writeLog('面板设置', '生成临时连接,过期时间:{}'.format(mw.formatDate(times=expire))) - return {'status': True, 'msg': "临时连接已生成", 'token': make_token, 'expire': expire} + return {'status': True, 'msg': "临时连接已生成", 'token': token, 'expire': expire} return mw.returnData(False, '连接生成失败') @blueprint.route('/remove_temp_login', endpoint='remove_temp_login', methods=['POST']) @panel_login_required def remove_temp_login(): tl_id = request.form.get('id', '10').strip() - - r = TempLogin.query.filter(TempLogin.id == tl_id).delete() - db.session.commit() + r = thisdb.deleteTempLoginById(tl_id) if r > 0: mw.writeLog('面板设置', '删除临时登录连接') return mw.returnData(True, '删除成功') diff --git a/web/admin/setting/timezone.py b/web/admin/setting/timezone.py index 246b411c3..4b542d123 100644 --- a/web/admin/setting/timezone.py +++ b/web/admin/setting/timezone.py @@ -15,7 +15,6 @@ import os from flask import Blueprint, render_template from flask import request -from admin import model from admin.user_login_check import panel_login_required import core.mw as mw diff --git a/web/admin/setup/__init__.py b/web/admin/setup/__init__.py index 234f2f7de..f7eb6eab2 100644 --- a/web/admin/setup/__init__.py +++ b/web/admin/setup/__init__.py @@ -8,6 +8,32 @@ # Author: midoks # --------------------------------------------------------------------------------- +import os + from .user import init_admin_user from .option import init_option +from .init_db_system import init_db_system +from .init_cmd import init_cmd + +from utils.firewall import Firewall as MwFirewall + +import thisdb +import config + +def init(): + # 检查数据库是否存在。如果没有就创建它。 + if not os.path.isfile(config.SQLITE_PATH): + # 初始化用户信息 + thisdb.initPanelData() + init_admin_user() + init_option() + init_db_system() + + init_cmd() + + # 自动识别防火墙配置 + firewall_port = thisdb.getOption('setpu_auto_identify_firewall_port', default='no') + if firewall_port == 'no': + MwFirewall.instance().aIF() + thisdb.setOption('setpu_auto_identify_firewall_port', 'yes') diff --git a/web/admin/setup/init_cmd.py b/web/admin/setup/init_cmd.py new file mode 100644 index 000000000..bb0280a59 --- /dev/null +++ b/web/admin/setup/init_cmd.py @@ -0,0 +1,79 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import shutil + +import core.mw as mw + +def cmdContent(): + script = mw.getPanelDir() + '/scripts/init.d/mw.tpl' + content = mw.readFile(script) + content = content.replace("{$SERVER_PATH}", mw.getPanelDir()) + content += "\n# make:{0}".format(mw.formatDate()) + return content + + +def init_cmd(): + cmd_content = cmdContent() + script_bin = mw.getPanelDir() + '/scripts/init.d/mw' + mw.writeFile(script_bin, cmd_content) + mw.execShell('chmod +x ' + script_bin) + + # 在linux系统中,确保/etc/init.d存在 + if not mw.isAppleSystem() and not os.path.exists("/etc/rc.d/init.d"): + mw.execShell('mkdir -p /etc/rc.d/init.d') + + if not mw.isAppleSystem() and not os.path.exists("/etc/init.d"): + mw.execShell('mkdir -p /etc/init.d') + # initd + if os.path.exists('/etc/rc.d/init.d'): + initd_bin = '/etc/rc.d/init.d/mw' + if not os.path.exists(initd_bin): + mw.writeFile(initd_bin, cmd_content) + mw.execShell('chmod +x ' + initd_bin) + # 加入自启动 + mw.execShell('which chkconfig && chkconfig --add mw') + + + if os.path.exists('/etc/init.d'): + initd_bin = '/etc/init.d/mw' + if not os.path.exists(initd_bin): + mw.writeFile(initd_bin, cmd_content) + mw.execShell('chmod +x ' + initd_bin) + # 加入自启动 + mw.execShell('which update-rc.d && update-rc.d -f mw defaults') + + # sys_name = mw.getOsName() + # if sys_name == 'opensuse': + # init_cmd_systemd() + return True + + +def init_cmd_systemd(): + systemd_dir = mw.systemdCfgDir() + + systemd_mw = systemd_dir + '/mw.service' + systemd_mw_task = systemd_dir + '/mw-task.service' + + systemd_mw_tpl = mw.getPanelDir() + '/scripts/init.d/mw.service.tpl' + systemd_mw_task_tpl = mw.getPanelDir() + '/scripts/init.d/mw-task.service.tpl' + + if os.path.exists(systemd_mw): + os.remove(systemd_mw) + if os.path.exists(systemd_mw_task): + os.remove(systemd_mw_task) + + contentReplace(systemd_mw_tpl, systemd_mw) + contentReplace(systemd_mw_task_tpl, systemd_mw_task) + + mw.execShell('systemctl enable mw') + mw.execShell('systemctl enable mw-task') + mw.execShell('systemctl daemon-reload') \ No newline at end of file diff --git a/web/admin/setup/init_db_system.py b/web/admin/setup/init_db_system.py new file mode 100644 index 000000000..bcce3db89 --- /dev/null +++ b/web/admin/setup/init_db_system.py @@ -0,0 +1,16 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +from utils.system import monitor + + +def init_db_system(): + monitor.instance().initDBFile() + return True \ No newline at end of file diff --git a/web/admin/setup/option.py b/web/admin/setup/option.py index 10d739875..b38a031fb 100644 --- a/web/admin/setup/option.py +++ b/web/admin/setup/option.py @@ -10,52 +10,62 @@ import json -from flask import request - -from admin import model -from admin.model import db, Users - import core.mw as mw +import thisdb def init_option(): - model.setOption('title', '后羿面板') - model.setOption('recycle_bin', 'open') - model.setOption('template', 'default') + thisdb.setOption('title', '夸父面板') + thisdb.setOption('recycle_bin', 'open') + thisdb.setOption('template', 'default') + + # SSL邮件地址 + thisdb.setOption('ssl_email', '') # 后台面板是否关闭 - model.setOption('admin_close', 'no') + thisdb.setOption('admin_close', 'no') # 未认证状态码 - model.setOption('unauthorized_status', '0') + thisdb.setOption('unauthorized_status', '0') # 调式模式,默认关闭 - model.setOption('debug', 'close') + thisdb.setOption('debug', 'close') # basic auth 配置 - model.setOption('basic_auth', json.dumps({'open':False})) - - + thisdb.setOption('basic_auth', json.dumps({'open':False})) + # 二步验证|默认关闭 + thisdb.setOption('two_step_verification', json.dumps({'open':False})) # 开启后台任务 - # model.setOption('run_bg_task', 'close') + # thisdb.setOption('run_bg_task', 'close') # 首页展示初始化 - model.setOption('display_index', '[]') + thisdb.setOption('display_index', '[]') # 监控默认配置 - model.setOption('monitor_status', 'open', type='monitor') - model.setOption('monitor_day', '30', type='monitor') - model.setOption('monitor_only_netio', 'open', type='monitor') + thisdb.setOption('monitor_status', 'open', type='monitor') + thisdb.setOption('monitor_day', '30', type='monitor') + thisdb.setOption('monitor_only_netio', 'open', type='monitor') # 初始化安全路径 - model.setOption('admin_path', mw.getRandomString(8)) - model.setOption('server_ip', '127.0.0.1') + thisdb.setOption('admin_path', mw.getRandomString(8)) + + # API是否开启|默认关闭 + thisdb.setOption('panel_api', json.dumps({'open':False})) + + # 获取服务器IP + ip = mw.getLocalIp() + thisdb.setOption('server_ip', ip) # 默认备份目录 - model.setOption('backup_path', mw.getFatherDir()+'/backup') + thisdb.setOption('backup_path', mw.getFatherDir()+'/backup') # 默认站点目录 - model.setOption('site_path', mw.getFatherDir()+'/wwwroot') - + thisdb.setOption('site_path', mw.getFatherDir()+'/wwwroot') + + # 异步邮件通知 + thisdb.setOption('notify_email', json.dumps({'open':False}), type='notify') + # 异步Telegram Bot 通知 + thisdb.setOption('notify_tgbot', json.dumps({'open':False}), type='notify') + return True \ No newline at end of file diff --git a/data/sql/default.sql b/web/admin/setup/sql/default.sql similarity index 61% rename from data/sql/default.sql rename to web/admin/setup/sql/default.sql index daa5e4521..b4011cf77 100755 --- a/data/sql/default.sql +++ b/web/admin/setup/sql/default.sql @@ -1,21 +1,21 @@ CREATE TABLE IF NOT EXISTS `backup` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `pid` INTEGER, `type` INTEGER, `name` TEXT, - `pid` INTEGER, `filename` TEXT, `size` INTEGER, - `addtime` TEXT + `add_time` TEXT ); CREATE TABLE IF NOT EXISTS `binding` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `pid` INTEGER, + `port` INTEGER, `domain` TEXT, `path` TEXT, - `port` INTEGER, - `addtime` TEXT + `add_time` TEXT ); @@ -27,40 +27,40 @@ CREATE TABLE IF NOT EXISTS `crontab` ( `where_hour` INTEGER, `where_minute` INTEGER, `echo` TEXT, - `addtime` TEXT, `status` INTEGER DEFAULT '1', `save` INTEGER DEFAULT '3', `backup_to` TEXT DEFAULT 'off', `sname` TEXT, `sbody` TEXT, 'stype' TEXT, - `urladdress` TEXT + `url_address` TEXT, + `add_time` TEXT, + `update_time` TEXT ); +CREATE UNIQUE INDEX crontab_name_idx ON crontab(name); CREATE TABLE IF NOT EXISTS `firewall` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `port` TEXT, `protocol` TEXT DEFAULT 'tcp', `ps` TEXT, - `addtime` TEXT + `add_time` TEXT, + `update_time` TEXT ); -ALTER TABLE `firewall` ADD COLUMN `protocol` TEXT DEFAULT 'tcp'; - -INSERT INTO `firewall` (`id`, `port`, `protocol`, `ps`, `addtime`) VALUES -(1, '80', 'tcp','网站默认端口', '0000-00-00 00:00:00'), -(2, '443', 'tcp/udp', 'HTTPS', '0000-00-00 00:00:00'); - +CREATE UNIQUE INDEX firewall_port_idx ON firewall(port); +INSERT INTO `firewall` (`id`, `port`, `protocol`, `ps`, `add_time`, `update_time`) VALUES +(1, '80', 'tcp', '网站默认端口', '0000-00-00 00:00:00','0000-00-00 00:00:00'), +(2, '443', 'tcp/udp', 'HTTPS', '0000-00-00 00:00:00','0000-00-00 00:00:00'); CREATE TABLE IF NOT EXISTS `logs` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `log` TEXT, `uid` INTEGER DEFAULT '1', - `addtime` TEXT + `add_time` TEXT ); -ALTER TABLE `logs` ADD COLUMN `uid` INTEGER DEFAULT '1'; CREATE TABLE IF NOT EXISTS `sites` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -73,48 +73,46 @@ CREATE TABLE IF NOT EXISTS `sites` ( `edate` TEXT, `ssl_effective_date` TEXT, `ssl_expiration_date` TEXT, - `addtime` TEXT + `add_time` TEXT, + `update_time` TEXT ); - -ALTER TABLE `sites` ADD COLUMN `ssl_effective_date` TEXT DEFAULT ''; -ALTER TABLE `sites` ADD COLUMN `ssl_expiration_date` TEXT DEFAULT ''; +CREATE UNIQUE INDEX sites_name_idx ON sites(name); CREATE TABLE IF NOT EXISTS `site_types` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT ); +CREATE UNIQUE INDEX site_types_name_idx ON site_types(name); CREATE TABLE IF NOT EXISTS `domain` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `pid` INTEGER, `name` TEXT, `port` INTEGER, - `addtime` TEXT + `add_time` TEXT ); CREATE TABLE IF NOT EXISTS `users` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `username` TEXT, + `name` TEXT, `password` TEXT, `login_ip` TEXT, `login_time` TEXT, `phone` TEXT, - `email` TEXT + `email` TEXT, + `add_time` TEXT, + `update_time` TEXT ); -INSERT INTO `users` (`id`, `username`, `password`, `login_ip`, `login_time`, `phone`, `email`) VALUES -(1, 'admin', '21232f297a57a5a743894a0e4a801fc3', '192.168.0.10', '2022-02-02 00:00:00', 0, 'midoks@163.com'); - - CREATE TABLE IF NOT EXISTS `tasks` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT, `type` TEXT, - `status` TEXT, - `addtime` TEXT, `start` INTEGER, `end` INTEGER, - `execstr` TEXT + `cmd` TEXT, + `status` INTEGER, + `add_time` TEXT ); CREATE TABLE IF NOT EXISTS `temp_login` ( @@ -126,7 +124,7 @@ CREATE TABLE IF NOT EXISTS `temp_login` ( `login_addr` REAL, `logout_time` INTEGER, `expire` INTEGER, - `addtime` INTEGER + `add_time` TEXT ); CREATE TABLE IF NOT EXISTS `panel` ( @@ -136,5 +134,26 @@ CREATE TABLE IF NOT EXISTS `panel` ( `username` TEXT, `password` TEXT, `click` INTEGER, - `addtime` INTEGER + `add_time` TEXT ); + +CREATE TABLE IF NOT EXISTS `app` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `name` TEXT, + `app_id` TEXT, + `app_secret` TEXT, + `white_list` TEXT, + `status` INTEGER, + `add_time` TEXT, + `update_time` TEXT +); +CREATE UNIQUE INDEX app_name_idx ON app(name); +CREATE UNIQUE INDEX app_id_idx ON app(app_id); + +CREATE TABLE IF NOT EXISTS `option` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `name` TEXT, + `type` TEXT, + `value` TEXT +); +CREATE UNIQUE INDEX option_name_idx ON option(name); diff --git a/data/sql/system.sql b/web/admin/setup/sql/system.sql similarity index 68% rename from data/sql/system.sql rename to web/admin/setup/sql/system.sql index 06ef0a595..31ed453ee 100755 --- a/data/sql/system.sql +++ b/web/admin/setup/sql/system.sql @@ -1,20 +1,22 @@ CREATE TABLE IF NOT EXISTS `network` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `up` INTEGER, - `down` INTEGER, + `up` TEXT, + `down` TEXT, `total_up` INTEGER, `total_down` INTEGER, `down_packets` INTEGER, `up_packets` INTEGER, `addtime` INTEGER ); +CREATE INDEX network_addtime_idx ON network(`addtime`); CREATE TABLE IF NOT EXISTS `cpuio` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `pro` INTEGER, - `mem` INTEGER, + `pro` TEXT, + `mem` TEXT, `addtime` INTEGER ); +CREATE INDEX cpuio_addtime_idx ON cpuio(`addtime`); CREATE TABLE IF NOT EXISTS `diskio` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -26,6 +28,7 @@ CREATE TABLE IF NOT EXISTS `diskio` ( `write_time` INTEGER, `addtime` INTEGER ); +CREATE INDEX diskio_addtime_idx ON diskio(`addtime`); CREATE TABLE IF NOT EXISTS `load_average` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, @@ -34,4 +37,5 @@ CREATE TABLE IF NOT EXISTS `load_average` ( `five` REAL, `fifteen` REAL, `addtime` INTEGER -); \ No newline at end of file +); +CREATE INDEX load_average_addtime_idx ON load_average(`addtime`); \ No newline at end of file diff --git a/web/admin/setup/user.py b/web/admin/setup/user.py index f31f85a27..1065ef0c1 100644 --- a/web/admin/setup/user.py +++ b/web/admin/setup/user.py @@ -10,32 +10,12 @@ from flask import request -from admin import model -from admin.model import db, Users import core.mw as mw +import thisdb # 初始化用户信息 def init_admin_user(): - data = Users.query.filter_by(id=1).first() - if not data: - name = mw.getRandomString(8).lower() - password = mw.getRandomString(8).lower() - file_pass_pl = mw.getPanelDataDir() + '/default_new.pl' - mw.writeFile(file_pass_pl, password) - insert_time = mw.formatDate() - login_ip = '127.0.0.1' - add_user = Users( - name=name, - password=mw.md5(password), - login_ip=login_ip, - login_time=insert_time, - phone='', - email='', - add_time=insert_time, - update_time=insert_time) - db.session.add(add_user) - db.session.commit() - db.session.close() + thisdb.initAdminUser() return True diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index 12ad0468f..d1128d871 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -8,55 +8,17 @@ # Author: midoks # --------------------------------------------------------------------------------- +from .site import * +from .site_types import * +from .site_default import * -from flask import Blueprint, render_template -from flask import request +from .backup import * -from admin.model import Sites -from admin.user_login_check import panel_login_required +from .php import * +from .logs import * +from .dir import * +from .redirect import * +from .proxy import * -from utils.mwplugin import MwPlugin -import core.mw as mw - -blueprint = Blueprint('site', __name__, url_prefix='/site', template_folder='../../templates/default') -@blueprint.route('/index', endpoint='index') -def index(): - return render_template('site.html') - -# 插件列表 -@blueprint.route('/list', endpoint='list', methods=['GET','POST']) -@panel_login_required -def list(): - p = request.form.get('p', '1') - limit = request.form.get('limit', '10') - type_id = request.form.get('type_id', '0').strip() - search = request.form.get('search', '').strip() - - count = Sites.query.count() - pagination = Sites.query.paginate(page=int(p), per_page=int(limit)) - - site_list = [] - for item in pagination.items: - t = {} - t['id'] = item.id - t['name'] = item.name - t['path'] = item.path - t['index'] = item.index - t['ps'] = item.ps - t['edate'] = item.edate - t['type_id'] = item.type_id - t['status'] = item.status - t['add_time'] = item.add_time - t['update_time'] = item.update_time - site_list.append(t) - - data = {} - data['data'] = site_list - data['page'] = mw.getPage({'count':count,'tojs':'getWeb','p':p, 'row':limit}) - - return data - -@blueprint.route('/get_site_types', endpoint='get_site_types',methods=['POST']) -@panel_login_required -def get_site_types(): - return [] \ No newline at end of file +from .ssl import * +from .ssl_acme import * \ No newline at end of file diff --git a/web/admin/site/backup.py b/web/admin/site/backup.py new file mode 100644 index 000000000..72b978fcf --- /dev/null +++ b/web/admin/site/backup.py @@ -0,0 +1,54 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites + +import core.mw as mw +import thisdb + +from .site import blueprint + +# 获取备份列表 +@blueprint.route('/get_backup', endpoint='get_backup',methods=['POST']) +@panel_login_required +def get_backup(): + limit = request.form.get('limit', '') + page = request.form.get('p', '') + site_id = request.form.get('search', '') + return MwSites.instance().getBackup(site_id,page=page,size=limit) + +# 设置备份 +@blueprint.route('/to_backup', endpoint='to_backup',methods=['POST']) +@panel_login_required +def to_backup(): + site_id = request.form.get('id', '') + return MwSites.instance().toBackup(site_id) + +# 删除备份 +@blueprint.route('/del_backup', endpoint='del_backup',methods=['POST']) +@panel_login_required +def del_backup(): + site_id = request.form.get('id', '') + return MwSites.instance().delBackup(site_id) + + + + + + + diff --git a/web/admin/site/dir.py b/web/admin/site/dir.py new file mode 100644 index 000000000..5cc7d15a7 --- /dev/null +++ b/web/admin/site/dir.py @@ -0,0 +1,74 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required +from utils.site import sites as MwSites + +import core.mw as mw +import thisdb + +from .site import blueprint + +# 获取网站目录 +@blueprint.route('/get_dir_user_ini', endpoint='get_dir_user_ini',methods=['POST']) +@panel_login_required +def get_dir_user_ini(): + site_id = request.form.get('id', '') + return MwSites.instance().getDirUserIni(site_id) + +# 设置防跨站攻击 +@blueprint.route('/set_dir_user_ini', endpoint='set_dir_user_ini',methods=['POST']) +@panel_login_required +def set_dir_user_ini(): + path = request.form.get('path', '') + run_path = request.form.get('run_path', '') + return MwSites.instance().setDirUserIni(path,run_path) + +# 获取子目录绑定 +@blueprint.route('/get_dir_binding', endpoint='get_dir_binding',methods=['POST']) +@panel_login_required +def get_dir_binding(): + site_id = request.form.get('id', '') + return MwSites.instance().getDirBinding(site_id) + + +# 添加子目录绑定 +@blueprint.route('/add_dir_bind', endpoint='add_dir_bind',methods=['POST']) +@panel_login_required +def add_dir_bind(): + site_id = request.form.get('id', '') + domain = request.form.get('domain', '') + dir_name = request.form.get('dir_name', '') + return MwSites.instance().addDirBind(site_id,domain,dir_name) + + +# 获取目录绑定rewrite +@blueprint.route('/get_dir_bind_rewrite', endpoint='get_dir_bind_rewrite',methods=['POST']) +@panel_login_required +def get_dir_bind_rewrite(): + binding_id = request.form.get('id', '') + add = request.form.get('add', '') + return MwSites.instance().getDirBindingRewrite(binding_id,add) + + +# 获取目录绑定rewrite +@blueprint.route('/del_dir_bind', endpoint='del_dir_bind',methods=['POST']) +@panel_login_required +def del_dir_bind(): + binding_id = request.form.get('id', '') + return MwSites.instance().delDirBinding(binding_id) + + diff --git a/web/admin/site/logs.py b/web/admin/site/logs.py new file mode 100644 index 000000000..e916022a8 --- /dev/null +++ b/web/admin/site/logs.py @@ -0,0 +1,45 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites + +import core.mw as mw +import thisdb + +from .site import blueprint + +@blueprint.route('/get_logs', endpoint='get_logs',methods=['POST']) +@panel_login_required +def get_logs(): + siteName = request.form.get('siteName', '') + return MwSites.instance().getLogs(siteName) + + +@blueprint.route('/get_error_logs', endpoint='get_error_logs',methods=['POST']) +@panel_login_required +def get_error_logs(): + siteName = request.form.get('siteName', '') + return MwSites.instance().getErrorLogs(siteName) + + + + + + + diff --git a/web/admin/site/php.py b/web/admin/site/php.py new file mode 100644 index 000000000..8808e6b39 --- /dev/null +++ b/web/admin/site/php.py @@ -0,0 +1,59 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites + +import core.mw as mw +import thisdb + +from .site import blueprint + +@blueprint.route('/get_cli_php_version', endpoint='get_cli_php_version',methods=['POST']) +@panel_login_required +def get_cli_php_version(): + php_dir = mw.getServerDir() + '/php' + if not os.path.exists(php_dir): + return mw.returnData(False, '未安装PHP,无法设置') + + php_bin = '/usr/bin/php' + php_versions = MwSites.instance().getPhpVersion() + php_versions = php_versions[1:] + + if len(php_versions) < 1: + return mw.returnData(False, '未安装PHP,无法设置') + + if os.path.exists(php_bin) and os.path.islink(php_bin): + link_re = os.readlink(php_bin) + for v in php_versions: + if link_re.find(v['version']) != -1: + return {"select": v, "versions": php_versions} + + return {"select": php_versions[0],"versions": php_versions} + +@blueprint.route('/set_cli_php_version', endpoint='set_cli_php_version',methods=['POST']) +@panel_login_required +def set_cli_php_version(): + if mw.isAppleSystem(): + return mw.returnData(False, "开发机不可设置!") + version = request.form.get('version', '') + return MwSites.instance().setCliPhpVersion(version) + + + + diff --git a/web/admin/site/proxy.py b/web/admin/site/proxy.py new file mode 100644 index 000000000..5c333845a --- /dev/null +++ b/web/admin/site/proxy.py @@ -0,0 +1,79 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites + +import core.mw as mw +import thisdb + +from .site import blueprint + + +# 获取代理列表 +@blueprint.route('/get_proxy_list', endpoint='get_proxy_list', methods=['POST']) +@panel_login_required +def get_proxy_list(): + site_name = request.form.get("siteName", '') + return MwSites.instance().getProxyList(site_name) + +# 获取代理列表 +@blueprint.route('/set_proxy', endpoint='set_proxy', methods=['POST']) +@panel_login_required +def set_proxy(): + site_name = request.form.get('siteName', '') + site_from = request.form.get('from', '') + to = request.form.get('to', '') + host = request.form.get('host', '') + name = request.form.get('name', '') + open_proxy = request.form.get('open_proxy', '') + open_cache = request.form.get('open_cache', '') + cache_time = request.form.get('cache_time', '') + proxy_id = request.form.get('id', '') + return MwSites.instance().setProxy(site_name,site_from,to,host,name,open_proxy, open_cache,cache_time, proxy_id) + +# 获取代理状态 +@blueprint.route('/set_proxy_status', endpoint='set_proxy_status', methods=['POST']) +@panel_login_required +def set_proxy_status(): + site_name = request.form.get("siteName", '') + status = request.form.get("status", '') + proxy_id = request.form.get("id", '') + return MwSites.instance().setProxyStatus(site_name,proxy_id,status) + + +# 获取代理配置 +@blueprint.route('/get_proxy_conf', endpoint='get_proxy_conf', methods=['POST']) +@panel_login_required +def get_proxy_conf(): + site_name = request.form.get("siteName", '') + rid = request.form.get("id", '') + return MwSites.instance().getProxyConf(site_name, rid) + + +# 删除代理配置 +@blueprint.route('/del_proxy', endpoint='del_proxy', methods=['POST']) +@panel_login_required +def del_proxy(): + site_name = request.form.get("siteName", '') + rid = request.form.get("id", '') + return MwSites.instance().delProxy(site_name, rid) + + + + diff --git a/web/admin/site/redirect.py b/web/admin/site/redirect.py new file mode 100644 index 000000000..b8e641cd5 --- /dev/null +++ b/web/admin/site/redirect.py @@ -0,0 +1,79 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites + +import core.mw as mw +import thisdb + +from .site import blueprint + +# 获取重定向列表 +@blueprint.route('/get_redirect', endpoint='get_redirect', methods=['POST']) +@panel_login_required +def get_redirect(): + site_name = request.form.get("siteName", '') + return MwSites.instance().getRedirect(site_name) + +# 设置重定向列表 +@blueprint.route('/set_redirect', endpoint='set_redirect', methods=['POST']) +@panel_login_required +def set_redirect(): + site_name = request.form.get("siteName", '') + site_from = request.form.get("from", '') + to = request.form.get("to", '') # redirect to + type = request.form.get("type", '') # path / domain + r_type = request.form.get("r_type", '') # redirect type + keep_path = request.form.get("keep_path", '') # keep path + return MwSites.instance().setRedirect(site_name, site_from, to, type, r_type, keep_path) + + +# 设置重定向状态 +@blueprint.route('/set_redirect_status', endpoint='set_redirect_status', methods=['POST']) +@panel_login_required +def set_redirect_status(): + site_name = request.form.get("siteName", '') + status = request.form.get("status") + redirect_id = request.form.get("id", '') + return MwSites.instance().setRedirectStatus(site_name, redirect_id, status) + +# 获取重定向配置 +@blueprint.route('/get_redirect_conf', endpoint='get_redirect_conf', methods=['POST']) +@panel_login_required +def get_redirect_conf(): + site_name = request.form.get("siteName", '') + redirect_id = request.form.get("id", '') + return MwSites.instance().getRedirectConf(site_name, redirect_id) + +# 删除重定向配置 +@blueprint.route('/del_redirect', endpoint='del_redirect', methods=['POST']) +@panel_login_required +def del_redirect(): + site_name = request.form.get("siteName", '') + redirect_id = request.form.get("id", '') + return MwSites.instance().delRedirect(site_name, redirect_id) + + + + + + + + + diff --git a/web/admin/site/site.py b/web/admin/site/site.py new file mode 100644 index 000000000..84720f5e1 --- /dev/null +++ b/web/admin/site/site.py @@ -0,0 +1,347 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import core.mw as mw +import thisdb + +blueprint = Blueprint('site', __name__, url_prefix='/site', template_folder='../../templates/default') +@blueprint.route('/index', endpoint='index') +def index(): + return render_template('site.html') + +# 站点列表 +@blueprint.route('/list', endpoint='list', methods=['GET','POST']) +@panel_login_required +def list(): + p = request.form.get('p', '1') + limit = request.form.get('limit', '10') + type_id = request.form.get('type_id', '0').strip() + search = request.form.get('search', '').strip() + order = request.form.get('order', '').strip() + + info = thisdb.getSitesList(page=int(p),size=int(limit),type_id=int(type_id), search=search,order=order) + + data = {} + data['data'] = info['list'] + data['page'] = mw.getPage({'count':info['count'],'tojs':'getWeb','p':p, 'row':limit}) + return data + +# 添加站点 +@blueprint.route('/add', endpoint='add',methods=['POST']) +@panel_login_required +def add(): + webinfo = request.form.get('webinfo', '') + ps = request.form.get('ps', '') + path = request.form.get('path', '') + version = request.form.get('version', '') + port = request.form.get('port', '') + return MwSites.instance().add(webinfo, port, ps, path, version) + +# 站点停止 +@blueprint.route('/stop', endpoint='stop',methods=['POST']) +@panel_login_required +def stop(): + site_id = request.form.get('id', '') + return MwSites.instance().stop(site_id) + +# 站点开启 +@blueprint.route('/start', endpoint='start',methods=['POST']) +@panel_login_required +def start(): + site_id = request.form.get('id', '') + return MwSites.instance().start(site_id) + +# 添加站点 - 域名 +@blueprint.route('/add_domain', endpoint='add_domain',methods=['POST']) +@panel_login_required +def add_domain(): + domain = request.form.get('domain', '') + site_name = request.form.get('site_name', '') + site_id = request.form.get('id', '') + return MwSites.instance().addDomain(site_id, site_name, domain) + +# 删除站点 - 域名 +@blueprint.route('/del_domain', endpoint='del_domain',methods=['POST']) +@panel_login_required +def del_domain(): + site_name = request.form.get('site_name', '') + site_id = request.form.get('id', '') + domain = request.form.get('domain', '') + port = request.form.get('port', '') + return MwSites.instance().delDomain(site_id, site_name, domain, port) + +# 站点删除 +@blueprint.route('/delete', endpoint='delete',methods=['POST']) +@panel_login_required +def delete(): + site_id = request.form.get('id', '') + path = request.form.get('path', '') + return MwSites.instance().delete(site_id, path) + +# 获取站点根目录 +@blueprint.route('/get_root_dir', endpoint='get_root_dir',methods=['POST']) +@panel_login_required +def get_root_dir(): + data = {} + data['dir'] = mw.getWwwDir() + return data + +# 获取站点默认文档 +@blueprint.route('/get_index', endpoint='get_index',methods=['POST']) +@panel_login_required +def get_index(): + site_id = request.form.get('id', '') + data = {} + index = MwSites.instance().getIndex(site_id) + data['index'] = index + return data + +# 获取站点默认文档 +@blueprint.route('/set_index', endpoint='set_index',methods=['POST']) +@panel_login_required +def set_index(): + site_id = request.form.get('id', '') + index = request.form.get('index', '') + return MwSites.instance().setIndex(site_id, index) + +# 获取站点默认文档 +@blueprint.route('/get_limit_net', endpoint='get_limit_net',methods=['POST']) +@panel_login_required +def get_limit_net(): + site_id = request.form.get('id', '') + return MwSites.instance().getLimitNet(site_id) + +# 获取站点默认文档 +@blueprint.route('/set_limit_net', endpoint='set_limit_net',methods=['POST']) +@panel_login_required +def set_limit_net(): + site_id = request.form.get('id', '') + perserver = request.form.get('perserver', '') + perip = request.form.get('perip', '') + limit_rate = request.form.get('limit_rate', '') + return MwSites.instance().setLimitNet(site_id, perserver,perip,limit_rate) + +# 获取站点默认文档 +@blueprint.route('/close_limit_net', endpoint='close_limit_net',methods=['POST']) +@panel_login_required +def close_limit_net(): + site_id = request.form.get('id', '') + return MwSites.instance().closeLimitNet(site_id) + +# 获取站点配置 +@blueprint.route('/get_host_conf', endpoint='get_host_conf',methods=['POST']) +@panel_login_required +def get_host_conf(): + siteName = request.form.get('siteName', '') + host = MwSites.instance().getHostConf(siteName) + return {'host': host} + +# 设置站点配置 +@blueprint.route('/save_host_conf', endpoint='save_host_conf',methods=['POST']) +@panel_login_required +def save_host_conf(): + path = request.form.get('path', '') + data = request.form.get('data', '') + encoding = request.form.get('encoding', '') + return MwSites.instance().saveHostConf(path,data,encoding) + +# 获取站点PHP版本 +@blueprint.route('/get_site_php_version', endpoint='get_site_php_version',methods=['POST']) +@panel_login_required +def get_site_php_version(): + siteName = request.form.get('siteName', '') + return MwSites.instance().getSitePhpVersion(siteName) + +# 获取站点PHP版本 +@blueprint.route('/get_site_domains', endpoint='get_site_domains',methods=['POST']) +@panel_login_required +def get_site_domains(): + site_id = request.form.get('id', '') + data = thisdb.getSitesDomainById(site_id) + return mw.returnData(True, 'OK', data) + +# 设置站点PHP版本 +@blueprint.route('/set_php_version', endpoint='set_php_version',methods=['POST']) +@panel_login_required +def set_php_version(): + siteName = request.form.get('siteName', '') + version = request.form.get('version', '') + return MwSites.instance().setPhpVersion(siteName,version) + +# 检查OpenResty安装/启动状态 +@blueprint.route('/check_web_status', endpoint='check_web_status',methods=['POST']) +@panel_login_required +def check_web_status(): + ''' + 创建站点检查web服务 + ''' + if not mw.isInstalledWeb(): + return mw.returnJson(False, '请安装并启动OpenResty服务!') + + # 这个快点 + pid = mw.getServerDir() + '/openresty/nginx/logs/nginx.pid' + if not os.path.exists(pid): + return mw.returnData(False, '请启动OpenResty服务!') + return mw.returnData(True, 'OK') + +# 获取PHP版本 +@blueprint.route('/get_php_version', endpoint='get_php_version',methods=['POST']) +@panel_login_required +def get_php_version(): + return MwSites.instance().getPhpVersion() + +# 设置网站到期 +@blueprint.route('/set_end_date', endpoint='set_end_date',methods=['POST']) +@panel_login_required +def set_end_date(): + site_id = request.form.get('id', '') + edate = request.form.get('edate', '') + return MwSites.instance().setEndDate(site_id, edate) + + +# 设置网站备注 +@blueprint.route('/set_ps', endpoint='set_ps',methods=['POST']) +@panel_login_required +def set_ps(): + site_id = request.form.get('id', '') + ps = request.form.get('ps', '') + return MwSites.instance().setPs(site_id, ps) + +# 站点绑定域名 +@blueprint.route('/get_domain', endpoint='get_domain',methods=['POST']) +@panel_login_required +def get_domain(): + site_id = request.form.get('pid', '') + return MwSites.instance().getDomain(site_id) + +# 获取默认为静态列表 +@blueprint.route('/get_rewrite_list', endpoint='get_rewrite_list',methods=['POST']) +@panel_login_required +def get_rewrite_list(): + return MwSites.instance().getRewriteList() + +# 获取站点Rewrite配置 +@blueprint.route('/get_rewrite_conf', endpoint='get_rewrite_conf',methods=['POST']) +@panel_login_required +def get_rewrite_conf(): + siteName = request.form.get('siteName', '') + rewrite = MwSites.instance().getRewriteConf(siteName) + return {'rewrite': rewrite} + +# 获取Rewrite模版名 +@blueprint.route('/get_rewrite_tpl', endpoint='get_rewrite_tpl',methods=['POST']) +@panel_login_required +def get_rewrite_tpl(): + tplname = request.form.get('tplname', '') + return MwSites.instance().getRewriteTpl(tplname) + +# 设置站点Rewrite +@blueprint.route('/set_rewrite', endpoint='set_rewrite',methods=['POST']) +@panel_login_required +def set_rewrite(): + data = request.form.get('data', '') + path = request.form.get('path', '') + encoding = request.form.get('encoding', '') + return MwSites.instance().setRewrite(path,data,encoding) + + +# 设置Rewrite模版名 +@blueprint.route('/set_rewrite_tpl', endpoint='set_rewrite_tpl',methods=['POST']) +@panel_login_required +def set_rewrite_tpl(): + name = request.form.get('name', '') + data = request.form.get('data', '') + return MwSites.instance().setRewriteTpl(name,data) + +# 网站日志开关 +@blueprint.route('/logs_open', endpoint='logs_open',methods=['POST']) +@panel_login_required +def logs_open(): + site_id = request.form.get('id', '') + return MwSites.instance().logsOpen(site_id) + +# 设置网站路径 +@blueprint.route('/set_path', endpoint='set_path',methods=['POST']) +@panel_login_required +def set_path(): + site_id = request.form.get('id', '') + path = request.form.get('path', '') + return MwSites.instance().setSitePath(site_id, path) + + +# 设置网站路径 +@blueprint.route('/set_site_run_path', endpoint='set_site_run_path',methods=['POST']) +@panel_login_required +def set_site_run_path(): + site_id = request.form.get('id', '') + run_path = request.form.get('run_path', '') + return MwSites.instance().setSiteRunPath(site_id, run_path) + + +# 设置网站 - 开启密码访问 +@blueprint.route('/set_has_pwd', endpoint='set_has_pwd',methods=['POST']) +@panel_login_required +def set_has_pwd(): + site_id = request.form.get('id', '') + username = request.form.get('username', '') + password = request.form.get('password', '') + return MwSites.instance().setHasPwd(site_id, username, password) + +# 设置网站 - 关闭密码访问 +@blueprint.route('/close_has_pwd', endpoint='close_has_pwd',methods=['POST']) +@panel_login_required +def close_has_pwd(): + site_id = request.form.get('id', '') + return MwSites.instance().closeHasPwd(site_id) + +# 获取防盗链信息 +@blueprint.route('/get_security', endpoint='get_security',methods=['POST']) +@panel_login_required +def get_security(): + site_id = request.form.get('id', '') + return MwSites.instance().getSecurity(site_id) + +# 设置防盗链 +@blueprint.route('/set_security', endpoint='set_security',methods=['POST']) +@panel_login_required +def set_security(): + fix = request.form.get('fix', '') + domains = request.form.get('domains', '') + status = request.form.get('status', '') + name = request.form.get('name', '') + none = request.form.get('none', '') + site_id = request.form.get('id', '') + return MwSites.instance().setSecurity(site_id, fix, domains, status, none) + + +# 设置默认网站信息 +@blueprint.route('/get_default_site', endpoint='get_default_site',methods=['POST']) +@panel_login_required +def get_default_site(): + return MwSites.instance().getDefaultSite() + +# 设置默认站 +@blueprint.route('/set_default_site', endpoint='set_default_site',methods=['POST']) +@panel_login_required +def set_default_site(): + name = request.form.get('name', '') + return MwSites.instance().setDefaultSite(name) + + + diff --git a/web/admin/site/site_default.py b/web/admin/site/site_default.py new file mode 100644 index 000000000..293d128fd --- /dev/null +++ b/web/admin/site/site_default.py @@ -0,0 +1,37 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites + +import core.mw as mw +import thisdb + +from .site import blueprint + +@blueprint.route('/get_site_doc', endpoint='get_site_doc',methods=['POST']) +def get_site_doc(): + stype = request.form.get('type', '0').strip() + vlist = [] + vlist.append('') + vlist.append(mw.getServerDir() +'/openresty/nginx/html/index.html') + vlist.append(mw.getServerDir() + '/openresty/nginx/html/404.html') + vlist.append(mw.getServerDir() +'/openresty/nginx/html/index.html') + vlist.append(mw.getServerDir() + '/web_conf/stop/index.html') + data = {} + data['path'] = vlist[int(stype)] + return mw.returnData(True, 'ok', data) \ No newline at end of file diff --git a/web/admin/site/site_types.py b/web/admin/site/site_types.py new file mode 100644 index 000000000..0f6e34bcb --- /dev/null +++ b/web/admin/site/site_types.py @@ -0,0 +1,84 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import core.mw as mw +import thisdb + +from .site import blueprint + +# 获取网站分类 +@blueprint.route('/get_site_types', endpoint='get_site_types',methods=['POST']) +@panel_login_required +def get_site_types(): + data = thisdb.getSiteTypesList() + data.insert(0, {"id": 0, "name": "默认分类"}) + return mw.returnData(True, "ok", data) + + +# 添加网站分类 +@blueprint.route('/add_site_type', endpoint='add_site_type',methods=['POST']) +@panel_login_required +def add_site_type(): + name = request.form.get('name', '').strip() + return MwSites.instance().addSiteTypes(name) + + +# 添加网站分类 +@blueprint.route('/remove_site_type', endpoint='remove_site_type',methods=['POST']) +@panel_login_required +def remove_site_type(): + site_type_id = request.form.get('id', '') + if mw.M('site_types').where('id=?', (site_type_id,)).count() == 0: + return mw.returnData(False, "指定分类不存在!") + mw.M('site_types').where('id=?', (site_type_id,)).delete() + mw.M("sites").where("type_id=?", (site_type_id,)).save("type_id", (0,)) + return mw.returnData(True, "分类已删除!") + +# 修改网站分类 +@blueprint.route('/modify_site_type_name', endpoint='modify_site_type_name',methods=['POST']) +@panel_login_required +def modify_site_type_name(): + name = request.form.get('name', '').strip() + site_type_id = request.form.get('id', '') + if not name: + return mw.returnData(False, "分类名称不能为空") + if len(name) > 18: + return mw.returnData(False, "分类名称长度不能超过6个汉字或18位字母") + if mw.M('site_types').where('id=?', (site_type_id,)).count() == 0: + return mw.returnData(False, "指定分类不存在!") + mw.M('site_types').where('id=?', (site_type_id,)).setField('name', name) + return mw.returnData(True, "修改成功!") + +# 设置网站分类 +@blueprint.route('/set_site_type', endpoint='set_site_type',methods=['POST']) +@panel_login_required +def set_site_type(): + # 设置指定站点的分类 + site_ids = request.form.get('site_ids', '') + site_type_id = request.form.get('id', '') + site_ids = json.loads(site_ids) + for site_id in site_ids: + mw.M('sites').where('id=?', (site_id,)).setField('type_id', site_type_id) + return mw.returnData(True, "设置成功!") + + + + + diff --git a/web/admin/site/ssl.py b/web/admin/site/ssl.py new file mode 100644 index 000000000..17e3895c0 --- /dev/null +++ b/web/admin/site/ssl.py @@ -0,0 +1,120 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import core.mw as mw +import thisdb + +from .site import blueprint + + +# 获取证书信息 +@blueprint.route('/get_ssl', endpoint='get_ssl', methods=['POST']) +@panel_login_required +def get_ssl(): + site_name = request.form.get('site_name', '') + ssl_type = request.form.get('ssl_type', '') + return MwSites.instance().getSsl(site_name, ssl_type) + +# 获取证书信息 +@blueprint.route('/set_ssl', endpoint='set_ssl', methods=['POST']) +@panel_login_required +def set_ssl(): + site_name = request.form.get('siteName', '') + key = request.form.get('key', '') + csr = request.form.get('csr', '') + return MwSites.instance().setSsl(site_name, key, csr) + + +# 删除证书 +@blueprint.route('/close_ssl_conf', endpoint='close_ssl_conf', methods=['POST']) +@panel_login_required +def close_ssl_conf(): + site_name = request.form.get('siteName', '') + ssl_type = request.form.get('updateOf', '') + return MwSites.instance().closeSslConf(site_name) + + +# 删除证书 +@blueprint.route('/delete_ssl', endpoint='delete_ssl', methods=['POST']) +@panel_login_required +def delete_ssl(): + site_name = request.form.get('site_name', '') + ssl_type = request.form.get('ssl_type', '') + return MwSites.instance().deleteSsl(site_name, ssl_type) + + +# 获取证书列表 +@blueprint.route('/get_cert_list', endpoint='get_cert_list', methods=['GET','POST']) +@panel_login_required +def get_cert_list(): + return MwSites.instance().getCertList() + + +# 获取DNSAPI +@blueprint.route('/get_dnsapi', endpoint='get_dnsapi', methods=['GET','POST']) +@panel_login_required +def get_dnsapi(): + return MwSites.instance().getDnsapi() + +# 设置DNSAPI +@blueprint.route('/set_dnsapi', endpoint='set_dnsapi', methods=['GET','POST']) +@panel_login_required +def set_dnsapi(): + type = request.form.get('type', '') + data = request.form.get('data') + return MwSites.instance().setDnsapi(type,data) + +# 设置证书到站点 +@blueprint.route('/set_cert_to_site', endpoint='set_cert_to_site', methods=['GET','POST']) +@panel_login_required +def set_cert_to_site(): + site_name = request.form.get('siteName', '') + cert_name = request.form.get('certName', '') + return MwSites.instance().setCertToSite(site_name, cert_name) + +# 删除证书 +@blueprint.route('/remove_cert', endpoint='remove_cert', methods=['GET','POST']) +@panel_login_required +def remove_cert(): + cert_name = request.form.get('certName', '') + return MwSites.instance().removeCert(cert_name) + +# 强制开启HTTPS +@blueprint.route('/http_to_https', endpoint='http_to_https', methods=['GET','POST']) +@panel_login_required +def http_to_https(): + site_name = request.form.get('siteName', '') + return MwSites.instance().httpToHttps(site_name) + +# 强制关闭HTTPS +@blueprint.route('/close_to_https', endpoint='close_to_https', methods=['GET','POST']) +@panel_login_required +def close_to_https(): + site_name = request.form.get('siteName', '') + return MwSites.instance().closeToHttps(site_name) + + + + + + + + + diff --git a/web/admin/site/ssl_acme.py b/web/admin/site/ssl_acme.py new file mode 100644 index 000000000..fea23b068 --- /dev/null +++ b/web/admin/site/ssl_acme.py @@ -0,0 +1,55 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import core.mw as mw +import thisdb + +from .site import blueprint + + +# 获取ACME日志 +@blueprint.route('/get_acme_logs', endpoint='get_acme_logs', methods=['POST']) +@panel_login_required +def get_acme_logs(): + log_file = mw.getPanelDir() + '/logs/acme.log' + if not os.path.exists(log_file): + mw.execShell('touch ' + log_file) + return mw.returnData(True, 'OK', log_file) + + +@blueprint.route('/create_acme', endpoint='create_acme', methods=['POST']) +@panel_login_required +def create_acme(): + site_name = request.form.get('siteName', '') + domains = request.form.get('domains', '') + force = request.form.get('force', '') + renew = request.form.get('renew', '') + email = request.form.get('email', '') + wildcard_domain = request.form.get('wildcard_domain','') + apply_type = request.form.get('apply_type', 'file') + dnspai = request.form.get('dnspai','') + return MwSites.instance().createAcme(site_name, domains, force, renew, apply_type, dnspai, email, wildcard_domain) + + + + + + + diff --git a/web/admin/site/ssl_let.py b/web/admin/site/ssl_let.py new file mode 100644 index 000000000..dda93e6fa --- /dev/null +++ b/web/admin/site/ssl_let.py @@ -0,0 +1,55 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import core.mw as mw +import thisdb + +from .site import blueprint + + +# 获取ACME日志 +@blueprint.route('/get_let_logs', endpoint='get_let_logs', methods=['POST']) +@panel_login_required +def get_let_logs(): + log_file = MwSites.instance().letLogFile() + if not os.path.exists(log_file): + mw.execShell('touch ' + log_file) + return mw.returnData(True, 'OK', log_file) + + +@blueprint.route('/create_let', endpoint='create_let', methods=['POST']) +@panel_login_required +def create_let(): + site_name = request.form.get('siteName', '') + domains = request.form.get('domains', '') + force = request.form.get('force', '') + renew = request.form.get('renew', '') + email = request.form.get('email', '') + wildcard_domain = request.form.get('wildcard_domain','') + apply_type = request.form.get('apply_type', 'file') + dnspai = request.form.get('dnspai','') + return MwSites.instance().createLet(site_name, domains, force, renew, apply_type, dnspai, email, wildcard_domain) + + + + + + + diff --git a/web/admin/soft/__init__.py b/web/admin/soft/__init__.py index 09a417826..21d7bb903 100644 --- a/web/admin/soft/__init__.py +++ b/web/admin/soft/__init__.py @@ -13,8 +13,11 @@ from flask import Blueprint, render_template from admin.user_login_check import panel_login_required +import thisdb + blueprint = Blueprint('soft', __name__, url_prefix='/soft', template_folder='../../templates') @blueprint.route('/index', endpoint='index') @panel_login_required def index(): - return render_template('default/soft.html') \ No newline at end of file + name = thisdb.getOption('template', default='default') + return render_template('%s/soft.html' % name) \ No newline at end of file diff --git a/web/admin/system/__init__.py b/web/admin/system/__init__.py index 840978ec2..47d29fb71 100644 --- a/web/admin/system/__init__.py +++ b/web/admin/system/__init__.py @@ -8,169 +8,6 @@ # Author: midoks # --------------------------------------------------------------------------------- - -from flask import Blueprint, render_template -from flask import request - -from admin.user_login_check import panel_login_required -import admin.model.option as option - -import core.mw as mw -import utils.system as sys - - -blueprint = Blueprint('system', __name__, url_prefix='/system', template_folder='../../templates') - -# 获取系统的统计信息 -@blueprint.route('/system_total', endpoint='system_total') -@panel_login_required -def system_total(): - data = sys.getMemInfo() - cpu = sys.getCpuInfo(interval=1) - data['cpuNum'] = cpu[1] - data['cpuRealUsed'] = cpu[0] - data['time'] = sys.getBootTime() - data['system'] = sys.getSystemVersion() - data['version'] = '0.0.1' - return data - -# 获取系统的网络流量信息 -@blueprint.route('/network', endpoint='network') -@panel_login_required -def network(): - stat = {} - stat['cpu'] = sys.getCpuInfo() - stat['load'] = sys.getLoadAverage() - stat['mem'] = sys.getMemInfo() - stat['iostat'] = sys.stats().disk() - stat['network'] = sys.stats().network() - return stat - -# 获取系统的磁盘信息 -@blueprint.route('/disk_info', endpoint='disk_info') -@panel_login_required -def disk_info(): - data = sys.getDiskInfo() - return data - -# 获取系统的负载统计信息 -@blueprint.route('/get_load_average', endpoint='get_load_average', methods=['GET']) -@panel_login_required -def get_load_average(): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = mw.M('load_average').dbPos(mw.getPanelDataDir(),'system')\ - .where("addtime>=? AND addtime<=?", (start, end,))\ - .field('id,pro,one,five,fifteen,addtime')\ - .order('id asc').select() - # return self.toAddtime(data) - # print(data) - return data - -# 获取系统的磁盘IO统计信息 -@blueprint.route('/get_disk_io', endpoint='get_disk_io', methods=['GET']) -@panel_login_required -def get_disk_io(): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = mw.M('diskio').dbPos(mw.getPanelDataDir(),'system')\ - .where("addtime>=? AND addtime<=?", (start, end))\ - .field('id,read_count,write_count,read_bytes,write_bytes,read_time,write_time,addtime')\ - .order('id asc').select() - return data - -# 获取系统的CPU/IO统计信息 -@blueprint.route('/get_cpu_io', endpoint='get_cpu_io', methods=['GET']) -@panel_login_required -def get_cpu_io(): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = mw.M('cpuio').dbPos(mw.getPanelDataDir(),'system')\ - .where("addtime>=? AND addtime<=?",(start, end))\ - .field('id,pro,mem,addtime')\ - .order('id asc').select() - # return self.toAddtime(data) - # print(data) - return data - - -# 获取系统网络IO统计信息 -@blueprint.route('/get_network_io', endpoint='get_network_io', methods=['GET']) -@panel_login_required -def get_network_io(): - start = request.args.get('start', '') - end = request.args.get('end', '') - data = mw.M('network').dbPos(mw.getPanelDataDir(),'system')\ - .where("addtime>=? AND addtime<=?", (start, end))\ - .field('id,up,down,total_up,total_down,down_packets,up_packets,addtime')\ - .order('id asc').select() - # return self.toAddtime(data) - # print(data) - return data - -# 获取系统网络IO统计信息 -@blueprint.route('/set_control', endpoint='set_control', methods=['POST']) -@panel_login_required -def set_control(): - stype = request.form.get('type', '') - day = request.form.get('day', '') - - if stype == '0': - option.setOption('monitor_status', 'close', type='monitor') - return mw.returnData(True, "设置成功!") - elif stype == '1': - _day = int(day) - if _day < 1: - return mw.returnData(False, "设置失败!") - option.setOption('monitor_day', day, type='monitor') - return mw.returnData(True, "设置成功!") - elif stype == '2': - option.setOption('monitor_only_netio', 'close', type='monitor') - return mw.returnData(True, "设置成功!") - elif stype == '3': - option.setOption('monitor_only_netio', 'open', type='monitor') - return mw.returnData(True, "设置成功!") - elif stype == 'del': - if not mw.isRestart(): - return mw.returnData(False, '请等待所有安装任务完成再执行') - os.remove("data/system.db") - - sql = db.Sql().dbfile('system') - csql = mw.readFile('data/sql/system.sql') - csql_list = csql.split(';') - for index in range(len(csql_list)): - sql.execute(csql_list[index], ()) - return mw.returnData(True, "监控服务已关闭") - else: - monitor_status = option.getOption('monitor_status', default='open', type='monitor') - monitor_day = option.getOption('monitor_day', default='30', type='monitor') - monitor_only_netio = option.getOption('monitor_only_netio', default='open', type='monitor') - data = {} - data['day'] = monitor_day - if monitor_status == 'open': - data['status'] = True - else: - data['status'] = False - if monitor_only_netio == 'open': - data['stat_all_status'] = True - else: - data['stat_all_status'] = False - - return data - - return mw.returnData(False, "异常!") - - - - -# 升级检测 -@blueprint.route('/update_server', endpoint='update_server') -@panel_login_required -def update_server(): - panel_type = request.args.get('type', 'check') - version = request.args.get('version', '') - - return mw.returnData(False, '已经是最新,无需更新!') - - +from .system import * +from .upgrade import * diff --git a/web/admin/system/system.py b/web/admin/system/system.py new file mode 100644 index 000000000..8135c3ce3 --- /dev/null +++ b/web/admin/system/system.py @@ -0,0 +1,163 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required +from utils.system import monitor + +import core.mw as mw +import utils.system as sys +import thisdb + +blueprint = Blueprint('system', __name__, url_prefix='/system', template_folder='../../templates') + +# 获取系统的统计信息 +@blueprint.route('/system_total', endpoint='system_total', methods=['GET','POST']) +@panel_login_required +def system_total(): + data = sys.getMemInfo() + cpu = sys.getCpuInfo(interval=1) + data['cpuNum'] = cpu[1] + data['cpuRealUsed'] = cpu[0] + data['time'] = sys.getBootTime() + data['system'] = sys.getSystemVersion() + data['version'] = '0.0.1' + return data + +# 获取环境信息 +@blueprint.route('/get_env_info', endpoint='get_env_info', methods=['GET','POST']) +@panel_login_required +def get_env_info(): + return sys.getEnvInfo() + +# 获取系统的网络流量信息 +@blueprint.route('/network', endpoint='network') +@panel_login_required +def network(): + stat = {} + stat['cpu'] = sys.getCpuInfo() + stat['load'] = sys.getLoadAverage() + stat['mem'] = sys.getMemInfo() + stat['iostat'] = sys.stats().disk() + stat['network'] = sys.stats().network() + return stat + +# 获取系统的磁盘信息 +@blueprint.route('/disk_info', endpoint='disk_info', methods=['GET','POST']) +@panel_login_required +def disk_info(): + data = sys.getDiskInfo() + return mw.returnData(True, 'ok', data) + +# 获取系统的负载统计信息 +@blueprint.route('/get_load_average', endpoint='get_load_average', methods=['GET']) +@panel_login_required +def get_load_average(): + start = request.args.get('start', '') + end = request.args.get('end', '') + data = sys.getLoadAverageByDB(start, end) + return mw.returnData(True, 'ok', data) + +# 获取系统的磁盘IO统计信息 +@blueprint.route('/get_disk_io', endpoint='get_disk_io', methods=['GET']) +@panel_login_required +def get_disk_io(): + start = request.args.get('start', '') + end = request.args.get('end', '') + data = sys.getDiskIoByDB(start, end) + return mw.returnData(True, 'ok', data) + +# 获取系统的CPU/IO统计信息 +@blueprint.route('/get_cpu_io', endpoint='get_cpu_io', methods=['GET']) +@panel_login_required +def get_cpu_io(): + start = request.args.get('start', '') + end = request.args.get('end', '') + data = sys.getCpuIoByDB(start, end) + return mw.returnData(True, 'ok', data) + +# 获取系统网络IO统计信息 +@blueprint.route('/get_network_io', endpoint='get_network_io', methods=['GET']) +@panel_login_required +def get_network_io(): + start = request.args.get('start', '') + end = request.args.get('end', '') + data = sys.getNetworkIoByDB(start, end) + return mw.returnData(True, 'ok', data) + +# 重启面板 +@blueprint.route('/restart', endpoint='restart', methods=['POST']) +@panel_login_required +def restart(): + mw.restartMw() + return mw.returnData(True, '面板已重启!') + +# 重启面板 +@blueprint.route('/restart_server', endpoint='restart_server', methods=['POST']) +@panel_login_required +def restart_server(): + if mw.isAppleSystem(): + return mw.returnData(False, "开发环境不可重起!") + sys.restartServer() + return mw.returnData(True, '正在重启服务器!') + +# 设置 +@blueprint.route('/set_control', endpoint='set_control', methods=['POST']) +@panel_login_required +def set_control(): + stype = request.form.get('type', '') + day = request.form.get('day', '') + + if stype == '0': + thisdb.setOption('monitor_status', 'close', type='monitor') + return mw.returnData(True, "设置成功!") + elif stype == '1': + _day = int(day) + if _day < 1: + return mw.returnData(False, "设置失败!") + thisdb.setOption('monitor_day', day, type='monitor') + return mw.returnData(True, "设置成功!") + elif stype == '2': + thisdb.setOption('monitor_only_netio', 'close', type='monitor') + return mw.returnData(True, "设置成功!") + elif stype == '3': + thisdb.setOption('monitor_only_netio', 'open', type='monitor') + return mw.returnData(True, "设置成功!") + elif stype == 'del': + if not mw.isRestart(): + return mw.returnData(False, '请等待所有安装任务完成再执行') + monitor.instance().clearDbFile() + return mw.returnData(True, "清空监控记录成功!") + else: + monitor_status = thisdb.getOption('monitor_status', default='open', type='monitor') + monitor_day = thisdb.getOption('monitor_day', default='30', type='monitor') + monitor_only_netio = thisdb.getOption('monitor_only_netio', default='open', type='monitor') + data = {} + data['day'] = monitor_day + if monitor_status == 'open': + data['status'] = True + else: + data['status'] = False + if monitor_only_netio == 'open': + data['stat_all_status'] = True + else: + data['stat_all_status'] = False + + return data + + return mw.returnData(False, "异常!") + + + + diff --git a/web/admin/system/upgrade.py b/web/admin/system/upgrade.py new file mode 100644 index 000000000..9418027f7 --- /dev/null +++ b/web/admin/system/upgrade.py @@ -0,0 +1,31 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +import core.mw as mw +import utils.system as sys + +from .system import blueprint + +# 升级检测 +@blueprint.route('/update_server', endpoint='update_server') +@panel_login_required +def update_server(): + panel_type = request.args.get('type', 'check') + version = request.args.get('version', '') + return sys.updateServer(panel_type, version) + + + diff --git a/web/admin/task/__init__.py b/web/admin/task/__init__.py index 2cb6ea08e..476a042b9 100644 --- a/web/admin/task/__init__.py +++ b/web/admin/task/__init__.py @@ -8,24 +8,25 @@ # Author: midoks # --------------------------------------------------------------------------------- +import json +import time from flask import Blueprint, render_template from flask import request -from admin import model -from admin.model import db,Tasks from admin.user_login_check import panel_login_required import core.mw as mw +import utils.task as MwTasks +import thisdb blueprint = Blueprint('task', __name__, url_prefix='/task', template_folder='../../templates/default') -@blueprint.route('/count', endpoint='task_count') +@blueprint.route('/count', endpoint='task_count',methods=['GET','POST']) @panel_login_required def task_count(): - return str(model.getTaskUnexecutedCount()) - + return mw.returnData(True, 'ok',thisdb.getTaskUnexecutedCount()) @blueprint.route('/list', endpoint='list', methods=['POST']) @panel_login_required @@ -33,74 +34,59 @@ def list(): p = request.form.get('p', '1') limit = request.form.get('limit', '10').strip() search = request.form.get('search', '').strip() - - count = Tasks.query.filter_by().count() - pagination = Tasks.query.filter_by().order_by(Tasks.id.desc()).paginate(page=int(p), per_page=int(limit)) - - rows = [] - for item in pagination.items: - t = {} - t['id'] = item.id - t['name'] = item.name - t['type'] = item.type - t['cmd'] = item.cmd - t['start'] = item.start - t['end'] = item.end - t['status'] = item.status - t['add_time'] = item.add_time - rows.append(t) - - data = {} - data['data'] = rows - data['page'] = mw.getPage({'count':count,'tojs':'remind','p':p}) - return data + return MwTasks.getTaskPage(int(p), int(limit)) @blueprint.route('/get_exec_log', endpoint='get_exec_log', methods=['POST']) @panel_login_required def get_exec_log(): - file = mw.getPanelTaskLog() + file = mw.getPanelTaskExecLog() return mw.getLastLine(file, 100) @blueprint.route('/get_task_speed', endpoint='get_task_speed', methods=['POST']) @panel_login_required def get_task_speed(): - count = model.getTaskUnexecutedCount() + count = thisdb.getTaskUnexecutedCount() if count == 0: return mw.returnData(False, '当前没有任务队列在执行-2!') - row = model.getTaskFirstByRun() + row = thisdb.getTaskFirstByRun() if row is None: return mw.returnData(False, '当前没有任务队列在执行-3!') - task_logfile = mw.getPanelTaskLog() + task_logfile = mw.getPanelTaskExecLog() data = {} data['name'] = row['name'] data['cmd'] = row['cmd'] if row['type'] == 'download': - readLine = "" + readLine = '' for i in range(3): try: readLine = mw.readFile(task_logfile) - if len(readLine) > 10: - data['msg'] = json.loads(readLine) - data['isDownload'] = True - break + data['msg'] = json.loads(readLine) + data['isDownload'] = True except Exception as e: if i == 2: - model.setTaskStatus(row['id'],0) + thisdb.setTaskStatus(row['id'],0) return mw.returnData(False, '当前没有任务队列在执行-4:' + str(e)) time.sleep(0.5) else: data['msg'] = mw.getLastLine(task_logfile, 10) data['isDownload'] = False - - data['task'] = model.getTaskList(status=-1) + data['count'] = count + data['task'] = thisdb.getTaskRunList(1,6) return data - + +@blueprint.route('/remove_task', endpoint='remove_task', methods=['POST']) +@panel_login_required +def remove_task(): + task_id = request.form.get('id', '') + if task_id == '': + return mw.returnData(False, '任务ID不能为空!') + return MwTasks.removeTask(task_id) \ No newline at end of file diff --git a/web/admin/user_login_check.py b/web/admin/user_login_check.py index 63fbb096d..7d7bc2e44 100644 --- a/web/admin/user_login_check.py +++ b/web/admin/user_login_check.py @@ -11,20 +11,33 @@ from flask import render_template from flask import Response +from flask import request from functools import wraps -from admin import model from admin import session from admin.common import isLogined +import thisdb def panel_login_required(func): @wraps(func) def wrapper(*args, **kwargs): + # 面板API调用检查 + app_id = request.headers.get('App-Id','') + app_secret = request.headers.get('App-Secret','') + if app_id != '' and app_secret != '': + panel_api = thisdb.getOptionByJson('panel_api', default={"open":True}) + if panel_api['open']: + return_code = 404 + info = thisdb.getAppByAppId(app_id) + if app_secret != info['app_secret']: + return Response(status=int(return_code)) + return func(*args, **kwargs) + if not isLogined(): - unauthorized_status = model.getOption('unauthorized_status') + unauthorized_status = thisdb.getOption('unauthorized_status') if unauthorized_status == '0': return render_template('default/path.html') return Response(status=int(unauthorized_status)) diff --git a/web/app.py b/web/app.py index b51d018a1..cb1bac80b 100644 --- a/web/app.py +++ b/web/app.py @@ -8,33 +8,26 @@ # Author: midoks # --------------------------------------------------------------------------------- + + import sys +import os + +from admin import app, socketio +import config if sys.version_info < (3, 6): raise RuntimeError('This application must be run under Python 3.6 or later.') -import os - # 我们需要在sys.path中包含根目录,以确保我们可以找到在独立运行时运行时所需的一切。 if sys.path[0] != os.path.dirname(os.path.realpath(__file__)): sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) -# print(sys.path) -import config -from admin import app, socketio - -# print(config.DEFAULT_SERVER,config.DEFAULT_SERVER_PORT) -# app = create_app() def main(): - socketio.run( - app, - debug=config.DEBUG, - allow_unsafe_werkzeug=True, - host=config.DEFAULT_SERVER, - port=7201, - ) + socketio.run(app,debug=config.DEBUG) + # app.run(debug=True) if __name__ == '__main__': - app.run(debug=True) + main() diff --git a/web/config.py b/web/config.py index 1b84db8df..afe82f97d 100644 --- a/web/config.py +++ b/web/config.py @@ -12,7 +12,6 @@ # 配置信息 # --------------------------------------------------------------------------------- - import builtins import logging import os @@ -41,22 +40,40 @@ CONFIG_DATABASE_CONNECTION_MAX_OVERFLOW = 100 CONSOLE_LOG_LEVEL = logging.WARNING FILE_LOG_LEVEL = logging.WARNING +# Number of values to trust for X-Forwarded-For +PROXY_X_FOR_COUNT = 1 +# Number of values to trust for X-Forwarded-Proto. +PROXY_X_PROTO_COUNT = 1 +# Number of values to trust for X-Forwarded-Host. +PROXY_X_HOST_COUNT = 0 +# Number of values to trust for X-Forwarded-Port. +PROXY_X_PORT_COUNT = 1 +# Number of values to trust for X-Forwarded-Prefix. +PROXY_X_PREFIX_COUNT = 0 + + DATA_DIR = mw.getPanelDataDir() # 日志文件名。这将进入数据目录,服务器模式下的非Windows平台除外。 LOG_FILE = os.path.join(mw.getMWLogs(), APP_LOG_NAME + '.log') +CONSOLE_LOG_FORMAT = '%(asctime)s: %(levelname)s\t%(name)s:\t%(message)s' +FILE_LOG_FORMAT = '%(asctime)s: %(levelname)s\t%(name)s:\t%(message)s' + # 日志旋转设置日志文件将根据LOG_ROTATION_SIZE和LOG_ROTATION_AGE的值进行切换。 # 旋转的文件将以格式命名Y-m-d_H-M-S -LOG_ROTATION_SIZE = 10 # MBs +LOG_ROTATION_SIZE = 1 # MBs LOG_ROTATION_AGE = 1440 # minutes -LOG_ROTATION_MAX_LOG_FILES = 90 # 要保留的最大备份数 +LOG_ROTATION_MAX_LOG_FILES = 5 # 要保留的最大备份数 # 用于存储用户帐户和设置的SQLite数据库的默认路径。 # 此默认设置将文件放置在与此相同的目录中 配置文件,但会生成一个在整个应用程序中使用的绝对路径。 SQLITE_PATH = os.path.join(DATA_DIR, APP_SQLITE_NAME + '.db') -DEFAULT_SERVER = '127.0.0.1' +DEFAULT_SERVER = '0.0.0.0' DEFAULT_SERVER_PORT = 7201 +# APP启动时间 +APP_START_TIME=0 + diff --git a/web/core/db.py b/web/core/db.py index 10bddcc09..2cd72efa8 100755 --- a/web/core/db.py +++ b/web/core/db.py @@ -12,11 +12,18 @@ # sqlite3操作 # --------------------------------------------------------------------------------- - -import sqlite3 import os import sys +import sqlite3 +import core.mw as mw + +def getPanelDir(): + return os.path.dirname(os.getcwd()) + +def getTracebackInfo(): + import traceback + return traceback.format_exc() class Sql(): #------------------------------ @@ -32,8 +39,10 @@ class Sql(): __OPT_FIELD = "*" # field条件 __OPT_PARAM = () # where值 + __debug = False + def __init__(self): - self.__DB_FILE = '../data/default.db' + self.__DB_FILE = getPanelDir()+'/data/panel.db' def __getConn(self): # 取数据库对象 @@ -42,23 +51,25 @@ class Sql(): self.__DB_CONN = sqlite3.connect(self.__DB_FILE) self.__DB_CONN.text_factory = str except Exception as ex: - # print(mw.getTracebackInfo()) + print(getTracebackInfo()) return "error: " + str(ex) def changeTextFactoryToBytes(self): self.__DB_CONN.text_factory = bytes return True + def debug(self, debug=False): + self.__debug = debug + return self + def autoTextFactory(self): if sys.version_info[0] == 3: - self.__DB_CONN.text_factory = lambda x: str( - x, encoding="utf-8", errors='ignore') + self.__DB_CONN.text_factory = lambda x: str(x, encoding="utf-8", errors='ignore') else: - self.__DB_CONN.text_factory = lambda x: unicode( - x, "utf-8", "ignore") + self.__DB_CONN.text_factory = lambda x: unicode(x, "utf-8", "ignore") def dbfile(self, name): - self.__DB_FILE = 'data/' + name + '.db' + self.__DB_FILE = getPanelDir()+'/data/' + name + '.db' return self def dbPos(self, path, name, suffix_name = 'db'): @@ -115,39 +126,73 @@ class Sql(): self.__OPT_FIELD = field return self + def getDbField(self,name): + sql = "PRAGMA table_info(%s)" % name + result = self.__DB_CONN.execute(sql) + data = result.fetchall() + + fields = [] + for i in data: + fields.append(i[1]) + return fields + + def getDbFieldString(self,name): + fields = self.getDbField(name) + return ','.join(fields) + + def select(self): # 查询数据集 self.__getConn() try: sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \ self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT - # print(sql) - # print(self.__OPT_PARAM) + + if self.__debug: + print(sql) + print(self.__OPT_PARAM) + result = self.__DB_CONN.execute(sql, self.__OPT_PARAM) data = result.fetchall() + if len(data) == 0: + return data + # 构造字曲系列 if self.__OPT_FIELD != "*": field = self.__OPT_FIELD.split(',') tmp = [] for row in data: i = 0 - tmp1 = {} + t = {} for key in field: - tmp1[key] = row[i] + t[key] = row[i] i += 1 - tmp.append(tmp1) - del(tmp1) + tmp.append(t) + del(t) data = tmp del(tmp) else: - # 将元组转换成列表 - tmp = map(list, data) + field = self.getDbField(self.__DB_TABLE) + tmp = [] + for row in data: + i = 0 + t = {} + for key in field: + t[key] = row[i] + i += 1 + tmp.append(t) + del(t) data = tmp del(tmp) + # 将元组转换成列表 + # tmp = map(list, data) + # data = tmp + # del(tmp) self.__close() return data except Exception as ex: - return "error: " + str(ex) + # return "error: " + str(ex) + return [] def inquiry(self, input_field=''): # 查询数据集 @@ -157,7 +202,8 @@ class Sql(): sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \ self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT - if os.path.exists('data/debug.pl'): + debug = getPanelDir()+'/data/debug.pl' + if os.path.exists(debug): print(sql, self.__OPT_PARAM) result = self.__DB_CONN.execute(sql, self.__OPT_PARAM) data = result.fetchall() @@ -192,10 +238,9 @@ class Sql(): def getField(self, keyName): # 取回指定字段 result = self.field(keyName).select() - # print(result) if len(result) == 1: return result[0][keyName] - return result + return None def setField(self, keyName, keyValue): # 更新指定字段 @@ -206,7 +251,7 @@ class Sql(): result = self.limit("1").select() if len(result) == 1: return result[0] - return result + return None def count(self): # 取行数 @@ -233,7 +278,8 @@ class Sql(): self.__DB_CONN.commit() return last_id except Exception as ex: - return "error: " + str(ex) + print(str(ex)) + return 0 # 插入数据 def insert(self, pdata): @@ -305,8 +351,8 @@ class Sql(): opt = opt[0:len(opt) - 1] sql = "UPDATE " + self.__DB_TABLE + " SET " + opt + self.__OPT_WHERE - # import mw - # mw.writeFile('/tmp/test.pl', sql) + if self.__debug: + print(sql, param) # 处理拼接WHERE与UPDATE参数 tmp = list(param) diff --git a/web/core/mw.py b/web/core/mw.py index 76693f045..35f2d940e 100644 --- a/web/core/mw.py +++ b/web/core/mw.py @@ -50,13 +50,24 @@ def execShell(cmdstring, cwd=None, timeout=None, shell=True): return sub.communicate() data = sub.communicate() - # python3 fix 返回byte数据 - if isinstance(data[0], bytes): - t1 = str(data[0], encoding='utf-8') - if isinstance(data[1], bytes): - t2 = str(data[1], encoding='utf-8') - return (t1, t2) + success = data[0] + error = data[1] + # python3 fix 返回byte数据 + if isinstance(success, bytes): + # success = str(success, encoding='utf-8') + try: + success = success.decode('utf-8') + except Exception as e: + success = str(e) + + if isinstance(error, bytes): + # error = str(error, encoding='utf-8') + try: + error = error.decode('utf-8') + except Exception as e: + error = str(e) + return (success, error) def getTracebackInfo(): @@ -84,13 +95,12 @@ def getPanelDataDir(): def getMWLogs(): return getPanelDir() + '/logs' +def getPanelLogs(): + return getPanelDir() + '/logs' + def getPanelTmp(): return getPanelDir() + '/tmp' -def getPanelTaskLog(): - return getMWLogs() + '/panel_task.log' - - def getServerDir(): return getFatherDir() + '/server' @@ -103,6 +113,34 @@ def getRecycleBinDir(): os.system('mkdir -p ' + rb_dir) return rb_dir +def getPanelTaskLog(): + return getMWLogs() + '/panel_task.log' + +def getPanelTaskExecLog(): + return getMWLogs() + '/panel_exec.log' + +def getWwwDir(): + import thisdb + site_path = thisdb.getOption('site_path') + return site_path + +def getBackupDir(): + import thisdb + backup_path = thisdb.getOption('backup_path') + return backup_path + +def setBackupDir(bdir): + import thisdb + thisdb.setOption('backup_path', bdir) + return True + +def getPanelPort(): + port_file = getPanelDir()+'/data/port.pl' + port = readFile(port_file).strip() + if not port: + return 7200 + return int(port) + def getRandomString(length): # 取随机字符串 rnd_str = '' @@ -156,6 +194,105 @@ def getCommonFile(): } return data +def checkCert(certPath='ssl/certificate.pem'): + # 验证证书 + openssl = '/usr/bin/openssl' + if not os.path.exists(openssl): + openssl = '/usr/local/openssl/bin/openssl' + if not os.path.exists(openssl): + openssl = 'openssl' + certPem = readFile(certPath) + s = "\n-----BEGIN CERTIFICATE-----" + tmp = certPem.strip().split(s) + for tmp1 in tmp: + if tmp1.find('-----BEGIN CERTIFICATE-----') == -1: + tmp1 = s + tmp1 + writeFile(certPath, tmp1) + result = execShell(openssl + " x509 -in " + + certPath + " -noout -subject") + if result[1].find('-bash:') != -1: + return True + if len(result[1]) > 2: + return False + if result[0].find('error:') != -1: + return False + return True + +def sortFileList(path, ftype = 'mtime', sort = 'desc'): + flist = os.listdir(path) + if ftype == 'mtime': + if sort == 'desc': + flist = sorted(flist, key=lambda f: os.path.getmtime(os.path.join(path,f)), reverse=True) + if sort == 'asc': + flist = sorted(flist, key=lambda f: os.path.getmtime(os.path.join(path,f)), reverse=False) + + if ftype == 'size': + if sort == 'desc': + flist = sorted(flist, key=lambda f: os.path.getsize(os.path.join(path,f)), reverse=True) + if sort == 'asc': + flist = sorted(flist, key=lambda f: os.path.getsize(os.path.join(path,f)), reverse=False) + + if ftype == 'fname': + if sort == 'desc': + flist = sorted(flist, key=lambda f: os.path.join(path,f), reverse=True) + if sort == 'asc': + flist = sorted(flist, key=lambda f: os.path.join(path,f), reverse=False) + return flist + + +def sortAllFileList(path, ftype = 'mtime', sort = 'desc', search = '',limit = 3000): + count = 0 + flist = [] + for d_list in os.walk(path): + if count >= limit: + break + + for d in d_list[1]: + if count >= limit: + break + if d.lower().find(search) != -1: + filename = d_list[0] + '/' + d + if not os.path.exists(filename): + continue + count += 1 + flist.append(filename) + + for f in d_list[2]: + if count >= limit: + break + + if f.lower().find(search) != -1: + filename = d_list[0] + '/' + f + if not os.path.exists(filename): + continue + count += 1 + flist.append(filename) + + if ftype == 'mtime': + if sort == 'desc': + flist = sorted(flist, key=lambda f: os.path.getmtime(f), reverse=True) + if sort == 'asc': + flist = sorted(flist, key=lambda f: os.path.getmtime(f), reverse=False) + + if ftype == 'size': + if sort == 'desc': + flist = sorted(flist, key=lambda f: os.path.getsize(f), reverse=True) + if sort == 'asc': + flist = sorted(flist, key=lambda f: os.path.getsize(f), reverse=False) + return flist + +def getPathSize(path): + # 取文件或目录大小 + if not os.path.exists(path): + return 0 + if not os.path.isdir(path): + return os.path.getsize(path) + size_total = 0 + for nf in os.walk(path): + for f in nf[2]: + filename = nf[0] + '/' + f + size_total += os.path.getsize(filename) + return size_total def toSize(size, middle='') -> str: """ @@ -176,6 +313,8 @@ def returnData(status, msg, data=None): return {'status': status, 'msg': msg, 'data': data} def returnJson(status, msg, data=None): + if not data: + return getJson({'status': status, 'msg': msg}) return getJson({'status': status, 'msg': msg, 'data': data}) def readFile(filename): @@ -186,7 +325,7 @@ def readFile(filename): fp.close() return fBody except Exception as e: - # print(e) + print('readFile:',str(e)) return False def writeFile(filename, content, mode='w+'): @@ -197,12 +336,46 @@ def writeFile(filename, content, mode='w+'): fp.close() return True except Exception as e: + writeFileLog(getTracebackInfo()) return False -def triggerTask(): - isTask = getPanelDir() + '/tmp/panelTask.pl' - writeFile(isTask, 'True') +def backFile(file, act=None): + """ + @name 备份配置文件 + @param file 需要备份的文件 + @param act 如果存在,则备份一份作为默认配置 + """ + file_type = "_bak" + if act: + file_type = "_def" + + # print("cp -p {0} {1}".format(file, file + file_type)) + execShell("cp -p {0} {1}".format(file, file + file_type)) + + +def removeBackFile(file, act=None): + """ + @name 删除备份配置文件 + @param file 需要删除备份文件 + @param act 如果存在,则还原默认配置 + """ + file_type = "_bak" + if act: + file_type = "_def" + execShell("rm -rf {0}".format(file + file_type)) + + +def restoreFile(file, act=None): + """ + @name 还原配置文件 + @param file 需要还原的文件 + @param act 如果存在,则还原默认配置 + """ + file_type = "_bak" + if act: + file_type = "_def" + execShell("cp -p {1} {0}".format(file, file + file_type)) def systemdCfgDir(): # ubuntu @@ -242,7 +415,11 @@ def md5(content): except Exception as ex: return False - +def hasPwd(password): + # 加密密码字符 + import crypt + return crypt.crypt(password, password) + def getFileMd5(filename): # 文件的MD5值 if not os.path.isfile(filename): @@ -259,10 +436,97 @@ def getFileMd5(filename): return myhash.hexdigest() +def getHost(port=False): + from flask import request + host_tmp = request.headers.get('host') + if not host_tmp: + if request.url_root: + tmp = re.findall(r"(https|http)://([\w:\.-]+)", request.url_root) + if tmp: + host_tmp = tmp[0][1] + if not host_tmp: + host_tmp = getLocalIp() + ':' + readFile('data/port.pl').strip() + try: + if host_tmp.find(':') == -1: + host_tmp += ':80' + except: + host_tmp = "127.0.0.1:8888" + h = host_tmp.split(':') + if port: + return h[-1] + return ':'.join(h[0:-1]) + def getClientIp(): from flask import request return request.remote_addr.replace('::ffff:', '') +def checkDomainPanel(): + import thisdb + from flask import Flask, redirect, request, url_for + + current_host = getHost() + domain = thisdb.getOption('panel_domain', default='') + port = getPanelPort() + scheme = 'http' + + panel_ssl_data = thisdb.getOptionByJson('panel_ssl', default={'open':False}) + if panel_ssl_data['open']: + if not inArray(['local','nginx'], panel_ssl_data['choose']): + return False + scheme = 'https' + + client_ip = getClientIp() + if client_ip in ['127.0.0.1', 'localhost', '::1']: + return False + + ip = getHostAddr() + if domain == '': + if ip in ['127.0.0.1', 'localhost', '::1']: + return False + if current_host.strip().lower() != ip.strip().lower(): + to = scheme + "://" + ip + ":" + str(port) + return redirect(to, code=302) + return False + else: + # print(current_host.strip().lower(), domain.strip().lower()) + if current_host.strip().lower() != domain.strip().lower(): + to = scheme + "://" + domain + ":" + str(port) + return redirect(to, code=302) + return False + +def getLocalIp(): + filename = getPanelDir()+'/data/iplist.txt' + try: + ipaddress = readFile(filename) + if not ipaddress or ipaddress == '127.0.0.1': + cmd = "curl --insecure -4 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text" + ip = execShell(cmd) + result = ip[0].strip() + if result == '': + raise Exception("ipv4 is empty!") + writeFile(filename, result) + return result + return ipaddress + except Exception as e: + cmd = "curl --insecure -6 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text" + ip = execShell(cmd) + result = ip[0].strip() + if result == '': + return '127.0.0.1' + writeFile(filename, result) + return result + finally: + pass + return '127.0.0.1' + + +def inArray(arrays, searchStr): + # 搜索数据中是否存在 + for key in arrays: + if key == searchStr: + return True + + return False def getJson(data): import json @@ -284,26 +548,23 @@ def getSslCrt(): def getOs(): return sys.platform - def getOsName(): cmd = "cat /etc/*-release | grep PRETTY_NAME |awk -F = '{print $2}' | awk -F '\"' '{print $2}'| awk '{print $1}'" data = execShell(cmd) return data[0].strip().lower() - def getOsID(): cmd = "cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F '\"' '{print $2}'" data = execShell(cmd) return data[0].strip() # 获取文件权限描述 -def getFileStatsDesc( - filename: str | None = None, - path: str | None = None, -): - # print(filename,path) - filename = filename.replace('//', '/') +def getFileStatsDesc(filename, path=None): + import pwd + if path == '' or filename == '': + return ';;;;;' try: + filename = filename.replace('//', '/') stat = os.stat(filename) accept = str(oct(stat.st_mode)[-3:]) mtime = str(int(stat.st_mtime)) @@ -312,6 +573,7 @@ def getFileStatsDesc( user = str(pwd.getpwuid(stat.st_uid).pw_name) except: user = str(stat.st_uid) + size = str(stat.st_size) link = '' if os.path.islink(filename): @@ -339,6 +601,57 @@ def getHostAddr(): return readFile(ip_text).strip() return '127.0.0.1' +def checkIp(ip): + # 检查是否为IPv4地址 + import re + p = re.compile(r'^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$') + if p.match(ip): + return True + else: + return False + +def createLinuxUser(user, group): + execShell("groupadd {}".format(group)) + execShell('useradd -s /sbin/nologin -g {} {}'.format(user, group)) + return True + + +def setOwn(filename, user, group=None): + if isAppleSystem(): + return True + + # 设置用户组 + if not os.path.exists(filename): + return False + from pwd import getpwnam + try: + user_info = getpwnam(user) + user = user_info.pw_uid + if group: + user_info = getpwnam(group) + group = user_info.pw_gid + except: + if user == 'www': + createLinuxUser(user) + # 如果指定用户或组不存在,则使用www + try: + user_info = getpwnam('www') + except: + createLinuxUser(user) + user_info = getpwnam('www') + user = user_info.pw_uid + group = user_info.pw_gid + os.chown(filename, user, group) + return True + +def setMode(filename, mode): + # 设置文件权限 + if not os.path.exists(filename): + return False + mode = int(str(mode), 8) + os.chmod(filename, mode) + return True + def getSqitePrefix(): WIN = sys.platform.startswith('win') if WIN: # 如果是 Windows 系统,使用三个斜线 @@ -347,6 +660,26 @@ def getSqitePrefix(): prefix = 'sqlite:////' return prefix +def checkPort(port): + # 检查端口是否合法 + ports = ['21', '443', '888'] + if port in ports: + return False + intport = int(port) + if intport < 1 or intport > 65535: + return False + return True + +def getStrBetween(startStr, endStr, srcStr): + # 字符串取中间 + start = srcStr.find(startStr) + if start == -1: + return None + end = srcStr.find(endStr) + if end == -1: + return None + return srcStr[start + 1:end] + def getCpuType(): cpuType = '' if isAppleSystem(): @@ -375,6 +708,34 @@ def getCpuType(): return cpuType +def getLanguage(): + panel_dir = getPanelDir() + path = panel_dir+'/data/language.pl' + if not os.path.exists(path): + return 'Simplified_Chinese' + return readFile(path).strip() + + +def getStaticJson(name="public"): + file = 'static/language/' + getLanguage() + '/' + name + '.json' + if not os.path.exists(file): + file = 'route/static/language/' + getLanguage() + '/' + name + '.json' + return file + + +def returnMsg(status, msg, args=()): + # 取通用字曲返回 + pjson = getStaticJson('public') + logMessage = json.loads(readFile(pjson)) + keys = logMessage.keys() + + if msg in keys: + msg = logMessage[msg] + for i in range(len(args)): + rep = '{' + str(i + 1) + '}' + msg = msg.replace(rep, args[i]) + return {'status': status, 'msg': msg, 'data': args} + def getInfo(msg, args=()): # 取提示消息 for i in range(len(args)): @@ -487,8 +848,9 @@ def getPageObject(args, result='1,2,3,4,5,8'): def getHostPort(): - if os.path.exists('data/port.pl'): - return readFile('data/port.pl').strip() + port_file = getPanelDir() + '/data/port.pl' + if os.path.exists(port_file): + return readFile(port_file).strip() return '7200' @@ -519,10 +881,9 @@ def isDebugMode(): if isAppleSystem(): return True - debugPath = getPanelDir() + "/data/debug.pl" - if os.path.exists(debugPath): + debug = M('option').field('name').where('name=?',('debug',)).getField('value') + if debug == 'open': return True - return False def isNumber(s): @@ -541,7 +902,6 @@ def isNumber(s): return False - # 检查端口是否占用 def isOpenPort(port): import socket @@ -562,30 +922,85 @@ def debugLog(*data): def writeLog(stype, msg, args=()): # 写日志 uid = 0 - try: - from flask import session - if 'uid' in session: - uid = session['uid'] - except Exception as e: - print("writeLog:"+str(e)) + # try: + # from flask import session + # if 'uid' in session: + # uid = session['uid'] + # except Exception as e: + # print("writeLog:"+str(e)) # pass # writeFileLog(getTracebackInfo()) return writeDbLog(stype, msg, args, uid) +def writeFileLog(msg, path=None, limit_size=50 * 1024 * 1024, save_limit=3): + log_file = getPanelDir() + '/logs/debug.log' + if path != None: + log_file = path + + if os.path.exists(log_file): + size = os.path.getsize(log_file) + if size > limit_size: + log_file_rename = log_file + "_" + \ + time.strftime("%Y-%m-%d_%H%M%S") + '.log' + os.rename(log_file, log_file_rename) + logs = sorted(glob.glob(log_file + "_*")) + count = len(logs) + save_limit = count - save_limit + for i in range(count): + if i > save_limit: + break + os.remove(logs[i]) + # print('|---多余日志[' + logs[i] + ']已删除!') + + f = open(log_file, 'ab+') + msg += "\n" + if __name__ == '__main__': + print(msg) + f.write(msg.encode('utf-8')) + f.close() + return True + def writeDbLog(stype, msg, args=(), uid=1): try: - from admin import model + import thisdb format_msg = getInfo(msg, args) - model.addLog(stype, format_msg, uid) + thisdb.addLog(stype, format_msg, uid=uid) return True except Exception as e: print("writeDbLog:"+str(e)) return False +def writeSpeed(title, used, total, speed=0): + panel_dir = getPanelDir() + speed_file= panel_dir + '/data/panel_speed.pl' + # 写进度 + if not title: + data = {'title': None, 'progress': 0,'total': 0, 'used': 0, 'speed': 0} + else: + progress = int((100.0 * used / total)) + data = {'title': title, 'progress': progress,'total': total, 'used': used, 'speed': speed} + writeFile(speed_file, json.dumps(data)) + return True -def M(table): + +def getSpeed(): + panel_dir = getPanelDir() + speed_file= panel_dir + '/data/panel_speed.pl' + # 取进度 + path = getPanelDir() + data = readFile(speed_file) + if not data: + data = json.dumps({'title': None, 'progress': 0,'total': 0, 'used': 0, 'speed': 0}) + writeFile(speed_file, data) + return json.loads(data) + + + +def M(table=''): import core.db as db sql = db.Sql() + if table == '': + return sql return sql.table(table) @@ -625,11 +1040,383 @@ def deDoubleCrypt(key, strings): writeFileLog(getTracebackInfo()) return strings +def aesEncrypt(data, key='ABCDEFGHIJKLMNOP', vi='0102030405060708'): + # aes加密 + # @param data 被加密的数据 + # @param key 加解密密匙 16位 + # @param vi 16位 + + from cryptography.hazmat.primitives import padding + from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + from cryptography.hazmat.backends import default_backend + + if not isinstance(data, bytes): + data = data.encode() + + # AES_CBC_KEY = os.urandom(32) + # AES_CBC_IV = os.urandom(16) + + AES_CBC_KEY = key.encode() + AES_CBC_IV = vi.encode() + + # print("AES_CBC_KEY:", AES_CBC_KEY) + # print("AES_CBC_IV:", AES_CBC_IV) + + padder = padding.PKCS7(algorithms.AES.block_size).padder() + padded_data = padder.update(data) + padder.finalize() + + cipher = Cipher(algorithms.AES(AES_CBC_KEY), + modes.CBC(AES_CBC_IV), + backend=default_backend()) + encryptor = cipher.encryptor() + + edata = encryptor.update(padded_data) + + # print(edata) + # print(str(edata)) + # print(edata.decode()) + return edata + + +def aesDecrypt(data, key='ABCDEFGHIJKLMNOP', vi='0102030405060708'): + # aes加密 + # @param data 被解密的数据 + # @param key 加解密密匙 16位 + # @param vi 16位 + + from cryptography.hazmat.primitives import padding + from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + from cryptography.hazmat.backends import default_backend + + from cryptography.hazmat.primitives import padding + from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + from cryptography.hazmat.backends import default_backend + + if not isinstance(data, bytes): + data = data.encode() + + AES_CBC_KEY = key.encode() + AES_CBC_IV = vi.encode() + + cipher = Cipher(algorithms.AES(AES_CBC_KEY), + modes.CBC(AES_CBC_IV), + backend=default_backend()) + decryptor = cipher.decryptor() + + ddata = decryptor.update(data) + + unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() + data = unpadder.update(ddata) + + try: + uppadded_data = data + unpadder.finalize() + except ValueError: + raise Exception('无效的加密信息!') + + return uppadded_data + +def aesEncrypt_Crypto(data, key, vi): + # 该方法保留,暂时不使用 + # aes加密 + # @param data 被加密的数据 + # @param key 加解密密匙 16位 + # @param vi 16位 + + from Crypto.Cipher import AES + cryptor = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8')) + # 判断是否含有中文 + zhmodel = re.compile(u'[\u4e00-\u9fff]') + match = zhmodel.search(data) + if match == None: + # 无中文时 + add = 16 - len(data) % 16 + pad = lambda s: s + add * chr(add) + data = pad(data) + enctext = cryptor.encrypt(data.encode('utf8')) + else: + # 含有中文时 + data = data.encode() + add = 16 - len(data) % 16 + data = data + add * (chr(add)).encode() + enctext = cryptor.encrypt(data) + encodestrs = base64.b64encode(enctext).decode('utf8') + return encodestrs + + +def aesDecrypt_Crypto(data, key, vi): + # 该方法保留,暂时不使用 + # aes加密 + # @param data 被加密的数据 + # @param key 加解密密匙 16位 + # @param vi 16位 + + from crypto.Cipher import AES + data = data.encode('utf8') + encodebytes = base64.urlsafe_b64decode(data) + cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8')) + text_decrypted = cipher.decrypt(encodebytes) + # 判断是否含有中文 + zhmodel = re.compile(u'[\u4e00-\u9fff]') + match = zhmodel.search(text_decrypted) + if match == False: + # 无中文时补位 + unpad = lambda s: s[0:-s[-1]] + text_decrypted = unpad(text_decrypted) + text_decrypted = text_decrypted.decode('utf8').rstrip() # 去掉补位的右侧空格 + return text_decrypted + +def getDefault(data,val,def_val=''): + if val in data: + return data[val] + return def_val + +def encodeImage(imgsrc, newsrc): + # 图片加密 + import struct + old_fp = open(imgsrc, 'rb') + imgFile = old_fp.read() + old_fp.close() + + new_fp = open(newsrc,"wb") + for x in imgFile: + value = x ^ 86 + value = hex(value) + s = struct.pack('B',int(value,16)) + new_fp.write(s) + new_fp.close() + return True + +def buildSoftLink(src, dst, force=False): + ''' + 建立软连接 + ''' + if not os.path.exists(src): + return False + + if os.path.exists(dst) and force: + os.remove(dst) + + if not os.path.exists(dst): + execShell('ln -sf "' + src + '" "' + dst + '"') + return True + return False +# ------------------------------ network start ----------------------------- + +def HttpGet(url, timeout=10): + """ + 发送GET请求 + @url 被请求的URL地址(必需) + @timeout 超时时间默认60秒 + return string + """ + if sys.version_info[0] == 2: + try: + import urllib2 + import ssl + if sys.version_info[0] == 2: + reload(urllib2) + reload(ssl) + try: + ssl._create_default_https_context = ssl._create_unverified_context + except: + pass + response = urllib2.urlopen(url, timeout=timeout) + return response.read() + except Exception as ex: + return str(ex) + else: + try: + import urllib.request + import ssl + try: + ssl._create_default_https_context = ssl._create_unverified_context + except: + pass + response = urllib.request.urlopen(url, timeout=timeout) + result = response.read() + if type(result) == bytes: + result = result.decode('utf-8') + return result + except Exception as ex: + return str(ex) + + +def HttpGet2(url, timeout): + import urllib.request + + try: + import ssl + try: + ssl._create_default_https_context = ssl._create_unverified_context + except: + pass + req = urllib.request.urlopen(url, timeout=timeout) + result = req.read().decode('utf-8') + return result + + except Exception as e: + return str(e) + + +def httpGet(url, timeout=10): + return HttpGet2(url, timeout) + + +def HttpPost(url, data, timeout=10): + """ + 发送POST请求 + @url 被请求的URL地址(必需) + @data POST参数,可以是字符串或字典(必需) + @timeout 超时时间默认60秒 + return string + """ + if sys.version_info[0] == 2: + try: + import urllib + import urllib2 + import ssl + ssl._create_default_https_context = ssl._create_unverified_context + data = urllib.urlencode(data) + req = urllib2.Request(url, data) + response = urllib2.urlopen(req, timeout=timeout) + return response.read() + except Exception as ex: + return str(ex) + else: + try: + import urllib.request + import ssl + try: + ssl._create_default_https_context = ssl._create_unverified_context + except: + pass + data = urllib.parse.urlencode(data).encode('utf-8') + req = urllib.request.Request(url, data) + response = urllib.request.urlopen(req, timeout=timeout) + result = response.read() + if type(result) == bytes: + result = result.decode('utf-8') + return result + except Exception as ex: + return str(ex) + + +def httpPost(url, data, timeout=10): + return HttpPost(url, data, timeout) + +# ------------------------------ network end ----------------------------- + +# ------------------------------ panel start ----------------------------- + +def isRestart(): + # 检查是否允许重启 + num = M('tasks').where('status!=?', ('1',)).count() + if num > 0: + return False + return True + +def getAcmeDir(): + acme = '/root/.acme.sh' + if isAppleSystem(): + cmd = "who | sed -n '2, 1p' |awk '{print $1}'" + user = execShell(cmd)[0].strip() + acme = '/Users/' + user + '/.acme.sh' + if not os.path.exists(acme): + acme = '/.acme.sh' + return acme + + +def getAcmeDomainDir(domain): + acme_dir = getAcmeDir() + acme_domain = acme_dir + '/' + domain + acme_domain_ecc = acme_domain + '_ecc' + if os.path.exists(acme_domain_ecc): + acme_domain = acme_domain_ecc + return acme_domain + + +def fileNameCheck(filename): + f_strs = [';', '&', '<', '>'] + for fs in f_strs: + if filename.find(fs) != -1: + return False + return True + +def getTriggerTaskLockFile(): + return getPanelDir() + '/logs/panel_task.lock' + +def triggerTask(): + lock_file = getTriggerTaskLockFile() + writeFile(lock_file, 'True') + +def restartTask(): + initd = getPanelDir() + '/scripts/init.d/mw' + if os.path.exists(initd): + cmd = initd + ' ' + 'restart_task' + os.system(cmd) + return True + +def restartMw(): + restart_file = getPanelDir()+'/data/restart.pl' + writeFile(restart_file, 'True') + return True + +def panelCmd(method): + cmd = '/etc/init.d/mw' + if os.path.exists(cmd): + execShell(cmd + ' ' + method) + return + + cmd = getPanelDir() + '/scripts/init.d/mw' + if os.path.exists(cmd): + data = execShell(cmd + ' ' + method) + return + +# ------------------------------ panel end ----------------------------- + # ------------------------------ openresty start ----------------------------- +def getOpVer(): + version = '' + version_file_pl = getServerDir() + '/openresty/version.pl' + if os.path.exists(version_file_pl): + version = readFile(version_file_pl) + version = version.strip() + return version + +def checkWebConfig(): + op_dir = getServerDir() + '/openresty/nginx' + # "ulimit -n 10240 && " + + cmd = op_dir + "/sbin/nginx -t -c " + op_dir + "/conf/nginx.conf" + result = execShell(cmd) + searchStr = 'test is successful' + if result[1].find(searchStr) == -1: + msg = getInfo('配置文件错误: {1}', (result[1],)) + writeLog("软件管理", msg) + return result[1] + return True + +def isIpAddr(ip): + check_ip = re.compile(r'^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\\d)$') + if check_ip.match(ip): + return True + else: + return False + +def getWebStatus(): + pid = getServerDir() + '/openresty/nginx/logs/nginx.pid' + if os.path.exists(pid): + return True + return False + + def restartWeb(): return opWeb("reload") +def deleteFile(file): + if os.path.exists(file): + os.remove(file) + def isInstalledWeb(): path = getServerDir() + '/openresty/nginx/sbin/nginx' if os.path.exists(path): @@ -641,19 +1428,25 @@ def opWeb(method): return False # systemd - systemd = '/lib/systemd/system/openresty.service' + systemd = systemdCfgDir() + '/openresty.service' if os.path.exists(systemd): execShell('systemctl ' + method + ' openresty') return True + + sys_initd = '/etc/init.d/openresty' + if os.path.exists(sys_initd): + os.system(sys_initd + ' ' + method) + return True + # initd initd = getServerDir() + '/openresty/init.d/openresty' - if os.path.exists(initd): execShell(initd + ' ' + method) return True return False + def opLuaMake(cmd_name): path = getServerDir() + '/web_conf/nginx/lua/lua.conf' root_dir = getServerDir() + '/web_conf/nginx/lua/' + cmd_name @@ -710,9 +1503,59 @@ def opLuaMakeAll(): # ------------------------------ openresty end ----------------------------- +# --------------------------------------------------------------------------------- +# PHP START +# --------------------------------------------------------------------------------- + +def getFpmConfFile(version): + return getServerDir() + '/php/' + version + '/etc/php-fpm.d/www.conf' + +def getFpmAddress(version): + fpm_address = '/tmp/php-cgi-{}.sock'.format(version) + php_fpm_file = getFpmConfFile(version) + try: + content = readFile(php_fpm_file) + tmp = re.findall(r"listen\s*=\s*(.+)", content) + if not tmp: + return fpm_address + if tmp[0].find('sock') != -1: + return fpm_address + if tmp[0].find(':') != -1: + listen_tmp = tmp[0].split(':') + if bind: + fpm_address = (listen_tmp[0], int(listen_tmp[1])) + else: + fpm_address = ('127.0.0.1', int(listen_tmp[1])) + else: + fpm_address = ('127.0.0.1', int(tmp[0])) + return fpm_address + except: + return fpm_address + +def requestFcgiPHP(sock, uri, document_root='/tmp', method='GET', pdata=b''): + # 直接请求到PHP-FPM + # version php版本 + # uri 请求uri + # filename 要执行的php文件 + # args 请求参数 + # method 请求方式 + + import utils.php.fpm as fpm + p = fpm.fpm(sock, document_root) + + if type(pdata) == dict: + pdata = url_encode(pdata) + result = p.load_url_public(uri, pdata, method) + return result +# --------------------------------------------------------------------------------- +# PHP END +# --------------------------------------------------------------------------------- + + # --------------------------------------------------------------------------------- # 数据库 START # --------------------------------------------------------------------------------- + def getMyORM(): ''' 获取MySQL资源的ORM @@ -724,6 +1567,430 @@ def getMyORM(): # 数据库 START # --------------------------------------------------------------------------------- +##################### ssl start ######################################### + +def strfDate(sdate): + return time.strftime('%Y-%m-%d', time.strptime(sdate, '%Y%m%d%H%M%S')) + +# 获取证书名称 +def getCertName(certPath): + if not os.path.exists(certPath): + return None + try: + import OpenSSL + result = {} + x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, readFile(certPath)) + # 取产品名称 + issuer = x509.get_issuer() + result['issuer'] = '' + if hasattr(issuer, 'CN'): + result['issuer'] = issuer.CN + if not result['issuer']: + is_key = [b'0', '0'] + issue_comp = issuer.get_components() + if len(issue_comp) == 1: + is_key = [b'CN', 'CN'] + for iss in issue_comp: + if iss[0] in is_key: + result['issuer'] = iss[1].decode() + break + if not result['issuer']: + if hasattr(issuer, 'O'): + result['issuer'] = issuer.O + # 取到期时间 + result['notAfter'] = strfDate(bytes.decode(x509.get_notAfter())[:-1]) + # 取申请时间 + result['notBefore'] = strfDate(bytes.decode(x509.get_notBefore())[:-1]) + # 取可选名称 + result['dns'] = [] + for i in range(x509.get_extension_count()): + s_name = x509.get_extension(i) + if s_name.get_short_name() in [b'subjectAltName', 'subjectAltName']: + s_dns = str(s_name).split(',') + for d in s_dns: + result['dns'].append(d.split(':')[1]) + subject = x509.get_subject().get_components() + # 取主要认证名称 + if len(subject) == 1: + result['subject'] = subject[0][1].decode() + else: + if not result['dns']: + for sub in subject: + if sub[0] == b'CN': + result['subject'] = sub[1].decode() + break + if 'subject' in result: + result['dns'].append(result['subject']) + else: + result['subject'] = result['dns'][0] + result['endtime'] = int(int(time.mktime(time.strptime( + result['notAfter'], "%Y-%m-%d")) - time.time()) / 86400) + return result + except Exception as e: + writeFileLog(getTracebackInfo()) + return None + +def createLocalSSL(): + pdir = getPanelDir() + local_dir = pdir+'/ssl/local' + if not os.path.exists(local_dir): + execShell('mkdir -p ' + local_dir) + + # 自签证书 + # if os.path.exists('ssl/local/input.pl'): + # return True + + client_ip = getClientIp() + + import OpenSSL + key = OpenSSL.crypto.PKey() + key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) + cert = OpenSSL.crypto.X509() + cert.set_serial_number(0) + + if client_ip == '127.0.0.1': + cert.get_subject().CN = '127.0.0.1' + else: + cert.get_subject().CN = getLocalIp() + + cert.set_issuer(cert.get_subject()) + cert.gmtime_adj_notBefore(0) + cert.gmtime_adj_notAfter(86400 * 3650) + cert.set_pubkey(key) + cert.sign(key, 'md5') + cert_ca = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) + private_key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) + if len(cert_ca) > 100 and len(private_key) > 100: + writeFile(local_dir+'/cert.pem', cert_ca, 'wb+') + writeFile(local_dir+'/private.pem', private_key, 'wb+') + return True + return False + + +def getSSHPort(): + try: + file = '/etc/ssh/sshd_config' + conf = readFile(file) + rep = "(#*)?Port\\s+([0-9]+)\\s*\n" + port = re.search(rep, conf).groups(0)[1] + return int(port) + except: + return 22 + + +def getSSHStatus(): + if os.path.exists('/usr/bin/apt-get'): + status = execShell("service ssh status | grep -P '(dead|stop)'") + else: + import system_api + version = system_api.system_api().getSystemVersion() + if version.find(' Mac ') != -1: + return True + if version.find(' 7.') != -1: + status = execShell("systemctl status sshd.service | grep 'dead'") + else: + status = execShell( + "/etc/init.d/sshd status | grep -e 'stopped' -e '已停'") + if len(status[0]) > 3: + status = False + else: + status = True + return status + +##################### ssl end ######################################### + +def getGlibcVersion(): + try: + cmd_result = execShell("ldd --version")[0] + if not cmd_result: return '' + glibc_version = cmd_result.split("\n")[0].split()[-1] + except: + return '' + return glibc_version + +##################### ssh start ######################################### +def getSshDir(): + if isAppleSystem(): + user = execShell("who | sed -n '2, 1p' |awk '{print $1}'")[0].strip() + return '/Users/' + user + '/.ssh' + return '/root/.ssh' + + +def processExists(pname, exe=None, cmdline=None): + # 进程是否存在 + try: + import psutil + pids = psutil.pids() + for pid in pids: + try: + p = psutil.Process(pid) + if p.name() == pname: + if not exe and not cmdline: + return True + else: + if exe: + if p.exe() == exe: + return True + if cmdline: + if cmdline in p.cmdline(): + return True + except: + pass + return False + except: + return True + + +def createRsa(): + # ssh-keygen -t rsa -P "" -C "midoks@163.com" + ssh_dir = getSshDir() + # mw.execShell("rm -f /root/.ssh/*") + if not os.path.exists(ssh_dir + '/authorized_keys'): + execShell('touch ' + ssh_dir + '/authorized_keys') + + if not os.path.exists(ssh_dir + '/id_rsa.pub') and os.path.exists(ssh_dir + '/id_rsa'): + execShell('echo y | ssh-keygen -q -t rsa -P "" -f ' + + ssh_dir + '/id_rsa') + else: + execShell('ssh-keygen -q -t rsa -P "" -f ' + ssh_dir + '/id_rsa') + + execShell('cat ' + ssh_dir + '/id_rsa.pub >> ' + + ssh_dir + '/authorized_keys') + execShell('chmod 600 ' + ssh_dir + '/authorized_keys') + + +def createSshInfo(): + ssh_dir = getSshDir() + if not os.path.exists(ssh_dir + '/id_rsa') or not os.path.exists(ssh_dir + '/id_rsa.pub'): + createRsa() + + # 检查是否写入authorized_keys + data = execShell("cat " + ssh_dir + "/id_rsa.pub | awk '{print $3}'") + if data[0] != "": + cmd = "cat " + ssh_dir + "/authorized_keys | grep " + data[0] + ak_data = execShell(cmd) + if ak_data[0] == "": + cmd = 'cat ' + ssh_dir + '/id_rsa.pub >> ' + ssh_dir + '/authorized_keys' + execShell(cmd) + execShell('chmod 600 ' + ssh_dir + '/authorized_keys') + + +def connectSsh(): + import paramiko + ssh = paramiko.SSHClient() + createSshInfo() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + port = getSSHPort() + try: + ssh.connect('127.0.0.1', port, timeout=5) + except Exception as e: + ssh.connect('localhost', port, timeout=5) + except Exception as e: + ssh.connect(getHostAddr(), port, timeout=30) + except Exception as e: + return False + + shell = ssh.invoke_shell(term='xterm', width=83, height=21) + shell.setblocking(0) + return shell + + +def clearSsh(): + # 服务器IP + ip = getHostAddr() + sh = ''' +#!/bin/bash +PLIST=`who | grep localhost | awk '{print $2}'` +for i in $PLIST +do + ps -t /dev/$i |grep -v TTY | awk '{print $1}' | xargs kill -9 +done + +# getHostAddr +PLIST=`who | grep "${ip}" | awk '{print $2}'` +for i in $PLIST +do + ps -t /dev/$i |grep -v TTY | awk '{print $1}' | xargs kill -9 +done +''' + if not isAppleSystem(): + info = execShell(sh) + print(info[0], info[1]) +##################### ssh end ######################################### + +##################### notify start ######################################### + + +def initNotifyConfig(): + p = getNotifyPath() + if not os.path.exists(p): + writeFile(p, '{}') + return True + + +def getNotifyPath(): + path = 'data/notify.json' + return path + + +def getNotifyData(is_parse=False): + initNotifyConfig() + notify_file = getNotifyPath() + notify_data = readFile(notify_file) + + data = json.loads(notify_data) + + if is_parse: + tag_list = ['tgbot', 'email'] + for t in tag_list: + if t in data and 'cfg' in data[t]: + data[t]['data'] = json.loads(deDoubleCrypt(t, data[t]['cfg'])) + return data + + +def writeNotify(data): + p = getNotifyPath() + return writeFile(p, json.dumps(data)) + + +def tgbotNotifyChatID(): + data = getNotifyData(True) + if 'tgbot' in data and 'enable' in data['tgbot']: + if data['tgbot']['enable']: + t = data['tgbot']['data'] + return t['chat_id'] + return '' + + +def tgbotNotifyObject(): + data = getNotifyData(True) + if 'tgbot' in data and 'enable' in data['tgbot']: + if data['tgbot']['enable']: + t = data['tgbot']['data'] + import telebot + bot = telebot.TeleBot(app_token) + return True, bot + return False, None + + +def tgbotNotifyMessage(app_token, chat_id, msg): + import telebot + bot = telebot.TeleBot(app_token) + try: + data = bot.send_message(chat_id, msg) + return True + except Exception as e: + writeFileLog(str(e)) + return False + + +def tgbotNotifyHttpPost(app_token, chat_id, msg): + try: + url = 'https://api.telegram.org/bot' + app_token + '/sendMessage' + post_data = { + 'chat_id': chat_id, + 'text': msg, + } + rdata = httpPost(url, post_data) + return True + except Exception as e: + writeFileLog(str(e)) + return str(e) + return False + + +def tgbotNotifyTest(app_token, chat_id): + msg = 'MW-通知验证测试OK' + return tgbotNotifyHttpPost(app_token, chat_id, msg) + + +def emailNotifyMessage(data): + ''' + 邮件通知 + ''' + import utils.email as email + try: + if data['smtp_ssl'] == 'ssl': + r = email.sendSSL(data['smtp_host'], data['smtp_port'], + data['username'], data['password'], + data['to_mail_addr'], data['subject'], data['content']) + else: + r = email.send(data['smtp_host'], data['smtp_port'], + data['username'], data['password'], + data['to_mail_addr'], data['subject'], data['content']) + + print(r) + return True + except Exception as e: + print(getTracebackInfo()) + return str(e) + return False + + +def emailNotifyTest(data): + # print(data) + data['subject'] = 'MW通知测试' + data['content'] = data['mail_test'] + return emailNotifyMessage(data) + + +def notifyMessageTry(msg, stype='common', trigger_time=300, is_write_log=True): + + lock_file = getPanelTmp() + '/notify_lock.json' + if not os.path.exists(lock_file): + writeFile(lock_file, '{}') + + lock_data = json.loads(readFile(lock_file)) + if stype in lock_data: + diff_time = time.time() - lock_data[stype]['do_time'] + if diff_time >= trigger_time: + lock_data[stype]['do_time'] = time.time() + else: + return False + else: + lock_data[stype] = {'do_time': time.time()} + + writeFile(lock_file, json.dumps(lock_data)) + + if is_write_log: + writeLog("通知管理[" + stype + "]", msg) + + data = getNotifyData(True) + # tag_list = ['tgbot', 'email'] + # tagbot + do_notify = False + if 'tgbot' in data and 'enable' in data['tgbot']: + if data['tgbot']['enable']: + t = data['tgbot']['data'] + i = sys.version_info + + # telebot 在python小于3.7无法使用 + if i[0] < 3 or i[1] < 7: + do_notify = tgbotNotifyHttpPost( + t['app_token'], t['chat_id'], msg) + else: + do_notify = tgbotNotifyMessage( + t['app_token'], t['chat_id'], msg) + + if 'email' in data and 'enable' in data['email']: + if data['email']['enable']: + t = data['email']['data'] + t['subject'] = 'MW通知' + t['content'] = msg + do_notify = emailNotifyMessage(t) + return do_notify + + +def notifyMessage(msg, stype='common', trigger_time=300, is_write_log=True): + try: + return notifyMessageTry(msg, stype, trigger_time, is_write_log) + except Exception as e: + writeFileLog(getTracebackInfo()) + return False + + +##################### notify end ######################################### # --------------------------------------------------------------------------------- # 打印相关 START diff --git a/web/core/orm.py b/web/core/orm.py index 89ba623e9..f5a166996 100755 --- a/web/core/orm.py +++ b/web/core/orm.py @@ -119,10 +119,17 @@ class ORM: print(e) return True + def find(self, sql): + d = self.query(sql) + if d is not None: + if len(d) > 0: + return d[0] + return None + def query(self, sql): # 执行SQL语句返回数据集 if not self.__Conn(): - return self.__DB_ERR + return None try: self.__DB_CUR.execute(sql) result = self.__DB_CUR.fetchall() @@ -132,7 +139,7 @@ class ORM: self.__Close() return result except Exception as ex: - return ex + return None def __Close(self): # 关闭连接 diff --git a/rewrite/nginx/EmpireCMS.conf b/web/misc/nginx/rewrite/EmpireCMS.conf similarity index 100% rename from rewrite/nginx/EmpireCMS.conf rename to web/misc/nginx/rewrite/EmpireCMS.conf diff --git a/rewrite/nginx/cloudfare_real_ip.conf b/web/misc/nginx/rewrite/cloudfare_real_ip.conf similarity index 100% rename from rewrite/nginx/cloudfare_real_ip.conf rename to web/misc/nginx/rewrite/cloudfare_real_ip.conf diff --git a/rewrite/nginx/dedecms.conf b/web/misc/nginx/rewrite/dedecms.conf similarity index 100% rename from rewrite/nginx/dedecms.conf rename to web/misc/nginx/rewrite/dedecms.conf diff --git a/rewrite/nginx/discuzx.conf b/web/misc/nginx/rewrite/discuzx.conf similarity index 100% rename from rewrite/nginx/discuzx.conf rename to web/misc/nginx/rewrite/discuzx.conf diff --git a/rewrite/nginx/discuzx2.conf b/web/misc/nginx/rewrite/discuzx2.conf similarity index 100% rename from rewrite/nginx/discuzx2.conf rename to web/misc/nginx/rewrite/discuzx2.conf diff --git a/rewrite/nginx/discuzx3.conf b/web/misc/nginx/rewrite/discuzx3.conf similarity index 100% rename from rewrite/nginx/discuzx3.conf rename to web/misc/nginx/rewrite/discuzx3.conf diff --git a/rewrite/nginx/drupal.conf b/web/misc/nginx/rewrite/drupal.conf similarity index 100% rename from rewrite/nginx/drupal.conf rename to web/misc/nginx/rewrite/drupal.conf diff --git a/rewrite/nginx/ecshop.conf b/web/misc/nginx/rewrite/ecshop.conf similarity index 100% rename from rewrite/nginx/ecshop.conf rename to web/misc/nginx/rewrite/ecshop.conf diff --git a/rewrite/nginx/emlog.conf b/web/misc/nginx/rewrite/emlog.conf similarity index 100% rename from rewrite/nginx/emlog.conf rename to web/misc/nginx/rewrite/emlog.conf diff --git a/rewrite/nginx/fastdfs.conf b/web/misc/nginx/rewrite/fastdfs.conf similarity index 100% rename from rewrite/nginx/fastdfs.conf rename to web/misc/nginx/rewrite/fastdfs.conf diff --git a/rewrite/nginx/gunicorn.conf b/web/misc/nginx/rewrite/gunicorn.conf similarity index 100% rename from rewrite/nginx/gunicorn.conf rename to web/misc/nginx/rewrite/gunicorn.conf diff --git a/rewrite/nginx/header_cors.conf b/web/misc/nginx/rewrite/header_cors.conf similarity index 100% rename from rewrite/nginx/header_cors.conf rename to web/misc/nginx/rewrite/header_cors.conf diff --git a/rewrite/nginx/laravel5.conf b/web/misc/nginx/rewrite/laravel5.conf similarity index 100% rename from rewrite/nginx/laravel5.conf rename to web/misc/nginx/rewrite/laravel5.conf diff --git a/rewrite/nginx/mvc.conf b/web/misc/nginx/rewrite/mvc.conf similarity index 100% rename from rewrite/nginx/mvc.conf rename to web/misc/nginx/rewrite/mvc.conf diff --git a/rewrite/nginx/nezha.conf b/web/misc/nginx/rewrite/nezha.conf similarity index 100% rename from rewrite/nginx/nezha.conf rename to web/misc/nginx/rewrite/nezha.conf diff --git a/rewrite/nginx/phpcms.conf b/web/misc/nginx/rewrite/phpcms.conf similarity index 100% rename from rewrite/nginx/phpcms.conf rename to web/misc/nginx/rewrite/phpcms.conf diff --git a/rewrite/nginx/phpwind.conf b/web/misc/nginx/rewrite/phpwind.conf similarity index 100% rename from rewrite/nginx/phpwind.conf rename to web/misc/nginx/rewrite/phpwind.conf diff --git a/rewrite/nginx/proxy.conf b/web/misc/nginx/rewrite/proxy.conf similarity index 100% rename from rewrite/nginx/proxy.conf rename to web/misc/nginx/rewrite/proxy.conf diff --git a/rewrite/nginx/proxydg.conf b/web/misc/nginx/rewrite/proxydg.conf similarity index 100% rename from rewrite/nginx/proxydg.conf rename to web/misc/nginx/rewrite/proxydg.conf diff --git a/rewrite/nginx/sablog.conf b/web/misc/nginx/rewrite/sablog.conf similarity index 100% rename from rewrite/nginx/sablog.conf rename to web/misc/nginx/rewrite/sablog.conf diff --git a/rewrite/nginx/seacms.conf b/web/misc/nginx/rewrite/seacms.conf similarity index 100% rename from rewrite/nginx/seacms.conf rename to web/misc/nginx/rewrite/seacms.conf diff --git a/rewrite/nginx/shopex.conf b/web/misc/nginx/rewrite/shopex.conf similarity index 100% rename from rewrite/nginx/shopex.conf rename to web/misc/nginx/rewrite/shopex.conf diff --git a/rewrite/nginx/sub.conf.tpl b/web/misc/nginx/rewrite/sub.conf.tpl similarity index 100% rename from rewrite/nginx/sub.conf.tpl rename to web/misc/nginx/rewrite/sub.conf.tpl diff --git a/rewrite/nginx/thinkphp.conf b/web/misc/nginx/rewrite/thinkphp.conf similarity index 100% rename from rewrite/nginx/thinkphp.conf rename to web/misc/nginx/rewrite/thinkphp.conf diff --git a/rewrite/nginx/typecho.conf b/web/misc/nginx/rewrite/typecho.conf similarity index 100% rename from rewrite/nginx/typecho.conf rename to web/misc/nginx/rewrite/typecho.conf diff --git a/rewrite/nginx/v2ray.conf b/web/misc/nginx/rewrite/v2ray.conf similarity index 100% rename from rewrite/nginx/v2ray.conf rename to web/misc/nginx/rewrite/v2ray.conf diff --git a/rewrite/nginx/walle.conf b/web/misc/nginx/rewrite/walle.conf similarity index 100% rename from rewrite/nginx/walle.conf rename to web/misc/nginx/rewrite/walle.conf diff --git a/rewrite/nginx/whmcs.conf b/web/misc/nginx/rewrite/whmcs.conf similarity index 100% rename from rewrite/nginx/whmcs.conf rename to web/misc/nginx/rewrite/whmcs.conf diff --git a/rewrite/nginx/wmcms.conf b/web/misc/nginx/rewrite/wmcms.conf similarity index 100% rename from rewrite/nginx/wmcms.conf rename to web/misc/nginx/rewrite/wmcms.conf diff --git a/rewrite/nginx/wordpress.conf b/web/misc/nginx/rewrite/wordpress.conf similarity index 100% rename from rewrite/nginx/wordpress.conf rename to web/misc/nginx/rewrite/wordpress.conf diff --git a/rewrite/nginx/zblog.conf b/web/misc/nginx/rewrite/zblog.conf similarity index 100% rename from rewrite/nginx/zblog.conf rename to web/misc/nginx/rewrite/zblog.conf diff --git a/data/tpl/nginx.conf b/web/misc/nginx/tpl/nginx.conf similarity index 87% rename from data/tpl/nginx.conf rename to web/misc/nginx/tpl/nginx.conf index 1e4655be1..5da306f9d 100755 --- a/data/tpl/nginx.conf +++ b/web/misc/nginx/tpl/nginx.conf @@ -28,7 +28,6 @@ server include {$OR_REWRITE}/{$SERVER_NAME}.conf; #REWRITE-END - #禁止访问的文件或目录 location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md) { @@ -39,12 +38,14 @@ server location ~ \.well-known{ allow all; } - - location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf|js|css|ttf|woff2)$ + + location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css|ttf|woff2)$ { - expires 30d; - error_log /dev/null; + expires 30d; access_log /dev/null; + if ($invalid_referer){ + return 404; + } } access_log {$LOGPATH}/{$SERVER_NAME}.log main; diff --git a/data/tpl/nginx_dirbind.conf b/web/misc/nginx/tpl/nginx_dirbind.conf similarity index 100% rename from data/tpl/nginx_dirbind.conf rename to web/misc/nginx/tpl/nginx_dirbind.conf diff --git a/data/tpl/nginx_panel.conf b/web/misc/nginx/tpl/nginx_panel.conf similarity index 100% rename from data/tpl/nginx_panel.conf rename to web/misc/nginx/tpl/nginx_panel.conf diff --git a/web/misc/test/api/mw_api.php b/web/misc/test/api/mw_api.php new file mode 100755 index 000000000..76f5db522 --- /dev/null +++ b/web/misc/test/api/mw_api.php @@ -0,0 +1,87 @@ +MW_PANEL = $mw_panel; + } + + if ($app_id) { + $this->MW_APP_ID = $app_id; + } + + if ($app_secret) { + $this->MW_APP_SERECT = $app_secret; + } + } + + /** + * 发起POST请求 + * @param String $url 目标网填,带http:// + * @param Array|String $data 欲提交的数据 + * @return string + */ + private function httpPost($url, $data, $timeout = 60) { + + $ch = curl_init(); + // 设置头部信息 + $headers = [ + 'app-id: ' . $this->MW_APP_ID, + 'app-secret: ' . $this->MW_APP_SERECT, + ]; + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file); + curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + $output = curl_exec($ch); + curl_close($ch); + return $output; + } + + public function panel($endpoint, $data) { + $url = $this->MW_PANEL . $endpoint; + return $this->httpPost($url, $data); + } + + //示例取面板日志 + public function getLogsList() { + $post_data['p'] = '1'; + $post_data['limit'] = 10; + + //请求面板接口 + $data = $this->panel('/logs/get_log_list', $post_data); + + //解析JSON数据 + // $data = json_decode($result, true); + return $data; + } + +} + +//实例化对象 +$api = new mwApi(); +//获取面板日志 +$rdata = $api->getLogsList(); + +// var_dump($rdata); +//输出JSON数据到浏览器 +echo json_encode($rdata); + +?> \ No newline at end of file diff --git a/web/misc/test/api/mw_api.py b/web/misc/test/api/mw_api.py new file mode 100755 index 000000000..b78a5e073 --- /dev/null +++ b/web/misc/test/api/mw_api.py @@ -0,0 +1,57 @@ +# coding: utf-8 +# +----------------------------------------------------------------------------------- +# | MW Linux面板 +# +----------------------------------------------------------------------------------- +# | Copyright (c) 2015-2099 MW(http://github.com/midoks/mdserver) All rights reserved. +# +----------------------------------------------------------------------------------- +# | Author: midoks +# +----------------------------------------------------------------------------------- + +#------------------------------ +# API-Demo of Python +#------------------------------ +import time +import hashlib +import sys +import os +import json + + +class mwApi: + __MW_PANEL = 'http://154.12.53.90:51377/' + __MW_APP_ID = 'yhYkxGssPD' + __MW_APP_SERECT = 'ErmBdr563eJ5GMM5sWbc' + + # 如果希望多台面板,可以在实例化对象时,将面板地址与密钥传入 + def __init__(self, panel_url=None, app_id=None, app_serect=None): + if panel_url: + self.__MW_PANEL = panel_url + self.__MW_APP_ID = app_id + self.__MW_APP_SERECT = app_serect + + def post(self, endpoint, request_data): + import requests + url = self.__MW_PANEL + endpoint + post_data = requests.post(url, data=request_data, headers={ + 'app-id':self.__MW_APP_ID, + 'app-secret':self.__MW_APP_SERECT + }) + try: + return post_data.json() + except Exception as e: + return post_data.text + # 取面板日志 + def getLogs(self): + result = self.post('/task/count',{'limit':10,'p':1}) + return result + + +if __name__ == '__main__': + # 实例化MW-API对象 + api = mwApi() + + # 调用get_logs方法 + rdata = api.getLogs() + + # 打印响应数据 + print(rdata) diff --git a/web/setting.py b/web/setting.py index 1d6961488..ae6b782ff 100755 --- a/web/setting.py +++ b/web/setting.py @@ -12,61 +12,79 @@ # 配置文件 # --------------------------------------------------------------------------------- - import time import sys import random import os +# 初始化db +from admin import setup +setup.init() import core.mw as mw - - import utils.system as system +import thisdb + cpu_info = system.getCpuInfo() workers = cpu_info[1] +# workers = 1 panel_dir = mw.getPanelDir() - - log_dir = mw.getMWLogs() if not os.path.exists(log_dir): os.mkdir(log_dir) +data_dir = panel_dir+'/data' +if not os.path.exists(data_dir): + os.mkdir(data_dir) + # default port -mw_port = '7200' -if os.path.exists(panel_dir+'/data/port.pl'): - mw_port = mw.readFile(panel_dir+'/data/port.pl') - mw_port.strip() -# else: -# import firewall_api -# import common -# common.initDB() -# mw_port = str(random.randint(10000, 65530)) -# firewall_api.firewall_api().addAcceptPortArgs(mw_port, 'WEB面板', 'port') -# mw.writeFile('data/port.pl', mw_port) +panel_port = '7200' +from utils.firewall import Firewall as MwFirewall +default_port_file = panel_dir+'/data/port.pl' +if os.path.exists(default_port_file): + panel_port = mw.readFile(default_port_file) + panel_port.strip() + MwFirewall.instance().addAcceptPort(panel_port,'PANEL端口', 'port') +else: + panel_port = str(random.randint(10000, 65530)) + MwFirewall.instance().addPanelPort(panel_port) + mw.writeFile(default_port_file, panel_port) bind = [] -if os.path.exists('data/ipv6.pl'): - bind.append('[0:0:0:0:0:0:0:0]:%s' % mw_port) +default_ipv6_file = panel_dir+'/data/ipv6.pl' +if os.path.exists(default_ipv6_file): + bind.append('[0:0:0:0:0:0:0:0]:%s' % panel_port) else: - bind.append('0.0.0.0:%s' % mw_port) + bind.append('0.0.0.0:%s' % panel_port) + +panel_ssl_data = thisdb.getOptionByJson('panel_ssl', default={'open':False}) +if panel_ssl_data['open']: + choose = panel_ssl_data['choose'] + if mw.inArray(['local','nginx'],choose): + panel_cert = panel_dir+'/ssl/'+choose+'/cert.pem' + panel_private = panel_dir+'/ssl/'+choose+'/private.pem' + if os.path.exists(panel_cert) and os.path.exists(panel_private): + certfile = panel_cert + keyfile = panel_private + ciphers = 'TLSv1 TLSv1.1 TLSv1.2 TLSv1.3' + ssl_version = 2 -print(mw_port) if workers > 2: workers = 2 -threads = workers * 1 +threads = workers * 2 backlog = 512 reload = False daemon = True -# worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' -timeout = 7200 +# # worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' +worker_class = 'eventlet' +timeout = 600 keepalive = 60 -preload_app = True +preload_app = False capture_output = True access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"' loglevel = 'info' errorlog = log_dir + '/panel_error.log' accesslog = log_dir + '/panel.log' -pidfile = log_dir + '/mw.pid' +pidfile = log_dir + '/panel.pid' diff --git a/web/static/app/config.js b/web/static/app/config.js index 066a16dbe..aad1c776e 100755 --- a/web/static/app/config.js +++ b/web/static/app/config.js @@ -268,13 +268,13 @@ function modifyAuthPath() { btn:['提交','关闭', '随机生成'], shadeClose: false, content: '
                \ -
                \ - 入口地址\ -
                \ - \ -
                \ -
                \ -
                ', +
                \ + 入口地址\ +
                \ + \ +
                \ +
                \ + ', yes:function(index){ var auth_path = $("input[name='auth_path_set']").val(); if (auth_path == '/' || auth_path == ''){ @@ -310,126 +310,125 @@ function modifyAuthPath() { }); } -function setPassword(a) { - if(a == 1) { - p1 = $("#p1").val(); - p2 = $("#p2").val(); - if(p1 == "" || p1.length < 8) { - layer.msg('面板密码不能少于8位!', {icon: 2}); - return - } - - //准备弱口令匹配元素 - var checks = ['admin888','123123123','12345678','45678910','87654321','asdfghjkl','password','qwerqwer']; - pchecks = 'abcdefghijklmnopqrstuvwxyz1234567890'; - for(var i=0;i\ + btn:["修改","关闭","随机"], + content: "
                \
                \ 密码\ -
                \ +
                \
                \
                \ 重复\ -
                \ +
                \
                \ -
                \ - 随机\ - \ - \ -
                \ -
                " + ", + yes:function(){ + var p1 = $("#p1").val(); + var p2 = $("#p2").val(); + if(p1 == "" || p1.length < 8) { + layer.msg('面板密码不能少于8位!', {icon: 2}); + return + } + + //准备弱口令匹配元素 + var checks = ['admin888','123123123','12345678','45678910','87654321','asdfghjkl','password','qwerqwer']; + pchecks = 'abcdefghijklmnopqrstuvwxyz1234567890'; + for(var i=0;i\ + btn:["确定","取消","随机"], + content: "
                \
                用户名\ -
                \ +
                \
                \
                \ 重复\ -
                \ +
                \
                \ -
                \ - \ - \ -
                \ -
                " + ", + yes: function(){ + p1 = $("#p1").val(); + p2 = $("#p2").val(); + if(p1 == "" || p1.length < 3) { + layer.msg('用户名长度不能少于3位', {icon: 2}); + return; + } + if(p1 != p2) { + layer.msg('两次输入的用户名不一致', {icon: 2}); + return; + } + $.post("/setting/set_name", "name1=" + encodeURIComponent(p1) + "&name2=" + encodeURIComponent(p2), function(b) { + if(b.status) { + layer.closeAll(); + layer.msg(b.msg, {icon: 1}); + $("input[name='username_']").val(p1) + } else { + layer.msg(b.msg, {icon: 2}); + } + },'json'); + return + }, + btn3:function(){ + var pwd = randomStrPwd(12); + $("#p1").val(pwd); + $("#p2").val(pwd); + layer.msg('请在修改前记录好您的用户名!',{time:2000}); + return false; + } }) } @@ -540,10 +539,19 @@ function setPanelSSL(){ }); } -function setNotifyApi(tag, obj){ +function setNotifyTgbot(obj){ var enable = $(obj).prop("checked"); - // console.log(tag,obj,enable); - $.post('/setting/set_notify_enable', {'tag':tag, 'enable':enable},function(rdata){ + $.post('/setting/set_notify_tgbot_enable', {'enable':enable},function(rdata){ + showMsg(rdata.msg, function(){ + if (rdata.status){} + } ,{icon:rdata.status?1:2}, 1000); + },'json'); +} + + +function setNotifyEmail(obj){ + var enable = $(obj).prop("checked"); + $.post('/setting/set_notify_email_enable', {'enable':enable},function(rdata){ showMsg(rdata.msg, function(){ if (rdata.status){} } ,{icon:rdata.status?1:2}, 1000); @@ -552,16 +560,16 @@ function setNotifyApi(tag, obj){ function getTgbot(){ var loadT = layer.msg('正在获取TgBot信息...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/setting/get_notify',{},function(data){ + $.post('/setting/get_notify_tgbot',{},function(data){ layer.close(loadT); var app_token = ''; var chat_id = ''; if (data.status){ - if (typeof(data['data']['tgbot']) !='undefined'){ - app_token = data['data']['tgbot']['data']['app_token']; - chat_id = data['data']['tgbot']['data']['chat_id']; + if (data['data']['tgbot'].length != 0){ + app_token = data['data']['tgbot']['app_token']; + chat_id = data['data']['tgbot']['chat_id']; } } @@ -598,7 +606,7 @@ function getTgbot(){ return false; } - $.post('/setting/set_notify',{'tag':'tgbot', 'data':JSON.stringify(pdata)},function(rdata){ + $.post('/setting/set_notify_tgbot',{'tag':'tgbot', 'data':JSON.stringify(pdata)},function(rdata){ showMsg(rdata.msg, function(){ if (rdata.status){ layer.close(index); @@ -622,7 +630,7 @@ function getTgbot(){ return false; } - $.post('/setting/set_notify_test',{'tag':'tgbot', 'data':JSON.stringify(pdata)},function(rdata){ + $.post('/setting/set_notify_tgbot_test',{'tag':'tgbot', 'data':JSON.stringify(pdata)},function(rdata){ showMsg(rdata.msg, function(){ if (rdata.status){ layer.close(index); @@ -637,7 +645,7 @@ function getTgbot(){ function getEmailCfg(){ var loadT = layer.msg('正在获取邮件配置信息...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/setting/get_notify',{},function(data){ + $.post('/setting/get_notify_email',{},function(data){ layer.close(loadT); var smtp_host = 'smtp.163.com'; @@ -651,14 +659,13 @@ function getEmailCfg(){ if (data.status){ if (typeof(data['data']['email']) !='undefined'){ - smtp_host = data['data']['email']['data']['smtp_host']; - smtp_port = data['data']['email']['data']['smtp_port']; - username = data['data']['email']['data']['username']; - password = data['data']['email']['data']['password']; - to_mail_addr = data['data']['email']['data']['to_mail_addr']; + smtp_host = data['data']['email']['smtp_host']; + smtp_port = data['data']['email']['smtp_port']; + username = data['data']['email']['username']; + password = data['data']['email']['password']; + to_mail_addr = data['data']['email']['to_mail_addr']; - - var smtp_ssl = data['data']['email']['data']['smtp_ssl']; + var smtp_ssl = data['data']['email']['smtp_ssl']; if (smtp_ssl == 'ssl'){ smtp_ssl_no = ''; smtp_ssl_yes = 'checked'; @@ -742,13 +749,13 @@ function getEmailCfg(){ return false; } - $.post('/config/set_notify',{'tag':'email', 'data':JSON.stringify(pdata)},function(rdata){ + $.post('/setting/set_notify_email',{'tag':'email', 'data':JSON.stringify(pdata)},function(rdata){ showMsg(rdata.msg, function(){ if (rdata.status){ layer.close(index); } },{icon:rdata.status?1:2},2000); - }); + },'json'); }, btn3:function(index){ @@ -791,17 +798,17 @@ function getEmailCfg(){ layer.msg('验证测试不能为空!', {icon:2}); return false; } - $.post('/config/set_notify_test',{'tag':'email', 'data':JSON.stringify(pdata)},function(rdata){ + $.post('/setting/set_notify_email_test',{'tag':'email', 'data':JSON.stringify(pdata)},function(rdata){ showMsg(rdata.msg, function(){ if (rdata.status){ layer.close(index); } },{icon:rdata.status?1:2},2000); - }); + },'json'); return false; } }); - }); + },'json'); } function getPanelSSL(){ @@ -898,7 +905,7 @@ function getPanelSSL(){ btn3:function(){ data['choose'] = 'nginx'; var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/save_panel_ssl',data,function(rdata){ + $.post('/setting/save_panel_ssl',data,function(rdata){ layer.close(loadT); if(rdata.status){ layer.closeAll(); @@ -910,7 +917,7 @@ function getPanelSSL(){ function(index) { data['choose'] = 'local'; var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/save_panel_ssl',data,function(rdata){ + $.post('/setting/save_panel_ssl',data,function(rdata){ layer.close(loadT); if(rdata.status){ layer.closeAll(); @@ -935,7 +942,7 @@ function getPanelSSL(){ var data = {}; data['choose'] = 'nginx'; var loadT = layer.msg('正在删除面板SSL【nginx】...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('/config/del_panel_ssl',data,function(rdata){ + $.post('/setting/del_panel_ssl',data,function(rdata){ layer.close(loadT); if(rdata.status){ layer.closeAll(); @@ -1236,13 +1243,13 @@ function setTempAccess(){ }); } -//二次验证 +//二步验证 function setAuthBind(){ $.post('/setting/get_auth_secret', {}, function(rdata){ console.log(rdata); var tip = layer.open({ area: ['500px', '355px'], - title: '二次验证设置', + title: '二步验证设置', closeBtn:1, shift: 0, type: 1, @@ -1457,11 +1464,169 @@ function showPanelApi(){ function setPanelApi(){ var cfg_panel_api = $('#cfg_panel_api').prop("checked"); - $.post('/setting/set_panel_token', {'op_type':"2"},function(rdata){ + $.post('/setting/set_panel_api', {},function(rdata){ showMsg(rdata.msg, function(){ if (rdata.status){ - showPanelApi(); + addApp(); } } ,{icon:rdata.status?1:2}, 1000); },'json'); } + + +function deleteApp(id){ + layer.confirm('您确定要删除吗?',{title:'删除应用',closeBtn:2,icon:13,cancel:function(){}}, function() { + $.post('/setting/delete_app', {'id':id},function(rdata){ + layer.msg(rdata.msg,{icon:rdata.status?1:2}); + if (rdata.status){ + getAppList(); + } + },'json'); + }); +} + +function toggleAppstatus(id){ + $.post('/setting/toggle_app_status', {id:id}, function(rdata){ + showMsg(rdata.msg, function(){ + if (rdata.status){ + getAppList(); + } + } ,{icon:rdata.status?1:2}, 2000); + },'json'); +} + +function getAppList(page) { + + if (typeof(page) == 'undefined'){ + page = 1; + } + + $.post('/setting/get_app_list', {page:page}, function(rdata){ + var tbody = ''; + for (var i = 0; i < rdata.data.length; i++) { + var row = rdata.data[i]; + + tbody += ''; + + tbody += '' + row['app_id'] +''; + tbody += '' + row['app_secret'] +''; + tbody += '' + row['white_list'] +''; + + if (row['status'] == 1){ + tbody += '已开启'; + } else { + tbody += '已关闭'; + } + + tbody += '' + row['add_time'] +''; + + tbody += ''; + tbody += '删除'; + tbody += ''; + + tbody += ''; + } + + $('#app_list_body tbody').html(tbody); + $('#app_list_body .page').html(rdata.page); + },'json'); +} + +function addApp(){ + layer.open({ + area: '570px', + title: '添加应用', + shift: 0, + type: 1, + content: '
                \ +
                \ + 应用ID\ +
                \ + \ + \ +
                \ +
                \ +
                \ + 应用密钥\ +
                \ + \ + \ +
                \ +
                \ +
                \ + IP白名单
                (每行1个)
                \ +
                \ +
                \ +
                \ + \ +
                \ +
                \ +
                  \ +
                • 开启API后,必需在IP白名单列表中的IP才能访问面板API接口
                • \ +
                • 请谨慎在生产环境开启,这可能增加服务器安全风险;
                • \ +
                \ +
                ', + success:function(obj,cur_layer){ + $('input[name="app_id"]').val(getRandomString(10)); + $('input[name="app_secret"]').val(getRandomString(20)); + + $('.app_id').click(function(){ + $('input[name="app_id"]').val(getRandomString(10)); + }); + + $('.app_secret').click(function(){ + $('input[name="app_secret"]').val(getRandomString(20)); + }); + + $('.save_app_data').click(function(){ + var app_id = $('input[name="app_id"]').val(); + var app_secret = $('input[name="app_secret"]').val(); + var limit_addr = $('textarea[name="api_limit_addr"]').val(); + $.post('/setting/add_app', {'app_id':app_id,'app_secret':app_secret,'limit_addr':limit_addr},function(rdata){ + showMsg(rdata.msg, function(){ + if (rdata.status){ + getAppList(); + layer.close(cur_layer); + } + } ,{icon:rdata.status?1:2}, 2000); + },'json'); + }); + + } + }); +} + +function appPage(){ + layer.open({ + area: ['900px', '380px'], + title: 'APP应用管理', + closeBtn:1, + shift: 0, + type: 1, + content: "", + success:function(){ + getAppList(); + $('.app_add').click(function(){ + addApp(); + }); + } + }); +} diff --git a/web/static/app/control.js b/web/static/app/control.js index 4b68cd96b..59e499333 100755 --- a/web/static/app/control.js +++ b/web/static/app/control.js @@ -240,6 +240,7 @@ function getBeforeDate(n){ //cpu function cpu(b,e){ $.get('/system/get_cpu_io?start='+b+'&end='+e,function(rdata){ + var rdata = rdata.data; var myChartCpu = echarts.init(document.getElementById('cupview')); var xData = []; var yData = []; @@ -311,6 +312,7 @@ function cpu(b,e){ //内存 function mem(b,e){ $.get('/system/get_cpu_io?start='+b+'&end='+e,function(rdata){ + var rdata = rdata.data; var myChartMen = echarts.init(document.getElementById('memview')); var xData = []; //var yData = []; @@ -382,6 +384,7 @@ function mem(b,e){ //磁盘io function disk(b,e){ $.get('/system/get_disk_io?start='+b+'&end='+e,function(rdata){ + var rdata = rdata.data; var myChartDisk = echarts.init(document.getElementById('diskview')); var rData = []; var wData = []; @@ -469,6 +472,7 @@ function disk(b,e){ //网络Io function network(b,e){ $.get('/system/get_network_io?start='+b+'&end='+e,function(rdata){ + var rdata = rdata.data; var myChartNetwork = echarts.init(document.getElementById('network')); var aData = []; var bData = []; @@ -490,7 +494,8 @@ function network(b,e){ option = { tooltip: { trigger: 'axis', - axisPointer: { type: 'cross' } + axisPointer: { type: 'cross' }, + formatter:"时间:{b0}
                {a0}: {c0} Kb/s
                {a1}: {c1} Kb/s", }, legend: { data:[lan.index.net_up,lan.index.net_down] @@ -503,7 +508,7 @@ function network(b,e){ }, yAxis: { type: 'value', - name: lan.index.unit+':KB/s', + name: '单位:KB/s', boundaryGap: [0, '100%'], splitLine:{ lineStyle:{ color:"#ddd" } }, axisLine:{ lineStyle:{ color:"#666" } } @@ -556,6 +561,7 @@ function network(b,e){ //负载 function getload_old(b,e){ $.get('/system/get_load_average?start='+b+'&end='+e,function(rdata){ + var rdata = data.data; var myChartgetload = echarts.init(document.getElementById('getloadview')); var aData = []; var bData = []; @@ -686,6 +692,7 @@ function getload_old(b,e){ //系统负载 function getload(b,e){ $.get('/system/get_load_average?start='+b+'&end='+e,function(rdata){ + var rdata = rdata.data; var myChartgetload = echarts.init(document.getElementById('getloadview')); var aData = []; var bData = []; diff --git a/web/static/app/crontab.js b/web/static/app/crontab.js index 1d6302e1f..0ba5debd4 100755 --- a/web/static/app/crontab.js +++ b/web/static/app/crontab.js @@ -85,8 +85,8 @@ function getCronData(page){ $.post("/crontab/list?p="+page,'', function(rdata){ layer.close(load); var cbody = ""; - if(rdata == ""){ - cbody=""+lan.crontab.task_empty+""; + if(rdata.data.length == 0){ + cbody="当前没有计划任务"; } else { for(var i=0;i"+rdata.data[i].cycle+"\ "+cron_save +"\ "+cron_backupto+"\ - "+rdata.data[i].addtime+"\ + "+rdata.data[i].add_time+"\ \ 执行 | \ 编辑 | \ @@ -266,11 +266,11 @@ function planAdd(){ $("#cronConfig input[name='week']").val($(".planweek").find("b").attr("val")); var sType = $(".planjs").find("b").attr("val"); - var sBody = encodeURIComponent($("#implement textarea[name='sBody']").val()); + var sBody = encodeURIComponent($("#implement textarea[name='sbody']").val()); if (sType == 'toShell'){ if(sBody == ''){ - $("#implement textarea[name='sBody']").focus(); + $("#implement textarea[name='sbody']").focus(); layer.msg('脚本代码不能为空!',{icon:2}); return; } @@ -293,16 +293,17 @@ function planAdd(){ } // urladdress = encodeURIComponent(urladdress); $("#cronConfig input[name='urladdress']").val(urladdress); - $("#cronConfig input[name='sType']").val(sType); - $("#cronConfig textarea[name='sBody']").val(decodeURIComponent(sBody)); + $("#cronConfig input[name='stype']").val(sType); + $("#cronConfig textarea[name='sbody']").val(decodeURIComponent(sBody)); - if(sType == 'site' || sType == 'database' || sType == 'path'){ + if(sType == 'site' || sType == 'database' || sType.indexOf('database_')>-1 || sType == 'path'){ var backupTo = $(".planBackupTo").find("b").attr("val"); - $("#backupTo").val(backupTo); + $("#backup_to").val(backupTo); } var sName = $("#sName").attr("val"); - + $("#cronConfig input[name='sname']").val(sName); + // if(sName == 'backupAll'){ // var alist = $("ul[aria-labelledby='backdata'] li a"); // var dataList = new Array(); @@ -338,10 +339,9 @@ function planAdd(){ $("#cronConfig input[name='where1']").val(where1); } - - $("#cronConfig input[name='sName']").val(sName); + layer.msg('正在添加,请稍候...!',{icon:16,time:0,shade: [0.3, '#000']}); - var data = $("#cronConfig").serialize() + '&sBody='+sBody + '&urladdress=' + urladdress; + var data = $("#cronConfig").serialize() + '&sbody='+sBody; // console.log(data); $.post('/crontab/add',data,function(rdata){ if(!rdata.status) { @@ -357,51 +357,6 @@ function planAdd(){ },'json'); } -//批量添加任务 -// function allAddCrontab(dataList,successCount,errorMsg){ -// if(dataList.length < 1) { -// layer.msg(lan.get('add_all_task_ok',[successCount]),{icon:1}); -// return; -// } -// var loadT = layer.msg(lan.get('add',[dataList[0]]),{icon:16,time:0,shade: [0.3, '#000']}); -// var sType = $(".planjs").find("b").attr("val"); -// var minute = parseInt($("#cronConfig input[name='minute']").val()); -// var hour = parseInt($("#cronConfig input[name='hour']").val()); -// var sTitle = (sType == 'site')?lan.crontab.backup_site:lan.crontab.backup_database; -// if(sType == 'logs') sTitle = lan.crontab.backup_log; -// minute += 5; -// if(hour !== '' && minute > 59){ -// if(hour >= 23) hour = 0; -// $("#cronConfig input[name='hour']").val(hour+1); -// minute = 5; -// } -// $("#cronConfig input[name='minute']").val(minute); -// $("#cronConfig input[name='name']").val(sTitle + '['+dataList[0]+']'); -// $("#cronConfig input[name='sName']").val(dataList[0]); -// var pdata = $("#cronConfig").serialize() + '&sBody=&urladdress='; -// $.ajax({ -// type:'POST', -// url:'/crontab/add', -// data:pdata, -// async: true, -// success:function(frdata){ -// layer.close(loadT); -// if(frdata.status){ -// successCount++; -// getCronData(1); -// }else{ -// if(!errorMsg){ -// errorMsg = '

                '+lan.crontab.backup_all_err+'

                '; -// } -// errorMsg += '
              • '+dataList[0]+' -> '+frdata.msg+'
              • ' -// } - -// dataList.splice(0,1); -// allAddCrontab(dataList,successCount,errorMsg); -// } -// }); -// } - initDropdownMenu(); function initDropdownMenu(){ $(".dropdown ul li a").click(function(){ @@ -647,31 +602,31 @@ function toBackup(type){ } var sBody = '\ - '+ changeDir +'\ -
                备份到
                \ - \ -
                保留最新
                \ - \ - 份\ -
                '; + \ + \ + \ + '+ changeDir +'\ +
                备份到
                \ + \ +
                保留最新
                \ + \ + 份\ +
                '; $("#implement").html(sBody); getselectname(); $('.changePathDir').click(function(){ - changePathCallback($('#sName').val(),function(select_dir){ + changePathCallback($('#sname').val(),function(select_dir){ $(".planname input[name='name']").val('备份目录['+select_dir+']'); $('#implement .sname b').attr('val',select_dir).text(select_dir); }); @@ -679,7 +634,7 @@ function toBackup(type){ $(".dropdown ul li a").click(function(){ - var sName = $("#sName").attr("val"); + var sName = $("#sname").attr("val"); if(!sName) return; $(".planname input[name='name']").val(sMsg+'['+sName+']'); }); @@ -781,85 +736,85 @@ function editTaskInfo(id){ shadeClose:false, closeBtn:1, content:'
                \ -
                \ - 任务类型\ - \ +
                \ + 任务类型\ + \ +
                \ +
                \ + 任务名称\ +
                \ +
                \ +
                \ + 执行周期\ + \ +
                \ + \ -
                \ - 任务名称\ -
                \ +
                日
                \ +
                时
                \ +
                分
                \ +
                \ +
                \ + \ +
                \ + 脚本内容\ +
                \ +
                \ +
                \ + 提示\ +
                释放PHP、MYSQL、PURE-FTPD、OpenResty的内存占用,建议在每天半夜执行!
                \ +
                \ +
                \ + URL地址\ +
                \ +
                \ +
                \ +
                保存编辑
                \ +
                \ +
                ', success:function(){ @@ -1069,26 +1024,26 @@ function toWeek(){ //指定1 function toWhere1(ix){ var mBody ='
                \ - \ - '+ix+'\ -
                '; + \ + '+ix+'\ +
                '; $("#ptime").append(mBody); } //小时 function toHour(){ var mBody = '
                \ - \ - 小时\ -
                '; + \ + 小时\ +
                '; $("#ptime").append(mBody); } //分钟 function toMinute(){ var mBody = '
                \ - \ - 分钟\ -
                '; + \ + 分钟\ + '; $("#ptime").append(mBody); } @@ -1096,14 +1051,14 @@ function toMinute(){ //从文件 function toFile(){ var tBody = '\ - '; + '; $("#implement").html(tBody); $(".planname input[name='name']").removeAttr('readonly style').val(""); } //从脚本 function toShell(){ - var tBody = ""; + var tBody = ""; $("#implement").html(tBody); $(".planname input[name='name']").removeAttr('readonly style').val(""); } @@ -1118,8 +1073,8 @@ function toUrl(){ //释放内存 function rememory(){ $(".planname input[name='name']").removeAttr('readonly style').val(""); - $(".planname input[name='name']").val(lan.crontab.mem); - $("#implement").html(lan.crontab.mem_ps); + $(".planname input[name='name']").val('释放内存'); + $("#implement").html('释放PHP、MYSQL、PURE-FTPD、APACHE、NGINX的内存占用,建议在每天半夜执行!'); return; } //上传 diff --git a/web/static/app/files.js b/web/static/app/files.js index 5b0a5aec1..75f1f79ab 100755 --- a/web/static/app/files.js +++ b/web/static/app/files.js @@ -1,15 +1,8 @@ -// console.log(lan); - //判断磁盘数量超出宽度 function isDiskWidth(){ - var comlistWidth = $("#comlist").width(); - var bodyWidth = $(".file-box").width(); - if(comlistWidth + 530 > bodyWidth){ - $("#comlist").css({"width":bodyWidth-530+"px","height":"34px","overflow":"auto"}); - } - else{ - $("#comlist").removeAttr("style"); - } + var comlist_width = $("#comlist").width(); + var body_width = $(".file-box").width(); + $("#comlist").css({"width":(body_width-520)+"px","height":"34px","overflow":"auto"}); } //打开回收站 @@ -227,7 +220,9 @@ function getFileName(name){ function reisImage(fileName){ var exts = ['jpg','jpeg','png','bmp','gif','tiff','ico']; for(var i=0; i\  " + (rdata[i].path=='/'?lan.files.path_root:rdata[i].path) + "(" + rdata[i].size[2] + ")"; @@ -1056,7 +1052,9 @@ function createDir(type, path) { ', success:function(){ $("#newDirName").focus().keyup(function(e){ - if(e.keyCode == 13) $("#createDirBtn").click(); + if(e.keyCode == 13) { + $("#createDirBtn").click(); + } }); } }); diff --git a/web/static/app/firewall.js b/web/static/app/firewall.js index 07f7230f1..cb5ad5fa6 100755 --- a/web/static/app/firewall.js +++ b/web/static/app/firewall.js @@ -49,48 +49,56 @@ $("#firewalldType").change(function(){ function sshMgr(){ - $.post('/firewall/get_ssh_info', '', function(rdata){ var ssh_status = rdata.status ? 'checked':''; var pass_prohibit_status = rdata.pass_prohibit_status ? 'checked':''; + var pubkey_prohibit_status = rdata.pubkey_prohibit_status ? 'checked':''; var con = '
                \ -
                \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
                名称状态
                启动SSH\ -
                \ - \ - \ -
                \ -
                禁止密码登陆\ -
                \ - \ - \ -
                \ -
                \ -
                \ -
                '; +
                \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
                名称状态
                启动SSH\ +
                \ + \ + \ +
                \ +
                禁止密码登陆\ +
                \ + \ + \ +
                \ +
                禁止密钥登陆\ +
                \ + \ + \ +
                \ +
                \ +
                \ + '; layer.open({ type: 1, title: "SSH管理", - area: ['300px', '230px'], + area: ['300px', '260px'], closeBtn: 1, shadeClose: false, content: '
                '+con+'
                ', success:function(){ }, }); - },'json'); } @@ -273,6 +281,37 @@ function setSshPassStatus(){ }); } +/** + * 设置远程服务状态 + * @param {Int} state 0.启用 1.关闭 + */ +function setSshPubkeyStatus(){ + status = $("#pubkey_status").prop("checked")==true?1:0; + var msg = status==1?'开启密码登陆,继续吗?':'确定禁止密码登陆吗?'; + layer.confirm(msg,{title:'警告',closeBtn:2,cancel:function(){ + if(status == 0){ + $("#pubkey_status").prop("checked",false); + } else { + $("#pubkey_status").prop("checked",true); + } + }},function(index){ + if(index > 0){ + layer.msg('正在处理,请稍候...',{icon:16,time:20000}); + $.post('/firewall/set_ssh_pubkey_status','status='+status,function(rdata){ + layer.msg(rdata.msg,{icon:rdata.status?1:2}); + },'json'); + } + },function(){ + if(status == 0){ + $("#pubkey_status").prop("checked",false); + } else { + $("#pubkey_status").prop("checked",true); + } + }); +} + + + /** * 取回数据 * @param {Int} page 分页号 @@ -297,23 +336,22 @@ function showAccept(page,search) { break; } body += "\ - " + data.data[i].id + "\ - " + data.data[i].protocol + "\ - " + (data.data[i].port.indexOf('.') == -1?'放行端口'+':['+data.data[i].port+']':'屏蔽IP'+':['+data.data[i].port+']') + "\ - " + status + "\ - " + data.data[i].addtime + "\ - " + data.data[i].ps + "\ - 删除\ - "; + " + data.data[i].id + "\ + " + data.data[i].protocol + "\ + " + (data.data[i].port.indexOf('.') == -1?'放行端口'+':['+data.data[i].port+']':'屏蔽IP'+':['+data.data[i].port+']') + "\ + " + status + "\ + " + data.data[i].ps + "\ + " + data.data[i].add_time + "\ + 删除\ + "; } if (data.data.length == 0){ body = '当前没有数据'; } - - $("#firewallBody").html(body); - $("#firewallPage").html(data.page); + $("#firewall_body").html(body); + $("#firewall_page").html(data.page); },'json'); } diff --git a/web/static/app/index.js b/web/static/app/index.js index c623ad35e..309d983cb 100755 --- a/web/static/app/index.js +++ b/web/static/app/index.js @@ -117,6 +117,7 @@ function getPercent(num, total) { function getDiskInfo() { $.get('/system/disk_info', function(rdata) { + var rdata = rdata.data; var dBody; for (var i = 0; i < rdata.length; i++) { var LoadColor = setcolor(parseInt(rdata[i].size[3].replace('%', '')), false, 75, 90, 95); diff --git a/web/static/app/logs.js b/web/static/app/logs.js index 18f46c210..24590f2e2 100644 --- a/web/static/app/logs.js +++ b/web/static/app/logs.js @@ -58,7 +58,8 @@ $('#panelLogs .clear').click(function(){ function getAuditLogsFiles(){ var loadT = layer.msg('正在获取日志审计列表...', { icon: 16, time: 0, shade: 0.3 }); - $.post('/logs/get_audit_logs_files',{}, function(data) { + $.post('/logs/get_audit_logs_files',{}, function(rdata) { + var data = rdata.data; layer.close(loadT); var option = ''; for (var i = 0; i < data.length; i++) { diff --git a/web/static/app/public.js b/web/static/app/public.js index cb8eaae70..d2e028997 100755 --- a/web/static/app/public.js +++ b/web/static/app/public.js @@ -259,7 +259,6 @@ function getFormatTime(tm, format) { function changePathCallback(default_dir, callback) { - var c = layer.open({ type: 1, area: "650px", @@ -268,40 +267,40 @@ function changePathCallback(default_dir, callback) { shift: 5, shadeClose: false, content: "
                \ -
                \ - \ -
                当前路径:
                \ -
                \ -
                \ -
                \ -
                计算机
                \ -
                \ -
                \ -
                  \ -
                  \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
                  文件名修改时间权限所有者
                  \ -
                  \ -
                  \ +
                  \ + \ +
                  当前路径:
                  \ +
                  \ +
                  \ +
                  \ +
                  计算机
                  \ +
                  \ +
                  \ +
                    \ +
                    \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
                    文件名修改时间权限所有者
                    \
                    \
                    \ -
                    \ - \ - \ - \ -
                    ", +
                    \ +
                    \ +
                    \ + \ + \ + \ +
                    ", success:function(layero,layer_index){ $('.btn-close').click(function(){ layer.close(layer_index); @@ -333,38 +332,37 @@ function changePath(d) { closeBtn: 1, shift: 5, shadeClose: false, - content: "\ -
                    \ -
                    \ - \ -
                    当前路径:
                    \ -
                    \ -
                    \ -
                    计算机
                    \ -
                    \ -
                      \ -
                      \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
                      文件名修改时间权限所有者
                      \ -
                      \ + content: "
                      \ +
                      \ + \ +
                      当前路径:
                      \ +
                      \ +
                      \ +
                      计算机
                      \ +
                      \ +
                        \ +
                        \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
                        文件名修改时间权限所有者
                        \
                        \
                        \
                        \ -
                        \ - \ - \ - \ -
                        " +
                        \ +
                        \ + \ + \ + \ +
                        " }); setCookie("changePath", c); var b = $("#" + d).val(); @@ -387,14 +385,15 @@ function getDiskList(b) { var a = ""; var c = "path=" + b + "&disk=True"; $.post("/files/get_dir", c, function(h) { - if(h.DISK != undefined) { - for(var f = 0; f < h.DISK.length; f++) { - a += "
                         " + h.DISK[f].path + "
                        " - } - $("#changecomlist").html(a) - } - for(var f = 0; f < h.DIR.length; f++) { - var g = h.DIR[f].split(";"); + // console.log(h); + // if(h.dir != undefined) { + // for(var f = 0; f < h.dir.length; f++) { + // a += "
                         " + h.dir[f].path + "
                        "; + // } + // $("#changecomlist").html(a); + // } + for(var f = 0; f < h.dir.length; f++) { + var g = h.dir[f].split(";"); var e = g[0]; if(isChineseChar(e)) { @@ -408,16 +407,16 @@ function getDiskList(b) { } d += "\ - \ + \ " + e + "" + getLocalTime(g[2]) + "\ " + g[3] + "\ " + g[4] + "\ - X\ + X\ "; } - if(h.FILES != null && h.FILES != "") { - for(var f = 0; f < h.FILES.length; f++) { - var g = h.FILES[f].split(";"); + if(h.files != null && h.files != "") { + for(var f = 0; f < h.files.length; f++) { + var g = h.files[f].split(";"); var e = g[0]; if(isChineseChar(e)) { if(e.length > 10) { @@ -441,10 +440,10 @@ function getDiskList(b) { $(".default").hide(); $(".file-list").show(); $("#tbody").html(d); - if(h.PATH.substr(h.PATH.length - 1, 1) != "/") { - h.PATH += "/"; + if(h.path.substr(h.path.length - 1, 1) != "/") { + h.path += "/"; } - $("#PathPlace").find("span").html(h.PATH); + $("#PathPlace").find("span").html(h.path); activeDisk(); return; },'json'); @@ -485,7 +484,7 @@ function createFolder() { }) } -function NewDelFile(c) { +function newDelFile(c) { var a = $("#PathPlace").find("span").text(); newTxt = c.replace(new RegExp(/(\/\/)/g), "/"); var b = "path=" + newTxt + "&empty=True"; @@ -530,10 +529,11 @@ function activeDisk() { } function backMyComputer() { - $(".default").show(); - $(".file-list").hide(); - $("#PathPlace").find("span").html(""); - activeDisk(); + // $(".default").show(); + // $(".file-list").hide(); + // $("#PathPlace").find("span").html(""); + // activeDisk(); + return; } function backFile() { @@ -921,27 +921,13 @@ function task() { messageBox(); } -function ActionTask() { - var a = layer.msg(lan.public.the_del, { - icon: 16, - time: 0, - shade: [0.3, "#000"] - }); - $.post("/files?action=ActionTask", "", function(b) { - layer.close(a); - layer.msg(b.msg, { - icon: b.status ? 1 : 5 - }) - }) -} - function removeTask(b) { var a = layer.msg('正在删除,请稍候...', { icon: 16, time: 0, shade: [0.3, "#000"] }); - $.post("/files/remove_task", "id=" + b, function(c) { + $.post("/task/remove_task", "id=" + b, function(c) { layer.close(a); layer.msg(c.msg, { icon: c.status ? 1 : 5 @@ -951,59 +937,11 @@ function removeTask(b) { }); } -function GetTaskList(a) { - a = a == undefined ? 1 : a; - $.post("/task/list", "tojs=GetTaskList&table=tasks&limit=10&p=" + a, function(g) { - console.log(g); - var e = ""; - var b = ""; - var c = ""; - var f = false; - for(var d = 0; d < g.data.length; d++) { - switch(g.data[d].status) { - case "-1": - f = true; - if(g.data[d].type != "download") { - b = "
                      • \ - " + g.data[d].name + "\ - 正在安装 | 关闭\ -
                        \
                        -							
                      • "; - } else { - b = "
                      • \ -
                        \ - " + g.data[d].name + "0.0M/12.5M\ - 0%\ - 下载中 | "+lan.public.close+"\ - \ -
                      • "; - } - break; - case "0": - c += "
                      • \ - " + g.data[d].name + "\ - 等待 | 删除\ -
                      • "; - break; - case "1": - e += "
                      • \ - " + g.data[d].name + "\ - " + g.data[d].addtime + " "+'已完成'+" "+ '耗时' + (g.data[d].end - g.data[d].start)+"秒\ -
                      • "; - } - } - $("#srunning").html(b + c); - $("#sbody").html(e); - return f - }) -} - - //获取任务总数 function getTaskCount() { - $.get("/task/count", "", function(a) { - $(".task").text(a); - }); + $.get("/task/count", '', function(data) { + $(".task").text(data.data); + },'json'); } getTaskCount(); setInterval(function(){ @@ -1137,9 +1075,15 @@ function listOrder(skey,type,obj){ //获取关联列表 function getPanelList(){ var con =''; - $.post("/config/get_panel_list",function(rdata){ + $.post("/setting/get_panel_list",function(rdata){ + if (!rdata.status){ + return; + } + + var rdata = rdata.data; + for(var i=0; i\ + con +='

                        \ '+rdata[i].title+'\ \

                        '; @@ -1151,34 +1095,54 @@ function getPanelList(){ },function(){ $(this).css("opacity",".6"); }).click(function(){ - $("#btpanelform").remove(); + // $("#panel_form").remove(); var murl = $(this).attr("data-url"); var user = $(this).attr("data-user"); var pw = $(this).attr("data-pw"); - layer.open({ - type: 2, - title: false, - closeBtn: 0, //不显示关闭按钮 - shade: [0], - area: ['340px', '215px'], - offset: 'rb', //右下角弹出 - time: 5, //2秒后自动关闭 - anim: 2, - content: [murl+'/login', 'no'] - }); - var loginForm =''; - $("body").append(loginForm); - layer.msg('正在打开面板...',{icon:16,shade: [0.3, '#000'],time:1000}); - setTimeout(function(){ - $("#toBtpanel").submit(); - },500); - setTimeout(function(){ - window.open(murl); - },1000); + + var random_str = getRandomString(8); + + var timestamp = Date.parse(new Date()); + var data = { + 'rand':random_str, + 'username':user, + 'password':pw, + 'time':timestamp + }; + + + data_json =JSON.stringify(data); + login_args = base64_encode(data_json); + + endpoint_url = murl + '?login='+login_args; + window.open(endpoint_url); + // layer.open({ + // type: 2, + // title: false, + // closeBtn: 0, //不显示关闭按钮 + // shade: [0], + // area: ['340px', '215px'], + // offset: 'rb', //右下角弹出 + // time: 5, //2秒后自动关闭 + // anim: 2, + // content: [murl, 'no'] + // }); + // window.open(murl); + // + // var loginForm =''; + // $("body").append(loginForm); + // // console.log($("panel_form").html()); + // layer.msg('正在打开面板...',{icon:16,shade: [0.3, '#000'],time:1000}); + // setTimeout(function(){ + // $("#toBtpanel").submit(); + // },1000); + // setTimeout(function(){ + // window.open(murl); + // },2000); }); $(".btedit").click(function(e){ e.stopPropagation(); @@ -1202,7 +1166,7 @@ function bindPanel(a,type,ip,btid,url,user,pw){ if(pw == undefined) pw=""; if(ip == undefined) ip=""; if(a == 1) { - var gurl = "/config/add_panel_info"; + var gurl = "/setting/add_panel_info"; var btaddress = $("#btaddress").val(); if(!btaddress.match(/^(http|https)+:\/\/([\w-]+\.)+[\w-]+:\d+/)){ layer.msg('面板地址格式不正确,示例:

                        http://192.168.0.1:8888

                        ',{icon:5,time:5000}); @@ -1217,7 +1181,7 @@ function bindPanel(a,type,ip,btid,url,user,pw){ return; } if(type=="c"){ - gurl = "/config/set_panel_info"; + gurl = "/setting/set_panel_info"; data = data+"&id="+btid; } $.post(gurl, data, function(b) { @@ -1275,7 +1239,7 @@ function bindPanel(a,type,ip,btid,url,user,pw){ } //删除快捷登录 function bindPaneldel(id){ - $.post("/config/del_panel_info","id="+id,function(rdata){ + $.post("/setting/del_panel_info","id="+id,function(rdata){ layer.closeAll(); layer.msg(rdata.msg,{icon:rdata.status?1:2}); getPanelList(); @@ -1308,6 +1272,20 @@ function getSpeed(sele){ },1000); },'json'); } + +function tasklist(){ + var con='
                          \ + 若任务长时间未执行,请尝试在首页点【重启面板】来重置任务队列'; + $("#msg_box .taskcon").html(con); + $.post("/task/list", "tojs=getTaskList&table=tasks&limit=10&p=1", function(g) { + $('#msg_box .msg_count').html(g.count); + },'json'); + + setTimeout(function(){ + getReloads(); + },100); +} + //消息盒子 function messageBox() { layer.open({ @@ -1317,22 +1295,24 @@ function messageBox() { closeBtn: 1, shadeClose: false, content: '
                          \ -
                          \ -
                          \ -

                          任务列表(0)

                          \ -

                          消息列表(0)

                          \ -

                          执行日志

                          \ -
                          \ -
                          \ -
                          \ -
                          \ -
                          \ -
                          ' +
                          \ +
                          \ +

                          任务列表(0)

                          \ +

                          消息列表(0)

                          \ +

                          执行日志

                          \ +
                          \ +
                          \ +
                          \ +
                          \ +
                          \ +
                          ', + success:function(){ + $(".bt-w-menu p").click(function(){ + $(this).addClass("bgw").siblings().removeClass("bgw"); + }); + tasklist(); + } }); - $(".bt-w-menu p").click(function(){ - $(this).addClass("bgw").siblings().removeClass("bgw"); - }); - tasklist(); } //取执行日志 @@ -1373,10 +1353,10 @@ function getSFM(seconds, dateFormat = 'H:i:s') { function remind(a){ a = a == undefined ? 1 : a; + $(".taskcon").html(''); $.post("/task/list", "table=tasks&result=2,4,6,8&limit=10&p=" + a, function(g) { var e = ''; var f = false; - var task_count = 0; for(var d = 0; d < g.data.length; d++) { var status = g.data[d].status; var status_text = '已经完成'; @@ -1421,12 +1401,10 @@ function remind(a){
                          \
                          \
                          '; - - $(".task_count").text(task_count); - $(".msg_count").text(g.count); $(".taskcon").html(con); - $("#taskPage").html(g.page); + $(".msg_count").text(g.count); + $("#taskPage").html(g.page); $("#Rs-checkAll").click(function(){ if($(this).prop("checked")){ $("#remind").find("input").prop("checked",true); @@ -1438,40 +1416,37 @@ function remind(a){ } function getReloads() { - var a = 0; - var mm = $(".bt-w-menu .bgw").html() - if(mm == undefined || mm.indexOf(lan.bt.task_list) == -1) { + var mm = $("#msg_box .bt-w-menu .bgw").html(); + if(mm == undefined || mm.indexOf('任务列表') == -1) { clearInterval(speed); - a = 0; speed = null; return } if(speed) {return;} - speed = setInterval(function() { - var mm = $(".bt-w-menu .bgw").html() - if(mm == undefined || mm.indexOf(lan.bt.task_list) == -1) { + + function renderRunTask(){ + var mm = $("#msg_box .bt-w-menu .bgw").html(); + if(mm == undefined || mm.indexOf('任务列表') == -1) { clearInterval(speed); speed = null; a = 0; return } - a++; $.post('/task/get_task_speed', '', function(h) { if(h.task == undefined) { + $(".task_count").text(0); $(".cmdlist").html('当前没有任务!'); return; } - var b = ""; - var d = ""; - $("#task").text(h.task.length); - $(".task_count").text(h.task.length); + var b = ''; + var d = ''; for(var g = 0; g < h.task.length; g++) { if(h.task[g].status == "-1") { if(h.task[g].type != "download") { var c = ""; var f = h.msg.split("\n"); for(var e = 0; e < f.length; e++) { - c += f[e] + "
                          " + c += f[e] + "
                          "; } if(h.task[g].name.indexOf("扫描") != -1) { b = "
                        • \ @@ -1491,14 +1466,16 @@ function getReloads() { b = "
                        • \
                          \ " + h.task[g].name + "" + (toSize(h.msg.used) + "/" + toSize(h.msg.total)) + "\ - " + h.msg.pre + "%\ - "+lan.bt.task_downloading+" | "+lan.public.close+"\ + " + h.msg.pre + "%\ + 下载中 | "+lan.public.close+"\
                        • " } } else { d += "
                        • " + h.task[g].name + "等待 | 删除
                        • ' } } + $("#task").text(h.count); + $(".task_count").text(h.count); $(".cmdlist").html(b + d); $(".cmd").html(c); try{ @@ -1507,7 +1484,12 @@ function getReloads() { return; } },'json').error(function(){}); - }, 1000); + } + + renderRunTask(); + speed = setInterval(function() { + renderRunTask(); + }, 2000); } //检查选中项 @@ -1526,54 +1508,6 @@ function RscheckSelect(){ },5); } - -function tasklist(a){ - var con='
                            \ - 若任务长时间未执行,请尝试在首页点【重启面板】来重置任务队列'; - $(".taskcon").html(con); - a = a == undefined ? 1 : a; - $.post("/task/list", "tojs=getTaskList&table=tasks&limit=10&p=" + a, function(g) { - var e = ""; - var b = ""; - var c = ""; - var f = false; - var task_count =0; - for(var d = 0; d < g.data.length; d++) { - switch(g.data[d].status) { - case "-1": - f = true; - if(g.data[d].type != "download") { - b = "
                          • \ - " + g.data[d].name + "\ - 正在安装 | 关闭\ -
                            \
                            -						
                          • "; - } else { - b = "
                          • \ -
                            " + g.data[d].name + "0.0M/12.5M\ - 0%下载中 | 关闭\ -
                          • "; - } - task_count++; - break; - case "0": - c += "
                          • \ - " + g.data[d].name + "\ - 等待 | "+lan.public.del+"\ -
                          • "; - task_count++; - break; - } - } - - - $(".task_count").text(task_count); - $(".cmdlist").html(b + c); - getReloads(); - return f - },'json'); -} - function activeDisk() { var a = $("#PathPlace").find("span").text().substring(0, 1); switch(a) { @@ -1601,13 +1535,6 @@ function activeDisk() { } -function backMyComputer() { - $(".default").show(); - $(".file-list").hide(); - $("#PathPlace").find("span").html(""); - activeDisk(); -} - //检查登陆状态 function check_login(){ $.post('/check_login',{},function(rdata){ @@ -1633,13 +1560,6 @@ function scroll_handle(e){ $(this).find("thead").css({"transform":"translateY("+scrollTop+"px)","position":"relative","z-index":"1"}); } - -$(function(){ -/// -setInterval(function(){check_login();},6000); -/// -}); - function asyncLoadImage(obj, url){ if (typeof(url) == 'undefined'){ @@ -2777,8 +2697,8 @@ function pluginStandAloneLogs(_name, version, func, _args, line){ } /*** 其中功能,针对插件通过库使用 end ***/ - $(function() { + setInterval(function(){check_login();},6000); autoHeight(); }); $(window).resize(function() { diff --git a/web/static/app/site.js b/web/static/app/site.js index 815774ad8..30c634146 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -10,6 +10,16 @@ $('#site_search').click(function(){ getWeb(1, -1, $('#site_search_input').val()); }); +//设置到期日期 +function getDate(a) { + var dd = new Date(); + dd.setTime(dd.getTime() + (a == undefined || isNaN(parseInt(a)) ? 0 : parseInt(a)) * 86400000); + var y = dd.getFullYear(); + var m = dd.getMonth() + 1; + var d = dd.getDate(); + return y + "-" + (m < 10 ? ('0' + m) : m) + "-" + (d < 10 ? ('0' + d) : d); +} + /** * 取回网站数据列表 * @param {Number} page 当前页 @@ -35,83 +45,73 @@ $('#site_search').click(function(){ type = '&type_id='+type_id; } - var sUrl = '/site/list'; var pdata = 'limit=10&p=' + page + '&search=' + search + order + type; var loadT = layer.load(); //取回数据 - $.post(sUrl, pdata, function(data) { + $.post('/site/list', pdata, function(data) { layer.close(loadT); //构造数据列表 var body = ''; $("#webBody").html(body); - for (var i = 0; i < data.data.length; i++) { + var list = data.data; + for (var i = 0; i < list.length; i++) { //当前站点状态 - if (data.data[i].status == '正在运行' || data.data[i].status == '1') { - var status = "运行中"; + if (list[i].status == '正在运行' || list[i].status == '1') { + var status = "运行中"; } else { - var status = "已停止"; + var status = "已停止"; } //是否有备份 - if (data.data[i].backup_count > 0) { - var backup = "有备份"; + if (list[i].backup_count > 0) { + var backup = "有备份"; } else { - var backup = "无备份"; + var backup = "无备份"; } //是否设置有效期 - var web_end_time = (data.data[i].edate == "0000-00-00") ? '永久': data.data[i].edate; + var web_end_time = (list[i].edate == "0000-00-00") ? '永久': list[i].edate; //表格主体 - var shortwebname = data.data[i].name; - var shortpath = data.data[i].path; - if(data.data[i].name.length > 30) { - shortwebname = data.data[i].name.substring(0, 30) + "..."; + var shortwebname = list[i].name; + var shortpath = list[i].path; + if(list[i].name.length > 30) { + shortwebname = list[i].name.substring(0, 30) + "..."; } - if(data.data[i].path.length > 30){ - shortpath = data.data[i].path.substring(0, 30) + "..."; + if(list[i].path.length > 30){ + shortpath = list[i].path.substring(0, 30) + "..."; } - var idname = data.data[i].name.replace(/\./g,'_'); + var idname = list[i].name.replace(/\./g,'_'); - body = "\ - " + shortwebname + "\ + body = "\ + " + shortwebname + "\ " + status + "\ " + backup + "\ - " + shortpath + "\ - " + web_end_time + "\ - " + data.data[i].ps + "\ + " + shortpath + "\ + " + web_end_time + "\ + " + list[i].ps + "\ \ - 设置\ - | 删除\ + 设置\ + | 删除\ " $("#webBody").append(body); - //setEdate(data.data[i].id,data.data[i].edate); - //设置到期日期 - function getDate(a) { - var dd = new Date(); - dd.setTime(dd.getTime() + (a == undefined || isNaN(parseInt(a)) ? 0 : parseInt(a)) * 86400000); - var y = dd.getFullYear(); - var m = dd.getMonth() + 1; - var d = dd.getDate(); - return y + "-" + (m < 10 ? ('0' + m) : m) + "-" + (d < 10 ? ('0' + d) : d); - } - $('#webBody').on('click','#site_'+ data.data[i].id,function(){ + $('#webBody').on('click','#site_'+ list[i].id,function(){ var _this = $(this); var id = $(this).attr('data-ids'); laydate.render({ - elem: '#site_'+ id //指定元素 - ,min:getDate(1) - ,max:'2099-12-31' - ,vlue:getDate(365) - ,type:'date' - ,format :'yyyy-MM-dd' - ,trigger:'click' - ,btns:['perpetual', 'confirm'] - ,theme:'#20a53a' - ,done:function(dates){ + elem: '#site_'+ id, + min:getDate(-1), + max:'9999-12-31', + vlue:getDate(365), + type:'date', + format :'yyyy-MM-dd', + trigger:'click', + btns:['perpetual', 'confirm'], + theme:'#20a53a', + done:function(dates){ if(_this.html() == '永久'){ dates = '0000-00-00'; } - var loadT = layer.msg(lan.site.saving_txt, { icon: 16, time: 0, shade: [0.3, "#000"]}); + var loadT = layer.msg('正在保存...', { icon: 16, time: 0, shade: [0.3, "#000"]}); $.post('/site/set_end_date','id='+id+'&edate='+dates,function(rdata){ layer.close(loadT); layer.msg(rdata.msg,{icon:rdata.status?1:5}); @@ -123,7 +123,6 @@ $('#site_search').click(function(){ } if(body.length < 10){ body = "当前没有站点数据"; - // $(".dataTables_paginate").hide(); $("#webBody").html(body); } //输出数据列表 @@ -135,7 +134,6 @@ $('#site_search').click(function(){ //输出分页 $("#webPage").html(data.page); - // $("#webPage").html('
                            站点分类:
                            '); $(".btlinkbed").click(function(){ var dataid = $(this).attr("data-id"); @@ -337,9 +335,7 @@ function webAddPage(type) { if (len > 20) { str = str.substring(0, 20); $(this).val(str); - layer.msg('不能超出20个字符!', { - icon: 0 - }); + layer.msg('不能超出20个字符!', {icon: 0}); } }) //获取当前时间时间戳,截取后6位 @@ -351,30 +347,30 @@ function webAddPage(type) { //修改网站目录 function webPathEdit(id){ - $.post('/site/get_dir_user_ini','&id='+id, function(data){ - var userini = data['data']; - var webpath = userini['path']; - var siteName = userini['name']; - var runPath = userini['runPath']['runPath']; - var userinicheckeds = userini.userini?'checked':''; - var logscheckeds = userini.logs?'checked':''; - var opt = '' + $.post('/site/get_dir_user_ini','id='+id, function(data){ + var data = data['data']; + var site_path = data['path']; + var site_name = data['name']; + var run_path = data['run_path']['run_path']; + var user_ini_checked = data.user_ini?'checked':''; + var logs_checked = data.logs?'checked':''; + var opt = ''; var selected = ''; - for(var i=0;i'+ userini.runPath.dirs[i] +'' + opt += ''; } - var webPathHtml = "
                            \ + var content = "
                            \
                            \ - \ - \ + \ + \
                            \
                            \ 网站目录\ - \ + \ \ \
                            \ @@ -389,11 +385,11 @@ function webPathEdit(id){ " +'
                            ' +'密码访问' - +'' + +'' +'' +'' +'
                            ' - +'
                            ' + +'
                            ' +'

                            授权账号

                            ' +'

                            访问密码

                            ' +'

                            重复密码

                            ' @@ -401,14 +397,11 @@ function webPathEdit(id){ +'
                            ' +'
                            '; - $("#webedit-con").html(webPathHtml); + $("#webedit-con").html(content); $("#userini").change(function(){ - $.post('/site/set_dir_user_ini',{ - 'path':webpath, - 'runPath':runPath, - },function(userini){ - layer.msg(userini.msg+'

                            注意:设置防跨站需要重启PHP才能生效!

                            ',{icon:userini.status?1:2}); - tryRestartPHP(siteName); + $.post('/site/set_dir_user_ini',{'path':site_path,'run_path':run_path,},function(userini){ + layer.msg(data.msg+'

                            注意:设置防跨站需要重启PHP才能生效!

                            ',{icon:data.status?1:2}); + tryRestartPHP(site_name); },'json'); }); @@ -458,10 +451,9 @@ function setPathSafe(id){ function setSiteRunPath(id){ var NewPath = $("#runPath").val(); var loadT = layer.msg(lan.public.the,{icon:16,time:10000,shade: [0.3, '#000']}); - $.post('/site/set_site_run_path','id='+id+'&runPath='+NewPath,function(rdata){ + $.post('/site/set_site_run_path','id='+id+'&run_path='+NewPath,function(rdata){ layer.close(loadT); - var ico = rdata.status?1:2; - layer.msg(rdata.msg,{icon:ico}); + layer.msg(rdata.msg,{icon:rdata.status?1:2}); },'json'); } @@ -695,7 +687,7 @@ function checkDomain() { * @param {Int} id 网站ID * @param {String} webname 主域名 */ -function domainAdd(id, webname,type) { +function domainAdd(id, webname, type) { var Domain = $("#newdomain").val().split("\n"); var domainlist = ''; @@ -710,7 +702,7 @@ function domainAdd(id, webname,type) { domainlist = domainlist.substring(0,domainlist.length-1); var loadT = layer.load(); - var data = "domain=" + domainlist + "&webname=" + webname + "&id=" + id; + var data = "domain=" + domainlist + "&site_name=" + webname + "&id=" + id; $.post('/site/add_domain', data, function(retuls) { layer.close(loadT); domainEdit(id, webname, retuls.msg, retuls.status); @@ -730,10 +722,9 @@ function delDomain(wid, wname, domain, port,type) { layer.msg(lan.site.domain_last_cannot); } layer.confirm(lan.site.domain_del_confirm,{icon:3,closeBtn:2}, function(index) { - var url = "/site/del_domain" - var data = "id=" + wid + "&webname=" + wname + "&domain=" + domain + "&port=" + port; - var loadT = layer.msg(lan.public.the_del,{time:0,icon:16}); - $.post(url,data, function(ret) { + var data = "id=" + wid + "&site_name=" + wname + "&domain=" + domain + "&port=" + port; + var loadT = layer.msg('您真的要从站点中删除这个域名吗?',{time:0,icon:16}); + $.post('/site/del_domain', data, function(ret) { layer.close(loadT); layer.msg(ret.msg,{icon:ret.status?1:2}) if(type == 1){ @@ -812,7 +803,7 @@ function getBackup(id,name,page) { var ftpdown = "下载 | "; body += ""+frdata.data[i].name+"\ " + (toSize(frdata.data[i].size)) + "\ - " + frdata.data[i].addtime + "\ + " + frdata.data[i].add_time + "\ "+ ftpdown + "删除\ " } @@ -822,9 +813,9 @@ function getBackup(id,name,page) { if(name == 0){ var sBody = "\ - \ - "+body+"\ -
                            文件名称文件大小打包时间操作
                            " + 文件名称文件大小打包时间操作\ + "+body+"\ + " $("#webBackupList").html(sBody); $(".page").html(frdata.page); return; @@ -839,17 +830,17 @@ function getBackup(id,name,page) { shift: 0, shadeClose: false, content: "
                            \ - \ -
                            \ - \ - \ - \ - \ - " + body + "\ -
                            文件名称文件大小打包时间操作
                            \ -
                            " + frdata.page + "
                            \ -
                            \ -
                            " + \ +
                            \ + \ + \ + \ + \ + " + body + "\ +
                            文件名称文件大小打包时间操作
                            \ +
                            " + frdata.page + "
                            \ +
                            \ +
                            " }); },'json'); } @@ -1030,18 +1021,19 @@ function setIndexList(id){ } -/*站点修改*/ +/* 站点修改 */ function webEdit(id,website,endTime,addtime){ + // 暂时关闭 - 子目录绑定 + //

                            子目录绑定

                            \ layer.open({ type: 1, - area: '700px', + area: ['700px','570px'], title: '站点修改['+website+'] -- 添加时间['+addtime+']', closeBtn: 1, shift: 0, content: "
                            \ -
                            \ +
                            \

                            域名管理

                            \ -

                            子目录绑定

                            \

                            网站目录

                            \

                            流量限制

                            \

                            伪静态

                            \ @@ -1055,7 +1047,7 @@ function webEdit(id,website,endTime,addtime){

                            响应日志

                            \

                            错误日志

                            \
                            \ -
                            \ +
                            \
                            ", success:function(){ //域名输入提示 @@ -1086,7 +1078,7 @@ function webEdit(id,website,endTime,addtime){ }); } -//取网站日志 +//取网站日志pluginLogs function getSiteLogs(siteName){ var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); $.post('/site/get_logs',{siteName:siteName},function(logs){ @@ -1095,9 +1087,10 @@ function getSiteLogs(siteName){ logs.msg = ''; } if (logs.msg == '') logs.msg = '当前没有日志.'; - var phpCon = ''; - $("#webedit-con").html(phpCon); - var ob = document.getElementById('error_log'); + var h = parseInt($('.bt-w-menu').css('height')) - 35; + var con = ''; + $("#webedit-con").html(con); + var ob = document.getElementById('site_log'); ob.scrollTop = ob.scrollHeight; },'json'); } @@ -1106,14 +1099,14 @@ function getSiteLogs(siteName){ function getSiteErrorLogs(siteName){ var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']}); $.post('/site/get_error_logs',{siteName:siteName},function(logs){ - // console.log(logs); layer.close(loadT); if(logs.status !== true){ logs.msg = ''; } if (logs.msg == '') logs.msg = '当前没有日志.'; - var phpCon = ''; - $("#webedit-con").html(phpCon); + var h = parseInt($('.bt-w-menu').css('height')) - 35; + var con = ''; + $("#webedit-con").html(con); var ob = document.getElementById('error_log'); ob.scrollTop = ob.scrollHeight; },'json'); @@ -1122,9 +1115,8 @@ function getSiteErrorLogs(siteName){ //防盗链 function security(id,name){ - var loadT = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); + var loadT = layer.msg('正在提交任务...',{icon:16,time:0,shade: [0.3, '#000']}); $.post('/site/get_security',{id:id,name:name},function(rdata){ - console.log(rdata); layer.close(loadT); var mbody = '
                            ' +'

                            URL后缀

                            ' @@ -1246,7 +1238,7 @@ function saveLimitNet(id, type){ if(isChecked == undefined || type == 1 ){ var data = 'id='+id+'&perserver='+$("input[name='perserver']").val()+'&perip='+$("input[name='perip']").val()+'&limit_rate='+$("input[name='limit_rate']").val(); var loadT = layer.msg(lan.public.config,{icon:16,time:10000}) - $.post('/site/save_limit_net',data,function(rdata){ + $.post('/site/set_limit_net',data,function(rdata){ layer.close(loadT); limitNet(id); layer.msg(rdata.msg,{icon:rdata.status?1:2}); @@ -1351,13 +1343,13 @@ function showRewrite(rdata){ //添加子目录绑定 function addDirBinding(id){ var domain = $("input[name='domain']").val(); - var dirName = $("select[name='dirName']").val(); - if(domain == '' || dirName == '' || dirName == null){ + var dir_name = $("select[name='dirName']").val(); + if(domain == '' || dir_name == '' || dir_name == null){ layer.msg(lan.site.d_s_empty,{icon:2}); return; } - var data = 'id='+id+'&domain='+domain+'&dirName='+dirName; + var data = 'id='+id+'&domain='+domain+'&dir_name='+dir_name; $.post('/site/add_dir_bind',data,function(rdata){ dirBinding(id); layer.msg(rdata.msg,{icon:rdata.status?1:2}); @@ -1379,79 +1371,75 @@ function to301(siteName, type, obj){ // 设置 页面展示 if(type == 1) { - obj = { + var obj = { + type: 1, + keep_path: 1, to: 'http://', from: '', r_type: '', + type: 'path' + }; + + var keep_path_ht = obj.keep_path == 1 ? 'checked="checked"' : ''; + var redirect_title = type == 1 ? '创建重定向' : '修改重定向[' + obj.redirectname + ']'; + layer.open({ type: 1, - type: 'path', - keep_path: 1 - } - var redirect_form = layer.open({ - type: 1, - skin: 'demo-class', - area: '650px', - title: type == 1 ? '创建重定向' : '修改重定向[' + obj.redirectname + ']', + area: ['650px','270px'], + title: redirect_title, closeBtn: 1, shift: 5, + btn: ['提交','关闭'], shadeClose: false, - content: "
                            " + - "
                            " + - "
                            " + - "保留URI参数" + - "" + - "
                            " + - "
                            " + - "
                            " + - "重定向类型" + - "
                            " + - "" + - "重定向方式" + - "
                            " + - "
                            " + - "
                            " + - "重定向源" + - "
                            " + - "" + - "目标URL" + - "" + - "
                            " + - "
                            " + - "
                            " + - "
                            " + - "" - }); - setTimeout(function() { - $('.btn-colse-prosy').click(function() { - layer.close(redirect_form); - }); - $('.btn-submit-redirect').click(function() { + content: "
                            \ +
                            \ +
                            \ + 保留URI参数\ + \ + \ +
                            \ +
                            \ +
                            重定向类型\ +
                            \ + \ + 重定向方式\ + \ +
                            \ +
                            \ +
                            \ + 重定向源\ +
                            \ + \ + 目标URL\ + \ +
                            \ +
                            \ +
                            ", + success:function(index,layero){ + }, + yes:function(index,index1){ var keep_path = $('[name="keep_path"]').prop('checked') ? 1 : 0; var r_type = $('[name="r_type"]').val(); var type = $('[name="type"]').val(); var from = $('[name="from"]').val(); var to = $('[name="to"]').val(); - $.post('/site/set_redirect', { - siteName: siteName, - type: type, - r_type: r_type, - from: from, - to: to, - keep_path: keep_path - }, function(res) { - res = JSON.parse(res); - if (res.status) { - layer.close(redirect_form); - to301(siteName) + var pdata = {siteName: siteName, type: type,r_type: r_type,from: from,to: to,keep_path: keep_path}; + $.post('/site/set_redirect', pdata, function(data) { + if (data.status) { + layer.close(index); + to301(siteName); } else { - layer.msg(res.msg, { - icon: 2 - }); + layer.msg(data.msg, {icon: 2}); } - }); - }); - }, 100); + },'json'); + } + }); } if (type == 2) { @@ -1459,14 +1447,13 @@ function to301(siteName, type, obj){ siteName: siteName, id: obj, }, function(res) { - res = JSON.parse(res); if (res.status == true) { layer.msg('删除成功', {time: 1000,icon: 1}); to301(siteName); } else { layer.msg(res.msg, {time: 1000,icon: 2}); } - }); + },'json'); return } @@ -1475,7 +1462,6 @@ function to301(siteName, type, obj){ var data = {siteName: siteName,id: obj}; $.post('/site/get_redirect_conf', data, function(res) { layer.close(laoding); - res = JSON.parse(res); if (res.status == true) { var mBody = "
                            \ \ @@ -1533,40 +1519,53 @@ function to301(siteName, type, obj){ } var body = '
                            \ -
                            \ - \ -
                            \ -
                            \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
                            重定向类型重定向方式保留URL参数操作
                            \ -
                            \ -
                            '; +
                            \ + \ +
                            \ +
                            \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
                            重定向类型重定向方式保留URL参数状态操作
                            \ +
                            \ +
                            '; $("#webedit-con").html(body); var loadT = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); $.post('/site/get_redirect','siteName='+siteName, function(res) { layer.close(loadT); $("#md-301-loading").remove(); - if (res.status === true) { - let data = res.data.result; + if (res.status) { + var data = res.data.result; data.forEach(function(item){ - lan_r_type = item.r_type == 0 ? "永久重定向" : "临时重定向" - keep_path = item.keep_path == 0 ? "不保留" : "保留" + var lan_r_type = item.r_type == 0 ? "永久重定向" : "临时重定向"; + var keep_path = item.keep_path == 0 ? "不保留" : "保留"; + + var switchProxy = ''; + if (!item['status']){ + switchProxy = ''; + } + let tmp = '\ '+item.r_from+'\ '+lan_r_type+'\ - '+keep_path+'\ - 详细 | 删除\ + '+keep_path+'\ + '+switchProxy+'\ + \ + 详细 | \ + 删除\ + \ '; $("#md-301-body").append(tmp); }) @@ -1577,6 +1576,28 @@ function to301(siteName, type, obj){ } +function toRedirect(siteName, redirect_id, type){ + if (type == 10 || type == 11) { + //[11]启动 或 停止[10] + var status = type == 10 ? '0' : '1'; + var loading = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); + + var pdata = {siteName: siteName, 'status':status,'id':redirect_id }; + $.post('/site/set_redirect_status', pdata, function(rdata) { + layer.close(loading); + if (!rdata.status){ + layer.msg(res.msg, {time: 3000,icon: 2}); + return; + } + + showMsg("设置成功",function(){ + to301(siteName); + },{icon: 1,time:2000}); + },'json'); + return; + } +} + //反向代理 function toProxy(siteName, type, obj) { // 设置 页面展示 @@ -1845,27 +1866,27 @@ function toProxy(siteName, type, obj) { } var body = '
                            \ -
                            \ - \ -
                            \ -
                            \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
                            名称代理目录目标地址缓存状态操作
                            \ -
                            \ -
                            '; +
                            \ + \ +
                            \ +
                            \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
                            名称代理目录目标地址缓存状态操作
                            \ +
                            \ +
                            '; $("#webedit-con").html(body); var loading = layer.msg(lan.site.the_msg,{icon:16,time:0,shade: [0.3, '#000']}); @@ -1983,12 +2004,14 @@ function setCertSsl(certName,siteName){ //ssl function setSSL(id,siteName){ + // Let\'s Encrypt + // 暂时关闭 Let 申请模式 var sslHtml = '\
                            \ 当前证书 - [未部署SSL]\ - Let\'s Encrypt\ + \ ACME\ 证书夹' + '
                            \ @@ -2051,17 +2074,98 @@ function renewSSL(type,id,siteName){ }); } -function fileCheck(){ + + +function renderDnsapiHtml(data){ + var fields = data.data; + var fields_html = ''; + + for (var d in fields) { + fields_html += ""+d+"\ +
                            \ + \ +
                            "; + } + + layer.open({ + type: 1, + area: '500px', + title: '设置'+data['title']+'接口', + closeBtn: 1, + shift: 5, + shadeClose: true, + btn:["确定","取消"], + content: "
                            \ +
                            \ + DNSAPI类型\ +
                            \ + \ +
                            \ +
                            \ +
                            \ + "+fields_html+"\ +
                            \ +
                            \ +
                            \ +
                              \ +
                            • 使用【"+data['title']+"】的API接口自动解析申请SSL
                            • \ +
                            \ +
                            \ +
                            \ +
                            ", + yes:function(index,l) { + var type_name = $('select[name="type_name"]').val(); + var data_field = {}; + for (var d in fields) { + data_field[d] = $('input[name="'+d+'"]').val(); + } + + $.post('/site/set_dnsapi', {'type':type_name,'data':JSON.stringify(data_field)}, function(rdata){ + showMsg(rdata.msg, function(){ + if (rdata.status){ + layer.close(index); + renderDnsapi(); + } + },{icon:rdata.status?1:2}); + },'json'); + } + }); } -function dnsCheck(){ +function renderDnsapi(){ + $('#dnsapi_set').css('display', 'none'); + $.post('/site/get_dnsapi', {}, function(data){ + var dnsapi_option = ''; + for (var i = 0; i < data.length; i++) { + dnsapi_option+=''; + } + + $('#dnsapi_option select').html(dnsapi_option); + $('#dnsapi_option select').on('change',function(){ + var val = $(this).val(); + var index = $('#dnsapi_option option:selected').attr('index'); + if (val == 'none'){ + $('#dnsapi_option button').css('display','none'); + } else { + $('#dnsapi_option button').css('display','inline-block'); + if (!(data[index]['title'].indexOf('[')>0)){ + renderDnsapiHtml(data[index]); + } + } + }); + + $('#dnsapi_set').unbind('click').on('click', function(){ + var index = $('#dnsapi_option option:selected').attr('index'); + renderDnsapiHtml(data[index]); + }); + },'json'); } -//SSL -function opSSL(type, id, siteName, callback){ - +function opSSLNow(type, id, siteName, callback){ var now = '
                            \ \
                            \ @@ -2076,60 +2180,92 @@ function opSSL(type, id, siteName, callback){
                          • 粘贴您的*.key以及*.pem内容,然后保存即可。
                          • \
                          • 如果浏览器提示证书链不完整,请检查是否正确拼接PEM证书
                          • PEM格式证书 = 域名证书.crt + 根证书(root_bundle).crt
                          • \
                          • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
                          • \ - '; + '; - // \ - // \ - var lets = '
                            \ -
                            \ -
                            \ -
                            \ - 验证方式\ -
                            \ - \ - \ -
                            \ -
                            \ -
                            \ -
                            \ -
                            \ - \ - \ -
                            \ -
                            \ -
                            \ -
                            \ - 管理员邮箱\ - \ -
                            \ -
                            \ - 域名\ -
                              \ -
                              \ -
                              \ - \ -
                              \ -
                                \ -
                              • 申请之前,请确保域名已解析,如未解析会导致审核失败
                              • \ -
                              • Let\'s Encrypt免费证书,有效期3个月,支持多域名。默认会自动续签
                              • \ -
                              • 若您的站点使用了CDN或301重定向会导致续签失败
                              • \ -
                              • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
                              • \ -
                              \ -
                              '; + $(".tab-con").html(now); + var key = ''; + var csr = ''; + var loadT = layer.msg('正在提交任务...',{icon:16,time:0,shade: [0.3, '#000']}); + $.post('/site/get_ssl','site_name='+siteName,function(data){ + layer.close(loadT); + var rdata = data['data']; - // \ - // \ + if (rdata['cert_data']){ + var issuer = rdata['cert_data']['issuer'].split(" "); + var domains = rdata['cert_data']['dns'].join("、"); + + var cert_data = "
                              \ +
                              证书品牌:"+issuer[0]+"
                              \ +
                              到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
                              \ +
                              \ +
                              \ +
                              认证域名:"+domains+"
                              \ +
                              强制HTTPS:\ + \ +
                              \ +
                              "; + $(".ssl_state_info").html(cert_data); + $(".ssl_state_info").css('display','block'); + } + + if(rdata.key == false){ + rdata.key = ''; + } else { + $(".ssl-btn").append(''); + } + + if(rdata.csr == false){ + rdata.csr = ''; + } + $("#key").val(rdata.key); + $("#csr").val(rdata.csr); + + $("#toHttps").attr('checked',rdata.httpTohttps); + if(rdata.status){ + $('.warning_info').css('display','none'); + + $(".ssl-btn").append(""); + $('#now_ssl').html('当前证书 - [已部署SSL]'); + } else{ + $('.warning_info').css('display','block'); + $('#now_ssl').html('当前证书 - [未部署SSL]'); + } + + + if (typeof (callback) != 'undefined'){ + callback(rdata); + } + },'json'); +} + +function opSSLAcme(type, id, siteName, callback){ var acme = '
                              \
                              \
                              \ -
                              \ - 验证方式\ -
                              \ - \ - \ -
                              \ -
                              \ + 验证方式\ +
                              \ + \ + \ + \ + \ +
                              \
                              \ + \ + \
                              \
                              \ \ @@ -2138,7 +2274,7 @@ function opSSL(type, id, siteName, callback){
                              \
                              \
                              \ - 管理员邮箱\ + 邮箱\ \
                              \
                              \ @@ -2156,235 +2292,261 @@ function opSSL(type, id, siteName, callback){ \
                              '; - - switch(type){ - case 'lets': - $(".tab-con").html(lets); - $.post('/site/get_ssl', 'site_name='+siteName+'&ssl_type=lets', function(data){ - var rdata = data['data']; - if(rdata.csr == false){ - $.post('/site/get_site_domains','id='+id, function(rdata) { - var data = rdata['data']; - var opt=''; - for(var i=0;i'+data.domains[i].name+'' - } + $(".tab-con").html(acme); + + $('input[name="apply_type"]').on('change', function(){ + var val = $(this).val(); + if (val == 'file'){ + $('#dnsapi_option').css('display','none'); + $('#wildcard_domain_block').css('display','none'); + } else { + $('#dnsapi_option').css('display','block'); + $('#wildcard_domain_block').css('display','block'); + } + }); + + renderDnsapi(); + + $.post('/site/get_ssl','site_name='+siteName+'&ssl_type=acme', function(data){ + var rdata = data['data']; + if(rdata.csr == false){ + $.post('/site/get_site_domains','id='+id, function(rdata) { + var data = rdata['data']; + var opt=''; + for(var i=0;i'+data.domains[i].name + +''; + } + } + $("input[name='admin_email']").val(data.email); + $("#ymlist").html(opt); + $("#ymlist li input").click(function(e){ + e.stopPropagation(); + }) + $("#ymlist li").click(function(){ + var o = $(this).find("input"); + if(o.prop("checked")){ + o.prop("checked",false) + } + else{ + o.prop("checked",true); + } + }) + $(".letsApply").click(function(){ + var c = $("#ymlist input[type='checkbox']"); + var str = []; + var domains = ''; + for(var i=0; i
                              \ -
                              证书(PEM格式)
                              \ -
                              \ -
                              \ - \ - \ - \ -
                              \ -
                              \ -
                                \ -
                              • 已为您自动生成Let\'s Encrypt免费证书
                              • \ -
                              • 由Let\'s Encrypt免费申请证书,有效期3个月,支持多域名。默认会自动续签
                              • \ -
                              • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
                              • \ -
                              '; - $(".tab-con").html(lets); - - if (rdata['cert_data']){ - var issuer = rdata['cert_data']['issuer'].split(" "); - var domains = rdata['cert_data']['dns'].join("、"); - - var cert_data = "
                              \ -
                              证书品牌:"+issuer[0]+"
                              \ -
                              到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
                              \ -
                              \ -
                              \ -
                              认证域名:"+domains+"
                              \ -
                              "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - },'json'); - break; - case 'acme': - $(".tab-con").html(acme); - $.post('/site/get_ssl', 'site_name='+siteName+'&ssl_type=acme', function(data){ - var rdata = data['data']; - if(rdata.csr == false){ - $.post('/site/get_site_domains','id='+id, function(rdata) { - var data = rdata['data']; - var opt=''; - for(var i=0;i'+data.domains[i].name - +''; - } - } - $("input[name='admin_email']").val(data.email); - $("#ymlist").html(opt); - $("#ymlist li input").click(function(e){ - e.stopPropagation(); - }) - $("#ymlist li").click(function(){ - var o = $(this).find("input"); - if(o.prop("checked")){ - o.prop("checked",false) - } - else{ - o.prop("checked",true); - } - }) - $(".letsApply").click(function(){ - var c = $("#ymlist input[type='checkbox']"); - var str = []; - var domains = ''; - for(var i=0; i
                              \ -
                              证书(PEM格式)
                              \ -
                              \ -
                              \ - \ - \ -
                              \ -
                              \ -
                                \ -
                              • 已为您自动生成ACME免费证书
                              • \ -
                              • 由ACME免费申请证书,有效期3个月,支持多域名。默认会自动续签
                              • \ -
                              • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
                              • \ -
                              '; - $(".tab-con").html(acme); - - if (rdata['cert_data']){ - var issuer = rdata['cert_data']['issuer'].split(" "); - var domains = rdata['cert_data']['dns'].join("、"); - - var cert_data = "
                              \ -
                              证书品牌:"+issuer[0]+"
                              \ -
                              到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
                              \ -
                              \ -
                              \ -
                              认证域名:"+domains+"
                              \ -
                              "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - },'json'); - break; - case 'now': - $(".tab-con").html(now); - var key = ''; - var csr = ''; - var loadT = layer.msg('正在提交任务...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('site/get_ssl','site_name='+siteName,function(data){ - layer.close(loadT); - var rdata = data['data']; - - if (rdata['cert_data']){ - var issuer = rdata['cert_data']['issuer'].split(" "); - var domains = rdata['cert_data']['dns'].join("、"); - - var cert_data = "
                              \ -
                              证书品牌:"+issuer[0]+"
                              \ -
                              到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
                              \ -
                              \ -
                              \ -
                              认证域名:"+domains+"
                              \ -
                              强制HTTPS:\ - \ -
                              \ -
                              "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - - if(rdata.key == false){ - rdata.key = ''; - } else { - $(".ssl-btn").append(''); - } - - if(rdata.csr == false){ - rdata.csr = ''; - } - $("#key").val(rdata.key); - $("#csr").val(rdata.csr); - - $("#toHttps").attr('checked',rdata.httpTohttps); - if(rdata.status){ - $('.warning_info').css('display','none'); - - $(".ssl-btn").append(""); - $('#now_ssl').html('当前证书 - [已部署SSL]'); - } else{ - $('.warning_info').css('display','block'); - $('#now_ssl').html('当前证书 - [未部署SSL]'); - } - + } + domains = JSON.stringify(str); + newAcmeSSL(siteName, id, domains); + }); if (typeof (callback) != 'undefined'){ callback(rdata); } },'json'); - break; - default: - layer.msg("错误类型", {icon:5}); - break; + return; + } + var acme = '
                              \ + \ +
                              \ +
                              密钥(KEY)
                              \ +
                              证书(PEM格式)
                              \ +
                              \ +
                              \ + \ + \ +
                              \ +
                              \ +
                                \ +
                              • 已为您自动生成ACME免费证书
                              • \ +
                              • 由ACME免费申请证书,有效期3个月,支持多域名。默认会自动续签
                              • \ +
                              • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
                              • \ +
                              '; + $(".tab-con").html(acme); + + if (rdata['cert_data']){ + var issuer = rdata['cert_data']['issuer'].split(" "); + var domains = rdata['cert_data']['dns'].join("、"); + + var cert_data = "
                              \ +
                              证书品牌:"+issuer[0]+"
                              \ +
                              到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
                              \ +
                              \ +
                              \ +
                              认证域名:"+domains+"
                              \ +
                              "; + $(".ssl_state_info").html(cert_data); + $(".ssl_state_info").css('display','block'); + } + },'json'); +} + +function opSSLLet(type, id, siteName, callback){ + var lets = '
                              \ +
                              \ +
                              \ + 验证方式\ +
                              \ + \ + \ + \ + \ +
                              \ +
                              \ + \ + \ +
                              \ +
                              \ + \ + \ +
                              \ +
                              \ +
                              \ +
                              \ + 邮箱\ + \ +
                              \ +
                              \ + 域名\ +
                                \ +
                                \ +
                                \ + \ +
                                \ +
                                  \ +
                                • 申请之前,请确保域名已解析,如未解析会导致审核失败
                                • \ +
                                • Let\'s Encrypt免费证书,有效期3个月,支持多域名。默认会自动续签
                                • \ +
                                • 若您的站点使用了CDN或301重定向会导致续签失败
                                • \ +
                                • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
                                • \ +
                                \ +
                                '; + + $(".tab-con").html(lets); + + $('input[name="apply_type"]').on('change', function(){ + var val = $(this).val(); + if (val == 'file'){ + $('#dnsapi_option').css('display','none'); + $('#wildcard_domain_block').css('display','none'); + } else { + $('#dnsapi_option').css('display','block'); + $('#wildcard_domain_block').css('display','block'); + } + }); + + renderDnsapi(); + + $.post('/site/get_ssl', 'site_name='+siteName+'&ssl_type=lets', function(data){ + var rdata = data['data']; + if(rdata.csr == false){ + $.post('/site/get_site_domains','id='+id, function(rdata) { + var data = rdata['data']; + var opt=''; + for(var i=0;i'+data.domains[i].name+'' + } + } + $("input[name='admin_email']").val(data.email); + $("#ymlist").html(opt); + $("#ymlist li input").click(function(e){ + e.stopPropagation(); + }) + $("#ymlist li").click(function(){ + var o = $(this).find("input"); + if(o.prop("checked")){ + o.prop("checked",false) + } + else{ + o.prop("checked",true); + } + }) + $(".letsApply").click(function(){ + var c = $("#ymlist input[type='checkbox']"); + var str = []; + var domains = ''; + for(var i=0; i
                                \ +
                                证书(PEM格式)
                                \ +
                                \ +
                                \ + \ + \ + \ +
                                \ + \ +
                                  \ +
                                • 已为您自动生成Let\'s Encrypt免费证书
                                • \ +
                                • 由Let\'s Encrypt免费申请证书,有效期3个月,支持多域名。默认会自动续签
                                • \ +
                                • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
                                • \ +
                                '; + $(".tab-con").html(lets); + + if (rdata['cert_data']){ + var issuer = rdata['cert_data']['issuer'].split(" "); + var domains = rdata['cert_data']['dns'].join("、"); + + var cert_data = "
                                \ +
                                证书品牌:"+issuer[0]+"
                                \ +
                                到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
                                \ +
                                \ +
                                \ +
                                认证域名:"+domains+"
                                \ +
                                "; + $(".ssl_state_info").html(cert_data); + $(".ssl_state_info").css('display','block'); + } + },'json'); +} + +//SSL +function opSSL(type, id, siteName, callback){ + switch(type){ + case 'lets':opSSLLet(type, id, siteName, callback);break; + case 'acme':opSSLAcme(type, id, siteName, callback);break; + case 'now':opSSLNow(type, id, siteName, callback);break; + default:layer.msg("错误类型", {icon:5});break; } } @@ -2422,40 +2584,64 @@ function ocSSL(action,siteName){ //生成SSL function newSSL(siteName, id, domains){ showSpeedWindow('正在申请...', 'site.get_let_logs', function(layers,index){ - var force = ''; - if ($("#checkDomain").prop("checked")){ - force = '&force=true'; + var pdata = {}; + pdata['siteName'] = siteName; + pdata['domains'] = domains; + pdata['email'] = $("input[name='admin_email']").val(); + + if($("#checkDomain").prop("checked")){ + pdata['force'] = 'true'; } - var email = $("input[name='admin_email']").val(); - $.post('/site/create_let','siteName='+siteName+'&domains='+domains+'&email='+email + force,function(rdata){ - layer.close(index); - if(rdata.status){ - showMsg(rdata.msg, function(){ + + if($("#wildcard_domain").prop("checked")){ + pdata['wildcard_domain'] = 'true'; + } + + var apply_type = $('input[name="apply_type"]:checked').val(); + pdata['apply_type'] = apply_type; + if (apply_type == 'dns'){ + pdata['dnspai'] = $('#dnsapi_option option:selected').val(); + } + + $.post('/site/create_let',pdata,function(rdata){ + showMsg(rdata.msg, function(){ + layer.close(index); + if(rdata.status){ $(".tab-nav span:first-child").click(); - },{icon:1}, 2000); - return; - } - layer.msg(rdata.msg,{icon:2,area:'500px',time:0,shade:0.3,shadeClose:true}); + } + },{icon:rdata.status?1:2}, 3000); },'json'); }); } function newAcmeSSL(siteName, id, domains){ showSpeedWindow('正在由ACME申请...', 'site.get_acme_logs', function(layers,index){ - var force = ''; + var pdata = {}; + pdata['siteName'] = siteName; + pdata['domains'] = domains; + pdata['email'] = $("input[name='admin_email']").val(); + if($("#checkDomain").prop("checked")){ - force = '&force=true'; + pdata['force'] = 'true'; } - var email = $("input[name='admin_email']").val(); - $.post('/site/create_acme','siteName='+siteName+'&domains='+domains+'&email='+email + force,function(rdata){ - layer.close(index); - if(rdata.status){ - showMsg(rdata.msg, function(){ + + if($("#wildcard_domain").prop("checked")){ + pdata['wildcard_domain'] = 'true'; + } + + var apply_type = $('input[name="apply_type"]:checked').val(); + pdata['apply_type'] = apply_type; + if (apply_type == 'dns'){ + pdata['dnspai'] = $('#dnsapi_option option:selected').val(); + } + + $.post('/site/create_acme',pdata,function(rdata){ + showMsg(rdata.msg, function(){ + layer.close(index); + if(rdata.status){ $(".tab-nav span:first-child").click(); - },{icon:1}, 2000); - return; - } - layer.msg(rdata.msg,{icon:2,area:'500px',time:0,shade:0.3,shadeClose:true}); + } + },{icon:rdata.status?1:2}, 3000); },'json'); }); } @@ -2704,28 +2890,31 @@ function siteDefaultPage(){ closeBtn: 1, shift: 0, content: '
                                \ - \ - \ - \ - \ -
                                ' + \ + \ + \ + \ + ' }); } -function changeDefault(type){ +function changeDefault(type, obj){ + $(obj).attr('disabled', true); $.post('/site/get_site_doc','type='+type, function(rdata){ - showMsg('操作成功!',function(){ - if (rdata.status){ - vhref = rdata.data.path; - onlineEditFile(0,vhref); - } - },{icon:rdata.status?1:2}); + setTimeout(function(){ + $(obj).attr('disabled', false); + },3000); + if (rdata.status){ + var path = rdata.data.path; + onlineEditFile(0,path); + } },'json'); } function getClassType(){ var select = $('.site_type > select'); $.post('/site/get_site_types',function(rdata){ + var rdata = rdata.data; $(select).html(''); $(select).append(''); for (var i = 0; i\ @@ -2756,16 +2947,16 @@ function setClassType(){ closeBtn: 1, shift: 0, content: '
                                \ -
                                \ -
                                \ -
                                \ -
                                \ - \ - \ - '+list+'\ -
                                名称操作
                                \ +
                                \ +
                                \ +
                                \
                                \ -
                                ' + \ + \ + '+list+'\ +
                                名称操作
                                \ +
                                \ +
                                ' }); },'json'); } @@ -2896,7 +3087,7 @@ function tryRestartPHP(siteName){ var phpversion = data.phpversion; if (phpversion == "00"){ - return + return; } var php_sign = 'php'; @@ -2913,11 +3104,10 @@ function tryRestartPHP(siteName){ var reqData = {name: php_sign, func:'restart'} reqData['version'] = phpversion; - // console.log(reqData); var loadT = layer.msg('尝试自动重启PHP['+phpversion+']...', { icon: 16, time: 0, shade: 0.3 }); $.post('/plugins/run', reqData, function(data) { layer.close(loadT); - layer.msg(data.msg,{icon:data.status?1:2,time:3000,shade: [0.3, '#000']}); + layer.msg( 'PHP['+phpversion+']'+(data.status?'重启成功!':'重启失败!'),{icon:data.status?1:2,time:3000,shade: [0.3, '#000']}); },'json'); },'json'); } \ No newline at end of file diff --git a/web/static/app/soft.js b/web/static/app/soft.js index eacbfcf5f..c4eafaec8 100755 --- a/web/static/app/soft.js +++ b/web/static/app/soft.js @@ -337,6 +337,7 @@ function indexListHtml(callback){ // var loadT = layer.msg('正在获取列表...', { icon: 16, time: 0, shade: [0.3, '#000'] }); $.get('/plugins/index_list', function(rdata) { + var rdata = rdata.data; // layer.close(loadT); $("#indexsoft").html(''); var con = ''; diff --git a/web/templates/default/crontab.html b/web/templates/default/crontab.html index 7284ec8ee..4765bd408 100755 --- a/web/templates/default/crontab.html +++ b/web/templates/default/crontab.html @@ -86,7 +86,7 @@
                                脚本内容 -
                                +
                                添加任务
                                @@ -103,11 +103,11 @@ - - - - - + + + + + diff --git a/web/templates/default/firewall.html b/web/templates/default/firewall.html index e0b1e5224..811df6531 100755 --- a/web/templates/default/firewall.html +++ b/web/templates/default/firewall.html @@ -13,14 +13,16 @@ color:#666; margin: 0 30px; } + .weblog span{ margin-right:5px; } + .weblog a{ color:#20a53a; } .firewall-port-box{ - margin-bottom:15px; + margin-bottom:5px; }
                                @@ -66,14 +68,13 @@
                                -
                                -
                                - 启用防火墙 -
                                - -
                                +
                                + 启用防火墙 +
                                + +
                                - +
                                @@ -92,7 +93,7 @@ - + +
                                开发模式
                                - +
                                @@ -106,14 +106,14 @@

                                绑定域名 - + 为面板绑定一个访问域名,注意:一旦绑定域名,只能通过域名访问面板

                                面板SSL - + 为面板设置https协议访问,提升面板访问安全性 @@ -129,9 +129,9 @@

                                API接口 - + - + 提供面板API接口访问的支持

                                @@ -149,11 +149,11 @@

                                - 二次验证 - + 二步验证 + - - 二次验证,加强安全登录 + + 二步验证,加强安全登录

                                @@ -164,7 +164,7 @@

                                TG机器人通知 - + Telegram Bot机器人通知【国内可能无法使用】 @@ -172,7 +172,7 @@

                                邮件通知 - + 邮件通知 diff --git a/web/templates/default/site.html b/web/templates/default/site.html index aa91828b4..29e0628c9 100755 --- a/web/templates/default/site.html +++ b/web/templates/default/site.html @@ -37,10 +37,12 @@ 域名 - 网站状态 + 网站状态 备份 网站目录 - 到期日期 + + 到期日期 + 备注 操作 @@ -55,8 +57,10 @@

                                站点分类:
                                diff --git a/web/thisdb/__init__.py b/web/thisdb/__init__.py new file mode 100644 index 000000000..41674e2d2 --- /dev/null +++ b/web/thisdb/__init__.py @@ -0,0 +1,28 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +from .init import * +from .option import * + +from .sites import * +from .site_types import * +from .backup import * + +from .domain import * +from .binding import * + +from .tasks import * +from .logs import * +from .crontab import * +from .firewall import * + +from .temp_login import * +from .user import * +from .app import * diff --git a/web/thisdb/app.py b/web/thisdb/app.py new file mode 100644 index 000000000..27fbd0e4e --- /dev/null +++ b/web/thisdb/app.py @@ -0,0 +1,64 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import core.mw as mw + +__FIELD = 'id,app_id,app_secret,white_list,status,add_time,update_time' + +def addApp(app_id,app_secret,white_list): + now_time = mw.getDateFromNow() + add_data = { + 'app_id': app_id, + 'app_secret': app_secret, + 'status': 1, + 'white_list':white_list, + 'add_time': now_time, + 'update_time': now_time + } + return mw.M('app').insert(add_data) + +def getAppList(page=1,size=10): + m = mw.M('app').field(__FIELD) + + start = (int(page) - 1) * (int(size)) + limit = str(start) + ',' +str(size) + app_list = m.limit(limit).order('id desc').select() + count = m.count() + + data = {} + data['list'] = app_list + data['count'] = count + return data + +def getAppById(aid): + return mw.M('app').field(__FIELD).where("id=?", (aid,)).find() + +def getAppByAppId(app_id): + return mw.M('app').field(__FIELD).where("app_id=?", (app_id,)).find() + +def deleteAppById(aid): + return mw.M('app').where("id=?", (aid,)).delete() + +def toggleAppStatus(aid): + info = mw.M('app').field(__FIELD).where("id=?", (aid,)).find() + if info['status'] == 1: + return mw.M('app').where('id=?',(aid,)).update({'status':0}) + return mw.M('app').where('id=?',(aid,)).update({'status':1}) + + +def setAppData(aid, status=None, app_id=None, app_secret=None): + up_data = {} + if status is not None: + up_data['status'] = status + if app_id is not None: + up_data['app_id'] = app_id + if app_secret is not None: + up_data['app_secret'] = app_secret + return mw.M('app').where('id=?',(aid,)).update(up_data) diff --git a/web/thisdb/backup.py b/web/thisdb/backup.py new file mode 100644 index 000000000..961498b82 --- /dev/null +++ b/web/thisdb/backup.py @@ -0,0 +1,44 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import core.mw as mw + +__FIELD = 'id,pid,type,name,filename,size,add_time' + +def addBackup(pid,name,filename,size,type=0): + + add_time = mw.formatDate() + insert_data = { + 'type':type, + 'name':name, + 'pid':pid, + 'filename':filename, + 'size':size, + 'add_time':add_time, + } + mw.M('backup').insert(insert_data) + return True + +def getBackupById(bp_id): + return mw.M('backup').field(__FIELD).where("id=?", (bp_id,)).find() + +def getBackupPage(site_id,page = 1,size = 10): + start = (int(page) - 1) * int(size) + limit = str(start) + ',' + str(size) + bk_list = mw.M('backup').where('pid=?', (site_id,)).field(__FIELD).limit(limit).order('id desc').select() + count = mw.M('backup').where('pid=?', (site_id,)).count() + + rdata = {} + rdata['list'] = bk_list + rdata['count'] = count + return rdata + +def deleteBackupById(bp_id): + return mw.M('backup').where("id=?", (bp_id,)).delete() diff --git a/web/thisdb/binding.py b/web/thisdb/binding.py new file mode 100644 index 000000000..689989530 --- /dev/null +++ b/web/thisdb/binding.py @@ -0,0 +1,45 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import core.mw as mw + +# 站点-子目录绑定 + +__FIELD = 'id,pid,domain,port,path,add_time' + +def getBindingCountByDomain(name): + # .debug(True) + return mw.M('binding').where("domain=?", (name,)).count() + +def addBinding(pid, domain, port, path): + now_time = mw.getDateFromNow() + insert_data = { + 'pid': pid, + 'domain': domain, + 'port':port, + 'path':path, + 'add_time': now_time, + } + return mw.M('binding').insert(insert_data) + +def getBindingListBySiteId(site_id): + # .debug(True) + binding_list = mw.M('binding').field(__FIELD).where('pid=?', (site_id,)).select() + return binding_list + +def getBindingById(site_id): + return mw.M('binding').where("id=?", (site_id,)).field(__FIELD).find() + + +def deleteBindingById(binding_id): + return mw.M('binding').where("id=?", (binding_id,)).delete() + +def deleteBindingBySiteId(site_id): + return mw.M('binding').where("pid=?", (site_id,)).delete() diff --git a/web/thisdb/crontab.py b/web/thisdb/crontab.py new file mode 100644 index 000000000..c9c3d98e0 --- /dev/null +++ b/web/thisdb/crontab.py @@ -0,0 +1,55 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os + +import core.mw as mw + +__field = 'id,name,type,where1,where_hour,where_minute,echo,status,save,backup_to,stype,sname,sbody,url_address,add_time,update_time' + +def addCrontab(data): + now_time = mw.formatDate() + data['add_time'] = now_time + data['update_time'] = now_time + return mw.M('crontab').insert(data) + +def getCronByName(name): + return mw.M('crontab').where("name=?", (name,)).find() + +def setCrontabData(cron_id, data): + mw.M('crontab').where('id=?', (cron_id,)).update(data) + return True + +def setCrontabStatus(cron_id, status): + mw.M('crontab').where('id=?', (cron_id,)).update({'status':status}) + return True + +def getCrond(id): + return mw.M('crontab').where('id=?', (id,)).field(__field).find() + +def deleteCronById(cron_id): + mw.M('crontab').where("id=?", (cron_id,)).delete() + return True + +def getCrontabList( + page = 1, + size = 10, +): + start = (int(page) - 1) * size + limit = str(start) + ',' + str(size) + + cron_list = mw.M('crontab').field(__field).limit(limit).order('id desc').select() + count = mw.M('crontab').count() + + data = {} + data['count'] = count + data['list'] = cron_list + return data + diff --git a/web/thisdb/domain.py b/web/thisdb/domain.py new file mode 100644 index 000000000..799fc9202 --- /dev/null +++ b/web/thisdb/domain.py @@ -0,0 +1,42 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import core.mw as mw + +__FIELD = 'id,pid,name,port,add_time' + +def getDomainCountByName(name): + # .debug(True) + return mw.M('domain').where("name=?", (name,)).count() + +def getDomainCountBySiteId(site_id): + # .debug(True) + return mw.M('domain').where("pid=?", (site_id,)).count() + +def addDomain(site_id, name, port): + now_time = mw.getDateFromNow() + insert_data = { + 'pid': site_id, + 'name': name, + 'port':port, + 'add_time': now_time, + } + return mw.M('domain').insert(insert_data) + +def getDomainBySiteId(site_id): + # .debug(True) + return mw.M('domain').field(__FIELD).where("pid=?", (site_id,)).select() + + +def deleteDomainId(domain_id): + return mw.M('domain').where("id=?", (domain_id,)).delete() + +def deleteDomainBySiteId(site_id): + return mw.M('domain').where("pid=?", (site_id,)).delete() diff --git a/web/thisdb/firewall.py b/web/thisdb/firewall.py new file mode 100644 index 000000000..bff32e6f2 --- /dev/null +++ b/web/thisdb/firewall.py @@ -0,0 +1,49 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import core.mw as mw + +__FIELD = 'id,port,protocol,ps,add_time,update_time' + +def getFirewallList(page=1,size=10): + start = (int(page) - 1) * (int(size)) + limit = str(start) + ',' +str(size) + + firewall_list = mw.M('firewall').field(__FIELD).limit(limit).order('id desc').select() + count = mw.M('firewall').count() + + data = {} + data['count'] = count + data['list'] = firewall_list + return data + +def addFirewall(port, protocol='tcp',ps='备注') -> bool: + ''' + 设置配置的值 + :port -> str 端口 (必填) + :protocol -> str 协议 (可选|tcp,udp,tcp/udp) + :ps -> str 备注 (可选) + ''' + now_time = mw.formatDate() + insert_data = { + 'port':port, + 'protocol':protocol, + 'ps':ps, + 'add_time':now_time, + 'update_time':now_time, + } + mw.M('firewall').insert(insert_data) + return True + + +def getFirewallCountByPort(port): + return mw.M('firewall').where('port=?',(port,)).count() + + diff --git a/web/thisdb/init.py b/web/thisdb/init.py new file mode 100644 index 000000000..b6b690b3a --- /dev/null +++ b/web/thisdb/init.py @@ -0,0 +1,28 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os + +import core.mw as mw + + +def initPanelData(): + __dbfile = mw.getPanelDataDir() + '/panel.db' + if os.path.exists(__dbfile): + return True + sql_file = mw.getPanelDir() + '/web/admin/setup/sql/default.sql' + sql = mw.M().dbPos(mw.getPanelDataDir(),'panel') + content = mw.readFile(sql_file) + csql_data = content.split(';') + for i in range(len(csql_data)): + # print(csql_data[i]) + # print(sql.execute(csql_data[i], ())) + sql.execute(csql_data[i], ()) + return True \ No newline at end of file diff --git a/web/thisdb/logs.py b/web/thisdb/logs.py new file mode 100644 index 000000000..2b1049111 --- /dev/null +++ b/web/thisdb/logs.py @@ -0,0 +1,57 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import json + + +import core.mw as mw + +def clearLog(): + mw.M('logs').where('id>?', (0,)).delete() + mw.M('logs').execute("update sqlite_sequence set seq=0 where name='logs'") + return True + +def addLog(type, log, uid = 1) -> str: + ''' + 添加日志 + :type -> str 类型 (必填) + :log -> str 日志内容 (必填) + :uid -> int 用户ID + ''' + add_time = mw.formatDate() + insert_data = { + 'type':type, + 'log':log, + 'add_time':add_time, + } + mw.M('logs').insert(insert_data) + return True + +def getLogsList(page = 1,size = 10,search = ''): + sql_where = '' + if search != '' : + sql_where = " type like '%" + search + "%' or log like '%" + search + "%' " + + field = 'id,type,log,uid,add_time' + dbM = dbC = mw.M('logs').field(field) + + if sql_where != '': + count = mw.M('logs').field(field).where(sql_where).count() + else: + count = mw.M('logs').field(field).count() + + start = (int(page) - 1) * (int(size)) + limit = str(start) + ',' +str(size) + logs_list = mw.M('logs').field(field).limit(limit).order('id desc').select() + + data = {} + data['list'] = logs_list + data['count'] = count + return data \ No newline at end of file diff --git a/web/admin/model/option.py b/web/thisdb/option.py similarity index 53% rename from web/admin/model/option.py rename to web/thisdb/option.py index 98a229217..35a32f5cc 100644 --- a/web/admin/model/option.py +++ b/web/thisdb/option.py @@ -8,68 +8,51 @@ # Author: midoks # --------------------------------------------------------------------------------- - +import os import json +import core.mw as mw -from admin.model import db, Option - -def getOption(name, - type: str | None = 'common', - default : str | None = None -) -> str: +def getOption(name,type='common',default=None) -> str: ''' 获取配置的值 :name -> str 名称 (必填) :type -> str 类型 (可选|默认common) :default -> str 默认值 (可选) ''' - data = Option.query.filter_by(name=name, type=type).first() - if data is not None: - return data.value - - if default is not None: + data = mw.M('option').field('name').where('name=? and type=?',(name, type,)).getField('value') + if data is None: return default - return '' + return data -def getOptionByJson(name, - type: str | None = 'common', - default : object | None = None -) -> object: + +def getOptionByJson(name,type='common',default=None) -> object: ''' 获取配置的值,返回对象类型 :name -> str 名称 (必填) :type -> str 类型 (可选|默认common) :default -> str 默认值 (可选) ''' - data = Option.query.filter_by(name=name, type=type).first() - if data is not None: - return json.loads(data.value) - - if default is not None: + data = mw.M('option').field('name').where('name=? and type=?',(name, type,)).getField('value') + if data is None: return default - return [] + if data is not None: + return json.loads(data) -def setOption(name, value, - type: str | None = 'common' -) -> bool: +def setOption(name, value, type = 'common') -> bool: ''' 设置配置的值 :name -> str 名称 (必填) :value -> object值 (必填) :type -> str 类型 (可选|默认common) ''' - data = Option.query.filter_by(name=name, type=type).first() - if data is None: - add_option = Option( - name=name, - type=type, - value=value) - db.session.add(add_option) - db.session.commit() - db.session.close() + + data = mw.M('option').field('name,type,value').where('name=? and type=?',(name, type,)).find() + if data is not None: + mw.M('option').field('name').where('name=? and type=?',(name, type,)).setField('value', value) return True - - db.session.query(Option).filter_by(name=name, type=type).update({"value":value}) - db.session.commit() - db.session.close() - return True \ No newline at end of file + add_option = { + 'name':name, + 'type':type, + 'value':value + } + return mw.M('option').insert(add_option) \ No newline at end of file diff --git a/web/admin/model/__init__.py b/web/thisdb/site_types.py similarity index 52% rename from web/admin/model/__init__.py rename to web/thisdb/site_types.py index 45762ecbd..54751f64d 100644 --- a/web/admin/model/__init__.py +++ b/web/thisdb/site_types.py @@ -8,20 +8,21 @@ # Author: midoks # --------------------------------------------------------------------------------- -from .initdb import * +__FIELD = 'id,name' -from .logs import addLog,clearLog +import core.mw as mw -from .option import getOption,getOptionByJson,setOption -from .option import setOption +def addSiteTypes(name): + return mw.M('site_types').add("name", (name,)) -from .sites import getSitesCount +def getSiteTypesCount(): + return mw.M('site_types').count() -from .task import addTask -from .task import getTaskCount,getTaskUnexecutedCount,getTaskList,getTaskFirstByRun -from .task import setTaskStatus,setTaskData +def getSiteTypesCountByName(name): + return mw.M('site_types').where('name=?', (name,)).count() -from .user import getUserByName,getUserById,isLoginCheck +def getSiteTypesList(): + # .debug(True) + return mw.M('site_types').field(__FIELD).order("id asc").select() -from .temp_login import getTempLoginByToken,clearTempLogin diff --git a/web/thisdb/sites.py b/web/thisdb/sites.py new file mode 100644 index 000000000..3b46f98f7 --- /dev/null +++ b/web/thisdb/sites.py @@ -0,0 +1,128 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import core.mw as mw + +from .option import getOption + +__FIELD = 'id,name,path,status,ps,edate,type_id,add_time,update_time' + +def checkSitesDomainIsExist(domain, port): + nums = mw.M('domain').where("name=? AND port=?", (domain, port,)).count() + if nums>0: + return True + + nums = mw.M('binding').where("name=? AND port=?", (domain, port,)).count() + if nums>0: + return True + return False + +def getSitesCount(): + return mw.M('sites').count() + +def getSitesById(site_id): + return mw.M('sites').field(__FIELD).where("id=?", (site_id,)).find() + +def getSitesByName(site_name): + return mw.M('sites').field(__FIELD).where("name=?", (site_name,)).find() + +def getSitesDomainById(site_id): + data = {} + domains = mw.M('domain').where('pid=?', (site_id,)).field('name,id').select() + binding = mw.M('binding').where('pid=?', (site_id,)).field('domain,id').select() + for b in binding: + t = {} + t['name'] = b['domain'] + t['id'] = b['id'] + domains.append(t) + data['domains'] = domains + data['email'] = getOption('ssl_email', default='') + return data + +def addSites(name, path): + now_time = mw.getDateFromNow() + insert_data = { + 'name': name, + 'path': path, + 'status': 1, + 'ps':name, + 'type_id':0, + 'edate':'0000-00-00', + 'add_time': now_time, + 'update_time': now_time + } + return mw.M('sites').insert(insert_data) + + +def isSitesExist(name): + if mw.M('sites').where("name=?", (name,)).count() > 0: + return True + return False + +def getSitesEdateList(edate): + elist = mw.M('sites').field(__FIELD).where('edate>? AND edate= 0 and search != '' : + sql_where = sql_where + " and type_id=" + str(type_id) + "" + if type_id != '' and int(type_id) >= 0: + sql_where = " type_id=" + str(type_id) + + + dbM = dbC = mw.M('sites').field(__FIELD) + + if sql_where != '': + count = dbC.where(sql_where).count() + else: + count = dbC.count() + + start = (int(page) - 1) * (int(size)) + limit = str(start) + ',' +str(size) + + if order is not None: + site_list = dbM.limit(limit).order(order).select() + else: + site_list = dbM.limit(limit).order('id desc').select() + + + data = {} + data['list'] = site_list + data['count'] = count + return data + + +def deleteSitesById(site_id): + return mw.M('sites').where("id=?", (site_id,)).delete() + +def setSitesData(site_id, edate = None, ps = None, path = None,status = None): + update_data = {} + if edate is not None: + update_data['edate'] = edate + if ps is not None: + update_data['ps'] = ps + + if path is not None: + update_data['path'] = path + + if status is not None: + update_data['status'] = status + + return mw.M('sites').where('id=?',(site_id,)).update(update_data) + diff --git a/web/thisdb/tasks.py b/web/thisdb/tasks.py new file mode 100644 index 000000000..7ee8ab8c5 --- /dev/null +++ b/web/thisdb/tasks.py @@ -0,0 +1,138 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import core.mw as mw + +__FIELD = 'id,name,type,start,end,cmd,status,add_time' + +def getTaskCount(status = -1) -> int: + return mw.M('tasks').where('status=?',(1,)).count() + + +# 未执行任务总数 +def getTaskUnexecutedCount() -> int: + return mw.M('tasks').where('status!=?',(1,)).count() + +def addTaskByDownload(name = '下载文件',cmd = None,type = 'download',status = 0): + ''' + 添加后台任务 + :name -> str 类型 + :cmd -> str 日志内容 (必填) + :type -> str 用户ID + ''' + if cmd is None: + return False + + add_time = mw.formatDate() + insert_data = { + 'name':name, + 'type':type, + 'cmd':cmd, + 'start':0, + 'end':0, + 'status':status, + 'add_time':add_time, + } + mw.M('tasks').insert(insert_data) + return True + +def addTask( + name = '常用任务', + cmd = None, + type = 'execshell', + status = 0, +): + ''' + 添加后台任务 + :name -> str 类型 + :cmd -> str 日志内容 (必填) + :type -> str 用户ID + ''' + if cmd is None: + return False + + add_time = mw.formatDate() + insert_data = { + 'name':name, + 'type':type, + 'cmd':cmd, + 'start':0, + 'end':0, + 'status':status, + 'add_time':add_time, + } + mw.M('tasks').insert(insert_data) + return True + + +def getTaskList( + status = 1, + page = 1, + size = 10, +): + start = (page - 1) * size + limit = str(start) + ',' + str(size) + task_list = mw.M('tasks').where('status=?', (status,)).field(__FIELD).limit(limit).order('id asc').select() + return task_list + +def getTaskPage( + status = 1, + page = 1, + size = 10, +): + start = (page - 1) * size + limit = str(start) + ',' + str(size) + task_list = mw.M('tasks').where('', ()).field(__FIELD).limit(limit).order('id desc').select() + count = mw.M('tasks').where('', ()).count() + + rdata = {} + rdata['list'] = task_list + rdata['count'] = count + return rdata + +def getTaskFirstByRun() -> None: + data = mw.M('tasks').where('status!=?', (1,)).field(__FIELD).order('id asc').find() + if data is None: + return None + return data + +def getTaskRunList(page = 1,size = 10): + start = (page - 1) * size + limit = str(start) + ',' + str(size) + task_list = mw.M('tasks').where('status!=?', (1,)).field(__FIELD).limit(limit).order('id asc').select() + return task_list + +def getTaskRunAll(): + task_list = mw.M('tasks').where('status!=?', (1,)).field(__FIELD).order('id asc').select() + return task_list + +def getTaskRunPage(page = 1, size = 10): + start = (page - 1) * size + limit = str(start) + ',' + str(size) + + task_list = mw.M('tasks').where('status!=?', (1,)).field(__FIELD).limit(limit).order('id asc').select() + count = mw.M('tasks').where('status!=?', (1,)).count() + + rdata = {} + rdata['list'] = task_list + rdata['count'] = count + return rdata + + +def setTaskStatus(task_id,status = 0): + mw.M('tasks').where('id=?',(task_id,)).update({'status':status}) + return True + +def setTaskData(id, start = None,end = None): + if start is not None: + mw.M('tasks').where('id=?',(id,)).update({'start':start}) + if end is not None: + mw.M('tasks').where('id=?',(id,)).update({'end':end}) + return True \ No newline at end of file diff --git a/web/thisdb/temp_login.py b/web/thisdb/temp_login.py new file mode 100644 index 000000000..558f2526f --- /dev/null +++ b/web/thisdb/temp_login.py @@ -0,0 +1,69 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import time + +import core.mw as mw + +__FIELD = 'id,token,salt,state,login_time,login_addr,logout_time,expire,add_time' + +def addTempLogin(token = None,expire = None): + if expire is None: + start_time = int(time.time()) + expire=start_time+3600 + + if token is None: + salt = mw.getRandomString(12) + r = mw.getRandomString(48) + token = mw.md5(r + salt) + + now_time = mw.formatDate() + insert_data = { + 'token':token, + 'salt':'0', + 'state':0, + 'login_time':0, + 'login_addr':'', + 'expire':expire, + 'add_time':now_time, + } + + return mw.M('temp_login').insert(insert_data) + +def getTempLoginPage(page = 1,size = 10): + start = (page - 1) * size + limit = str(start) + ',' + str(size) + tl_list = mw.M('temp_login').where('', ()).field(__FIELD).limit(limit).order('id desc').select() + count = mw.M('temp_login').where('', ()).count() + + rdata = {} + rdata['list'] = tl_list + rdata['count'] = count + return rdata + +def getTempLoginByToken(token, +) -> None: + ''' + 获取用户信息通过用户名 + ''' + data = mw.M('temp_login').where('token=?', (token,)).field(__FIELD).order('id asc').find() + return data + +def deleteTempLoginById(id) : + return mw.M('temp_login').where('id=?', (id,)).delete() + +def clearTempLogin()->bool: + ''' + 清空过期数据 + ''' + + now_time = int(time.time()) + mw.M('temp_login').where('expire +# --------------------------------------------------------------------------------- + +import core.mw as mw + +__field = 'id,name,password,login_ip,login_time,phone,email,add_time,update_time' + +# 初始化用户信息 +def initAdminUser(): + data = mw.M('users').field(__field).where('id=?', (1,)).find() + if data is None: + name = mw.getRandomString(8).lower() + password = mw.getRandomString(8).lower() + now_time = mw.formatDate() + login_ip = '127.0.0.1' + add_user = { + 'name':name, + 'password':mw.md5(password), + 'login_ip':login_ip, + 'login_time':now_time, + 'phone':'', + 'email':'', + 'add_time':now_time, + 'update_time':now_time + } + file_pass_pl = mw.getPanelDataDir() + '/default.pl' + mw.writeFile(file_pass_pl, password) + mw.M('users').insert(add_user) + return True + + +def getUserByName(name) -> None: + ''' + 获取用户信息通过用户名 + ''' + data = mw.M('users').field(__field).where('name=?', (name,)).find() + if data is None: + return None + row = {} + row['id'] = data['id'] + row['name'] = data['name'] + row['password'] = data['password'] + row['login_ip'] = data['login_ip'] + row['login_time'] = data['login_time'] + row['phone'] = data['phone'] + row['email'] = data['email'] + row['add_time'] = data['add_time'] + row['update_time'] = data['update_time'] + return row + +def getUserById(id) -> None: + ''' + 获取用户信息通过用户名 + ''' + data = mw.M('users').field(__field).where('id=?', (1,)).find() + if data is None: + return None + row = {} + row['id'] = data['id'] + row['name'] = data['name'] + row['password'] = data['password'] + row['login_ip'] = data['login_ip'] + row['login_time'] = data['login_time'] + row['phone'] = data['phone'] + row['email'] = data['email'] + row['add_time'] = data['add_time'] + row['update_time'] = data['update_time'] + return row + + +def getUserByRoot() -> None: + ''' + 获取用户信息通过用户名 + ''' + return getUserById(1) + +def updateUserLoginTime(): + now_time = mw.formatDate() + mw.M('users').field(__field).where('id=?', (1,)).update({'login_time':now_time}) + return True + +def setUserByName(name, new_name): + return mw.M('users').where("name=?", (name,)).setField('name', new_name.strip()) + +def setUserPwdByName(name, password): + pwd = mw.md5(password) + return mw.M('users').where("name=?", (name,)).setField('password', pwd) + +def setUserByRoot(name = None,password = None) -> bool: + ''' + 设置配置的值 + :name -> str 名称 (必填) + :value -> object值 (必填) + :type -> str 类型 (可选|默认common) + ''' + data = {} + if name is not None: + mw.M('users').where('id=?', (1,)).setField('username', name) + + if password is not None: + pwd = mw.md5(password) + mw.M('users').where('id=?', (1,)).setField('password', pwd) + + if not data: + return False + return True \ No newline at end of file diff --git a/web/utils/adult_log.py b/web/utils/adult_log.py index f1b4e4a80..d69d1ccc5 100644 --- a/web/utils/adult_log.py +++ b/web/utils/adult_log.py @@ -134,7 +134,7 @@ def parseAuditFile(log_name, result): log_list.append(_line) return log_list -def getAuditLast(self, log_name): +def getAuditLast(log_name): # 获取日志 cmd = '''LANG=en_US.UTF-8 last -n 200 -x -f {} |grep -v 127.0.0.1|grep -v " begins"'''.format( '/var/log/' + log_name) @@ -173,7 +173,7 @@ def getAuditLast(self, log_name): # lastlog_list = sorted(lastlog_list,key=lambda x:x['时间'],reverse=True) return mw.returnData(True, 'ok!', lastlog_list) -def getAuditLastLog(self): +def getAuditLastLog(): cmd = '''LANG=en_US.UTF-8 lastlog|grep -v Username''' result = mw.execShell(cmd) lastlog_list = [] diff --git a/web/admin/model/sites.py b/web/utils/cert/cert.py similarity index 85% rename from web/admin/model/sites.py rename to web/utils/cert/cert.py index dbb54064f..b69881868 100644 --- a/web/admin/model/sites.py +++ b/web/utils/cert/cert.py @@ -8,9 +8,3 @@ # Author: midoks # --------------------------------------------------------------------------------- -from admin.model import db, Sites - - -def getSitesCount(): - return Sites.query.count() - diff --git a/web/utils/config.py b/web/utils/config.py index 91bed8319..326566aac 100644 --- a/web/utils/config.py +++ b/web/utils/config.py @@ -8,12 +8,13 @@ # Author: midoks # --------------------------------------------------------------------------------- -from admin import model +import os import core.mw as mw +import thisdb def getUnauthStatus( - code: str | None = '0' + code= '0' ): code = str(code) data = {} @@ -42,26 +43,48 @@ def getGlobalVar(): 获取全局变量 ''' data = {} - data['title'] = model.getOption('title', default='后羿面板') - data['ip'] = model.getOption('server_ip', default='127.0.0.1') + data['title'] = thisdb.getOption('title', default='后羿面板') + data['ip'] = thisdb.getOption('server_ip', default='127.0.0.1') - data['site_path'] = model.getOption('site_path', default=mw.getFatherDir()+'/wwwroot') - data['backup_path'] = model.getOption('backup_path', default=mw.getFatherDir()+'/backup') - data['admin_path'] = '/'+model.getOption('admin_path', default='') - data['site_count'] = model.getSitesCount() + data['site_path'] = thisdb.getOption('site_path', default=mw.getFatherDir()+'/wwwroot') + data['backup_path'] = thisdb.getOption('backup_path', default=mw.getFatherDir()+'/backup') + data['admin_path'] = '/'+thisdb.getOption('admin_path', default='') + data['debug'] = thisdb.getOption('debug', default='close') + data['admin_close'] = thisdb.getOption('admin_close', default='no') + data['site_count'] = thisdb.getSitesCount() data['port'] = mw.getHostPort() + __file = mw.getCommonFile() + if os.path.exists(__file['ipv6']): + data['ipv6'] = 'checked' + else: + data['ipv6'] = '' + + # 获取ROOT用户名 + data['username'] = mw.M('users').where("id=?", (1,)).getField('name') + # 获取未认证状态信息 - unauthorized_status = model.getOption('unauthorized_status', default='0') + unauthorized_status = thisdb.getOption('unauthorized_status', default='0') data['unauthorized_status'] = getUnauthStatus(code=unauthorized_status) - data['basic_auth'] = model.getOptionByJson('basic_auth', default={'open':False}) + data['basic_auth'] = thisdb.getOptionByJson('basic_auth', default={'open':False}) + data['two_step_verification'] = thisdb.getOptionByJson('two_step_verification', default={'open':False}) # 服务器时间 sformat = 'date +"%Y-%m-%d %H:%M:%S %Z %z"' data['systemdate'] = mw.execShell(sformat)[0].strip() - data['hook_menu'] = model.getOptionByJson('hook_menu',type='hook',default=[]) - data['hook_global_static'] = model.getOptionByJson('hook_global_static',type='hook',default=[]) + data['hook_menu'] = thisdb.getOptionByJson('hook_menu',type='hook',default=[]) + data['hook_global_static'] = thisdb.getOptionByJson('hook_global_static',type='hook',default=[]) + data['hook_database'] = thisdb.getOptionByJson('hook_database',type='hook',default=[]) + + + # 邮件通知设置 + data['notify_email'] = thisdb.getOptionByJson('notify_email', default={'open':False}, type='notify') + data['notify_tgbot'] = thisdb.getOptionByJson('notify_tgbot', default={'open':False}, type='notify') + + data['panel_api'] = thisdb.getOptionByJson('panel_api', default={'open':False}) + data['panel_ssl'] = thisdb.getOptionByJson('panel_ssl', default={'open':False}) + data['panel_domain'] = thisdb.getOption('panel_domain', default='') return data \ No newline at end of file diff --git a/web/utils/crontab.py b/web/utils/crontab.py new file mode 100644 index 000000000..5670a327b --- /dev/null +++ b/web/utils/crontab.py @@ -0,0 +1,569 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import sys +import re +import time +import json +import threading +import multiprocessing + +import core.mw as mw +import thisdb + + +class crontab(object): + # lock + _instance_lock = threading.Lock() + + @classmethod + def instance(cls, *args, **kwargs): + if not hasattr(crontab, "_instance"): + with crontab._instance_lock: + if not hasattr(crontab, "_instance"): + crontab._instance = crontab(*args, **kwargs) + return crontab._instance + + def modifyCrond(self,cron_id,data): + if len(data['name']) < 1: + return mw.returnData(False, '任务名称不能为空!') + + is_check_pass, msg = self.cronCheck(data) + if not is_check_pass: + return mw.returnData(is_check_pass, msg) + + info = thisdb.getCrond(cron_id) + + dbdata = {} + dbdata['name'] = data['name'] + dbdata['type'] = data['type'] + dbdata['where1'] = data['where1'] + dbdata['where_hour'] = data['hour'] + dbdata['where_minute'] = data['minute'] + dbdata['save'] = data['save'] + dbdata['backup_to'] = data['backup_to'] + dbdata['sname'] = data['sname'] + dbdata['sbody'] = data['sbody'] + dbdata['stype'] = data['stype'] + dbdata['url_address'] = data['url_address'] + + if not self.removeForCrond(info['echo']): + return mw.returnData(False, '无法写入文件,是否开启了系统加固功能!') + + thisdb.setCrontabData(cron_id, dbdata) + self.syncToCrond(cron_id) + msg = '修改计划任务[' + data['name'] + ']成功' + mw.writeLog('计划任务', msg) + return mw.returnData(True, msg) + + # 取数据列表 + def getDataList(self,stype=''): + + bak_data = [] + if stype == 'site' or stype == 'sites' or stype == 'database' or stype.find('database_') > -1 or stype == 'path': + bak_data = thisdb.getOptionByJson('hook_backup',type='hook', default=[]) + + if stype == 'database' or stype.find('database_') > -1: + sqlite3_name = 'mysql' + path = mw.getServerDir() + '/mysql' + if stype != 'database': + soft_name = stype.replace('database_', '') + path = mw.getServerDir() + '/' + soft_name + + if soft_name == 'postgresql': + sqlite3_name = 'pgsql' + + if soft_name == 'mongodb': + sqlite3_name = 'mongodb' + + db_list = {} + db_list['orderOpt'] = bak_data + + if not os.path.exists(path + '/' + sqlite3_name + '.db'): + db_list['data'] = [] + else: + db_list['data'] = mw.M('databases').dbPos(path, sqlite3_name).field('name,ps').select() + return db_list + + if stype == 'path': + db_list = {} + db_list['data'] = [{"name": mw.getWwwDir(), "ps": "www"}] + db_list['orderOpt'] = bak_data + return db_list + + data = {} + data['orderOpt'] = bak_data + + default_db = 'sites' + data['data'] = mw.M(default_db).field('name,ps').select() + return data + + def setCronStatus(self,cron_id): + data = thisdb.getCrond(cron_id) + + status = 1 + status_msg = '开启' + if data['status'] == status: + status = 0 + status_msg = '关闭' + self.removeForCrond(data['echo']) + else: + data['status'] = 1 + self.syncToCrond(cron_id) + + thisdb.setCrontabStatus(cron_id, status) + + msg = '修改计划任务[' + data['name'] + ']状态为[' + str(status_msg) + ']' + mw.writeLog('计划任务', msg) + return mw.returnJson(True, msg) + + + def cronLog(self, cron_id): + data = thisdb.getCrond(cron_id) + log_file = mw.getServerDir() + '/cron/' + data['echo'] + '.log' + if not os.path.exists(log_file): + return mw.returnData(False, '当前日志为空!') + content = mw.getLastLine(log_file, 500) + return mw.returnData(True, content) + + def startTask(self, cron_id): + data = thisdb.getCrond(cron_id) + cmd_file = mw.getServerDir() + '/cron/' + data['echo'] + os.system('chmod +x ' + cmd_file) + os.system('nohup ' + cmd_file + ' >> ' + cmd_file + '.log 2>&1 &') + return mw.returnData(True, '计划任务【%s】已执行!' % data['name']) + + + # 获取指定任务数据 + def getCrondFind(self, cron_id): + return thisdb.getCrond(cron_id) + + def add(self, data): + if len(data['name']) < 1: + # 任务名称不能为空 + return -1 + + is_check_pass, msg = self.cronCheck(data) + if not is_check_pass: + return mw.returnData(is_check_pass, msg) + + cmd, title = self.getCrondCycle(data) + cron_path = mw.getServerDir() + '/cron' + cron_name = self.getShell(data) + + cmd += ' ' + cron_path + '/' + cron_name + ' >> ' + cron_path + '/' + cron_name + '.log 2>&1' + + if not mw.isAppleSystem(): + sh_data = self.writeShell(cmd) + if not sh_data['status']: + return sh_data + self.crondReload() + + add_dbdata = {} + add_dbdata['name'] = data['name'] + add_dbdata['type'] = data['type'] + add_dbdata['where1'] = data['where1'] + add_dbdata['where_hour'] = data['hour'] + add_dbdata['where_minute'] = data['minute'] + add_dbdata['save'] = data['save'] + add_dbdata['backup_to'] = data['backup_to'] + add_dbdata['sname'] = data['sname'] + add_dbdata['sbody'] = data['sbody'] + add_dbdata['stype'] = data['stype'] + add_dbdata['echo'] = cron_name + add_dbdata['url_address'] = data['url_address'] + + tid = thisdb.addCrontab(add_dbdata) + return tid + + def delete(self, tid): + data = thisdb.getCrond(tid) + if not self.removeForCrond(data['echo']): + return mw.returnData(False, '无法写入文件,是否开启了系统加固功能!') + + cron_path = mw.getServerDir() + '/cron' + cron_file = cron_path + '/' + data['echo'] + + if os.path.exists(cron_file): + os.remove(cron_file) + cron_file = cron_path + '/' + data['echo'] + '.log' + if os.path.exists(cron_file): + os.remove(cron_file) + + thisdb.deleteCronById(tid) + msg = mw.getInfo('删除计划任务[{1}]成功!', (data['name'],)) + mw.writeLog('计划任务', msg) + return mw.returnData(True, msg) + + + def delLogs(self,cron_id): + try: + data = thisdb.getCrond(cron_id) + log_file = mw.getServerDir() + '/cron/' + data['echo'] + '.log' + if os.path.exists(log_file): + os.remove(log_file) + return mw.returnData(True, '任务日志已清空!') + except: + return mw.returnData(False, '任务日志清空失败!') + + def getCrontabHuman(self, data): + rdata = [] + for i in range(len(data)): + t = data[i] + if t['type'] == "day": + t['type'] = '每天' + t['cycle'] = mw.getInfo('每天, {1}点{2}分 执行', (str(t['where_hour']), str(t['where_minute']))) + elif t['type'] == "day-n": + t['type'] = mw.getInfo('每{1}天', (str(t['where1']),)) + t['cycle'] = mw.getInfo('每隔{1}天, {2}点{3}分 执行', (str(t['where1']), str(t['where_hour']), str(t['where_minute']))) + elif t['type'] == "hour": + t['type'] = '每小时' + t['cycle'] = mw.getInfo('每小时, 第{1}分钟 执行', (str(t['where_minute']),)) + elif t['type'] == "hour-n": + t['type'] = mw.getInfo('每{1}小时', (str(t['where1']),)) + t['cycle'] = mw.getInfo('每{1}小时, 第{2}分钟 执行', (str(t['where1']), str(t['where_minute']))) + elif t['type'] == "minute-n": + t['type'] = mw.getInfo('每{1}分钟', (str(t['where1']),)) + t['cycle'] = mw.getInfo('每隔{1}分钟执行', (str(t['where1']),)) + elif t['type'] == "week": + t['type'] = '每周' + if not t['where1']: + t['where1'] = '0' + t['cycle'] = mw.getInfo('每周{1}, {2}点{3}分执行', (self.toWeek(int(t['where1'])), str(t['where_hour']), str(t['where_minute']))) + elif t['type'] == "month": + t['type'] = '每月' + t['cycle'] = mw.getInfo('每月, {1}日 {2}点{3}分执行', (str(t['where1']), str(t['where_hour']), str(t['where_minute']))) + rdata.append(t) + return rdata + + # 从crond删除 + def removeForCrond(self, echo): + if mw.isAppleSystem(): + return True + + cron_file = [ + '/var/spool/cron/crontabs/root', + '/var/spool/cron/root', + ] + + file = '' + for i in cron_file: + if os.path.exists(i): + file = i + + + if file == '': + return False + + content = mw.readFile(file) + rep = ".+" + str(echo) + ".+\n" + content = re.sub(rep, "", content) + if not mw.writeFile(file, content): + return False + self.crondReload() + return True + + def getCrontabList(self, + page = 1, + size = 10 + ): + info = thisdb.getCrontabList(page=int(page),size=int(size)) + + rdata = {} + rdata['data'] = self.getCrontabHuman(info['list']) + rdata['page'] = mw.getPage({'count':info['count'],'tojs':'getCronData','p':page,'row':size}) + + + # backup hook + rdata['backup_hook'] = thisdb.getOptionByJson('hook_backup', type='hook', default=[]) + return rdata + + def getCrondCycle(self, params): + cron_cmd = '' + title = '' + if params['type'] == "day": + cron_cmd = self.getDay(params) + title = '每天' + elif params['type'] == "day-n": + cron_cmd = self.getDay_N(params) + title = mw.getInfo('每{1}天', (params['where1'],)) + elif params['type'] == "hour": + cron_cmd = self.getHour(params) + title = '每小时' + elif params['type'] == "hour-n": + cron_cmd = self.getHour_N(params) + title = '每小时' + elif params['type'] == "minute-n": + cron_cmd = self.minute_N(params) + elif params['type'] == "week": + params['where1'] = params['week'] + cron_cmd = self.week(params) + elif params['type'] == "month": + cron_cmd = self.month(params) + return cron_cmd, title + + # 转换大写星期 + def toWeek(self, num): + wheres = { + 0: '日', + 1: '一', + 2: '二', + 3: '三', + 4: '四', + 5: '五', + 6: '六' + } + try: + return wheres[num] + except: + return '' + + # 取任务构造Day + def getDay(self, param): + cmd = "{0} {1} * * * ".format(param['minute'], param['hour']) + return cmd + + # 取任务构造Day_n + def getDay_N(self, param): + cmd = "{0} {1} */{2} * * ".format(param['minute'], param['hour'], param['where1']) + return cmd + + # 取任务构造Hour + def getHour(self, param): + cmd = "{0} * * * * ".format(param['minute']) + return cmd + + # 取任务构造Hour-N + def getHour_N(self, param): + cmd = "{0} */{1} * * * ".format(param['minute'], param['where1']) + return cmd + + # 取任务构造Minute-N + def minute_N(self, param): + cmd = "*/{0} * * * * ".format(param['where1']) + return cmd + + # 取任务构造week + def week(self, param): + cmd = "{0} {1} * * {2}".format(param['minute'], param['hour'], param['week']) + return cmd + + # 取任务构造Month + def month(self, param): + cmd = "{0} {1} {2} * * ".format(param['minute'], param['hour'], param['where1']) + return cmd + + # 参数校验 + def cronCheck(self, params): + if params['stype'] == 'site' or params['stype'] == 'database' or params['stype'].find('database_') > -1 or params['stype'] == 'logs' or params['stype'] == 'path': + if params['save'] == '': + return False, '保留份数不能为空!' + + if params['type'] == 'day': + if params['hour'] == '': + return False, '小时不能为空!' + if params['minute'] == '': + return False, '分钟不能为空!' + + if params['type'] == 'day-n': + if params['where1'] == '': + return False, '天不能为空!' + if params['hour'] == '': + return False, '小时不能为空!' + if params['minute'] == '': + return False, '分钟不能为空!' + if params['type'] == 'hour': + if params['minute'] == '': + return False, '分钟不能为空!' + + if params['type'] == 'hour-n': + if params['where1'] == '': + return False, '小时不能为空!' + if params['minute'] == '': + return False, '分钟不能为空!' + + if params['type'] == 'minute-n': + if params['where1'] == '': + return False, '分钟不能为空!' + + if params['type'] == 'week': + if params['hour'] == '': + return False, '小时不能为空!' + if params['minute'] == '': + return False, '分钟不能为空!' + + if params['type'] == 'month': + if params['where1'] == '': + return False, '日不能为空!' + if params['hour'] == '': + return False, '小时不能为空!' + if params['minute'] == '': + return False, '分钟不能为空!' + return True, 'OK' + + + # 取执行脚本 + def getShell(self, param): + # try: + stype = param['stype'] + if stype == 'toFile': + shell = param.sFile + else: + head = "#!/bin/bash\nPATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin\nexport PATH\n" + start_head = ''' +SCRIPT_RUN_TIME="0s" +MW_ToSeconds() +{ + SEC=$1 + if [ $SEC -lt 60 ]; then + SCRIPT_RUN_TIME="${SEC}s" + elif [ $SEC -ge 60 ] && [ $SEC -lt 3600 ];then + SCRIPT_RUN_TIME="$(( SEC / 60 ))m$(( SEC % 60 ))s" + elif [ $SEC -ge 3600 ]; then + SCRIPT_RUN_TIME="$(( SEC / 3600 ))h$(( (SEC % 3600) / 60 ))m$(( (SEC % 3600) % 60 ))s" + fi +} +START_MW_SHELL_TIME=`date +%s` +''' + + source_bin_activate = ''' +export LANG=en_US.UTF-8 +MW_PATH=%s/bin/activate +if [ -f $MW_PATH ];then + source $MW_PATH +fi''' % (mw.getPanelDir(),) + + head = head + start_head + source_bin_activate + "\n" + log = '.log' + + #所有 + if param['sname'] == 'ALL': + log = '' + + script_dir = mw.getPanelDir() + "/scripts" + source_stype = 'database' + if stype.find('database_') > -1: + plugin_name = stype.replace('database_', '') + script_dir = mw.getPanelDir() + "/plugins/" + plugin_name + "/scripts" + + source_stype = stype + stype = 'database' + + wheres = { + 'path': head + "python3 " + script_dir + "/backup.py path " + param['sname'] + " " + str(param['save']), + 'site': head + "python3 " + script_dir + "/backup.py site " + param['sname'] + " " + str(param['save']), + 'database': head + "python3 " + script_dir + "/backup.py database " + param['sname'] + " " + str(param['save']), + 'logs': head + "python3 " + script_dir + "/logs_backup.py " + param['sname'] + log + " " + str(param['save']), + 'rememory': head + "/bin/bash " + script_dir + '/rememory.sh' + } + if param['backup_to'] != 'localhost': + cfile = mw.getPluginDir() + "/" + param['backup_to'] + "/index.py" + + wheres['path'] = head + "python3 " + cfile + " path " + param['sname'] + " " + str(param['save']) + wheres['site'] = head + "python3 " + cfile + " site " + param['sname'] + " " + str(param['save']) + wheres['database'] = head + "python3 " + cfile + " " + source_stype + " " + param['sname'] + " " + str(param['save']) + try: + shell = wheres[stype] + except: + if stype == 'toUrl': + shell = head + "curl -sS --connect-timeout 10 -m 60 '" + param['urladdress'] + "'" + else: + shell = head + param['sbody'].replace("\r\n", "\n") + + shell += ''' +echo "----------------------------------------------------------------------------" +endDate=`date +"%Y-%m-%d %H:%M:%S"` +END_MW_SHELL_TIME=`date +"%s"` +((SHELL_COS_TIME=($END_MW_SHELL_TIME-$START_MW_SHELL_TIME))) +MW_ToSeconds $SHELL_COS_TIME +echo "★[$endDate] Successful | Script Run [$SCRIPT_RUN_TIME] " +echo "----------------------------------------------------------------------------" +''' + cron_path = mw.getServerDir() + '/cron' + if not os.path.exists(cron_path): + mw.execShell('mkdir -p ' + cron_path) + + if not 'echo' in param: + cron_name = mw.md5(mw.md5(str(time.time()) + '_mw')) + else: + cron_name = param['echo'] + file = cron_path + '/' + cron_name + mw.writeFile(file, self.checkScript(shell)) + mw.execShell('chmod 750 ' + file) + return cron_name + + # 检查脚本 + def checkScript(self, shell): + keys = ['shutdown', 'init 0', 'mkfs', 'passwd', + 'chpasswd', '--stdin', 'mkfs.ext', 'mke2fs'] + for k in keys: + shell = shell.replace(k, '[***]') + return shell + + # 将Shell脚本写到文件 + def writeShell(self, bash_script): + if mw.isAppleSystem(): + return mw.returnData(True, 'ok') + file = '/var/spool/cron/crontabs/root' + sys_os = mw.getOs() + if sys_os == 'darwin': + file = '/etc/crontab' + elif sys_os.startswith("freebsd"): + file = '/var/cron/tabs/root' + + if not os.path.exists(file): + file = '/var/spool/cron/root' + + if not os.path.exists(file): + mw.writeFile(file, '') + + content = mw.readFile(file) + content += str(bash_script) + "\n" + if mw.writeFile(file, content): + if not os.path.exists(file): + mw.execShell("chmod 600 '" + file +"' && chown root.root " + file) + else: + mw.execShell("chmod 600 '" + file +"' && chown root.crontab " + file) + return mw.returnData(True, 'ok') + return mw.returnData(False, '文件写入失败,是否开启系统加固功能!') + + # 重载配置 + def crondReload(self): + if mw.isAppleSystem(): + if os.path.exists('/etc/crontab'): + pass + else: + if os.path.exists('/etc/init.d/crond'): + mw.execShell('/etc/init.d/crond reload') + elif os.path.exists('/etc/init.d/cron'): + mw.execShell('service cron restart') + else: + mw.execShell("systemctl reload crond") + + def syncToCrond(self, cron_id): + info = thisdb.getCrond(cron_id) + if 'status' in info: + if info['status'] == 0: + return False + + if 'where_hour' in info: + info['hour'] = info['where_hour'] + info['minute'] = info['where_minute'] + info['week'] = info['where1'] + + cmd, _ = self.getCrondCycle(info) + cron_path = mw.getServerDir() + '/cron' + cron_name = self.getShell(info) + cmd += ' ' + cron_path + '/' + cron_name + ' >> ' + cron_path + '/' + cron_name + '.log 2>&1' + self.writeShell(cmd) + self.crondReload() + return True + diff --git a/class/plugin/memail.py b/web/utils/email.py similarity index 71% rename from class/plugin/memail.py rename to web/utils/email.py index 6ba7b62d5..9112622cb 100644 --- a/class/plugin/memail.py +++ b/web/utils/email.py @@ -1,4 +1,13 @@ -# coding: utf-8 +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + import smtplib diff --git a/web/utils/enhanced_log_rotation.py b/web/utils/enhanced_log_rotation.py new file mode 100644 index 000000000..1dfa449c0 --- /dev/null +++ b/web/utils/enhanced_log_rotation.py @@ -0,0 +1,61 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + + +import re +from logging import handlers + + +class EnhancedRotatingFileHandler(handlers.TimedRotatingFileHandler, + handlers.RotatingFileHandler): + """ + Handler for logging to a set of files, which switches from one file + to the next when the current file reaches a certain size, or at certain + timed intervals + @filename - log file name + @max_bytes - file size in bytes to rotate file + @interval - Duration to rotate file + @backup_count - Maximum number of files to retain + @encoding - file encoding + @when - 'when' events supported: + # S - Seconds + # M - Minutes + # H - Hours + # D - Days + # midnight - roll over at midnight + # W{0-6} - roll over on a certain day; 0 - Monday + Here we are defaulting rotation with minutes interval + """ + def __init__(self, filename, max_bytes=1, interval=60, backup_count=0, + encoding=None, when='M'): + max_bytes = max_bytes * 1024 * 1024 + handlers.TimedRotatingFileHandler.__init__(self, filename=filename, + when=when, + interval=interval, + backupCount=backup_count, + encoding=encoding) + + handlers.RotatingFileHandler.__init__(self, filename=filename, + mode='a', + maxBytes=max_bytes, + backupCount=backup_count, + encoding=encoding) + + # Time & Size combined rollover + def shouldRollover(self, record): + return handlers.TimedRotatingFileHandler.shouldRollover(self, record) \ + or handlers.RotatingFileHandler.shouldRollover(self, record) + + # Roll overs current file + def doRollover(self): + self.suffix = "%Y-%m-%d_%H-%M-%S" + self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(\.\w+)?$" + self.extMatch = re.compile(self.extMatch, re.ASCII) + handlers.TimedRotatingFileHandler.doRollover(self) \ No newline at end of file diff --git a/web/utils/file.py b/web/utils/file.py index 4638252ef..42244ba48 100644 --- a/web/utils/file.py +++ b/web/utils/file.py @@ -11,10 +11,408 @@ import os import pwd import time - -from admin import model +import shutil +import json +import base64 import core.mw as mw +import thisdb + +def uploadSegment(path,name,size,start,dir_mode,file_mode,b64_data,upload_files): + if not mw.fileNameCheck(name): + return mw.returnData(False, '文件名中不能包含特殊字符!') + + if path == '/': + return mw.returnData(False, '不能直接上传文件到系统根目录!') + + if name.find('./') != -1 or path.find('./') != -1: + return mw.returnData(False, '错误的参数') + + if not os.path.exists(path): + os.makedirs(path, 493) + if not dir_mode != '' or not file_mode != '': + mw.setMode(path) + + save_path = os.path.join(path, name + '.' + str(int(size)) + '.upload.tmp') + d_size = 0 + if os.path.exists(save_path): + d_size = os.path.getsize(save_path) + + if d_size != int(start): + return mw.returnData(True, 'size', d_size) + + f = open(save_path, 'ab') + if b64_data == '1': + b64_data = base64.b64decode(b64_data) + f.write(b64_data) + else: + for tmp_f in upload_files: + f.write(tmp_f.read()) + + f.close() + f_size = os.path.getsize(save_path) + if f_size != int(size): + return mw.returnData(True, 'size', f_size) + + new_name = os.path.join(path, name) + if os.path.exists(new_name): + if new_name.find('.user.ini') != -1: + mw.execShell("chattr -i " + new_name) + try: + os.remove(new_name) + except: + mw.execShell("rm -f %s" % new_name) + + os.renames(save_path, new_name) + + if dir_mode != '' and dir_mode != '': + mode_tmp1 = dir_mode.split(',') + mw.setMode(path, mode_tmp1[0]) + mw.setOwn(path, mode_tmp1[1]) + mode_tmp2 = file_mode.split(',') + mw.setMode(new_name, mode_tmp2[0]) + mw.setOwn(new_name, mode_tmp2[1]) + else: + setMode(new_name) + + msg = mw.getInfo('上传文件[{1}] 到 [{2}]成功!', (new_name, path)) + mw.writeLog('文件管理', msg) + return mw.returnData(True, '上传成功!', f_size) + + +def mvFile(sfile, dfile): + if not checkFileName(dfile): + return mw.returnData(False, '文件名中不能包含特殊字符!') + if not os.path.exists(sfile): + return mw.returnData(False, '指定文件不存在!') + + if not checkDir(sfile): + return mw.returnData(False, 'FILE_DANGER') + + try: + shutil.move(sfile, dfile) + msg = mw.getInfo('移动或重名命文件[{1}]到[{2}]成功!', (sfile, dfile,)) + mw.writeLog('文件管理', msg) + return mw.returnData(True, '移动或重名命文件成功!') + except: + return mw.returnData(False, '移动或重名命文件失败!') + +def uncompress(sfile, dfile, path): + if not os.path.exists(sfile): + return mw.returnData(False, '指定文件不存在!') + + filename = os.path.basename(sfile) + extension = os.path.splitext(filename)[-1] + extension = extension.strip('.') + + tar_gz = 'tar.gz' + tar_gz_len = len(tar_gz) + suffix_gz = sfile[-tar_gz_len:] + if suffix_gz == tar_gz: + extension = suffix_gz + + if not extension in ['tar.gz', 'gz', 'zip', 'rar']: + return mw.returnData(False, '现在仅支持gz,zip,rar格式解压!') + + if mw.isAppleSystem() and extension == 'rar': + return mw.returnData(False, 'macosx暂时不支持rar格式解压') + + cmd = "cd " + path + " " + try: + tmps = mw.getPanelDir() + '/logs/panel_exec.log' + if extension == 'zip': + cmd += "&& unzip -o -d '" + dfile + "' '" + sfile + "' > " + tmps + " 2>&1 &" + mw.execShell(cmd) + if extension == 'tar.gz': + cmd += "&& tar -zxvf " + sfile + " -C " + dfile + " > " + tmps + " 2>&1 &" + mw.execShell(cmd) + if extension == 'gz': + cmd += "&& gunzip -k " + sfile + " > " + tmps + " 2>&1 &" + mw.execShell(cmd) + if extension == 'rar': + cmd += "&& unrar x " + sfile + " " + dfile + " > " + tmps + " 2>&1 &" + mw.execShell(cmd) + + if os.path.exists(dfile): + setFileAccept(dfile) + mw.writeLog("文件管理", '文件[{1}]解压[{2}]成功!', (sfile, dfile,)) + return mw.returnData(True, '文件解压成功!') + except Exception as e: + return mw.returnData(False, '文件解压失败!:' + str(e)) + +def setBatchData(path, stype, access, user, data): + from admin import session + if stype == '1' or stype == '2': + session['selected'] = { + 'path': path, + 'type': stype, + 'access': access, + 'user': user, + 'data': data + } + return mw.returnData(True, '标记成功,请在目标目录点击粘贴所有按钮!') + elif stype == '3': + for key in json.loads(data): + try: + filename = path + '/' + key + if not checkDir(filename): + return mw.returnData(False, 'FILE_DANGER') + os.system('chmod -R ' + access + " '" + filename + "'") + os.system('chown -R ' + user + ':' + user + " '" + filename + "'") + except: + continue + mw.writeLog('文件管理', '批量设置权限成功!') + return mw.returnData(True, '批量设置权限成功!') + else: + recycle_bin = thisdb.getOption('recycle_bin') + is_recycle = False + if recycle_bin == 'open': + is_recycle = True + data = json.loads(data) + l = len(data) + i = 0 + for key in data: + try: + filename = path + '/' + key + topath = filename + if not os.path.exists(filename): + continue + + i += 1 + mw.writeSpeed(key, i, l) + if os.path.isdir(filename): + if not checkDir(filename): + return mw.returnData(False, '请不要花样作死!') + if is_recycle: + mvRecycleBin(topath) + else: + shutil.rmtree(filename) + else: + if key == '.user.ini': + os.system('which chattr && chattr -i ' + filename) + if is_recycle: + mvRecycleBin(topath) + else: + os.remove(filename) + except: + continue + mw.writeSpeed(None, 0, 0) + mw.writeLog('文件管理', '批量删除成功!') + return mw.returnData(True, '批量删除成功!') + +def batchPaste(path, stype): + from admin import session + if not checkDir(path): + return mw.returnData(False, '请不要花样作死!') + i = 0 + myfiles = json.loads(session['selected']['data']) + l = len(myfiles) + if stype == '1': + for key in myfiles: + i += 1 + mw.writeSpeed(key, i, l) + try: + + sfile = session['selected'][ + 'path'] + '/' + key + dfile = path + '/' + key + + if os.path.isdir(sfile): + shutil.copytree(sfile, dfile) + else: + shutil.copyfile(sfile, dfile) + stat = os.stat(sfile) + os.chown(dfile, stat.st_uid, stat.st_gid) + except: + continue + msg = mw.getInfo('从[{1}]批量复制到[{2}]成功',(session['selected']['path'], path,)) + mw.writeLog('文件管理', msg) + else: + for key in myfiles: + try: + i += 1 + mw.writeSpeed(key, i, l) + + sfile = session['selected'][ + 'path'] + '/' + key + dfile = path + '/' + key + + shutil.move(sfile, dfile) + except: + continue + msg = mw.getInfo('从[{1}]批量移动到[{2}]成功',(session['selected']['path'], path,)) + mw.writeLog('文件管理', msg) + mw.writeSpeed(None, 0, 0) + errorCount = len(myfiles) - i + del(session['selected']) + msg = mw.getInfo('批量操作成功[{1}],失败[{2}]', (str(i), str(errorCount))) + return mw.returnData(True, msg) + + +def zip(sfile, dfile, stype, path): + if sfile.find(',') == -1: + if not os.path.exists(path + '/' + sfile): + return mw.returnData(False, '指定文件不存在!') + + try: + tmps = mw.getPanelDir() + '/logs/panel_exec.log' + if stype == 'zip': + mw.execShell("cd '" + path + "' && zip '" + dfile + + "' -r '" + sfile + "' > " + tmps + " 2>&1") + else: + sfiles = '' + for sfile in sfile.split(','): + if not sfile: + continue + sfiles += " '" + sfile + "'" + mw.execShell("cd '" + path + "' && tar -zcvf '" + dfile + "' " + sfiles + " > " + tmps + " 2>&1") + setFileAccept(dfile) + mw.writeLog("文件管理", '文件[{1}]压缩[{2}]成功!', (sfile, dfile)) + return mw.returnData(True, '文件压缩成功!') + except: + return mw.returnData(False, '文件压缩失败!') + +def unzip(sfile, dfile, stype, path): + if not os.path.exists(sfile): + return mw.returnData(False, '指定文件不存在!') + + try: + tmps = mw.getPanelDir() + '/logs/panel_exec.log' + if stype == 'zip': + mw.execShell("cd " + path + " && unzip -o -d '" + dfile + "' '" + sfile + "' > " + tmps + " 2>&1 &") + else: + sfiles = '' + for sfile in sfile.split(','): + if not sfile: + continue + sfiles += " '" + sfile + "'" + mw.execShell("cd " + path + " && tar -zxvf " + sfiles + " -C " + dfile + " > " + tmps + " 2>&1 &") + + setFileAccept(dfile) + mw.writeLog("文件管理", '文件[{1}]解压[{2}]成功!', (sfile, dfile)) + return mw.returnData(True, '文件解压成功!') + except: + return mw.returnData(False, '文件解压失败!') + +def getAccess(filename): + data = {} + try: + stat = os.stat(filename) + data['chmod'] = str(oct(stat.st_mode)[-3:]) + data['chown'] = pwd.getpwuid(stat.st_uid).pw_name + except: + data['chmod'] = 755 + data['chown'] = 'www' + return data + +def copyDir(src_file, dst_file): + if not os.path.exists(src_file): + return mw.returnData(False, '指定目录不存在!') + + if os.path.exists(dst_file): + return mw.returnData(False, '指定目录已存在!') + + try: + shutil.copytree(src_file, dst_file) + stat = os.stat(src_file) + os.chown(dst_file, stat.st_uid, stat.st_gid) + msg = mw.getInfo('复制目录[{1}]到[{2}]成功!', (src_file, dst_file)) + mw.writeLog('文件管理', msg) + return mw.returnData(True, '目录复制成功!') + except: + return mw.returnData(False, '目录复制失败!') + +def copyFile(src_file, dst_file): + if src_file == dst_file: + return mw.returnJson(False, '源与目的一致!') + + if not os.path.exists(src_file): + return mw.returnJson(False, '指定文件不存在!') + + if os.path.isdir(src_file): + return copyDir(src_file, dst_file) + + try: + shutil.copyfile(src_file, dst_file) + msg = mw.getInfo('复制文件[{1}]到[{2}]成功!', (src_file, dst_file,)) + mw.writeLog('文件管理', msg) + stat = os.stat(src_file) + os.chown(dst_file, stat.st_uid, stat.st_gid) + return mw.returnData(True, '文件复制成功!') + except: + return mw.returnData(False, '文件复制失败!') + +def setFileAccept(filename): + auth = 'www:www' + if mw.getOs() == 'darwin': + user = mw.execShell("who | sed -n '2, 1p' |awk '{print $1}'")[0].strip() + auth = user + ':staff' + os.system('chown -R ' + auth + ' ' + filename) + os.system('chmod -R 755 ' + filename) + +def createFile(file_path): + try: + if not checkFileName(file_path): + return mw.returnData(False, '文件名中不能包含特殊字符!') + if os.path.exists(file_path): + return mw.returnData(False, '指定文件已存在!') + _path = os.path.dirname(file_path) + if not os.path.exists(_path): + os.makedirs(_path) + open(file_path, 'w+').close() + setFileAccept(file) + msg = mw.getInfo('创建文件[{1}]成功!', (file_path,)) + mw.writeLog('文件管理', msg) + return mw.returnData(True, '文件创建成功!') + except Exception as e: + return mw.returnData(True, '文件创建失败:'+str(e)) + +def createDir(path): + try: + if not checkFileName(path): + return mw.returnData(False, '目录名中不能包含特殊字符!') + if os.path.exists(path): + return mw.returnData(False, '指定目录已存在!') + os.makedirs(path) + setFileAccept(path) + msg = mw.getInfo('创建目录[{1}]成功!', (path,)) + mw.writeLog('文件管理', msg) + return mw.returnData(True, '目录创建成功!') + except Exception as e: + print(e) + return mw.returnData(False, '目录创建失败!') + +# 检查敏感目录 +def checkDir(path): + path = path.replace('//', '/') + if path[-1:] == '/': + path = path[:-1] + + sense_dir = ('', + '/', + '/*', + '/www', + '/root', + '/boot', + '/bin', + '/etc', + '/home', + '/dev', + '/sbin', + '/var', + '/usr', + '/tmp', + '/sys', + '/proc', + '/media', + '/mnt', + '/opt', + '/lib', + '/srv', + '/selinux', + '/www/server', + mw.getRootDir()) + return not path in sense_dir def getFileBody(path): if not os.path.exists(path): @@ -46,36 +444,24 @@ def getFileBody(path): return mw.returnData(False, '文件未正常打开!') return mw.returnData(True, 'OK', data) -# 获取文件权限描述 -def getFileStatsDesc( - filename: str | None = None, - path: str | None = None, -): - if path is None or filename is None: - return ';;;;;' - filename = filename.replace('//', '/') +def saveBody(path, data, encoding): + if not os.path.exists(path): + return mw.returnData(False, '文件不存在') try: - stat = os.stat(filename) - accept = str(oct(stat.st_mode)[-3:]) - mtime = str(int(stat.st_mtime)) - user = '' - try: - user = str(pwd.getpwuid(stat.st_uid).pw_name) - except: - user = str(stat.st_uid) - size = str(stat.st_size) - link = '' - if os.path.islink(filename): - link = ' -> ' + os.readlink(filename) + if encoding == 'ascii': + encoding = 'utf-8' - if path: - path_t = (path + '/').replace('//', '/') - filename = filename.replace(path_t, '', 1) + data = data.encode(encoding, errors='ignore').decode(encoding) + fp = open(path, 'w+', encoding=encoding) + fp.write(data) + fp.close() - return filename + ';' + size + ';' + mtime + ';' + accept + ';' + user + ';' + link - except Exception as e: - print(str(e)) - return ';;;;;' + if path.find("web_conf") > 0: + mw.restartWeb() + mw.writeLog('文件管理', '文件[{1}]保存成功', (path,)) + return mw.returnData(True, '文件保存成功') + except Exception as ex: + return mw.returnData(False, '文件保存错误:' + str(ex)) def sortFileList(path, ftype = 'mtime', sort = 'desc'): @@ -165,7 +551,7 @@ def getAllDirList(path, page=1, size=10, order = '', search=None): for dst_file in plist: if not os.path.exists(dst_file): continue - stat = getFileStatsDesc(dst_file, path) + stat = mw.getFileStatsDesc(dst_file, path) if os.path.isdir(dst_file): dirnames.append(stat) else: @@ -213,7 +599,7 @@ def getDirList(path, page=1, size=10, order = '', search=None): if not os.path.exists(abs_file): continue - stats = getFileStatsDesc(abs_file, path) + stats = mw.getFileStatsDesc(abs_file, path) if os.path.isdir(abs_file): dirnames.append(stats) else: @@ -247,7 +633,7 @@ def getDirSize(filePath, size=0): # 获取目录大小(bash) def getDirSizeByBash(path): tmp = mw.execShell('du -sh ' + path) - return tmp[0].split()[0] + return tmp[0].split()[0].lower() # 计算文件数量 def getCount(path, search = None): @@ -274,6 +660,37 @@ def getAccess(fname): data['chown'] = 'www' return data +def setFileAccess(filename,user,access): + sall = '-R' + try: + if not checkDir(filename): + return mw.returnData(False, '请不要花样作死') + + if not os.path.exists(filename): + return mw.returnData(False, '指定文件不存在!') + + os.system('chmod ' + sall + ' ' + access + " '" + filename + "'") + os.system('chown ' + sall + ' ' + user + ':' + user + " '" + filename + "'") + msg = mw.getInfo('设置[{1}]权限为[{2}]所有者为[{3}]', (filename, access, user,)) + mw.writeLog('文件管理', msg) + return mw.returnData(True, '设置成功!') + except Exception as e: + return mw.returnData(False, '设置失败!'+str(e)) + +def getSysUserList(): + pwd_file = '/etc/passwd' + if os.path.exists(pwd_file): + content = mw.readFile(pwd_file) + clist = content.split('\n') + sys_users = [] + for line in clist: + if line.find(":")<0: + continue + lines = line.split(":",1) + sys_users.append(lines[0]) + return sys_users + return ['root','mysql','www'] + def fileDelete(path): if not os.path.exists(path): return mw.returnData(False, '指定文件不存在!') @@ -284,7 +701,7 @@ def fileDelete(path): mw.execShell(cmd) try: - recycle_bin = model.getOption('recycle_bin') + recycle_bin = thisdb.getOption('recycle_bin') if recycle_bin == 'open': if mvRecycleBin(path): return mw.returnData(True, '已将文件移动到回收站!') @@ -303,7 +720,7 @@ def dirDelete(path): if path.find('.user.ini'): os.system("which chattr && chattr -i '" + path + "'") try: - recycle_bin = model.getOption('recycle_bin') + recycle_bin = thisdb.getOption('recycle_bin') if recycle_bin == 'open': if mvRecycleBin(path): return mw.returnData(True, '已将文件移动到回收站!') @@ -315,19 +732,19 @@ def dirDelete(path): # 关闭 def toggleRecycleBin(): - recycle_bin = model.getOption('recycle_bin') + recycle_bin = thisdb.getOption('recycle_bin') if recycle_bin == 'open': - model.setOption('recycle_bin','close') + thisdb.setOption('recycle_bin','close') mw.writeLog('文件管理', '已关闭回收站功能!') return mw.returnData(True, '已关闭回收站功能!') else: - model.setOption('recycle_bin','open') + thisdb.setOption('recycle_bin','open') mw.writeLog('文件管理', '已开启回收站功能!') return mw.returnData(True, '已开启回收站功能!') def getRecycleBin(): rb_dir = mw.getRecycleBinDir() - recycle_bin = model.getOption('recycle_bin') + recycle_bin = thisdb.getOption('recycle_bin') data = {} data['dirs'] = [] @@ -392,9 +809,39 @@ def reRecycleBin(path): mw.writeLog('文件管理', msg) return mw.returnData(False, '恢复失败!') + +def closeRecycleBin(): + rb_dir = mw.getRecycleBinDir() + mw.execShell('which chattr && chattr -R -i ' + rb_dir) + rlist = os.listdir(rb_dir) + i = 0 + l = len(rlist) + for name in rlist: + i += 1 + path = rb_dir + '/' + name + mw.writeSpeed(name, i, l) + if os.path.isdir(path): + shutil.rmtree(path) + else: + os.remove(path) + mw.writeSpeed(None, 0, 0) + mw.writeLog('文件管理', '已清空回收站!') + return mw.returnJson(True, '已清空回收站!') + + # 设置文件和目录权限 def setMode(path): s_path = os.path.dirname(path) p_stat = os.stat(s_path) os.chown(path, p_stat.st_uid, p_stat.st_gid) - os.chmod(path, p_stat.st_mode) \ No newline at end of file + os.chmod(path, p_stat.st_mode) + + +def closeLogs(): + log_file = mw.getLogsDir() + os.system('rm -rf ' + log_file + '/*') + mw.opWeb('reload') + # os.system('kill -USR1 `cat ' + mw.getServerDir() +'/openresty/nginx/logs/nginx.pid`') + mw.writeLog('文件管理', '网站日志已被清空!') + tmp = getDirSizeByBash(log_file) + return mw.returnData(True, tmp) diff --git a/web/utils/firewall.py b/web/utils/firewall.py index 086f14d78..655a58743 100644 --- a/web/utils/firewall.py +++ b/web/utils/firewall.py @@ -11,8 +11,12 @@ import os import re import threading +import re +import time + import core.mw as mw +import thisdb class Firewall(object): @@ -43,6 +47,91 @@ class Firewall(object): elif mw.isAppleSystem(): self.__isMac = True + # 自动识别防火墙配置 | Automatically identify firewall + def aIF(self): + if self.__isFirewalld: + self.AIF_Firewalld() + if self.__isUfw: + self.aIF_Ufw() + + + def aIF_Ufw(self): + t = mw.execShell("ufw status|awk '{print $1}' | grep -v 'Status'|grep -v 'To'|grep -v '-'") + if t[1] != '': + return True + + all_port = t[0].strip() + ports_list = all_port.split('\n') + + ports_all = [] + for pinfo in ports_list: + info = pinfo.split('/') + + is_same = False + for i in range(len(ports_all)): + if ports_all[i]['port'] == info[0] and ports_all[i]['protocol'] != info[1]: + ports_all[i]['protocol'] = ports_all[i]['protocol']+'/'+info[1] + is_same = True + + if not is_same: + t = {} + t['port'] = info[0].replace('-',':') + t['protocol'] = info[1] + ports_all.append(t) + for add_info in ports_all: + if thisdb.getFirewallCountByPort(add_info['port']) == 0: + thisdb.addFirewall(add_info['port'], ps='自动识别',protocol=add_info['protocol']) + + def AIF_Firewalld(self): + t = mw.execShell("firewall-cmd --list-all | grep ' ports'") + if t[1] != '': + return True + + all_port = t[0].strip() + data = all_port.split(":") + ports_str = data[1] + ports_list = ports_str.strip().split(' ') + + ports_all = [] + for pinfo in ports_list: + info = pinfo.split('/') + + is_same = False + for i in range(len(ports_all)): + if ports_all[i]['port'] == info[0] and ports_all[i]['protocol'] != info[1]: + ports_all[i]['protocol'] = ports_all[i]['protocol']+'/'+info[1] + is_same = True + + if not is_same: + t = {} + t['port'] = info[0].replace('-',':') + t['protocol'] = info[1] + ports_all.append(t) + + for add_info in ports_all: + if thisdb.getFirewallCountByPort(add_info['port']) == 0: + thisdb.addFirewall(add_info['port'], ps='自动识别',protocol=add_info['protocol']) + + def getList(self, page=1,size=10): + info = thisdb.getFirewallList(page=page, size=size) + + rdata = {} + rdata['data'] = info['list'] + rdata['page'] = mw.getPage({'count':info['count'],'tojs':'showAccept','p':page,'row':size}) + return rdata + + def reload(self): + if self.__isUfw: + mw.execShell('/usr/sbin/ufw reload') + return + elif self.__isIptables: + mw.execShell('service iptables save') + mw.execShell('service iptables restart') + elif self.__isFirewalld: + mw.execShell('firewall-cmd --reload') + else: + pass + def getFwStatus(self): if self.__isUfw: cmd = "/usr/sbin/ufw status| grep Status | awk -F ':' '{print $2}'" @@ -69,11 +158,6 @@ class Firewall(object): def getSshInfo(self): data = {} - file = '/etc/ssh/sshd_config' - conf = mw.readFile(file) - rep = r"#*Port\s+([0-9]+)\s*\n" - port = re.search(rep, conf).groups(0)[0] - isPing = True try: if mw.isAppleSystem(): @@ -100,15 +184,32 @@ class Firewall(object): if ssh_status[0] != '': status = False + data['pubkey_prohibit_status'] = False data['pass_prohibit_status'] = False - # 密码登陆配置检查 - pass_rep = r"PasswordAuthentication\s+(\w*)\s*\n" - pass_status = re.search(pass_rep, conf) - if pass_status: - if pass_status and pass_status.groups(0)[0].strip() == 'no': + port = '22' + sshd_file = '/etc/ssh/sshd_config' + if os.path.exists(sshd_file): + conf = mw.readFile(sshd_file) + rep = r"#*Port\s+([0-9]+)\s*\n" + port = re.search(rep, conf).groups(0)[0] + + # 密码登陆配置检查 + pass_rep = r"PasswordAuthentication\s+(\w*)\s*\n" + pass_status = re.search(pass_rep, conf) + if pass_status: + if pass_status and pass_status.groups(0)[0].strip() == 'no': + data['pass_prohibit_status'] = True + else: data['pass_prohibit_status'] = True - else: - data['pass_prohibit_status'] = True + + # 密钥登陆配置检查 + pass_rep = r"PubkeyAuthentication\s+(\w*)\s*\n" + pass_status = re.search(pass_rep, conf) + if pass_status: + if pass_status and pass_status.groups(0)[0].strip() == 'no': + data['pubkey_prohibit_status'] = True + else: + data['pubkey_prohibit_status'] = True data['port'] = port data['status'] = status @@ -117,13 +218,12 @@ class Firewall(object): data['firewall_status'] = False else: data['firewall_status'] = self.getFwStatus() - return mw.getJson(data) + return data - def setPing(self): + def setPing(self, status): if mw.isAppleSystem(): return mw.returnData(True, '开发机不能操作!') - status = request.form.get('status') filename = '/etc/sysctl.conf' conf = mw.readFile(filename) if conf.find('net.ipv4.icmp_echo') != -1: @@ -134,4 +234,247 @@ class Firewall(object): mw.writeFile(filename, conf) mw.execShell('sysctl -p') - return mw.returnData(True, '设置成功!') \ No newline at end of file + return mw.returnData(True, '设置成功!') + + def setSshPort(self, port): + if int(port) < 22 or int(port) > 65535: + return mw.returnData(False, '端口范围必需在22-65535之间!') + + ports = ['21', '25', '80', '443', '888'] + if port in ports: + return mw.returnData(False, '(' + port + ')' + '特殊端口不可设置!') + + file = '/etc/ssh/sshd_config' + conf = mw.readFile(file) + + rep = r"#*Port\s+([0-9]+)\s*\n" + conf = re.sub(rep, "Port " + port + "\n", conf) + mw.writeFile(file, conf) + + self.addAcceptPort(port, 'SSH端口修改', 'port') + self.reload() + return mw.returnData(True, '修改成功!') + + def setFw(self, status): + if self.__isIptables: + self.setFwIptables(status) + return mw.returnData(True, '设置成功!') + + if status == '1': + if self.__isUfw: + mw.execShell('/usr/sbin/ufw disable') + elif self.__isFirewalld: + mw.execShell('systemctl stop firewalld.service') + mw.execShell('systemctl disable firewalld.service') + else: + pass + else: + if self.__isUfw: + mw.execShell("echo 'y'| ufw enable") + elif self.__isFirewalld: + mw.execShell('systemctl start firewalld.service') + mw.execShell('systemctl enable firewalld.service') + else: + pass + return mw.returnData(True, '设置成功!') + + def addAcceptPortCmd(self, port, protocol ='tcp'): + if self.__isUfw: + if protocol == 'tcp': + mw.execShell('ufw allow ' + port + '/tcp') + if protocol == 'udp': + mw.execShell('ufw allow ' + port + '/udp') + if protocol == 'tcp/udp': + mw.execShell('ufw allow ' + port + '/tcp') + mw.execShell('ufw allow ' + port + '/udp') + elif self.__isFirewalld: + port = port.replace(':', '-') + if protocol == 'tcp': + cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp' + mw.execShell(cmd) + if protocol == 'udp': + cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/udp' + mw.execShell(cmd) + if protocol == 'tcp/udp': + cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp' + mw.execShell(cmd) + cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/udp' + mw.execShell(cmd) + elif self.__isIptables: + if protocol == 'tcp': + cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT' + mw.execShell(cmd) + if protocol == 'udp': + cmd = 'iptables -I INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT' + mw.execShell(cmd) + if protocol == 'tcp/udp': + cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT' + mw.execShell(cmd) + cmd = 'iptables -I INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT' + mw.execShell(cmd) + else: + pass + return True + + # 添加放行端口 + def addAcceptPort(self, port, ps, stype, + protocol='tcp' + ): + if not self.getFwStatus(): + self.setFw(0) + return mw.returnData(False, '防火墙启动时,才能添加规则!') + + rep = r"^\d{1,5}(:\d{1,5})?$" + if not re.search(rep, port): + return mw.returnData(False, '端口范围不正确!') + + if thisdb.getFirewallCountByPort(port) > 0: + return mw.returnData(False, '您要放行的端口已存在,无需重复放行!') + + thisdb.addFirewall(port, ps=ps,protocol=protocol) + self.addAcceptPortCmd(port, protocol=protocol) + self.reload() + + msg = mw.getInfo('放行端口[{1}][{2}]成功', (port, protocol,)) + mw.writeLog("防火墙管理", msg) + return mw.returnData(True, msg) + + def addPanelPort(self, port): + self.setFw(0) + + protocol = 'tcp' + ps = 'PANEL端口' + + if thisdb.getFirewallCountByPort(port) > 0: + return mw.returnData(False, '您要放行的端口已存在,无需重复放行!') + + thisdb.addFirewall(port, ps=ps,protocol=protocol) + self.addAcceptPortCmd(port, protocol=protocol) + self.reload() + + msg = mw.getInfo('放行端口[{1}][{2}]成功', (port, protocol,)) + mw.writeLog("防火墙管理", msg) + return mw.returnData(True, msg) + + def delAcceptPort(self, firewall_id, port, + protocol='tcp' + ): + panel_port = mw.getPanelPort() + + if port.find(':') > 0: + pass + elif port.find('-') > 0: + pass + else: + if(int(port) == int(panel_port)): + return mw.returnData(False, '失败,不能删除当前面板端口!') + try: + self.delAcceptPortCmd(port, protocol) + mw.M('firewall').where("id=?", (firewall_id,)).delete() + return mw.returnData(True, '删除成功!') + except Exception as e: + return mw.returnData(False, '删除失败!:' + str(e)) + + def delAcceptPortCmd(self, port, + protocol ='tcp' + ): + if self.__isUfw: + if protocol == 'tcp': + mw.execShell('ufw delete allow ' + port + '/tcp') + if protocol == 'udp': + mw.execShell('ufw delete allow ' + port + '/udp') + if protocol == 'tcp/udp': + mw.execShell('ufw delete allow ' + port + '/tcp') + mw.execShell('ufw delete allow ' + port + '/udp') + elif self.__isFirewalld: + port = port.replace(':', '-') + if protocol == 'tcp': + mw.execShell( + 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp') + if protocol == 'udp': + mw.execShell( + 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp') + if protocol == 'tcp/udp': + mw.execShell( + 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp') + mw.execShell( + 'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp') + elif self.__isIptables: + if protocol == 'tcp': + mw.execShell( + 'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT') + if protocol == 'udp': + mw.execShell( + 'iptables -D INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT') + if protocol == 'tcp/udp': + mw.execShell( + 'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT') + mw.execShell( + 'iptables -D INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT') + else: + pass + + mw.M('firewall').where("port=?", (port,)).delete() + msg = mw.getInfo('删除防火墙放行端口[{1}][{2}]成功!', (port, protocol,)) + mw.writeLog("防火墙管理", msg) + self.reload() + return True + + def setSshPassStatus(self, status): + msg = '禁止密码登陆成功' + if status == "1": + msg = '开启密码登陆成功' + + file = '/etc/ssh/sshd_config' + if not os.path.exists(file): + return mw.returnJson(False, '无法设置!') + + conf = mw.readFile(file) + + pass_rep = r"PasswordAuthentication\s+(\w*)\s*\n" + pass_status = re.search(pass_rep, conf) + if not pass_status: + rep = r"(#)?PasswordAuthentication\s+(\w*)\s*\n" + conf = re.sub(rep, "PasswordAuthentication yes\n", conf) + + if status == '1': + rep = r"PasswordAuthentication\s+(\w*)\s*\n" + conf = re.sub(rep, "PasswordAuthentication yes\n", conf) + else: + rep = r"PasswordAuthentication\s+(\w*)\s*\n" + conf = re.sub(rep, "PasswordAuthentication no\n", conf) + mw.writeFile(file, conf) + mw.execShell("systemctl restart sshd.service") + mw.writeLog("SSH管理", msg) + return mw.returnData(True, msg) + + def setSshPubkeyStatus(self, status): + msg = '禁止密钥登陆成功' + if status == "1": + msg = '开启密钥登陆成功' + + file = '/etc/ssh/sshd_config' + if not os.path.exists(file): + return mw.returnJson(False, '无法设置!') + + content = mw.readFile(file) + + pubkey_rep = r"PubkeyAuthentication\s+(\w*)\s*\n" + pubkey_status = re.search(pubkey_rep, content) + if not pubkey_status: + rep = r"(#)?PubkeyAuthentication\s+(\w*)\s*\n" + content = re.sub(rep, "PubkeyAuthentication yes\n", content) + + if status == '1': + rep = r"PubkeyAuthentication\s+(\w*)\s*\n" + content = re.sub(rep, "PubkeyAuthentication yes\n", content) + else: + rep = r"PubkeyAuthentication\s+(\w*)\s*\n" + content = re.sub(rep, "PubkeyAuthentication no\n", content) + mw.writeFile(file, content) + mw.execShell("systemctl restart sshd.service") + mw.writeLog("SSH管理", msg) + return mw.returnData(True, msg) + + + diff --git a/class/plugin/fcgi_client.py b/web/utils/php/fcgi_client.py similarity index 100% rename from class/plugin/fcgi_client.py rename to web/utils/php/fcgi_client.py diff --git a/class/plugin/fpm.py b/web/utils/php/fpm.py similarity index 97% rename from class/plugin/fpm.py rename to web/utils/php/fpm.py index ed6e8058d..66f72494e 100644 --- a/class/plugin/fpm.py +++ b/web/utils/php/fpm.py @@ -8,7 +8,8 @@ import re import sys import time import struct -import fcgi_client + +from .fcgi_client import FCGIApp FCGI_Header = '!BBHHBx' @@ -150,7 +151,7 @@ class fpm(object): @param content stream(POST数据io对象) @return fastcgi-socket ''' - fcgi = fcgi_client.FCGIApp(connect=self.fcgi_sock) + fcgi = FCGIApp(connect=self.fcgi_sock) try: script_name, query_string = url.split('?') except ValueError: @@ -168,7 +169,7 @@ class fpm(object): 'SCRIPT_NAME': self.last_path + script_name, 'REQUEST_URI': url, 'GATEWAY_INTERFACE': 'CGI/1.1', - 'SERVER_SOFTWARE': 'MW-Panel', + 'SERVER_SOFTWARE': 'MW-PANEL', 'REDIRECT_STATUS': '200', 'CONTENT_TYPE': content_type, 'CONTENT_LENGTH': str(content_length), diff --git a/web/utils/mwplugin.py b/web/utils/plugin.py similarity index 55% rename from web/utils/mwplugin.py rename to web/utils/plugin.py index 6a2ff736b..306941a12 100644 --- a/web/utils/mwplugin.py +++ b/web/utils/plugin.py @@ -14,12 +14,10 @@ import json import threading import multiprocessing -from admin import model - import core.mw as mw -import admin.model.option as option +import thisdb -class pa_thread(threading.Thread): +class pg_thread(threading.Thread): def __init__(self, func, args, name=''): threading.Thread.__init__(self) @@ -34,7 +32,7 @@ class pa_thread(threading.Thread): except Exception: return None -class MwPlugin(object): +class plugin(object): def_plugin_type = [ { @@ -80,34 +78,29 @@ class MwPlugin(object): ] __plugin_dir = 'plugins' - __index = 'data/json/index.json' - __index_data = None + __tasks = None + + __plugin_status_cachekey = 'plugin_list_status' + __plugin_status_data = None # lock _instance_lock = threading.Lock() @classmethod def instance(cls, *args, **kwargs): - if not hasattr(MwPlugin, "_instance"): - with MwPlugin._instance_lock: - if not hasattr(MwPlugin, "_instance"): - MwPlugin._instance = MwPlugin(*args, **kwargs) - return MwPlugin._instance + if not hasattr(plugin, "_instance"): + with plugin._instance_lock: + if not hasattr(plugin, "_instance"): + plugin._instance = plugin(*args, **kwargs) + return plugin._instance """插件类初始化""" def __init__(self): self.__plugin_dir = mw.getPluginDir() - self.__index = mw.getPanelDataDir() + '/json/index.json' - self.initIndexData() - def initIndexData(self): - if not os.path.exists(self.__index): - mw.writeFile(self.__index, '[]') - self.__index_data = json.loads(mw.readFile(self.__index)) - def getIndexList(self): - indexList = option.getOptionByJson('display_index') + indexList = thisdb.getOptionByJson('display_index') plist = [] for i in indexList: tmp = i.split('-') @@ -138,10 +131,63 @@ class MwPlugin(object): except Exception as e: print('getIndexList:', mw.getTracebackInfo()) - # 使用gevent模式时,无法使用多进程 - # plist = self.checkStatusMProcess(plist) - plist = self.checkStatusMThreads(plist) - return plist + plist = self.checkStatusMThreadsByCache(plist) + return mw.returnData(True, 'ok', plist) + + def init(self): + plugin_names = { + 'openresty': '1.27.1', + 'php': '56', + 'swap': '1.1', + 'mysql': '5.7', + 'phpmyadmin': '4.4.15', + } + + pn_dir = mw.getPluginDir() + pn_server_dir = mw.getServerDir() + pn_list = [] + for pn in plugin_names: + info = {} + pn_json = pn_dir + '/' + pn + '/info.json' + pn_server = pn_server_dir + '/' + pn + if not os.path.exists(pn_server): + + tmp = mw.readFile(pn_json) + tmp = json.loads(tmp) + + info['title'] = tmp['title'] + info['name'] = tmp['name'] + info['versions'] = tmp['versions'] + info['default_ver'] = plugin_names[pn] + pn_list.append(info) + else: + return mw.returnData(False, 'ok') + + return mw.returnData(True, 'ok', pn_list) + + def initInstall(self, plugin_list): + try: + pn_list = json.loads(plugin_list) + for pn in pn_list: + name = pn['name'] + version = pn['version'] + info_file = self.__plugin_dir + '/' + name + '/' + 'info.json' + pluginInfo = json.loads(mw.readFile(info_file)) + self.hookInstall(pluginInfo) + + cmd = 'cd {0} && bash {1} install {2}'.format( + mw.getPluginDir() + '/'+name, + pluginInfo['shell'], + version + ) + title = '安装[' + name + '-' + version + ']' + thisdb.addTask(name=title,cmd=cmd) + os.mkdir(mw.getServerDir() + '/php') + # 任务执行相关 + mw.triggerTask() + return mw.returnData(True, '添加成功') + except Exception as e: + return mw.returnData(False, mw.getTracebackInfo()) def menuGetAbsPath(self, tag, path): if path[0:1] == '/': @@ -151,30 +197,30 @@ class MwPlugin(object): def addIndex(self, name, version): vname = name + '-' + version - indexList = option.getOptionByJson('display_index') + indexList = thisdb.getOptionByJson('display_index',default=[]) + if vname in indexList: return mw.returnData(False, '请不要重复添加!') if len(indexList) > 12: return mw.returnData(False, '首页最多只能显示12个软件!') indexList.append(vname) - option.setOption('display_index', json.dumps(indexList)) + + thisdb.setOption('display_index', json.dumps(indexList)) return mw.returnData(True, '添加成功!') def removeIndex(self, name, version): vname = name + '-' + version - indexList = option.getOptionByJson('display_index') + indexList = thisdb.getOptionByJson('display_index') if not vname in indexList: return mw.returnData(True, '删除成功!!') indexList.remove(vname) - - print(indexList) - option.setOption('display_index', json.dumps(indexList)) + thisdb.setOption('display_index', json.dumps(indexList)) return mw.returnData(True, '删除成功!') def hookInstallOption(self, hook_name, info): hn_name = 'hook_'+hook_name - src_data = option.getOptionByJson(hn_name,type='hook',default=[]) + src_data = thisdb.getOptionByJson(hn_name,type='hook',default=[]) isNeedAdd = True for x in range(len(src_data)): if src_data[x]['title'] == info['title'] and src_data[x]['name'] == info['name']: @@ -183,17 +229,17 @@ class MwPlugin(object): if isNeedAdd: src_data.append(info) - option.setOption(hn_name, json.dumps(src_data), type='hook') + thisdb.setOption(hn_name, json.dumps(src_data), type='hook') return True def hookUninstallOption(self, hook_name, info): hn_name = 'hook_'+hook_name - src_data = option.getOptionByJson(hn_name,type='hook',default=[]) + src_data = thisdb.getOptionByJson(hn_name,type='hook',default=[]) for idx in range(len(src_data)): if src_data[idx]['name'] == info['name']: src_data.remove(src_data[idx]) break - option.setOption(hn_name, json.dumps(src_data), type='hook') + thisdb.setOption(hn_name, json.dumps(src_data), type='hook') return True def hookInstall(self, info): @@ -233,7 +279,7 @@ class MwPlugin(object): return False def install(self, name, version, - upgrade: bool | None = None + upgrade = None ): if name.strip() == '': return mw.returnData(False, '缺少插件名称!', ()) @@ -260,15 +306,14 @@ class MwPlugin(object): self.hookInstall(info_data) title = '{0}[{1}-{2}]'.format(msg_head,name,version) - model.addTask(name=title,cmd=exec_bash, status=0) - + thisdb.addTask(name=title,cmd=exec_bash, status=0) + mw.triggerTask() # 调式日志 mw.debugLog(exec_bash) return mw.returnData(True, '已将安装任务添加到队列!') # 卸载插件 def uninstall(self, name, version): - rundir = mw.getRunDir() if name.strip() == '': return mw.returnData(False, "缺少插件名称!", ()) @@ -294,7 +339,7 @@ class MwPlugin(object): # 插件搜索匹配 def searchKey(self, info, - keyword: str | None = None, + keyword = None, ): if keyword == None: return True @@ -309,21 +354,45 @@ class MwPlugin(object): return False def getVersion(self, path): - version_t = path + '/version.pl' - if os.path.exists(version_t): - return mw.readFile(version_t).strip() + version_pl = path + '/version.pl' + if os.path.exists(version_pl): + return mw.readFile(version_pl).strip() return '' def checkIndexList(self, name, version): - indexList = option.getOptionByJson('display_index') + indexList = thisdb.getOptionByJson('display_index',default=[]) for i in indexList: t = i.split('-') if t[0] == name: return True return False + def checkSetupTask(self, name, version, coexist): + self.__tasks = thisdb.getTaskRunAll() + isTask = '1' + for task in self.__tasks: + tmpt = mw.getStrBetween('[', ']', task['name']) + if not tmpt: + continue + task_sign = tmpt.split('-') + task_len = len(task_sign) + + task_name = task_sign[0].lower() + task_ver = task_sign[1] + if task_len > 2: + nameArr = task_sign[0:task_len - 1] + task_name = '-'.join(nameArr).lower() + task_ver = task_sign[task_len - 1] + if coexist: + if task_name == name and task_ver == version: + isTask = task['status'] + else: + if task_name == name: + isTask = task['status'] + return isTask + def checkDisplayIndex(self, name, version, coexist): - indexList = self.__index_data + indexList = thisdb.getOptionByJson('display_index',default=[]) if coexist: if type(version) == list: for index in range(len(version)): @@ -348,13 +417,13 @@ class MwPlugin(object): data_t = data.copy() data_t['title'] = data_t['title'] + '-' + data['versions'][index] data_t['versions'] = data['versions'][index] - pg = self.getPluginInfo(data_t) + pg = self.makePluginInfo(data_t) plugins_info.append(pg) return plugins_info # 构造插件基本信息 - def getPluginInfo(self, info): + def makePluginInfo(self, info): checks = '' path = '' coexist = False @@ -407,15 +476,18 @@ class MwPlugin(object): if path.find('VERSION') > -1: pInfo['path'] = path.replace('VERSION', info['versions']) + pInfo['task'] = self.checkSetupTask(pInfo['name'], info['versions'], coexist) pInfo['display'] = self.checkDisplayIndex(info['name'], pInfo['versions'], coexist) pInfo['setup'] = os.path.exists(pInfo['install_checks']) - if coexist and pInfo['setup']: pInfo['setup_version'] = info['versions'] - else: - pInfo['setup_version'] = self.getVersion(pInfo['install_checks']) + elif pInfo['setup']: + if os.path.isdir(pInfo['install_checks']): + pInfo['setup_version'] = self.getVersion(pInfo['install_checks']) + else: + pInfo['setup_version'] = mw.readFile(pInfo['install_checks']).strip() if 'install_pre_inspection' in info: pInfo['install_pre_inspection'] = info['install_pre_inspection'] @@ -425,19 +497,32 @@ class MwPlugin(object): return pInfo def makeCoexistData(self, data): - plugins_t = [] + plugins = [] if type(data['versions']) == list and 'coexist' in data and data['coexist']: data_t = self.makeCoexist(data) for index in range(len(data_t)): - plugins_t.append(data_t[index]) + plugins.append(data_t[index]) else: - pg = self.getPluginInfo(data) - plugins_t.append(pg) - return plugins_t + pg = self.makePluginInfo(data) + plugins.append(pg) + return plugins + + def makeCoexistDataInstalled(self, data): + plugins = [] + if type(data['versions']) == list and 'coexist' in data and data['coexist']: + data_t = self.makeCoexist(data) + for index in range(len(data_t)): + if data_t[index]['setup']: + plugins.append(data_t[index]) + else: + pg = self.makePluginInfo(data) + if pg['setup']: + plugins.append(pg) + return plugins # 对多版本共存进行处理 def makeCoexistList(self, data, - plugin_type: str | None = None, + plugin_type = None, ): plugins_t = [] # 返回指定类型 @@ -448,53 +533,132 @@ class MwPlugin(object): if plugin_type == None or plugin_type == '0': return self.makeCoexistData(data) # 已经安装 - if plugin_type == '-1': - return self.makeCoexistData(data) + if str(plugin_type) == '-1': + return self.makeCoexistDataInstalled(data) return plugins_t - - - def getPluginList(self, name, - keyword: str | None = None, - type: str | None = None, - ): - info = [] + def getPluginInfo(self, name): + info = {} path = self.__plugin_dir + '/' + name info_path = path + '/info.json' if not os.path.exists(info_path): return info - try: data = json.loads(mw.readFile(info_path)) + return data except Exception as e: return info + + def getPluginList(self, name, + keyword = None, + type = None, + ): + infos = [] + data = self.getPluginInfo(name) + if data is None or len(data) == 0: + return infos # 判断是否搜索 if keyword != '' and not self.searchKey(data, keyword): - return info + return infos plugin_t = self.makeCoexistList(data, type) for index in range(len(plugin_t)): - info.append(plugin_t[index]) - return info + infos.append(plugin_t[index]) + return infos # 检查插件状态 def checkStatusThreads(self, info, i): if not info['setup']: return False data = self.run(info['name'], 'status', info['setup_version']) - if data[0] == 'start': + if data[0].strip() == 'start': return True else: return False + # 检查插件状态 + def checkStatusThreadsByCache(self, info, i): + # 初始化db + if not info['setup']: + return False + + plugin_list_status = self.__plugin_status_data + if plugin_list_status is not None: + k = info['name'] + if 'coexist' in info and info['coexist']: + k = info['title'] + # print(k) + if k in plugin_list_status: + if plugin_list_status[k]: + return True + else: + return False + + data = self.run(info['name'], 'status', info['setup_version']) + if data[0].strip() == 'start': + return True + else: + return False + + # 多线程检查插件状态[cache] + def checkStatusMThreadsByCache(self, info): + try: + self.__plugin_status_data = thisdb.getOptionByJson(self.__plugin_status_cachekey, default=None) + threads = [] + ntmp_list = range(len(info)) + for i in ntmp_list: + t = pg_thread(self.checkStatusThreadsByCache,(info[i], i)) + threads.append(t) + + for i in ntmp_list: + threads[i].start() + for i in ntmp_list: + threads[i].join() + + for i in ntmp_list: + t = threads[i].getResult() + k = info[i]['name'] + self.__plugin_status_data[k] = t + info[i]['status'] = t + + thisdb.setOption(self.__plugin_status_cachekey, json.dumps(self.__plugin_status_data)) + except Exception as e: + print(mw.getTracebackInfo()) + print('checkStatusMThreadsByCache:', str(e)) + return info + + def autoCachePluginStatus(self): + info = [] + for name in os.listdir(self.__plugin_dir): + if name.startswith('.'): + continue + t = self.getPluginList(name) + for index in range(len(t)): + info.append(t[index]) + + self.__plugin_status_data = {} + for x in info: + if not x['setup']: + continue + data = self.run(x['name'], 'status', x['setup_version']) + k = x['name'] + if 'coexist' in x and x['coexist']: + k = x['title'] + if data[0].strip() == 'start': + self.__plugin_status_data[k] = True + else: + self.__plugin_status_data[k] = False + thisdb.setOption(self.__plugin_status_cachekey, json.dumps(self.__plugin_status_data)) + return True + # 多线程检查插件状态 def checkStatusMThreads(self, info): try: threads = [] ntmp_list = range(len(info)) for i in ntmp_list: - t = pa_thread(self.checkStatusThreads,(info[i], i)) + t = pg_thread(self.checkStatusThreads,(info[i], i)) threads.append(t) for i in ntmp_list: @@ -512,14 +676,12 @@ class MwPlugin(object): def getAllPluginList( self, - type: str | None = None, - keyword: str | None = None, - page: int | None = 1, - size: int | None = 10, + type = None, + keyword = None, + page = 1, + size = 10, ): info = [] - - # print(mw.getPluginDir()) for name in os.listdir(self.__plugin_dir): if name.startswith('.'): continue @@ -529,36 +691,122 @@ class MwPlugin(object): start = (page - 1) * size end = start + size - _info = info[start:end] + x = info[start:end] - # print(info) - - _info = self.checkStatusMThreads(_info) - return (_info, len(info)) + x = self.checkStatusMThreadsByCache(x) + return (x, len(info)) def getList( self, - type: str | None = None, - keyword: str | None = None, - page: int | None = 1, - size: int | None = 10, + type = None, + keyword = None, + page = 1, + size = 10, ) -> object: + ''' # print(type,keyword,page,size) + ''' rdata = {} rdata['type'] = self.def_plugin_type - data = self.getAllPluginList(type,keyword, page, size) + data = self.getAllPluginList(type, keyword, page, size) rdata['data'] = data[0] rdata['list'] = mw.getPage({'count':data[1],'p':page,'tojs':'getSList','row':size}) return rdata + def updateZip(self, request_zip): + tmp_path = mw.getPanelDir() + '/temp' + if not os.path.exists(tmp_path): + os.makedirs(tmp_path) + mw.execShell("rm -rf " + tmp_path + '/*') + + tmp_file = tmp_path + '/plugin_tmp.zip' + if request_zip.filename[-4:] != '.zip': + return mw.returnData(False, '仅支持zip文件!') + + request_zip.save(tmp_file) + mw.execShell('cd ' + tmp_path + ' && unzip ' + tmp_file) + os.remove(tmp_file) + + p_info = tmp_path + '/info.json' + if not os.path.exists(p_info): + d_path = None + for df in os.walk(tmp_path): + if len(df[2]) < 3: + continue + if not 'info.json' in df[2]: + continue + if not 'install.sh' in df[2]: + continue + if not os.path.exists(df[0] + '/info.json'): + continue + d_path = df[0] + if d_path: + tmp_path = d_path + p_info = tmp_path + '/info.json' + try: + data = json.loads(mw.readFile(p_info)) + data['size'] = mw.getPathSize(tmp_path) + if not 'author' in data: + data['author'] = '未知' + if not 'home' in data: + data['home'] = 'https://github.com/midoks/mdserver-web' + plugin_path = mw.getPluginDir() + data['name'] + '/info.json' + data['old_version'] = '0' + data['tmp_path'] = tmp_path + if os.path.exists(plugin_path): + try: + old_info = json.loads(mw.readFile(plugin_path)) + data['old_version'] = old_info['versions'] + except: + pass + except: + mw.execShell("rm -rf " + tmp_path) + return mw.returnData(False, '在压缩包中没有找到插件信息,请检查插件包!') + protectPlist = ('openresty', 'mysql', 'php', 'redis', 'memcached' + 'mongodb', 'swap', 'gogs', 'pureftp') + if data['name'] in protectPlist: + return mw.returnData(False, '[' + data['name'] + '],重要插件不可修改!') + return data + + def inputZipApi(self, plugin_name,tmp_path): + if not os.path.exists(tmp_path): + return mw.returnData(False, '临时文件不存在,请重新上传!') + plugin_path = mw.getPluginDir() + '/' + plugin_name + if not os.path.exists(plugin_path): + print(mw.execShell('mkdir -p ' + plugin_path)) + mw.execShell("cp -rf " + tmp_path + '/* ' + plugin_path + '/') + mw.execShell('chmod -R 755 ' + plugin_path) + p_info = mw.readFile(plugin_path + '/info.json') + if p_info: + mw.writeLog('软件管理', '安装第三方插件[%s]' %json.loads(p_info)['title']) + return mw.returnData(True, '安装成功!') + mw.execShell("rm -rf " + plugin_path) + return mw.returnData(False, '安装失败!') + + # [start|stop]操作,删除缓存! + def runByCache(self, name, func, version): + ppos = mw.getServerDir()+'/'+name + if not os.path.exists(ppos): + return + data = thisdb.getOptionByJson(self.__plugin_status_cachekey, default={}) + info = self.getPluginInfo(name) + if 'coexist' in info and info['coexist']: + name = info['title'] + '-'+ version + if name in data: + del(data[name]) + thisdb.setOption(self.__plugin_status_cachekey, json.dumps(data)) + # shell/bash方式调用 def run(self, name, func, - version: str | None = '', - args: str | None = '', - script: str | None = 'index', + version = '', + args = '', + script = 'index', ): + if mw.inArray(['start','stop','restart','reload','uninstall_pre_inspection','install','uninstall'], func): + self.runByCache(name, func, version) + path = self.__plugin_dir + '/' + name + '/' + script + '.py' if not os.path.exists(path): path = self.__plugin_dir + '/' + name + '/' + name + '.py' @@ -573,6 +821,7 @@ class MwPlugin(object): return ('', '') py_cmd = 'cd ' + mw.getPanelDir() + " && "+ py_cmd data = mw.execShell(py_cmd) + # print(data) if mw.isDebugMode(): print('run:', py_cmd) @@ -582,29 +831,25 @@ class MwPlugin(object): # 映射包调用 def callback(self, name, func, - args: str | None = '', - script: str | None = 'index', + args = '', + script = 'index', ): - package = self.__plugin_dir + '/' + name if not os.path.exists(package): return (False, "插件不存在!") if not package in sys.path: sys.path.append(package) - eval_str = "__import__('" + script + "')." + func + '(' + args + ')' - + cmd = "__import__('" + script + "')." + func + '(' + args + ')' if mw.isDebugMode(): - print('callback', eval_str) + print('callback', cmd) data = None try: - data = eval(eval_str) + data = eval(cmd) except Exception as e: print(mw.getTracebackInfo()) - return (False, mw.getTracebackInfo()) - - + return (False, mw.getTracebackInfo()) return (True, data) diff --git a/web/utils/setting.py b/web/utils/setting.py new file mode 100644 index 000000000..c6dd8aedc --- /dev/null +++ b/web/utils/setting.py @@ -0,0 +1,294 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import re +import threading +import re +import time +import json + +import core.mw as mw +import thisdb + +class setting(object): + + # lock + _instance_lock = threading.Lock() + + @classmethod + def instance(cls, *args, **kwargs): + if not hasattr(setting, "_instance"): + with setting._instance_lock: + if not hasattr(setting, "_instance"): + setting._instance = setting(*args, **kwargs) + return setting._instance + + def __init__(self): + pass + + + # 保存面板证书 + def savePanelSsl(self, choose, cert_pem, private_key): + if not mw.inArray(['local','nginx'], choose): + return mw.returnData(True, '保存错误面板SSL类型!') + + pdir = mw.getPanelDir() + keyPath = pdir+'/ssl/'+choose+'/private.pem' + certPath = pdir+'/ssl/'+choose+'/cert.pem' + check_cert_pl = '/tmp/cert.pl' + + if not os.path.exists(keyPath): + return mw.returnData(False, '【'+choose+'】SSL类型不存在,先申请!') + + if(private_key.find('KEY') == -1): + return mw.returnData(False, '秘钥错误,请检查!') + if(cert_pem.find('CERTIFICATE') == -1): + return mw.returnData(False, '证书错误,请检查!') + + mw.writeFile(check_cert_pl, cert_pem) + if private_key: + mw.writeFile(keyPath, private_key) + if cert_pem: + mw.writeFile(certPath, cert_pem) + if not mw.checkCert(check_cert_pl): + os.remove(check_cert_pl) + return mw.returnData(False, '证书错误,请检查!') + os.remove(check_cert_pl) + return mw.returnData(True, '证书已保存!') + + + def getPanelSsl(self): + rdata = {} + rdata['choose'] = 'local' + + + pdir = mw.getPanelDir() + + keyPath = pdir+'/ssl/local/private.pem' + certPath = pdir+'/ssl/local/cert.pem' + + if not os.path.exists(certPath): + mw.createLocalSSL() + + cert = {} + cert['privateKey'] = mw.readFile(keyPath) + cert['is_https'] = '' + cert['certPem'] = mw.readFile(certPath) + cert['info'] = mw.getCertName(certPath) + rdata['local'] = cert + + panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf" + if not os.path.exists(panel_ssl): + cert['is_https'] = '' + else: + ssl_data = mw.readFile(panel_ssl) + if ssl_data.find('$server_port !~ 443') != -1: + cert['is_https'] = 'checked' + + keyPath = pdir+'/ssl/nginx/private.pem' + certPath = pdir+'/ssl/nginx/cert.pem' + + cert = {} + cert['privateKey'] = '' + cert['certPem'] = '' + cert['info'] = {} + if os.path.exists(keyPath): + cert['privateKey'] = mw.readFile(keyPath) + + if os.path.exists(keyPath): + cert['certPem'] = mw.readFile(certPath) + cert['info'] = mw.getCertName(certPath) + + rdata['nginx'] = cert + + return rdata + + # 删除面板证书 + def delPanelSsl(self, choose): + ip = mw.getLocalIp() + if mw.isAppleSystem(): + ip = '127.0.0.1' + + + if not mw.inArray(['local','nginx'], choose): + return mw.returnData(True, '删除错误面板SSL类型!') + + port = mw.getPanelPort() + + to_panel_url = 'http://'+ip+":"+port+'/config' + + if choose == 'local': + dst_path = mw.getPanelDir() + '/ssl/local' + if os.path.exists(dst_path): + mw.execShell('rm -rf ' + dst_path) + mw.restartMw(); + return mw.returnData(True, '删除本地面板SSL成功!',to_panel_url) + else: + return mw.returnData(True, '已经删除本地面板SSL!',to_panel_url) + + if choose == 'nginx': + + bind_domain = self.__file['bind_domain'] + if not os.path.exists(bind_domain): + return mw.returnData(False, '未绑定域名!') + + siteName = mw.readFile(bind_domain).strip() + + src_path = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName + + dst_path = mw.getPanelDir() + '/ssl/nginx' + dst_csrpath = dst_path + '/cert.pem' + dst_keypath = dst_path + '/private.pem' + + if os.path.exists(src_path) or os.path.exists(dst_path): + if os.path.exists(src_letpath): + mw.execShell('rm -rf ' + src_letpath) + if os.path.exists(dst_csrpath): + mw.execShell('rm -rf ' + dst_csrpath) + if os.path.exists(dst_keypath): + mw.execShell('rm -rf ' + dst_keypath) + mw.restartNginx() + return mw.returnData(True, '删除面板SSL成功!') + + mw.restartNginx() + mw.restartMw() + return mw.returnData(False, '已经删除面板SSL!') + return mw.returnData(False, '未知类型!') + + # 面板本地SSL设置 + def setPanelLocalSsl(self, cert_type): + panel_ssl_data = thisdb.getOptionByJson('panel_ssl', default={'open':False}) + + if not panel_ssl_data['open']: + panel_ssl_data['open'] = True + + pdir = mw.getPanelDir() + cert = {} + keyPath = pdir+'/ssl/local/private.pem' + certPath = pdir+'/ssl/local/cert.pem' + if not os.path.exists(certPath): + mw.createLocalSSL() + + panel_ssl_data['choose'] = 'local' + thisdb.setOption('panel_ssl', json.dumps(panel_ssl_data)) + mw.restartMw() + return mw.returnData(True, '设置成功') + + def closePanelSsl(self): + panel_ssl_data = thisdb.getOptionByJson('panel_ssl', default={'open':False}) + + if panel_ssl_data['open']: + panel_ssl_data['open'] = False + + thisdb.setOption('panel_ssl', json.dumps(panel_ssl_data)) + mw.restartMw() + return mw.returnData(True, '设置成功') + + + # 申请面板let证书 + # def applyPanelAcmeSsl(self): + # bind_domain = self.__file['bind_domain'] + # if not os.path.exists(bind_domain): + # return mw.returnJson(False, '先要绑定域名!') + + # # 生成nginx配置 + # domain = mw.readFile(bind_domain) + # panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf" + # dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf" + # if not os.path.exists(dst_panel_path): + # reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$" + # if not re.match(reg, domain): + # return mw.returnJson(False, '主域名格式不正确') + + # op_dir = mw.getServerDir() + "/openresty" + # if not os.path.exists(op_dir): + # return mw.returnJson(False, '依赖OpenResty,先安装启动它!') + + # content = mw.readFile(panel_tpl) + # content = content.replace("{$PORT}", "80") + # content = content.replace("{$SERVER_NAME}", domain) + # content = content.replace("{$PANAL_PORT}", mw.readFile('data/port.pl')) + # content = content.replace("{$LOGPATH}", mw.getRunDir() + '/logs') + # content = content.replace("{$PANAL_ADDR}", mw.getRunDir()) + # mw.writeFile(dst_panel_path, content) + # mw.restartNginx() + + # siteName = mw.readFile(bind_domain).strip() + # auth_to = mw.getRunDir() + "/tmp" + # to_args = { + # 'domains': [siteName], + # 'auth_type': 'http', + # 'auth_to': auth_to, + # } + + # src_path = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName + # src_csrpath = src_path + "/fullchain.pem" # 生成证书路径 + # src_keypath = src_path + "/privkey.pem" # 密钥文件路径 + + # dst_path = mw.getRunDir() + '/ssl/nginx' + # dst_csrpath = dst_path + '/cert.pem' + # dst_keypath = dst_path + '/private.pem' + + # is_already_apply = False + + # if not os.path.exists(src_path): + # import cert_api + # data = cert_api.cert_api().applyCertApi(to_args) + # if not data['status']: + # msg = data['msg'] + # if type(data['msg']) != str: + # msg = data['msg'][0] + # emsg = data['msg'][1]['challenges'][0]['error'] + # msg = msg + '

                                响应状态:' + str(emsg['status']) + '

                                错误类型:' + emsg[ + # 'type'] + '

                                错误代码:' + emsg['detail'] + '

                                ' + # return mw.returnJson(data['status'], msg, data['msg']) + # else: + # is_already_apply = True + + # mw.buildSoftLink(src_csrpath, dst_csrpath, True) + # mw.buildSoftLink(src_keypath, dst_keypath, True) + # mw.execShell('echo "acme" > "' + dst_path + '/README"') + + # tmp_well_know = auth_to + '/.well-known' + # if os.path.exists(tmp_well_know): + # mw.execShell('rm -rf ' + tmp_well_know) + + # if os.path.exists(dst_path): + # choose_file = self.__file['ssl'] + # mw.writeFile(choose_file, 'nginx') + + # data = self.getPanelSslData() + + # if is_already_apply: + # return mw.returnJson(True, '重复申请!', data) + # return mw.returnJson(True, '申请成功!', data) + + def setPanelDomain(self, domain): + port = mw.getPanelPort() + + panel_domain = thisdb.getOption('panel_domain', default='') + if domain == '': + ip = mw.getLocalIp() + client_ip = mw.getClientIp() + if client_ip in ['127.0.0.1', 'localhost', '::1']: + ip = client_ip + + to_panel_url = 'http://'+ip+":"+str(port)+'/setting/index' + thisdb.setOption('panel_domain', '') + mw.restartMw() + return mw.returnData(True, '清空域名成功!', to_panel_url) + + thisdb.setOption('panel_domain', domain) + to_panel_url = 'http://'+domain+":"+str(port)+'/setting/index' + mw.restartMw() + return mw.returnData(True, '设置域名成功!',to_panel_url) + + diff --git a/web/utils/site.py b/web/utils/site.py new file mode 100644 index 000000000..33eb03133 --- /dev/null +++ b/web/utils/site.py @@ -0,0 +1,2503 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import sys +import re +import json +import time +import threading +import multiprocessing + +import core.mw as mw +import thisdb + + +class sites(object): + # lock + _instance_lock = threading.Lock() + + @classmethod + def instance(cls, *args, **kwargs): + if not hasattr(sites, "_instance"): + with sites._instance_lock: + if not hasattr(sites, "_instance"): + sites._instance = sites(*args, **kwargs) + return sites._instance + + def __init__(self): + # nginx conf + self.setupPath = mw.getServerDir() + '/web_conf' + self.logsPath = mw.getLogsDir() + + self.vhostPath = vhost = self.setupPath + '/nginx/vhost' + if not os.path.exists(vhost): + mw.execShell("mkdir -p " + vhost + " && chmod -R 755 " + vhost) + self.rewritePath = rewrite = self.setupPath + '/nginx/rewrite' + if not os.path.exists(rewrite): + mw.execShell("mkdir -p " + rewrite + " && chmod -R 755 " + rewrite) + + self.passPath = passwd = self.setupPath + '/nginx/pass' + if not os.path.exists(passwd): + mw.execShell("mkdir -p " + passwd + " && chmod -R 755 " + passwd) + + self.redirectPath = redirect = self.setupPath + '/nginx/redirect' + if not os.path.exists(redirect): + mw.execShell("mkdir -p " + redirect +" && chmod -R 755 " + redirect) + + self.proxyPath = proxy = self.setupPath + '/nginx/proxy' + if not os.path.exists(proxy): + mw.execShell("mkdir -p " + proxy + " && chmod -R 755 " + proxy) + + # ssl conf + self.sslDir = self.setupPath + '/ssl' + self.sslLetsDir = self.setupPath + '/letsencrypt' + if not os.path.exists(self.sslLetsDir): + mw.execShell("mkdir -p " + self.sslLetsDir +" && chmod -R 755 " + self.sslLetsDir) + + + def runHook(self, hook_name, func_name): + # 站点操作Hook + hook_cfg = thisdb.getOptionByJson('hook_site_cb',type='hook',default=[]) + hook_num = len(hook_cfg) + if hook_num == 0: + return + + from utils.plugin import plugin as MwPlugin + pa = MwPlugin.instance() + + for x in range(hook_num): + hook_data = hook_cfg[x] + if func_name in hook_data: + app_name = hook_data["name"] + run_func = hook_data[func_name]['func'] + pa.run(app_name, run_func) + return True + + # 域名编码转换 + def toPunycode(self, domain): + if sys.version_info[0] == 2: + domain = domain.encode('utf8') + tmp = domain.split('.') + newdomain = '' + for dkey in tmp: + # 匹配非ascii字符 + match = re.search(u"[\x80-\xff]+", dkey) + if not match: + newdomain += dkey + '.' + else: + newdomain += 'xn--' + dkey.decode('utf-8').encode('punycode') + '.' + return newdomain[0:-1] + + # 中文路径处理 + def toPunycodePath(self, path): + if sys.version_info[0] == 2: + path = path.encode('utf-8') + if os.path.exists(path): + return path + match = re.search(u"[\x80-\xff]+", path) + if not match: + return path + npath = '' + for ph in path.split('/'): + npath += '/' + self.toPunycode(ph) + return npath.replace('//', '/') + + def getHostConf(self, siteName): + return self.vhostPath + '/' + siteName + '.conf' + + def saveHostConf(self, path, data, encoding): + import utils.file as file + mw.backFile(path) + rdata = file.saveBody(path, data, encoding) + + if rdata['status']: + isError = mw.checkWebConfig() + if isError != True: + mw.restoreFile(path) + msg = 'ERROR: 检测到配置文件有错误,请先排除后再操作

                                ' + isError.replace("\n", '
                                ') + '
                                ' + return mw.returnData(False, msg) + mw.restartWeb() + mw.removeBackFile(path) + return rdata + + def getRewriteConf(self, site_name): + return self.rewritePath + '/' + site_name + '.conf' + + def getRedirectDataPath(self, site_name): + return "{}/{}/data.json".format(self.redirectPath, site_name) + + def getRedirectPath(self, site_name): + return "{}/{}".format(self.redirectPath, site_name) + + def getProxyDataPath(self, site_name): + return "{}/{}/data.json".format(self.proxyPath, site_name) + + def getProxyPath(self, site_name): + return "{}/{}".format(self.proxyPath, site_name) + + def getDirBindRewrite(self, site_name, dir_name): + return self.rewritePath + '/' + site_name + '_' + dir_name + '.conf' + + def getIndexConf(self): + return mw.getServerDir() + '/openresty/nginx/conf/nginx.conf' + + + # 路径处理 + def getPath(self, path): + if path[-1] == '/': + return path[0:-1] + return path + + def getSitePath(self, site_name): + file = self.getHostConf(site_name) + if os.path.exists(file): + conf = mw.readFile(file) + rep = r'\s*root\s*(.+);' + find_cnf = re.search(rep, conf) + if not find_cnf: + return '' + path = find_cnf.groups()[0] + return path + return '' + + def createRootDir(self, path): + autoInit = False + if not os.path.exists(path): + autoInit = True + os.makedirs(path) + if not mw.isAppleSystem(): + mw.execShell('chown -R www:www ' + path) + + if autoInit: + mw.writeFile(path + '/index.html', 'Work has started!!!') + mw.execShell('chmod -R 755 ' + path) + + def add(self, site_info, port, ps, path, version): + site_root_dir = mw.getWwwDir() + if site_root_dir == path.rstrip('/'): + return mw.returnData(False, '不要以网站根目录创建站点!') + + site_info = json.loads(site_info) + + self.siteName = self.toPunycode(site_info['domain'].strip().split(':')[0]).strip() + self.sitePath = self.toPunycodePath(self.getPath(path.replace(' ', ''))) + self.sitePort = port.strip().replace(' ', '') + self.phpVersion = version + + if thisdb.isSitesExist(self.siteName): + return mw.returnData(False, '您添加的站点[%s]已存在!' % self.siteName) + + site_id = thisdb.addSites(self.siteName, self.sitePath) + if site_id < 1: + return mw.returnData(False, '添加失败!') + + self.createRootDir(self.sitePath) + self.nginxAddConf() + + # 主域名配置 + thisdb.addDomain(site_id, self.siteName, self.sitePort) + # 添加更多域名 + for domain in site_info['domainlist']: + self.addDomain(site_id, self.siteName, domain) + + mw.restartWeb() + + self.runHook('site_cb', 'add') + return mw.returnData(True, '添加成功') + + def stop(self, site_id): + site_info = thisdb.getSitesById(site_id) + + path = self.setupPath + '/stop' + if not os.path.exists(path): + os.makedirs(path) + default_text = 'The website has been closed!!!' + mw.writeFile(path + '/index.html', default_text) + + binding = thisdb.getBindingListBySiteId(site_id) + for b in binding: + bpath = path + '/' + b['path'] + if not os.path.exists(bpath): + mw.execShell('mkdir -p ' + bpath) + mw.execShell('ln -sf ' + path +'/index.html ' + bpath + '/index.html') + + + # nginx + file = self.getHostConf(site_info['name']) + conf = mw.readFile(file) + if conf: + conf = conf.replace(site_info['path'], path) + mw.writeFile(file, conf) + + thisdb.setSitesData(site_id, status='0') + msg = mw.getInfo('网站[{1}]已被停用!', (site_info['name'],)) + mw.writeLog('网站管理', msg) + mw.restartWeb() + return mw.returnData(True, '站点已停用!') + + def start(self, site_id): + site_info = thisdb.getSitesById(site_id) + + path = self.setupPath + '/stop' + # nginx + file = self.getHostConf(site_info['name']) + conf = mw.readFile(file) + if conf: + conf = conf.replace(path, site_info['path']) + mw.writeFile(file, conf) + + thisdb.setSitesData(site_id, status='1') + + msg = mw.getInfo('网站[{1}]已被启用!', (site_info['name'],)) + mw.writeLog('网站管理', msg) + mw.restartWeb() + return mw.returnData(True, '站点已启用!') + + def nginxAddDomain(self, site_name, domain, port): + file = self.getHostConf(site_name) + conf = mw.readFile(file) + if not conf: + return + + # 添加域名 + rep = r"server_name\s*(.*);" + tmp = re.search(rep, conf).group() + domains = tmp.split(' ') + if not mw.inArray(domains, domain): + newServerName = tmp.replace(';', ' ' + domain + ';') + conf = conf.replace(tmp, newServerName) + + # 添加端口 + rep = r"listen\s+([0-9]+)\s*[default_server]*\s*;" + tmp = re.findall(rep, conf) + if not mw.inArray(tmp, port): + listen = re.search(rep, conf).group() + conf = conf.replace( + listen, listen + "\n\tlisten " + port + ';') + # 保存配置文件 + mw.writeFile(file, conf) + return True + + def addDomain(self, site_id, site_name, domain): + isError = mw.checkWebConfig() + if isError != True: + msg = 'ERROR: 检测到配置文件有错误,请先排除后再操作

                                ' + isError.replace("\n", '
                                ') + '
                                ' + return mw.returnData(False, msg) + + domains = domain.split(',') + for d in domains: + if d == '': + continue + d = d.split(':') + name = self.toPunycode(d[0]) + port = '80' + if len(d) == 2: + port = d[1] + + if not mw.checkPort(port): + return mw.returnData(False, '端口范围不合法!') + + reg = r"^([\w\-\*]{1,100}\.){1,4}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$" + if not re.match(reg, name): + return mw.returnData(False, '域名格式不正确!') + + if thisdb.checkSitesDomainIsExist(name, port): + return mw.returnData(False, '您添加的域名[{}:{}],已使用。请仔细检查!'.format(name, port)) + + self.nginxAddDomain(site_name, name, port) + + msg = mw.getInfo('网站[{1}]添加域名[{2}]成功!', (site_name, name)) + mw.writeLog('网站管理', msg) + thisdb.addDomain(site_id, name, port) + + mw.restartWeb() + self.runHook('site_cb', 'add') + return mw.returnData(True, '域名添加成功!') + + def delDomain(self, site_id, site_name, domain, port): + domain_nums = thisdb.getDomainCountBySiteId(site_id) + if domain_nums == 1: + return mw.returnData(False, '最后一个域名不能删除!') + + info = mw.M('domain').field('id,name').where("pid=? AND name=? AND port=?",(site_id, domain, port)).find() + + file = self.getHostConf(site_name) + conf = mw.readFile(file) + if conf: + # 删除域名 + rep = r"server_name\s+(.+);" + tmp = re.search(rep, conf).group() + newServerName = tmp.replace(' ' + domain + ';', ';') + newServerName = newServerName.replace(' ' + domain + ' ', ' ') + conf = conf.replace(tmp, newServerName) + + # 删除端口 + rep = r"listen\s+([0-9]+);" + tmp = re.findall(rep, conf) + port_nums = mw.M('domain').where('pid=? AND port=?', (site_id, port)).count() + if mw.inArray(tmp, port) == True and port_nums < 2: + rep = r"\n*\s+listen\s+" + port + ";" + conf = re.sub(rep, '', conf) + # 保存配置 + mw.writeFile(file, conf) + + thisdb.deleteDomainId(info['id']) + msg = mw.getInfo('网站[{1}]删除域名[{2}:{3}]成功!', (site_name, domain, port)) + mw.writeLog('网站管理', msg) + mw.restartWeb() + return mw.returnData(True, '站点删除成功!') + + def deleteALlLogs(self, webname): + assLogPath = mw.getLogsDir() + '/' + webname + '.log' + errLogPath = mw.getLogsDir() + '/' + webname + '.error.log' + confFile = self.setupPath + '/nginx/vhost/' + webname + '.conf' + rewriteFile = self.setupPath + '/nginx/rewrite/' + webname + '.conf' + passFile = self.setupPath + '/nginx/pass/' + webname + '.conf' + keyPath = self.sslDir + webname + '/privkey.pem' + certPath = self.sslDir + webname + '/fullchain.pem' + logs = [assLogPath, + errLogPath, + confFile, + rewriteFile, + passFile, + keyPath, + certPath] + for i in logs: + mw.deleteFile(i) + + # 重定向目录 + redirectDir = self.setupPath + '/nginx/redirect/' + webname + if os.path.exists(redirectDir): + mw.execShell('rm -rf ' + redirectDir) + # 代理目录 + proxyDir = self.setupPath + '/nginx/proxy/' + webname + if os.path.exists(proxyDir): + mw.execShell('rm -rf ' + proxyDir) + + def delete(self, site_id, path): + info = thisdb.getSitesById(site_id) + webname = info['name'] + self.deleteALlLogs(webname) + + if path == '1': + web_root_path = mw.getWwwDir() + '/' + webname + mw.execShell('rm -rf ' + web_root_path) + + # ssl + ssl_dir = self.sslDir + '/' + webname + if os.path.exists(ssl_dir): + mw.execShell('rm -rf ' + ssl_dir) + + ssl_lets_dir = self.sslLetsDir + '/' + webname + if os.path.exists(ssl_lets_dir): + mw.execShell('rm -rf ' + ssl_lets_dir) + + ssl_acme_dir = mw.getAcmeDir() + '/' + webname + if os.path.exists(ssl_acme_dir): + mw.execShell('rm -rf ' + ssl_acme_dir) + + thisdb.deleteSitesById(site_id) + thisdb.deleteDomainBySiteId(site_id) + + binding_list = thisdb.getBindingListBySiteId(site_id) + for x in binding_list: + tag = mw.getLogsDir() + "/" + webname + "_" + x['domain'] + site_log = tag + ".log" + site_error = tag + ".error.log" + + if os.path.exists(site_log): + mw.execShell('rm -rf ' + site_log) + if os.path.exists(site_error): + mw.execShell('rm -rf ' + site_error) + + thisdb.deleteBindingBySiteId(site_id) + mw.restartWeb() + + self.runHook('site_cb', 'delete') + return mw.returnData(True, '站点【%s】删除成功!' % webname) + + def nginxAddConf(self): + + source_tpl = self.getNgxTplDir() + '/nginx.conf' + vhost_file = self.getHostConf(self.siteName) + content = mw.readFile(source_tpl) + + content = content.replace('{$PORT}', self.sitePort) + content = content.replace('{$SERVER_NAME}', self.siteName) + content = content.replace('{$ROOT_DIR}', self.sitePath) + content = content.replace('{$PHP_DIR}', self.setupPath + '/php') + content = content.replace('{$PHPVER}', self.phpVersion) + content = content.replace('{$OR_REWRITE}', self.rewritePath) + # content = content.replace('{$OR_REDIRECT}', self.redirectPath) + # content = content.replace('{$OR_PROXY}', self.proxyPath) + + logsPath = mw.getLogsDir() + content = content.replace('{$LOGPATH}', logsPath) + mw.writeFile(vhost_file, content) + + # 和反代配置冲突 && 默认伪静态为空 +# rewrite_content = ''' +# location /{ +# if ($PHP_ENV != "1"){ +# break; +# } + +# if (!-e $request_filename) { +# rewrite ^(.*)$ /index.php/$1 last; +# break; +# } +# } +# ''' + rewrite_file = self.getRewriteConf(self.siteName) + mw.writeFile(rewrite_file, '') + + # 设置网站过期 + def setEndDate(self, site_id, end_date): + info = thisdb.getSitesById(site_id) + thisdb.setSitesData(site_id, edate=end_date) + mw.writeLog('网站管理', '设置成功,站点【{1}】到期【{2}】后将自动停止!', (info['name'], end_date,)) + return mw.returnData(True, '设置成功,站点到期后将自动停止!') + + def setSsl(self, site_name, key, csr): + path = self.sslDir + '/' + site_name + if not os.path.exists(path): + mw.execShell('mkdir -p ' + path) + + csrpath = path + "/fullchain.pem" # 生成证书路径 + keypath = path + "/privkey.pem" # 密钥文件路径 + + if(key.find('KEY') == -1): + return mw.returnJson(False, '秘钥错误,请检查!') + if(csr.find('CERTIFICATE') == -1): + return mw.returnJson(False, '证书错误,请检查!') + + tmp_cert = '/tmp/cert.pl' + mw.writeFile(tmp_cert, csr) + if not mw.checkCert(tmp_cert): + os.remove(tmp_cert) + return mw.returnData(False, '证书错误,请粘贴正确的PEM格式证书!') + os.remove(tmp_cert) + + mw.backFile(keypath) + mw.backFile(csrpath) + + mw.writeFile(keypath, key) + mw.writeFile(csrpath, csr) + + # 写入配置文件 + result = self.setSslConf(site_name) + if not result['status']: + return result + + isError = mw.checkWebConfig() + if(type(isError) == str): + mw.restoreFile(keypath) + mw.restoreFile(csrpath) + + msg = 'ERROR:
                                ' + isError.replace("\n", '
                                ') + '
                                ' + return mw.returnData(False, msg) + + mw.writeLog('网站管理', '证书已保存!') + mw.restartWeb() + return mw.returnData(True, '证书已保存!') + + # ssl相关方法 start + def setSslConf(self, site_name): + file = self.getHostConf(site_name) + conf = mw.readFile(file) + if not conf: + return mw.returnData(False, '站点[%s]配置异常!'.format(site_name)) + + version = mw.getOpVer() + keyPath = self.sslDir + '/' + site_name + '/privkey.pem' + certPath = self.sslDir + '/' + site_name + '/fullchain.pem' + + if conf.find('ssl_certificate') == -1: + # ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; + # add_header Alt-Svc 'h3=":443";ma=86400,h3-29=":443";ma=86400'; + http3Header = """ + add_header Strict-Transport-Security "max-age=63072000"; + add_header Alt-Svc 'h3=":443";ma=86400'; +""" + if not version.startswith('1.25') or version.startswith('1.27'): + http3Header = ''; + + sslStr = """#error_page 404/404.html; + ssl_certificate %s; + ssl_certificate_key %s; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + %s + error_page 497 https://$host$request_uri;""" % (certPath, keyPath, http3Header) + if(conf.find('ssl_certificate') != -1): + return mw.returnData(True, 'SSL开启成功!') + + conf = conf.replace('#error_page 404/404.html;', sslStr) + + rep = r"listen\s+([0-9]+)\s*[default_server|reuseport]*;" + tmp = re.findall(rep, conf) + if not mw.inArray(tmp, '443'): + listen = re.search(rep, conf).group() + + if version.startswith('1.25') or version.startswith('1.27'): + http_ssl = "\n\tlisten 443 ssl;" + http_ssl = http_ssl + "\n\tlisten [::]:443 ssl;" + http_ssl = http_ssl + "\n\thttp2 on;" + else: + http_ssl = "\n\tlisten 443 ssl;" + http_ssl = http_ssl + "\n\tlisten [::]:443 ssl;" + + + conf = conf.replace(listen, listen + http_ssl) + + mw.backFile(file) + mw.writeFile(file, conf) + isError = mw.checkWebConfig() + if not isError: + mw.restoreFile(file) + return mw.returnData(False, '证书错误:
                                ' + isError.replace("\n", '
                                ') + '
                                ') + + self.saveCert(site_name, keyPath, certPath) + msg = mw.getInfo('网站[{1}]开启SSL成功!', (site_name,)) + mw.writeLog('网站管理', msg) + + mw.restartWeb() + return mw.returnData(True, 'SSL开启成功!') + + # 设置网站备注 + def setPs(self, site_id, ps): + if thisdb.setSitesData(site_id, ps=ps): + return mw.returnData(True, '修改成功!') + return mw.returnData(False, '修改失败!') + + # 获取默认站点 + def getDefaultSite(self): + data = {} + data['sites'] = mw.M('sites').field('name').order('id desc').select() + data['default_site'] = thisdb.getOption('default_site', default='') + return data + + # 获取域名列表 + def getDomain(self, site_id): + data = thisdb.getDomainBySiteId(site_id) + return data + + # 获取日志内容 + def getLogs(self, siteName): + logPath = mw.getLogsDir() + '/' + siteName + '.log' + if not os.path.exists(logPath): + return mw.returnData(False, '日志为空') + return mw.returnData(True, mw.getLastLine(logPath, 100)) + + # 获取错误日志内容 + def getErrorLogs(self, siteName): + logPath = mw.getLogsDir() + '/' + siteName + '.error.log' + if not os.path.exists(logPath): + return mw.returnData(False, '日志为空') + return mw.returnData(True, mw.getLastLine(logPath, 100)) + + def getNgxRewriteDir(self): + return mw.getPanelDir() + '/web/misc/nginx/rewrite' + + def getNgxTplDir(self): + return mw.getPanelDir() + '/web/misc/nginx/tpl' + + # 获取模版名内容 + def getRewriteTpl(self, name): + path = self.getNgxRewriteDir() +'/'+ name + ".conf" + if not os.path.exists(path): + return mw.returnData(False, '模版不存在!') + return mw.returnData(True, 'OK', path) + + def setRewrite(self,path,data,encoding): + if not os.path.exists(path): + mw.writeFile(path, '') + + mw.backFile(path) + mw.writeFile(path, data) + isError = mw.checkWebConfig() + if(type(isError) == str): + mw.restoreFile(path) + msg = 'ERROR:
                                ' + isError.replace("\n", '
                                ') + '
                                ' + return mw.returnJson(False, msg) + mw.restartWeb() + return mw.returnData(True, '设置成功!') + + def setRewriteTpl(self,name,data): + path = self.getNgxRewriteDir() +'/'+ name + ".conf" + if os.path.exists(path): + return mw.returnData(False, '模版已经存在!') + + if data == "": + return mw.returnData(False, '模版内容不能为空!') + ok = mw.writeFile(path, data) + if not ok: + return mw.returnData(False, '模版保持失败!') + + return mw.returnData(True, '设置模板成功!') + + def getRewriteList(self): + rewriteList = {} + rewriteList['rewrite'] = [] + rewriteList['rewrite'].append('0.当前') + rewrite_nginx_dir = self.getNgxRewriteDir() + for ds in os.listdir(rewrite_nginx_dir): + if ds.startswith('.'): + continue + if ds.endswith('conf'): + rewriteList['rewrite'].append(ds[0:len(ds) - 5]) + rewriteList['rewrite'] = sorted(rewriteList['rewrite']) + return rewriteList + + # 取日志状态 + def getLogsStatus(self, siteName): + filename = self.getHostConf(siteName) + conf = mw.readFile(filename) + if conf.find('#ErrorLog') != -1: + return False + if conf.find("access_log off") != -1: + return False + return True + + # 取目录加密状态 + def getHasPwd(self, siteName): + filename = self.getHostConf(siteName) + conf = mw.readFile(filename) + if conf.find('#AUTH_START') != -1: + return True + return False + + # 取当站点前运行目录 + def getSiteRunPath(self, site_name, site_path): + filename = self.getHostConf(site_name) + if os.path.exists(filename): + conf = mw.readFile(filename) + rep = r'\s*root\s*(.+);' + path = re.search(rep, conf).groups()[0] + + data = {} + if site_path == path: + data['path'] = '/' + else: + data['path'] = path.replace(site_path, '') + + dirnames = [] + dirnames.append('/') + for filename in os.listdir(site_path): + try: + file_path = site_path + '/' + filename + if os.path.islink(file_path): + continue + if os.path.isdir(file_path): + dirnames.append('/' + filename) + except: + pass + + data['dirs'] = dirnames + return data + + def getDirUserIni(self, site_id): + + info = thisdb.getSitesById(site_id) + + path = info['path'] + name = info['name'] + data = {} + data['logs'] = self.getLogsStatus(name) + data['run_path'] = self.getSiteRunPath(name, path) + + data['user_ini'] = False + if os.path.exists(path + '/.user.ini'): + data['user_ini'] = True + + if data['run_path']['path'] != '/': + user_ini = path + data['run_path']['path'] + '/.user.ini' + if os.path.exists(user_ini): + data['userini'] = True + + data['pass'] = self.getHasPwd(name) + data['path'] = path + data['name'] = name + return mw.returnData(True, 'OK', data) + + # 清除多余user.ini + def delUserInI(self, path, up=0): + filename = path + '/.user.ini' + if os.path.exists(filename): + mw.execShell("which chattr && chattr -i " + filename) + os.remove(filename) + + for f in os.listdir(path): + try: + npath = path + '/' + f + if os.path.isdir(npath): + if up < 100: + self.delUserInI(npath, up + 1) + + user_ini = npath + '/.user.ini' + print('ff:',user_ini) + if not os.path.exists(user_ini): + continue + mw.execShell('which chattr && chattr -i ' + user_ini) + os.remove(user_ini) + except: + continue + return True + + + # 设置目录防御 + def addDirUserIni(self, site_path, run_path): + new_path = site_path + run_path + filename = new_path + '/.user.ini' + if os.path.exists(filename): + return mw.returnData(True, '已打开防跨站设置!') + + open_path = 'open_basedir={}/:{}/'.format(new_path, site_path) + if run_path == '/' or run_path == '': + open_path = 'open_basedir={}/'.format(site_path) + + mw.writeFile(filename, open_path + ':/www/server/php:/tmp/:/proc/') + mw.execShell("which chattr && chattr +i " + filename) + + def setDirUserIni(self, site_path, run_path): + filename = site_path + '/.user.ini' + if os.path.exists(filename): + self.delUserInI(site_path) + return mw.returnData(True, '已清除防跨站设置!') + self.addDirUserIni(site_path, run_path) + return mw.returnData(True, '已打开防跨站设置!') + + + def getDirBinding(self, site_id): + info = thisdb.getSitesById(site_id) + path = info['path'] + if not os.path.exists(path): + checks = ['/', '/usr', '/etc'] + if path in checks: + data = {} + data['dirs'] = [] + data['binding'] = [] + return mw.returnData(True, 'OK', data) + os.system('mkdir -p ' + path) + os.system('chmod 755 ' + path) + os.system('chown www:www ' + path) + siteName = info['name'] + mw.writeLog('网站管理', '站点[' + siteName + '],根目录[' + path + ']不存在,已重新创建!') + + dirnames = [] + for filename in os.listdir(path): + try: + filePath = path + '/' + filename + if os.path.islink(filePath): + continue + if os.path.isdir(filePath): + dirnames.append(filename) + except: + pass + + data = {} + data['dirs'] = dirnames + data['binding'] = thisdb.getBindingListBySiteId(site_id) + return mw.returnJson(True, 'OK', data) + + def addDirBind(self, site_id, domain, dir_name): + domain_split = domain.split(':') + domain = domain_split[0] + port = '80' + if len(domain_split) > 1: + port = domain_split[1] + if dir_name == '': + mw.returnData(False, '目录不能为空!') + + reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$" + if not re.match(reg, domain): + return mw.returnData(False, '主域名格式不正确!') + + info = thisdb.getSitesById(site_id) + webdir = info['path'] + '/' + dir_name + + if thisdb.getBindingCountByDomain(domain): + return mw.returnData(False, '您添加的域名在子目录已存在!') + + if thisdb.getDomainCountByName(domain) > 0: + return mw.returnData(False, '您添加的域名已存在!') + + filename = self.getHostConf(info['name']) + conf = mw.readFile(filename) + if conf: + rep = r"enable-php-([0-9]{2,3})\.conf" + domain_split = re.search(rep, conf).groups() + version = domain_split[0] + + source_dirbind_tpl = self.getNgxTplDir() + '/nginx_dirbind.conf' + content = mw.readFile(source_dirbind_tpl) + content = content.replace('{$PORT}', port) + content = content.replace('{$PHPVER}', version) + content = content.replace('{$DIRBIND}', domain) + content = content.replace('{$ROOT_DIR}', webdir) + content = content.replace('{$SERVER_MAIN}', info['name']) + content = content.replace('{$OR_REWRITE}', self.rewritePath) + content = content.replace('{$PHP_DIR}', self.setupPath + '/php') + content = content.replace('{$LOGPATH}', mw.getLogsDir()) + + conf += "\r\n" + content + mw.backFile(filename) + mw.writeFile(filename, conf) + conf = mw.readFile(filename) + + # 检查配置是否有误 + isError = mw.checkWebConfig() + if isError != True: + mw.restoreFile(filename) + msg = 'ERROR:
                                ' + isError.replace("\n", '
                                ') + '
                                ' + return mw.returnData(False, msg) + + thisdb.addBinding(site_id,domain,port,dir_name) + msg = mw.getInfo('网站[{1}]子目录[{2}]绑定到[{3}]',(info['name'], dir_name, domain)) + mw.writeLog('网站管理', msg) + mw.restartWeb() + mw.removeBackFile(filename) + return mw.returnData(True, '添加成功!') + + # 取子目录Rewrite + def getDirBindingRewrite(self, binding_id, add): + binding_info = thisdb.getBindingById(binding_id) + info = thisdb.getSitesById(binding_info['pid']) + + filename = self.getDirBindRewrite(info['name'], binding_info['path']) + if add == '1': + mw.writeFile(filename, '') + file = self.getHostConf(info['name']) + conf = mw.readFile(file) + domain = binding_info['domain'] + rep = "\n#BINDING-" + domain + "-START(.|\n)+BINDING-" + domain + "-END" + tmp = re.search(rep, conf).group() + dirConf = tmp.replace('rewrite/' + info['name'] + '.conf;', 'rewrite/' + info['name'] + '_' + binding_info['path'] + '.conf;') + conf = conf.replace(tmp, dirConf) + mw.writeFile(file, conf) + data = {} + data['rewrite_dir'] = self.rewritePath + data['status'] = False + if os.path.exists(filename): + data['status'] = True + data['data'] = mw.readFile(filename) + data['rlist'] = [] + for ds in os.listdir(self.rewritePath): + if ds[0:1] == '.': + continue + if ds == 'list.txt': + continue + data['rlist'].append(ds[0:len(ds) - 5]) + data['filename'] = filename + return data + + def delDirBinding(self, binding_id): + + binding_info = thisdb.getBindingById(binding_id) + info = thisdb.getSitesById(binding_info['pid']) + + filename = self.getHostConf(info['name']) + conf = mw.readFile(filename) + if conf: + rep = r"\s*.+BINDING-" + binding_info['domain'] + "-START(.|\n)+BINDING-" + binding_info['domain'] + "-END" + conf = re.sub(rep, '', conf) + mw.writeFile(filename, conf) + + filename = self.getDirBindRewrite(info['name'], binding_info['path']) + if os.path.exists(filename): + os.remove(filename) + + msg = mw.getInfo('删除网站[{1}]子目录[{2}]绑定',(info['name'], binding_info['path'])) + mw.writeLog('网站管理', msg) + mw.restartWeb() + + thisdb.deleteBindingById(binding_id) + return mw.returnJson(True, '删除成功!') + + + def logsOpen(self, site_id): + info = thisdb.getSitesById(site_id) + name = info['name'] + + filename = self.getHostConf(name) + if os.path.exists(filename): + conf = mw.readFile(filename) + rep = self.logsPath + '/' + name + '.log' + if conf.find(rep) != -1: + conf = conf.replace(rep + ' main', 'off') + else: + conf = conf.replace('access_log off', 'access_log ' + rep + ' main') + mw.writeFile(filename, conf) + + mw.restartWeb() + return mw.returnData(True, '操作成功!') + + def setSitePath(self, site_id, path): + path = self.getPath(path) + if path == "" or site_id == '0': + return mw.returnData(False, "目录不能为空!") + + import utils.file as file + if not file.checkDir(path): + return mw.returnData(False, "不能以系统关键目录作为站点目录") + + info = thisdb.getSitesById(site_id) + if info['path'] == path: + return mw.returnData(False, "与原路径一致,无需修改!") + conf_file = self.getHostConf(info['name']) + content = mw.readFile(conf_file) + if content: + content = content.replace(info['path'], path) + mw.writeFile(file, content) + + thisdb.setSitesData(site_id, path=path) + msg = mw.getInfo('修改网站[{1}]物理路径成功!', (info['name'],)) + mw.writeLog('网站管理', msg) + mw.restartWeb() + return mw.returnData(True, "设置成功!") + + # 设置当前站点运行目录 + def setSiteRunPath(self, site_id, run_path): + info = thisdb.getSitesById(site_id) + site_name = info['name'] + site_path = info['path'] + + new_path = site_path + run_path + + # 处理Nginx + filename = self.getHostConf(site_name) + if os.path.exists(filename): + conf = mw.readFile(filename) + rep = r'\s*root\s*(.+);' + path = re.search(rep, conf).groups()[0] + conf = conf.replace(path, new_path) + mw.writeFile(filename, conf) + + self.setSitePath(site_path, run_path) + mw.restartWeb() + return mw.returnData(True, '设置成功!') + + # 设置目录加密 + def setHasPwd(self, site_id, username, password): + if len(username.strip()) == 0 or len(password.strip()) == 0: + return mw.returnData(False, '用户名或密码不能为空!') + + info = thisdb.getSitesById(site_id) + siteName = info['name'] + filename = self.passPath + '/' + siteName + '.pass' + passconf = username + ':' + mw.hasPwd(password) + + configFile = self.getHostConf(siteName) + # 处理Nginx配置 + conf = mw.readFile(configFile) + if conf: + rep = '#error_page 404 /404.html;' + if conf.find(rep) == -1: + rep = '#error_page 404/404.html;' + data = ''' + #AUTH_START + auth_basic "Authorization"; + auth_basic_user_file %s; + #AUTH_END''' % (filename,) + conf = conf.replace(rep, rep + data) + mw.writeFile(configFile, conf) + # 写密码配置 + passDir = self.passPath + if not os.path.exists(passDir): + mw.execShell('mkdir -p ' + passDir) + mw.writeFile(filename, passconf) + + msg = mw.getInfo('设置网站[{1}]为需要密码认证!', (siteName,)) + mw.writeLog("网站管理", msg) + mw.restartWeb() + return mw.returnData(True, '设置成功!') + + # 取消目录加密 + def closeHasPwd(self, site_id): + info = thisdb.getSitesById(site_id) + siteName = info['name'] + configFile = self.getHostConf(siteName) + if os.path.exists(configFile): + conf = mw.readFile(configFile) + rep = r"\n\s*#AUTH_START(.|\n){1,200}#AUTH_END" + conf = re.sub(rep, '', conf) + mw.writeFile(configFile, conf) + + msg = mw.getInfo('清除网站[{1}]的密码认证!', (siteName,)) + mw.writeLog("网站管理", msg) + mw.restartWeb() + return mw.returnData(True, '设置成功!') + + def getSecurity(self, site_id): + info = thisdb.getSitesById(site_id) + name = info['name'] + filename = self.getHostConf(name) + conf = mw.readFile(filename) + data = {} + if conf.find('SECURITY-START') != -1: + rep = "#SECURITY-START(\n|.){1,500}#SECURITY-END" + tmp = re.search(rep, conf).group() + data['fix'] = re.search(r"\(.+\)\$", tmp).group().replace('(', '').replace(')$', '').replace('|', ',') + + data['status'] = False + data['none'] = False + + valid_referers = re.search(r"valid_referers\s+(.+);\n", tmp) + valid_referers_none = re.search(r"valid_referers\s+none\s+blocked\s+(.+);\n", tmp) + + if valid_referers or valid_referers_none: + data['status'] = True + + if valid_referers_none: + domain_t = valid_referers_none.groups()[0].split() + data['domains'] = ','.join(domain_t) + data['none'] = True + elif valid_referers: + domain_t = valid_referers.groups()[0].split() + data['domains'] = ','.join(domain_t) + data['none'] = False + else: + data['fix'] = 'gif|jpg|jpeg|png|bmp|swf|js|css|ttf|woff2' + domains = thisdb.getDomainBySiteId(site_id) + tmp = [] + for domain in domains: + tmp.append(domain['name']) + data['domains'] = ','.join(tmp) + data['status'] = False + data['none'] = False + return data + + def setSecurity(self, site_id, fix, domains, status, none=''): + info = thisdb.getSitesById(site_id) + name = info['name'] + if len(fix) < 2: + return mw.returnData(False, 'URL后缀不能为空!') + + file = self.getHostConf(name) + if os.path.exists(file): + conf = mw.readFile(file) + if status == 'false': + rep = r"\s{0,4}#SECURITY-START(\n|.){1,500}#SECURITY-END\n?" + conf = re.sub(rep, '', conf) + mw.writeLog('网站管理', '站点[' + name + ']已关闭防盗链设置!') + else: + rep = r"\s{0,4}#SECURITY-START(\n|.){1,500}#SECURITY-END\n?" + conf = re.sub(rep, '', conf) + + valid_referers = domains.strip().replace(',', ' ') + if none == 'true': + valid_referers = 'none blocked ' + valid_referers + + pre_path = self.setupPath + "/php/conf" + re_path = r"include\s+" + pre_path + "/enable-php-" + rconf = r'''#SECURITY-START 防盗链配置 + location ~ .*\.(%s)$ + { + expires 30d; + access_log /dev/null; + valid_referers %s; + if ($invalid_referer){ + return 404; + } + } + #SECURITY-END + include %s/enable-php-''' % (fix.strip().replace(',', '|'), valid_referers, pre_path) + conf = re.sub(re_path, rconf, conf) + mw.writeLog('网站管理', '站点[' + name + ']已开启防盗链!') + mw.writeFile(file, conf) + mw.restartWeb() + return mw.returnData(True, '设置成功!') + + def getSitePhpVersion(self, siteName): + conf = mw.readFile(self.getHostConf(siteName)) + rep = r"enable-php-(.*)\.conf" + find_php_cnf = re.search(rep, conf) + + def_pver = '00' + if find_php_cnf: + tmp = find_php_cnf.groups() + def_pver = tmp[0] + + data = {} + data['phpversion'] = def_pver + return data + + def httpToHttps(self, site_name): + file = self.getHostConf(site_name) + conf = mw.readFile(file) + if not conf: + return mw.returnData(False, '站点[{}]配置异常!'.format(site_name)) + + if conf.find('ssl_certificate') == -1: + return mw.returnData(False, '当前未开启SSL') + to = "#error_page 404/404.html;\n\ + #HTTP_TO_HTTPS_START\n\ + if ($server_port !~ 443){\n\ + rewrite ^(/.*)$ https://$host$1 permanent;\n\ + }\n\ + #HTTP_TO_HTTPS_END" + conf = conf.replace('#error_page 404/404.html;', to) + mw.writeFile(file, conf) + + mw.restartWeb() + return mw.returnData(True, '设置成功!') + + def closeToHttps(self, site_name): + file = self.getHostConf(site_name) + conf = mw.readFile(file) + if not conf: + return mw.returnData(False, '站点[{}]配置异常!'.format(site_name)) + rep = r"\n\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END" + conf = re.sub(rep, '', conf) + rep = r"\s+if.+server_port.+\n.+\n\s+\s*}" + conf = re.sub(rep, '', conf) + mw.writeFile(file, conf) + + mw.restartWeb() + return mw.returnData(True, '关闭HTTPS跳转成功!') + + def getIndex(self, site_id): + info = thisdb.getSitesById(site_id) + file = self.getHostConf(info['name']) + conf = mw.readFile(file) + rep = r"\s+index\s+(.+);" + tmp = re.search(rep, conf).groups() + return tmp[0].replace(' ', ',') + + def setIndex(self, site_id, index): + if index.find('.') == -1: + return mw.returnData(False, '默认文档格式不正确,例:index.html') + + index = index.replace(' ', '') + index = index.replace(',,', ',') + + if len(index) < 3: + return mw.returnData(False, '默认文档不能为空!') + + info = thisdb.getSitesById(site_id) + siteName = info['name'] + index_l = index.replace(",", " ") + file = self.getHostConf(siteName) + conf = mw.readFile(file) + if conf: + rep = r"\s+index\s+.+;" + conf = re.sub(rep, "\n\tindex " + index_l + ";", conf) + mw.writeFile(file, conf) + + mw.writeLog('网站管理', '站点[{1}]设置{2}成功', (siteName, index_l)) + return mw.returnData(True, '设置成功!') + + def getLimitNet(self, site_id): + info = thisdb.getSitesById(site_id) + siteName = info['name'] + filename = self.getHostConf(siteName) + # 站点总并发 + data = {} + conf = mw.readFile(filename) + try: + rep = r"\s+limit_conn\s+perserver\s+([0-9]+);" + tmp = re.search(rep, conf).groups() + data['perserver'] = int(tmp[0]) + + # IP并发限制 + rep = r"\s+limit_conn\s+perip\s+([0-9]+);" + tmp = re.search(rep, conf).groups() + data['perip'] = int(tmp[0]) + + # 请求并发限制 + rep = r"\s+limit_rate\s+([0-9]+)\w+;" + tmp = re.search(rep, conf).groups() + data['limit_rate'] = int(tmp[0]) + except: + data['perserver'] = 0 + data['perip'] = 0 + data['limit_rate'] = 0 + return data + + def setLimitNet(self, site_id, perserver, perip, limit_rate): + str_perserver = 'limit_conn perserver ' + perserver + ';' + str_perip = 'limit_conn perip ' + perip + ';' + str_limit_rate = 'limit_rate ' + limit_rate + 'k;' + + info = thisdb.getSitesById(site_id) + siteName = info['name'] + + filename = self.getHostConf(siteName) + conf = mw.readFile(filename) + if(conf.find('limit_conn perserver') != -1): + # 替换总并发 + rep = r"limit_conn\s+perserver\s+([0-9]+);" + conf = re.sub(rep, str_perserver, conf) + + # 替换IP并发限制 + rep = r"limit_conn\s+perip\s+([0-9]+);" + conf = re.sub(rep, str_perip, conf) + + # 替换请求流量限制 + rep = r"limit_rate\s+([0-9]+)\w+;" + conf = re.sub(rep, str_limit_rate, conf) + else: + conf = conf.replace('#error_page 404/404.html;', "#error_page 404/404.html;\n "+\ + str_perserver+"\n "+\ + str_perip+"\n "+\ + str_limit_rate) + + mw.writeFile(filename, conf) + mw.restartWeb() + mw.writeLog('网站管理', '网站[{1}]流量限制已开启!', (siteName,)) + return mw.returnData(True, '设置成功!') + + def closeLimitNet(self, site_id): + info = thisdb.getSitesById(site_id) + siteName = info['name'] + + filename = self.getHostConf(siteName) + conf = mw.readFile(filename) + # 清理总并发 + rep = r"\s+limit_conn\s+perserver\s+([0-9]+);" + conf = re.sub(rep, '', conf) + + # 清理IP并发限制 + rep = r"\s+limit_conn\s+perip\s+([0-9]+);" + conf = re.sub(rep, '', conf) + + # 清理请求流量限制 + rep = r"\s+limit_rate\s+([0-9]+)\w+;" + conf = re.sub(rep, '', conf) + mw.writeFile(filename, conf) + mw.restartWeb() + mw.writeLog('网站管理', '网站[{1}]流量限制已关闭!', (siteName,)) + return mw.returnData(True, '已关闭流量限制!') + + + # 获取重定向配置 + def getRedirect(self, site_name): + redirect_file = self.getRedirectDataPath(site_name) + if not os.path.exists(redirect_file): + mw.execShell("mkdir " + self.getRedirectPath(site_name)) + return mw.returnData(True, "no exists!", {"result": [], "count": 0}) + + content = mw.readFile(redirect_file) + data = json.loads(content) + + for i in range(len(data)): + redirect_dir = self.getRedirectPath(site_name) + redirect_file = redirect_dir + '/' + data[i]['id'] + '.conf' + if os.path.exists(redirect_file): + data[i]['status'] = True + else: + data[i]['status'] = False + return mw.returnData(True, "ok", {"result": data, "count": len(data)}) + + def setRedirectStatus(self, site_name, redirect_id, status): + if status == '' or site_name == '' or redirect_id == '': + return mw.returnData(False, "必填项不能为空!") + + conf_file = "{}/{}/{}.conf".format(self.redirectPath, site_name, redirect_id) + conf_txt = "{}/{}/{}.conf.txt".format(self.redirectPath, site_name, redirect_id) + + if status == '1': + mw.execShell('mv ' + conf_txt + ' ' + conf_file) + else: + mw.execShell('mv ' + conf_file + ' ' + conf_txt) + + mw.restartWeb() + return mw.returnData(True, "OK") + + # 操作 重定向配置 + def operateRedirectConf(self, siteName, method='start'): + vhost_file = self.getHostConf(siteName) + content = mw.readFile(vhost_file) + + cnf_301 = '''#301-START + include %s/*.conf; + #301-END''' % (self.getRedirectPath(siteName,)) + + cnf_301_source = '#301-START' + # print('operateRedirectConf', content.find('#301-END')) + if content.find('#301-END') != -1: + if method == 'stop': + rep = '#301-START(\n|.){1,500}#301-END' + content = re.sub(rep, '#301-START', content) + else: + if method == 'start': + content = re.sub(cnf_301_source, cnf_301, content) + + mw.writeFile(vhost_file, content) + + # get redirect status + def setRedirect(self, site_name, site_from, to, type, r_type, keep_path): + if site_name == '' or site_from == '' or to == '' or type == '' or r_type == '': + return mw.returnData(False, "必填项不能为空!") + + redirect_file = self.getRedirectDataPath(site_name) + content = mw.readFile(redirect_file) if os.path.exists(redirect_file) else "" + data = json.loads(content) if content != "" else [] + + _r_type = 0 if r_type == "301" else 1 + _type_code = 0 if type == "path" else 1 + _keep_path = 1 if keep_path == "1" else 0 + + # check if domain exists in site + if _type_code == 1: + domain_list = mw.M('domain').where("name=?", (site_name,)).field('id,pid,name,port,add_time').select() + site_domain_lists = mw.M('domain').where("pid=?", (domain_list[0]['pid'],)).field('name').select() + found = False + for item in site_domain_lists: + if item['name'] == site_from: + found = True + break + if found == False: + return mw.returnData(False, "域名不存在!") + + file_content = "" + # path + if _type_code == 0: + redirect_type = "permanent" if _r_type == 0 else "redirect" + if not site_from.startswith("/"): + site_from = "/{}".format(site_from) + if _keep_path == 1: + to = "{}$1".format(to) + site_from = "{}(.*)".format(site_from) + file_content = "rewrite ^{} {} {};".format(site_from, to, redirect_type) + # domain + else: + if _keep_path == 1: + _to = "{}$request_uri".format(to) + + redirect_type = "301" if _type_code == 0 else "302" + _if = "if ($host ~ '^{}')".format(site_from) + _return = "return {} {}; ".format(redirect_type, to) + file_content = _if + "{\r\n " + _return + "\r\n}" + + _id = mw.md5("{}+{}".format(file_content, site_name)) + + # 防止规则重复 + for item in data: + if item["r_from"] == site_from: + return mw.returnData(False, "重复的规则!") + + rep = r"http(s)?\:\/\/([a-zA-Z0-9][-a-zA-Z0-9]{0,62}\.)+([a-zA-Z0-9][a-zA-Z0-9]{0,62})+.?" + if not re.match(rep, to): + return mw.returnData(False, "错误的目标地址") + + # write data json file + data.append({"r_from": site_from, "type": _type_code, "r_type": _type_code,"r_to": to, 'keep_path': _keep_path, 'id': _id}) + mw.writeFile(redirect_file, json.dumps(data)) + mw.writeFile("{}/{}.conf".format(self.getRedirectPath(site_name), _id), file_content) + + self.operateRedirectConf(site_name, 'start') + mw.restartWeb() + return mw.returnData(True, "设置成功") + + def getRedirectConf(self, site_name, redirect_id): + if redirect_id == '' or site_name == '': + return mw.returnData(False, "必填项不能为空!") + + path = self.getRedirectPath(site_name) + conf = "{}/{}.conf".format(path, redirect_id) + data = mw.readFile(conf) + if data == False: + return mw.returnData(False, "获取失败!") + return mw.returnData(True, "ok", {"result": data}) + + # 删除指定重定向 + def delRedirect(self,siteName, rid): + if rid == '' or siteName == '': + return mw.returnData(False, "必填项不能为空!") + + try: + data_path = self.getRedirectDataPath(siteName) + data_content = mw.readFile(data_path) if os.path.exists(data_path) else "" + data = json.loads(data_content) if data_content != "" else [] + for item in data: + if item["id"] == rid: + data.remove(item) + break + # write database + mw.writeFile(data_path, json.dumps(data)) + # data is empty ,should stop + if len(data) == 0: + self.operateRedirectConf(siteName, 'stop') + # remove conf file + mw.execShell("rm -rf {}/{}.conf".format(self.getRedirectPath(siteName), rid)) + except Exception as e: + return mw.returnData(False, "删除失败:"+str(e)) + return mw.returnData(True, "删除成功!") + + # 读取 网站 反向代理列表 + def getProxyList(self, site_name): + data_path = self.getProxyDataPath(site_name) + + if not os.path.exists(data_path): + mw.execShell("mkdir {}/{}".format(self.proxyPath, site_name)) + return mw.returnData(True, "", {"result": [], "count": 0}) + + content = mw.readFile(data_path) + data = json.loads(content) + tmp = [] + for proxy in data: + proxy_dir = "{}/{}".format(self.proxyPath, site_name) + proxy_dir_file = proxy_dir + '/' + proxy['id'] + '.conf' + if os.path.exists(proxy_dir_file): + proxy['status'] = True + else: + proxy['status'] = False + tmp.append(proxy) + return mw.returnData(True, "ok", {"result": data, "count": len(data)}) + + # 操作 反向代理配置 + def operateProxyConf(self, site_name, method='start'): + vhost_file = self.getHostConf(site_name) + content = mw.readFile(vhost_file) + + proxy_cnf = '''#PROXY-START + include %s/*.conf; + #PROXY-END''' % (self.getProxyPath(site_name)) + + proxy_cnf_source = '#PROXY-START' + + if content.find('#PROXY-END') != -1: + if method == 'stop': + rep = '#PROXY-START(\n|.){1,500}#PROXY-END' + content = re.sub(rep, '#PROXY-START', content) + else: + if method == 'start': + content = re.sub(proxy_cnf_source, proxy_cnf, content) + + mw.writeFile(vhost_file, content) + + # 设置 网站 反向代理列表 + def setProxy(self, site_name, site_from, to, host, name, open_proxy, open_cache, cache_time, proxy_id): + from urllib.parse import urlparse + if site_name == "" or site_from == "" or to == "" or host == "" or name == "": + return mw.returnData(False, "必填项不能为空") + + rep = r"http(s)?\:\/\/([a-zA-Z0-9][-a-zA-Z0-9]{0,62}\.)+([a-zA-Z0-9][a-zA-Z0-9]{0,62})+.?" + if not re.match(rep, to): + return mw.returnData(False, "错误的目标地址!") + + # get host from url + try: + if host == "$host": + host_tmp = urlparse(_to) + host = host_tmp.netloc + except: + return mw.returnData(False, "错误的目标地址") + + proxy_site_path = self.getProxyDataPath(site_name) + data_content = mw.readFile(proxy_site_path) if os.path.exists(proxy_site_path) else "" + data = json.loads(data_content) if data_content != "" else [] + + proxy_action = 'add' + if proxy_id == "": + proxy_id = mw.md5("{}".format(name)) + else: + proxy_action = 'edit' + + if proxy_action == "add": + for item in data: + if item["name"] == name: + return mw.returnData(False, "名称重复!!") + if item["from"] == site_from: + return mw.returnData(False, "代理目录已存在!!") + + tpl = "#PROXY-START\n\ +location ^~ {from} {\n\ + proxy_pass {to};\n\ + proxy_set_header Host {host};\n\ + proxy_ssl_server_name on;\n\ + proxy_set_header X-Real-IP $remote_addr;\n\ + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n\ + proxy_set_header REMOTE-HOST $remote_addr;\n\ + proxy_set_header Upgrade $http_upgrade;\n\ + proxy_set_header Connection $connection_upgrade;\n\ + proxy_http_version 1.1;\n\ + \n\ + add_header X-Cache $upstream_cache_status;\n\ + \n\ + {proxy_cache}\n\ +}\n\ +# PROXY-END" + + tpl_proxy_cache = "\n\ + if ( $uri ~* \\.(gif|png|jpg|css|js|woff|woff2)$\" )\n\ + {\n\ + expires {cache_time}m;\n\ + }\n\ + proxy_ignore_headers Set-Cookie Cache-Control expires;\n\ + proxy_cache mw_cache;\n\ + proxy_cache_key \"$host$uri$is_args$args\";\n\ + proxy_cache_valid 200 304 301 302 {cache_time}m;\n\ +" + + tpl_proxy_nocache = "\n\ + set $static_files_app 0; \n\ + if ( $uri ~* \\.(gif|png|jpg|css|js|woff|woff2)$\" )\n\ + {\n\ + set $static_files_app 1;\n\ + expires 12h;\n\ + }\n\ + if ( $static_files_app = 0 )\n\ + {\n\ + add_header Cache-Control no-cache;\n\ + }\n\ +" + + # replace + if site_from[0] != '/': + site_from = '/' + site_from + tpl = tpl.replace("{from}", site_from, 999) + tpl = tpl.replace("{to}", to) + tpl = tpl.replace("{host}", host, 999) + tpl = tpl.replace("{cache_time}", cache_time, 999) + + if open_cache == 'on': + tpl_proxy_cache = tpl_proxy_cache.replace("{cache_time}", cache_time, 999) + tpl = tpl.replace("{proxy_cache}", tpl_proxy_cache, 999) + else: + tpl = tpl.replace("{proxy_cache}", tpl_proxy_nocache, 999) + + + conf_proxy = "{}/{}.conf".format(self.getProxyPath(site_name), proxy_id) + conf_bk = "{}/{}.conf.txt".format(self.getProxyPath(site_name), proxy_id) + mw.writeFile(conf_proxy, tpl) + + rule_test = mw.checkWebConfig() + if rule_test != True: + os.remove(conf_proxy) + return mw.returnData(False, "OpenResty配置测试不通过, 请重试: {}".format(rule_test)) + + if proxy_action == "add": + # 添加代理 + proxy_id = mw.md5("{}".format(name)) + for item in data: + if item["name"] == name: + return mw.returnData(False, "名称重复!") + if item["from"] == site_from: + return mw.returnData(False, "代理目录已存在!") + data.append({ + "name": name, + "from": site_from, + "to": to, + "host": host, + "open_cache": open_cache, + "open_proxy": open_proxy, + "cache_time": cache_time, + "id": proxy_id, + }) + else: + # 修改代理 + dindex = -1 + for x in range(len(data)): + if data[x]["id"] == proxy_id: + dindex = x + break + if dindex < 0: + return mw.returnData(False, "异常请求") + data[dindex]['from'] = site_from + data[dindex]['to'] = to + data[dindex]['host'] = host + data[dindex]['open_cache'] = open_cache + data[dindex]['open_proxy'] = open_proxy + data[dindex]['cache_time'] = cache_time + + if open_proxy != 'on': + os.rename(conf_proxy, conf_bk) + + mw.writeFile(proxy_site_path, json.dumps(data)) + self.operateProxyConf(site_name, 'start') + mw.restartWeb() + return mw.returnData(True, "ok", {"hash": proxy_id}) + + def setProxyStatus(self, site_name, proxy_id, status): + if status == '' or site_name == '' or proxy_id == '': + return mw.returnData(False, "必填项不能为空!") + + conf_file = "{}/{}/{}.conf".format(self.proxyPath, site_name, proxy_id) + conf_txt = "{}/{}/{}.conf.txt".format(self.proxyPath, site_name, proxy_id) + + if status == '1': + mw.execShell('mv ' + conf_txt + ' ' + conf_file) + else: + mw.execShell('mv ' + conf_file + ' ' + conf_txt) + + mw.restartWeb() + return mw.returnData(True, "OK") + + + def closeProxyAll(self, site_name): + self.close_proxy = [] + proxy_path = self.getProxyDataPath(site_name) + if os.path.exists(proxy_path): + content = mw.readFile(proxy_path) + data = json.loads(content) + for proxy in data: + proxy_dir = "{}/{}".format(self.proxyPath, site_name) + proxy_conf = proxy_dir + '/' + proxy['id'] + '.conf' + proxy_txt = "{}/{}/{}.conf.txt".format(self.proxyPath, site_name, proxy['id']) + if os.path.exists(proxy_conf): + self.close_proxy.append(proxy['id']) + mw.execShell('mv ' + proxy_conf + ' ' + proxy_txt) + mw.restartWeb() + return True + + def openProxyByOpen(self, site_name): + for proxy_id in self.close_proxy: + proxy_dir = "{}/{}".format(self.proxyPath, site_name) + proxy_conf = proxy_dir + '/' + proxy_id + '.conf' + proxy_txt = "{}/{}/{}.conf.txt".format(self.proxyPath, site_name, proxy_id) + if os.path.exists(proxy_txt): + mw.execShell('mv ' + proxy_txt + ' ' + proxy_conf) + + if len(self.close_proxy) > 0: + mw.restartWeb() + self.close_proxy = [] + return True + + def closeRedirectAll(self, site_name): + self.close_redirect = [] + redirect_path = self.getRedirectDataPath(site_name) + if os.path.exists(redirect_path): + content = mw.readFile(redirect_path) + data = json.loads(content) + for redirect_data in data: + redirect_dir = "{}/{}".format(self.redirectPath, site_name) + redirect_conf = redirect_dir + '/' + redirect_data['id'] + '.conf' + redirect_txt = "{}/{}/{}.conf.txt".format(self.redirectPath, site_name, redirect_data['id']) + if os.path.exists(redirect_conf): + self.close_redirect.append(redirect_data['id']) + mw.execShell('mv ' + redirect_conf + ' ' + redirect_txt) + mw.restartWeb() + + def openRedirectByOpen(self, site_name): + for redirect_id in self.close_redirect: + redirect_dir = "{}/{}".format(self.redirectPath, site_name) + redirect_conf = redirect_dir + '/' + redirect_id + '.conf' + redirect_txt = "{}/{}/{}.conf.txt".format(self.redirectPath, site_name, redirect_id) + if os.path.exists(redirect_txt): + mw.execShell('mv ' + redirect_txt + ' ' + redirect_conf) + + if len(self.close_redirect) > 0: + mw.restartWeb() + self.close_redirect = [] + return True + + + def getProxyConf(self, site_name, proxy_id): + if pid == '' or site_name == '': + return mw.returnData(False, "必填项不能为空!") + + conf_file = "{}/{}/{}.conf".format(self.proxyPath, site_name, proxy_id) + if not os.path.exists(conf_file): + conf_file = "{}/{}/{}.conf.txt".format(self.proxyPath, site_name, proxy_id) + + if not os.path.exists(conf_file): + return mw.returnData(False, "获取失败!") + + data = mw.readFile(conf_file) + return mw.returnData(True, "ok", {"result": data}) + + def delProxy(self, site_name, proxy_id): + if proxy_id == '' or site_name == '': + return mw.returnData(False, "必填项不能为空!") + + try: + data_path = self.getProxyDataPath(site_name) + data_content = mw.readFile(data_path) if os.path.exists(data_path) else "" + data = json.loads(data_content) if data_content != "" else [] + for item in data: + if item["id"] == proxy_id: + data.remove(item) + break + # write database + mw.writeFile(data_path, json.dumps(data)) + + # data is empty,should stop + if len(data) == 0: + self.operateProxyConf(site_name, 'stop') + # remove conf file + cmd = "rm -rf {}/{}.conf*".format(self.getProxyPath(site_name), proxy_id) + mw.execShell(cmd) + except: + return mw.returnData(False, "删除反代失败!") + + mw.restartWeb() + return mw.returnData(True, "删除反代成功!") + + # 是否跳转到https + def isToHttps(self, site_name): + file = self.getHostConf(site_name) + conf = mw.readFile(file) + if conf: + if conf.find('$server_port !~ 443') != -1: + return True + return False + + def getSsl(self, site_name, ssl_type): + file = self.getHostConf(site_name) + content = mw.readFile(file) + + key_text = 'ssl_certificate' + status = True + stype = 0 + if content.find(key_text) == -1: + status = False + stype = -1 + + to_https = self.isToHttps(site_name) + + site_info = thisdb.getSitesByName(site_name) + domains = thisdb.getDomainBySiteId(site_info['id']) + + path = self.sslDir + '/' + site_name + if ssl_type == 'lets': + csr_path = self.sslLetsDir + '/' + site_name + '/fullchain.pem' # Let生成证书路径 + key_path = self.sslLetsDir + '/' + site_name + '/privkey.pem' # Let密钥文件路径 + elif ssl_type == 'acme': + csr_path = path + '/fullchain.pem' + key_path = path + '/privkey.pem' + # acme_dir = mw.getAcmeDomainDir(site_name) + # csr_path = acme_dir + '/fullchain.cer' # ACME生成证书路径 + # key_path = acme_dir + '/' + site_name + '.key' # ACME密钥文件路径 + else: + csr_path = path + '/fullchain.pem' # 生成证书路径 + key_path = path + '/privkey.pem' # 密钥文件路径 + + key = '' + if os.path.exists(key_path): + key = mw.readFile(key_path) + + csr = '' + if os.path.exists(csr_path): + csr = mw.readFile(csr_path) + + cert_data = mw.getCertName(csr_path) + # print(csr_path,cert_data) + data = { + 'status': status, + 'domain': domains, + 'key': key, + 'csr': csr, + 'type': stype, + 'httpTohttps': to_https, + 'cert_data': cert_data, + } + return mw.returnData(True, 'OK', data) + + def saveCert(self, site_name, keyPath, certPath): + try: + certInfo = mw.getCertName(certPath) + if not certInfo: + return mw.returnData(False, '证书解析失败!') + vpath = self.sslDir + '/' + site_name + if not os.path.exists(vpath): + os.system('mkdir -p ' + vpath) + mw.writeFile(vpath + '/privkey.pem', mw.readFile(keyPath)) + mw.writeFile(vpath + '/fullchain.pem', mw.readFile(certPath)) + mw.writeFile(vpath + '/info.json', json.dumps(certInfo)) + return mw.returnData(True, '证书保存成功!') + except Exception as e: + return mw.returnData(False, '证书保存失败!') + + def getCertList(self): + try: + vpath = self.sslDir + if not os.path.exists(vpath): + os.system('mkdir -p ' + vpath) + data = [] + for d in os.listdir(vpath): + + # keyPath = self.sslDir + siteName + '/privkey.pem' + # certPath = self.sslDir + siteName + '/fullchain.pem' + + keyPath = vpath + '/' + d + '/privkey.pem' + certPath = vpath + '/' + d + '/fullchain.pem' + if os.path.exists(keyPath) and os.path.exists(certPath): + self.saveCert(d, keyPath, certPath) + + mpath = vpath + '/' + d + '/info.json' + if not os.path.exists(mpath): + continue + + tmp = mw.readFile(mpath) + if not tmp: + continue + tmp1 = json.loads(tmp) + data.append(tmp1) + return mw.returnData(True, 'OK', data) + except: + return mw.returnData(True, 'OK', []) + + + def setPhpVersion(self, siteName, version): + # nginx + file = self.getHostConf(siteName) + conf = mw.readFile(file) + if conf: + rep = r"enable-php-(.*)\.conf" + tmp = re.search(rep, conf).group() + conf = conf.replace(tmp, 'enable-php-' + version + '.conf') + mw.writeFile(file, conf) + + msg = mw.getInfo('成功切换网站[{1}]的PHP版本为PHP-{2}', (siteName, version)) + mw.writeLog("网站管理", msg) + mw.restartWeb() + return mw.returnData(True, msg) + + + def setDefaultSite(self, name): + # 清理旧的 + default_site = thisdb.getOption('default_site', default='') + if default_site: + path = self.getHostConf(default_site) + if os.path.exists(path): + conf = mw.readFile(path) + rep = r"listen\s+80.+;" + conf = re.sub(rep, 'listen 80;', conf, 1) + rep = r"listen\s+443.+;" + conf = re.sub(rep, 'listen 443 ssl;', conf, 1) + mw.writeFile(path, conf) + + path = self.getHostConf(name) + if os.path.exists(path): + conf = mw.readFile(path) + rep = r"listen\s+80\s*;" + conf = re.sub(rep, 'listen 80 default_server;', conf, 1) + rep = r"listen\s+443\s*ssl\s*\w*\s*;" + conf = re.sub(rep, 'listen 443 ssl default_server;', conf, 1) + mw.writeFile(path, conf) + + thisdb.setOption('default_site', name) + mw.restartWeb() + return mw.returnData(True, '设置成功!') + + def setCliPhpVersion(self, version): + php_bin = '/usr/bin/php' + php_bin_src = "/www/server/php/%s/bin/php" % version + php_ize = '/usr/bin/phpize' + php_ize_src = "/www/server/php/%s/bin/phpize" % version + php_fpm = '/usr/bin/php-fpm' + php_fpm_src = "/www/server/php/%s/sbin/php-fpm" % version + php_pecl = '/usr/bin/pecl' + php_pecl_src = "/www/server/php/%s/bin/pecl" % version + php_pear = '/usr/bin/pear' + php_pear_src = "/www/server/php/%s/bin/pear" % version + if not os.path.exists(php_bin_src): + return mw.returnData(False, '指定PHP版本未安装!') + + is_chattr = mw.execShell('lsattr /usr|grep /usr/bin')[0].find('-i-') + if is_chattr != -1: + mw.execShell('chattr -i /usr/bin') + mw.execShell("rm -f " + php_bin + ' ' + php_ize + ' ' + php_fpm + ' ' + php_pecl + ' ' + php_pear) + mw.execShell("ln -sf %s %s" % (php_bin_src, php_bin)) + mw.execShell("ln -sf %s %s" % (php_ize_src, php_ize)) + mw.execShell("ln -sf %s %s" % (php_fpm_src, php_fpm)) + mw.execShell("ln -sf %s %s" % (php_pecl_src, php_pecl)) + mw.execShell("ln -sf %s %s" % (php_pear_src, php_pear)) + if is_chattr != -1: + mw.execShell('chattr +i /usr/bin') + mw.writeLog('面板设置', '设置PHP-CLI版本为: %s' % version) + return mw.returnData(True, '设置成功!') + + def addSiteTypes(name): + if not name: + return mw.returnData(False, "分类名称不能为空") + if len(name) > 18: + return mw.returnData(False, "分类名称长度不能超过6个汉字或18位字母") + + all_count = thisdb.getSiteTypesCount() + if all_count >= 10: + return mw.returnData(False, '最多添加10个分类!') + + name_count = thisdb.getSiteTypesCountByName(name) + if name_count > 0: + return mw.returnData(False, "指定分类名称已存在!") + + thisdb.addSiteTypes(name) + return mw.returnData(True, '添加成功!') + + def getDnsapi(self): + dnsapi_data = thisdb.getOptionByJson('dnsapi', default={}) + dnsapi_option = [ + {"name":"none", "title":'手动解析', 'key':'', 'help':''}, + {"name":"dns_ali", "title":'Aliyun', 'key':'Ali_Key:Ali_Secret', 'help':'阿里云控制台》用户头像》accesskeys按指引获取AccessKey/SecretKey'}, + {"name":"dns_cf", "title":'cloudflare', 'key':'CF_Key:CF_Email', 'help':'CloudFlare后台获取Global API Key'}, + {"name":"dns_dp", "title":'dnspod/国内', 'key':'DP_Id:DP_Key','help':'DnsPod后台》用户中心》安全设置,开启API Token'}, + {"name":"dns_dpi", "title":'dnspod/国际', 'key':'DPI_Id:DPI_Key','help':'DnsPod后台》用户中心》安全设置,开启API Token'}, + {"name":"dns_tencent", "title":"腾讯云DNS", 'key':'Tencent_SecretId:Tencent_SecretKey', 'help':'腾讯云后台获取通行证'}, + {"name":"dns_gd", "title":'GoDaddy', 'key':'GD_Key:GD_Secret'}, + # {"name":"dns_pdns", "title":'PowerDNS', 'key':'PDNS_Url:PDNS_ServerId:PDNS_Token:PDNS_Ttl'}, + # {"name":"dns_lua", "title":'LuaDNS', 'key':'LUA_Key:LUA_Email'}, + # {"name":"dns_me", "title":'DNSMadeEasy', 'key':'ME_Key:ME_Secret'}, + {"name":"dns_aws", "title":'Amazon Route53', 'key':'AWS_ACCESS_KEY_ID:AWS_SECRET_ACCESS_KEY'}, + # {"name":"dns_ispconfig", "title":'ISPConfig', 'key':'ISPC_User:ISPC_Password:ISPC_Api:ISPC_Api_Insecure'}, + # {"name":"dns_ad", "title":'Alwaysdata', 'key':'AD_API_KEY'}, + {"name":"dns_linode_v4", "title":'Linode', 'key':'LINODE_V4_API_KEY'}, + # {"name":"dns_freedns", "title":'FreeDNS', 'key':'FREEDNS_User:FREEDNS_Password'}, + # {"name":"dns_cyon", "title":'cyon.ch', 'key':'CY_Username:CY_Password:CY_OTP_Secret'}, + # {"name":"dns_gandi_livedns", "title":'LiveDNS', 'key':'GANDI_LIVEDNS_TOKEN'}, + # {"name":"dns_knot", "title":'Knot', 'key':'KNOT_SERVER:KNOT_KEY'}, + {"name":"dns_dgon", "title":'DigitalOcean', 'key':'DO_API_KEY'}, + # {"name":"dns_cloudns", "title":'ClouDNS.net', 'key':'CLOUDNS_SUB_AUTH_ID:CLOUDNS_AUTH_PASSWORD'}, + {"name":"dns_namesilo", "title":'Namesilo', 'key':'Namesilo_Key'}, + {"name":"dns_azure", "title":'Azure', 'key':'AZUREDNS_SUBSCRIPTIONID:AZUREDNS_TENANTID:AZUREDNS_APPID:AZUREDNS_CLIENTSECRET'}, + # {"name":"dns_selectel", "title":'selectel.com', 'key':'SL_Key'}, + # {"name":"dns_zonomi", "title":'zonomi.com', 'key':'ZM_Key'}, + # {"name":"dns_kinghost", "title":'KingHost', 'key':'KINGHOST_Username:KINGHOST_Password'}, + # {"name":"dns_zilore", "title":'Zilore', 'key':'Zilore_Key'}, + {"name":"dns_gcloud", "title":'Google Cloud DNS', 'key':'CLOUDSDK_ACTIVE_CONFIG_NAME'}, + # {"name":"dns_mydnsjp", "title":'MyDNS.JP', 'key':'MYDNSJP_MasterID:MYDNSJP_Password'}, + # {"name":"dns_doapi", "title":'do.de', 'key':'DO_LETOKEN'}, + # {"name":"dns_online", "title":'Online', 'key':'ONLINE_API_KEY'}, + # {"name":"dns_cn", "title":'Core-Networks', 'key':'CN_User:CN_Password'}, + # {"name":"dns_ultra", "title":'UltraDNS', 'key':'ULTRA_USR:ULTRA_PWD'}, + # {"name":"dns_hetzner", "title":'Hetzner', 'key':'HETZNER_Token'}, + # {"name":"dns_ddnss", "title":'DDNSS.de', 'key':'DDNSS_Token'}, + ]; + + for i in range(len(dnsapi_option)): + dval = dnsapi_option[i]['key'] + dname = dnsapi_option[i]['name'] + if dname == 'none': + continue + + keys = dval.split(':') + data = {} + if dname in dnsapi_data: + dnsapi_option[i]['data'] = dnsapi_data[dname] + dnsapi_option[i]['title'] = dnsapi_option[i]['title'] + ' - [已配置]' + else: + t = {} + for field in keys: + t[field] = '' + dnsapi_option[i]['data'] = t + return dnsapi_option + + def setDnsapi(self, type, data): + dnsapi_data = thisdb.getOptionByJson('dnsapi', default={}) + dnsapi_data[type] = json.loads(data) + thisdb.setOption('dnsapi',json.dumps(dnsapi_data)) + return mw.returnData(True, '设置成功!') + + def acmeLogFile(self): + return mw.getPanelDir() + '/logs/acme.log' + + def writeAcmeLog(self,msg): + log_file = self.acmeLogFile() + mw.writeFile(log_file, msg+"\n", 'w+') + return True + + def letLogFile(self): + return mw.getPanelDir() + '/logs/letsencrypt.log' + + def writeLetLog(self,msg): + log_file = self.letLogFile() + mw.writeFile(log_file, msg+"\n", "wb+") + return True + + def closeSslConf(self, site_name): + file = self.getHostConf(site_name) + conf = mw.readFile(file) + + if conf: + rep = "\n\\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END" + conf = re.sub(rep, '', conf) + rep = "\\s+ssl_certificate\\s+.+;\\s+ssl_certificate_key\\s+.+;" + conf = re.sub(rep, '', conf) + rep = "\\s+ssl_protocols\\s+.+;\n" + conf = re.sub(rep, '', conf) + rep = "\\s+ssl_ciphers\\s+.+;\n" + conf = re.sub(rep, '', conf) + rep = "\\s+ssl_prefer_server_ciphers\\s+.+;\n" + conf = re.sub(rep, '', conf) + rep = "\\s+ssl_session_cache\\s+.+;\n" + conf = re.sub(rep, '', conf) + rep = r"\s+ssl_session_timeout\s+.+;\n" + conf = re.sub(rep, '', conf) + rep = r"\s+ssl_ecdh_curve\s+.+;\n" + conf = re.sub(rep, '', conf) + rep = r"\s+ssl_session_tickets\s+.+;\n" + conf = re.sub(rep, '', conf) + rep = r"\s+ssl_stapling\s+.+;\n" + conf = re.sub(rep, '', conf) + rep = r"\s+ssl_stapling_verify\s+.+;\n" + conf = re.sub(rep, '', conf) + rep = r"\s+add_header\s+.+;\n" + conf = re.sub(rep, '', conf) + rep = r"\s+add_header\s+.+;\n" + conf = re.sub(rep, '', conf) + rep = r"\s+ssl\s+on;" + conf = re.sub(rep, '', conf) + rep = r"\s+error_page\s497.+;" + conf = re.sub(rep, '', conf) + rep = r"\s+if.+server_port.+\n.+\n\s+\s*}" + conf = re.sub(rep, '', conf) + rep = r"\s+listen\s+443.*;" + conf = re.sub(rep, '', conf) + rep = r"\s+listen\s+\[\:\:\]\:443.*;" + conf = re.sub(rep, '', conf) + rep = r"\s+http2\s+on;" + conf = re.sub(rep, '', conf) + mw.writeFile(file, conf) + + msg = mw.getInfo('网站[{1}]关闭SSL成功!', (site_name,)) + mw.writeLog('网站管理', msg) + mw.restartWeb() + return mw.returnData(True, 'SSL已关闭!') + + def deleteSsl(self,site_name,ssl_type): + path = self.sslDir + '/' + site_name + csr_path = path + '/fullchain.pem' + + file = self.getHostConf(site_name) + content = mw.readFile(file) + key_text = 'ssl_certificate' + status = True + if content.find(key_text) == -1: + status = False + + if ssl_type == 'now': + if status: + return mw.returnData(False, '使用中,先关闭再删除') + if os.path.exists(path): + mw.execShell('rm -rf ' + path) + else: + return mw.returnData(False, '还未申请!') + elif ssl_type == 'lets': + ssl_lets_dir = self.sslLetsDir + '/' + site_name + csr_lets_path = ssl_lets_dir + '/fullchain.pem' # 生成证书路径 + if mw.md5(mw.readFile(csr_lets_path)) == mw.md5(mw.readFile(csr_path)): + return mw.returnData(False, '使用中,先关闭再删除') + mw.execShell('rm -rf ' + ssl_lets_dir) + elif ssl_type == 'acme': + ssl_acme_dir = mw.getAcmeDomainDir(site_name) + csr_acme_path = ssl_acme_dir + '/fullchain.cer' # 生成证书路径 + if mw.md5(mw.readFile(csr_acme_path)) == mw.md5(mw.readFile(csr_path)): + return mw.returnData(False, '使用中,先关闭再删除') + mw.execShell('rm -rf ' + ssl_acme_dir) + + mw.restartWeb() + return mw.returnData(True, '删除成功') + + def createAcmeFile(self, site_name, domains, email, force, renew): + site_conf = self.getHostConf(site_name) + if not os.path.exists(site_conf): + return mw.returnData(False, '配置异常!') + + # 关闭反向代理 + self.closeProxyAll(site_name) + # 关闭重定向 + self.closeRedirectAll(site_name) + + site_info = thisdb.getSitesByName(site_name) + path = self.getSitePath(site_name) + if path == '': + return mw.returnData(False, '【'+site_name+'】配置文件,异常!') + + src_path = site_info['path'] + acme_dir = mw.getAcmeDir() + + if force == 'true': + force_bool = True + + if renew == 'true': + cmd = acme_dir + "/acme.sh --renew --yes-I-know-dns-manual-mode-enough-go-ahead-please" + else: + cmd = acme_dir + "/acme.sh --issue --force" + + # 确定主域名顺序 + t = [] + if site_name in domains: + t.append(site_name) + for dd in domains: + if dd == site_name: + continue + t.append(dd) + domains = t + + domain_nums = 0 + for d in domains: + if mw.checkIp(d): + continue + if d.find('*.') != -1: + return mw.returnData(False, '泛域名不能使用【文件验证】的方式申请证书!') + cmd += ' -w ' + path + cmd += ' -d ' + d + domain_nums += 1 + if domain_nums == 0: + return mw.returnData(False, '请选择域名(不包括IP地址与泛域名)!') + + self.writeAcmeLog('开始ACME申请...') + log_file = self.acmeLogFile() + + cmd = 'export ACCOUNT_EMAIL=' + email + ' && ' + cmd + ' >> ' + log_file + result = mw.execShell(cmd) + + # 开启代理 + self.openProxyByOpen(site_name) + # 开启重定向 + self.openRedirectByOpen(site_name) + + src_path = mw.getAcmeDomainDir(domains[0]) + src_cert = src_path + '/fullchain.cer' + src_key = src_path + '/' + domains[0] + '.key' + src_cert.replace("\\*", "*") + + msg = '签发失败,您尝试申请证书的失败次数已达上限!

                                1、检查域名是否绑定到对应站点

                                \ +

                                2、检查域名是否正确解析到本服务器,或解析还未完全生效

                                \ +

                                3、如果您的站点设置了反向代理,或使用了CDN,请先将其关闭

                                \ +

                                4、如果您的站点设置了301重定向,请先将其关闭

                                \ +

                                5、如果以上检查都确认没有问题,请尝试更换DNS服务商

                                ' + if not os.path.exists(src_cert): + data = {} + data['err'] = result + data['out'] = result[0] + data['msg'] = msg + data['result'] = {} + if result[1].find('new-authz error:') != -1: + data['result'] = json.loads(re.search("{.+}", result[1]).group()) + if data['result']['status'] == 429: + data['msg'] = msg + data['status'] = False + return data + + dst_path = self.sslDir + '/' + site_name + dst_cert = dst_path + "/fullchain.pem" # 生成证书路径 + dst_key = dst_path + "/privkey.pem" # 密钥文件路径 + + if not os.path.exists(dst_path): + mw.execShell("mkdir -p " + dst_path) + + mw.buildSoftLink(src_cert, dst_cert, True) + mw.buildSoftLink(src_key, dst_key, True) + mw.execShell('echo "acme" > "' + dst_path + '/README"') + + # 写入配置文件 + result = self.setSslConf(site_name) + if not result['status']: + return result + result['csr'] = mw.readFile(src_cert) + result['key'] = mw.readFile(src_key) + + mw.restartWeb() + return mw.returnData(True, '证书已更新!', result) + + def getDnsapiExportVar(self, data): + def_var = '' + for k in data: + def_var += 'export '+k+'="'+data[k]+'"\n' + return def_var + + def getDomainRootName(self, domain): + s = domain.split('.') + count = len(s) + last_index = count - 1 + top_domain = s[last_index-1]+'.'+s[last_index] + return top_domain + + def createAcmeDns(self, site_name, domains, email, dnspai, wildcard_domain, force, renew): + dnsapi_option = thisdb.getOptionByJson('dnsapi', default={}) + if not dnspai in dnsapi_option: + return mw.returnData(False, '['+dnspai+']未设置!') + + dnsapi_data = dnsapi_option[dnspai] + for k in dnsapi_data: + if dnsapi_data[k] == '': + return mw.returnData(False, k+'为空!') + + acme_dir = mw.getAcmeDir() + for d in domains: + cmd = ''' +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin:%s +export PATH +''' % (acme_dir,) + cmd += "acme.sh --register-account -m "+email+" \n" + cmd += self.getDnsapiExportVar(dnsapi_data) + if wildcard_domain == 'true': + top_domain = self.getDomainRootName(d) + cmd += 'acme.sh --issue --dns '+str(dnspai)+' -d '+top_domain+' -d "*.'+top_domain+'"' + d = top_domain + else: + cmd += 'acme.sh --issue --dns '+str(dnspai)+' -d '+d + + log_file = self.acmeLogFile() + cmd += ' >> ' + log_file + # print(cmd) + result = mw.execShell(cmd) + # print(result) + + # acme源的ssl证书 + src_path = mw.getAcmeDomainDir(d) + src_cert = src_path + '/fullchain.cer' + src_key = src_path + '/' + d + '.key' + src_cert.replace("\\*", "*") + + msg = '签发失败,您尝试申请证书的失败次数已达上限!\ +

                                1、检查域名是否正确解析到本服务器,或解析还未完全生效

                                \ +

                                2、如果以上检查都确认没有问题,请尝试更换DNS服务商

                                ' + if not os.path.exists(src_cert): + data = {} + data['err'] = result + data['out'] = result[0] + data['msg'] = msg + data['result'] = {} + if result[1].find('new-authz error:') != -1: + data['result'] = json.loads(re.search("{.+}", result[1]).group()) + if data['result']['status'] == 429: + data['msg'] = msg + data['status'] = False + return data + + # acme源建立软链接(目标) + dst_path = self.sslDir + '/' + site_name + dst_cert = dst_path + "/fullchain.pem" # 生成证书路径 + dst_key = dst_path + "/privkey.pem" # 密钥文件路径 + + if not os.path.exists(dst_path): + mw.execShell("mkdir -p " + dst_path) + + mw.buildSoftLink(src_cert, dst_cert, True) + mw.buildSoftLink(src_key, dst_key, True) + mw.execShell('echo "acme" > "' + dst_path + '/README"') + + # 写入配置文件 + result = self.setSslConf(site_name) + if not result['status']: + return result + result['csr'] = mw.readFile(src_cert) + result['key'] = mw.readFile(src_key) + + mw.restartWeb() + return mw.returnData(True, '证书已更新!', result) + + def createAcme(self, site_name, domains, force, renew, apply_type, dnspai, email, wildcard_domain): + domains = json.loads(domains) + if len(domains) < 1: + return mw.returnData(False, '请选择域名') + if email.strip() != '': + thisdb.setOption('ssl_email', email) + + if email.strip() == '': + email = mw.getRandomString(10)+"."+mw.getRandomString(3) + '@gmail.com' + + # 检测acme是否安装 + acme_dir = mw.getAcmeDir() + if not os.path.exists(acme_dir): + try: + mw.execShell("curl -sS curl https://get.acme.sh | sh") + except: + pass + if not os.path.exists(acme_dir): + return mw.returnData(False, '尝试自动安装ACME失败,请通过以下命令尝试手动安装

                                安装命令: curl https://get.acme.sh | sh

                                ') + + # 避免频繁执行 + checkAcmeRun = mw.execShell('ps -ef|grep acme.sh |grep -v grep') + if checkAcmeRun[0] != '': + return mw.returnData(False, '正在申请或更新SSL中...') + + if apply_type == 'file': + return self.createAcmeFile(site_name, domains, email,force,renew) + elif apply_type == 'dns': + return self.createAcmeDns(site_name, domains, email, dnspai, wildcard_domain,force, renew) + return mw.returnData(False, '异常请求') + + def createLet(self, site_name, domains, force, renew, apply_type, dnspai, email, wildcard_domain): + domains = json.loads(domains) + if len(domains) < 1: + return mw.returnData(False, '请选择域名') + if email.strip() != '': + thisdb.setOption('ssl_email', email) + + + host_conf_file = self.getHostConf(site_name) + if os.path.exists(host_conf_file): + siteConf = mw.readFile(host_conf_file) + if siteConf.find('301-END') != -1: + return mw.returnJson(False, '检测到您的站点做了301重定向设置,请先关闭重定向!') + + # 检测存在反向代理 + data_path = self.getProxyDataPath(site_name) + data_content = mw.readFile(data_path) + if data_content != False: + try: + data = json.loads(data_content) + except: + pass + for proxy in data: + proxy_dir = "{}/{}".format(self.proxyPath, site_name) + proxy_dir_file = proxy_dir + '/' + proxy['id'] + '.conf' + if os.path.exists(proxy_dir_file): + return mw.returnJson(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!') + + # fix binddir domain ssl apply question + mw.backFile(host_conf_file) + auth_to = self.getSitePath(site_name) + rep = r"\s*root\s*(.+);" + replace_root = "\n\troot " + auth_to + ";" + siteConf = re.sub(rep, replace_root, siteConf) + mw.writeFile(host_conf_file, siteConf) + mw.restartWeb() + + to_args = { + 'domains': domains, + 'auth_type': 'http', + 'auth_to': auth_to, + } + + src_letpath = mw.getServerDir() + '/web_conf/letsencrypt/' + site_name + src_csrpath = src_letpath + "/fullchain.pem" # 生成证书路径 + src_keypath = src_letpath + "/privkey.pem" # 密钥文件路径 + + dst_letpath = self.sslDir + '/' + site_name + dst_csrpath = dst_letpath + '/fullchain.pem' + dst_keypath = dst_letpath + '/privkey.pem' + + if not os.path.exists(src_letpath): + import cert_api + data = cert_api.cert_api().applyCertApi(to_args) + mw.restoreFile(host_conf_file) + if not data['status']: + msg = data['msg'] + if type(data['msg']) != str: + msg = data['msg'][0] + emsg = data['msg'][1]['challenges'][0]['error'] + msg = msg + '

                                响应状态:' + str(emsg['status']) + '

                                错误类型:' + emsg[ + 'type'] + '

                                错误代码:' + emsg['detail'] + '

                                ' + return mw.returnData(data['status'], msg, data['msg']) + + mw.execShell('mkdir -p ' + dst_letpath) + mw.buildSoftLink(src_csrpath, dst_csrpath, True) + mw.buildSoftLink(src_keypath, dst_keypath, True) + mw.execShell('echo "lets" > "' + dst_letpath + '/README"') + + # 写入配置文件 + result = self.setSslConf(site_name) + if not result['status']: + return result + + result['csr'] = mw.readFile(src_csrpath) + result['key'] = mw.readFile(src_keypath) + + mw.restartWeb() + return mw.returnData(data['status'], data['msg'], result) + + def setCertToSite(self, site_name, cert_name): + try: + path = self.sslDir + '/' + site_name.strip() + if not os.path.exists(path): + return mw.returnData(False, '证书不存在!') + + result = self.setSslConf(site_name) + if not result['status']: + return result + + mw.restartWeb() + mw.writeLog('网站管理', '证书已部署!') + return mw.returnData(True, '证书已部署!') + except Exception as ex: + return mw.returnData(False, '设置错误:' + str(ex)) + + def removeCert(self, cert_name): + try: + path = self.sslDir + '/' + cert_name + if not os.path.exists(path): + return mw.returnData(False, '证书已不存在!') + os.system("rm -rf " + path) + return mw.returnData(True, '证书已删除!') + except Exception as ex: + return mw.returnData(False, '证书删除失败:'+str(ex)) + + def getBackup(self,site_id,page=1,size=10): + site_info = thisdb.getSitesById(site_id) + info = thisdb.getBackupPage(site_id, page, size) + + data = {} + data['data'] = info['list'] + data['site'] = site_info + data['page'] = mw.getPage({'count':info['count'],'tojs':'getBackup','p':page, 'row':size}) + return data + + def toBackup(self, site_id): + site_info = thisdb.getSitesById(site_id) + + filename = site_info['name'] + '_' + time.strftime('%Y%m%d_%H%M%S', time.localtime()) + '.zip' + backup_path = mw.getBackupDir() + '/site' + zip_name = backup_path + '/' + filename + if not (os.path.exists(backup_path)): + os.makedirs(backup_path) + exec_log = mw.getPanelDir() + '/logs/panel_exec.log' + cmd = "cd '" + site_info['path'] + "' && zip '" + zip_name + "' -r ./* > " + exec_log + " 2>&1" + mw.execShell(cmd) + + fsize = 0 + if os.path.exists(zip_name): + fsize = os.path.getsize(zip_name) + + thisdb.addBackup(site_id,filename,zip_name,fsize) + + msg = mw.getInfo('备份网站[{1}]成功!', (site_info['name'],)) + mw.writeLog('网站管理', msg) + return mw.returnData(True, '备份成功!') + + def delBackup(self,backup_id): + info = thisdb.getBackupById(backup_id) + if os.path.exists(info['filename']): + os.remove(info['filename']) + msg = mw.getInfo('删除网站[{1}]的备份[{2}]成功!', (info['name'], info['filename'])) + mw.writeLog('网站管理', msg) + + thisdb.deleteBackupById(backup_id) + return mw.returnData(True, '站点删除成功!') + + + def getPhpVersion(self): + phpVersions = ('00', '52', '53', '54', '55', '56', + '70', '71', '72', '73', '74', '80', + '81', '82', '83', '84', '85') + data = [] + for val in phpVersions: + tmp = {} + if val == '00': + tmp['version'] = '00' + tmp['name'] = '纯静态' + data.append(tmp) + + # 标准判断 + checkPath = mw.getServerDir() + '/php/' + val + '/bin/php' + if os.path.exists(checkPath): + tmp['version'] = val + tmp['name'] = 'PHP-' + val + data.append(tmp) + + # 其他PHP安装类型 + conf_dir = mw.getServerDir() + "/web_conf/php/conf" + conf_list = os.listdir(conf_dir) + l = len(conf_list) + rep = r"enable-php-(.*?)\.conf" + for name in conf_list: + tmp = {} + try: + matchVer = re.search(rep, name).groups()[0] + except Exception as e: + continue + + if matchVer in phpVersions: + continue + + tmp['version'] = matchVer + tmp['name'] = 'PHP-' + matchVer + data.append(tmp) + return data \ No newline at end of file diff --git a/class/core/ssh_local.py b/web/utils/ssh/ssh_local.py similarity index 97% rename from class/core/ssh_local.py rename to web/utils/ssh/ssh_local.py index 275ff1254..48e4e663e 100644 --- a/class/core/ssh_local.py +++ b/web/utils/ssh/ssh_local.py @@ -22,7 +22,7 @@ import re from io import BytesIO, StringIO -import mw +import core.mw as mw import paramiko from flask_socketio import SocketIO, emit, send @@ -39,7 +39,7 @@ class ssh_local(object): _instance_lock = threading.Lock() def __init__(self): - pass + self.__debug_file = mw.getPanelDir()+ '/logs/ssh_terminal.log' @classmethod def instance(cls, *args, **kwargs): diff --git a/class/core/ssh_terminal.py b/web/utils/ssh/ssh_terminal.py similarity index 99% rename from class/core/ssh_terminal.py rename to web/utils/ssh/ssh_terminal.py index 315289fb7..e683e60c0 100644 --- a/class/core/ssh_terminal.py +++ b/web/utils/ssh/ssh_terminal.py @@ -22,7 +22,7 @@ import re from io import BytesIO, StringIO -import mw +import core.mw as mw import paramiko from flask_socketio import SocketIO, emit, send @@ -30,7 +30,7 @@ from flask_socketio import SocketIO, emit, send class ssh_terminal(object): - __debug_file = 'logs/ssh_terminal.log' + __debug_file = 'logs/ssh_terminal.log' __log_type = 'SSH终端' # websocketio 唯一标识 @@ -59,6 +59,7 @@ class ssh_terminal(object): _instance_lock = threading.Lock() def __init__(self): + self.__debug_file = mw.getPanelDir()+ '/logs/ssh_terminal.log' ht = threading.Thread(target=self.heartbeat) ht.start() diff --git a/web/utils/system/__init__.py b/web/utils/system/__init__.py index 0d5869331..b73ae550e 100644 --- a/web/utils/system/__init__.py +++ b/web/utils/system/__init__.py @@ -8,285 +8,9 @@ # Author: midoks # --------------------------------------------------------------------------------- -import os -import sys -import time -import math -import psutil +from .main import * +from .query import * +from .stats import * +from .monitor import monitor - -import core.mw as mw - -def getDiskInfo(): - # 取磁盘分区信息 - temp = mw.execShell("df -h -P|grep '/'|grep -v tmpfs | grep -v devfs")[0] - tempInodes = mw.execShell("df -i -P|grep '/'|grep -v tmpfs | grep -v devfs")[0] - temp1 = temp.split('\n') - tempInodes1 = tempInodes.split('\n') - diskInfo = [] - n = 0 - cuts = ['/mnt/cdrom', '/boot', '/boot/efi', '/dev', - '/dev/shm', '/zroot', '/run/lock', '/run', '/run/shm', '/run/user'] - for tmp in temp1: - n += 1 - inodes = tempInodes1[n - 1].split() - disk = tmp.split() - if len(disk) < 5: - continue - if disk[1].find('M') != -1: - continue - if disk[1].find('K') != -1: - continue - if len(disk[5].split('/')) > 4: - continue - if disk[5] in cuts: - continue - arr = {} - arr['path'] = disk[5] - tmp1 = [disk[1], disk[2], disk[3], disk[4]] - arr['size'] = tmp1 - arr['inodes'] = [inodes[1], inodes[2], inodes[3], inodes[4]] - diskInfo.append(arr) - return diskInfo - -class stats: - cache = {} - - # 磁盘统计 - def disk(self): - iokey = 'disk_stat' - diskInfo = {} - diskInfo['ALL'] = {} - diskInfo['ALL']['read_count'] = 0 - diskInfo['ALL']['write_count'] = 0 - diskInfo['ALL']['read_bytes'] = 0 - diskInfo['ALL']['write_bytes'] = 0 - diskInfo['ALL']['read_time'] = 0 - diskInfo['ALL']['write_time'] = 0 - diskInfo['ALL']['read_merged_count'] = 0 - diskInfo['ALL']['write_merged_count'] = 0 - - try: - diskio = None - if iokey in self.cache: - diskio = self.cache[iokey] - - mtime = int(time.time()) - if not diskio: - diskio = {} - diskio['info'] = None - diskio['time'] = mtime - - diskio_cache = diskio['info'] - stime = mtime - diskio['time'] - if not stime: stime = 1 - - diskio_group = psutil.disk_io_counters(perdisk=True) - if not diskio_cache: - diskio_cache = diskio_group - - for disk_name in diskio_group.keys(): - diskInfo[disk_name] = {} - # print('disk_name',disk_name) - # print(diskio_group[disk_name].write_time , diskio_cache[disk_name].write_time) - # print(diskio_group[disk_name].write_count , diskio_cache[disk_name].write_count) - - diskInfo[disk_name]['read_count'] = int((diskio_group[disk_name].read_count - diskio_cache[disk_name].read_count) / stime) - diskInfo[disk_name]['write_count'] = int((diskio_group[disk_name].write_count - diskio_cache[disk_name].write_count) / stime) - diskInfo[disk_name]['read_bytes'] = int((diskio_group[disk_name].read_bytes - diskio_cache[disk_name].read_bytes) / stime) - diskInfo[disk_name]['write_bytes'] = int((diskio_group[disk_name].write_bytes - diskio_cache[disk_name].write_bytes) / stime) - diskInfo[disk_name]['read_time'] = int((diskio_group[disk_name].read_time - diskio_cache[disk_name].read_time) / stime) - diskInfo[disk_name]['write_time'] = int((diskio_group[disk_name].write_time - diskio_cache[disk_name].write_time) / stime) - - if 'read_merged_count' in diskio_group[disk_name] and 'read_merged_count' in diskio_cache[disk_name]: - diskInfo[disk_name]['read_merged_count'] = int((diskio_group[disk_name].read_merged_count - diskio_cache[disk_name].read_merged_count) / stime) - if 'write_merged_count' in diskio_group[disk_name] and 'write_merged_count' in diskio_cache[disk_name]: - diskInfo[disk_name]['write_merged_count'] = int((diskio_group[disk_name].write_merged_count - diskio_cache[disk_name].write_merged_count) / stime) - - diskInfo['ALL']['read_count'] += diskInfo[disk_name]['read_count'] - diskInfo['ALL']['write_count'] += diskInfo[disk_name]['write_count'] - diskInfo['ALL']['read_bytes'] += diskInfo[disk_name]['read_bytes'] - diskInfo['ALL']['write_bytes'] += diskInfo[disk_name]['write_bytes'] - if diskInfo['ALL']['read_time'] < diskInfo[disk_name]['read_time']: - diskInfo['ALL']['read_time'] = diskInfo[disk_name]['read_time'] - if diskInfo['ALL']['write_time'] < diskInfo[disk_name]['write_time']: - diskInfo['ALL']['write_time'] = diskInfo[disk_name]['write_time'] - - if 'read_merged_count' in diskInfo[disk_name] and 'read_merged_count' in diskInfo[disk_name]: - diskInfo['ALL']['read_merged_count'] += diskInfo[disk_name]['read_merged_count'] - if 'write_merged_count' in diskInfo[disk_name] and 'write_merged_count' in diskInfo[disk_name]: - diskInfo['ALL']['write_merged_count'] += diskInfo[disk_name]['write_merged_count'] - - self.cache[iokey] = {'info':diskio_group,'time':mtime} - except Exception as e: - pass - - return diskInfo - - # 网络统计 - def network(self): - netInfo = {} - - netInfo['ALL'] = {} - netInfo['ALL']['up'] = 0 - netInfo['ALL']['down'] = 0 - netInfo['ALL']['upTotal'] = 0 - netInfo['ALL']['downTotal'] = 0 - netInfo['ALL']['upPackets'] = 0 - netInfo['ALL']['downPackets'] = 0 - - mtime = time.time() - iokey = 'net_stat' - netio = None - if iokey in self.cache: - netio = self.cache[iokey] - - if not netio: - netio = {} - netio['info'] = None - netio['all_io'] = None - netio['time'] = mtime - - stime = mtime - netio['time'] - if not stime: stime = 1 - - # print("new:",stime) - netio_group = psutil.net_io_counters(pernic=True).keys() - - netio_cache = netio['info'] - allio_cache = netio['all_io'] - if not netio_cache: - netio_cache = {} - - netio_group_t = {} - for name in netio_group: - netInfo[name] = {} - - io_data = psutil.net_io_counters(pernic=True).get(name) - if not name in netio_cache: - netio_cache[name] = io_data - - netio_group_t[name] = io_data - - netInfo[name]['up'] = round(float((io_data[0] - netio_cache[name][0]) / stime), 2) - netInfo[name]['down'] = round(float((io_data[1] - netio_cache[name][1])/ stime), 2) - - netInfo[name]['upTotal'] = io_data[0] - netInfo[name]['downTotal'] = io_data[1] - netInfo[name]['upPackets'] = io_data[2] - netInfo[name]['downPackets'] = io_data[3] - - all_io = psutil.net_io_counters()[:4] - if not allio_cache: - allio_cache = all_io - - netInfo['ALL']['up'] = round(float((all_io[0] - allio_cache[0]) /stime), 2) - netInfo['ALL']['down'] = round(float((all_io[1] - allio_cache[1]) /stime), 2) - netInfo['ALL']['upTotal'] = all_io[0] - netInfo['ALL']['downTotal'] = all_io[1] - netInfo['ALL']['upPackets'] = all_io[2] - netInfo['ALL']['downPackets'] = all_io[3] - - self.cache[iokey] = {'info':netio_group_t,'all_io':all_io,'time':mtime} - return netInfo - - - -def getLoadAverage(): - c = os.getloadavg() - data = {} - data['one'] = round(float(c[0]), 2) - data['five'] = round(float(c[1]), 2) - data['fifteen'] = round(float(c[2]), 2) - data['max'] = psutil.cpu_count() * 2 - data['limit'] = data['max'] - data['safe'] = data['max'] * 0.75 - return data - -def getSystemVersion(): - # 取操作系统版本 - current_os = mw.getOs() - # sys_temper = self.getSystemDeviceTemperature() - # print(sys_temper) - # mac - if current_os == 'darwin': - data = mw.execShell('sw_vers')[0] - data_list = data.strip().split("\n") - mac_version = '' - for x in data_list: - xlist = x.split("\t") - mac_version += xlist[len(xlist)-1] + ' ' - - arch_ver = mw.execShell("arch") - return mac_version + " (" + arch_ver[0].strip() + ")" - - # freebsd - if current_os.startswith('freebsd'): - version = mw.execShell( - "cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") - arch_ver = mw.execShell( - "sysctl -a | egrep -i 'hw.machine_arch' | awk -F ':' '{print $2}'") - return version[0].strip() + " (" + arch_ver[0].strip() + ")" - - redhat_series = '/etc/redhat-release' - if os.path.exists(redhat_series): - version = mw.readFile('/etc/redhat-release') - version = version.replace('release ', '').strip() - - arch_ver = mw.execShell("arch") - return version + " (" + arch_ver[0].strip() + ")" - - os_series = '/etc/os-release' - if os.path.exists(os_series): - version = mw.execShell( - "cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") - - arch_ver = mw.execShell("arch") - return version[0].strip() + " (" + arch_ver[0].strip() + ")" - -def getBootTime(): - # 取系统启动时间 - uptime = mw.readFile('/proc/uptime') - if uptime == False: - start_time = psutil.boot_time() - run_time = time.time() - start_time - else: - run_time = uptime.split()[0] - tStr = float(run_time) - min = tStr / 60 - hours = min / 60 - days = math.floor(hours / 24) - hours = math.floor(hours - (days * 24)) - min = math.floor(min - (days * 60 * 24) - (hours * 60)) - return mw.getInfo('已不间断运行: {1}天{2}小时{3}分钟', (str(int(days)), str(int(hours)), str(int(min)))) - -def getCpuInfo(interval=1): - # 取CPU信息 - cpuCount = psutil.cpu_count() - cpuLogicalNum = psutil.cpu_count(logical=False) - used = psutil.cpu_percent(interval=interval) - cpuLogicalNum = 0 - if os.path.exists('/proc/cpuinfo'): - c_tmp = mw.readFile('/proc/cpuinfo') - d_tmp = re.findall("physical id.+", c_tmp) - cpuLogicalNum = len(set(d_tmp)) - - used_all = psutil.cpu_percent(percpu=True) - cpu_name = mw.getCpuType() + " * {}".format(cpuLogicalNum) - return used, cpuCount, used_all, cpu_name, cpuCount, cpuLogicalNum - -def getMemInfo(): - # 取内存信息 - mem = psutil.virtual_memory() - if sys.platform == 'darwin': - memInfo = {'memTotal': mem.total} - memInfo['memRealUsed'] = memInfo['memTotal'] * (mem.percent / 100) - else: - memInfo = { - 'memTotal': mem.total, - 'memFree': mem.free, - 'memBuffers': mem.buffers, - 'memCached': mem.cached - } - memInfo['memRealUsed'] = memInfo['memTotal'] - memInfo['memFree'] - memInfo['memBuffers'] - memInfo['memCached'] - return memInfo \ No newline at end of file +from .update import * \ No newline at end of file diff --git a/web/utils/system/main.py b/web/utils/system/main.py new file mode 100644 index 000000000..853e0717d --- /dev/null +++ b/web/utils/system/main.py @@ -0,0 +1,221 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import sys +import re +import time +import math +import psutil + +import core.mw as mw + +from threading import Thread +from time import sleep + +def mw_async(f): + def wrapper(*args, **kwargs): + thr = Thread(target=f, args=args, kwargs=kwargs) + thr.start() + return wrapper + +@mw_async +def restartServer(): + if not mw.isRestart(): + return mw.returnData(False, '请等待所有安装任务完成再执行!') + mw.execShell("sync && init 6 &") + return mw.returnData(True, '命令发送成功!') + +def getPid(self, pname): + try: + pids = psutil.pids() + for pid in pids: + if psutil.Process(pid).name() == pname: + return True + return False + except: + return False + +def getEnvInfo(): + data = {} + data['status'] = True + sdir = mw.getServerDir() + + data['webserver'] = '未安装' + if os.path.exists(sdir + '/openresty/nginx/sbin/nginx'): + data['webserver'] = 'OpenResty' + data['php'] = [] + phpversions = ['52', '53', '54', '55', '56', '70', '71', '72', '73', '74', '80', '81', '82', '83', '84'] + phpPath = sdir + '/php/' + for pv in phpversions: + if not os.path.exists(phpPath + pv + '/bin/php'): + continue + data['php'].append(pv) + data['mysql'] = False + if os.path.exists(sdir + '/mysql/bin/mysql'): + data['mysql'] = True + try: + diskInfo = psutil.disk_usage('/www') + except: + diskInfo = psutil.disk_usage('/') + data['disk'] = diskInfo[2] + return mw.returnData(True, 'ok', data) + +def getDiskInfo(): + # 取磁盘分区信息 + temp = mw.execShell("df -h -P|grep '/'|grep -v tmpfs | grep -v devfs")[0] + tempInodes = mw.execShell("df -i -P|grep '/'|grep -v tmpfs | grep -v devfs")[0] + temp1 = temp.split('\n') + tempInodes1 = tempInodes.split('\n') + diskInfo = [] + n = 0 + cuts = ['/mnt/cdrom', '/boot', '/boot/efi', '/dev', + '/dev/shm', '/zroot', '/run/lock', '/run', '/run/shm', '/run/user'] + for tmp in temp1: + n += 1 + inodes = tempInodes1[n - 1].split() + disk = tmp.split() + if len(disk) < 5: + continue + if disk[1].find('M') != -1: + continue + if disk[1].find('K') != -1: + continue + if len(disk[5].split('/')) > 4: + continue + if disk[5] in cuts: + continue + arr = {} + arr['path'] = disk[5] + tmp1 = [disk[1], disk[2], disk[3], disk[4]] + arr['size'] = tmp1 + arr['inodes'] = [inodes[1], inodes[2], inodes[3], inodes[4]] + diskInfo.append(arr) + return diskInfo + + +def getLoadAverage(): + c = os.getloadavg() + data = {} + data['one'] = round(float(c[0]), 2) + data['five'] = round(float(c[1]), 2) + data['fifteen'] = round(float(c[2]), 2) + data['max'] = psutil.cpu_count() * 2 + data['limit'] = data['max'] + data['safe'] = data['max'] * 0.75 + return data + +def getSystemVersion(): + # 取操作系统版本 + current_os = mw.getOs() + # sys_temper = self.getSystemDeviceTemperature() + # print(sys_temper) + # mac + if current_os == 'darwin': + data = mw.execShell('sw_vers')[0] + data_list = data.strip().split("\n") + mac_version = '' + for x in data_list: + xlist = x.split("\t") + mac_version += xlist[len(xlist)-1] + ' ' + + arch_ver = mw.execShell("arch") + return mac_version + " (" + arch_ver[0].strip() + ")" + + # freebsd + if current_os.startswith('freebsd'): + version = mw.execShell( + "cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") + arch_ver = mw.execShell( + "sysctl -a | egrep -i 'hw.machine_arch' | awk -F ':' '{print $2}'") + return version[0].strip() + " (" + arch_ver[0].strip() + ")" + + redhat_series = '/etc/redhat-release' + if os.path.exists(redhat_series): + version = mw.readFile('/etc/redhat-release') + version = version.replace('release ', '').strip() + + arch_ver = mw.execShell("arch") + return version + " (" + arch_ver[0].strip() + ")" + + os_series = '/etc/os-release' + if os.path.exists(os_series): + version = mw.execShell( + "cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'") + + arch_ver = mw.execShell("arch") + return version[0].strip() + " (" + arch_ver[0].strip() + ")" + +def getBootTime(): + # 取系统启动时间 + if os.path.exists('/proc/uptime'): + uptime = mw.readFile('/proc/uptime') + run_time = uptime.split()[0] + else: + start_time = psutil.boot_time() + run_time = time.time() - start_time + + tStr = float(run_time) + min = tStr / 60 + hours = min / 60 + days = math.floor(hours / 24) + hours = math.floor(hours - (days * 24)) + min = math.floor(min - (days * 60 * 24) - (hours * 60)) + return mw.getInfo('已不间断运行: {1}天{2}小时{3}分钟', (str(int(days)), str(int(hours)), str(int(min)))) + +def getCpuInfo(interval=1): + # 取CPU信息 + cpuCount = psutil.cpu_count() + cpuLogicalNum = psutil.cpu_count(logical=False) + used = psutil.cpu_percent(interval=interval) + cpuLogicalNum = 0 + if os.path.exists('/proc/cpuinfo'): + c_tmp = mw.readFile('/proc/cpuinfo') + d_tmp = re.findall("physical id.+", c_tmp) + cpuLogicalNum = len(set(d_tmp)) + + used_all = psutil.cpu_percent(percpu=True) + cpu_name = mw.getCpuType() + " * {}".format(cpuLogicalNum) + return used, cpuCount, used_all, cpu_name, cpuCount, cpuLogicalNum + +def getMemInfo(): + # 取内存信息 + mem = psutil.virtual_memory() + if sys.platform == 'darwin': + memInfo = {'memTotal': mem.total} + memInfo['memRealUsed'] = memInfo['memTotal'] * (mem.percent / 100) + else: + memInfo = { + 'memTotal': mem.total, + 'memFree': mem.free, + 'memBuffers': mem.buffers, + 'memCached': mem.cached + } + memInfo['memRealUsed'] = memInfo['memTotal'] - memInfo['memFree'] - memInfo['memBuffers'] - memInfo['memCached'] + return memInfo + +def getMemUsed(): + # 取内存使用率 + try: + import psutil + mem = psutil.virtual_memory() + + if mw.getOs() == 'darwin': + return mem.percent + + memInfo = {'memTotal': mem.total / 1024 / 1024, 'memFree': mem.free / 1024 / 1024, + 'memBuffers': mem.buffers / 1024 / 1024, 'memCached': mem.cached / 1024 / 1024} + tmp = memInfo['memTotal'] - memInfo['memFree'] - \ + memInfo['memBuffers'] - memInfo['memCached'] + tmp1 = memInfo['memTotal'] / 100 + return (tmp / tmp1) + except Exception as ex: + return 1 + diff --git a/web/utils/system/monitor.py b/web/utils/system/monitor.py new file mode 100644 index 000000000..801aaabfe --- /dev/null +++ b/web/utils/system/monitor.py @@ -0,0 +1,182 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import sys +import re +import time +import math +import psutil +import threading + + +from .main import getMemUsed + +import core.mw as mw +import core.db as db + + + +# 监控系统数据入库 +class monitor: + + _dbfile = mw.getPanelDataDir() + '/system.db' + _diskinfo = None + _netinfo = None + + def __init__(self): + pass + + # lock + _instance_lock = threading.Lock() + + @classmethod + def instance(cls, *args, **kwargs): + if not hasattr(monitor, "_instance"): + with monitor._instance_lock: + if not hasattr(monitor, "_instance"): + monitor._instance = monitor(*args, **kwargs) + return monitor._instance + + def clearDbFile(self): + os.remove(self._dbfile) + self.initDBFile() + return True + + def initDBFile(self): + if os.path.exists(self._dbfile): + return True + sql_file = mw.getPanelDir() + '/web/admin/setup/sql/system.sql' + sql = db.Sql().dbPos(mw.getPanelDataDir(),'system') + csql = mw.readFile(sql_file) + csql_list = csql.split(';') + for index in range(len(csql_list)): + sql.execute(csql_list[index], ()) + return True + + def getMonitorDay(self): + monitor_day = mw.M('option').field('name').where('name=?',('monitor_day',)).getField('value') + return int(monitor_day) + + def isOnlyNetIoStats(self): + monitor_only_netio = mw.M('option').field('name').where('name=?',('monitor_only_netio',)).getField('value') + if monitor_only_netio == 'open': + return True + return False + + def psutilNetIoCounters(self): + ''' + 统计网卡流量 + ''' + if self.isOnlyNetIoStats(): + local_lo = (0, 0, 0, 0) + ioName = psutil.net_io_counters(pernic=True).keys() + for x in ioName: + if x.find("lo") > -1: + local_lo = psutil.net_io_counters(pernic=True).get(x)[:4] + + all_io = psutil.net_io_counters()[:4] + result_io = tuple([all_io[i] - local_lo[i] for i in range(0, len(all_io))]) + + return result_io + return psutil.net_io_counters()[:4] + + def getDiskInfo(self): + now_diskinfo = psutil.disk_io_counters() + if self._diskinfo is None: + self._diskinfo = now_diskinfo + + info = {} + info['read_count'] = now_diskinfo.read_count - self._diskinfo.read_count + info['write_count'] = now_diskinfo.write_count - self._diskinfo.write_count + info['read_bytes'] = now_diskinfo.read_bytes - self._diskinfo.read_bytes + info['write_bytes'] = now_diskinfo.write_bytes - self._diskinfo.write_bytes + info['read_time'] = now_diskinfo.read_time - self._diskinfo.read_time + info['write_time'] = now_diskinfo.write_time - self._diskinfo.write_time + + self._diskinfo = now_diskinfo + return info + + def getNetIoInfo(self): + # 取当前网络Io + net_io = self.psutilNetIoCounters() + if not self._netinfo: + self._netinfo = net_io + info = {} + info['upTotal'] = net_io[0] + info['downTotal'] = net_io[1] + info['up'] = round(float((net_io[0] - self._netinfo[0]) / 1024), 2) + info['down'] = round(float((net_io[1] - self._netinfo[1]) / 1024), 2) + info['downPackets'] = net_io[3] + info['upPackets'] = net_io[2] + + self._netinfo = net_io + return info + + def getLoadAverage(self): + c = os.getloadavg() + data = {} + data['one'] = round(float(c[0]), 2) + data['five'] = round(float(c[1]), 2) + data['fifteen'] = round(float(c[2]), 2) + data['max'] = psutil.cpu_count() * 2 + data['limit'] = data['max'] + data['safe'] = data['max'] * 0.75 + return data + + def run(self): + self.initDBFile() + monitor_day = self.getMonitorDay() + + info = {} + # 取当前CPU Io + info['used'] = psutil.cpu_percent(interval=1) + info['used'] = round(info['used'], 2) + + info['mem'] = getMemUsed() + info['mem'] = round(info['mem'], 2) + + netio = self.getNetIoInfo() + diskio = self.getDiskInfo() + + addtime = int(time.time()) + deltime = addtime - (monitor_day * 86400) + + # CPU/内存数据入库 + cpu_mem_data = (info['used'], info['mem'], addtime) + cmd_objm = mw.M('cpuio').dbPos(mw.getPanelDataDir(),'system') + cmd_objm.add('pro,mem,addtime', cpu_mem_data) + cmd_objm.where("addtime 100: + lpro = 100 + load_objm = mw.M('load_average').dbPos(mw.getPanelDataDir(),'system') + load_objm.add('pro,one,five,fifteen,addtime', (lpro, load_data['one'], load_data['five'], load_data['fifteen'], addtime)) + load_objm.where("addtime +# --------------------------------------------------------------------------------- + +import os +import sys +import re +import time +import math +import psutil + + +import core.mw as mw + +# -------------------------------------------- 数据查询相关 -------------------------------------------- +# 格式化addtime列 +def toAddtime(data, tomem=False): + import time + if tomem: + import psutil + mPre = (psutil.virtual_memory().total / 1024 / 1024) / 100 + length = len(data) + he = 1 + if length > 100: + he = 1 + if length > 1000: + he = 3 + if length > 10000: + he = 15 + if he == 1: + for i in range(length): + data[i]['addtime'] = time.strftime( + '%m/%d %H:%M', time.localtime(float(data[i]['addtime']))) + if tomem and data[i]['mem'] > 100: + data[i]['mem'] = data[i]['mem'] / mPre + + return data + else: + count = 0 + tmp = [] + for value in data: + if count < he: + count += 1 + continue + value['addtime'] = time.strftime( + '%m/%d %H:%M', time.localtime(float(value['addtime']))) + if tomem and value['mem'] > 100: + value['mem'] = value['mem'] / mPre + tmp.append(value) + count = 0 + return tmp + +# 格式化addtime列 +def toUseAddtime(data): + dlen = len(data) + for i in range(dlen): + data[i]['addtime'] = time.strftime('%m/%d %H:%M:%S', time.localtime(float(data[i]['addtime']))) + return data + +def getLoadAverageByDB(start, end): + # 获取系统的负载统计信息 + data = mw.M('load_average').dbPos(mw.getPanelDataDir(),'system')\ + .where("addtime>=? AND addtime<=?", (start, end,))\ + .field('pro,one,five,fifteen,addtime')\ + .order('id asc').select() + data = toUseAddtime(data) + return data + +def getDiskIoByDB(start, end): + # 获取系统的磁盘IO统计信息 + data = mw.M('diskio').dbPos(mw.getPanelDataDir(),'system')\ + .where("addtime>=? AND addtime<=?", (start, end))\ + .field('read_count,write_count,read_bytes,write_bytes,read_time,write_time,addtime')\ + .order('id asc').select() + data = toUseAddtime(data) + return data + +def getCpuIoByDB(start, end): + # 获取系统的CPU/IO统计信息 + data = mw.M('cpuio').dbPos(mw.getPanelDataDir(),'system')\ + .where("addtime>=? AND addtime<=?",(start, end))\ + .field('pro,mem,addtime')\ + .order('id asc').select() + data = toUseAddtime(data) + return data + +def getNetworkIoByDB(start, end): + # 获取系统网络IO统计信息 + # id, + data = mw.M('network').dbPos(mw.getPanelDataDir(),'system')\ + .where("addtime>=? AND addtime<=?", (start, end))\ + .field('up,down,total_up,total_down,down_packets,up_packets,addtime')\ + .order('id asc').select() + data = toUseAddtime(data) + return data + +# -------------------------------------------- 数据查询相关 -------------------------------------------- diff --git a/web/utils/system/stats.py b/web/utils/system/stats.py new file mode 100644 index 000000000..123e5e80b --- /dev/null +++ b/web/utils/system/stats.py @@ -0,0 +1,161 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import sys +import re +import time +import math +import psutil + +import core.mw as mw + +class stats: + cache = {} + + # 磁盘统计 + def disk(self): + iokey = 'disk_stat' + diskInfo = {} + diskInfo['ALL'] = {} + diskInfo['ALL']['read_count'] = 0 + diskInfo['ALL']['write_count'] = 0 + diskInfo['ALL']['read_bytes'] = 0 + diskInfo['ALL']['write_bytes'] = 0 + diskInfo['ALL']['read_time'] = 0 + diskInfo['ALL']['write_time'] = 0 + diskInfo['ALL']['read_merged_count'] = 0 + diskInfo['ALL']['write_merged_count'] = 0 + + try: + diskio = None + if iokey in self.cache: + diskio = self.cache[iokey] + + mtime = int(time.time()) + if not diskio: + diskio = {} + diskio['info'] = None + diskio['time'] = mtime + + diskio_cache = diskio['info'] + stime = mtime - diskio['time'] + if not stime: stime = 1 + + diskio_group = psutil.disk_io_counters(perdisk=True) + if not diskio_cache: + diskio_cache = diskio_group + + for disk_name in diskio_group.keys(): + diskInfo[disk_name] = {} + # print('disk_name',disk_name) + # print(diskio_group[disk_name].write_time , diskio_cache[disk_name].write_time) + # print(diskio_group[disk_name].write_count , diskio_cache[disk_name].write_count) + + diskInfo[disk_name]['read_count'] = int((diskio_group[disk_name].read_count - diskio_cache[disk_name].read_count) / stime) + diskInfo[disk_name]['write_count'] = int((diskio_group[disk_name].write_count - diskio_cache[disk_name].write_count) / stime) + diskInfo[disk_name]['read_bytes'] = int((diskio_group[disk_name].read_bytes - diskio_cache[disk_name].read_bytes) / stime) + diskInfo[disk_name]['write_bytes'] = int((diskio_group[disk_name].write_bytes - diskio_cache[disk_name].write_bytes) / stime) + diskInfo[disk_name]['read_time'] = int((diskio_group[disk_name].read_time - diskio_cache[disk_name].read_time) / stime) + diskInfo[disk_name]['write_time'] = int((diskio_group[disk_name].write_time - diskio_cache[disk_name].write_time) / stime) + + if 'read_merged_count' in diskio_group[disk_name] and 'read_merged_count' in diskio_cache[disk_name]: + diskInfo[disk_name]['read_merged_count'] = int((diskio_group[disk_name].read_merged_count - diskio_cache[disk_name].read_merged_count) / stime) + if 'write_merged_count' in diskio_group[disk_name] and 'write_merged_count' in diskio_cache[disk_name]: + diskInfo[disk_name]['write_merged_count'] = int((diskio_group[disk_name].write_merged_count - diskio_cache[disk_name].write_merged_count) / stime) + + diskInfo['ALL']['read_count'] += diskInfo[disk_name]['read_count'] + diskInfo['ALL']['write_count'] += diskInfo[disk_name]['write_count'] + diskInfo['ALL']['read_bytes'] += diskInfo[disk_name]['read_bytes'] + diskInfo['ALL']['write_bytes'] += diskInfo[disk_name]['write_bytes'] + if diskInfo['ALL']['read_time'] < diskInfo[disk_name]['read_time']: + diskInfo['ALL']['read_time'] = diskInfo[disk_name]['read_time'] + if diskInfo['ALL']['write_time'] < diskInfo[disk_name]['write_time']: + diskInfo['ALL']['write_time'] = diskInfo[disk_name]['write_time'] + + if 'read_merged_count' in diskInfo[disk_name] and 'read_merged_count' in diskInfo[disk_name]: + diskInfo['ALL']['read_merged_count'] += diskInfo[disk_name]['read_merged_count'] + if 'write_merged_count' in diskInfo[disk_name] and 'write_merged_count' in diskInfo[disk_name]: + diskInfo['ALL']['write_merged_count'] += diskInfo[disk_name]['write_merged_count'] + + self.cache[iokey] = {'info':diskio_group,'time':mtime} + except Exception as e: + pass + + return diskInfo + + # 网络统计 + def network(self): + netInfo = {} + + netInfo['ALL'] = {} + netInfo['ALL']['up'] = 0 + netInfo['ALL']['down'] = 0 + netInfo['ALL']['upTotal'] = 0 + netInfo['ALL']['downTotal'] = 0 + netInfo['ALL']['upPackets'] = 0 + netInfo['ALL']['downPackets'] = 0 + + mtime = time.time() + iokey = 'net_stat' + netio = None + if iokey in self.cache: + netio = self.cache[iokey] + + if not netio: + netio = {} + netio['info'] = None + netio['all_io'] = None + netio['time'] = mtime + + stime = mtime - netio['time'] + if not stime: stime = 1 + + # print("new:",stime) + netio_group = psutil.net_io_counters(pernic=True).keys() + + netio_cache = netio['info'] + allio_cache = netio['all_io'] + if not netio_cache: + netio_cache = {} + + netio_group_t = {} + for name in netio_group: + netInfo[name] = {} + + io_data = psutil.net_io_counters(pernic=True).get(name) + if not name in netio_cache: + netio_cache[name] = io_data + + netio_group_t[name] = io_data + + netInfo[name]['up'] = round(float((io_data[0] - netio_cache[name][0]) / stime), 2) + netInfo[name]['down'] = round(float((io_data[1] - netio_cache[name][1])/ stime), 2) + + netInfo[name]['upTotal'] = io_data[0] + netInfo[name]['downTotal'] = io_data[1] + netInfo[name]['upPackets'] = io_data[2] + netInfo[name]['downPackets'] = io_data[3] + + all_io = psutil.net_io_counters()[:4] + if not allio_cache: + allio_cache = all_io + + netInfo['ALL']['up'] = round(float((all_io[0] - allio_cache[0]) /stime), 2) + netInfo['ALL']['down'] = round(float((all_io[1] - allio_cache[1]) /stime), 2) + netInfo['ALL']['upTotal'] = all_io[0] + netInfo['ALL']['downTotal'] = all_io[1] + netInfo['ALL']['upPackets'] = all_io[2] + netInfo['ALL']['downPackets'] = all_io[3] + + self.cache[iokey] = {'info':netio_group_t,'all_io':all_io,'time':mtime} + return netInfo + + diff --git a/web/utils/system/update.py b/web/utils/system/update.py new file mode 100644 index 000000000..54a78c35f --- /dev/null +++ b/web/utils/system/update.py @@ -0,0 +1,150 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import sys +import re +import time +import math +import psutil +import json + +import core.mw as mw + +def versionDiff(now, new): + ''' + test 测试 + new 有新版本 + none 没有新版本 + ''' + new_list = new.split('.') + if len(new_list) > 3: + return 'test' + + now_list = now.split('.') + ret = 'none' + from distutils.version import LooseVersion + if LooseVersion(new) > LooseVersion(now): + return 'new' + else: + return 'none' + +def getServerInfo(): + import urllib.request + import ssl + upAddr = 'https://api.github.com/repos/midoks/mdserver-web/releases/latest' + try: + context = ssl._create_unverified_context() + req = urllib.request.urlopen(upAddr, context=context, timeout=3) + result = req.read().decode('utf-8') + version = json.loads(result) + return version + except Exception as e: + print(str(e)) + return None + return None + +def updateServer(stype, version=''): + import config + # 更新服务 + try: + if not mw.isRestart(): + return mw.returnData(False, '请等待所有安装任务完成再执行!') + + version_new_info = getServerInfo() + if version_new_info is None: + return mw.returnData(False, '服务器数据或网络有问题!') + + version_now = config.APP_VERSION + new_ver = version_new_info['name'] + if stype == 'check': + diff = versionDiff(version_now, new_ver) + if diff == 'new': + return mw.returnData(True, '有新版本!', new_ver) + elif diff == 'test': + return mw.returnData(True, '有测试版本!', new_ver) + else: + return mw.returnData(False, '已经是最新,无需更新!') + + if stype == 'info': + diff = versionDiff(version_now, new_ver) + data = {} + data['version'] = new_ver + data['content'] = version_new_info['body'].replace("\n", "
                                ") + return mw.returnData(True, '更新信息!', data) + + if stype == 'update': + if version == '': + return mw.returnData(False, '缺少版本信息!') + + if new_ver != version: + return mw.returnData(False, '更新失败,请重试!') + + toPath = mw.getPanelDir() + '/temp' + if not os.path.exists(toPath): + mw.execShell('mkdir -p ' + toPath) + + newUrl = "https://github.com/midoks/mdserver-web/archive/refs/tags/" + version + ".zip" + + dist_mw = toPath + '/mw.zip' + if not os.path.exists(dist_mw): + mw.execShell('wget --no-check-certificate -O ' + dist_mw + ' ' + newUrl) + + dist_to = toPath + "/mdserver-web-" + version + if not os.path.exists(dist_to): + os.system('unzip -o ' + toPath + '/mw.zip' + ' -d ' + toPath) + + cmd_cp = 'cp -rf ' + toPath + '/mdserver-web-' + version + '/* ' + mw.getServerDir() + '/mdserver-web' + mw.execShell(cmd_cp) + + mw.execShell('rm -rf ' + toPath + '/mdserver-web-' + version) + mw.execShell('rm -rf ' + toPath + '/mw.zip') + + update_env = ''' +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin + +P_VER=`python3 -V | awk '{print $2}'` + +if [ ! -f /www/server/mdserver-web/bin/activate ];then +cd /www/server/mdserver-web && python3 -m venv . +cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate +else +cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate +fi + +cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"") +PIPSRC="https://pypi.python.org/simple" +if [ ! -z "$cn" ];then +PIPSRC="https://pypi.tuna.tsinghua.edu.cn/simple" +fi + +cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/requirements.txt -i $PIPSRC + +P_VER_D=`echo "$P_VER"|awk -F '.' '{print $1}'` +P_VER_M=`echo "$P_VER"|awk -F '.' '{print $2}'` +NEW_P_VER=${P_VER_D}.${P_VER_M} + +if [ -f /www/server/mdserver-web/version/r${NEW_P_VER}.txt ];then +cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/version/r${NEW_P_VER}.txt -i $PIPSRC +fi +''' + os.system(update_env) + mw.restartMw() + return mw.returnData(True, '安装更新成功!') + + return mw.returnData(False, '已经是最新,无需更新!') + except Exception as ex: + # print('updateServer', ex) + return mw.returnData(False, "连接服务器失败!" + str(ex)) + + + + diff --git a/web/utils/task.py b/web/utils/task.py new file mode 100644 index 000000000..63f9e18df --- /dev/null +++ b/web/utils/task.py @@ -0,0 +1,64 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import pwd +import time + +import core.mw as mw +import thisdb + +def getTaskPage(page=1,size=10): + info = thisdb.getTaskPage(page=page, size=size) + + rdata = {} + rdata['data'] = info['list'] + rdata['count'] = info['count'] + rdata['page'] = mw.getPage({'count':info['count'],'tojs':'remind','p':page,'row':size}) + return rdata + +# 删除进程下的所有进程 +def removeTaskRecursion(pid): + cmd = "ps -ef|grep %s | grep -v grep |sed -n '2,1p' | awk '{print $2}'" % pid + sub_pid = mw.execShell(cmd) + if sub_pid[0].strip() == '': + return 'ok' + + if sub_pid[0].strip() == pid : + return 'ok' + + removeTaskRecursion(sub_pid[0].strip()) + mw.execShell('kill -9 ' + sub_pid[0].strip()) + return sub_pid[0].strip() + +# 删除任务 +def removeTask(task_id): + try: + name = mw.M('tasks').where('id=?', (task_id,)).getField('name') + status = mw.M('tasks').where('id=?', (task_id,)).getField('status') + mw.M('tasks').delete(task_id) + if str(status) == '-1': + cmd = "ps aux | grep 'panel_task.py' | grep -v grep |awk '{print $2}'" + task_pid = mw.execShell(cmd) + task_list = task_pid[0].strip().split("\n") + for i in range(len(task_list)): + removeTaskRecursion(task_list[i]) + t = mw.execShell('kill -9 ' + task_list[i]) + print(t) + mw.triggerTask() + mw.restartTask() + except Exception as e: + mw.restartTask() + + # 删除日志 + task_log = mw.getPanelDir() + "/tmp/panelTask.pl" + if os.path.exists(task_log): + os.remove(task_log) + return mw.returnData(True, '任务已删除!') \ No newline at end of file diff --git a/web/version.py b/web/version.py index 31ccc059f..e78644ee3 100644 --- a/web/version.py +++ b/web/version.py @@ -15,7 +15,7 @@ APP_RELEASE = 0 APP_REVISION = 18 APP_SMALL_VERSION = 0 -# 应用程序版本后缀,例如“beta1”、“dev”。通常为空字符串GA发布。 +# 应用程序版本后缀,例如“beta1”、“dev”。通常为空字符串GA发布 APP_SUFFIX = ''