From 0f133962dc241712357e23def8265f526b0c5d8e Mon Sep 17 00:00:00 2001 From: WanWanYun Date: Mon, 10 Nov 2025 21:50:16 +0800 Subject: [PATCH] =?UTF-8?q?Initial=20commit=20-=20=E7=8E=A9=E7=8E=A9?= =?UTF-8?q?=E4=BA=91=E6=96=87=E4=BB=B6=E7=AE=A1=E7=90=86=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=20v1.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 完整的前后端代码 - 支持本地存储和SFTP存储 - 文件分享功能 - 上传工具源代码 - 完整的部署文档 - Nginx配置模板 技术栈: - 后端: Node.js + Express + SQLite - 前端: Vue.js 3 + Axios - 存储: 本地存储 / SFTP远程存储 --- .gitignore | 67 + DEPLOY.md | 183 + DEPLOYMENT.md | 189 + DOCKER部署指南.md | 1211 + QUICK_START.txt | 49 + README.md | 244 + VERSION.txt | 98 + backend/.env.example | 8 + backend/Dockerfile | 21 + backend/auth.js | 108 + backend/backup.bat | 52 + backend/database.js | 554 + backend/package-lock.json | 3010 +++ backend/package.json | 34 + backend/server.js | 2113 ++ backend/start.bat | 10 + backend/storage.js | 321 + deploy.sh | 110 + docker-compose.yml | 53 + frontend/app.html | 1997 ++ frontend/app.js | 1708 ++ frontend/index.html | 191 + frontend/libs/axios.min.js | 3 + frontend/libs/fontawesome/css/all.min.css | 9 + .../fontawesome/webfonts/fa-brands-400.woff2 | Bin 0 -> 108020 bytes .../fontawesome/webfonts/fa-regular-400.woff2 | Bin 0 -> 24948 bytes .../fontawesome/webfonts/fa-solid-900.woff2 | Bin 0 -> 150124 bytes frontend/libs/vue.global.js | 18323 ++++++++++++++++ frontend/libs/vue.global.prod.js | 13 + frontend/share.html | 767 + nginx/nginx.conf | 34 + nginx/nginx.conf.example | 52 + upload-tool/README.txt | 92 + upload-tool/build.bat | 52 + upload-tool/requirements.txt | 3 + upload-tool/upload_tool.py | 499 + 36 files changed, 32178 insertions(+) create mode 100644 .gitignore create mode 100644 DEPLOY.md create mode 100644 DEPLOYMENT.md create mode 100644 DOCKER部署指南.md create mode 100644 QUICK_START.txt create mode 100644 README.md create mode 100644 VERSION.txt create mode 100644 backend/.env.example create mode 100644 backend/Dockerfile create mode 100644 backend/auth.js create mode 100644 backend/backup.bat create mode 100644 backend/database.js create mode 100644 backend/package-lock.json create mode 100644 backend/package.json create mode 100644 backend/server.js create mode 100644 backend/start.bat create mode 100644 backend/storage.js create mode 100644 deploy.sh create mode 100644 docker-compose.yml create mode 100644 frontend/app.html create mode 100644 frontend/app.js create mode 100644 frontend/index.html create mode 100644 frontend/libs/axios.min.js create mode 100644 frontend/libs/fontawesome/css/all.min.css create mode 100644 frontend/libs/fontawesome/webfonts/fa-brands-400.woff2 create mode 100644 frontend/libs/fontawesome/webfonts/fa-regular-400.woff2 create mode 100644 frontend/libs/fontawesome/webfonts/fa-solid-900.woff2 create mode 100644 frontend/libs/vue.global.js create mode 100644 frontend/libs/vue.global.prod.js create mode 100644 frontend/share.html create mode 100644 nginx/nginx.conf create mode 100644 nginx/nginx.conf.example create mode 100644 upload-tool/README.txt create mode 100644 upload-tool/build.bat create mode 100644 upload-tool/requirements.txt create mode 100644 upload-tool/upload_tool.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c59e420 --- /dev/null +++ b/.gitignore @@ -0,0 +1,67 @@ +# 依赖 +node_modules/ +__pycache__/ +*.pyc +*.pyo + +# 数据库 +*.db +*.db-journal +*.sqlite +*.sqlite3 + +# 临时文件 +backend/uploads/ +storage/ # 本地存储数据 +*.log +.DS_Store +Thumbs.db + +# 环境配置 +.env +!backend/.env.example + +# SSL证书 +certbot/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# 上传工具构建产物 +upload-tool/dist/ +upload-tool/build/ +upload-tool/__pycache__/ +upload-tool/config.json +upload-tool/*.spec + +# 备份文件 +*.bak +*.backup +*.old + +# 操作系统 +.DS_Store +.Spotlight-V100 +.Trashes +ehthumbs.db +Desktop.ini + +# 压缩文件 +*.zip +*.tar +*.gz +*.rar +*.7z + +# npm/yarn +npm-debug.log* +yarn-debug.log* +yarn-error.log* +package-lock.json.bak + +# Claude配置 +.claude/ diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..3eeb7b1 --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,183 @@ +# 玩玩云 - 部署指南 + +## 🚀 快速部署(3分钟完成) + +### 第一步:上传项目到服务器 + +```bash +# 方法1: 使用scp上传 +scp -r ftp-web-manager root@服务器IP:/var/www/ + +# 方法2: 使用FTP工具上传到 /var/www/ 目录 +``` + +### 第二步:SSH登录服务器 + +```bash +ssh root@服务器IP +``` + +### 第三步:一键部署 + +```bash +cd /var/www/ftp-web-manager +bash deploy.sh +``` + +部署脚本会自动: +- ✅ 检查Docker环境 +- ✅ 创建必要目录 +- ✅ 构建Docker镜像 +- ✅ 启动所有服务 +- ✅ 显示访问信息 + +### 第四步:访问系统 + +打开浏览器访问: +``` +http://服务器IP:8080 +``` + +使用默认账号登录: +``` +用户名: admin +密码: admin123 +``` + +**⚠️ 重要:首次登录后立即修改密码!** + +--- + +## 📋 环境要求 + +- Docker 20.10.0+ +- Docker Compose 2.0.0+ +- 最低 1GB 内存(推荐 2GB+) +- Linux 系统(Ubuntu/Debian/CentOS) + +--- + +## 🔧 手动部署(如果自动脚本失败) + +```bash +# 1. 进入项目目录 +cd /var/www/ftp-web-manager + +# 2. 创建必要目录 +mkdir -p certbot/conf certbot/www backend/uploads + +# 3. 构建并启动 +docker-compose up --build -d + +# 4. 查看日志 +docker-compose logs -f +``` + +--- + +## ✅ 部署验证 + +检查容器状态: +```bash +docker-compose ps +``` + +应该看到3个容器都是 "Up" 状态: +- wanwanyun-backend +- wanwanyun-frontend +- wanwanyun-certbot + +查看后端日志: +```bash +docker-compose logs backend +``` + +应该看到: +``` +数据库初始化完成 +默认管理员账号已创建 +玩玩云已启动 +``` + +--- + +## 🛑 停止服务 + +```bash +cd /var/www/ftp-web-manager +docker-compose down +``` + +--- + +## 🔄 重启服务 + +```bash +cd /var/www/ftp-web-manager +docker-compose restart +``` + +--- + +## 📦 更新代码 + +```bash +cd /var/www/ftp-web-manager +git pull # 或重新上传文件 +docker-compose up --build -d +``` + +--- + +## ❓ 常见问题 + +### Q: 端口8080被占用怎么办? + +修改 docker-compose.yml 中的端口映射: +```yaml +ports: + - "8081:80" # 改为8081或其他端口 +``` + +### Q: Docker容器启动失败? + +```bash +# 查看详细日志 +docker-compose logs backend + +# 重新构建 +docker-compose down +docker-compose up --build -d +``` + +### Q: 忘记管理员密码怎么办? + +删除数据库文件重新初始化: +```bash +docker-compose down +rm backend/ftp-manager.db +docker-compose up -d +``` + +### Q: 如何配置HTTPS? + +参考主README.md中的SSL配置章节。 + +--- + +## 📞 获取帮助 + +- 查看详细文档: README.md +- 查看部署检查报告: 桌面上的检查报告文件 +- 查看对话历史: 桌面上的对话总结文件 + +--- + +**部署成功后,记得:** +1. ✅ 修改admin密码 +2. ✅ 配置SFTP连接 +3. ✅ 设置JWT密钥(backend/.env) +4. ✅ 配置HTTPS(生产环境) +5. ✅ 定期备份数据库 + +祝您使用愉快!☁️ diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..6d97557 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,189 @@ +# 玩玩云部署指南 + +## 快速部署 + +### 1. 基础部署(Docker Compose) + +```bash +# 克隆项目 +git clone +cd ftp-web-manager + +# 启动服务 +docker-compose up -d +``` + +服务将在以下端口运行: +- Frontend (Nginx): 8080 (HTTP), 8443 (HTTPS) +- Backend (Node.js): 40001 + +### 2. 如果使用宿主机Nginx作为反向代理 + +如果你在宿主机上使用Nginx作为SSL终止/反向代理(推荐用于生产环境),需要在Nginx配置中添加大文件上传支持。 + +#### 2.1 创建Nginx配置文件 + +创建 `/etc/nginx/sites-available/wanwanyun.conf`(或对应的配置目录): + +```nginx +server { + listen 80; + server_name your-domain.com; + + # HTTP重定向到HTTPS + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl http2; + server_name your-domain.com; + + # SSL证书配置(使用Let's Encrypt或其他证书) + ssl_certificate /path/to/fullchain.pem; + ssl_certificate_key /path/to/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + # 反向代理到Docker容器 + location / { + # ⚠️ 重要:设置最大上传文件大小为5GB + client_max_body_size 5G; + + # ⚠️ 重要:大文件上传超时设置(1小时) + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + proxy_connect_timeout 3600s; + + # 代理到Docker容器的8080端口 + proxy_pass http://127.0.0.1:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} +``` + +#### 2.2 应用配置 + +```bash +# 启用站点配置 +ln -s /etc/nginx/sites-available/wanwanyun.conf /etc/nginx/sites-enabled/ + +# 测试Nginx配置 +nginx -t + +# 重新加载Nginx +nginx -s reload +``` + +### 3. 宝塔面板用户 + +如果使用宝塔面板,配置文件通常在: +- `/www/server/panel/vhost/nginx/your-domain.conf` + +在站点的 `location /` 块中添加: + +```nginx +location / { + # 设置最大上传文件大小为5GB + client_max_body_size 5G; + + # 大文件上传超时设置(1小时) + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + proxy_connect_timeout 3600s; + + # ... 其他代理配置 +} +``` + +然后重载Nginx: +```bash +nginx -s reload +``` + +## 上传限制说明 + +系统支持的最大上传文件大小为 **5GB**,需要在以下三个层级进行配置: + +### 1. ✅ 容器内Nginx(已配置) +- 文件:`nginx/nginx.conf` +- 配置:`client_max_body_size 5G;` + +### 2. ✅ 后端Multer(已配置) +- 文件:`backend/server.js` +- 配置:`limits: { fileSize: 5 * 1024 * 1024 * 1024 }` + +### 3. ⚠️ 宿主机Nginx(需要手动配置) +- 如果使用宿主机Nginx作为反向代理 +- 必须在 `location /` 块中添加 `client_max_body_size 5G;` +- 否则上传会在64MB时失败(Nginx默认限制) + +## 故障排查 + +### 上传文件提示413错误 + +**问题**:上传大于64MB的文件时失败,浏览器控制台显示 `413 Payload Too Large` + +**原因**:宿主机Nginx的 `client_max_body_size` 限制(默认1MB或64MB) + +**解决方案**: +1. 找到宿主机Nginx配置文件(通常是 `/etc/nginx/sites-available/` 或 `/www/server/panel/vhost/nginx/`) +2. 在 `location /` 块中添加: + ```nginx + client_max_body_size 5G; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + proxy_connect_timeout 3600s; + ``` +3. 测试并重载Nginx: + ```bash + nginx -t + nginx -s reload + ``` + +### 上传进度 + +前端已实现实时上传进度显示(使用axios的 `onUploadProgress`),无需额外配置。 + +## 存储配置 + +系统支持两种存储方式: + +### 本地存储 +- 文件存储在:`backend/local-storage/` +- 可设置用户配额限制 +- 适合中小型部署 + +### SFTP存储 +- 用户可配置自己的SFTP服务器 +- 支持HTTP直接下载(配置 `http_download_base_url`) +- 适合大规模部署 + +## 安全建议 + +1. **使用HTTPS**:生产环境务必配置SSL证书 +2. **定期备份数据库**:`backend/data.db` 包含所有用户数据 +3. **限制管理员账号**:定期审查用户权限 +4. **配置防火墙**:只开放必要的端口(80, 443) + +## 技术支持 + +如有问题,请查看日志: + +```bash +# 后端日志 +docker logs wanwanyun-backend + +# 前端日志 +docker logs wanwanyun-frontend + +# Nginx日志 +tail -f /www/wwwlogs/your-domain.log +tail -f /www/wwwlogs/your-domain.error.log +``` diff --git a/DOCKER部署指南.md b/DOCKER部署指南.md new file mode 100644 index 0000000..cb6ac37 --- /dev/null +++ b/DOCKER部署指南.md @@ -0,0 +1,1211 @@ +# 玩玩云 - Docker部署指南 + +> **📌 重要提示:代码自动适配域名** +> +> 本项目代码**完全支持域名动态适配**,无需修改任何代码: +> - ✅ 前端会自动使用当前访问的域名和协议(http/https) +> - ✅ 后端会从HTTP请求头动态获取域名信息 +> - ✅ 上传工具配置文件会自动生成正确的API地址 +> +> **您只需要:** +> 1. 配置宝塔/Nginx的反向代理指向Docker容器 +> 2. 配置SSL证书(可选,推荐) +> 3. 启动Docker容器 +> +> **无需修改代码中的任何域名或IP地址!** + +## 目录 +1. [系统要求](#系统要求) +2. [部署架构](#部署架构) +3. [快速部署步骤](#快速部署步骤) +4. [详细配置说明](#详细配置说明) +5. [常见问题解决](#常见问题解决) +6. [维护操作](#维护操作) + +--- + +## 系统要求 + +### 服务器配置 +- **操作系统**: Ubuntu 20.04+ / CentOS 7+ / Debian 10+ +- **内存**: 最低 1GB RAM(推荐 2GB+) +- **磁盘**: 最低 10GB 可用空间 +- **端口**: 需要开放以下端口 + - `80` - HTTP (可选,用于重定向到HTTPS) + - `443` - HTTPS (SSL访问) + - `8080` - 内部HTTP端口(Docker容器) + - `40001` - 后端API端口(Docker容器) + +### 软件依赖 +- **Docker**: 20.10.0+ +- **Docker Compose**: 2.0.0+ +- **Git**: 用于代码管理(可选) +- **Nginx**: 宝塔面板或系统级Nginx(用于SSL终止和反向代理) + +--- + +## 部署架构 + +``` +互联网 + ↓ +宝塔 Nginx (443 HTTPS) ← SSL证书在此层 + ↓ +Docker Nginx (8080 HTTP) ← 前端静态文件 + ↓ +Backend API (40001) ← Node.js + SQLite + ↓ +SFTP服务器 (用户自建) +``` + +### 容器架构 +``` +docker-compose.yml +├── frontend (nginx:alpine) +│ ├── 静态文件: /usr/share/nginx/html +│ ├── Nginx配置: /etc/nginx/conf.d/default.conf +│ └── 端口: 8080:80, 8443:443 +│ +├── backend (wanwanyun-backend) +│ ├── Node.js应用: /app +│ ├── 数据库: /app/users.db (SQLite) +│ ├── 上传工具: /upload-tool +│ └── 端口: 40001:40001 +│ +└── certbot (certbot/certbot) + └── SSL证书: /etc/letsencrypt (可选) +``` + +--- + +## 快速部署步骤 + +### 步骤1: 准备服务器环境 + +```bash +# 1. 安装Docker +curl -fsSL https://get.docker.com | sh +systemctl start docker +systemctl enable docker + +# 2. 安装Docker Compose +curl -L "https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +chmod +x /usr/local/bin/docker-compose + +# 3. 验证安装 +docker --version +docker-compose --version +``` + +### 步骤2: 上传项目文件 + +```bash +# 在服务器上创建项目目录 +mkdir -p /var/www/wanwanyun +cd /var/www/wanwanyun + +# 从本地上传所有文件(在本地执行) +scp -r C:/Users/Administrator/Desktop/ftp-web-manager/* root@YOUR_SERVER_IP:/var/www/wanwanyun/ +``` + +或使用Git: +```bash +cd /var/www/wanwanyun +git clone YOUR_REPOSITORY_URL . +``` + +### 步骤3: 构建后端镜像 + +```bash +cd /var/www/wanwanyun +docker build -t wanwanyun-backend -f backend/Dockerfile . +``` + +**Dockerfile内容**(应该在 `backend/Dockerfile`): +```dockerfile +FROM node:18-alpine + +WORKDIR /app + +# 复制package.json和package-lock.json +COPY backend/package*.json ./ + +# 安装依赖 +RUN npm install --production + +# 复制应用代码 +COPY backend/ ./ + +# 暴露端口 +EXPOSE 40001 + +# 启动应用 +CMD ["node", "server.js"] +``` + +### 步骤4: 启动容器 + +```bash +cd /var/www/wanwanyun +docker-compose up -d + +# 查看容器状态 +docker-compose ps + +# 查看日志 +docker-compose logs -f +``` + +### 步骤5: 配置宝塔Nginx(或系统Nginx) + +#### 宝塔面板配置 + +1. **添加网站** + - 域名: `example.com`(替换为您的实际域名) + - 根目录: `/var/www/wanwanyun/frontend`(仅作为占位,实际不使用) + - PHP版本: 纯静态 + +2. **配置SSL证书** + - 在宝塔面板申请Let's Encrypt证书 + - 或上传自有证书 + +3. **修改Nginx配置** + +编辑网站配置文件(通常在 `/www/server/panel/vhost/nginx/您的域名.conf`): + +```nginx +# HTTP重定向到HTTPS +server { + listen 80; + server_name example.com; # 替换为您的域名 + return 301 https://$server_name$request_uri; +} + +# HTTPS配置 +server { + listen 443 ssl http2; + server_name example.com; # 替换为您的域名 + + # SSL证书路径(宝塔自动配置) + ssl_certificate /www/server/panel/vhost/cert/example.com/fullchain.pem; + ssl_certificate_key /www/server/panel/vhost/cert/example.com/privkey.pem; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + # 反向代理到Docker容器 + location / { + proxy_pass http://127.0.0.1:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + # API反向代理 + location /api/ { + proxy_pass http://127.0.0.1:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # 分享链接 + location /s/ { + proxy_pass http://127.0.0.1:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + # 访问日志 + access_log /www/wwwlogs/example.com.log; + error_log /www/wwwlogs/example.com.error.log; +} +``` + +4. **重载Nginx** +```bash +nginx -t # 测试配置 +nginx -s reload # 重载配置 +``` + +#### 系统Nginx配置(无宝塔) + +配置文件位置: `/etc/nginx/sites-available/wanwanyun.conf` + +```nginx +# 与上述配置相同,但SSL证书路径可能不同 +ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; +ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; +``` + +创建软链接并重载: +```bash +ln -s /etc/nginx/sites-available/wanwanyun.conf /etc/nginx/sites-enabled/ +nginx -t +systemctl reload nginx +``` + +### 步骤6: 验证部署 + +1. **访问网站** + ``` + https://example.com # 替换为您的域名 + ``` + +2. **默认管理员账号** + - 首次启动会自动创建: + - 用户名: `admin` + - 密码: `admin123` + - ⚠️ **登录后请立即修改密码!** + +3. **测试功能** + - ✓ 登录/注册 + - ✓ SFTP配置 + - ✓ 文件列表 + - ✓ 文件上传/下载 + - ✓ 文件分享 + - ✓ 下载上传工具 + +--- + +## 详细配置说明 + +### 1. docker-compose.yml 配置 + +```yaml +version: '3.8' + +services: + backend: + image: wanwanyun-backend + container_name: wanwanyun-backend + restart: always + ports: + - "40001:40001" + volumes: + - ./backend:/app + - /app/node_modules + - ./upload-tool:/upload-tool # 上传工具目录 + environment: + - NODE_ENV=production + networks: + - wanwanyun-network + + frontend: + image: nginx:alpine + container_name: wanwanyun-frontend + restart: always + ports: + - "8080:80" # HTTP端口 + - "8443:443" # HTTPS端口(可选,如果使用Docker内SSL) + volumes: + - ./frontend:/usr/share/nginx/html + - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf + - ./certbot/conf:/etc/letsencrypt # 可选 + - ./certbot/www:/var/www/certbot # 可选 + depends_on: + - backend + networks: + - wanwanyun-network + command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'" + + certbot: # 可选:Docker内自动续期SSL证书 + image: certbot/certbot + container_name: wanwanyun-certbot + restart: unless-stopped + volumes: + - ./certbot/conf:/etc/letsencrypt + - ./certbot/www:/var/www/certbot + entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" + +networks: + wanwanyun-network: + driver: bridge +``` + +### 2. Nginx配置(Docker内部) + +文件位置: `nginx/nginx.conf` + +```nginx +server { + listen 80; + server_name localhost; + + # 前端静态文件 + location / { + root /usr/share/nginx/html; + index index.html; + try_files $uri $uri/ =404; + } + + # 后端API反向代理 + location /api/ { + proxy_pass http://backend:40001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + } + + # 分享链接重定向 + location /s/ { + proxy_pass http://backend:40001; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} +``` + +### 3. 环境变量配置(可选) + +创建 `.env` 文件: +```env +# 数据库配置 +DB_PATH=/app/users.db + +# JWT密钥(请修改为随机字符串) +JWT_SECRET=your-secret-key-change-this-in-production + +# 后端端口 +PORT=40001 + +# 最大上传大小(字节) +MAX_UPLOAD_SIZE=104857600 + +# 生产环境标志 +NODE_ENV=production +``` + +### 4. 目录结构说明 + +``` +/var/www/wanwanyun/ +├── backend/ # 后端代码 +│ ├── server.js # 主服务器文件 +│ ├── package.json # Node.js依赖 +│ ├── Dockerfile # 后端镜像构建文件 +│ └── users.db # SQLite数据库(自动生成) +│ +├── frontend/ # 前端代码 +│ ├── index.html # 登录页面 +│ ├── app.html # 主应用页面 +│ ├── app.js # Vue.js应用逻辑 +│ └── libs/ # 第三方库 +│ ├── vue.global.js +│ ├── axios.min.js +│ └── fontawesome/ +│ +├── nginx/ # Nginx配置 +│ ├── nginx.conf # 当前使用的配置 +│ └── nginx.conf.example # 配置示例 +│ +├── upload-tool/ # 上传工具 +│ └── dist/ +│ └── 玩玩云上传工具.exe +│ +├── certbot/ # SSL证书(可选) +│ ├── conf/ +│ └── www/ +│ +└── docker-compose.yml # Docker编排文件 +``` + +--- + +## 常见问题解决 + +### 问题1: 502 Bad Gateway + +**症状**: 访问网站显示nginx 502错误 + +**原因**: +- Docker容器未启动 +- 宝塔nginx配置错误(HTTPS代理到HTTP容器) +- 端口冲突 + +**解决方案**: + +```bash +# 1. 检查容器状态 +docker-compose ps + +# 2. 查看容器日志 +docker-compose logs backend +docker-compose logs frontend + +# 3. 重启容器 +docker-compose restart + +# 4. 检查宝塔nginx配置 +# 确保proxy_pass使用HTTP而不是HTTPS +proxy_pass http://127.0.0.1:8080; # 正确 +# proxy_pass https://127.0.0.1:8443; # 错误(如果Docker内未配置SSL) + +# 5. 检查端口占用 +netstat -tulnp | grep 8080 +netstat -tulnp | grep 40001 +``` + +### 问题2: 上传工具无法下载 + +**症状**: 点击"下载上传工具"后浏览器报错 + +**原因**: Docker容器未挂载 `upload-tool` 目录 + +**解决方案**: + +```bash +# 1. 检查docker-compose.yml中是否有以下配置 +# backend服务的volumes部分应包含: +- ./upload-tool:/upload-tool + +# 2. 检查文件是否存在 +ls -la /var/www/wanwanyun/upload-tool/dist/ + +# 3. 重新创建容器(不是重启) +cd /var/www/wanwanyun +docker-compose up -d --force-recreate backend + +# 4. 验证容器内可访问文件 +docker exec wanwanyun-backend ls -la /upload-tool/dist/ +``` + +### 问题3: SFTP连接失败 + +**症状**: 保存SFTP配置时报错 "Connection refused" 或 "Permission denied" + +**原因**: +- SFTP服务器IP/端口错误 +- 防火墙阻止 +- 认证信息错误 +- 服务器IP被SFTP服务器限制 + +**解决方案**: + +```bash +# 1. 在服务器上测试SFTP连接 +sshpass -p 'YOUR_PASSWORD' sftp -P 22 username@sftp.server.com + +# 2. 检查防火墙 +# 确保后端容器可以访问外部SFTP服务器 +docker exec wanwanyun-backend ping sftp.server.com + +# 3. 如果SFTP服务器限制IP +# 需要在SFTP服务器上添加Docker服务器IP到白名单 + +# 4. 检查后端日志 +docker logs wanwanyun-backend | grep -i sftp +``` + +### 问题4: 文件删除失败 "Permission denied" + +**症状**: 删除某些文件时提示权限被拒绝 + +**原因**: SFTP chroot安全限制,根目录文件不可删除 + +**解决方案**: + +这**不是bug**,是安全设计: +- `/` 根目录: 只读,系统配置文件(.bashrc等) +- `/files` 目录: 可读写,用户工作目录 + +**建议**: 所有文件应上传到 `/files` 目录 + +### 问题5: 语法错误 "Uncaught SyntaxError" + +**症状**: 浏览器控制台显示 app.js 语法错误 + +**原因**: JavaScript字符串中有字面换行符 + +**解决方案**: + +```bash +# 检查是否有换行符错误 +cd /var/www/wanwanyun/frontend +cat -A app.js | grep -n "\\$" + +# 如果发现问题,重新从本地同步 +scp C:/Users/Administrator/Desktop/ftp-web-manager/frontend/app.js root@SERVER:/var/www/wanwanyun/frontend/ + +# 重启前端容器 +docker-compose restart frontend +``` + +### 问题6: 数据库锁定 "Database is locked" + +**症状**: 多个用户同时操作时报数据库锁定错误 + +**原因**: SQLite不支持高并发写入 + +**解决方案**: + +1. **短期方案**: 增加超时时间 + +修改 `backend/server.js`: +```javascript +const db = new Database('users.db', { + timeout: 10000 // 增加到10秒 +}); +``` + +2. **长期方案**: 迁移到PostgreSQL或MySQL + +### 问题7: 端口冲突 + +**症状**: 启动容器时报错 "port is already allocated" + +**解决方案**: + +```bash +# 1. 查找占用端口的进程 +netstat -tulnp | grep 8080 +netstat -tulnp | grep 40001 + +# 2. 停止冲突的服务 +systemctl stop nginx # 如果是系统nginx占用8080 + +# 3. 或修改docker-compose.yml中的端口映射 +# 将 "8080:80" 改为 "8081:80" +# 相应修改宝塔nginx配置中的proxy_pass端口 +``` + +### 问题8: SSL证书过期 + +**症状**: 浏览器显示证书无效警告 + +**解决方案**: + +#### 使用宝塔面板: +```bash +# 在宝塔面板网站设置中点击"续签证书" +# 或使用宝塔计划任务自动续签 +``` + +#### 使用Certbot (Docker): +```bash +# 手动续签 +docker-compose run --rm certbot renew + +# 重载nginx +docker-compose exec frontend nginx -s reload + +# certbot容器会自动每12小时检查并续签 +``` + +### 问题9: 容器内存不足 + +**症状**: 容器频繁重启,日志显示 "Out of memory" + +**解决方案**: + +```bash +# 1. 检查内存使用 +docker stats + +# 2. 限制容器内存(在docker-compose.yml中) +services: + backend: + # ... 其他配置 + deploy: + resources: + limits: + memory: 512M + reservations: + memory: 256M + +# 3. 清理Docker缓存 +docker system prune -a +``` + +### 问题10: X-Forwarded-Proto检测失败 + +**症状**: 下载上传工具时生成的URL是HTTP而不是HTTPS + +**原因**: 反向代理未传递协议头 + +**解决方案**: + +确保宝塔nginx配置包含: +```nginx +proxy_set_header X-Forwarded-Proto $scheme; +``` + +验证后端正确读取: +```javascript +// backend/server.js +const protocol = req.get('x-forwarded-proto') || req.protocol; +``` + +--- + +## 维护操作 + +### 日常维护 + +#### 查看日志 +```bash +# 查看所有容器日志 +docker-compose logs -f + +# 查看特定容器日志 +docker-compose logs -f backend +docker-compose logs -f frontend + +# 查看最近100行 +docker-compose logs --tail=100 backend + +# 实时滚动日志 +docker-compose logs -f --tail=50 +``` + +#### 重启服务 +```bash +# 重启所有容器 +docker-compose restart + +# 重启特定容器 +docker-compose restart backend +docker-compose restart frontend + +# 重新创建容器(配置更改后) +docker-compose up -d --force-recreate +``` + +#### 停止和启动 +```bash +# 停止所有容器 +docker-compose stop + +# 启动所有容器 +docker-compose start + +# 完全停止并移除容器 +docker-compose down + +# 启动(创建容器) +docker-compose up -d +``` + +### 更新部署 + +#### 更新代码 +```bash +# 1. 备份数据库 +cp /var/www/wanwanyun/backend/users.db /var/www/wanwanyun/backend/users.db.backup + +# 2. 拉取最新代码(如果使用Git) +cd /var/www/wanwanyun +git pull + +# 3. 或从本地上传更新的文件 +scp C:/Users/Administrator/Desktop/ftp-web-manager/backend/server.js root@SERVER:/var/www/wanwanyun/backend/ +scp C:/Users/Administrator/Desktop/ftp-web-manager/frontend/app.js root@SERVER:/var/www/wanwanyun/frontend/ + +# 4. 重启容器 +docker-compose restart backend frontend +``` + +#### 更新Docker镜像 +```bash +# 1. 停止容器 +docker-compose down + +# 2. 重新构建后端镜像 +docker build -t wanwanyun-backend -f backend/Dockerfile . + +# 3. 拉取最新官方镜像 +docker-compose pull frontend certbot + +# 4. 启动 +docker-compose up -d +``` + +### 备份和恢复 + +#### 备份数据 +```bash +# 创建备份目录 +mkdir -p /backup/wanwanyun/$(date +%Y%m%d) + +# 备份数据库 +cp /var/www/wanwanyun/backend/users.db /backup/wanwanyun/$(date +%Y%m%d)/ + +# 备份整个项目 +tar -czf /backup/wanwanyun/$(date +%Y%m%d)/wanwanyun-full.tar.gz /var/www/wanwanyun + +# 定期备份脚本(添加到crontab) +# 每天凌晨2点备份 +# 0 2 * * * /usr/bin/tar -czf /backup/wanwanyun/$(date +\%Y\%m\%d)/backup.tar.gz /var/www/wanwanyun/backend/users.db +``` + +#### 恢复数据 +```bash +# 停止容器 +docker-compose down + +# 恢复数据库 +cp /backup/wanwanyun/20251108/users.db /var/www/wanwanyun/backend/ + +# 启动容器 +docker-compose up -d +``` + +### 监控和性能 + +#### 监控容器资源 +```bash +# 实时监控 +docker stats + +# 查看容器详细信息 +docker inspect wanwanyun-backend +docker inspect wanwanyun-frontend +``` + +#### 性能优化 + +1. **启用Gzip压缩** (nginx/nginx.conf): +```nginx +gzip on; +gzip_types text/plain text/css application/json application/javascript text/xml application/xml; +gzip_min_length 1000; +``` + +2. **配置浏览器缓存**: +```nginx +location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { + expires 7d; + add_header Cache-Control "public, immutable"; +} +``` + +3. **限制并发连接**: +```nginx +limit_conn_zone $binary_remote_addr zone=addr:10m; +limit_conn addr 10; +``` + +### 清理和优化 + +```bash +# 清理未使用的Docker资源 +docker system prune -a + +# 清理日志(如果过大) +truncate -s 0 /var/lib/docker/containers/*/*-json.log + +# 查看磁盘使用 +docker system df +``` + +--- + +## 安全建议 + +### 1. 修改默认配置 + +- 修改JWT_SECRET为随机字符串 +- 修改数据库默认路径 +- 限制管理员账号数量 + +### 2. 防火墙配置 + +```bash +# 只开放必要端口 +ufw allow 80/tcp +ufw allow 443/tcp +ufw deny 40001/tcp # 不对外开放后端端口 +ufw deny 8080/tcp # 不对外开放Docker HTTP端口 +ufw enable +``` + +### 3. 日志审计 + +```bash +# 定期检查访问日志 +tail -f /www/wwwlogs/example.com.log + +# 查找异常请求 +grep "POST /api/login" /www/wwwlogs/example.com.log | grep -v "200" +``` + +### 4. 定期更新 + +```bash +# 更新系统 +apt update && apt upgrade -y + +# 更新Docker镜像 +docker-compose pull +docker-compose up -d +``` + +--- + +## 故障排查流程 + +### 基本诊断命令 + +```bash +# 1. 检查容器状态 +docker-compose ps + +# 2. 检查端口监听 +netstat -tulnp | grep -E "80|443|8080|40001" + +# 3. 测试后端API +curl http://localhost:40001/api/user/profile + +# 4. 测试前端访问 +curl http://localhost:8080 + +# 5. 检查nginx配置 +nginx -t + +# 6. 查看完整日志 +docker-compose logs --tail=100 + +# 7. 进入容器调试 +docker exec -it wanwanyun-backend sh +docker exec -it wanwanyun-frontend sh +``` + +### 故障决策树 + +``` +网站无法访问 +├── 502 错误 +│ ├── 容器未启动 → docker-compose up -d +│ ├── nginx配置错误 → 检查proxy_pass +│ └── 端口冲突 → netstat检查 +│ +├── 404 错误 +│ ├── 路由配置错误 → 检查nginx.conf +│ └── 文件不存在 → 检查/usr/share/nginx/html +│ +├── 500 错误 +│ ├── 后端崩溃 → docker logs backend +│ ├── 数据库错误 → 检查users.db权限 +│ └── 代码错误 → 查看error.log +│ +└── 连接超时 + ├── 防火墙阻止 → ufw status + ├── 容器网络问题 → docker network ls + └── DNS解析失败 → ping域名 +``` + +--- + +## 联系和支持 + +如果遇到无法解决的问题: + +1. 收集以下信息: + - 错误信息截图 + - `docker-compose logs` 输出 + - `nginx -T` 配置输出 + - 系统信息 `uname -a` + +2. 检查日志中的错误堆栈 + +3. 参考本指南的"常见问题解决"部分 + +--- + +## 附录 + +### A. 完整的docker-compose.yml示例 + +参见项目根目录的 `docker-compose.yml` 文件 + +### B. 完整的Nginx配置示例 + +参见 `nginx/nginx.conf` 和 `nginx/nginx.conf.example` + +### C. 后端Dockerfile示例 + +```dockerfile +FROM node:18-alpine + +WORKDIR /app + +COPY backend/package*.json ./ +RUN npm install --production + +COPY backend/ ./ + +EXPOSE 40001 + +CMD ["node", "server.js"] +``` + +### D. 环境检查脚本 + +```bash +#!/bin/bash +# 保存为 check-env.sh + +echo "=== 玩玩云环境检查 ===" + +echo -e "\n1. Docker版本:" +docker --version + +echo -e "\n2. Docker Compose版本:" +docker-compose --version + +echo -e "\n3. 容器状态:" +docker-compose ps + +echo -e "\n4. 端口监听:" +netstat -tulnp | grep -E "80|443|8080|40001" + +echo -e "\n5. 磁盘空间:" +df -h /var/www/wanwanyun + +echo -e "\n6. 内存使用:" +docker stats --no-stream + +echo -e "\n7. 最近错误日志:" +docker-compose logs --tail=20 | grep -i error + +echo -e "\n=== 检查完成 ===" +``` + +--- + +**文档版本**: v1.0 +**最后更新**: 2025-11-08 +**适用版本**: 玩玩云 v1.0 + +--- + +## SFTP服务器配置指南 + +### 方案:使用bindfs实现根目录可写的chroot + +本项目推荐使用bindfs技术配置SFTP服务器,可以实现: +- ✅ **根目录可写** - 上传工具可直接上传到 `/filename` +- ✅ **Chroot隔离** - 用户只能访问独立空间,安全性高 +- ✅ **无需修改代码** - 上传工具保持原样 + +### 配置步骤 + +#### 1. 安装必要工具 + +```bash +# 在SFTP服务器上执行 +apt-get update +apt-get install -y bindfs openssh-server +``` + +#### 2. 创建用户和目录 + +```bash +# 创建真实数据目录 +mkdir -p /var/sftp_data/wwy_upload +chown -R wwy_upload:wwy_upload /var/sftp_data/wwy_upload +chmod 755 /var/sftp_data/wwy_upload + +# 创建chroot目录 +mkdir -p /home/wwy_ftp_upload + +# 创建用户 +useradd -d /var/sftp_data/wwy_upload -s /bin/bash wwy_upload +printf 'wwy_upload:Wwy@2024Pass' | chpasswd +``` + +#### 3. 配置bindfs挂载 + +```bash +# 使用bindfs挂载(关键步骤) +bindfs --mirror=wwy_upload \ + --force-user=root --force-group=root \ + --perms=0755 \ + /var/sftp_data/wwy_upload /home/wwy_ftp_upload +``` + +**参数说明**: +- `--mirror=wwy_upload`: 以wwy_upload用户身份镜像所有操作 +- `--force-user=root --force-group=root`: 让目录看起来归root所有(满足chroot要求) +- `--perms=0755`: 设置目录权限 + +#### 4. 配置开机自动挂载 + +编辑 `/etc/fstab`,添加: + +``` +/var/sftp_data/wwy_upload /home/wwy_ftp_upload fuse.bindfs mirror=wwy_upload,force-user=root,force-group=root,perms=0755 0 0 +``` + +#### 5. 配置SSH chroot + +编辑 `/etc/ssh/sshd_config`,添加: + +``` +Match User wwy_upload + ChrootDirectory /home/wwy_ftp_upload + ForceCommand internal-sftp + AllowTcpForwarding no + X11Forwarding no +``` + +重启SSH服务: + +```bash +systemctl restart sshd +``` + +#### 6. 测试配置 + +```bash +# 测试上传到根目录 +echo 'Test file' > /tmp/test.txt +sshpass -p 'Wwy@2024Pass' sftp wwy_upload@localhost < { + const filePath = req.query.path; + const sftp = await connectToSFTP(req.user); + + // 获取文件大小(用于显示下载进度) + const fileStats = await sftp.stat(filePath); + const fileSize = fileStats.size; + + // 设置响应头 + res.setHeader('Content-Type', 'application/octet-stream'); + res.setHeader('Content-Length', fileSize); // 浏览器可显示进度 + res.setHeader('Content-Disposition', 'attachment; filename="..."'); + + // 流式传输(服务器不保存临时文件) + const stream = sftp.createReadStream(filePath); + stream.pipe(res); +}); +``` + +### 自动选择逻辑 + +前端自动判断使用哪种模式: + +```javascript +downloadFile(file) { + if (file.httpDownloadUrl) { + // 如果配置了HTTP URL,使用HTTP直接下载 + window.open(file.httpDownloadUrl, "_blank"); + } else { + // 如果没有配置,通过后端SFTP下载 + const link = document.createElement('a'); + link.href = `${this.apiBase}/api/files/download?path=${filePath}&token=${token}`; + link.click(); + } +} +``` + +--- + +## 版本更新日志 + +### v1.1 (2025-11-09) + +**新增功能**: +- ✅ SFTP流式下载(无需配置HTTP URL) +- ✅ 下载进度显示(文件大小、速度、剩余时间) +- ✅ bindfs SFTP服务器配置方案 + +**改进**: +- ✅ 服务器零存储下载(纯中转) +- ✅ 下载逻辑自动选择(HTTP/SFTP) +- ✅ 优化用户体验 + +**修复**: +- ✅ 修复下载无进度显示的问题 +- ✅ 修复未配置HTTP URL时无法下载的问题 + +### v1.0 (2025-11-08) + +**初始版本**: +- ✅ 文件管理基础功能 +- ✅ 文件分享功能 +- ✅ 多用户系统 +- ✅ 上传工具 +- ✅ Docker部署 + +--- + +**文档版本**: v1.1 +**最后更新**: 2025-11-09 +**适用版本**: 玩玩云 v1.1 diff --git a/QUICK_START.txt b/QUICK_START.txt new file mode 100644 index 0000000..c10196e --- /dev/null +++ b/QUICK_START.txt @@ -0,0 +1,49 @@ +╔══════════════════════════════════════════════════════════════╗ +║ 玩玩云 - 快速开始指南 ║ +╚══════════════════════════════════════════════════════════════╝ + +【最简单的部署方法 - 3步完成】 + +1️⃣ 上传项目到服务器的 /var/www/ 目录 + +2️⃣ SSH登录服务器,执行: + cd /var/www/ftp-web-manager + bash deploy.sh + +3️⃣ 打开浏览器访问: + http://服务器IP:8080 + + 默认账号:admin + 默认密码:admin123 + +✅ 部署完成! + +─────────────────────────────────────────────────────────────── + +【环境要求】 +✓ Docker 20.10+ +✓ Docker Compose 2.0+ +✓ Linux系统 +✓ 1GB+ 内存 + +─────────────────────────────────────────────────────────────── + +【详细文档】 +📖 完整部署指南:DEPLOY.md +📖 使用说明:README.md +📖 部署检查报告:桌面上的检查报告 + +─────────────────────────────────────────────────────────────── + +【重要提示】 +⚠️ 首次登录后立即修改admin密码 +⚠️ 生产环境请配置HTTPS +⚠️ 定期备份 backend/ftp-manager.db 数据库文件 + +─────────────────────────────────────────────────────────────── + +【获取帮助】 +💬 查看常见问题:DEPLOY.md +💬 查看详细文档:README.md + +祝您使用愉快!☁️ diff --git a/README.md b/README.md new file mode 100644 index 0000000..30475e1 --- /dev/null +++ b/README.md @@ -0,0 +1,244 @@ +# 玩玩云 - Web SFTP 文件管理系统 + +> 一个基于Web的SFTP文件管理系统,提供文件上传、下载、分享等功能,支持多用户管理。 + +## 📋 项目简介 + +玩玩云是一个现代化的Web文件管理系统,让您可以通过浏览器管理SFTP服务器上的文件。系统支持文件的上传、下载、重命名、删除、分享等操作,并提供桌面端上传工具,方便快速上传大文件。 + +### 主要特性 + +- ✅ **文件管理** - 浏览、上传、下载、重命名、删除文件 +- ✅ **文件分享** - 生成分享链接,支持密码保护和有效期设置 +- ✅ **多用户系统** - 用户注册、登录、权限管理 +- ✅ **桌面上传工具** - 拖拽上传,实时显示进度 +- ✅ **流式下载** - 服务器零存储,纯中转下载 +- ✅ **管理员功能** - 用户管理、文件审查、系统设置 +- ✅ **Docker部署** - 一键部署,易于维护 + +## 🛠️ 技术栈 + +### 前端 +- **Vue.js 3** - 渐进式JavaScript框架 +- **Axios** - HTTP请求库 +- **Font Awesome** - 图标库 + +### 后端 +- **Node.js 20** - JavaScript运行时 +- **Express** - Web应用框架 +- **better-sqlite3** - 轻量级数据库 +- **ssh2-sftp-client** - SFTP客户端 +- **JWT** - 用户认证 + +### 部署 +- **Docker** - 容器化 +- **Docker Compose** - 容器编排 +- **Nginx** - 反向代理 + +## 🚀 快速开始 + +### 环境要求 + +- **Docker**: 20.10.0+ +- **Docker Compose**: 2.0.0+ +- **操作系统**: Linux (Ubuntu 20.04+ / Debian 10+ / CentOS 7+) +- **内存**: 最低 1GB RAM(推荐 2GB+) + +### 方法1: 一键部署(推荐) + +```bash +# 1. 上传或克隆项目 +cd /var/www +# 将项目文件上传到此目录 + +# 2. 进入项目目录 +cd ftp-web-manager + +# 3. 一键部署 +bash deploy.sh +``` + +deploy.sh脚本会自动: +- 检查Docker和Docker Compose环境 +- 创建必要的目录 +- 构建并启动所有服务 +- 显示访问信息和默认账号 + +### 方法2: 手动部署 + +```bash +# 1. 进入项目目录 +cd /var/www/ftp-web-manager + +# 2. 创建必要的目录 +mkdir -p certbot/conf certbot/www backend/uploads + +# 3. 构建并启动服务 +docker-compose up --build -d + +# 4. 查看日志 +docker-compose logs -f +``` + +### 访问系统 + +部署完成后: + +- **前端地址**: http://服务器IP:8080 +- **后端API**: http://服务器IP:40001 +- **默认管理员账号**: + - 用户名: `admin` + - 密码: `admin123` + - ⚠️ **请立即登录并修改密码!** + +## 📖 使用教程 + +### 配置SFTP服务器 + +首次使用需要配置SFTP连接信息: + +1. 登录后点击右上角用户菜单 +2. 选择"设置" +3. 填写SFTP配置: + - **SFTP主机**: 您的SFTP服务器IP + - **SFTP端口**: 默认22 + - **SFTP用户名**: SFTP账号 + - **SFTP密码**: SFTP密码 + - **HTTP下载基础URL**(可选): 如果有HTTP直接下载地址 + +4. 点击"保存配置" + +### 文件管理 + +- **浏览文件**: 点击文件夹图标进入子目录 +- **上传文件**: 点击"上传文件"按钮选择本地文件 +- **下载文件**: 点击文件行的下载按钮 +- **重命名**: 点击"重命名"按钮修改文件名 +- **删除**: 点击"删除"按钮删除文件 + +### 文件分享 + +1. 点击文件行的"分享"按钮 +2. 设置分享选项: + - **分享密码**(可选) + - **有效期**(可选) +3. 复制分享链接发送给他人 + +## 🔧 维护操作 + +### 查看日志 + +```bash +# 查看所有日志 +docker-compose logs -f + +# 查看后端日志 +docker-compose logs -f backend +``` + +### 重启服务 + +```bash +# 重启所有容器 +docker-compose restart + +# 重启指定容器 +docker-compose restart backend +``` + +### 停止服务 + +```bash +docker-compose down +``` + +### 备份数据 + +```bash +# 备份数据库 +cp backend/ftp-manager.db backup/ftp-manager.db.$(date +%Y%m%d) + +# 备份整个项目 +tar -czf backup/wanwanyun-$(date +%Y%m%d).tar.gz . +``` + +### 更新代码 + +```bash +# 拉取最新代码 +git pull + +# 重新构建并重启 +docker-compose up --build -d +``` + +## 🔐 安全建议 + +1. **修改默认密码**: 首次登录后立即修改admin密码 +2. **使用HTTPS**: 配置SSL证书,使用HTTPS访问 +3. **修改JWT密钥**: 在backend/.env文件中设置随机的JWT_SECRET +4. **定期备份**: 定期备份数据库文件 +5. **限制端口**: 不要对外暴露40001端口,只通过Nginx访问 + +## ❓ 常见问题 + +### Docker容器启动失败 + +```bash +# 查看日志 +docker-compose logs backend + +# 重新构建 +docker-compose down +docker-compose up --build -d +``` + +### 上传失败提示权限错误 + +检查SFTP服务器目录权限,确保上传目录有写权限。 + +### 分享链接无法访问 + +检查nginx配置和防火墙设置,确保端口8080可访问。 + +## 📁 项目结构 + +``` +ftp-web-manager/ +├── backend/ # 后端代码 +│ ├── server.js # 主服务器文件 +│ ├── database.js # 数据库操作 +│ ├── auth.js # 认证中间件 +│ ├── Dockerfile # Docker镜像 +│ └── package.json # 依赖配置 +│ +├── frontend/ # 前端代码 +│ ├── index.html # 登录页面 +│ ├── app.html # 主应用页面 +│ ├── share.html # 分享页面 +│ └── libs/ # 第三方库 +│ +├── nginx/ # Nginx配置 +│ └── nginx.conf # 配置文件 +│ +├── upload-tool/ # 上传工具 +│ ├── upload_tool.py # Python源码 +│ └── build.bat # 打包脚本 +│ +├── docker-compose.yml # Docker编排 +├── deploy.sh # 一键部署脚本 +├── .gitignore # Git忽略文件 +└── README.md # 本文件 +``` + +## 🤝 贡献指南 + +欢迎提交Issue和Pull Request! + +## 📄 许可证 + +本项目仅供学习和个人使用。 + +--- + +**玩玩云** - 让文件管理更简单 ☁️ diff --git a/VERSION.txt b/VERSION.txt new file mode 100644 index 0000000..31bf171 --- /dev/null +++ b/VERSION.txt @@ -0,0 +1,98 @@ +玩玩云 - 版本信息 +═══════════════════════════════════════ + +版本号: v1.0.0 +发布日期: 2025-11-09 +状态: 生产就绪 ✅ + +═══════════════════════════════════════ + +【本版本特性】 + +✅ 完整的文件管理功能 + - SFTP文件浏览、上传、下载 + - 文件重命名、删除 + - 流式下载,支持进度显示 + +✅ 文件分享功能 + - 生成分享链接 + - 支持密码保护 + - 支持有效期设置 + - 双模式下载(HTTP/SFTP) + +✅ 用户管理系统 + - 用户注册、登录 + - 密码加密存储 + - JWT认证 + - 管理员权限管理 + +✅ 桌面上传工具 + - 拖拽上传 + - 实时进度显示 + - 自动配置 + +✅ Docker容器化部署 + - 一键部署脚本 + - 自动环境检查 + - 完整的日志记录 + +═══════════════════════════════════════ + +【技术栈】 + +后端: +- Node.js 20 +- Express 4.x +- better-sqlite3 +- ssh2-sftp-client +- JWT认证 + +前端: +- Vue.js 3 +- Axios +- Font Awesome + +部署: +- Docker +- Docker Compose +- Nginx + +═══════════════════════════════════════ + +【已修复的问题】 + +✅ 数据库初始化语法错误 +✅ 分享链接重定向错误 +✅ 分享页面下载按钮缺失 +✅ 密码验证错误 +✅ SFTP连接过早关闭 +✅ Docker配置不完整 + +═══════════════════════════════════════ + +【部署状态】 + +✅ 数据库自动初始化 +✅ 默认管理员自动创建 +✅ 数据库迁移逻辑完整 +✅ Docker镜像自动构建 +✅ 所有依赖配置齐全 +✅ 部署脚本完整可用 + +═══════════════════════════════════════ + +【安全特性】 + +✅ 密码bcrypt加密 +✅ JWT令牌认证 +✅ SFTP密码安全存储 +✅ SQL注入防护 +✅ XSS防护 +✅ CORS配置 + +═══════════════════════════════════════ + +更新日志: 查看 CHANGELOG.md (如有) +许可证: 仅供学习和个人使用 + +═══════════════════════════════════════ diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000..80aa116 --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,8 @@ +# FTP服务器配置 +FTP_HOST=your-ftp-host.com +FTP_PORT=21 +FTP_USER=your-username +FTP_PASSWORD=your-password + +# 服务器配置 +PORT=3000 diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..93a5e04 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,21 @@ +FROM node:20-alpine + +WORKDIR /app + +# 安装编译工具 +RUN apk add --no-cache python3 make g++ + +# 复制package文件 +COPY package*.json ./ + +# 安装依赖 +RUN npm install --production + +# 复制应用代码 +COPY . . + +# 暴露端口 +EXPOSE 40001 + +# 启动应用 +CMD ["node", "server.js"] diff --git a/backend/auth.js b/backend/auth.js new file mode 100644 index 0000000..28f95f9 --- /dev/null +++ b/backend/auth.js @@ -0,0 +1,108 @@ +const jwt = require('jsonwebtoken'); +const { UserDB } = require('./database'); + +// JWT密钥(生产环境应该放在环境变量中) +const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production'; + +// 生成JWT Token +function generateToken(user) { + return jwt.sign( + { + id: user.id, + username: user.username, + is_admin: user.is_admin + }, + JWT_SECRET, + { expiresIn: '7d' } + ); +} + +// 验证Token中间件 +function authMiddleware(req, res, next) { + // 从请求头、cookie或URL参数中获取token + const token = req.headers.authorization?.replace('Bearer ', '') || req.cookies?.token || req.query?.token; + + if (!token) { + return res.status(401).json({ + success: false, + message: '未提供认证令牌' + }); + } + + try { + const decoded = jwt.verify(token, JWT_SECRET); + const user = UserDB.findById(decoded.id); + + if (!user) { + return res.status(401).json({ + success: false, + message: '用户不存在' + }); + } + + if (user.is_banned) { + return res.status(403).json({ + success: false, + message: '账号已被封禁' + }); + } + + if (!user.is_active) { + return res.status(403).json({ + success: false, + message: '账号未激活' + }); + } + + // 将用户信息附加到请求对象(包含所有存储相关字段) + req.user = { + id: user.id, + username: user.username, + email: user.email, + is_admin: user.is_admin, + has_ftp_config: user.has_ftp_config, + ftp_host: user.ftp_host, + ftp_port: user.ftp_port, + ftp_user: user.ftp_user, + ftp_password: user.ftp_password, + http_download_base_url: user.http_download_base_url, + // 存储相关字段(v2.0新增) + storage_permission: user.storage_permission || 'sftp_only', + current_storage_type: user.current_storage_type || 'sftp', + local_storage_quota: user.local_storage_quota || 1073741824, + local_storage_used: user.local_storage_used || 0 + }; + + next(); + } catch (error) { + if (error.name === 'TokenExpiredError') { + return res.status(401).json({ + success: false, + message: '令牌已过期' + }); + } + + return res.status(401).json({ + success: false, + message: '无效的令牌' + }); + } +} + +// 管理员权限中间件 +function adminMiddleware(req, res, next) { + if (!req.user || !req.user.is_admin) { + return res.status(403).json({ + success: false, + message: '需要管理员权限' + }); + } + next(); +} + +module.exports = { + JWT_SECRET, + generateToken, + authMiddleware, + adminMiddleware +}; diff --git a/backend/backup.bat b/backend/backup.bat new file mode 100644 index 0000000..5c4eb22 --- /dev/null +++ b/backend/backup.bat @@ -0,0 +1,52 @@ +@echo off +chcp 65001 >nul +echo ======================================== +echo 数据库备份工具 +echo ======================================== +echo. + +cd /d %~dp0 + +REM 创建备份目录 +if not exist backup mkdir backup + +REM 生成时间戳 +set YEAR=%date:~0,4% +set MONTH=%date:~5,2% +set DAY=%date:~8,2% +set HOUR=%time:~0,2% +set MINUTE=%time:~3,2% +set SECOND=%time:~6,2% + +REM 去掉小时前面的空格 +if "%HOUR:~0,1%" == " " set HOUR=0%HOUR:~1,1% + +set TIMESTAMP=%YEAR%%MONTH%%DAY%_%HOUR%%MINUTE%%SECOND% + +REM 备份数据库 +copy ftp-manager.db backup\ftp-manager-%TIMESTAMP%.db >nul + +if %errorlevel% == 0 ( + echo [成功] 备份完成! + echo 文件: backup\ftp-manager-%TIMESTAMP%.db + + REM 获取文件大小 + for %%A in (backup\ftp-manager-%TIMESTAMP%.db) do echo 大小: %%~zA 字节 +) else ( + echo [错误] 备份失败! +) + +echo. + +REM 清理30天前的备份 +echo 清理30天前的旧备份... +forfiles /P backup /M ftp-manager-*.db /D -30 /C "cmd /c del @path" 2>nul +if %errorlevel% == 0 ( + echo [成功] 旧备份已清理 +) else ( + echo [提示] 没有需要清理的旧备份 +) + +echo. +echo ======================================== +pause diff --git a/backend/database.js b/backend/database.js new file mode 100644 index 0000000..fa3200b --- /dev/null +++ b/backend/database.js @@ -0,0 +1,554 @@ +const Database = require('better-sqlite3'); +const bcrypt = require('bcryptjs'); +const path = require('path'); + +// 创建或连接数据库 +const db = new Database(path.join(__dirname, 'ftp-manager.db')); + +// 启用外键约束 +db.pragma('foreign_keys = ON'); + +// 初始化数据库表 +function initDatabase() { + // 用户表 + db.exec(` + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT UNIQUE NOT NULL, + email TEXT UNIQUE NOT NULL, + password TEXT NOT NULL, + + -- FTP配置(可选) + ftp_host TEXT, + ftp_port INTEGER DEFAULT 22, + ftp_user TEXT, + ftp_password TEXT, + http_download_base_url TEXT, + + -- 上传工具API密钥 + upload_api_key TEXT, + + -- 用户状态 + is_admin INTEGER DEFAULT 0, + is_active INTEGER DEFAULT 1, + is_banned INTEGER DEFAULT 0, + has_ftp_config INTEGER DEFAULT 0, + + -- 时间戳 + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) + `); + + // 分享链接表 + db.exec(` + CREATE TABLE IF NOT EXISTS shares ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + share_code TEXT UNIQUE NOT NULL, + share_path TEXT NOT NULL, + share_type TEXT DEFAULT 'file', + share_password TEXT, + + -- 分享统计 + view_count INTEGER DEFAULT 0, + download_count INTEGER DEFAULT 0, + + -- 时间戳 + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + expires_at DATETIME, + + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE + ) + `); + + // 系统设置表 + db.exec(` + CREATE TABLE IF NOT EXISTS system_settings ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) + `); + + // 密码重置请求表 + db.exec(` + CREATE TABLE IF NOT EXISTS password_reset_requests ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + new_password TEXT NOT NULL, + status TEXT DEFAULT 'pending', -- pending, approved, rejected + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + reviewed_at DATETIME, + reviewed_by INTEGER, + + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE, + FOREIGN KEY (reviewed_by) REFERENCES users (id) + ) + `); + + // 创建索引 + db.exec(` + CREATE INDEX IF NOT EXISTS idx_users_username ON users(username); + CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); + CREATE INDEX IF NOT EXISTS idx_shares_code ON shares(share_code); + CREATE INDEX IF NOT EXISTS idx_shares_user ON shares(user_id); + CREATE INDEX IF NOT EXISTS idx_reset_requests_user ON password_reset_requests(user_id); + CREATE INDEX IF NOT EXISTS idx_reset_requests_status ON password_reset_requests(status); + `); + + // 数据库迁移:添加upload_api_key字段(如果不存在) + try { + const columns = db.prepare("PRAGMA table_info(users)").all(); + const hasUploadApiKey = columns.some(col => col.name === 'upload_api_key'); + + if (!hasUploadApiKey) { + db.exec(`ALTER TABLE users ADD COLUMN upload_api_key TEXT`); + console.log('数据库迁移:添加upload_api_key字段完成'); + } + } catch (error) { + console.error('数据库迁移失败:', error); + } + + // 数据库迁移:添加share_type字段(如果不存在) + try { + const shareColumns = db.prepare("PRAGMA table_info(shares)").all(); + const hasShareType = shareColumns.some(col => col.name === 'share_type'); + + if (!hasShareType) { + db.exec(`ALTER TABLE shares ADD COLUMN share_type TEXT DEFAULT 'file'`); + console.log('数据库迁移:添加share_type字段完成'); + } + } catch (error) { + console.error('数据库迁移(share_type)失败:', error); + } + + console.log('数据库初始化完成'); +} + +// 创建默认管理员账号 +function createDefaultAdmin() { + const adminExists = db.prepare('SELECT id FROM users WHERE is_admin = 1').get(); + + if (!adminExists) { + const hashedPassword = bcrypt.hashSync('admin123', 10); + + db.prepare(` + INSERT INTO users ( + username, email, password, + is_admin, is_active, has_ftp_config + ) VALUES (?, ?, ?, ?, ?, ?) + `).run( + 'admin', + 'admin@example.com', + hashedPassword, + 1, + 1, + 0 // 管理员不需要FTP配置 + ); + + console.log('默认管理员账号已创建'); + console.log('用户名: admin'); + console.log('密码: admin123'); + console.log('⚠️ 请登录后立即修改密码!'); + } +} + +// 用户相关操作 +const UserDB = { + // 创建用户 + create(userData) { + const hashedPassword = bcrypt.hashSync(userData.password, 10); + + const hasFtpConfig = userData.ftp_host && userData.ftp_user && userData.ftp_password ? 1 : 0; + + const stmt = db.prepare(` + INSERT INTO users ( + username, email, password, + ftp_host, ftp_port, ftp_user, ftp_password, http_download_base_url, + has_ftp_config + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + `); + + const result = stmt.run( + userData.username, + userData.email, + hashedPassword, + userData.ftp_host || null, + userData.ftp_port || 22, + userData.ftp_user || null, + userData.ftp_password || null, + userData.http_download_base_url || null, + hasFtpConfig + ); + + return result.lastInsertRowid; + }, + + // 根据用户名查找 + findByUsername(username) { + return db.prepare('SELECT * FROM users WHERE username = ?').get(username); + }, + + // 根据邮箱查找 + findByEmail(email) { + return db.prepare('SELECT * FROM users WHERE email = ?').get(email); + }, + + // 根据ID查找 + findById(id) { + return db.prepare('SELECT * FROM users WHERE id = ?').get(id); + }, + + // 验证密码 + verifyPassword(plainPassword, hashedPassword) { + return bcrypt.compareSync(plainPassword, hashedPassword); + }, + + // 更新用户 + update(id, updates) { + const fields = []; + const values = []; + + for (const [key, value] of Object.entries(updates)) { + if (key === 'password') { + fields.push(`${key} = ?`); + values.push(bcrypt.hashSync(value, 10)); + } else { + fields.push(`${key} = ?`); + values.push(value); + } + } + + fields.push('updated_at = CURRENT_TIMESTAMP'); + values.push(id); + + const stmt = db.prepare(`UPDATE users SET ${fields.join(', ')} WHERE id = ?`); + return stmt.run(...values); + }, + + // 获取所有用户 + getAll(filters = {}) { + let query = 'SELECT * FROM users WHERE 1=1'; + const params = []; + + if (filters.is_admin !== undefined) { + query += ' AND is_admin = ?'; + params.push(filters.is_admin); + } + + if (filters.is_banned !== undefined) { + query += ' AND is_banned = ?'; + params.push(filters.is_banned); + } + + query += ' ORDER BY created_at DESC'; + + return db.prepare(query).all(...params); + }, + + // 删除用户 + delete(id) { + return db.prepare('DELETE FROM users WHERE id = ?').run(id); + }, + + // 封禁/解封用户 + setBanStatus(id, isBanned) { + return db.prepare('UPDATE users SET is_banned = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?') + .run(isBanned ? 1 : 0, id); + } +}; + +// 分享链接相关操作 +const ShareDB = { + // 生成随机分享码 + generateShareCode(length = 8) { + const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + let code = ''; + for (let i = 0; i < length; i++) { + code += chars.charAt(Math.floor(Math.random() * chars.length)); + } + return code; + }, + + // 创建分享链接 + // 创建分享链接 + create(userId, options = {}) { + const { + share_type = 'file', + file_path = '', + file_name = '', + password = null, + expiry_days = null + } = options; + + let shareCode; + let attempts = 0; + + // 尝试生成唯一的分享码 + do { + shareCode = this.generateShareCode(); + attempts++; + if (attempts > 10) { + shareCode = this.generateShareCode(10); // 增加长度 + } + } while (this.findByCode(shareCode) && attempts < 20); + + // 计算过期时间 + let expiresAt = null; + if (expiry_days) { + const expireDate = new Date(); + expireDate.setDate(expireDate.getDate() + parseInt(expiry_days)); + expiresAt = expireDate.toISOString(); + } + + const stmt = db.prepare(` + INSERT INTO shares (user_id, share_code, share_path, share_type, share_password, expires_at) + VALUES (?, ?, ?, ?, ?, ?) + `); + + const hashedPassword = password ? bcrypt.hashSync(password, 10) : null; + const sharePath = share_type === 'file' ? file_path : '/'; + + const result = stmt.run( + userId, + shareCode, + sharePath, + share_type, + hashedPassword, + expiresAt + ); + + return { + id: result.lastInsertRowid, + share_code: shareCode, + share_type: share_type + }; + }, + + // 根据分享码查找 + findByCode(shareCode) { + return db.prepare(` + SELECT s.*, u.username, u.ftp_host, u.ftp_port, u.ftp_user, u.ftp_password, u.http_download_base_url + FROM shares s + JOIN users u ON s.user_id = u.id + WHERE s.share_code = ? + `).get(shareCode); + }, + + // 根据ID查找 + findById(id) { + return db.prepare('SELECT * FROM shares WHERE id = ?').get(id); + }, + + // 验证分享密码 + verifyPassword(plainPassword, hashedPassword) { + return bcrypt.compareSync(plainPassword, hashedPassword); + }, + + // 获取用户的所有分享 + getUserShares(userId) { + return db.prepare(` + SELECT * FROM shares + WHERE user_id = ? + ORDER BY created_at DESC + `).all(userId); + }, + + // 增加查看次数 + incrementViewCount(shareCode) { + return db.prepare(` + UPDATE shares + SET view_count = view_count + 1 + WHERE share_code = ? + `).run(shareCode); + }, + + // 增加下载次数 + incrementDownloadCount(shareCode) { + return db.prepare(` + UPDATE shares + SET download_count = download_count + 1 + WHERE share_code = ? + `).run(shareCode); + }, + + // 删除分享 + delete(id, userId = null) { + if (userId) { + return db.prepare('DELETE FROM shares WHERE id = ? AND user_id = ?').run(id, userId); + } + return db.prepare('DELETE FROM shares WHERE id = ?').run(id); + }, + + // 获取所有分享(管理员) + getAll() { + return db.prepare(` + SELECT s.*, u.username + FROM shares s + JOIN users u ON s.user_id = u.id + ORDER BY s.created_at DESC + `).all(); + } +}; + +// 系统设置管理 +const SettingsDB = { + // 获取设置 + get(key) { + const row = db.prepare('SELECT value FROM system_settings WHERE key = ?').get(key); + return row ? row.value : null; + }, + + // 设置值 + set(key, value) { + db.prepare(` + INSERT INTO system_settings (key, value, updated_at) + VALUES (?, ?, CURRENT_TIMESTAMP) + ON CONFLICT(key) DO UPDATE SET + value = excluded.value, + updated_at = CURRENT_TIMESTAMP + `).run(key, value); + }, + + // 获取所有设置 + getAll() { + return db.prepare('SELECT key, value FROM system_settings').all(); + } +}; + +// 密码重置请求管理 +const PasswordResetDB = { + // 创建密码重置请求 + create(userId, newPassword) { + const hashedPassword = bcrypt.hashSync(newPassword, 10); + + // 删除该用户之前的pending请求 + db.prepare('DELETE FROM password_reset_requests WHERE user_id = ? AND status = ?') + .run(userId, 'pending'); + + const stmt = db.prepare(` + INSERT INTO password_reset_requests (user_id, new_password, status) + VALUES (?, ?, 'pending') + `); + + const result = stmt.run(userId, hashedPassword); + return result.lastInsertRowid; + }, + + // 获取待审核的请求 + getPending() { + return db.prepare(` + SELECT r.*, u.username, u.email + FROM password_reset_requests r + JOIN users u ON r.user_id = u.id + WHERE r.status = 'pending' + ORDER BY r.created_at DESC + `).all(); + }, + + // 审核请求(批准或拒绝) + review(requestId, adminId, approved) { + const request = db.prepare('SELECT * FROM password_reset_requests WHERE id = ?').get(requestId); + + if (!request || request.status !== 'pending') { + throw new Error('请求不存在或已被处理'); + } + + const newStatus = approved ? 'approved' : 'rejected'; + + db.prepare(` + UPDATE password_reset_requests + SET status = ?, reviewed_at = CURRENT_TIMESTAMP, reviewed_by = ? + WHERE id = ? + `).run(newStatus, adminId, requestId); + + // 如果批准,更新用户密码 + if (approved) { + db.prepare('UPDATE users SET password = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?') + .run(request.new_password, request.user_id); + } + + return true; + }, + + // 获取用户的所有请求 + getUserRequests(userId) { + return db.prepare(` + SELECT * FROM password_reset_requests + WHERE user_id = ? + ORDER BY created_at DESC + `).all(userId); + }, + + // 检查用户是否有待处理的请求 + hasPendingRequest(userId) { + const request = db.prepare(` + SELECT id FROM password_reset_requests + WHERE user_id = ? AND status = 'pending' + `).get(userId); + return !!request; + } +}; + +// 初始化默认设置 +function initDefaultSettings() { + // 默认上传限制为100MB + if (!SettingsDB.get('max_upload_size')) { + SettingsDB.set('max_upload_size', '104857600'); // 100MB in bytes + } +} + +// 数据库版本迁移 - v2.0 本地存储功能 +function migrateToV2() { + try { + const columns = db.prepare("PRAGMA table_info(users)").all(); + const hasStoragePermission = columns.some(col => col.name === 'storage_permission'); + + if (!hasStoragePermission) { + console.log('[数据库迁移] 检测到旧版本数据库,开始升级到 v2.0...'); + + // 添加本地存储相关字段 + db.exec(` + ALTER TABLE users ADD COLUMN storage_permission TEXT DEFAULT 'sftp_only'; + ALTER TABLE users ADD COLUMN current_storage_type TEXT DEFAULT 'sftp'; + ALTER TABLE users ADD COLUMN local_storage_quota INTEGER DEFAULT 1073741824; + ALTER TABLE users ADD COLUMN local_storage_used INTEGER DEFAULT 0; + `); + + // 更新现有用户为SFTP模式(保持兼容) + const updateStmt = db.prepare("UPDATE users SET current_storage_type = 'sftp' WHERE has_ftp_config = 1"); + updateStmt.run(); + + console.log('[数据库迁移] ✓ 用户表已升级'); + + // 为分享表添加存储类型字段 + const shareColumns = db.prepare("PRAGMA table_info(shares)").all(); + const hasShareStorageType = shareColumns.some(col => col.name === 'storage_type'); + + if (!hasShareStorageType) { + db.exec(`ALTER TABLE shares ADD COLUMN storage_type TEXT DEFAULT 'sftp';`); + console.log('[数据库迁移] ✓ 分享表已升级'); + } + + console.log('[数据库迁移] ✅ 数据库升级到 v2.0 完成!本地存储功能已启用'); + } + } catch (error) { + console.error('[数据库迁移] 迁移失败:', error); + throw error; + } +} + +// 初始化数据库 +initDatabase(); +createDefaultAdmin(); +initDefaultSettings(); +migrateToV2(); // 执行数据库迁移 + +module.exports = { + db, + UserDB, + ShareDB, + SettingsDB, + PasswordResetDB +}; diff --git a/backend/package-lock.json b/backend/package-lock.json new file mode 100644 index 0000000..b78fa46 --- /dev/null +++ b/backend/package-lock.json @@ -0,0 +1,3010 @@ +{ + "name": "ftp-web-manager-backend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ftp-web-manager-backend", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "archiver": "^7.0.1", + "basic-ftp": "^5.0.4", + "bcryptjs": "^3.0.3", + "better-sqlite3": "^12.4.1", + "cookie-parser": "^1.4.7", + "cors": "^2.8.5", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "express-validator": "^7.3.0", + "jsonwebtoken": "^9.0.2", + "multer": "^2.0.2", + "ssh2-sftp-client": "^12.0.1" + }, + "devDependencies": { + "nodemon": "^3.0.1" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/archiver/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/archiver/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/archiver/node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/b4a": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", + "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bcryptjs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", + "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, + "node_modules/better-sqlite3": { + "version": "12.4.1", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.4.1.tgz", + "integrity": "sha512-3yVdyZhklTiNrtg+4WqHpJpFDd+WHTg2oM7UcR80GqL05AOV0xEJzc6qNvFYoEtE+hRp1n9MpN6/+4yhlGkDXQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/buildcheck": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz", + "integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==", + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/compress-commons/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cpu-features": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", + "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "buildcheck": "~0.0.6", + "nan": "^2.19.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/crc32-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-validator": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.3.0.tgz", + "integrity": "sha512-ujK2BX5JUun5NR4JuBo83YSXoDDIpoGz3QxgHTzQcHFevkKnwV1in4K7YNuuXQ1W3a2ObXB/P4OTnTZpUyGWiw==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21", + "validator": "~13.15.15" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/nan": { + "version": "2.23.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.1.tgz", + "integrity": "sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==", + "license": "MIT", + "optional": true + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.80.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.80.0.tgz", + "integrity": "sha512-LyPuZJcI9HVwzXK1GPxWNzrr+vr8Hp/3UqlmWxxh8p54U1ZbclOqbSog9lWHaCX+dBaiGi6n/hIX+mKu74GmPA==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nodemon": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", + "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ssh2": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.17.0.tgz", + "integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==", + "hasInstallScript": true, + "dependencies": { + "asn1": "^0.2.6", + "bcrypt-pbkdf": "^1.0.2" + }, + "engines": { + "node": ">=10.16.0" + }, + "optionalDependencies": { + "cpu-features": "~0.0.10", + "nan": "^2.23.0" + } + }, + "node_modules/ssh2-sftp-client": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/ssh2-sftp-client/-/ssh2-sftp-client-12.0.1.tgz", + "integrity": "sha512-ICJ1L2PmBel2Q2ctbyxzTFZCPKSHYYD6s2TFZv7NXmZDrDNGk8lHBb/SK2WgXLMXNANH78qoumeJzxlWZqSqWg==", + "license": "Apache-2.0", + "dependencies": { + "concat-stream": "^2.0.0", + "ssh2": "^1.16.0" + }, + "engines": { + "node": ">=18.20.4" + }, + "funding": { + "type": "individual", + "url": "https://square.link/u/4g7sPflL" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "license": "Unlicense" + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/validator": { + "version": "13.15.20", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz", + "integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/zip-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + } + } +} diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..cbf0b1f --- /dev/null +++ b/backend/package.json @@ -0,0 +1,34 @@ +{ + "name": "ftp-web-manager-backend", + "version": "1.0.0", + "description": "FTP Web Manager Backend", + "main": "server.js", + "scripts": { + "start": "node server.js", + "dev": "nodemon server.js" + }, + "keywords": [ + "ftp", + "web", + "file-manager" + ], + "author": "", + "license": "MIT", + "dependencies": { + "archiver": "^7.0.1", + "basic-ftp": "^5.0.4", + "bcryptjs": "^3.0.3", + "better-sqlite3": "^12.4.1", + "cookie-parser": "^1.4.7", + "cors": "^2.8.5", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "express-validator": "^7.3.0", + "jsonwebtoken": "^9.0.2", + "multer": "^2.0.2", + "ssh2-sftp-client": "^12.0.1" + }, + "devDependencies": { + "nodemon": "^3.0.1" + } +} diff --git a/backend/server.js b/backend/server.js new file mode 100644 index 0000000..5296440 --- /dev/null +++ b/backend/server.js @@ -0,0 +1,2113 @@ +const express = require('express'); +const cors = require('cors'); +const cookieParser = require('cookie-parser'); +const SftpClient = require('ssh2-sftp-client'); +const multer = require('multer'); +const path = require('path'); +const fs = require('fs'); +const { body, validationResult } = require('express-validator'); +const archiver = require('archiver'); +const { execSync } = require('child_process'); + +const { db, UserDB, ShareDB, SettingsDB, PasswordResetDB } = require('./database'); +const { generateToken, authMiddleware, adminMiddleware } = require('./auth'); + +const app = express(); +const PORT = process.env.PORT || 40001; + +// 中间件 +app.use(cors({ credentials: true, origin: true })); +app.use(express.json()); +app.use(cookieParser()); + +// 请求日志 +app.use((req, res, next) => { + console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`); + next(); +}); + +// 文件上传配置(临时存储) +const upload = multer({ + dest: path.join(__dirname, 'uploads'), + limits: { fileSize: 5 * 1024 * 1024 * 1024 } // 5GB限制 +}); + +// 分享文件信息缓存(内存缓存) +// 格式: Map +const shareFileCache = new Map(); + +// ===== 工具函数 ===== + +// SFTP连接 +async function connectToSFTP(config) { + const sftp = new SftpClient(); + await sftp.connect({ + host: config.ftp_host, + port: config.ftp_port || 22, + username: config.ftp_user, + password: config.ftp_password + }); + return sftp; +} + +// 格式化文件大小 +function formatFileSize(bytes) { + if (bytes === 0) return '0 B'; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]; +} + +// ===== 公开API ===== + +// 健康检查 +app.get('/api/health', (req, res) => { + res.json({ success: true, message: 'Server is running' }); +}); + +// 用户注册(简化版) +app.post('/api/register', + [ + body('username').isLength({ min: 3, max: 20 }).withMessage('用户名长度3-20个字符'), + body('email').optional({ checkFalsy: true }).isEmail().withMessage('邮箱格式不正确'), + body('password').isLength({ min: 6 }).withMessage('密码至少6个字符') + ], + async (req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + errors: errors.array() + }); + } + + try { + const { username, email, password } = req.body; + + // 检查用户名是否存在 + if (UserDB.findByUsername(username)) { + return res.status(400).json({ + success: false, + message: '用户名已存在' + }); + } + + // 如果提供了邮箱,检查邮箱是否存在 + if (email && UserDB.findByEmail(email)) { + return res.status(400).json({ + success: false, + message: '邮箱已被使用' + }); + } + + // 创建用户(不需要FTP配置) + const userId = UserDB.create({ + username, + email: email || `${username}@localhost`, // 如果没提供邮箱,使用默认值 + password + }); + + res.json({ + success: true, + message: '注册成功', + user_id: userId + }); + } catch (error) { + console.error('注册失败:', error); + res.status(500).json({ + success: false, + message: '注册失败: ' + error.message + }); + } + } +); + +// 用户登录 +app.post('/api/login', + [ + body('username').notEmpty().withMessage('用户名不能为空'), + body('password').notEmpty().withMessage('密码不能为空') + ], + (req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + errors: errors.array() + }); + } + + const { username, password } = req.body; + + try { + const user = UserDB.findByUsername(username); + + if (!user) { + return res.status(401).json({ + success: false, + message: '用户名或密码错误' + }); + } + + if (user.is_banned) { + return res.status(403).json({ + success: false, + message: '账号已被封禁' + }); + } + + if (!UserDB.verifyPassword(password, user.password)) { + return res.status(401).json({ + success: false, + message: '用户名或密码错误' + }); + } + + const token = generateToken(user); + + res.cookie('token', token, { + httpOnly: true, + maxAge: 7 * 24 * 60 * 60 * 1000 // 7天 + }); + + res.json({ + success: true, + message: '登录成功', + token, + user: { + id: user.id, + username: user.username, + email: user.email, + is_admin: user.is_admin, + has_ftp_config: user.has_ftp_config, + // 存储相关字段 + storage_permission: user.storage_permission || 'sftp_only', + current_storage_type: user.current_storage_type || 'sftp', + local_storage_quota: user.local_storage_quota || 1073741824, + local_storage_used: user.local_storage_used || 0 + } + }); + } catch (error) { + console.error('登录失败:', error); + res.status(500).json({ + success: false, + message: '登录失败: ' + error.message + }); + } + } +); + +// ===== 需要认证的API ===== + +// 获取当前用户信息 +app.get('/api/user/profile', authMiddleware, (req, res) => { + // 不返回密码明文 + const { ftp_password, password, ...safeUser } = req.user; + res.json({ + success: true, + user: safeUser + }); +}); + +// 更新FTP配置 +app.post('/api/user/update-ftp', + authMiddleware, + [ + body('ftp_host').notEmpty().withMessage('FTP主机不能为空'), + body('ftp_port').isInt({ min: 1, max: 65535 }).withMessage('FTP端口范围1-65535'), + body('ftp_user').notEmpty().withMessage('FTP用户名不能为空') + ], + async (req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + errors: errors.array() + }); + } + + try { + const { ftp_host, ftp_port, ftp_user, ftp_password, http_download_base_url } = req.body; + // 调试日志:查看接收到的配置 + console.log("[DEBUG] 收到SFTP配置:", { + ftp_host, + ftp_port, + ftp_user, + ftp_password: ftp_password ? "***" : "(empty)", + http_download_base_url + }); + + + // 如果用户已配置FTP且密码为空,使用现有密码 + let actualPassword = ftp_password; + if (!ftp_password && req.user.has_ftp_config && req.user.ftp_password) { + actualPassword = req.user.ftp_password; + } else if (!ftp_password) { + return res.status(400).json({ + success: false, + message: 'FTP密码不能为空' + }); + } + + // 验证FTP连接 + try { + const sftp = await connectToSFTP({ ftp_host, ftp_port, ftp_user, ftp_password: actualPassword }); + await sftp.end(); + } catch (error) { + return res.status(400).json({ + success: false, + message: 'SFTP连接失败,请检查配置: ' + error.message + }); + } + + // 更新用户配置 + UserDB.update(req.user.id, { + ftp_host, + ftp_port, + ftp_user, + ftp_password: actualPassword, + http_download_base_url: http_download_base_url || null, + has_ftp_config: 1 + }); + + res.json({ + success: true, + message: 'SFTP配置已更新' + }); + } catch (error) { + console.error('更新配置失败:', error); + res.status(500).json({ + success: false, + message: '更新配置失败: ' + error.message + }); + } + } +); + +// 修改管理员账号信息(仅管理员可修改用户名) +app.post('/api/admin/update-profile', + authMiddleware, + adminMiddleware, + [ + body('username').isLength({ min: 3, max: 20 }).withMessage('用户名长度3-20个字符') + ], + async (req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + errors: errors.array() + }); + } + + try { + const { username } = req.body; + + // 检查用户名是否被占用(排除自己) + if (username !== req.user.username) { + const existingUser = UserDB.findByUsername(username); + if (existingUser && existingUser.id !== req.user.id) { + return res.status(400).json({ + success: false, + message: '用户名已被使用' + }); + } + + // 更新用户名 + UserDB.update(req.user.id, { username }); + + // 获取更新后的用户信息 + const updatedUser = UserDB.findById(req.user.id); + + // 生成新的token(因为用户名变了) + const newToken = generateToken(updatedUser); + + res.json({ + success: true, + message: '用户名已更新', + token: newToken, + user: { + id: updatedUser.id, + username: updatedUser.username, + email: updatedUser.email, + is_admin: updatedUser.is_admin + } + }); + } else { + res.json({ + success: true, + message: '没有需要更新的信息' + }); + } + } catch (error) { + console.error('更新账号信息失败:', error); + res.status(500).json({ + success: false, + message: '更新失败: ' + error.message + }); + } + } +); + +// 修改当前用户密码(管理员直接修改,不需要验证当前密码) +app.post('/api/user/change-password', + authMiddleware, + [ + body('new_password').isLength({ min: 6 }).withMessage('新密码至少6个字符') + ], + (req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + errors: errors.array() + }); + } + + try { + const { new_password } = req.body; + + // 直接更新密码,不需要验证当前密码 + UserDB.update(req.user.id, { password: new_password }); + + res.json({ + success: true, + message: '密码修改成功' + }); + } catch (error) { + console.error('修改密码失败:', error); + res.status(500).json({ + success: false, + message: '修改密码失败: ' + error.message + }); + } + } +); + +// 修改当前用户名 +app.post('/api/user/update-username', + authMiddleware, + [ + body('username').isLength({ min: 3 }).withMessage('用户名至少3个字符') + ], + (req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + errors: errors.array() + }); + } + + try { + const { username } = req.body; + + // 检查用户名是否已存在 + const existingUser = UserDB.findByUsername(username); + if (existingUser && existingUser.id !== req.user.id) { + return res.status(400).json({ + success: false, + message: '用户名已存在' + }); + } + + // 更新用户名 + UserDB.update(req.user.id, { username }); + + res.json({ + success: true, + message: '用户名修改成功' + }); + } catch (error) { + console.error('修改用户名失败:', error); + res.status(500).json({ + success: false, + message: '修改用户名失败: ' + error.message + }); + } + } +); + +// 切换存储方式 +app.post('/api/user/switch-storage', + authMiddleware, + [ + body('storage_type').isIn(['local', 'sftp']).withMessage('无效的存储类型') + ], + async (req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + errors: errors.array() + }); + } + + try { + const { storage_type } = req.body; + + // 检查权限 + if (req.user.storage_permission === 'local_only' && storage_type !== 'local') { + return res.status(403).json({ + success: false, + message: '您只能使用本地存储' + }); + } + + if (req.user.storage_permission === 'sftp_only' && storage_type !== 'sftp') { + return res.status(403).json({ + success: false, + message: '您只能使用SFTP存储' + }); + } + + // 检查SFTP配置 + if (storage_type === 'sftp' && !req.user.has_ftp_config) { + return res.status(400).json({ + success: false, + message: '请先配置SFTP服务器' + }); + } + + // 更新存储类型 + UserDB.update(req.user.id, { current_storage_type: storage_type }); + + res.json({ + success: true, + message: '存储方式已切换', + storage_type + }); + } catch (error) { + console.error('切换存储失败:', error); + res.status(500).json({ + success: false, + message: '切换存储失败: ' + error.message + }); + } + } +); + +// 获取文件列表 +app.get('/api/files', authMiddleware, async (req, res) => { + const dirPath = req.query.path || '/'; + let storage; + + try { + // 使用统一存储接口 + const { StorageInterface } = require('./storage'); + const storageInterface = new StorageInterface(req.user); + storage = await storageInterface.connect(); + + const list = await storage.list(dirPath); + + const httpBaseUrl = req.user.http_download_base_url || ''; + const storageType = req.user.current_storage_type || 'sftp'; + + const formattedList = list.map(item => { + // 构建完整的文件路径用于下载 + let httpDownloadUrl = null; + // 只有SFTP存储且配置了HTTP下载地址时才提供HTTP下载URL + if (storageType === 'sftp' && httpBaseUrl && item.type !== 'd') { + // 移除基础URL末尾的斜杠(如果有) + const baseUrl = httpBaseUrl.replace(/\/+$/, ''); + + // 构建完整路径:当前目录路径 + 文件名 + const fullPath = dirPath === '/' + ? `/${item.name}` + : `${dirPath}/${item.name}`; + + // 拼接最终的下载URL + httpDownloadUrl = `${baseUrl}${fullPath}`; + } + + return { + name: item.name, + type: item.type === 'd' ? 'directory' : 'file', + size: item.size, + sizeFormatted: formatFileSize(item.size), + modifiedAt: new Date(item.modifyTime), + isDirectory: item.type === 'd', + httpDownloadUrl: httpDownloadUrl + }; + }); + + formattedList.sort((a, b) => { + if (a.isDirectory && !b.isDirectory) return -1; + if (!a.isDirectory && b.isDirectory) return 1; + return a.name.localeCompare(b.name); + }); + + res.json({ + success: true, + path: dirPath, + items: formattedList, + storageType: storageType, + storagePermission: req.user.storage_permission || 'sftp_only' + }); + } catch (error) { + console.error('获取文件列表失败:', error); + res.status(500).json({ + success: false, + message: '获取文件列表失败: ' + error.message + }); + } finally { + if (storage) await storage.end(); + } +}); + +// 重命名文件 +app.post('/api/files/rename', authMiddleware, async (req, res) => { + const { oldName, newName, path } = req.body; + let storage; + + if (!oldName || !newName) { + return res.status(400).json({ + success: false, + message: '缺少文件名参数' + }); + } + + try { + // 使用统一存储接口 + const { StorageInterface } = require('./storage'); + const storageInterface = new StorageInterface(req.user); + storage = await storageInterface.connect(); + + const oldPath = path === '/' ? `/${oldName}` : `${path}/${oldName}`; + const newPath = path === '/' ? `/${newName}` : `${path}/${newName}`; + + await storage.rename(oldPath, newPath); + + res.json({ + success: true, + message: '文件重命名成功' + }); + } catch (error) { + console.error('重命名文件失败:', error); + res.status(500).json({ + success: false, + message: '重命名文件失败: ' + error.message + }); + } finally { + if (storage) await storage.end(); + } +}); + +// 删除文件 +app.post('/api/files/delete', authMiddleware, async (req, res) => { + const { fileName, path } = req.body; + let storage; + + if (!fileName) { + return res.status(400).json({ + success: false, + message: '缺少文件名参数' + }); + } + + try { + // 使用统一存储接口 + const { StorageInterface } = require('./storage'); + const storageInterface = new StorageInterface(req.user); + storage = await storageInterface.connect(); + + const filePath = path === '/' ? `/${fileName}` : `${path}/${fileName}`; + + await storage.delete(filePath); + + res.json({ + success: true, + message: '文件删除成功' + }); + } catch (error) { + console.error('删除文件失败:', error); + res.status(500).json({ + success: false, + message: '删除文件失败: ' + error.message + }); + } finally { + if (storage) await storage.end(); + } +}); + +// 上传文件 +app.post('/api/upload', authMiddleware, upload.single('file'), async (req, res) => { + if (!req.file) { + return res.status(400).json({ + success: false, + message: '没有上传文件' + }); + } + + // 检查文件大小限制 + const maxUploadSize = parseInt(SettingsDB.get('max_upload_size') || '104857600'); + if (req.file.size > maxUploadSize) { + // 删除已上传的临时文件 + if (fs.existsSync(req.file.path)) { + fs.unlinkSync(req.file.path); + } + + return res.status(413).json({ + success: false, + message: '文件超过上传限制', + maxSize: maxUploadSize, + fileSize: req.file.size + }); + } + + const remotePath = req.body.path || '/'; + const remoteFilePath = remotePath === '/' + ? `/${req.file.originalname}` + : `${remotePath}/${req.file.originalname}`; + + let storage; + + try { + // 使用统一存储接口 + const { StorageInterface } = require('./storage'); + const storageInterface = new StorageInterface(req.user); + storage = await storageInterface.connect(); + + // storage.put() 内部已经实现了临时文件+重命名逻辑 + await storage.put(req.file.path, remoteFilePath); + console.log(`[上传] 文件上传成功: ${remoteFilePath}`); + + // 删除本地临时文件 + fs.unlinkSync(req.file.path); + + res.json({ + success: true, + message: '文件上传成功', + filename: req.file.originalname, + path: remoteFilePath + }); + } catch (error) { + console.error('文件上传失败:', error); + + // 删除临时文件 + if (fs.existsSync(req.file.path)) { + fs.unlinkSync(req.file.path); + } + + res.status(500).json({ + success: false, + message: '文件上传失败: ' + error.message + }); + } finally { + if (storage) await storage.end(); + } +}); + + +// 下载文件 +app.get('/api/files/download', authMiddleware, async (req, res) => { + const filePath = req.query.path; + let storage; + + if (!filePath) { + return res.status(400).json({ + success: false, + message: '缺少文件路径参数' + }); + } + + try { + // 使用统一存储接口 + const { StorageInterface } = require('./storage'); + const storageInterface = new StorageInterface(req.user); + storage = await storageInterface.connect(); + + // 获取文件名 + const fileName = filePath.split('/').pop(); + + // 先获取文件信息(获取文件大小) + const fileStats = await storage.stat(filePath); + const fileSize = fileStats.size; + console.log('[下载] 文件: ' + fileName + ', 大小: ' + fileSize + ' 字节'); + + // 设置响应头(包含文件大小,浏览器可显示下载进度) + res.setHeader('Content-Type', 'application/octet-stream'); + res.setHeader('Content-Length', fileSize); + res.setHeader('Content-Disposition', 'attachment; filename="' + encodeURIComponent(fileName) + '"; filename*=UTF-8\'\'' + encodeURIComponent(fileName)); + + // 创建文件流并传输(流式下载,服务器不保存临时文件) + const stream = storage.createReadStream(filePath); + + stream.on('error', (error) => { + console.error('文件流错误:', error); + if (!res.headersSent) { + res.status(500).json({ + success: false, + message: '文件下载失败: ' + error.message + }); + } + }); + + stream.pipe(res); + + } catch (error) { + console.error('下载文件失败:', error); + if (!res.headersSent) { + res.status(500).json({ + success: false, + message: '下载文件失败: ' + error.message + }); + } + } +}); + +// 生成上传工具(生成新密钥并创建配置文件) +app.post('/api/upload/generate-tool', authMiddleware, async (req, res) => { + try { + // 生成新的API密钥(32位随机字符串) + const crypto = require('crypto'); + const newApiKey = crypto.randomBytes(16).toString('hex'); + + // 更新用户的upload_api_key + UserDB.update(req.user.id, { upload_api_key: newApiKey }); + + // 创建配置文件内容 + const config = { + username: req.user.username, + api_key: newApiKey, + api_base_url: `${req.get('x-forwarded-proto') || req.protocol}://${req.get('host')}` + }; + + res.json({ + success: true, + message: '上传工具已生成', + config: config + }); + } catch (error) { + console.error('生成上传工具失败:', error); + res.status(500).json({ + success: false, + message: '生成上传工具失败: ' + error.message + }); + } +}); + +// 下载上传工具(zip包含exe+config.json+README.txt) +app.get('/api/upload/download-tool', authMiddleware, async (req, res) => { + let tempZipPath = null; + + try { + console.log(`[上传工具] 用户 ${req.user.username} 请求下载上传工具`); + + // 生成新的API密钥 + const crypto = require('crypto'); + const newApiKey = crypto.randomBytes(16).toString('hex'); + + // 更新用户的upload_api_key + UserDB.update(req.user.id, { upload_api_key: newApiKey }); + + // 创建配置文件内容 + const config = { + username: req.user.username, + api_key: newApiKey, + api_base_url: `${req.get('x-forwarded-proto') || req.protocol}://${req.get('host')}` + }; + console.log("[上传工具配置]", JSON.stringify(config, null, 2)); + + // 检查exe文件是否存在 + const toolDir = path.join(__dirname, '..', 'upload-tool'); + const exePath = path.join(toolDir, 'dist', '玩玩云上传工具.exe'); + const readmePath = path.join(toolDir, 'README.txt'); + + if (!fs.existsSync(exePath)) { + console.error('[上传工具] exe文件不存在:', exePath); + return res.status(500).json({ + success: false, + message: '上传工具尚未打包,请联系管理员运行 upload-tool/build.bat' + }); + } + + // 创建临时zip文件路径 + const uploadsDir = path.join(__dirname, 'uploads'); + if (!fs.existsSync(uploadsDir)) { + fs.mkdirSync(uploadsDir, { recursive: true }); + } + tempZipPath = path.join(uploadsDir, `tool_${req.user.username}_${Date.now()}.zip`); + + console.log('[上传工具] 开始创建zip包到临时文件:', tempZipPath); + + // 创建文件写入流 + const output = fs.createWriteStream(tempZipPath); + const archive = archiver('zip', { + store: true // 使用STORE模式,不压缩,速度最快 + }); + + // 等待zip文件创建完成 + await new Promise((resolve, reject) => { + output.on('close', () => { + console.log(`[上传工具] zip创建完成,大小: ${archive.pointer()} 字节`); + resolve(); + }); + + archive.on('error', (err) => { + console.error('[上传工具] archiver错误:', err); + reject(err); + }); + + // 连接archive到文件流 + archive.pipe(output); + + // 添加exe文件 + console.log('[上传工具] 添加exe文件...'); + archive.file(exePath, { name: '玩玩云上传工具.exe' }); + + // 添加config.json + console.log('[上传工具] 添加config.json...'); + archive.append(JSON.stringify(config, null, 2), { name: 'config.json' }); + + // 添加README.txt + if (fs.existsSync(readmePath)) { + console.log('[上传工具] 添加README.txt...'); + archive.file(readmePath, { name: 'README.txt' }); + } + + // 完成打包 + console.log('[上传工具] 执行finalize...'); + archive.finalize(); + }); + + // 获取文件大小 + const stats = fs.statSync(tempZipPath); + const fileSize = stats.size; + console.log(`[上传工具] 准备发送文件,大小: ${fileSize} 字节`); + + // 设置响应头(包含Content-Length) + const filename = `玩玩云上传工具_${req.user.username}.zip`; + res.setHeader('Content-Type', 'application/zip'); + res.setHeader('Content-Length', fileSize); + res.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(filename)}"; filename*=UTF-8''${encodeURIComponent(filename)}`); + + // 创建文件读取流并发送 + const fileStream = fs.createReadStream(tempZipPath); + + fileStream.on('end', () => { + console.log(`[上传工具] 用户 ${req.user.username} 下载完成`); + // 删除临时文件 + if (tempZipPath && fs.existsSync(tempZipPath)) { + fs.unlinkSync(tempZipPath); + console.log('[上传工具] 临时文件已删除'); + } + }); + + fileStream.on('error', (err) => { + console.error('[上传工具] 文件流错误:', err); + // 删除临时文件 + if (tempZipPath && fs.existsSync(tempZipPath)) { + fs.unlinkSync(tempZipPath); + } + }); + + fileStream.pipe(res); + + } catch (error) { + console.error('[上传工具] 异常:', error); + + // 删除临时文件 + if (tempZipPath && fs.existsSync(tempZipPath)) { + fs.unlinkSync(tempZipPath); + console.log('[上传工具] 临时文件已删除(异常)'); + } + + if (!res.headersSent) { + res.status(500).json({ + success: false, + message: '下载失败: ' + error.message + }); + } + } +}); + +// 通过API密钥获取SFTP配置(供Python工具调用) +app.post('/api/upload/get-config', async (req, res) => { + try { + const { api_key } = req.body; + + if (!api_key) { + return res.status(400).json({ + success: false, + message: 'API密钥不能为空' + }); + } + + // 查找拥有此API密钥的用户 + const user = db.prepare('SELECT * FROM users WHERE upload_api_key = ?').get(api_key); + + if (!user) { + return res.status(401).json({ + success: false, + message: 'API密钥无效或已过期' + }); + } + + if (user.is_banned) { + return res.status(403).json({ + success: false, + message: '账号已被封禁' + }); + } + + if (!user.has_ftp_config) { + return res.status(400).json({ + success: false, + message: '用户未配置SFTP服务器' + }); + } + + // 返回SFTP配置 + res.json({ + success: true, + sftp_config: { + host: user.ftp_host, + port: user.ftp_port, + username: user.ftp_user, + password: user.ftp_password + } + }); + } catch (error) { + console.error('获取SFTP配置失败:', error); + res.status(500).json({ + success: false, + message: '获取SFTP配置失败: ' + error.message + }); + } +}); + +// 创建分享链接 +app.post('/api/share/create', authMiddleware, (req, res) => { + try { + const { share_type, file_path, file_name, password, expiry_days } = req.body; + console.log("[DEBUG] 创建分享请求:", { share_type, file_path, file_name, password: password ? "***" : null, expiry_days }); + + if (share_type === 'file' && !file_path) { + return res.status(400).json({ + success: false, + message: '文件路径不能为空' + }); + } + + const result = ShareDB.create(req.user.id, { + share_type: share_type || 'file', + file_path: file_path || '', + file_name: file_name || '', + password: password || null, + expiry_days: expiry_days || null + }); + + // 更新分享的存储类型 + const { db } = require('./database'); + db.prepare('UPDATE shares SET storage_type = ? WHERE id = ?') + .run(req.user.current_storage_type || 'sftp', result.id); + + const shareUrl = `${req.protocol}://${req.get('host')}/s/${result.share_code}`; + + res.json({ + success: true, + message: '分享链接创建成功', + share_code: result.share_code, + share_url: shareUrl, + share_type: result.share_type + }); + } catch (error) { + console.error('创建分享链接失败:', error); + res.status(500).json({ + success: false, + message: '创建分享链接失败: ' + error.message + }); + } +}); + +// 获取我的分享列表 +app.get('/api/share/my', authMiddleware, (req, res) => { + try { + const shares = ShareDB.getUserShares(req.user.id); + + res.json({ + success: true, + shares: shares.map(share => ({ + ...share, + share_url: `${req.protocol}://${req.get('host')}/s/${share.share_code}` + })) + }); + } catch (error) { + console.error('获取分享列表失败:', error); + res.status(500).json({ + success: false, + message: '获取分享列表失败: ' + error.message + }); + } +}); + +// 删除分享 +app.delete('/api/share/:id', authMiddleware, (req, res) => { + try { + // 先获取分享信息以获得share_code + const share = ShareDB.findById(req.params.id); + + if (share && share.user_id === req.user.id) { + // 删除缓存 + if (shareFileCache.has(share.share_code)) { + shareFileCache.delete(share.share_code); + console.log(`[缓存清除] 分享码: ${share.share_code}`); + } + + // 删除数据库记录 + ShareDB.delete(req.params.id, req.user.id); + + res.json({ + success: true, + message: '分享已删除' + }); + } else { + res.status(404).json({ + success: false, + message: '分享不存在或无权限' + }); + } + } catch (error) { + console.error('删除分享失败:', error); + res.status(500).json({ + success: false, + message: '删除分享失败: ' + error.message + }); + } +}); + +// ===== 分享链接访问(公开) ===== + +// 访问分享链接 - 验证密码(支持本地存储和SFTP) +app.post('/api/share/:code/verify', async (req, res) => { + const { code } = req.params; + const { password } = req.body; + let storage; + + try { + const share = ShareDB.findByCode(code); + + if (!share) { + return res.status(404).json({ + success: false, + message: '分享不存在' + }); + } + + // 如果设置了密码,验证密码 + if (share.share_password) { + if (!password) { + return res.status(401).json({ + success: false, + message: '需要密码', + needPassword: true + }); + } + + if (!ShareDB.verifyPassword(password, share.share_password)) { + return res.status(401).json({ + success: false, + message: '密码错误' + }); + } + } + + // 增加查看次数 + ShareDB.incrementViewCount(code); + + // 构建返回数据 + const responseData = { + success: true, + share: { + share_path: share.share_path, + share_type: share.share_type, + username: share.username, + created_at: share.created_at + } + }; + + // 如果是单文件分享,查询存储获取文件信息(带缓存) + if (share.share_type === 'file') { + const filePath = share.share_path; + const lastSlashIndex = filePath.lastIndexOf('/'); + const dirPath = lastSlashIndex > 0 ? filePath.substring(0, lastSlashIndex) : '/'; + const fileName = lastSlashIndex >= 0 ? filePath.substring(lastSlashIndex + 1) : filePath; + + // 检查缓存 + if (shareFileCache.has(code)) { + console.log(`[缓存命中] 分享码: ${code}`); + responseData.file = shareFileCache.get(code); + } else { + // 缓存未命中,查询存储 + const storageType = share.storage_type || 'sftp'; + console.log(`[缓存未命中] 分享码: ${code},存储类型: ${storageType}`); + try { + // 获取分享者的用户信息 + const shareOwner = UserDB.findById(share.user_id); + if (!shareOwner) { + throw new Error('分享者不存在'); + } + + // 使用统一存储接口 + const { StorageInterface } = require('./storage'); + const userForStorage = { + ...shareOwner, + current_storage_type: storageType + }; + + const storageInterface = new StorageInterface(userForStorage); + storage = await storageInterface.connect(); + + const list = await storage.list(dirPath); + const fileInfo = list.find(item => item.name === fileName); + + // 检查文件是否存在 + if (!fileInfo) { + shareFileCache.delete(code); + throw new Error("分享的文件已被删除或不存在"); + } + + if (fileInfo) { + // 移除基础URL末尾的斜杠 + const httpBaseUrl = share.http_download_base_url || ''; + const baseUrl = httpBaseUrl ? httpBaseUrl.replace(/\/+$/, '') : ''; + const normalizedFilePath = filePath.startsWith('/') ? filePath : `/${filePath}`; + // SFTP存储才提供HTTP下载URL,本地存储使用API下载 + const httpDownloadUrl = (storageType === 'sftp' && baseUrl) ? `${baseUrl}${normalizedFilePath}` : null; + + const fileData = { + name: fileName, + type: 'file', + isDirectory: false, + httpDownloadUrl: httpDownloadUrl, + size: fileInfo.size, + sizeFormatted: formatFileSize(fileInfo.size), + modifiedAt: new Date(fileInfo.modifyTime) + }; + + // 存入缓存 + shareFileCache.set(code, fileData); + console.log(`[缓存存储] 分享码: ${code},文件: ${fileName}`); + + responseData.file = fileData; + } + } catch (storageError) { + console.error('获取文件信息失败:', storageError); + + // 如果是文件不存在的错误,重新抛出 + if (storageError.message && storageError.message.includes("分享的文件已被删除或不存在")) { + throw storageError; + } + // 存储失败时仍返回基本信息,只是没有大小 + const httpBaseUrl = share.http_download_base_url || ''; + const baseUrl = httpBaseUrl ? httpBaseUrl.replace(/\/+$/, '') : ''; + const normalizedFilePath = filePath.startsWith('/') ? filePath : `/${filePath}`; + const storageType = share.storage_type || 'sftp'; + const httpDownloadUrl = (storageType === 'sftp' && baseUrl) ? `${baseUrl}${normalizedFilePath}` : null; + + responseData.file = { + name: fileName, + type: 'file', + isDirectory: false, + httpDownloadUrl: httpDownloadUrl, + size: 0, + sizeFormatted: '-' + }; + } + } + } + + res.json(responseData); + } catch (error) { + console.error('验证分享失败:', error); + res.status(500).json({ + success: false, + message: '验证失败: ' + error.message + }); + } finally { + if (storage) await storage.end(); + } +}); + +// 获取分享的文件列表(支持本地存储和SFTP) +app.post('/api/share/:code/list', async (req, res) => { + const { code } = req.params; + const { password, path: subPath } = req.body; + + let storage; + + try { + const share = ShareDB.findByCode(code); + + if (!share) { + return res.status(404).json({ + success: false, + message: '分享不存在' + }); + } + + // 验证密码 + if (share.share_password && !ShareDB.verifyPassword(password, share.share_password)) { + return res.status(401).json({ + success: false, + message: '密码错误' + }); + } + + // 获取分享者的用户信息 + const shareOwner = UserDB.findById(share.user_id); + if (!shareOwner) { + return res.status(404).json({ + success: false, + message: '分享者不存在' + }); + } + + // 使用统一存储接口,根据分享的storage_type选择存储后端 + const { StorageInterface } = require('./storage'); + const storageType = share.storage_type || 'sftp'; + console.log(`[分享列表] 存储类型: ${storageType}, 分享路径: ${share.share_path}`); + + // 临时构造用户对象以使用存储接口 + const userForStorage = { + ...shareOwner, + current_storage_type: storageType + }; + + const storageInterface = new StorageInterface(userForStorage); + storage = await storageInterface.connect(); + + const httpBaseUrl = share.http_download_base_url || ''; + let formattedList = []; + + // 如果是单文件分享 + if (share.share_type === 'file') { + // share_path 就是文件路径 + const filePath = share.share_path; + + // 提取父目录和文件名 + const lastSlashIndex = filePath.lastIndexOf('/'); + const dirPath = lastSlashIndex > 0 ? filePath.substring(0, lastSlashIndex) : '/'; + const fileName = lastSlashIndex >= 0 ? filePath.substring(lastSlashIndex + 1) : filePath; + + // 列出父目录 + const list = await storage.list(dirPath); + + // 只返回这个文件 + const fileInfo = list.find(item => item.name === fileName); + + if (fileInfo) { + // 移除基础URL末尾的斜杠 + const baseUrl = httpBaseUrl ? httpBaseUrl.replace(/\/+$/, '') : ''; + + // 确保文件路径以斜杠开头 + const normalizedFilePath = filePath.startsWith('/') ? filePath : `/${filePath}`; + + // SFTP存储才提供HTTP下载URL,本地存储使用API下载 + const httpDownloadUrl = (storageType === 'sftp' && baseUrl) ? `${baseUrl}${normalizedFilePath}` : null; + + formattedList = [{ + name: fileInfo.name, + type: 'file', + size: fileInfo.size, + sizeFormatted: formatFileSize(fileInfo.size), + modifiedAt: new Date(fileInfo.modifyTime), + isDirectory: false, + httpDownloadUrl: httpDownloadUrl + }]; + } + } + // 如果是目录分享(分享所有文件) + else { + const fullPath = subPath ? `${share.share_path}/${subPath}`.replace('//', '/') : share.share_path; + const list = await storage.list(fullPath); + + formattedList = list.map(item => { + // 构建完整的文件路径用于下载 + let httpDownloadUrl = null; + // SFTP存储才提供HTTP下载URL,本地存储使用API下载 + if (storageType === 'sftp' && httpBaseUrl && item.type !== 'd') { + // 移除基础URL末尾的斜杠 + const baseUrl = httpBaseUrl.replace(/\/+$/, ''); + + // 确保fullPath以斜杠开头 + const normalizedPath = fullPath.startsWith('/') ? fullPath : `/${fullPath}`; + + // 构建完整路径:当前目录路径 + 文件名 + const filePath = normalizedPath === '/' + ? `/${item.name}` + : `${normalizedPath}/${item.name}`; + + // 拼接最终的下载URL + httpDownloadUrl = `${baseUrl}${filePath}`; + } + + return { + name: item.name, + type: item.type === 'd' ? 'directory' : 'file', + size: item.size, + sizeFormatted: formatFileSize(item.size), + modifiedAt: new Date(item.modifyTime), + isDirectory: item.type === 'd', + httpDownloadUrl: httpDownloadUrl + }; + }); + + formattedList.sort((a, b) => { + if (a.isDirectory && !b.isDirectory) return -1; + if (!a.isDirectory && b.isDirectory) return 1; + return a.name.localeCompare(b.name); + }); + } + + res.json({ + success: true, + path: share.share_path, + items: formattedList + }); + } catch (error) { + console.error('获取分享文件列表失败:', error); + res.status(500).json({ + success: false, + message: '获取文件列表失败: ' + error.message + }); + } finally { + if (storage) await storage.end(); + } +}); + +// 记录下载次数 +app.post('/api/share/:code/download', (req, res) => { + const { code } = req.params; + + try { + const share = ShareDB.findByCode(code); + + if (!share) { + return res.status(404).json({ + success: false, + message: '分享不存在' + }); + } + + // 增加下载次数 + ShareDB.incrementDownloadCount(code); + + res.json({ + success: true, + message: '下载统计已记录' + }); + } catch (error) { + console.error('记录下载失败:', error); + res.status(500).json({ + success: false, + message: '记录下载失败: ' + error.message + }); + } +}); + +// 分享文件下载(支持本地存储和SFTP,公开API,需要分享码和密码验证) +app.get('/api/share/:code/download-file', async (req, res) => { + const { code } = req.params; + const { path: filePath, password } = req.query; + let storage; + + if (!filePath) { + return res.status(400).json({ + success: false, + message: '缺少文件路径参数' + }); + } + + try { + const share = ShareDB.findByCode(code); + + if (!share) { + return res.status(404).json({ + success: false, + message: '分享不存在' + }); + } + + // 验证密码(如果需要) + if (share.share_password) { + if (!password || !ShareDB.verifyPassword(password, share.share_password)) { + return res.status(401).json({ + success: false, + message: '密码错误或未提供密码' + }); + } + } + + // 获取分享者的用户信息 + const shareOwner = UserDB.findById(share.user_id); + if (!shareOwner) { + return res.status(404).json({ + success: false, + message: '分享者不存在' + }); + } + + // 使用统一存储接口,根据分享的storage_type选择存储后端 + const { StorageInterface } = require('./storage'); + const storageType = share.storage_type || 'sftp'; + console.log(`[分享下载] 存储类型: ${storageType}, 文件路径: ${filePath}`); + + // 临时构造用户对象以使用存储接口 + const userForStorage = { + ...shareOwner, + current_storage_type: storageType + }; + + const storageInterface = new StorageInterface(userForStorage); + storage = await storageInterface.connect(); + + // 获取文件名 + const fileName = filePath.split('/').pop(); + + // 获取文件信息(获取文件大小) + const fileStats = await storage.stat(filePath); + const fileSize = fileStats.size; + console.log(`[分享下载] 文件: ${fileName}, 大小: ${fileSize} 字节`); + + // 增加下载次数 + ShareDB.incrementDownloadCount(code); + + // 设置响应头(包含文件大小,浏览器可显示下载进度) + res.setHeader('Content-Type', 'application/octet-stream'); + res.setHeader('Content-Length', fileSize); + res.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(fileName)}"; filename*=UTF-8''${encodeURIComponent(fileName)}`); + + // 创建文件流并传输(流式下载,服务器不保存临时文件) + const stream = storage.createReadStream(filePath); + + stream.on('error', (error) => { + console.error('文件流错误:', error); + if (!res.headersSent) { + res.status(500).json({ + success: false, + message: '文件下载失败: ' + error.message + }); + } + // 发生错误时关闭存储连接 + if (storage) { + storage.end().catch(err => console.error('关闭存储连接失败:', err)); + } + }); + + // 在传输完成后关闭存储连接 + stream.on('close', () => { + console.log('[分享下载] 文件传输完成,关闭存储连接'); + if (storage) { + storage.end().catch(err => console.error('关闭存储连接失败:', err)); + } + }); + + stream.pipe(res); + + } catch (error) { + console.error('分享下载文件失败:', error); + if (!res.headersSent) { + res.status(500).json({ + success: false, + message: '下载文件失败: ' + error.message + }); + } + // 如果发生错误,关闭存储连接 + if (storage) { + storage.end().catch(err => console.error('关闭存储连接失败:', err)); + } + } +}); + +// ===== 管理员API ===== + +// 获取系统设置 +app.get('/api/admin/settings', authMiddleware, adminMiddleware, (req, res) => { + try { + const maxUploadSize = parseInt(SettingsDB.get('max_upload_size') || '104857600'); + + res.json({ + success: true, + settings: { + max_upload_size: maxUploadSize + } + }); + } catch (error) { + console.error('获取系统设置失败:', error); + res.status(500).json({ + success: false, + message: '获取系统设置失败: ' + error.message + }); + } +}); + +// 更新系统设置 +app.post('/api/admin/settings', authMiddleware, adminMiddleware, (req, res) => { + try { + const { max_upload_size } = req.body; + + if (max_upload_size !== undefined) { + const size = parseInt(max_upload_size); + if (isNaN(size) || size < 0) { + return res.status(400).json({ + success: false, + message: '无效的文件大小' + }); + } + SettingsDB.set('max_upload_size', size.toString()); + } + + res.json({ + success: true, + message: '系统设置已更新' + }); + } catch (error) { + console.error('更新系统设置失败:', error); + res.status(500).json({ + success: false, + message: '更新系统设置失败: ' + error.message + }); + } +}); + +// 获取服务器存储统计信息 +app.get('/api/admin/storage-stats', authMiddleware, adminMiddleware, (req, res) => { + try { + // 获取本地存储目录 + const localStorageDir = path.join(__dirname, 'local-storage'); + + // 获取磁盘信息(使用df命令) + let totalDisk = 0; + let usedDisk = 0; + let availableDisk = 0; + + try { + // 获取本地存储目录所在分区的磁盘信息 + const dfOutput = execSync(`df -B 1 / | tail -1`, { encoding: 'utf8' }); + const parts = dfOutput.trim().split(/\s+/); + + if (parts.length >= 4) { + totalDisk = parseInt(parts[1]) || 0; // 总大小 + usedDisk = parseInt(parts[2]) || 0; // 已使用 + availableDisk = parseInt(parts[3]) || 0; // 可用 + } + } catch (dfError) { + console.error('获取磁盘信息失败:', dfError.message); + // 如果df命令失败,尝试使用Windows的wmic命令 + try { + // 获取本地存储目录所在的驱动器号 + const driveLetter = localStorageDir.charAt(0); + const wmicOutput = execSync(`wmic logicaldisk where "DeviceID='${driveLetter}:'" get Size,FreeSpace /value`, { encoding: 'utf8' }); + + const freeMatch = wmicOutput.match(/FreeSpace=(\d+)/); + const sizeMatch = wmicOutput.match(/Size=(\d+)/); + + if (sizeMatch && freeMatch) { + totalDisk = parseInt(sizeMatch[1]) || 0; + availableDisk = parseInt(freeMatch[1]) || 0; + usedDisk = totalDisk - availableDisk; + } + } catch (wmicError) { + console.error('获取Windows磁盘信息失败:', wmicError.message); + } + } + + // 从数据库获取所有用户的本地存储配额和使用情况 + const users = UserDB.getAll(); + let totalUserQuotas = 0; + let totalUserUsed = 0; + + users.forEach(user => { + // 只统计使用本地存储的用户(local_only 或 user_choice) + const storagePermission = user.storage_permission || 'sftp_only'; + if (storagePermission === 'local_only' || storagePermission === 'user_choice') { + totalUserQuotas += user.local_storage_quota || 0; + totalUserUsed += user.local_storage_used || 0; + } + }); + + res.json({ + success: true, + stats: { + totalDisk, // 磁盘总容量 + usedDisk, // 磁盘已使用 + availableDisk, // 磁盘可用空间 + totalUserQuotas, // 用户配额总和 + totalUserUsed, // 用户实际使用总和 + totalUsers: users.length // 用户总数 + } + }); + +// 获取所有用户 +} catch (error) { console.error('获取存储统计失败:', error); res.status(500).json({ success: false, message: '获取存储统计失败: ' + error.message }); }}); +app.get('/api/admin/users', authMiddleware, adminMiddleware, (req, res) => { + try { + const users = UserDB.getAll(); + + res.json({ + success: true, + users: users.map(u => ({ + id: u.id, + username: u.username, + email: u.email, + is_admin: u.is_admin, + is_active: u.is_active, + is_banned: u.is_banned, + has_ftp_config: u.has_ftp_config, + created_at: u.created_at, + // 新增:存储相关字段 + storage_permission: u.storage_permission || 'sftp_only', + current_storage_type: u.current_storage_type || 'sftp', + local_storage_quota: u.local_storage_quota || 1073741824, + local_storage_used: u.local_storage_used || 0 + })) + }); + } catch (error) { + console.error('获取用户列表失败:', error); + res.status(500).json({ + success: false, + message: '获取用户列表失败: ' + error.message + }); + } +}); + +// 封禁/解封用户 +app.post('/api/admin/users/:id/ban', authMiddleware, adminMiddleware, (req, res) => { + try { + const { id } = req.params; + const { banned } = req.body; + + UserDB.setBanStatus(id, banned); + + res.json({ + success: true, + message: banned ? '用户已封禁' : '用户已解封' + }); + } catch (error) { + console.error('操作失败:', error); + res.status(500).json({ + success: false, + message: '操作失败: ' + error.message + }); + } +}); + +// 删除用户(级联删除文件和分享) +app.delete('/api/admin/users/:id', authMiddleware, adminMiddleware, async (req, res) => { + try { + const { id } = req.params; + + if (parseInt(id) === req.user.id) { + return res.status(400).json({ + success: false, + message: '不能删除自己的账号' + }); + } + + // 获取用户信息 + const user = UserDB.findById(id); + if (!user) { + return res.status(404).json({ + success: false, + message: '用户不存在' + }); + } + + const deletionLog = { + userId: id, + username: user.username, + deletedFiles: [], + deletedShares: 0, + warnings: [] + }; + + // 1. 删除本地存储文件(如果用户使用了本地存储) + const storagePermission = user.storage_permission || 'sftp_only'; + if (storagePermission === 'local_only' || storagePermission === 'user_choice') { + const storageRoot = process.env.STORAGE_ROOT || path.join(__dirname, 'storage'); + const userStorageDir = path.join(storageRoot, `user_${id}`); + + if (fs.existsSync(userStorageDir)) { + try { + // 递归删除用户目录 + const deletedSize = getUserDirectorySize(userStorageDir); + fs.rmSync(userStorageDir, { recursive: true, force: true }); + deletionLog.deletedFiles.push({ + type: 'local', + path: userStorageDir, + size: deletedSize + }); + console.log(`[删除用户] 已删除本地存储目录: ${userStorageDir}`); + } catch (error) { + console.error(`[删除用户] 删除本地存储失败:`, error); + deletionLog.warnings.push(`删除本地存储失败: ${error.message}`); + } + } + } + + // 2. SFTP存储文件 - 只记录警告,不实际删除(安全考虑) + if (user.has_ftp_config && (storagePermission === 'sftp_only' || storagePermission === 'user_choice')) { + deletionLog.warnings.push( + `用户配置了SFTP存储 (${user.ftp_host}:${user.ftp_port}),SFTP文件未自动删除,请手动处理` + ); + } + + // 3. 删除用户的所有分享记录 + try { + const userShares = ShareDB.getUserShares(id); + deletionLog.deletedShares = userShares.length; + + userShares.forEach(share => { + ShareDB.delete(share.id); + // 清除分享缓存 + if (shareFileCache.has(share.share_code)) { + shareFileCache.delete(share.share_code); + } + }); + + console.log(`[删除用户] 已删除 ${deletionLog.deletedShares} 条分享记录`); + } catch (error) { + console.error(`[删除用户] 删除分享记录失败:`, error); + deletionLog.warnings.push(`删除分享记录失败: ${error.message}`); + } + + // 4. 删除用户记录 + UserDB.delete(id); + + // 构建响应消息 + let message = `用户 ${user.username} 已删除`; + if (deletionLog.deletedFiles.length > 0) { + const totalSize = deletionLog.deletedFiles.reduce((sum, f) => sum + f.size, 0); + message += `,已清理本地文件 ${formatFileSize(totalSize)}`; + } + if (deletionLog.deletedShares > 0) { + message += `,已删除 ${deletionLog.deletedShares} 条分享`; + } + + res.json({ + success: true, + message, + details: deletionLog + }); + } catch (error) { + console.error('删除用户失败:', error); + res.status(500).json({ + success: false, + message: '删除用户失败: ' + error.message + }); + } +}); + +// 辅助函数:计算目录大小 +function getUserDirectorySize(dirPath) { + let totalSize = 0; + + function calculateSize(currentPath) { + try { + const stats = fs.statSync(currentPath); + + if (stats.isDirectory()) { + const files = fs.readdirSync(currentPath); + files.forEach(file => { + calculateSize(path.join(currentPath, file)); + }); + } else { + totalSize += stats.size; + } + } catch (error) { + console.error(`计算大小失败: ${currentPath}`, error); + } + } + + calculateSize(dirPath); + return totalSize; +} + +// 设置用户存储权限(管理员) +app.post('/api/admin/users/:id/storage-permission', + authMiddleware, + adminMiddleware, + [ + body('storage_permission').isIn(['local_only', 'sftp_only', 'user_choice']).withMessage('无效的存储权限') + ], + (req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + errors: errors.array() + }); + } + + try { + const { id } = req.params; + const { storage_permission, local_storage_quota } = req.body; + + const updates = { storage_permission }; + + // 如果提供了配额,更新配额(单位:字节) + if (local_storage_quota !== undefined) { + updates.local_storage_quota = parseInt(local_storage_quota); + } + + // 根据权限设置自动调整存储类型 + const user = UserDB.findById(id); + if (!user) { + return res.status(404).json({ + success: false, + message: '用户不存在' + }); + } + + if (storage_permission === 'local_only') { + updates.current_storage_type = 'local'; + } else if (storage_permission === 'sftp_only') { + // 只有配置了SFTP才切换到SFTP + if (user.has_ftp_config) { + updates.current_storage_type = 'sftp'; + } + } + // user_choice 不自动切换,保持用户当前选择 + + UserDB.update(id, updates); + + res.json({ + success: true, + message: '存储权限已更新' + }); + } catch (error) { + console.error('设置存储权限失败:', error); + res.status(500).json({ + success: false, + message: '设置存储权限失败: ' + error.message + }); + } + } +); + +// 重置用户密码 +// ===== 密码重置请求系统 ===== + +// 用户提交密码重置请求(公开API) +app.post('/api/password-reset/request', + [ + body('username').notEmpty().withMessage('用户名不能为空'), + body('new_password').isLength({ min: 6 }).withMessage('新密码至少6个字符') + ], + (req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + errors: errors.array() + }); + } + + try { + const { username, new_password } = req.body; + + const user = UserDB.findByUsername(username); + if (!user) { + return res.status(404).json({ + success: false, + message: '用户不存在' + }); + } + + // 检查是否已有待审核的请求 + if (PasswordResetDB.hasPendingRequest(user.id)) { + return res.status(400).json({ + success: false, + message: '您已经提交过密码重置请求,请等待管理员审核' + }); + } + + // 创建密码重置请求 + PasswordResetDB.create(user.id, new_password); + + res.json({ + success: true, + message: '密码重置请求已提交,请等待管理员审核' + }); + } catch (error) { + console.error('提交密码重置请求失败:', error); + res.status(500).json({ + success: false, + message: '提交失败: ' + error.message + }); + } + } +); + +// 获取待审核的密码重置请求(管理员) +app.get('/api/admin/password-reset/pending', authMiddleware, adminMiddleware, (req, res) => { + try { + const requests = PasswordResetDB.getPending(); + + res.json({ + success: true, + requests + }); + } catch (error) { + console.error('获取密码重置请求失败:', error); + res.status(500).json({ + success: false, + message: '获取请求失败: ' + error.message + }); + } +}); + +// 审核密码重置请求(管理员) +app.post('/api/admin/password-reset/:id/review', authMiddleware, adminMiddleware, (req, res) => { + try { + const { id } = req.params; + const { approved } = req.body; + + PasswordResetDB.review(id, req.user.id, approved); + + res.json({ + success: true, + message: approved ? '密码重置已批准' : '密码重置已拒绝' + }); + } catch (error) { + console.error('审核密码重置请求失败:', error); + res.status(500).json({ + success: false, + message: error.message || '审核失败' + }); + } +}); + +// ===== 管理员文件审查功能 ===== + +// 查看用户文件列表(管理员,只读) +app.get('/api/admin/users/:id/files', authMiddleware, adminMiddleware, async (req, res) => { + const { id } = req.params; + const dirPath = req.query.path || '/'; + let sftp; + + try { + const user = UserDB.findById(id); + + if (!user) { + return res.status(404).json({ + success: false, + message: '用户不存在' + }); + } + + if (!user.has_ftp_config) { + return res.status(400).json({ + success: false, + message: '该用户未配置SFTP服务器' + }); + } + + sftp = await connectToSFTP(user); + const list = await sftp.list(dirPath); + + const formattedList = list.map(item => ({ + name: item.name, + type: item.type === 'd' ? 'directory' : 'file', + size: item.size, + sizeFormatted: formatFileSize(item.size), + modifiedAt: new Date(item.modifyTime), + isDirectory: item.type === 'd' + })); + + formattedList.sort((a, b) => { + if (a.isDirectory && !b.isDirectory) return -1; + if (!a.isDirectory && b.isDirectory) return 1; + return a.name.localeCompare(b.name); + }); + + res.json({ + success: true, + username: user.username, + path: dirPath, + items: formattedList + }); + } catch (error) { + console.error('管理员查看用户文件失败:', error); + res.status(500).json({ + success: false, + message: '获取文件列表失败: ' + error.message + }); + } finally { + if (sftp) await sftp.end(); + } +}); + +// 获取所有分享(管理员) +app.get('/api/admin/shares', authMiddleware, adminMiddleware, (req, res) => { + try { + const shares = ShareDB.getAll(); + + res.json({ + success: true, + shares + }); + } catch (error) { + console.error('获取分享列表失败:', error); + res.status(500).json({ + success: false, + message: '获取分享列表失败: ' + error.message + }); + } +}); + +// 删除分享(管理员) +app.delete('/api/admin/shares/:id', authMiddleware, adminMiddleware, (req, res) => { + try { + // 先获取分享信息以获得share_code + const share = ShareDB.findById(req.params.id); + + if (share) { + // 删除缓存 + if (shareFileCache.has(share.share_code)) { + shareFileCache.delete(share.share_code); + console.log(`[缓存清除] 分享码: ${share.share_code} (管理员操作)`); + } + + // 删除数据库记录 + ShareDB.delete(req.params.id); + + res.json({ + success: true, + message: '分享已删除' + }); + } else { + res.status(404).json({ + success: false, + message: '分享不存在' + }); + } + } catch (error) { + console.error('删除分享失败:', error); + res.status(500).json({ + success: false, + message: '删除分享失败: ' + error.message + }); + } +}); + +// 分享页面访问路由 +app.get("/s/:code", (req, res) => { + const shareCode = req.params.code; + // 使用相对路径重定向,浏览器会自动使用当前的协议和host + const frontendUrl = `/share.html?code=${shareCode}`; + console.log(`[分享] 重定向到: ${frontendUrl}`); + res.redirect(frontendUrl); +}); + +// 启动服务器 +app.listen(PORT, '0.0.0.0', () => { + console.log(`\n========================================`); + console.log(`玩玩云已启动`); + console.log(`服务器地址: http://localhost:${PORT}`); + console.log(`外网访问地址: http://0.0.0.0:${PORT}`); + console.log(`========================================\n`); +}); diff --git a/backend/start.bat b/backend/start.bat new file mode 100644 index 0000000..719cb35 --- /dev/null +++ b/backend/start.bat @@ -0,0 +1,10 @@ +@echo off +echo ======================================== +echo FTP 网盘管理平台 - 启动脚本 +echo ======================================== +echo. + +cd /d %~dp0 +node server.js + +pause diff --git a/backend/storage.js b/backend/storage.js new file mode 100644 index 0000000..cc42b1b --- /dev/null +++ b/backend/storage.js @@ -0,0 +1,321 @@ +const SftpClient = require('ssh2-sftp-client'); +const fs = require('fs'); +const path = require('path'); +const { UserDB } = require('./database'); + +// ===== 统一存储接口 ===== + +/** + * 存储接口工厂 + * 根据用户的存储类型返回对应的存储客户端 + */ +class StorageInterface { + constructor(user) { + this.user = user; + this.type = user.current_storage_type || 'sftp'; + } + + /** + * 创建并返回存储客户端 + */ + async connect() { + if (this.type === 'local') { + const client = new LocalStorageClient(this.user); + await client.init(); + return client; + } else { + const client = new SftpStorageClient(this.user); + await client.connect(); + return client; + } + } +} + +// ===== 本地存储客户端 ===== + +class LocalStorageClient { + constructor(user) { + this.user = user; + // 使用环境变量或默认路径(不硬编码) + const storageRoot = process.env.STORAGE_ROOT || path.join(__dirname, 'storage'); + this.basePath = path.join(storageRoot, `user_${user.id}`); + } + + /** + * 初始化用户存储目录 + */ + async init() { + if (!fs.existsSync(this.basePath)) { + fs.mkdirSync(this.basePath, { recursive: true, mode: 0o755 }); + console.log(`[本地存储] 创建用户目录: ${this.basePath}`); + } + } + + /** + * 列出目录内容 + */ + async list(dirPath) { + const fullPath = this.getFullPath(dirPath); + + // 确保目录存在 + if (!fs.existsSync(fullPath)) { + fs.mkdirSync(fullPath, { recursive: true }); + return []; + } + + const items = fs.readdirSync(fullPath, { withFileTypes: true }); + + return items.map(item => { + const itemPath = path.join(fullPath, item.name); + const stats = fs.statSync(itemPath); + return { + name: item.name, + type: item.isDirectory() ? 'd' : '-', + size: stats.size, + modifyTime: stats.mtimeMs + }; + }); + } + + /** + * 上传文件 + */ + async put(localPath, remotePath) { + const destPath = this.getFullPath(remotePath); + + // 检查配额 + const fileSize = fs.statSync(localPath).size; + this.checkQuota(fileSize); + + // 确保目标目录存在 + const destDir = path.dirname(destPath); + if (!fs.existsSync(destDir)) { + fs.mkdirSync(destDir, { recursive: true }); + } + + // 使用临时文件+重命名模式,避免文件被占用问题 + const tempPath = `${destPath}.uploading_${Date.now()}`; + + try { + // 复制到临时文件 + fs.copyFileSync(localPath, tempPath); + + // 如果目标文件存在,先删除 + if (fs.existsSync(destPath)) { + fs.unlinkSync(destPath); + } + + // 重命名临时文件为目标文件 + fs.renameSync(tempPath, destPath); + + // 更新已使用空间 + this.updateUsedSpace(fileSize); + } catch (error) { + // 清理临时文件 + if (fs.existsSync(tempPath)) { + fs.unlinkSync(tempPath); + } + throw error; + } + } + + /** + * 删除文件 + */ + async delete(filePath) { + const fullPath = this.getFullPath(filePath); + const stats = fs.statSync(fullPath); + + fs.unlinkSync(fullPath); + + // 更新已使用空间 + this.updateUsedSpace(-stats.size); + } + + /** + * 重命名文件 + */ + async rename(oldPath, newPath) { + const oldFullPath = this.getFullPath(oldPath); + const newFullPath = this.getFullPath(newPath); + + // 确保新路径的目录存在 + const newDir = path.dirname(newFullPath); + if (!fs.existsSync(newDir)) { + fs.mkdirSync(newDir, { recursive: true }); + } + + fs.renameSync(oldFullPath, newFullPath); + } + + /** + * 获取文件信息 + */ + async stat(filePath) { + const fullPath = this.getFullPath(filePath); + return fs.statSync(fullPath); + } + + /** + * 创建文件读取流 + */ + createReadStream(filePath) { + const fullPath = this.getFullPath(filePath); + return fs.createReadStream(fullPath); + } + + /** + * 关闭连接(本地存储无需关闭) + */ + async end() { + // 本地存储无需关闭连接 + } + + // ===== 辅助方法 ===== + + /** + * 获取完整路径(带安全检查) + */ + getFullPath(relativePath) { + // 1. 规范化路径,移除 ../ 等危险路径 + const normalized = path.normalize(relativePath).replace(/^(\.\.[\/\\])+/, ''); + + // 2. 拼接完整路径 + const fullPath = path.join(this.basePath, normalized); + + // 3. 安全检查:确保路径在用户目录内(防止目录遍历攻击) + if (!fullPath.startsWith(this.basePath)) { + throw new Error('非法路径访问'); + } + + return fullPath; + } + + /** + * 检查配额 + */ + checkQuota(additionalSize) { + const newUsed = (this.user.local_storage_used || 0) + additionalSize; + if (newUsed > this.user.local_storage_quota) { + const used = this.formatSize(this.user.local_storage_used); + const quota = this.formatSize(this.user.local_storage_quota); + const need = this.formatSize(additionalSize); + throw new Error(`存储配额不足。已使用: ${used}, 配额: ${quota}, 需要: ${need}`); + } + } + + /** + * 更新已使用空间 + */ + updateUsedSpace(delta) { + const newUsed = Math.max(0, (this.user.local_storage_used || 0) + delta); + UserDB.update(this.user.id, { local_storage_used: newUsed }); + // 更新内存中的值 + this.user.local_storage_used = newUsed; + } + + /** + * 格式化文件大小 + */ + formatSize(bytes) { + if (bytes === 0) return '0 B'; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]; + } +} + +// ===== SFTP存储客户端 ===== + +class SftpStorageClient { + constructor(user) { + this.user = user; + this.sftp = new SftpClient(); + } + + /** + * 连接SFTP服务器 + */ + async connect() { + await this.sftp.connect({ + host: this.user.ftp_host, + port: this.user.ftp_port || 22, + username: this.user.ftp_user, + password: this.user.ftp_password + }); + return this; + } + + /** + * 列出目录内容 + */ + async list(dirPath) { + return await this.sftp.list(dirPath); + } + + /** + * 上传文件 + */ + async put(localPath, remotePath) { + // 使用临时文件+重命名模式(与upload_tool保持一致) + const tempRemotePath = `${remotePath}.uploading_${Date.now()}`; + + // 第一步:上传到临时文件 + await this.sftp.put(localPath, tempRemotePath); + + // 第二步:检查目标文件是否存在,如果存在先删除 + try { + await this.sftp.stat(remotePath); + await this.sftp.delete(remotePath); + } catch (err) { + // 文件不存在,无需删除 + } + + // 第三步:重命名临时文件为目标文件 + await this.sftp.rename(tempRemotePath, remotePath); + } + + /** + * 删除文件 + */ + async delete(filePath) { + return await this.sftp.delete(filePath); + } + + /** + * 重命名文件 + */ + async rename(oldPath, newPath) { + return await this.sftp.rename(oldPath, newPath); + } + + /** + * 获取文件信息 + */ + async stat(filePath) { + return await this.sftp.stat(filePath); + } + + /** + * 创建文件读取流 + */ + createReadStream(filePath) { + return this.sftp.createReadStream(filePath); + } + + /** + * 关闭连接 + */ + async end() { + if (this.sftp) { + await this.sftp.end(); + } + } +} + +module.exports = { + StorageInterface, + LocalStorageClient, + SftpStorageClient +}; diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..f87f06f --- /dev/null +++ b/deploy.sh @@ -0,0 +1,110 @@ +#!/bin/bash + +# 玩玩云一键部署脚本 +# 使用方法: bash deploy.sh + +set -e + +echo "=========================================" +echo " 玩玩云 - 一键部署脚本" +echo "=========================================" +echo "" + +# 检查Docker +if ! command -v docker &> /dev/null; then + echo "❌ 错误: Docker未安装" + echo "请先安装Docker: https://docs.docker.com/engine/install/" + exit 1 +fi + +# 检查Docker Compose +if ! command -v docker-compose &> /dev/null; then + echo "❌ 错误: Docker Compose未安装" + echo "请先安装Docker Compose: https://docs.docker.com/compose/install/" + exit 1 +fi + +echo "✓ Docker版本: $(docker --version)" +echo "✓ Docker Compose版本: $(docker-compose --version)" +echo "" + +# 检查必要的目录 +echo "📁 检查项目结构..." +REQUIRED_DIRS=("backend" "frontend" "nginx") +for dir in "${REQUIRED_DIRS[@]}"; do + if [ ! -d "$dir" ]; then + echo "❌ 错误: 缺少 $dir 目录" + exit 1 + fi +done +echo "✓ 项目结构完整" +echo "" + +# 创建必要的目录 +echo "📂 创建必要的目录..." +mkdir -p certbot/conf +mkdir -p certbot/www +mkdir -p backend/uploads +echo "✓ 目录创建完成" +echo "" + +# 检查.env文件 +if [ ! -f "backend/.env" ]; then + echo "⚠️ 警告: backend/.env 文件不存在" + if [ -f "backend/.env.example" ]; then + echo "正在从.env.example创建.env文件..." + cp backend/.env.example backend/.env + echo "✓ 已创建.env文件,请根据需要修改配置" + else + echo "⚠️ 建议创建.env文件配置JWT密钥等参数" + fi + echo "" +fi + +# 停止旧容器 +echo "🔄 停止旧容器..." +docker-compose down 2>/dev/null || true +echo "✓ 旧容器已停止" +echo "" + +# 构建并启动 +echo "🚀 构建并启动服务..." +docker-compose up --build -d + +# 等待服务启动 +echo "" +echo "⏳ 等待服务启动..." +sleep 5 + +# 检查容器状态 +echo "" +echo "📊 检查容器状态..." +docker-compose ps + +# 检查后端日志 +echo "" +echo "📝 后端启动日志:" +docker-compose logs --tail=20 backend + +# 显示访问信息 +echo "" +echo "=========================================" +echo " 🎉 部署完成!" +echo "=========================================" +echo "" +echo "📍 访问地址:" +echo " 前端: http://localhost:8080" +echo " 后端API: http://localhost:40001" +echo "" +echo "👤 默认管理员账号:" +echo " 用户名: admin" +echo " 密码: admin123" +echo " ⚠️ 请立即登录并修改密码!" +echo "" +echo "📚 查看日志:" +echo " docker-compose logs -f" +echo "" +echo "🛑 停止服务:" +echo " docker-compose down" +echo "" +echo "=========================================" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3159ed9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,53 @@ +version: '3.8' + +services: + backend: + build: + context: ./backend + dockerfile: Dockerfile + image: wanwanyun-backend + container_name: wanwanyun-backend + restart: always + ports: + - "40001:40001" + volumes: + - ./backend:/app + - /app/node_modules + - ./upload-tool:/upload-tool + - ./storage:/app/storage # 本地存储卷 + environment: + - NODE_ENV=production + - STORAGE_ROOT=/app/storage # 存储根目录(不硬编码) + networks: + - wanwanyun-network + + frontend: + image: nginx:alpine + container_name: wanwanyun-frontend + restart: always + ports: + - "8080:80" + - "8443:443" + volumes: + - ./frontend:/usr/share/nginx/html + - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf + - ./certbot/conf:/etc/letsencrypt + - ./certbot/www:/var/www/certbot + depends_on: + - backend + networks: + - wanwanyun-network + command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'" + + certbot: + image: certbot/certbot + container_name: wanwanyun-certbot + restart: unless-stopped + volumes: + - ./certbot/conf:/etc/letsencrypt + - ./certbot/www:/var/www/certbot + entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" + +networks: + wanwanyun-network: + driver: bridge diff --git a/frontend/app.html b/frontend/app.html new file mode 100644 index 0000000..64d63cd --- /dev/null +++ b/frontend/app.html @@ -0,0 +1,1997 @@ + + + + + + 玩玩云 - 文件管理平台 + + + + + + +
+
+
+
+ + {{ isLogin ? '登录' : '注册' }} +
+
{{ errorMessage }}
+
{{ successMessage }}
+
+
+ + +
+
+ + +
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ {{ isLogin ? '还没有账号?' : '已有账号?' }} + {{ isLogin ? '立即注册' : '去登录' }} +
+
+
+ + + + + +
+
+ +
+
+ + + 当前存储: {{ storageTypeText }} + +
+
+
+ 配额使用情况 + {{ localUsedFormatted }} / {{ localQuotaFormatted }} ({{ quotaPercentage }}%) +
+
+
+
+
+
+ + +
+
+ + + + + +
+
+ + +
+
+ + + + + +
+
+

加载中...

+
+ + +
+

文件夹是空的

+ +
+
+ +
拖放文件到这里上传
+
松开鼠标即可开始上传
+
+
+ + +
+
+
+ + + +
+ +
+ + + + + + + + + +
+
{{ file.name }}
+
{{ file.isDirectory ? '文件夹' : file.sizeFormatted }}
+
+
+ + +
+ + + + + + + + + + + + + + + +
文件名大小修改时间
+ + + +
+ +
+ + + + + + + + + + {{ file.name }} +
{{ file.isDirectory ? '-' : file.sizeFormatted }}{{ formatDate(file.modifiedTime) }}
+
+
+
+
+ + + + + + + + + + + + +
+
+ +
+

+ 存储管理 +

+ +
+
+ 当前存储方式: + {{ storageTypeText }} +
+ +
+ 配额使用: + {{ localUsedFormatted }} / {{ localQuotaFormatted }} ({{ quotaPercentage }}%) +
+
+
+
+ +
+ + +
+ +
+ + 提示: 本地存储速度快但有配额限制;SFTP存储需先配置服务器信息 +
+
+
+ + +
+

+ 本地存储 +

+ +
+
+ 存储方式: + 本地存储 + + 仅本地 + +
+ +
+ 配额使用: + {{ localUsedFormatted }} / {{ localQuotaFormatted }} ({{ quotaPercentage }}%) +
+
+
+
+ +
+ + 说明: 管理员已将您的存储权限设置为"仅本地存储",您的文件存储在服务器本地,速度快但有配额限制。如需使用SFTP存储,请联系管理员修改权限设置。 +
+
+
+ + +
+

+ SFTP存储 +

+ +
+
+ 存储方式: + SFTP存储 + + 仅SFTP + +
+ +
+ 服务器: + {{ user.ftp_host }}:{{ user.ftp_port }} +
+ +
+ + 说明: 管理员已将您的存储权限设置为"仅SFTP存储",您的文件存储在远程SFTP服务器上。如需使用本地存储,请联系管理员修改权限设置。 +
+
+
+ + +
+

SFTP配置

+
+ 请配置SFTP服务器 +
+ + +
+
+ +

+ 快速导入配置文件 +

+

+ 点击选择或拖拽 .inf 文件到此处 +

+

+ 导入后请检查配置信息并点击保存按钮 +

+ +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + + 配置后可直接通过HTTP下载文件。URL格式: 基础URL/文件路径,例如: http://example.com/files/test.exe + +
+ +
+
+ + +

账号设置

+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+
+
+ + +
+
+ +
+

我的分享

+
+ + +
+
+ + +
+ 还没有创建任何分享 +
+ + +
+
+
+ +
+
{{ share.share_path }}
+
+ 访问: {{ share.view_count }} | 下载: {{ share.download_count }} +
+
+ + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + +
文件路径分享链接访问次数下载次数操作
{{ share.share_path }} + {{ share.share_url }} + {{ share.view_count }}{{ share.download_count }} + +
+
+
+ + +
+
+ +
+

+ 服务器存储统计 +

+ +
+ +
+
+
+
磁盘总容量
+
{{ formatBytes(serverStorageStats.totalDisk) }}
+
+ +
+
+ + +
+
+
+
已使用空间
+
{{ formatBytes(serverStorageStats.usedDisk) }}
+
+ {{ serverStorageStats.totalDisk > 0 ? Math.round((serverStorageStats.usedDisk / serverStorageStats.totalDisk) * 100) : 0 }}% 使用率 +
+
+ +
+
+ + +
+
+
+
可用空间
+
{{ formatBytes(serverStorageStats.availableDisk) }}
+
+ +
+
+ + +
+
+
+
用户配额总和
+
{{ formatBytes(serverStorageStats.totalUserQuotas) }}
+
+ {{ serverStorageStats.totalUsers }} 个用户 +
+
+ +
+
+ + +
+
+
+
用户实际使用
+
{{ formatBytes(serverStorageStats.totalUserUsed) }}
+
+ {{ serverStorageStats.totalUserQuotas > 0 ? Math.round((serverStorageStats.totalUserUsed / serverStorageStats.totalUserQuotas) * 100) : 0 }}% 配额使用率 +
+
+ +
+
+ + +
+
+
+
安全可分配配额
+
+ {{ formatBytes(Math.max(0, serverStorageStats.availableDisk - (serverStorageStats.totalUserQuotas - serverStorageStats.totalUserUsed))) }} +
+
+ 可用空间 - 未使用的配额 +
+
+ +
+
+
+ + +
+ + 警告: 磁盘使用率已超过90%,建议及时清理空间或扩容! +
+ +
+ + 配额超分配: 用户配额总和 ({{ formatBytes(serverStorageStats.totalUserQuotas) }}) 已超过磁盘总容量 ({{ formatBytes(serverStorageStats.totalDisk) }})! +
+
+ +

用户管理

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ID用户名邮箱存储权限当前存储配额使用状态操作
{{ u.id }} + {{ u.username }} + + + + {{ u.email }} + 仅本地 + 仅SFTP + 用户选择 + + + 本地 + + + SFTP + + +
+
{{ formatBytes(u.local_storage_used) }} / {{ formatBytes(u.local_storage_quota) }}
+
+ {{ Math.round((u.local_storage_used / u.local_storage_quota) * 100) }}% +
+
+ - +
+ 已封禁 + 正常 + +
+ + + + + +
+
+
+
+ + +
+

密码重置审核

+
+ 暂无待审核的密码重置请求 +
+ + + + + + + + + + + + + + + + + +
用户名邮箱提交时间操作
{{ req.username }}{{ req.email }}{{ formatDate(req.created_at) }} + + +
+
+
+ + + + + + + + +
+
+ +
+
{{ toast.title }}
+
{{ toast.message }}
+
+
+
+ + +
+
+ +
+
正在上传文件
+
{{ uploadingFileName }}
+
{{ formatFileSize(uploadedBytes) }} / {{ formatFileSize(totalBytes) }}
+
+
{{ uploadProgress }}%
+
+
+
+
+
+ + +
+
+ 预览 +
+
+ 下载 +
+
+ 重命名 +
+
+ 分享 +
+
+
+ 删除 +
+
+ + + + + + + + + + + +
+ + + + + + diff --git a/frontend/app.js b/frontend/app.js new file mode 100644 index 0000000..44fffe6 --- /dev/null +++ b/frontend/app.js @@ -0,0 +1,1708 @@ +const { createApp } = Vue; + +createApp({ + data() { + return { + // API配置 + // API配置 - 动态适配localhost或生产环境 + apiBase: window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1' + ? 'http://localhost:40001' + : window.location.protocol + '//' + window.location.host, + + // 用户状态 + isLoggedIn: false, + user: null, + token: null, + + // 视图状态 + currentView: 'files', + isLogin: true, + fileViewMode: 'grid', // 文件显示模式: grid 大图标, list 列表 + shareViewMode: 'list', // 分享显示模式: grid 大图标, list 列表 + + // 表单数据 + loginForm: { + username: '', + password: '' + }, + registerForm: { + username: '', + email: '', + password: '' + }, + + // SFTP配置表单 + ftpConfigForm: { + ftp_host: '', + ftp_port: 22, + ftp_user: '', + ftp_password: '', + http_download_base_url: '' + }, + showFtpConfigModal: false, + + // 修改密码表单 + changePasswordForm: { + new_password: '' + }, + // 用户名修改表单 + usernameForm: { + newUsername: '' + }, + currentPath: '/', + files: [], + loading: false, + + // 分享管理 + shares: [], + showShareAllModal: false, + showShareFileModal: false, + shareAllForm: { + password: "", + expiryType: "never", + customDays: 7 + }, + shareFileForm: { + fileName: "", + filePath: "", + password: "", + expiryType: "never", + customDays: 7 + }, + shareResult: null, + + // 文件重命名 + showRenameModal: false, + renameForm: { + oldName: "", + newName: "", + path: "" + }, + + // 上传 + showUploadModal: false, + uploadProgress: 0, + uploadedBytes: 0, + totalBytes: 0, + uploadingFileName: '', + isDragging: false, + + // 上传工具下载 + downloadingTool: false, + + // 管理员 + adminUsers: [], + showResetPwdModal: false, + resetPwdUser: {}, + newPassword: '', + + // 密码重置审核 + passwordResetRequests: [], + + // 文件审查 + showFileInspectionModal: false, + inspectionUser: null, + inspectionFiles: [], + inspectionPath: '/', + inspectionLoading: false, + inspectionViewMode: 'grid', // 文件审查显示模式: grid 大图标, list 列表 + + // 忘记密码 + showForgotPasswordModal: false, + forgotPasswordForm: { + username: '', + new_password: '' + }, + + // 系统设置 + systemSettings: { + maxUploadSizeMB: 100 + }, + + // Toast通知 + toasts: [], + toastIdCounter: 0, + + // 提示信息 + errorMessage: '', + successMessage: '', + + // 存储相关 + storageType: 'sftp', // 当前使用的存储类型 + storagePermission: 'sftp_only', // 存储权限 + localQuota: 0, // 本地存储配额(字节) + localUsed: 0, // 本地存储已使用(字节) + + + // 右键菜单 + showContextMenu: false, + contextMenuX: 0, + contextMenuY: 0, + contextMenuFile: null, + + // 长按检测 + longPressTimer: null, + + // 媒体预览 + showImageViewer: false, + showVideoPlayer: false, + showAudioPlayer: false, + currentMediaUrl: '', + currentMediaName: '', + currentMediaType: '', // 'image', 'video', 'audio' + longPressDuration: 500, // 长按时间(毫秒) + // 管理员编辑用户存储权限 + showEditStorageModal: false, + editStorageForm: { + userId: null, + username: '', + storage_permission: 'sftp_only', + local_storage_quota_value: 1, // 配额数值 + quota_unit: 'GB' // 配额单位:MB 或 GB + }, + + // 服务器存储统计 + serverStorageStats: { + totalDisk: 0, + usedDisk: 0, + availableDisk: 0, + totalUserQuotas: 0, + totalUserUsed: 0, + totalUsers: 0 + } + }; + }, + + computed: { + pathParts() { + return this.currentPath.split('/').filter(p => p !== ''); + }, + + // 格式化配额显示 + localQuotaFormatted() { + return this.formatBytes(this.localQuota); + }, + + localUsedFormatted() { + return this.formatBytes(this.localUsed); + }, + + // 配额使用百分比 + quotaPercentage() { + if (this.localQuota === 0) return 0; + return Math.round((this.localUsed / this.localQuota) * 100); + }, + + // 存储类型显示文本 + storageTypeText() { + return this.storageType === 'local' ? '本地存储' : 'SFTP存储'; + } + }, + + methods: { + // 格式化文件大小 + formatFileSize(bytes) { + if (bytes === 0) return '0 B'; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i]; + }, + + // 拖拽上传处理 + handleDragEnter(e) { + e.preventDefault(); + e.stopPropagation(); + this.isDragging = true; + }, + handleDragOver(e) { + e.preventDefault(); + e.stopPropagation(); + this.isDragging = true; + }, +handleDragLeave(e) { + e.preventDefault(); + e.stopPropagation(); + + // 使用更可靠的检测:检查鼠标实际位置 + const container = e.currentTarget; + const rect = container.getBoundingClientRect(); + const x = e.clientX; + const y = e.clientY; + + // 如果鼠标位置在容器边界外,隐藏覆盖层 + // 添加5px的容差,避免边界问题 + const margin = 5; + const isOutside = + x < rect.left - margin || + x > rect.right + margin || + y < rect.top - margin || + y > rect.bottom + margin; + + if (isOutside) { + this.isDragging = false; + return; + } + + // 备用检测:检查 relatedTarget + const related = e.relatedTarget; + if (!related || !container.contains(related)) { + this.isDragging = false; + } + }, + + async handleDrop(e) { + e.preventDefault(); + e.stopPropagation(); + this.isDragging = false; + + const files = e.dataTransfer.files; + if (files.length > 0) { + const file = files[0]; + await this.uploadFile(file); + } + }, + + // ===== 认证相关 ===== + + toggleAuthMode() { + this.isLogin = !this.isLogin; + this.errorMessage = ''; + this.successMessage = ''; + }, + + async handleLogin() { + this.errorMessage = ''; + try { + const response = await axios.post(`${this.apiBase}/api/login`, this.loginForm); + + if (response.data.success) { + this.token = response.data.token; + this.user = response.data.user; + this.isLoggedIn = true; + + // 保存token到localStorage + localStorage.setItem('token', this.token); + localStorage.setItem('user', JSON.stringify(this.user)); + + // 直接从登录响应中获取存储信息 + this.storagePermission = this.user.storage_permission || 'sftp_only'; + this.storageType = this.user.current_storage_type || 'sftp'; + this.localQuota = this.user.local_storage_quota || 0; + this.localUsed = this.user.local_storage_used || 0; + + console.log('[登录] 存储权限:', this.storagePermission, '存储类型:', this.storageType); + + // 管理员直接跳转到管理后台 + if (this.user.is_admin) { + this.currentView = 'admin'; + } + // 普通用户:检查存储权限 + else { + // 如果用户可以使用本地存储,直接进入文件页面 + if (this.storagePermission === 'local_only' || this.storagePermission === 'user_choice') { + this.currentView = 'files'; + this.loadFiles('/'); + } + // 如果仅SFTP模式,需要检查是否配置了SFTP + else if (this.storagePermission === 'sftp_only') { + if (this.user.has_ftp_config) { + this.currentView = 'files'; + this.loadFiles('/'); + } else { + this.currentView = 'settings'; + alert('欢迎!请先配置您的SFTP服务器'); + } + } else { + // 默认行为:跳转到文件页面 + this.currentView = 'files'; + this.loadFiles('/'); + } + } + } + } catch (error) { + this.errorMessage = error.response?.data?.message || '登录失败'; + } + }, + + async handleRegister() { + this.errorMessage = ''; + this.successMessage = ''; + + try { + const response = await axios.post(`${this.apiBase}/api/register`, this.registerForm); + + if (response.data.success) { + this.successMessage = '注册成功!请登录'; + this.isLogin = true; + + // 清空表单 + this.registerForm = { + username: '', + email: '', + password: '' + }; + } + } catch (error) { + const errorData = error.response?.data; + if (errorData?.errors) { + this.errorMessage = errorData.errors.map(e => e.msg).join(', '); + } else { + this.errorMessage = errorData?.message || '注册失败'; + } + } + }, + + async updateFtpConfig() { + try { + const response = await axios.post( + `${this.apiBase}/api/user/update-ftp`, + this.ftpConfigForm, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + alert('SFTP配置已保存!'); + // 更新用户信息 + this.user.has_ftp_config = 1; + // 刷新到文件页面 + this.currentView = 'files'; + this.loadFiles('/'); + } + } catch (error) { + alert('配置失败: ' + (error.response?.data?.message || error.message)); + } + }, + + async updateAdminProfile() { + try { + const response = await axios.post( + `${this.apiBase}/api/admin/update-profile`, + { + username: this.adminProfileForm.username + }, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + alert('用户名已更新!请重新登录。'); + + // 更新token和用户信息 + if (response.data.token) { + this.token = response.data.token; + localStorage.setItem('token', response.data.token); + } + + if (response.data.user) { + this.user = response.data.user; + localStorage.setItem('user', JSON.stringify(response.data.user)); + } + + // 重新登录 + this.logout(); + } + } catch (error) { + alert('修改失败: ' + (error.response?.data?.message || error.message)); + } + }, + + async changePassword() { + if (this.changePasswordForm.new_password.length < 6) { + alert('新密码至少6个字符'); + return; + } + + try { + const response = await axios.post( + `${this.apiBase}/api/user/change-password`, + { + new_password: this.changePasswordForm.new_password + }, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + alert('密码修改成功!'); + this.changePasswordForm.new_password = ''; + } + } catch (error) { + alert('密码修改失败: ' + (error.response?.data?.message || error.message)); + } + }, + + async loadFtpConfig() { + try { + const response = await axios.get( + `${this.apiBase}/api/user/profile`, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success && response.data.user) { + const user = response.data.user; + // 填充SFTP配置表单(密码不回显) + this.ftpConfigForm.ftp_host = user.ftp_host || ''; + this.ftpConfigForm.ftp_port = user.ftp_port || 22; + this.ftpConfigForm.ftp_user = user.ftp_user || ''; + this.ftpConfigForm.ftp_password = ''; // 密码不回显 + this.ftpConfigForm.http_download_base_url = user.http_download_base_url || ''; + } + } catch (error) { + console.error('加载SFTP配置失败:', error); + } + }, + + // 处理配置文件上传 + handleConfigFileUpload(event) { + const file = event.target.files[0]; + if (!file) return; + + this.processConfigFile(file); + + // 清空文件选择,允许重复选择同一文件 + event.target.value = ''; + }, + + // 处理配置文件拖拽 + handleConfigFileDrop(event) { + const file = event.dataTransfer.files[0]; + if (!file) return; + + // 检查文件扩展名 + if (!file.name.toLowerCase().endsWith('.inf')) { + this.showToast('error', '错误', '只支持 .inf 格式的配置文件'); + return; + } + + this.processConfigFile(file); + + // 恢复背景色 + event.currentTarget.style.background = '#f8f9ff'; + }, + + // 处理配置文件 + async processConfigFile(file) { + const reader = new FileReader(); + reader.onload = async (e) => { + try { + const content = e.target.result; + const config = this.parseConfigFile(content); + + if (config) { + // 填充表单 + this.ftpConfigForm.ftp_host = config.ip || ''; + this.ftpConfigForm.ftp_port = config.port || 22; + this.ftpConfigForm.ftp_user = config.id || ''; + this.ftpConfigForm.ftp_password = config.pw || ''; + this.ftpConfigForm.http_download_base_url = config.arr || ''; + + // 提示用户配置已导入,需要确认后保存 + this.showToast('success', '成功', '配置文件已导入!请检查并确认信息后点击"保存配置"按钮'); + } else { + this.showToast('error', '错误', '配置文件格式不正确,请检查文件内容'); + } + } catch (error) { + console.error('解析配置文件失败:', error); + this.showToast('error', '错误', '解析配置文件失败: ' + error.message); + } + }; + + reader.readAsText(file); + }, + + // 解析INI格式的配置文件 + parseConfigFile(content) { + const lines = content.split('\n'); + const config = {}; + + for (let line of lines) { + line = line.trim(); + + // 跳过空行和注释 + if (!line || line.startsWith('#') || line.startsWith(';') || line.startsWith('[')) { + continue; + } + + // 解析 key=value 格式 + const equalsIndex = line.indexOf('='); + if (equalsIndex > 0) { + const key = line.substring(0, equalsIndex).trim(); + const value = line.substring(equalsIndex + 1).trim(); + config[key] = value; + } + } + + // 验证必需字段 + if (config.ip && config.id && config.pw && config.port) { + return config; + } + + return null; + }, + + async updateUsername() { + if (!this.usernameForm.newUsername || this.usernameForm.newUsername.length < 3) { + alert('用户名至少3个字符'); + return; + } + + try { + const response = await axios.post( + `${this.apiBase}/api/user/update-username`, + { username: this.usernameForm.newUsername }, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + alert('用户名修改成功!请重新登录'); + // 更新本地用户信息 + this.user.username = this.usernameForm.newUsername; + localStorage.setItem('user', JSON.stringify(this.user)); + this.usernameForm.newUsername = ''; + } + } catch (error) { + alert('用户名修改失败: ' + (error.response?.data?.message || error.message)); + } + }, + + async updateProfile() { + try { + const response = await axios.post( + `${this.apiBase}/api/user/update-profile`, + { email: this.profileForm.email }, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + alert('邮箱已更新!'); + // 更新本地用户信息 + if (response.data.user) { + this.user = response.data.user; + localStorage.setItem('user', JSON.stringify(this.user)); + } + } + } catch (error) { + alert('更新失败: ' + (error.response?.data?.message || error.message)); + } + }, + + logout() { + this.isLoggedIn = false; + this.user = null; + this.token = null; + localStorage.removeItem('token'); + localStorage.removeItem('user'); + }, + + // 检查本地存储的登录状态 + async checkLoginStatus() { + const token = localStorage.getItem('token'); + const user = localStorage.getItem('user'); + + if (token && user) { + this.token = token; + this.user = JSON.parse(user); + this.isLoggedIn = true; + + // 从localStorage中的用户信息初始化存储相关字段 + this.storagePermission = this.user.storage_permission || 'sftp_only'; + this.storageType = this.user.current_storage_type || 'sftp'; + this.localQuota = this.user.local_storage_quota || 0; + this.localUsed = this.user.local_storage_used || 0; + + console.log('[页面加载] 存储权限:', this.storagePermission, '存储类型:', this.storageType); + + // 加载最新的用户信息(异步更新) + this.loadUserProfile(); + + // 管理员跳转到管理后台 + if (this.user.is_admin) { + this.currentView = 'admin'; + } + // 普通用户:根据存储权限决定跳转 + else { + // 如果用户可以使用本地存储,直接加载文件 + if (this.storagePermission === 'local_only' || this.storagePermission === 'user_choice') { + this.loadFiles('/'); + } + // 如果仅SFTP模式,需要检查是否配置了SFTP + else if (this.storagePermission === 'sftp_only') { + if (this.user.has_ftp_config) { + this.loadFiles('/'); + } else { + this.currentView = 'settings'; + } + } else { + // 默认加载文件 + this.loadFiles('/'); + } + } + } + }, + + // 检查URL参数 + checkUrlParams() { + const urlParams = new URLSearchParams(window.location.search); + const action = urlParams.get('action'); + + if (action === 'login') { + this.isLogin = true; + } else if (action === 'register') { + this.isLogin = false; + } + }, + + // ===== 文件管理 ===== + + async loadFiles(path) { + this.loading = true; + this.currentPath = path; + + try { + const response = await axios.get(`${this.apiBase}/api/files`, { + params: { path }, + headers: { Authorization: `Bearer ${this.token}` } + }); + + if (response.data.success) { + this.files = response.data.items; + + // 更新存储类型信息 + if (response.data.storageType) { + this.storageType = response.data.storageType; + } + if (response.data.storagePermission) { + this.storagePermission = response.data.storagePermission; + } + + // 更新用户本地存储信息 + await this.loadUserProfile(); + } + } catch (error) { + console.error('加载文件失败:', error); + alert('加载文件失败: ' + (error.response?.data?.message || error.message)); + + if (error.response?.status === 401) { + this.logout(); + } + } finally { + this.loading = false; + } + }, + + handleFileClick(file) { + if (file.isDirectory) { + const newPath = this.currentPath === '/' + ? `/${file.name}` + : `${this.currentPath}/${file.name}`; + this.loadFiles(newPath); + } else { + // 检查文件类型,打开相应的预览 + if (file.name.match(/\.(jpg|jpeg|png|gif|bmp|svg|webp)$/i)) { + this.openImageViewer(file); + } else if (file.name.match(/\.(mp4|avi|mov|wmv|flv|mkv|webm)$/i)) { + this.openVideoPlayer(file); + } else if (file.name.match(/\.(mp3|wav|flac|aac|ogg|m4a)$/i)) { + this.openAudioPlayer(file); + } + // 其他文件类型不做任何操作,用户可以通过右键菜单下载 + } + }, + navigateToPath(path) { + this.loadFiles(path); + }, + + navigateToIndex(index) { + const parts = this.pathParts.slice(0, index + 1); + const path = '/' + parts.join('/'); + this.loadFiles(path); + }, + + downloadFile(file) { + console.log("[DEBUG] 下载文件:", file); + if (file.httpDownloadUrl) { + // 如果配置了HTTP下载URL,使用HTTP直接下载 + console.log("[DEBUG] 使用HTTP下载:", file.httpDownloadUrl); + window.open(file.httpDownloadUrl, "_blank"); + } else { + // 如果没有配置HTTP URL,通过后端SFTP下载 + console.log("[DEBUG] 使用SFTP下载"); + const filePath = this.currentPath === '/' + ? `/${file.name}` + : `${this.currentPath}/${file.name}`; + + // 使用标签下载,通过URL参数传递token + const link = document.createElement('a'); + link.href = `${this.apiBase}/api/files/download?path=${encodeURIComponent(filePath)}&token=${this.token}`; + link.setAttribute('download', file.name); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + }, + + // ===== 文件操作 ===== + + openRenameModal(file) { + this.renameForm.oldName = file.name; + this.renameForm.newName = file.name; + this.renameForm.path = this.currentPath; + this.showRenameModal = true; + }, + + async renameFile() { + if (!this.renameForm.newName || this.renameForm.newName === this.renameForm.oldName) { + alert('请输入新的文件名'); + return; + } + + try { + const response = await axios.post( + `${this.apiBase}/api/files/rename`, + this.renameForm, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + this.showToast('success', '成功', '文件已重命名'); + this.showRenameModal = false; + this.loadFiles(this.currentPath); + } + } catch (error) { + console.error('重命名失败:', error); + this.showToast('error', '错误', error.response?.data?.message || '重命名失败'); + } + }, + + confirmDeleteFile(file) { + const fileType = file.isDirectory ? '文件夹' : '文件'; + const warning = file.isDirectory ? "\n注意:只能删除空文件夹!" : ""; + if (confirm(`确定要删除${fileType} "${file.name}" 吗?此操作无法撤销!${warning}`)) { + this.deleteFile(file); + } + }, + + // ===== 右键菜单和长按功能 ===== + + // 显示右键菜单(PC端) + showFileContextMenu(file, event) { + if (file.isDirectory) return; // 文件夹不显示菜单 + + event.preventDefault(); + this.contextMenuFile = file; + this.contextMenuX = event.clientX; + this.contextMenuY = event.clientY; + this.showContextMenu = true; + + // 点击其他地方关闭菜单 + this.$nextTick(() => { + document.addEventListener('click', this.hideContextMenu, { once: true }); + }); + }, + + // 隐藏右键菜单 + hideContextMenu() { + this.showContextMenu = false; + this.contextMenuFile = null; + }, + + // 长按开始(移动端) + handleLongPressStart(file, event) { + if (file.isDirectory) return; // 文件夹不响应长按 + + // 阻止默认的长按行为(如文本选择) + event.preventDefault(); + + this.longPressTimer = setTimeout(() => { + // 触发长按菜单 + this.contextMenuFile = file; + + // 获取触摸点位置 + const touch = event.touches[0]; + this.contextMenuX = touch.clientX; + this.contextMenuY = touch.clientY; + this.showContextMenu = true; + + // 触摸震动反馈(如果支持) + if (navigator.vibrate) { + navigator.vibrate(50); + } + + // 点击其他地方关闭菜单 + this.$nextTick(() => { + document.addEventListener('click', this.hideContextMenu, { once: true }); + }); + }, this.longPressDuration); + }, + + // 长按取消(移动端) + handleLongPressEnd() { + if (this.longPressTimer) { + clearTimeout(this.longPressTimer); + this.longPressTimer = null; + } + }, + + // 从菜单执行操作 + contextMenuAction(action) { + if (!this.contextMenuFile) return; + + switch (action) { + case 'preview': + // 根据文件类型打开对应的预览 + if (this.contextMenuFile.name.match(/\.(jpg|jpeg|png|gif|bmp|svg|webp)$/i)) { + this.openImageViewer(this.contextMenuFile); + } else if (this.contextMenuFile.name.match(/\.(mp4|avi|mov|wmv|flv|mkv|webm)$/i)) { + this.openVideoPlayer(this.contextMenuFile); + } else if (this.contextMenuFile.name.match(/\.(mp3|wav|flac|aac|ogg|m4a)$/i)) { + this.openAudioPlayer(this.contextMenuFile); + } + break; + case 'download': + this.downloadFile(this.contextMenuFile); + break; + case 'rename': + this.openRenameModal(this.contextMenuFile); + break; + case 'share': + this.openShareFileModal(this.contextMenuFile); + break; + case 'delete': + this.confirmDeleteFile(this.contextMenuFile); + break; + } + + this.hideContextMenu(); + }, + + // ===== 媒体预览功能 ===== + + // 获取媒体文件URL + getMediaUrl(file) { + const filePath = this.currentPath === '/' + ? `/${file.name}` + : `${this.currentPath}/${file.name}`; + + // SFTP存储且配置了HTTP下载URL,使用HTTP直接访问 + if (file.httpDownloadUrl) { + return file.httpDownloadUrl; + } + + // 本地存储或未配置HTTP URL,使用API下载 + return `${this.apiBase}/api/files/download?path=${encodeURIComponent(filePath)}&token=${this.token}`; + }, + + // 获取文件缩略图URL + getThumbnailUrl(file) { + if (!file || file.isDirectory) return null; + + // 检查是否是图片或视频 + const isImage = file.name.match(/\.(jpg|jpeg|png|gif|bmp|svg|webp)$/i); + const isVideo = file.name.match(/\.(mp4|avi|mov|wmv|flv|mkv|webm)$/i); + + if (!isImage && !isVideo) return null; + + return this.getMediaUrl(file); + }, + + // 打开图片预览 + openImageViewer(file) { + this.currentMediaUrl = this.getMediaUrl(file); + this.currentMediaName = file.name; + this.currentMediaType = 'image'; + this.showImageViewer = true; + }, + + // 打开视频播放器 + openVideoPlayer(file) { + this.currentMediaUrl = this.getMediaUrl(file); + this.currentMediaName = file.name; + this.currentMediaType = 'video'; + this.showVideoPlayer = true; + }, + + // 打开音频播放器 + openAudioPlayer(file) { + this.currentMediaUrl = this.getMediaUrl(file); + this.currentMediaName = file.name; + this.currentMediaType = 'audio'; + this.showAudioPlayer = true; + }, + + // 关闭媒体预览 + closeMediaViewer() { + this.showImageViewer = false; + this.showVideoPlayer = false; + this.showAudioPlayer = false; + this.currentMediaUrl = ''; + this.currentMediaName = ''; + this.currentMediaType = ''; + }, + + // 下载当前预览的媒体文件 + downloadCurrentMedia() { + if (!this.currentMediaUrl) return; + + // 创建临时a标签触发下载 + const link = document.createElement('a'); + link.href = this.currentMediaUrl; + link.setAttribute('download', this.currentMediaName); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }, + + + // 判断文件是否支持预览 + isPreviewable(file) { + if (!file || file.isDirectory) return false; + return file.name.match(/\.(jpg|jpeg|png|gif|bmp|svg|webp|mp4|avi|mov|wmv|flv|mkv|webm|mp3|wav|flac|aac|ogg|m4a)$/i); + }, + async deleteFile(file) { + try { + const response = await axios.post( + `${this.apiBase}/api/files/delete`, + { + fileName: file.name, + path: this.currentPath, + isDirectory: file.isDirectory + }, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + this.showToast('success', '成功', '文件已删除'); + this.loadFiles(this.currentPath); + } + } catch (error) { + console.error('删除失败:', error); + this.showToast('error', '错误', error.response?.data?.message || '删除失败'); + } + }, + + downloadUploadTool() { + try { + this.downloadingTool = true; + this.showToast('info', '提示', '正在生成上传工具,下载即将开始...'); + + // 使用标签下载,通过URL参数传递token,浏览器会显示下载进度 + const link = document.createElement('a'); + link.href = `${this.apiBase}/api/upload/download-tool?token=${this.token}`; + link.setAttribute('download', `玩玩云上传工具_${this.user.username}.zip`); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + // 延迟重置按钮状态,给下载一些启动时间 + setTimeout(() => { + this.downloadingTool = false; + this.showToast('success', '提示', '下载已开始,请查看浏览器下载进度'); + }, 2000); + } catch (error) { + console.error('下载上传工具失败:', error); + this.showToast('error', '错误', '下载失败'); + this.downloadingTool = false; + } + }, + + // ===== 分享功能 ===== + + openShareFileModal(file) { + this.shareFileForm.fileName = file.name; + this.shareFileForm.filePath = this.currentPath === '/' + ? file.name + : `${this.currentPath}/${file.name}`; + this.shareFileForm.password = ''; + this.shareFileForm.expiryType = 'never'; + this.shareFileForm.customDays = 7; + this.shareResult = null; // 清空上次的分享结果 + this.showShareFileModal = true; + }, + + async createShareAll() { + try { + const expiryDays = this.shareAllForm.expiryType === 'never' ? null : + this.shareAllForm.expiryType === 'custom' ? this.shareAllForm.customDays : + parseInt(this.shareAllForm.expiryType); + + const response = await axios.post( + `${this.apiBase}/api/share/create`, + { + share_type: 'all', + password: this.shareAllForm.password || null, + expiry_days: expiryDays + }, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + this.shareResult = response.data; + this.showToast('success', '成功', '分享链接已创建'); + this.loadShares(); + } + } catch (error) { + console.error('创建分享失败:', error); + this.showToast('error', '错误', error.response?.data?.message || '创建分享失败'); + } + }, + + async createShareFile() { + try { + const expiryDays = this.shareFileForm.expiryType === 'never' ? null : + this.shareFileForm.expiryType === 'custom' ? this.shareFileForm.customDays : + parseInt(this.shareFileForm.expiryType); + + const response = await axios.post( + `${this.apiBase}/api/share/create`, + { + share_type: 'file', + file_path: this.shareFileForm.filePath, + file_name: this.shareFileForm.fileName, + password: this.shareFileForm.password || null, + expiry_days: expiryDays + }, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + this.shareResult = response.data; + this.showToast('success', '成功', '文件分享链接已创建'); + this.loadShares(); + } + } catch (error) { + console.error('创建分享失败:', error); + this.showToast('error', '错误', error.response?.data?.message || '创建分享失败'); + } + }, + + + // ===== 文件上传 ===== + + handleFileSelect(event) { + const files = event.target.files; + if (files && files.length > 0) { + // 支持多文件上传 + Array.from(files).forEach(file => { + this.uploadFile(file); + }); + // 清空input,允许重复上传相同文件 + event.target.value = ''; + } + }, + + handleFileDrop(event) { + this.isDragging = false; + const file = event.dataTransfer.files[0]; + if (file) { + this.uploadFile(file); + } + }, + + async uploadFile(file) { + // 本地存储配额预检查 + if (this.storageType === 'local') { + const estimatedUsage = this.localUsed + file.size; + if (estimatedUsage > this.localQuota) { + this.showToast( + 'error', + '配额不足', + `文件大小 ${this.formatBytes(file.size)},剩余配额 ${this.formatBytes(this.localQuota - this.localUsed)},无法上传` + ); + return; + } + + // 如果使用率将超过90%,给出警告 + const willExceed90 = (estimatedUsage / this.localQuota) > 0.9; + if (willExceed90) { + const confirmed = confirm( + `警告:上传此文件后将使用 ${Math.round((estimatedUsage / this.localQuota) * 100)}% 的配额。是否继续?` + ); + if (!confirmed) return; + } + } + + const formData = new FormData(); + formData.append('file', file); + formData.append('path', this.currentPath); + + try { + // 设置上传文件名和进度 + this.uploadingFileName = file.name; + this.uploadProgress = 0; + this.uploadedBytes = 0; + this.totalBytes = 0; + + const response = await axios.post(`${this.apiBase}/api/upload`, formData, { + headers: { + 'Authorization': `Bearer ${this.token}`, + 'Content-Type': 'multipart/form-data' + }, + onUploadProgress: (progressEvent) => { + this.uploadProgress = Math.round((progressEvent.loaded * 100) / progressEvent.total); + this.uploadedBytes = progressEvent.loaded; + this.totalBytes = progressEvent.total; + } + }); + + if (response.data.success) { + // 显示成功提示 + this.showToast('success', '上传成功', `文件 ${file.name} 已上传`); + + // 重置上传进度 + this.uploadProgress = 0; + this.uploadedBytes = 0; + this.totalBytes = 0; + this.uploadingFileName = ''; + + // 自动刷新文件列表 + await this.loadFiles(this.currentPath); + } + } catch (error) { + console.error('上传失败:', error); + + // 重置上传进度 + this.uploadProgress = 0; + this.uploadedBytes = 0; + this.totalBytes = 0; + this.uploadingFileName = ''; + + // 处理文件大小超限错误 + if (error.response?.status === 413) { + const errorData = error.response.data; + + // 判断响应是JSON还是HTML(Nginx返回HTML,Backend返回JSON) + if (typeof errorData === 'object' && errorData.maxSize && errorData.fileSize) { + // Backend返回的JSON响应 + const maxSizeMB = Math.round(errorData.maxSize / (1024 * 1024)); + const fileSizeMB = Math.round(errorData.fileSize / (1024 * 1024)); + this.showToast( + 'error', + '文件超过上传限制', + `文件大小 ${fileSizeMB}MB 超过限制 ${maxSizeMB}MB` + ); + } else { + // Nginx返回的HTML响应,显示通用消息 + const fileSizeMB = Math.round(file.size / (1024 * 1024)); + this.showToast( + 'error', + '文件超过上传限制', + `文件大小 ${fileSizeMB}MB 超过系统限制,请联系管理员` + ); + } + } else { + this.showToast('error', '上传失败', error.response?.data?.message || error.message); + } + } + }, + + // ===== 分享管理 ===== + + async loadShares() { + try { + const response = await axios.get(`${this.apiBase}/api/share/my`, { + headers: { Authorization: `Bearer ${this.token}` } + }); + + if (response.data.success) { + this.shares = response.data.shares; + } + } catch (error) { + console.error('加载分享列表失败:', error); + alert('加载分享列表失败: ' + (error.response?.data?.message || error.message)); + } + }, + + async createShare() { + this.shareForm.path = this.currentPath; + + try { + const response = await axios.post(`${this.apiBase}/api/share/create`, this.shareForm, { + headers: { Authorization: `Bearer ${this.token}` } + }); + + if (response.data.success) { + this.shareResult = response.data; + this.loadShares(); + } + } catch (error) { + console.error('创建分享失败:', error); + alert('创建分享失败: ' + (error.response?.data?.message || error.message)); + } + }, + + async deleteShare(id) { + if (!confirm('确定要删除这个分享吗?')) return; + + try { + const response = await axios.delete(`${this.apiBase}/api/share/${id}`, { + headers: { Authorization: `Bearer ${this.token}` } + }); + + if (response.data.success) { + alert('分享已删除'); + this.loadShares(); + } + } catch (error) { + console.error('删除分享失败:', error); + alert('删除分享失败: ' + (error.response?.data?.message || error.message)); + } + }, + + copyShareLink(url) { + // 复制分享链接到剪贴板 + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(url).then(() => { + this.showToast('success', '成功', '分享链接已复制到剪贴板'); + }).catch(() => { + this.fallbackCopyToClipboard(url); + }); + } else { + this.fallbackCopyToClipboard(url); + } + }, + + fallbackCopyToClipboard(text) { + // 备用复制方法 + const textArea = document.createElement('textarea'); + textArea.value = text; + textArea.style.position = 'fixed'; + textArea.style.left = '-999999px'; + document.body.appendChild(textArea); + textArea.select(); + try { + document.execCommand('copy'); + this.showToast('success', '成功', '分享链接已复制到剪贴板'); + } catch (err) { + this.showToast('error', '错误', '复制失败,请手动复制'); + } + document.body.removeChild(textArea); + }, + + // ===== 管理员功能 ===== + + async loadUsers() { + try { + const response = await axios.get(`${this.apiBase}/api/admin/users`, { + headers: { Authorization: `Bearer ${this.token}` } + }); + + if (response.data.success) { + this.adminUsers = response.data.users; + } + } catch (error) { + console.error('加载用户列表失败:', error); + alert('加载用户列表失败: ' + (error.response?.data?.message || error.message)); + } + }, + + async banUser(userId, banned) { + const action = banned ? '封禁' : '解封'; + if (!confirm(`确定要${action}这个用户吗?`)) return; + + try { + const response = await axios.post( + `${this.apiBase}/api/admin/users/${userId}/ban`, + { banned }, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + alert(response.data.message); + this.loadUsers(); + } + } catch (error) { + console.error('操作失败:', error); + alert('操作失败: ' + (error.response?.data?.message || error.message)); + } + }, + + async deleteUser(userId) { + if (!confirm('确定要删除这个用户吗?此操作不可恢复!')) return; + + try { + const response = await axios.delete(`${this.apiBase}/api/admin/users/${userId}`, { + headers: { Authorization: `Bearer ${this.token}` } + }); + + if (response.data.success) { + alert('用户已删除'); + this.loadUsers(); + } + } catch (error) { + console.error('删除用户失败:', error); + alert('删除用户失败: ' + (error.response?.data?.message || error.message)); + } + }, + + // ===== 忘记密码功能 ===== + + async requestPasswordReset() { + if (!this.forgotPasswordForm.username) { + this.showToast('error', '错误', '请输入用户名'); + return; + } + if (!this.forgotPasswordForm.new_password || this.forgotPasswordForm.new_password.length < 6) { + this.showToast('error', '错误', '新密码至少6个字符'); + return; + } + + try { + const response = await axios.post( + `${this.apiBase}/api/password-reset/request`, + this.forgotPasswordForm + ); + + if (response.data.success) { + this.showToast('success', '成功', '密码重置请求已提交,请等待管理员审核'); + this.showForgotPasswordModal = false; + this.forgotPasswordForm = { username: '', new_password: '' }; + } + } catch (error) { + console.error('提交密码重置请求失败:', error); + this.showToast('error', '错误', error.response?.data?.message || '提交失败'); + } + }, + + // ===== 管理员:密码重置审核 ===== + + async loadPasswordResetRequests() { + try { + const response = await axios.get(`${this.apiBase}/api/admin/password-reset/pending`, { + headers: { Authorization: `Bearer ${this.token}` } + }); + + if (response.data.success) { + this.passwordResetRequests = response.data.requests; + } + } catch (error) { + console.error('加载密码重置请求失败:', error); + this.showToast('error', '错误', '加载密码重置请求失败'); + } + }, + + async reviewPasswordReset(requestId, approved) { + const action = approved ? '批准' : '拒绝'; + if (!confirm(`确定要${action}这个密码重置请求吗?`)) return; + + try { + const response = await axios.post( + `${this.apiBase}/api/admin/password-reset/${requestId}/review`, + { approved }, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + this.showToast('success', '成功', response.data.message); + this.loadPasswordResetRequests(); + } + } catch (error) { + console.error('审核失败:', error); + this.showToast('error', '错误', error.response?.data?.message || '审核失败'); + } + }, + + // ===== 管理员:文件审查功能 ===== + + async openFileInspection(user) { + this.inspectionUser = user; + this.inspectionPath = '/'; + this.showFileInspectionModal = true; + await this.loadUserFiles('/'); + }, + + async loadUserFiles(path) { + this.inspectionLoading = true; + this.inspectionPath = path; + + try { + const response = await axios.get( + `${this.apiBase}/api/admin/users/${this.inspectionUser.id}/files`, + { + params: { path }, + headers: { Authorization: `Bearer ${this.token}` } + } + ); + + if (response.data.success) { + this.inspectionFiles = response.data.items; + } + } catch (error) { + console.error('加载用户文件失败:', error); + this.showToast('error', '错误', error.response?.data?.message || '加载文件失败'); + } finally { + this.inspectionLoading = false; + } + }, + + handleInspectionFileClick(file) { + if (file.isDirectory) { + const newPath = this.inspectionPath === '/' + ? `/${file.name}` + : `${this.inspectionPath}/${file.name}`; + this.loadUserFiles(newPath); + } + }, + + navigateInspectionToRoot() { + this.loadUserFiles('/'); + }, + + navigateInspectionUp() { + if (this.inspectionPath === '/') return; + const lastSlash = this.inspectionPath.lastIndexOf('/'); + const parentPath = lastSlash > 0 ? this.inspectionPath.substring(0, lastSlash) : '/'; + this.loadUserFiles(parentPath); + }, + + // ===== 存储管理 ===== + + // 加载用户个人资料(包含存储信息) + async loadUserProfile() { + try { + const response = await axios.get( + `${this.apiBase}/api/user/profile`, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success && response.data.user) { + const user = response.data.user; + this.localQuota = user.local_storage_quota || 0; + this.localUsed = user.local_storage_used || 0; + this.storagePermission = user.storage_permission || 'sftp_only'; + this.storageType = user.current_storage_type || 'sftp'; + } + } catch (error) { + console.error('加载用户资料失败:', error); + } + }, + + // 用户切换存储方式 + async switchStorage(type) { + if (!confirm(`确定要切换到${type === 'local' ? '本地存储' : 'SFTP存储'}吗?`)) { + return; + } + + try { + const response = await axios.post( + `${this.apiBase}/api/user/switch-storage`, + { storage_type: type }, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + this.storageType = type; + this.showToast('success', '成功', `已切换到${type === 'local' ? '本地存储' : 'SFTP存储'}`); + + // 重新加载文件列表 + if (this.currentView === 'files') { + this.loadFiles(this.currentPath); + } + } + } catch (error) { + console.error('切换存储失败:', error); + this.showToast('error', '错误', error.response?.data?.message || '切换存储失败'); + } + }, + + // 管理员:打开编辑用户存储权限模态框 + openEditStorageModal(user) { + this.editStorageForm.userId = user.id; + this.editStorageForm.username = user.username; + this.editStorageForm.storage_permission = user.storage_permission || 'sftp_only'; + + // 智能识别配额单位 + const quotaBytes = user.local_storage_quota || 1073741824; + const quotaMB = quotaBytes / 1024 / 1024; + const quotaGB = quotaMB / 1024; + + // 如果配额能被1024整除且大于等于1GB,使用GB单位,否则使用MB + if (quotaMB >= 1024 && quotaMB % 1024 === 0) { + this.editStorageForm.local_storage_quota_value = quotaGB; + this.editStorageForm.quota_unit = 'GB'; + } else { + this.editStorageForm.local_storage_quota_value = Math.round(quotaMB); + this.editStorageForm.quota_unit = 'MB'; + } + + this.showEditStorageModal = true; + }, + + // 管理员:更新用户存储权限 + async updateUserStorage() { + try { + // 根据单位计算字节数 + let quotaBytes; + if (this.editStorageForm.quota_unit === 'GB') { + quotaBytes = this.editStorageForm.local_storage_quota_value * 1024 * 1024 * 1024; + } else { + quotaBytes = this.editStorageForm.local_storage_quota_value * 1024 * 1024; + } + + const response = await axios.post( + `${this.apiBase}/api/admin/users/${this.editStorageForm.userId}/storage-permission`, + { + storage_permission: this.editStorageForm.storage_permission, + local_storage_quota: quotaBytes + }, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + this.showToast('success', '成功', '存储权限已更新'); + this.showEditStorageModal = false; + this.loadUsers(); + } + } catch (error) { + console.error('更新存储权限失败:', error); + this.showToast('error', '错误', error.response?.data?.message || '更新失败'); + } + }, + + // ===== 工具函数 ===== + + formatBytes(bytes) { + if (bytes === 0) return '0 B'; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]; + }, + + formatDate(dateString) { + if (!dateString) return '-'; + const date = new Date(dateString); + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + return `${year}-${month}-${day} ${hours}:${minutes}`; + }, + + // ===== Toast通知 ===== + + showToast(type, title, message) { + const toast = { + id: ++this.toastIdCounter, + type, + title, + message, + icon: type === 'error' ? 'fas fa-circle-exclamation' : type === 'success' ? 'fas fa-circle-check' : 'fas fa-circle-info', + hiding: false + }; + + this.toasts.push(toast); + + // 4.5秒后开始淡出动画 + setTimeout(() => { + const index = this.toasts.findIndex(t => t.id === toast.id); + if (index !== -1) { + this.toasts[index].hiding = true; + + // 0.5秒后移除(动画时长) + setTimeout(() => { + const removeIndex = this.toasts.findIndex(t => t.id === toast.id); + if (removeIndex !== -1) { + this.toasts.splice(removeIndex, 1); + } + }, 500); + } + }, 4500); + }, + + // ===== 系统设置管理 ===== + + async loadSystemSettings() { + try { + const response = await axios.get(`${this.apiBase}/api/admin/settings`, { + headers: { Authorization: `Bearer ${this.token}` } + }); + + if (response.data.success) { + const settings = response.data.settings; + this.systemSettings.maxUploadSizeMB = Math.round(settings.max_upload_size / (1024 * 1024)); + } + } catch (error) { + console.error('加载系统设置失败:', error); + this.showToast('error', '错误', '加载系统设置失败'); + } + }, + + async loadServerStorageStats() { + try { + const response = await axios.get(`${this.apiBase}/api/admin/storage-stats`, { + headers: { Authorization: `Bearer ${this.token}` } + }); + + if (response.data.success) { + this.serverStorageStats = response.data.stats; + } + } catch (error) { + console.error('加载服务器存储统计失败:', error); + this.showToast('error', '错误', '加载服务器存储统计失败'); + } + }, + + async updateSystemSettings() { + try { + const maxUploadSize = parseInt(this.systemSettings.maxUploadSizeMB) * 1024 * 1024; + + const response = await axios.post( + `${this.apiBase}/api/admin/settings`, + { max_upload_size: maxUploadSize }, + { headers: { Authorization: `Bearer ${this.token}` } } + ); + + if (response.data.success) { + this.showToast('success', '成功', '系统设置已更新'); + } + } catch (error) { + console.error('更新系统设置失败:', error); + this.showToast('error', '错误', '更新系统设置失败'); + } + } + }, + + mounted() { + // 阻止全局拖拽默认行为(防止拖到区域外打开新页面) + window.addEventListener("dragover", (e) => { + e.preventDefault(); + }); + window.addEventListener("drop", (e) => { + e.preventDefault(); + }); + + + // 添加全局 dragend 监听(拖拽结束时总是隐藏覆盖层) + window.addEventListener("dragend", () => { + this.isDragging = false; + }); + + // 添加 ESC 键监听(按 ESC 关闭拖拽覆盖层) + window.addEventListener("keydown", (e) => { + if (e.key === "Escape" && this.isDragging) { + this.isDragging = false; + } + }); + + // 检查URL参数 + this.checkUrlParams(); + // 检查登录状态 + this.checkLoginStatus(); + }, + + watch: { + currentView(newView) { + if (newView === 'shares') { + this.loadShares(); + } else if (newView === 'admin' && this.user?.is_admin) { + this.loadUsers(); + this.loadSystemSettings(); + this.loadPasswordResetRequests(); + this.loadServerStorageStats(); + } else if (newView === 'settings' && this.user && !this.user.is_admin) { + // 普通用户进入设置页面时加载SFTP配置 + this.loadFtpConfig(); + } + } + } +}).mount('#app'); diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..3563b19 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,191 @@ + + + + + + 玩玩云 - 主页 + + + + + + +
+

玩玩云管理平台

+

简单、安全、高效的文件管理解决方案
连接你的SFTP服务器,随时随地管理文件

+ +
+ +
+
+
+
连接你的SFTP
+
支持连接任何SFTP服务器,数据存储在你自己的服务器上,更安全可靠
+
+
+
+
轻松上传下载
+
网盘式界面,拖拽上传,快速下载,文件管理从未如此简单
+
+
+
+
一键分享
+
生成分享链接,可设置密码保护,轻松分享文件给朋友
+
+
+
+
安全可靠
+
JWT认证,密码加密存储,保护你的数据安全
+
+
+
+
响应式设计
+
完美支持桌面和移动设备,随时随地访问你的文件
+
+
+
+
分享统计
+
查看分享链接的访问次数和下载统计,了解分享效果
+
+
+ + + + diff --git a/frontend/libs/axios.min.js b/frontend/libs/axios.min.js new file mode 100644 index 0000000..2b482fa --- /dev/null +++ b/frontend/libs/axios.min.js @@ -0,0 +1,3 @@ +/*! Axios v1.13.2 Copyright (c) 2025 Matt Zabriskie and contributors */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).axios=t()}(this,(function(){"use strict";function e(e){var r,n;function o(r,n){try{var a=e[r](n),u=a.value,s=u instanceof t;Promise.resolve(s?u.v:u).then((function(t){if(s){var n="return"===r?"return":"next";if(!u.k||t.done)return o(n,t);t=e[n](t).value}i(a.done?"return":"normal",t)}),(function(e){o("throw",e)}))}catch(e){i("throw",e)}}function i(e,t){switch(e){case"return":r.resolve({value:t,done:!0});break;case"throw":r.reject(t);break;default:r.resolve({value:t,done:!1})}(r=r.next)?o(r.key,r.arg):n=null}this._invoke=function(e,t){return new Promise((function(i,a){var u={key:e,arg:t,resolve:i,reject:a,next:null};n?n=n.next=u:(r=n=u,o(e,t))}))},"function"!=typeof e.return&&(this.return=void 0)}function t(e,t){this.v=e,this.k=t}function r(e){var r={},n=!1;function o(r,o){return n=!0,o=new Promise((function(t){t(e[r](o))})),{done:!1,value:new t(o,1)}}return r["undefined"!=typeof Symbol&&Symbol.iterator||"@@iterator"]=function(){return this},r.next=function(e){return n?(n=!1,e):o("next",e)},"function"==typeof e.throw&&(r.throw=function(e){if(n)throw n=!1,e;return o("throw",e)}),"function"==typeof e.return&&(r.return=function(e){return n?(n=!1,e):o("return",e)}),r}function n(e){var t,r,n,i=2;for("undefined"!=typeof Symbol&&(r=Symbol.asyncIterator,n=Symbol.iterator);i--;){if(r&&null!=(t=e[r]))return t.call(e);if(n&&null!=(t=e[n]))return new o(t.call(e));r="@@asyncIterator",n="@@iterator"}throw new TypeError("Object is not async iterable")}function o(e){function t(e){if(Object(e)!==e)return Promise.reject(new TypeError(e+" is not an object."));var t=e.done;return Promise.resolve(e.value).then((function(e){return{value:e,done:t}}))}return o=function(e){this.s=e,this.n=e.next},o.prototype={s:null,n:null,next:function(){return t(this.n.apply(this.s,arguments))},return:function(e){var r=this.s.return;return void 0===r?Promise.resolve({value:e,done:!0}):t(r.apply(this.s,arguments))},throw:function(e){var r=this.s.return;return void 0===r?Promise.reject(e):t(r.apply(this.s,arguments))}},new o(e)}function i(e){return new t(e,0)}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function u(e){for(var t=1;t=0;--i){var a=this.tryEntries[i],u=a.completion;if("root"===a.tryLoc)return o("end");if(a.tryLoc<=this.prev){var s=n.call(a,"catchLoc"),c=n.call(a,"finallyLoc");if(s&&c){if(this.prev=0;--r){var o=this.tryEntries[r];if(o.tryLoc<=this.prev&&n.call(o,"finallyLoc")&&this.prev=0;--t){var r=this.tryEntries[t];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),A(r),y}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.tryLoc===e){var n=r.completion;if("throw"===n.type){var o=n.arg;A(r)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(t,r,n){return this.delegate={iterator:L(t),resultName:r,nextLoc:n},"next"===this.method&&(this.arg=e),y}},t}function c(e){var t=function(e,t){if("object"!=typeof e||!e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:String(t)}function f(e){return f="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},f(e)}function l(t){return function(){return new e(t.apply(this,arguments))}}function p(e,t,r,n,o,i,a){try{var u=e[i](a),s=u.value}catch(e){return void r(e)}u.done?t(s):Promise.resolve(s).then(n,o)}function d(e){return function(){var t=this,r=arguments;return new Promise((function(n,o){var i=e.apply(t,r);function a(e){p(i,n,o,a,u,"next",e)}function u(e){p(i,n,o,a,u,"throw",e)}a(void 0)}))}}function h(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function v(e,t){for(var r=0;re.length)&&(t=e.length);for(var r=0,n=new Array(t);r2&&void 0!==arguments[2]?arguments[2]:{},i=o.allOwnKeys,a=void 0!==i&&i;if(null!=e)if("object"!==f(e)&&(e=[e]),L(e))for(r=0,n=e.length;r0;)if(t===(r=n[o]).toLowerCase())return r;return null}var Q="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:global,Z=function(e){return!N(e)&&e!==Q};var ee,te=(ee="undefined"!=typeof Uint8Array&&R(Uint8Array),function(e){return ee&&e instanceof ee}),re=A("HTMLFormElement"),ne=function(e){var t=Object.prototype.hasOwnProperty;return function(e,r){return t.call(e,r)}}(),oe=A("RegExp"),ie=function(e,t){var r=Object.getOwnPropertyDescriptors(e),n={};$(r,(function(r,o){var i;!1!==(i=t(r,o,e))&&(n[o]=i||r)})),Object.defineProperties(e,n)};var ae,ue,se,ce,fe=A("AsyncFunction"),le=(ae="function"==typeof setImmediate,ue=F(Q.postMessage),ae?setImmediate:ue?(se="axios@".concat(Math.random()),ce=[],Q.addEventListener("message",(function(e){var t=e.source,r=e.data;t===Q&&r===se&&ce.length&&ce.shift()()}),!1),function(e){ce.push(e),Q.postMessage(se,"*")}):function(e){return setTimeout(e)}),pe="undefined"!=typeof queueMicrotask?queueMicrotask.bind(Q):"undefined"!=typeof process&&process.nextTick||le,de={isArray:L,isArrayBuffer:_,isBuffer:C,isFormData:function(e){var t;return e&&("function"==typeof FormData&&e instanceof FormData||F(e.append)&&("formdata"===(t=j(e))||"object"===t&&F(e.toString)&&"[object FormData]"===e.toString()))},isArrayBufferView:function(e){return"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&_(e.buffer)},isString:U,isNumber:B,isBoolean:function(e){return!0===e||!1===e},isObject:D,isPlainObject:I,isEmptyObject:function(e){if(!D(e)||C(e))return!1;try{return 0===Object.keys(e).length&&Object.getPrototypeOf(e)===Object.prototype}catch(e){return!1}},isReadableStream:K,isRequest:V,isResponse:G,isHeaders:X,isUndefined:N,isDate:q,isFile:M,isBlob:z,isRegExp:oe,isFunction:F,isStream:function(e){return D(e)&&F(e.pipe)},isURLSearchParams:J,isTypedArray:te,isFileList:H,forEach:$,merge:function e(){for(var t=Z(this)&&this||{},r=t.caseless,n=t.skipUndefined,o={},i=function(t,i){var a=r&&Y(o,i)||i;I(o[a])&&I(t)?o[a]=e(o[a],t):I(t)?o[a]=e({},t):L(t)?o[a]=t.slice():n&&N(t)||(o[a]=t)},a=0,u=arguments.length;a3&&void 0!==arguments[3]?arguments[3]:{},o=n.allOwnKeys;return $(t,(function(t,n){r&&F(t)?e[n]=O(t,r):e[n]=t}),{allOwnKeys:o}),e},trim:function(e){return e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")},stripBOM:function(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e},inherits:function(e,t,r,n){e.prototype=Object.create(t.prototype,n),e.prototype.constructor=e,Object.defineProperty(e,"super",{value:t.prototype}),r&&Object.assign(e.prototype,r)},toFlatObject:function(e,t,r,n){var o,i,a,u={};if(t=t||{},null==e)return t;do{for(i=(o=Object.getOwnPropertyNames(e)).length;i-- >0;)a=o[i],n&&!n(a,e,t)||u[a]||(t[a]=e[a],u[a]=!0);e=!1!==r&&R(e)}while(e&&(!r||r(e,t))&&e!==Object.prototype);return t},kindOf:j,kindOfTest:A,endsWith:function(e,t,r){e=String(e),(void 0===r||r>e.length)&&(r=e.length),r-=t.length;var n=e.indexOf(t,r);return-1!==n&&n===r},toArray:function(e){if(!e)return null;if(L(e))return e;var t=e.length;if(!B(t))return null;for(var r=new Array(t);t-- >0;)r[t]=e[t];return r},forEachEntry:function(e,t){for(var r,n=(e&&e[k]).call(e);(r=n.next())&&!r.done;){var o=r.value;t.call(e,o[0],o[1])}},matchAll:function(e,t){for(var r,n=[];null!==(r=e.exec(t));)n.push(r);return n},isHTMLForm:re,hasOwnProperty:ne,hasOwnProp:ne,reduceDescriptors:ie,freezeMethods:function(e){ie(e,(function(t,r){if(F(e)&&-1!==["arguments","caller","callee"].indexOf(r))return!1;var n=e[r];F(n)&&(t.enumerable=!1,"writable"in t?t.writable=!1:t.set||(t.set=function(){throw Error("Can not rewrite read-only method '"+r+"'")}))}))},toObjectSet:function(e,t){var r={},n=function(e){e.forEach((function(e){r[e]=!0}))};return L(e)?n(e):n(String(e).split(t)),r},toCamelCase:function(e){return e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,(function(e,t,r){return t.toUpperCase()+r}))},noop:function(){},toFiniteNumber:function(e,t){return null!=e&&Number.isFinite(e=+e)?e:t},findKey:Y,global:Q,isContextDefined:Z,isSpecCompliantForm:function(e){return!!(e&&F(e.append)&&"FormData"===e[T]&&e[k])},toJSONObject:function(e){var t=new Array(10);return function e(r,n){if(D(r)){if(t.indexOf(r)>=0)return;if(C(r))return r;if(!("toJSON"in r)){t[n]=r;var o=L(r)?[]:{};return $(r,(function(t,r){var i=e(t,n+1);!N(i)&&(o[r]=i)})),t[n]=void 0,o}}return r}(e,0)},isAsyncFn:fe,isThenable:function(e){return e&&(D(e)||F(e))&&F(e.then)&&F(e.catch)},setImmediate:le,asap:pe,isIterable:function(e){return null!=e&&F(e[k])}};function he(e,t,r,n,o){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack,this.message=e,this.name="AxiosError",t&&(this.code=t),r&&(this.config=r),n&&(this.request=n),o&&(this.response=o,this.status=o.status?o.status:null)}de.inherits(he,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:de.toJSONObject(this.config),code:this.code,status:this.status}}});var ve=he.prototype,ye={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach((function(e){ye[e]={value:e}})),Object.defineProperties(he,ye),Object.defineProperty(ve,"isAxiosError",{value:!0}),he.from=function(e,t,r,n,o,i){var a=Object.create(ve);de.toFlatObject(e,a,(function(e){return e!==Error.prototype}),(function(e){return"isAxiosError"!==e}));var u=e&&e.message?e.message:"Error",s=null==t&&e?e.code:t;return he.call(a,u,s,r,n,o),e&&null==a.cause&&Object.defineProperty(a,"cause",{value:e,configurable:!0}),a.name=e&&e.name||"Error",i&&Object.assign(a,i),a};function me(e){return de.isPlainObject(e)||de.isArray(e)}function be(e){return de.endsWith(e,"[]")?e.slice(0,-2):e}function ge(e,t,r){return e?e.concat(t).map((function(e,t){return e=be(e),!r&&t?"["+e+"]":e})).join(r?".":""):t}var we=de.toFlatObject(de,{},null,(function(e){return/^is[A-Z]/.test(e)}));function Ee(e,t,r){if(!de.isObject(e))throw new TypeError("target must be an object");t=t||new FormData;var n=(r=de.toFlatObject(r,{metaTokens:!0,dots:!1,indexes:!1},!1,(function(e,t){return!de.isUndefined(t[e])}))).metaTokens,o=r.visitor||c,i=r.dots,a=r.indexes,u=(r.Blob||"undefined"!=typeof Blob&&Blob)&&de.isSpecCompliantForm(t);if(!de.isFunction(o))throw new TypeError("visitor must be a function");function s(e){if(null===e)return"";if(de.isDate(e))return e.toISOString();if(de.isBoolean(e))return e.toString();if(!u&&de.isBlob(e))throw new he("Blob is not supported. Use a Buffer instead.");return de.isArrayBuffer(e)||de.isTypedArray(e)?u&&"function"==typeof Blob?new Blob([e]):Buffer.from(e):e}function c(e,r,o){var u=e;if(e&&!o&&"object"===f(e))if(de.endsWith(r,"{}"))r=n?r:r.slice(0,-2),e=JSON.stringify(e);else if(de.isArray(e)&&function(e){return de.isArray(e)&&!e.some(me)}(e)||(de.isFileList(e)||de.endsWith(r,"[]"))&&(u=de.toArray(e)))return r=be(r),u.forEach((function(e,n){!de.isUndefined(e)&&null!==e&&t.append(!0===a?ge([r],n,i):null===a?r:r+"[]",s(e))})),!1;return!!me(e)||(t.append(ge(o,r,i),s(e)),!1)}var l=[],p=Object.assign(we,{defaultVisitor:c,convertValue:s,isVisitable:me});if(!de.isObject(e))throw new TypeError("data must be an object");return function e(r,n){if(!de.isUndefined(r)){if(-1!==l.indexOf(r))throw Error("Circular reference detected in "+n.join("."));l.push(r),de.forEach(r,(function(r,i){!0===(!(de.isUndefined(r)||null===r)&&o.call(t,r,de.isString(i)?i.trim():i,n,p))&&e(r,n?n.concat(i):[i])})),l.pop()}}(e),t}function Oe(e){var t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,(function(e){return t[e]}))}function Se(e,t){this._pairs=[],e&&Ee(e,this,t)}var xe=Se.prototype;function Re(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+")}function ke(e,t,r){if(!t)return e;var n=r&&r.encode||Re;de.isFunction(r)&&(r={serialize:r});var o,i=r&&r.serialize;if(o=i?i(t,r):de.isURLSearchParams(t)?t.toString():new Se(t,r).toString(n)){var a=e.indexOf("#");-1!==a&&(e=e.slice(0,a)),e+=(-1===e.indexOf("?")?"?":"&")+o}return e}xe.append=function(e,t){this._pairs.push([e,t])},xe.toString=function(e){var t=e?function(t){return e.call(this,t,Oe)}:Oe;return this._pairs.map((function(e){return t(e[0])+"="+t(e[1])}),"").join("&")};var Te=function(){function e(){h(this,e),this.handlers=[]}return y(e,[{key:"use",value:function(e,t,r){return this.handlers.push({fulfilled:e,rejected:t,synchronous:!!r&&r.synchronous,runWhen:r?r.runWhen:null}),this.handlers.length-1}},{key:"eject",value:function(e){this.handlers[e]&&(this.handlers[e]=null)}},{key:"clear",value:function(){this.handlers&&(this.handlers=[])}},{key:"forEach",value:function(e){de.forEach(this.handlers,(function(t){null!==t&&e(t)}))}}]),e}(),je={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},Ae={isBrowser:!0,classes:{URLSearchParams:"undefined"!=typeof URLSearchParams?URLSearchParams:Se,FormData:"undefined"!=typeof FormData?FormData:null,Blob:"undefined"!=typeof Blob?Blob:null},protocols:["http","https","file","blob","url","data"]},Pe="undefined"!=typeof window&&"undefined"!=typeof document,Le="object"===("undefined"==typeof navigator?"undefined":f(navigator))&&navigator||void 0,Ne=Pe&&(!Le||["ReactNative","NativeScript","NS"].indexOf(Le.product)<0),Ce="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope&&"function"==typeof self.importScripts,_e=Pe&&window.location.href||"http://localhost",Ue=u(u({},Object.freeze({__proto__:null,hasBrowserEnv:Pe,hasStandardBrowserWebWorkerEnv:Ce,hasStandardBrowserEnv:Ne,navigator:Le,origin:_e})),Ae);function Fe(e){function t(e,r,n,o){var i=e[o++];if("__proto__"===i)return!0;var a=Number.isFinite(+i),u=o>=e.length;return i=!i&&de.isArray(n)?n.length:i,u?(de.hasOwnProp(n,i)?n[i]=[n[i],r]:n[i]=r,!a):(n[i]&&de.isObject(n[i])||(n[i]=[]),t(e,r,n[i],o)&&de.isArray(n[i])&&(n[i]=function(e){var t,r,n={},o=Object.keys(e),i=o.length;for(t=0;t-1,i=de.isObject(e);if(i&&de.isHTMLForm(e)&&(e=new FormData(e)),de.isFormData(e))return o?JSON.stringify(Fe(e)):e;if(de.isArrayBuffer(e)||de.isBuffer(e)||de.isStream(e)||de.isFile(e)||de.isBlob(e)||de.isReadableStream(e))return e;if(de.isArrayBufferView(e))return e.buffer;if(de.isURLSearchParams(e))return t.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),e.toString();if(i){if(n.indexOf("application/x-www-form-urlencoded")>-1)return function(e,t){return Ee(e,new Ue.classes.URLSearchParams,u({visitor:function(e,t,r,n){return Ue.isNode&&de.isBuffer(e)?(this.append(t,e.toString("base64")),!1):n.defaultVisitor.apply(this,arguments)}},t))}(e,this.formSerializer).toString();if((r=de.isFileList(e))||n.indexOf("multipart/form-data")>-1){var a=this.env&&this.env.FormData;return Ee(r?{"files[]":e}:e,a&&new a,this.formSerializer)}}return i||o?(t.setContentType("application/json",!1),function(e,t,r){if(de.isString(e))try{return(t||JSON.parse)(e),de.trim(e)}catch(e){if("SyntaxError"!==e.name)throw e}return(r||JSON.stringify)(e)}(e)):e}],transformResponse:[function(e){var t=this.transitional||Be.transitional,r=t&&t.forcedJSONParsing,n="json"===this.responseType;if(de.isResponse(e)||de.isReadableStream(e))return e;if(e&&de.isString(e)&&(r&&!this.responseType||n)){var o=!(t&&t.silentJSONParsing)&&n;try{return JSON.parse(e,this.parseReviver)}catch(e){if(o){if("SyntaxError"===e.name)throw he.from(e,he.ERR_BAD_RESPONSE,this,null,this.response);throw e}}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:Ue.classes.FormData,Blob:Ue.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":void 0}}};de.forEach(["delete","get","head","post","put","patch"],(function(e){Be.headers[e]={}}));var De=Be,Ie=de.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),qe=Symbol("internals");function Me(e){return e&&String(e).trim().toLowerCase()}function ze(e){return!1===e||null==e?e:de.isArray(e)?e.map(ze):String(e)}function He(e,t,r,n,o){return de.isFunction(n)?n.call(this,t,r):(o&&(t=r),de.isString(t)?de.isString(n)?-1!==t.indexOf(n):de.isRegExp(n)?n.test(t):void 0:void 0)}var Je=function(e,t){function r(e){h(this,r),e&&this.set(e)}return y(r,[{key:"set",value:function(e,t,r){var n=this;function o(e,t,r){var o=Me(t);if(!o)throw new Error("header name must be a non-empty string");var i=de.findKey(n,o);(!i||void 0===n[i]||!0===r||void 0===r&&!1!==n[i])&&(n[i||t]=ze(e))}var i=function(e,t){return de.forEach(e,(function(e,r){return o(e,r,t)}))};if(de.isPlainObject(e)||e instanceof this.constructor)i(e,t);else if(de.isString(e)&&(e=e.trim())&&!/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim()))i(function(e){var t,r,n,o={};return e&&e.split("\n").forEach((function(e){n=e.indexOf(":"),t=e.substring(0,n).trim().toLowerCase(),r=e.substring(n+1).trim(),!t||o[t]&&Ie[t]||("set-cookie"===t?o[t]?o[t].push(r):o[t]=[r]:o[t]=o[t]?o[t]+", "+r:r)})),o}(e),t);else if(de.isObject(e)&&de.isIterable(e)){var a,u,s,c={},f=function(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=w(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,o=function(){};return{s:o,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,u=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return a=e.done,e},e:function(e){u=!0,i=e},f:function(){try{a||null==r.return||r.return()}finally{if(u)throw i}}}}(e);try{for(f.s();!(s=f.n()).done;){var l=s.value;if(!de.isArray(l))throw TypeError("Object iterator must return a key-value pair");c[u=l[0]]=(a=c[u])?de.isArray(a)?[].concat(g(a),[l[1]]):[a,l[1]]:l[1]}}catch(e){f.e(e)}finally{f.f()}i(c,t)}else null!=e&&o(t,e,r);return this}},{key:"get",value:function(e,t){if(e=Me(e)){var r=de.findKey(this,e);if(r){var n=this[r];if(!t)return n;if(!0===t)return function(e){for(var t,r=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;t=n.exec(e);)r[t[1]]=t[2];return r}(n);if(de.isFunction(t))return t.call(this,n,r);if(de.isRegExp(t))return t.exec(n);throw new TypeError("parser must be boolean|regexp|function")}}}},{key:"has",value:function(e,t){if(e=Me(e)){var r=de.findKey(this,e);return!(!r||void 0===this[r]||t&&!He(0,this[r],r,t))}return!1}},{key:"delete",value:function(e,t){var r=this,n=!1;function o(e){if(e=Me(e)){var o=de.findKey(r,e);!o||t&&!He(0,r[o],o,t)||(delete r[o],n=!0)}}return de.isArray(e)?e.forEach(o):o(e),n}},{key:"clear",value:function(e){for(var t=Object.keys(this),r=t.length,n=!1;r--;){var o=t[r];e&&!He(0,this[o],o,e,!0)||(delete this[o],n=!0)}return n}},{key:"normalize",value:function(e){var t=this,r={};return de.forEach(this,(function(n,o){var i=de.findKey(r,o);if(i)return t[i]=ze(n),void delete t[o];var a=e?function(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(function(e,t,r){return t.toUpperCase()+r}))}(o):String(o).trim();a!==o&&delete t[o],t[a]=ze(n),r[a]=!0})),this}},{key:"concat",value:function(){for(var e,t=arguments.length,r=new Array(t),n=0;n1?r-1:0),o=1;o1&&void 0!==arguments[1]?arguments[1]:Date.now();o=i,r=null,n&&(clearTimeout(n),n=null),e.apply(void 0,g(t))};return[function(){for(var e=Date.now(),t=e-o,u=arguments.length,s=new Array(u),c=0;c=i?a(s,e):(r=s,n||(n=setTimeout((function(){n=null,a(r)}),i-t)))},function(){return r&&a(r)}]}de.inherits(Ge,he,{__CANCEL__:!0});var Qe=function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:3,n=0,o=$e(50,250);return Ye((function(r){var i=r.loaded,a=r.lengthComputable?r.total:void 0,u=i-n,s=o(u);n=i;var c=m({loaded:i,total:a,progress:a?i/a:void 0,bytes:u,rate:s||void 0,estimated:s&&a&&i<=a?(a-i)/s:void 0,event:r,lengthComputable:null!=a},t?"download":"upload",!0);e(c)}),r)},Ze=function(e,t){var r=null!=e;return[function(n){return t[0]({lengthComputable:r,total:e,loaded:n})},t[1]]},et=function(e){return function(){for(var t=arguments.length,r=new Array(t),n=0;n1?t-1:0),n=1;n1?"since :\n"+s.map(xt).join("\n"):" "+xt(s[0]):"as no adapter specified"),"ERR_NOT_SUPPORT")}return n},adapters:St};function Tt(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new Ge(null,e)}function jt(e){return Tt(e),e.headers=We.from(e.headers),e.data=Ke.call(e,e.transformRequest),-1!==["post","put","patch"].indexOf(e.method)&&e.headers.setContentType("application/x-www-form-urlencoded",!1),kt.getAdapter(e.adapter||De.adapter,e)(e).then((function(t){return Tt(e),t.data=Ke.call(e,e.transformResponse,t),t.headers=We.from(t.headers),t}),(function(t){return Ve(t)||(Tt(e),t&&t.response&&(t.response.data=Ke.call(e,e.transformResponse,t.response),t.response.headers=We.from(t.response.headers))),Promise.reject(t)}))}var At="1.13.2",Pt={};["object","boolean","number","function","string","symbol"].forEach((function(e,t){Pt[e]=function(r){return f(r)===e||"a"+(t<1?"n ":" ")+e}}));var Lt={};Pt.transitional=function(e,t,r){function n(e,t){return"[Axios v1.13.2] Transitional option '"+e+"'"+t+(r?". "+r:"")}return function(r,o,i){if(!1===e)throw new he(n(o," has been removed"+(t?" in "+t:"")),he.ERR_DEPRECATED);return t&&!Lt[o]&&(Lt[o]=!0,console.warn(n(o," has been deprecated since v"+t+" and will be removed in the near future"))),!e||e(r,o,i)}},Pt.spelling=function(e){return function(t,r){return console.warn("".concat(r," is likely a misspelling of ").concat(e)),!0}};var Nt={assertOptions:function(e,t,r){if("object"!==f(e))throw new he("options must be an object",he.ERR_BAD_OPTION_VALUE);for(var n=Object.keys(e),o=n.length;o-- >0;){var i=n[o],a=t[i];if(a){var u=e[i],s=void 0===u||a(u,i,e);if(!0!==s)throw new he("option "+i+" must be "+s,he.ERR_BAD_OPTION_VALUE)}else if(!0!==r)throw new he("Unknown option "+i,he.ERR_BAD_OPTION)}},validators:Pt},Ct=Nt.validators,_t=function(){function e(t){h(this,e),this.defaults=t||{},this.interceptors={request:new Te,response:new Te}}var t;return y(e,[{key:"request",value:(t=d(s().mark((function e(t,r){var n,o;return s().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,this._request(t,r);case 3:return e.abrupt("return",e.sent);case 6:if(e.prev=6,e.t0=e.catch(0),e.t0 instanceof Error){n={},Error.captureStackTrace?Error.captureStackTrace(n):n=new Error,o=n.stack?n.stack.replace(/^.+\n/,""):"";try{e.t0.stack?o&&!String(e.t0.stack).endsWith(o.replace(/^.+\n.+\n/,""))&&(e.t0.stack+="\n"+o):e.t0.stack=o}catch(e){}}throw e.t0;case 10:case"end":return e.stop()}}),e,this,[[0,6]])}))),function(e,r){return t.apply(this,arguments)})},{key:"_request",value:function(e,t){"string"==typeof e?(t=t||{}).url=e:t=e||{};var r=t=it(this.defaults,t),n=r.transitional,o=r.paramsSerializer,i=r.headers;void 0!==n&&Nt.assertOptions(n,{silentJSONParsing:Ct.transitional(Ct.boolean),forcedJSONParsing:Ct.transitional(Ct.boolean),clarifyTimeoutError:Ct.transitional(Ct.boolean)},!1),null!=o&&(de.isFunction(o)?t.paramsSerializer={serialize:o}:Nt.assertOptions(o,{encode:Ct.function,serialize:Ct.function},!0)),void 0!==t.allowAbsoluteUrls||(void 0!==this.defaults.allowAbsoluteUrls?t.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls:t.allowAbsoluteUrls=!0),Nt.assertOptions(t,{baseUrl:Ct.spelling("baseURL"),withXsrfToken:Ct.spelling("withXSRFToken")},!0),t.method=(t.method||this.defaults.method||"get").toLowerCase();var a=i&&de.merge(i.common,i[t.method]);i&&de.forEach(["delete","get","head","post","put","patch","common"],(function(e){delete i[e]})),t.headers=We.concat(a,i);var u=[],s=!0;this.interceptors.request.forEach((function(e){"function"==typeof e.runWhen&&!1===e.runWhen(t)||(s=s&&e.synchronous,u.unshift(e.fulfilled,e.rejected))}));var c,f=[];this.interceptors.response.forEach((function(e){f.push(e.fulfilled,e.rejected)}));var l,p=0;if(!s){var d=[jt.bind(this),void 0];for(d.unshift.apply(d,u),d.push.apply(d,f),l=d.length,c=Promise.resolve(t);p0;)n._listeners[t](e);n._listeners=null}})),this.promise.then=function(e){var t,r=new Promise((function(e){n.subscribe(e),t=e})).then(e);return r.cancel=function(){n.unsubscribe(t)},r},t((function(e,t,o){n.reason||(n.reason=new Ge(e,t,o),r(n.reason))}))}return y(e,[{key:"throwIfRequested",value:function(){if(this.reason)throw this.reason}},{key:"subscribe",value:function(e){this.reason?e(this.reason):this._listeners?this._listeners.push(e):this._listeners=[e]}},{key:"unsubscribe",value:function(e){if(this._listeners){var t=this._listeners.indexOf(e);-1!==t&&this._listeners.splice(t,1)}}},{key:"toAbortSignal",value:function(){var e=this,t=new AbortController,r=function(e){t.abort(e)};return this.subscribe(r),t.signal.unsubscribe=function(){return e.unsubscribe(r)},t.signal}}],[{key:"source",value:function(){var t;return{token:new e((function(e){t=e})),cancel:t}}}]),e}(),Bt=Ft;var Dt={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(Dt).forEach((function(e){var t=b(e,2),r=t[0],n=t[1];Dt[n]=r}));var It=Dt;var qt=function e(t){var r=new Ut(t),n=O(Ut.prototype.request,r);return de.extend(n,Ut.prototype,r,{allOwnKeys:!0}),de.extend(n,r,null,{allOwnKeys:!0}),n.create=function(r){return e(it(t,r))},n}(De);return qt.Axios=Ut,qt.CanceledError=Ge,qt.CancelToken=Bt,qt.isCancel=Ve,qt.VERSION=At,qt.toFormData=Ee,qt.AxiosError=he,qt.Cancel=qt.CanceledError,qt.all=function(e){return Promise.all(e)},qt.spread=function(e){return function(t){return e.apply(null,t)}},qt.isAxiosError=function(e){return de.isObject(e)&&!0===e.isAxiosError},qt.mergeConfig=it,qt.AxiosHeaders=We,qt.formToJSON=function(e){return Fe(de.isHTMLForm(e)?new FormData(e):e)},qt.getAdapter=kt.getAdapter,qt.HttpStatusCode=It,qt.default=qt,qt})); +//# sourceMappingURL=axios.min.js.map diff --git a/frontend/libs/fontawesome/css/all.min.css b/frontend/libs/fontawesome/css/all.min.css new file mode 100644 index 0000000..1f367c1 --- /dev/null +++ b/frontend/libs/fontawesome/css/all.min.css @@ -0,0 +1,9 @@ +/*! + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2023 Fonticons, Inc. + */ +.fa{font-family:var(--fa-style-family,"Font Awesome 6 Free");font-weight:var(--fa-style,900)}.fa,.fa-brands,.fa-classic,.fa-regular,.fa-sharp,.fa-solid,.fab,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:var(--fa-display,inline-block);font-style:normal;font-variant:normal;line-height:1;text-rendering:auto}.fa-classic,.fa-regular,.fa-solid,.far,.fas{font-family:"Font Awesome 6 Free"}.fa-brands,.fab{font-family:"Font Awesome 6 Brands"}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.08333em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.07143em;vertical-align:.05357em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.04167em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin,2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width, 2em)*-1);position:absolute;text-align:center;width:var(--fa-li-width,2em);line-height:inherit}.fa-border{border-radius:var(--fa-border-radius,.1em);border:var(--fa-border-width,.08em) var(--fa-border-style,solid) var(--fa-border-color,#eee);padding:var(--fa-border-padding,.2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin,.3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin,.3em)}.fa-beat{-webkit-animation-name:fa-beat;animation-name:fa-beat;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{-webkit-animation-name:fa-bounce;animation-name:fa-bounce;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{-webkit-animation-name:fa-fade;animation-name:fa-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade,.fa-fade{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s)}.fa-beat-fade{-webkit-animation-name:fa-beat-fade;animation-name:fa-beat-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{-webkit-animation-name:fa-flip;animation-name:fa-flip;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{-webkit-animation-name:fa-shake;animation-name:fa-shake;-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-shake,.fa-spin{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal)}.fa-spin{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-duration:var(--fa-animation-duration,2s);animation-duration:var(--fa-animation-duration,2s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,steps(8));animation-timing-function:var(--fa-animation-timing,steps(8))}@media (prefers-reduced-motion:reduce){.fa-beat,.fa-beat-fade,.fa-bounce,.fa-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{-webkit-animation-delay:-1ms;animation-delay:-1ms;-webkit-animation-duration:1ms;animation-duration:1ms;-webkit-animation-iteration-count:1;animation-iteration-count:1;-webkit-transition-delay:0s;transition-delay:0s;-webkit-transition-duration:0s;transition-duration:0s}}@-webkit-keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@-webkit-keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@-webkit-keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@-webkit-keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@-webkit-keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@-webkit-keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}.fa-rotate-by{-webkit-transform:rotate(var(--fa-rotate-angle,none));transform:rotate(var(--fa-rotate-angle,none))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index,auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse,#fff)} + +.fa-0:before{content:"\30"}.fa-1:before{content:"\31"}.fa-2:before{content:"\32"}.fa-3:before{content:"\33"}.fa-4:before{content:"\34"}.fa-5:before{content:"\35"}.fa-6:before{content:"\36"}.fa-7:before{content:"\37"}.fa-8:before{content:"\38"}.fa-9:before{content:"\39"}.fa-fill-drip:before{content:"\f576"}.fa-arrows-to-circle:before{content:"\e4bd"}.fa-chevron-circle-right:before,.fa-circle-chevron-right:before{content:"\f138"}.fa-at:before{content:"\40"}.fa-trash-alt:before,.fa-trash-can:before{content:"\f2ed"}.fa-text-height:before{content:"\f034"}.fa-user-times:before,.fa-user-xmark:before{content:"\f235"}.fa-stethoscope:before{content:"\f0f1"}.fa-comment-alt:before,.fa-message:before{content:"\f27a"}.fa-info:before{content:"\f129"}.fa-compress-alt:before,.fa-down-left-and-up-right-to-center:before{content:"\f422"}.fa-explosion:before{content:"\e4e9"}.fa-file-alt:before,.fa-file-lines:before,.fa-file-text:before{content:"\f15c"}.fa-wave-square:before{content:"\f83e"}.fa-ring:before{content:"\f70b"}.fa-building-un:before{content:"\e4d9"}.fa-dice-three:before{content:"\f527"}.fa-calendar-alt:before,.fa-calendar-days:before{content:"\f073"}.fa-anchor-circle-check:before{content:"\e4aa"}.fa-building-circle-arrow-right:before{content:"\e4d1"}.fa-volleyball-ball:before,.fa-volleyball:before{content:"\f45f"}.fa-arrows-up-to-line:before{content:"\e4c2"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-circle-minus:before,.fa-minus-circle:before{content:"\f056"}.fa-door-open:before{content:"\f52b"}.fa-right-from-bracket:before,.fa-sign-out-alt:before{content:"\f2f5"}.fa-atom:before{content:"\f5d2"}.fa-soap:before{content:"\e06e"}.fa-heart-music-camera-bolt:before,.fa-icons:before{content:"\f86d"}.fa-microphone-alt-slash:before,.fa-microphone-lines-slash:before{content:"\f539"}.fa-bridge-circle-check:before{content:"\e4c9"}.fa-pump-medical:before{content:"\e06a"}.fa-fingerprint:before{content:"\f577"}.fa-hand-point-right:before{content:"\f0a4"}.fa-magnifying-glass-location:before,.fa-search-location:before{content:"\f689"}.fa-forward-step:before,.fa-step-forward:before{content:"\f051"}.fa-face-smile-beam:before,.fa-smile-beam:before{content:"\f5b8"}.fa-flag-checkered:before{content:"\f11e"}.fa-football-ball:before,.fa-football:before{content:"\f44e"}.fa-school-circle-exclamation:before{content:"\e56c"}.fa-crop:before{content:"\f125"}.fa-angle-double-down:before,.fa-angles-down:before{content:"\f103"}.fa-users-rectangle:before{content:"\e594"}.fa-people-roof:before{content:"\e537"}.fa-people-line:before{content:"\e534"}.fa-beer-mug-empty:before,.fa-beer:before{content:"\f0fc"}.fa-diagram-predecessor:before{content:"\e477"}.fa-arrow-up-long:before,.fa-long-arrow-up:before{content:"\f176"}.fa-burn:before,.fa-fire-flame-simple:before{content:"\f46a"}.fa-male:before,.fa-person:before{content:"\f183"}.fa-laptop:before{content:"\f109"}.fa-file-csv:before{content:"\f6dd"}.fa-menorah:before{content:"\f676"}.fa-truck-plane:before{content:"\e58f"}.fa-record-vinyl:before{content:"\f8d9"}.fa-face-grin-stars:before,.fa-grin-stars:before{content:"\f587"}.fa-bong:before{content:"\f55c"}.fa-pastafarianism:before,.fa-spaghetti-monster-flying:before{content:"\f67b"}.fa-arrow-down-up-across-line:before{content:"\e4af"}.fa-spoon:before,.fa-utensil-spoon:before{content:"\f2e5"}.fa-jar-wheat:before{content:"\e517"}.fa-envelopes-bulk:before,.fa-mail-bulk:before{content:"\f674"}.fa-file-circle-exclamation:before{content:"\e4eb"}.fa-circle-h:before,.fa-hospital-symbol:before{content:"\f47e"}.fa-pager:before{content:"\f815"}.fa-address-book:before,.fa-contact-book:before{content:"\f2b9"}.fa-strikethrough:before{content:"\f0cc"}.fa-k:before{content:"\4b"}.fa-landmark-flag:before{content:"\e51c"}.fa-pencil-alt:before,.fa-pencil:before{content:"\f303"}.fa-backward:before{content:"\f04a"}.fa-caret-right:before{content:"\f0da"}.fa-comments:before{content:"\f086"}.fa-file-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-code-pull-request:before{content:"\e13c"}.fa-clipboard-list:before{content:"\f46d"}.fa-truck-loading:before,.fa-truck-ramp-box:before{content:"\f4de"}.fa-user-check:before{content:"\f4fc"}.fa-vial-virus:before{content:"\e597"}.fa-sheet-plastic:before{content:"\e571"}.fa-blog:before{content:"\f781"}.fa-user-ninja:before{content:"\f504"}.fa-person-arrow-up-from-line:before{content:"\e539"}.fa-scroll-torah:before,.fa-torah:before{content:"\f6a0"}.fa-broom-ball:before,.fa-quidditch-broom-ball:before,.fa-quidditch:before{content:"\f458"}.fa-toggle-off:before{content:"\f204"}.fa-archive:before,.fa-box-archive:before{content:"\f187"}.fa-person-drowning:before{content:"\e545"}.fa-arrow-down-9-1:before,.fa-sort-numeric-desc:before,.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-face-grin-tongue-squint:before,.fa-grin-tongue-squint:before{content:"\f58a"}.fa-spray-can:before{content:"\f5bd"}.fa-truck-monster:before{content:"\f63b"}.fa-w:before{content:"\57"}.fa-earth-africa:before,.fa-globe-africa:before{content:"\f57c"}.fa-rainbow:before{content:"\f75b"}.fa-circle-notch:before{content:"\f1ce"}.fa-tablet-alt:before,.fa-tablet-screen-button:before{content:"\f3fa"}.fa-paw:before{content:"\f1b0"}.fa-cloud:before{content:"\f0c2"}.fa-trowel-bricks:before{content:"\e58a"}.fa-face-flushed:before,.fa-flushed:before{content:"\f579"}.fa-hospital-user:before{content:"\f80d"}.fa-tent-arrow-left-right:before{content:"\e57f"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-binoculars:before{content:"\f1e5"}.fa-microphone-slash:before{content:"\f131"}.fa-box-tissue:before{content:"\e05b"}.fa-motorcycle:before{content:"\f21c"}.fa-bell-concierge:before,.fa-concierge-bell:before{content:"\f562"}.fa-pen-ruler:before,.fa-pencil-ruler:before{content:"\f5ae"}.fa-people-arrows-left-right:before,.fa-people-arrows:before{content:"\e068"}.fa-mars-and-venus-burst:before{content:"\e523"}.fa-caret-square-right:before,.fa-square-caret-right:before{content:"\f152"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-sun-plant-wilt:before{content:"\e57a"}.fa-toilets-portable:before{content:"\e584"}.fa-hockey-puck:before{content:"\f453"}.fa-table:before{content:"\f0ce"}.fa-magnifying-glass-arrow-right:before{content:"\e521"}.fa-digital-tachograph:before,.fa-tachograph-digital:before{content:"\f566"}.fa-users-slash:before{content:"\e073"}.fa-clover:before{content:"\e139"}.fa-mail-reply:before,.fa-reply:before{content:"\f3e5"}.fa-star-and-crescent:before{content:"\f699"}.fa-house-fire:before{content:"\e50c"}.fa-minus-square:before,.fa-square-minus:before{content:"\f146"}.fa-helicopter:before{content:"\f533"}.fa-compass:before{content:"\f14e"}.fa-caret-square-down:before,.fa-square-caret-down:before{content:"\f150"}.fa-file-circle-question:before{content:"\e4ef"}.fa-laptop-code:before{content:"\f5fc"}.fa-swatchbook:before{content:"\f5c3"}.fa-prescription-bottle:before{content:"\f485"}.fa-bars:before,.fa-navicon:before{content:"\f0c9"}.fa-people-group:before{content:"\e533"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-heart-broken:before,.fa-heart-crack:before{content:"\f7a9"}.fa-external-link-square-alt:before,.fa-square-up-right:before{content:"\f360"}.fa-face-kiss-beam:before,.fa-kiss-beam:before{content:"\f597"}.fa-film:before{content:"\f008"}.fa-ruler-horizontal:before{content:"\f547"}.fa-people-robbery:before{content:"\e536"}.fa-lightbulb:before{content:"\f0eb"}.fa-caret-left:before{content:"\f0d9"}.fa-circle-exclamation:before,.fa-exclamation-circle:before{content:"\f06a"}.fa-school-circle-xmark:before{content:"\e56d"}.fa-arrow-right-from-bracket:before,.fa-sign-out:before{content:"\f08b"}.fa-chevron-circle-down:before,.fa-circle-chevron-down:before{content:"\f13a"}.fa-unlock-alt:before,.fa-unlock-keyhole:before{content:"\f13e"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-headphones-alt:before,.fa-headphones-simple:before{content:"\f58f"}.fa-sitemap:before{content:"\f0e8"}.fa-circle-dollar-to-slot:before,.fa-donate:before{content:"\f4b9"}.fa-memory:before{content:"\f538"}.fa-road-spikes:before{content:"\e568"}.fa-fire-burner:before{content:"\e4f1"}.fa-flag:before{content:"\f024"}.fa-hanukiah:before{content:"\f6e6"}.fa-feather:before{content:"\f52d"}.fa-volume-down:before,.fa-volume-low:before{content:"\f027"}.fa-comment-slash:before{content:"\f4b3"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-compress:before{content:"\f066"}.fa-wheat-alt:before,.fa-wheat-awn:before{content:"\e2cd"}.fa-ankh:before{content:"\f644"}.fa-hands-holding-child:before{content:"\e4fa"}.fa-asterisk:before{content:"\2a"}.fa-check-square:before,.fa-square-check:before{content:"\f14a"}.fa-peseta-sign:before{content:"\e221"}.fa-header:before,.fa-heading:before{content:"\f1dc"}.fa-ghost:before{content:"\f6e2"}.fa-list-squares:before,.fa-list:before{content:"\f03a"}.fa-phone-square-alt:before,.fa-square-phone-flip:before{content:"\f87b"}.fa-cart-plus:before{content:"\f217"}.fa-gamepad:before{content:"\f11b"}.fa-circle-dot:before,.fa-dot-circle:before{content:"\f192"}.fa-dizzy:before,.fa-face-dizzy:before{content:"\f567"}.fa-egg:before{content:"\f7fb"}.fa-house-medical-circle-xmark:before{content:"\e513"}.fa-campground:before{content:"\f6bb"}.fa-folder-plus:before{content:"\f65e"}.fa-futbol-ball:before,.fa-futbol:before,.fa-soccer-ball:before{content:"\f1e3"}.fa-paint-brush:before,.fa-paintbrush:before{content:"\f1fc"}.fa-lock:before{content:"\f023"}.fa-gas-pump:before{content:"\f52f"}.fa-hot-tub-person:before,.fa-hot-tub:before{content:"\f593"}.fa-map-location:before,.fa-map-marked:before{content:"\f59f"}.fa-house-flood-water:before{content:"\e50e"}.fa-tree:before{content:"\f1bb"}.fa-bridge-lock:before{content:"\e4cc"}.fa-sack-dollar:before{content:"\f81d"}.fa-edit:before,.fa-pen-to-square:before{content:"\f044"}.fa-car-side:before{content:"\f5e4"}.fa-share-alt:before,.fa-share-nodes:before{content:"\f1e0"}.fa-heart-circle-minus:before{content:"\e4ff"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-microscope:before{content:"\f610"}.fa-sink:before{content:"\e06d"}.fa-bag-shopping:before,.fa-shopping-bag:before{content:"\f290"}.fa-arrow-down-z-a:before,.fa-sort-alpha-desc:before,.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-mitten:before{content:"\f7b5"}.fa-person-rays:before{content:"\e54d"}.fa-users:before{content:"\f0c0"}.fa-eye-slash:before{content:"\f070"}.fa-flask-vial:before{content:"\e4f3"}.fa-hand-paper:before,.fa-hand:before{content:"\f256"}.fa-om:before{content:"\f679"}.fa-worm:before{content:"\e599"}.fa-house-circle-xmark:before{content:"\e50b"}.fa-plug:before{content:"\f1e6"}.fa-chevron-up:before{content:"\f077"}.fa-hand-spock:before{content:"\f259"}.fa-stopwatch:before{content:"\f2f2"}.fa-face-kiss:before,.fa-kiss:before{content:"\f596"}.fa-bridge-circle-xmark:before{content:"\e4cb"}.fa-face-grin-tongue:before,.fa-grin-tongue:before{content:"\f589"}.fa-chess-bishop:before{content:"\f43a"}.fa-face-grin-wink:before,.fa-grin-wink:before{content:"\f58c"}.fa-deaf:before,.fa-deafness:before,.fa-ear-deaf:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-road-circle-check:before{content:"\e564"}.fa-dice-five:before{content:"\f523"}.fa-rss-square:before,.fa-square-rss:before{content:"\f143"}.fa-land-mine-on:before{content:"\e51b"}.fa-i-cursor:before{content:"\f246"}.fa-stamp:before{content:"\f5bf"}.fa-stairs:before{content:"\e289"}.fa-i:before{content:"\49"}.fa-hryvnia-sign:before,.fa-hryvnia:before{content:"\f6f2"}.fa-pills:before{content:"\f484"}.fa-face-grin-wide:before,.fa-grin-alt:before{content:"\f581"}.fa-tooth:before{content:"\f5c9"}.fa-v:before{content:"\56"}.fa-bangladeshi-taka-sign:before{content:"\e2e6"}.fa-bicycle:before{content:"\f206"}.fa-rod-asclepius:before,.fa-rod-snake:before,.fa-staff-aesculapius:before,.fa-staff-snake:before{content:"\e579"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-ambulance:before,.fa-truck-medical:before{content:"\f0f9"}.fa-wheat-awn-circle-exclamation:before{content:"\e598"}.fa-snowman:before{content:"\f7d0"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-road-barrier:before{content:"\e562"}.fa-school:before{content:"\f549"}.fa-igloo:before{content:"\f7ae"}.fa-joint:before{content:"\f595"}.fa-angle-right:before{content:"\f105"}.fa-horse:before{content:"\f6f0"}.fa-q:before{content:"\51"}.fa-g:before{content:"\47"}.fa-notes-medical:before{content:"\f481"}.fa-temperature-2:before,.fa-temperature-half:before,.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-dong-sign:before{content:"\e169"}.fa-capsules:before{content:"\f46b"}.fa-poo-bolt:before,.fa-poo-storm:before{content:"\f75a"}.fa-face-frown-open:before,.fa-frown-open:before{content:"\f57a"}.fa-hand-point-up:before{content:"\f0a6"}.fa-money-bill:before{content:"\f0d6"}.fa-bookmark:before{content:"\f02e"}.fa-align-justify:before{content:"\f039"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-helmet-un:before{content:"\e503"}.fa-bullseye:before{content:"\f140"}.fa-bacon:before{content:"\f7e5"}.fa-hand-point-down:before{content:"\f0a7"}.fa-arrow-up-from-bracket:before{content:"\e09a"}.fa-folder-blank:before,.fa-folder:before{content:"\f07b"}.fa-file-medical-alt:before,.fa-file-waveform:before{content:"\f478"}.fa-radiation:before{content:"\f7b9"}.fa-chart-simple:before{content:"\e473"}.fa-mars-stroke:before{content:"\f229"}.fa-vial:before{content:"\f492"}.fa-dashboard:before,.fa-gauge-med:before,.fa-gauge:before,.fa-tachometer-alt-average:before{content:"\f624"}.fa-magic-wand-sparkles:before,.fa-wand-magic-sparkles:before{content:"\e2ca"}.fa-e:before{content:"\45"}.fa-pen-alt:before,.fa-pen-clip:before{content:"\f305"}.fa-bridge-circle-exclamation:before{content:"\e4ca"}.fa-user:before{content:"\f007"}.fa-school-circle-check:before{content:"\e56b"}.fa-dumpster:before{content:"\f793"}.fa-shuttle-van:before,.fa-van-shuttle:before{content:"\f5b6"}.fa-building-user:before{content:"\e4da"}.fa-caret-square-left:before,.fa-square-caret-left:before{content:"\f191"}.fa-highlighter:before{content:"\f591"}.fa-key:before{content:"\f084"}.fa-bullhorn:before{content:"\f0a1"}.fa-globe:before{content:"\f0ac"}.fa-synagogue:before{content:"\f69b"}.fa-person-half-dress:before{content:"\e548"}.fa-road-bridge:before{content:"\e563"}.fa-location-arrow:before{content:"\f124"}.fa-c:before{content:"\43"}.fa-tablet-button:before{content:"\f10a"}.fa-building-lock:before{content:"\e4d6"}.fa-pizza-slice:before{content:"\f818"}.fa-money-bill-wave:before{content:"\f53a"}.fa-area-chart:before,.fa-chart-area:before{content:"\f1fe"}.fa-house-flag:before{content:"\e50d"}.fa-person-circle-minus:before{content:"\e540"}.fa-ban:before,.fa-cancel:before{content:"\f05e"}.fa-camera-rotate:before{content:"\e0d8"}.fa-air-freshener:before,.fa-spray-can-sparkles:before{content:"\f5d0"}.fa-star:before{content:"\f005"}.fa-repeat:before{content:"\f363"}.fa-cross:before{content:"\f654"}.fa-box:before{content:"\f466"}.fa-venus-mars:before{content:"\f228"}.fa-arrow-pointer:before,.fa-mouse-pointer:before{content:"\f245"}.fa-expand-arrows-alt:before,.fa-maximize:before{content:"\f31e"}.fa-charging-station:before{content:"\f5e7"}.fa-shapes:before,.fa-triangle-circle-square:before{content:"\f61f"}.fa-random:before,.fa-shuffle:before{content:"\f074"}.fa-person-running:before,.fa-running:before{content:"\f70c"}.fa-mobile-retro:before{content:"\e527"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-spider:before{content:"\f717"}.fa-hands-bound:before{content:"\e4f9"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-plane-circle-exclamation:before{content:"\e556"}.fa-x-ray:before{content:"\f497"}.fa-spell-check:before{content:"\f891"}.fa-slash:before{content:"\f715"}.fa-computer-mouse:before,.fa-mouse:before{content:"\f8cc"}.fa-arrow-right-to-bracket:before,.fa-sign-in:before{content:"\f090"}.fa-shop-slash:before,.fa-store-alt-slash:before{content:"\e070"}.fa-server:before{content:"\f233"}.fa-virus-covid-slash:before{content:"\e4a9"}.fa-shop-lock:before{content:"\e4a5"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-blender-phone:before{content:"\f6b6"}.fa-building-wheat:before{content:"\e4db"}.fa-person-breastfeeding:before{content:"\e53a"}.fa-right-to-bracket:before,.fa-sign-in-alt:before{content:"\f2f6"}.fa-venus:before{content:"\f221"}.fa-passport:before{content:"\f5ab"}.fa-heart-pulse:before,.fa-heartbeat:before{content:"\f21e"}.fa-people-carry-box:before,.fa-people-carry:before{content:"\f4ce"}.fa-temperature-high:before{content:"\f769"}.fa-microchip:before{content:"\f2db"}.fa-crown:before{content:"\f521"}.fa-weight-hanging:before{content:"\f5cd"}.fa-xmarks-lines:before{content:"\e59a"}.fa-file-prescription:before{content:"\f572"}.fa-weight-scale:before,.fa-weight:before{content:"\f496"}.fa-user-friends:before,.fa-user-group:before{content:"\f500"}.fa-arrow-up-a-z:before,.fa-sort-alpha-up:before{content:"\f15e"}.fa-chess-knight:before{content:"\f441"}.fa-face-laugh-squint:before,.fa-laugh-squint:before{content:"\f59b"}.fa-wheelchair:before{content:"\f193"}.fa-arrow-circle-up:before,.fa-circle-arrow-up:before{content:"\f0aa"}.fa-toggle-on:before{content:"\f205"}.fa-person-walking:before,.fa-walking:before{content:"\f554"}.fa-l:before{content:"\4c"}.fa-fire:before{content:"\f06d"}.fa-bed-pulse:before,.fa-procedures:before{content:"\f487"}.fa-shuttle-space:before,.fa-space-shuttle:before{content:"\f197"}.fa-face-laugh:before,.fa-laugh:before{content:"\f599"}.fa-folder-open:before{content:"\f07c"}.fa-heart-circle-plus:before{content:"\e500"}.fa-code-fork:before{content:"\e13b"}.fa-city:before{content:"\f64f"}.fa-microphone-alt:before,.fa-microphone-lines:before{content:"\f3c9"}.fa-pepper-hot:before{content:"\f816"}.fa-unlock:before{content:"\f09c"}.fa-colon-sign:before{content:"\e140"}.fa-headset:before{content:"\f590"}.fa-store-slash:before{content:"\e071"}.fa-road-circle-xmark:before{content:"\e566"}.fa-user-minus:before{content:"\f503"}.fa-mars-stroke-up:before,.fa-mars-stroke-v:before{content:"\f22a"}.fa-champagne-glasses:before,.fa-glass-cheers:before{content:"\f79f"}.fa-clipboard:before{content:"\f328"}.fa-house-circle-exclamation:before{content:"\e50a"}.fa-file-arrow-up:before,.fa-file-upload:before{content:"\f574"}.fa-wifi-3:before,.fa-wifi-strong:before,.fa-wifi:before{content:"\f1eb"}.fa-bath:before,.fa-bathtub:before{content:"\f2cd"}.fa-underline:before{content:"\f0cd"}.fa-user-edit:before,.fa-user-pen:before{content:"\f4ff"}.fa-signature:before{content:"\f5b7"}.fa-stroopwafel:before{content:"\f551"}.fa-bold:before{content:"\f032"}.fa-anchor-lock:before{content:"\e4ad"}.fa-building-ngo:before{content:"\e4d7"}.fa-manat-sign:before{content:"\e1d5"}.fa-not-equal:before{content:"\f53e"}.fa-border-style:before,.fa-border-top-left:before{content:"\f853"}.fa-map-location-dot:before,.fa-map-marked-alt:before{content:"\f5a0"}.fa-jedi:before{content:"\f669"}.fa-poll:before,.fa-square-poll-vertical:before{content:"\f681"}.fa-mug-hot:before{content:"\f7b6"}.fa-battery-car:before,.fa-car-battery:before{content:"\f5df"}.fa-gift:before{content:"\f06b"}.fa-dice-two:before{content:"\f528"}.fa-chess-queen:before{content:"\f445"}.fa-glasses:before{content:"\f530"}.fa-chess-board:before{content:"\f43c"}.fa-building-circle-check:before{content:"\e4d2"}.fa-person-chalkboard:before{content:"\e53d"}.fa-mars-stroke-h:before,.fa-mars-stroke-right:before{content:"\f22b"}.fa-hand-back-fist:before,.fa-hand-rock:before{content:"\f255"}.fa-caret-square-up:before,.fa-square-caret-up:before{content:"\f151"}.fa-cloud-showers-water:before{content:"\e4e4"}.fa-bar-chart:before,.fa-chart-bar:before{content:"\f080"}.fa-hands-bubbles:before,.fa-hands-wash:before{content:"\e05e"}.fa-less-than-equal:before{content:"\f537"}.fa-train:before{content:"\f238"}.fa-eye-low-vision:before,.fa-low-vision:before{content:"\f2a8"}.fa-crow:before{content:"\f520"}.fa-sailboat:before{content:"\e445"}.fa-window-restore:before{content:"\f2d2"}.fa-plus-square:before,.fa-square-plus:before{content:"\f0fe"}.fa-torii-gate:before{content:"\f6a1"}.fa-frog:before{content:"\f52e"}.fa-bucket:before{content:"\e4cf"}.fa-image:before{content:"\f03e"}.fa-microphone:before{content:"\f130"}.fa-cow:before{content:"\f6c8"}.fa-caret-up:before{content:"\f0d8"}.fa-screwdriver:before{content:"\f54a"}.fa-folder-closed:before{content:"\e185"}.fa-house-tsunami:before{content:"\e515"}.fa-square-nfi:before{content:"\e576"}.fa-arrow-up-from-ground-water:before{content:"\e4b5"}.fa-glass-martini-alt:before,.fa-martini-glass:before{content:"\f57b"}.fa-rotate-back:before,.fa-rotate-backward:before,.fa-rotate-left:before,.fa-undo-alt:before{content:"\f2ea"}.fa-columns:before,.fa-table-columns:before{content:"\f0db"}.fa-lemon:before{content:"\f094"}.fa-head-side-mask:before{content:"\e063"}.fa-handshake:before{content:"\f2b5"}.fa-gem:before{content:"\f3a5"}.fa-dolly-box:before,.fa-dolly:before{content:"\f472"}.fa-smoking:before{content:"\f48d"}.fa-compress-arrows-alt:before,.fa-minimize:before{content:"\f78c"}.fa-monument:before{content:"\f5a6"}.fa-snowplow:before{content:"\f7d2"}.fa-angle-double-right:before,.fa-angles-right:before{content:"\f101"}.fa-cannabis:before{content:"\f55f"}.fa-circle-play:before,.fa-play-circle:before{content:"\f144"}.fa-tablets:before{content:"\f490"}.fa-ethernet:before{content:"\f796"}.fa-eur:before,.fa-euro-sign:before,.fa-euro:before{content:"\f153"}.fa-chair:before{content:"\f6c0"}.fa-check-circle:before,.fa-circle-check:before{content:"\f058"}.fa-circle-stop:before,.fa-stop-circle:before{content:"\f28d"}.fa-compass-drafting:before,.fa-drafting-compass:before{content:"\f568"}.fa-plate-wheat:before{content:"\e55a"}.fa-icicles:before{content:"\f7ad"}.fa-person-shelter:before{content:"\e54f"}.fa-neuter:before{content:"\f22c"}.fa-id-badge:before{content:"\f2c1"}.fa-marker:before{content:"\f5a1"}.fa-face-laugh-beam:before,.fa-laugh-beam:before{content:"\f59a"}.fa-helicopter-symbol:before{content:"\e502"}.fa-universal-access:before{content:"\f29a"}.fa-chevron-circle-up:before,.fa-circle-chevron-up:before{content:"\f139"}.fa-lari-sign:before{content:"\e1c8"}.fa-volcano:before{content:"\f770"}.fa-person-walking-dashed-line-arrow-right:before{content:"\e553"}.fa-gbp:before,.fa-pound-sign:before,.fa-sterling-sign:before{content:"\f154"}.fa-viruses:before{content:"\e076"}.fa-square-person-confined:before{content:"\e577"}.fa-user-tie:before{content:"\f508"}.fa-arrow-down-long:before,.fa-long-arrow-down:before{content:"\f175"}.fa-tent-arrow-down-to-line:before{content:"\e57e"}.fa-certificate:before{content:"\f0a3"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-suitcase:before{content:"\f0f2"}.fa-person-skating:before,.fa-skating:before{content:"\f7c5"}.fa-filter-circle-dollar:before,.fa-funnel-dollar:before{content:"\f662"}.fa-camera-retro:before{content:"\f083"}.fa-arrow-circle-down:before,.fa-circle-arrow-down:before{content:"\f0ab"}.fa-arrow-right-to-file:before,.fa-file-import:before{content:"\f56f"}.fa-external-link-square:before,.fa-square-arrow-up-right:before{content:"\f14c"}.fa-box-open:before{content:"\f49e"}.fa-scroll:before{content:"\f70e"}.fa-spa:before{content:"\f5bb"}.fa-location-pin-lock:before{content:"\e51f"}.fa-pause:before{content:"\f04c"}.fa-hill-avalanche:before{content:"\e507"}.fa-temperature-0:before,.fa-temperature-empty:before,.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-bomb:before{content:"\f1e2"}.fa-registered:before{content:"\f25d"}.fa-address-card:before,.fa-contact-card:before,.fa-vcard:before{content:"\f2bb"}.fa-balance-scale-right:before,.fa-scale-unbalanced-flip:before{content:"\f516"}.fa-subscript:before{content:"\f12c"}.fa-diamond-turn-right:before,.fa-directions:before{content:"\f5eb"}.fa-burst:before{content:"\e4dc"}.fa-house-laptop:before,.fa-laptop-house:before{content:"\e066"}.fa-face-tired:before,.fa-tired:before{content:"\f5c8"}.fa-money-bills:before{content:"\e1f3"}.fa-smog:before{content:"\f75f"}.fa-crutch:before{content:"\f7f7"}.fa-cloud-arrow-up:before,.fa-cloud-upload-alt:before,.fa-cloud-upload:before{content:"\f0ee"}.fa-palette:before{content:"\f53f"}.fa-arrows-turn-right:before{content:"\e4c0"}.fa-vest:before{content:"\e085"}.fa-ferry:before{content:"\e4ea"}.fa-arrows-down-to-people:before{content:"\e4b9"}.fa-seedling:before,.fa-sprout:before{content:"\f4d8"}.fa-arrows-alt-h:before,.fa-left-right:before{content:"\f337"}.fa-boxes-packing:before{content:"\e4c7"}.fa-arrow-circle-left:before,.fa-circle-arrow-left:before{content:"\f0a8"}.fa-group-arrows-rotate:before{content:"\e4f6"}.fa-bowl-food:before{content:"\e4c6"}.fa-candy-cane:before{content:"\f786"}.fa-arrow-down-wide-short:before,.fa-sort-amount-asc:before,.fa-sort-amount-down:before{content:"\f160"}.fa-cloud-bolt:before,.fa-thunderstorm:before{content:"\f76c"}.fa-remove-format:before,.fa-text-slash:before{content:"\f87d"}.fa-face-smile-wink:before,.fa-smile-wink:before{content:"\f4da"}.fa-file-word:before{content:"\f1c2"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-arrows-h:before,.fa-arrows-left-right:before{content:"\f07e"}.fa-house-lock:before{content:"\e510"}.fa-cloud-arrow-down:before,.fa-cloud-download-alt:before,.fa-cloud-download:before{content:"\f0ed"}.fa-children:before{content:"\e4e1"}.fa-blackboard:before,.fa-chalkboard:before{content:"\f51b"}.fa-user-alt-slash:before,.fa-user-large-slash:before{content:"\f4fa"}.fa-envelope-open:before{content:"\f2b6"}.fa-handshake-alt-slash:before,.fa-handshake-simple-slash:before{content:"\e05f"}.fa-mattress-pillow:before{content:"\e525"}.fa-guarani-sign:before{content:"\e19a"}.fa-arrows-rotate:before,.fa-refresh:before,.fa-sync:before{content:"\f021"}.fa-fire-extinguisher:before{content:"\f134"}.fa-cruzeiro-sign:before{content:"\e152"}.fa-greater-than-equal:before{content:"\f532"}.fa-shield-alt:before,.fa-shield-halved:before{content:"\f3ed"}.fa-atlas:before,.fa-book-atlas:before{content:"\f558"}.fa-virus:before{content:"\e074"}.fa-envelope-circle-check:before{content:"\e4e8"}.fa-layer-group:before{content:"\f5fd"}.fa-arrows-to-dot:before{content:"\e4be"}.fa-archway:before{content:"\f557"}.fa-heart-circle-check:before{content:"\e4fd"}.fa-house-chimney-crack:before,.fa-house-damage:before{content:"\f6f1"}.fa-file-archive:before,.fa-file-zipper:before{content:"\f1c6"}.fa-square:before{content:"\f0c8"}.fa-glass-martini:before,.fa-martini-glass-empty:before{content:"\f000"}.fa-couch:before{content:"\f4b8"}.fa-cedi-sign:before{content:"\e0df"}.fa-italic:before{content:"\f033"}.fa-church:before{content:"\f51d"}.fa-comments-dollar:before{content:"\f653"}.fa-democrat:before{content:"\f747"}.fa-z:before{content:"\5a"}.fa-person-skiing:before,.fa-skiing:before{content:"\f7c9"}.fa-road-lock:before{content:"\e567"}.fa-a:before{content:"\41"}.fa-temperature-arrow-down:before,.fa-temperature-down:before{content:"\e03f"}.fa-feather-alt:before,.fa-feather-pointed:before{content:"\f56b"}.fa-p:before{content:"\50"}.fa-snowflake:before{content:"\f2dc"}.fa-newspaper:before{content:"\f1ea"}.fa-ad:before,.fa-rectangle-ad:before{content:"\f641"}.fa-arrow-circle-right:before,.fa-circle-arrow-right:before{content:"\f0a9"}.fa-filter-circle-xmark:before{content:"\e17b"}.fa-locust:before{content:"\e520"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-list-1-2:before,.fa-list-numeric:before,.fa-list-ol:before{content:"\f0cb"}.fa-person-dress-burst:before{content:"\e544"}.fa-money-check-alt:before,.fa-money-check-dollar:before{content:"\f53d"}.fa-vector-square:before{content:"\f5cb"}.fa-bread-slice:before{content:"\f7ec"}.fa-language:before{content:"\f1ab"}.fa-face-kiss-wink-heart:before,.fa-kiss-wink-heart:before{content:"\f598"}.fa-filter:before{content:"\f0b0"}.fa-question:before{content:"\3f"}.fa-file-signature:before{content:"\f573"}.fa-arrows-alt:before,.fa-up-down-left-right:before{content:"\f0b2"}.fa-house-chimney-user:before{content:"\e065"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-puzzle-piece:before{content:"\f12e"}.fa-money-check:before{content:"\f53c"}.fa-star-half-alt:before,.fa-star-half-stroke:before{content:"\f5c0"}.fa-code:before{content:"\f121"}.fa-glass-whiskey:before,.fa-whiskey-glass:before{content:"\f7a0"}.fa-building-circle-exclamation:before{content:"\e4d3"}.fa-magnifying-glass-chart:before{content:"\e522"}.fa-arrow-up-right-from-square:before,.fa-external-link:before{content:"\f08e"}.fa-cubes-stacked:before{content:"\e4e6"}.fa-krw:before,.fa-won-sign:before,.fa-won:before{content:"\f159"}.fa-virus-covid:before{content:"\e4a8"}.fa-austral-sign:before{content:"\e0a9"}.fa-f:before{content:"\46"}.fa-leaf:before{content:"\f06c"}.fa-road:before{content:"\f018"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-person-circle-plus:before{content:"\e541"}.fa-chart-pie:before,.fa-pie-chart:before{content:"\f200"}.fa-bolt-lightning:before{content:"\e0b7"}.fa-sack-xmark:before{content:"\e56a"}.fa-file-excel:before{content:"\f1c3"}.fa-file-contract:before{content:"\f56c"}.fa-fish-fins:before{content:"\e4f2"}.fa-building-flag:before{content:"\e4d5"}.fa-face-grin-beam:before,.fa-grin-beam:before{content:"\f582"}.fa-object-ungroup:before{content:"\f248"}.fa-poop:before{content:"\f619"}.fa-location-pin:before,.fa-map-marker:before{content:"\f041"}.fa-kaaba:before{content:"\f66b"}.fa-toilet-paper:before{content:"\f71e"}.fa-hard-hat:before,.fa-hat-hard:before,.fa-helmet-safety:before{content:"\f807"}.fa-eject:before{content:"\f052"}.fa-arrow-alt-circle-right:before,.fa-circle-right:before{content:"\f35a"}.fa-plane-circle-check:before{content:"\e555"}.fa-face-rolling-eyes:before,.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-object-group:before{content:"\f247"}.fa-chart-line:before,.fa-line-chart:before{content:"\f201"}.fa-mask-ventilator:before{content:"\e524"}.fa-arrow-right:before{content:"\f061"}.fa-map-signs:before,.fa-signs-post:before{content:"\f277"}.fa-cash-register:before{content:"\f788"}.fa-person-circle-question:before{content:"\e542"}.fa-h:before{content:"\48"}.fa-tarp:before{content:"\e57b"}.fa-screwdriver-wrench:before,.fa-tools:before{content:"\f7d9"}.fa-arrows-to-eye:before{content:"\e4bf"}.fa-plug-circle-bolt:before{content:"\e55b"}.fa-heart:before{content:"\f004"}.fa-mars-and-venus:before{content:"\f224"}.fa-home-user:before,.fa-house-user:before{content:"\e1b0"}.fa-dumpster-fire:before{content:"\f794"}.fa-house-crack:before{content:"\e3b1"}.fa-cocktail:before,.fa-martini-glass-citrus:before{content:"\f561"}.fa-face-surprise:before,.fa-surprise:before{content:"\f5c2"}.fa-bottle-water:before{content:"\e4c5"}.fa-circle-pause:before,.fa-pause-circle:before{content:"\f28b"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-apple-alt:before,.fa-apple-whole:before{content:"\f5d1"}.fa-kitchen-set:before{content:"\e51a"}.fa-r:before{content:"\52"}.fa-temperature-1:before,.fa-temperature-quarter:before,.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-cube:before{content:"\f1b2"}.fa-bitcoin-sign:before{content:"\e0b4"}.fa-shield-dog:before{content:"\e573"}.fa-solar-panel:before{content:"\f5ba"}.fa-lock-open:before{content:"\f3c1"}.fa-elevator:before{content:"\e16d"}.fa-money-bill-transfer:before{content:"\e528"}.fa-money-bill-trend-up:before{content:"\e529"}.fa-house-flood-water-circle-arrow-right:before{content:"\e50f"}.fa-poll-h:before,.fa-square-poll-horizontal:before{content:"\f682"}.fa-circle:before{content:"\f111"}.fa-backward-fast:before,.fa-fast-backward:before{content:"\f049"}.fa-recycle:before{content:"\f1b8"}.fa-user-astronaut:before{content:"\f4fb"}.fa-plane-slash:before{content:"\e069"}.fa-trademark:before{content:"\f25c"}.fa-basketball-ball:before,.fa-basketball:before{content:"\f434"}.fa-satellite-dish:before{content:"\f7c0"}.fa-arrow-alt-circle-up:before,.fa-circle-up:before{content:"\f35b"}.fa-mobile-alt:before,.fa-mobile-screen-button:before{content:"\f3cd"}.fa-volume-high:before,.fa-volume-up:before{content:"\f028"}.fa-users-rays:before{content:"\e593"}.fa-wallet:before{content:"\f555"}.fa-clipboard-check:before{content:"\f46c"}.fa-file-audio:before{content:"\f1c7"}.fa-burger:before,.fa-hamburger:before{content:"\f805"}.fa-wrench:before{content:"\f0ad"}.fa-bugs:before{content:"\e4d0"}.fa-rupee-sign:before,.fa-rupee:before{content:"\f156"}.fa-file-image:before{content:"\f1c5"}.fa-circle-question:before,.fa-question-circle:before{content:"\f059"}.fa-plane-departure:before{content:"\f5b0"}.fa-handshake-slash:before{content:"\e060"}.fa-book-bookmark:before{content:"\e0bb"}.fa-code-branch:before{content:"\f126"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-bridge:before{content:"\e4c8"}.fa-phone-alt:before,.fa-phone-flip:before{content:"\f879"}.fa-truck-front:before{content:"\e2b7"}.fa-cat:before{content:"\f6be"}.fa-anchor-circle-exclamation:before{content:"\e4ab"}.fa-truck-field:before{content:"\e58d"}.fa-route:before{content:"\f4d7"}.fa-clipboard-question:before{content:"\e4e3"}.fa-panorama:before{content:"\e209"}.fa-comment-medical:before{content:"\f7f5"}.fa-teeth-open:before{content:"\f62f"}.fa-file-circle-minus:before{content:"\e4ed"}.fa-tags:before{content:"\f02c"}.fa-wine-glass:before{content:"\f4e3"}.fa-fast-forward:before,.fa-forward-fast:before{content:"\f050"}.fa-face-meh-blank:before,.fa-meh-blank:before{content:"\f5a4"}.fa-parking:before,.fa-square-parking:before{content:"\f540"}.fa-house-signal:before{content:"\e012"}.fa-bars-progress:before,.fa-tasks-alt:before{content:"\f828"}.fa-faucet-drip:before{content:"\e006"}.fa-cart-flatbed:before,.fa-dolly-flatbed:before{content:"\f474"}.fa-ban-smoking:before,.fa-smoking-ban:before{content:"\f54d"}.fa-terminal:before{content:"\f120"}.fa-mobile-button:before{content:"\f10b"}.fa-house-medical-flag:before{content:"\e514"}.fa-basket-shopping:before,.fa-shopping-basket:before{content:"\f291"}.fa-tape:before{content:"\f4db"}.fa-bus-alt:before,.fa-bus-simple:before{content:"\f55e"}.fa-eye:before{content:"\f06e"}.fa-face-sad-cry:before,.fa-sad-cry:before{content:"\f5b3"}.fa-audio-description:before{content:"\f29e"}.fa-person-military-to-person:before{content:"\e54c"}.fa-file-shield:before{content:"\e4f0"}.fa-user-slash:before{content:"\f506"}.fa-pen:before{content:"\f304"}.fa-tower-observation:before{content:"\e586"}.fa-file-code:before{content:"\f1c9"}.fa-signal-5:before,.fa-signal-perfect:before,.fa-signal:before{content:"\f012"}.fa-bus:before{content:"\f207"}.fa-heart-circle-xmark:before{content:"\e501"}.fa-home-lg:before,.fa-house-chimney:before{content:"\e3af"}.fa-window-maximize:before{content:"\f2d0"}.fa-face-frown:before,.fa-frown:before{content:"\f119"}.fa-prescription:before{content:"\f5b1"}.fa-shop:before,.fa-store-alt:before{content:"\f54f"}.fa-floppy-disk:before,.fa-save:before{content:"\f0c7"}.fa-vihara:before{content:"\f6a7"}.fa-balance-scale-left:before,.fa-scale-unbalanced:before{content:"\f515"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-comment-dots:before,.fa-commenting:before{content:"\f4ad"}.fa-plant-wilt:before{content:"\e5aa"}.fa-diamond:before{content:"\f219"}.fa-face-grin-squint:before,.fa-grin-squint:before{content:"\f585"}.fa-hand-holding-dollar:before,.fa-hand-holding-usd:before{content:"\f4c0"}.fa-bacterium:before{content:"\e05a"}.fa-hand-pointer:before{content:"\f25a"}.fa-drum-steelpan:before{content:"\f56a"}.fa-hand-scissors:before{content:"\f257"}.fa-hands-praying:before,.fa-praying-hands:before{content:"\f684"}.fa-arrow-right-rotate:before,.fa-arrow-rotate-forward:before,.fa-arrow-rotate-right:before,.fa-redo:before{content:"\f01e"}.fa-biohazard:before{content:"\f780"}.fa-location-crosshairs:before,.fa-location:before{content:"\f601"}.fa-mars-double:before{content:"\f227"}.fa-child-dress:before{content:"\e59c"}.fa-users-between-lines:before{content:"\e591"}.fa-lungs-virus:before{content:"\e067"}.fa-face-grin-tears:before,.fa-grin-tears:before{content:"\f588"}.fa-phone:before{content:"\f095"}.fa-calendar-times:before,.fa-calendar-xmark:before{content:"\f273"}.fa-child-reaching:before{content:"\e59d"}.fa-head-side-virus:before{content:"\e064"}.fa-user-cog:before,.fa-user-gear:before{content:"\f4fe"}.fa-arrow-up-1-9:before,.fa-sort-numeric-up:before{content:"\f163"}.fa-door-closed:before{content:"\f52a"}.fa-shield-virus:before{content:"\e06c"}.fa-dice-six:before{content:"\f526"}.fa-mosquito-net:before{content:"\e52c"}.fa-bridge-water:before{content:"\e4ce"}.fa-person-booth:before{content:"\f756"}.fa-text-width:before{content:"\f035"}.fa-hat-wizard:before{content:"\f6e8"}.fa-pen-fancy:before{content:"\f5ac"}.fa-digging:before,.fa-person-digging:before{content:"\f85e"}.fa-trash:before{content:"\f1f8"}.fa-gauge-simple-med:before,.fa-gauge-simple:before,.fa-tachometer-average:before{content:"\f629"}.fa-book-medical:before{content:"\f7e6"}.fa-poo:before{content:"\f2fe"}.fa-quote-right-alt:before,.fa-quote-right:before{content:"\f10e"}.fa-shirt:before,.fa-t-shirt:before,.fa-tshirt:before{content:"\f553"}.fa-cubes:before{content:"\f1b3"}.fa-divide:before{content:"\f529"}.fa-tenge-sign:before,.fa-tenge:before{content:"\f7d7"}.fa-headphones:before{content:"\f025"}.fa-hands-holding:before{content:"\f4c2"}.fa-hands-clapping:before{content:"\e1a8"}.fa-republican:before{content:"\f75e"}.fa-arrow-left:before{content:"\f060"}.fa-person-circle-xmark:before{content:"\e543"}.fa-ruler:before{content:"\f545"}.fa-align-left:before{content:"\f036"}.fa-dice-d6:before{content:"\f6d1"}.fa-restroom:before{content:"\f7bd"}.fa-j:before{content:"\4a"}.fa-users-viewfinder:before{content:"\e595"}.fa-file-video:before{content:"\f1c8"}.fa-external-link-alt:before,.fa-up-right-from-square:before{content:"\f35d"}.fa-table-cells:before,.fa-th:before{content:"\f00a"}.fa-file-pdf:before{content:"\f1c1"}.fa-bible:before,.fa-book-bible:before{content:"\f647"}.fa-o:before{content:"\4f"}.fa-medkit:before,.fa-suitcase-medical:before{content:"\f0fa"}.fa-user-secret:before{content:"\f21b"}.fa-otter:before{content:"\f700"}.fa-female:before,.fa-person-dress:before{content:"\f182"}.fa-comment-dollar:before{content:"\f651"}.fa-briefcase-clock:before,.fa-business-time:before{content:"\f64a"}.fa-table-cells-large:before,.fa-th-large:before{content:"\f009"}.fa-book-tanakh:before,.fa-tanakh:before{content:"\f827"}.fa-phone-volume:before,.fa-volume-control-phone:before{content:"\f2a0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-clipboard-user:before{content:"\f7f3"}.fa-child:before{content:"\f1ae"}.fa-lira-sign:before{content:"\f195"}.fa-satellite:before{content:"\f7bf"}.fa-plane-lock:before{content:"\e558"}.fa-tag:before{content:"\f02b"}.fa-comment:before{content:"\f075"}.fa-birthday-cake:before,.fa-cake-candles:before,.fa-cake:before{content:"\f1fd"}.fa-envelope:before{content:"\f0e0"}.fa-angle-double-up:before,.fa-angles-up:before{content:"\f102"}.fa-paperclip:before{content:"\f0c6"}.fa-arrow-right-to-city:before{content:"\e4b3"}.fa-ribbon:before{content:"\f4d6"}.fa-lungs:before{content:"\f604"}.fa-arrow-up-9-1:before,.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-litecoin-sign:before{content:"\e1d3"}.fa-border-none:before{content:"\f850"}.fa-circle-nodes:before{content:"\e4e2"}.fa-parachute-box:before{content:"\f4cd"}.fa-indent:before{content:"\f03c"}.fa-truck-field-un:before{content:"\e58e"}.fa-hourglass-empty:before,.fa-hourglass:before{content:"\f254"}.fa-mountain:before{content:"\f6fc"}.fa-user-doctor:before,.fa-user-md:before{content:"\f0f0"}.fa-circle-info:before,.fa-info-circle:before{content:"\f05a"}.fa-cloud-meatball:before{content:"\f73b"}.fa-camera-alt:before,.fa-camera:before{content:"\f030"}.fa-square-virus:before{content:"\e578"}.fa-meteor:before{content:"\f753"}.fa-car-on:before{content:"\e4dd"}.fa-sleigh:before{content:"\f7cc"}.fa-arrow-down-1-9:before,.fa-sort-numeric-asc:before,.fa-sort-numeric-down:before{content:"\f162"}.fa-hand-holding-droplet:before,.fa-hand-holding-water:before{content:"\f4c1"}.fa-water:before{content:"\f773"}.fa-calendar-check:before{content:"\f274"}.fa-braille:before{content:"\f2a1"}.fa-prescription-bottle-alt:before,.fa-prescription-bottle-medical:before{content:"\f486"}.fa-landmark:before{content:"\f66f"}.fa-truck:before{content:"\f0d1"}.fa-crosshairs:before{content:"\f05b"}.fa-person-cane:before{content:"\e53c"}.fa-tent:before{content:"\e57d"}.fa-vest-patches:before{content:"\e086"}.fa-check-double:before{content:"\f560"}.fa-arrow-down-a-z:before,.fa-sort-alpha-asc:before,.fa-sort-alpha-down:before{content:"\f15d"}.fa-money-bill-wheat:before{content:"\e52a"}.fa-cookie:before{content:"\f563"}.fa-arrow-left-rotate:before,.fa-arrow-rotate-back:before,.fa-arrow-rotate-backward:before,.fa-arrow-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-hard-drive:before,.fa-hdd:before{content:"\f0a0"}.fa-face-grin-squint-tears:before,.fa-grin-squint-tears:before{content:"\f586"}.fa-dumbbell:before{content:"\f44b"}.fa-list-alt:before,.fa-rectangle-list:before{content:"\f022"}.fa-tarp-droplet:before{content:"\e57c"}.fa-house-medical-circle-check:before{content:"\e511"}.fa-person-skiing-nordic:before,.fa-skiing-nordic:before{content:"\f7ca"}.fa-calendar-plus:before{content:"\f271"}.fa-plane-arrival:before{content:"\f5af"}.fa-arrow-alt-circle-left:before,.fa-circle-left:before{content:"\f359"}.fa-subway:before,.fa-train-subway:before{content:"\f239"}.fa-chart-gantt:before{content:"\e0e4"}.fa-indian-rupee-sign:before,.fa-indian-rupee:before,.fa-inr:before{content:"\e1bc"}.fa-crop-alt:before,.fa-crop-simple:before{content:"\f565"}.fa-money-bill-1:before,.fa-money-bill-alt:before{content:"\f3d1"}.fa-left-long:before,.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-dna:before{content:"\f471"}.fa-virus-slash:before{content:"\e075"}.fa-minus:before,.fa-subtract:before{content:"\f068"}.fa-chess:before{content:"\f439"}.fa-arrow-left-long:before,.fa-long-arrow-left:before{content:"\f177"}.fa-plug-circle-check:before{content:"\e55c"}.fa-street-view:before{content:"\f21d"}.fa-franc-sign:before{content:"\e18f"}.fa-volume-off:before{content:"\f026"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before,.fa-hands-american-sign-language-interpreting:before,.fa-hands-asl-interpreting:before{content:"\f2a3"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-droplet-slash:before,.fa-tint-slash:before{content:"\f5c7"}.fa-mosque:before{content:"\f678"}.fa-mosquito:before{content:"\e52b"}.fa-star-of-david:before{content:"\f69a"}.fa-person-military-rifle:before{content:"\e54b"}.fa-cart-shopping:before,.fa-shopping-cart:before{content:"\f07a"}.fa-vials:before{content:"\f493"}.fa-plug-circle-plus:before{content:"\e55f"}.fa-place-of-worship:before{content:"\f67f"}.fa-grip-vertical:before{content:"\f58e"}.fa-arrow-turn-up:before,.fa-level-up:before{content:"\f148"}.fa-u:before{content:"\55"}.fa-square-root-alt:before,.fa-square-root-variable:before{content:"\f698"}.fa-clock-four:before,.fa-clock:before{content:"\f017"}.fa-backward-step:before,.fa-step-backward:before{content:"\f048"}.fa-pallet:before{content:"\f482"}.fa-faucet:before{content:"\e005"}.fa-baseball-bat-ball:before{content:"\f432"}.fa-s:before{content:"\53"}.fa-timeline:before{content:"\e29c"}.fa-keyboard:before{content:"\f11c"}.fa-caret-down:before{content:"\f0d7"}.fa-clinic-medical:before,.fa-house-chimney-medical:before{content:"\f7f2"}.fa-temperature-3:before,.fa-temperature-three-quarters:before,.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-mobile-android-alt:before,.fa-mobile-screen:before{content:"\f3cf"}.fa-plane-up:before{content:"\e22d"}.fa-piggy-bank:before{content:"\f4d3"}.fa-battery-3:before,.fa-battery-half:before{content:"\f242"}.fa-mountain-city:before{content:"\e52e"}.fa-coins:before{content:"\f51e"}.fa-khanda:before{content:"\f66d"}.fa-sliders-h:before,.fa-sliders:before{content:"\f1de"}.fa-folder-tree:before{content:"\f802"}.fa-network-wired:before{content:"\f6ff"}.fa-map-pin:before{content:"\f276"}.fa-hamsa:before{content:"\f665"}.fa-cent-sign:before{content:"\e3f5"}.fa-flask:before{content:"\f0c3"}.fa-person-pregnant:before{content:"\e31e"}.fa-wand-sparkles:before{content:"\f72b"}.fa-ellipsis-v:before,.fa-ellipsis-vertical:before{content:"\f142"}.fa-ticket:before{content:"\f145"}.fa-power-off:before{content:"\f011"}.fa-long-arrow-alt-right:before,.fa-right-long:before{content:"\f30b"}.fa-flag-usa:before{content:"\f74d"}.fa-laptop-file:before{content:"\e51d"}.fa-teletype:before,.fa-tty:before{content:"\f1e4"}.fa-diagram-next:before{content:"\e476"}.fa-person-rifle:before{content:"\e54e"}.fa-house-medical-circle-exclamation:before{content:"\e512"}.fa-closed-captioning:before{content:"\f20a"}.fa-hiking:before,.fa-person-hiking:before{content:"\f6ec"}.fa-venus-double:before{content:"\f226"}.fa-images:before{content:"\f302"}.fa-calculator:before{content:"\f1ec"}.fa-people-pulling:before{content:"\e535"}.fa-n:before{content:"\4e"}.fa-cable-car:before,.fa-tram:before{content:"\f7da"}.fa-cloud-rain:before{content:"\f73d"}.fa-building-circle-xmark:before{content:"\e4d4"}.fa-ship:before{content:"\f21a"}.fa-arrows-down-to-line:before{content:"\e4b8"}.fa-download:before{content:"\f019"}.fa-face-grin:before,.fa-grin:before{content:"\f580"}.fa-backspace:before,.fa-delete-left:before{content:"\f55a"}.fa-eye-dropper-empty:before,.fa-eye-dropper:before,.fa-eyedropper:before{content:"\f1fb"}.fa-file-circle-check:before{content:"\e5a0"}.fa-forward:before{content:"\f04e"}.fa-mobile-android:before,.fa-mobile-phone:before,.fa-mobile:before{content:"\f3ce"}.fa-face-meh:before,.fa-meh:before{content:"\f11a"}.fa-align-center:before{content:"\f037"}.fa-book-dead:before,.fa-book-skull:before{content:"\f6b7"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-heart-circle-exclamation:before{content:"\e4fe"}.fa-home-alt:before,.fa-home-lg-alt:before,.fa-home:before,.fa-house:before{content:"\f015"}.fa-calendar-week:before{content:"\f784"}.fa-laptop-medical:before{content:"\f812"}.fa-b:before{content:"\42"}.fa-file-medical:before{content:"\f477"}.fa-dice-one:before{content:"\f525"}.fa-kiwi-bird:before{content:"\f535"}.fa-arrow-right-arrow-left:before,.fa-exchange:before{content:"\f0ec"}.fa-redo-alt:before,.fa-rotate-forward:before,.fa-rotate-right:before{content:"\f2f9"}.fa-cutlery:before,.fa-utensils:before{content:"\f2e7"}.fa-arrow-up-wide-short:before,.fa-sort-amount-up:before{content:"\f161"}.fa-mill-sign:before{content:"\e1ed"}.fa-bowl-rice:before{content:"\e2eb"}.fa-skull:before{content:"\f54c"}.fa-broadcast-tower:before,.fa-tower-broadcast:before{content:"\f519"}.fa-truck-pickup:before{content:"\f63c"}.fa-long-arrow-alt-up:before,.fa-up-long:before{content:"\f30c"}.fa-stop:before{content:"\f04d"}.fa-code-merge:before{content:"\f387"}.fa-upload:before{content:"\f093"}.fa-hurricane:before{content:"\f751"}.fa-mound:before{content:"\e52d"}.fa-toilet-portable:before{content:"\e583"}.fa-compact-disc:before{content:"\f51f"}.fa-file-arrow-down:before,.fa-file-download:before{content:"\f56d"}.fa-caravan:before{content:"\f8ff"}.fa-shield-cat:before{content:"\e572"}.fa-bolt:before,.fa-zap:before{content:"\f0e7"}.fa-glass-water:before{content:"\e4f4"}.fa-oil-well:before{content:"\e532"}.fa-vault:before{content:"\e2c5"}.fa-mars:before{content:"\f222"}.fa-toilet:before{content:"\f7d8"}.fa-plane-circle-xmark:before{content:"\e557"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen-sign:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble-sign:before,.fa-ruble:before{content:"\f158"}.fa-sun:before{content:"\f185"}.fa-guitar:before{content:"\f7a6"}.fa-face-laugh-wink:before,.fa-laugh-wink:before{content:"\f59c"}.fa-horse-head:before{content:"\f7ab"}.fa-bore-hole:before{content:"\e4c3"}.fa-industry:before{content:"\f275"}.fa-arrow-alt-circle-down:before,.fa-circle-down:before{content:"\f358"}.fa-arrows-turn-to-dots:before{content:"\e4c1"}.fa-florin-sign:before{content:"\e184"}.fa-arrow-down-short-wide:before,.fa-sort-amount-desc:before,.fa-sort-amount-down-alt:before{content:"\f884"}.fa-less-than:before{content:"\3c"}.fa-angle-down:before{content:"\f107"}.fa-car-tunnel:before{content:"\e4de"}.fa-head-side-cough:before{content:"\e061"}.fa-grip-lines:before{content:"\f7a4"}.fa-thumbs-down:before{content:"\f165"}.fa-user-lock:before{content:"\f502"}.fa-arrow-right-long:before,.fa-long-arrow-right:before{content:"\f178"}.fa-anchor-circle-xmark:before{content:"\e4ac"}.fa-ellipsis-h:before,.fa-ellipsis:before{content:"\f141"}.fa-chess-pawn:before{content:"\f443"}.fa-first-aid:before,.fa-kit-medical:before{content:"\f479"}.fa-person-through-window:before{content:"\e5a9"}.fa-toolbox:before{content:"\f552"}.fa-hands-holding-circle:before{content:"\e4fb"}.fa-bug:before{content:"\f188"}.fa-credit-card-alt:before,.fa-credit-card:before{content:"\f09d"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-hand-holding-hand:before{content:"\e4f7"}.fa-book-open-reader:before,.fa-book-reader:before{content:"\f5da"}.fa-mountain-sun:before{content:"\e52f"}.fa-arrows-left-right-to-line:before{content:"\e4ba"}.fa-dice-d20:before{content:"\f6cf"}.fa-truck-droplet:before{content:"\e58c"}.fa-file-circle-xmark:before{content:"\e5a1"}.fa-temperature-arrow-up:before,.fa-temperature-up:before{content:"\e040"}.fa-medal:before{content:"\f5a2"}.fa-bed:before{content:"\f236"}.fa-h-square:before,.fa-square-h:before{content:"\f0fd"}.fa-podcast:before{content:"\f2ce"}.fa-temperature-4:before,.fa-temperature-full:before,.fa-thermometer-4:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-bell:before{content:"\f0f3"}.fa-superscript:before{content:"\f12b"}.fa-plug-circle-xmark:before{content:"\e560"}.fa-star-of-life:before{content:"\f621"}.fa-phone-slash:before{content:"\f3dd"}.fa-paint-roller:before{content:"\f5aa"}.fa-hands-helping:before,.fa-handshake-angle:before{content:"\f4c4"}.fa-location-dot:before,.fa-map-marker-alt:before{content:"\f3c5"}.fa-file:before{content:"\f15b"}.fa-greater-than:before{content:"\3e"}.fa-person-swimming:before,.fa-swimmer:before{content:"\f5c4"}.fa-arrow-down:before{content:"\f063"}.fa-droplet:before,.fa-tint:before{content:"\f043"}.fa-eraser:before{content:"\f12d"}.fa-earth-america:before,.fa-earth-americas:before,.fa-earth:before,.fa-globe-americas:before{content:"\f57d"}.fa-person-burst:before{content:"\e53b"}.fa-dove:before{content:"\f4ba"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-socks:before{content:"\f696"}.fa-inbox:before{content:"\f01c"}.fa-section:before{content:"\e447"}.fa-gauge-high:before,.fa-tachometer-alt-fast:before,.fa-tachometer-alt:before{content:"\f625"}.fa-envelope-open-text:before{content:"\f658"}.fa-hospital-alt:before,.fa-hospital-wide:before,.fa-hospital:before{content:"\f0f8"}.fa-wine-bottle:before{content:"\f72f"}.fa-chess-rook:before{content:"\f447"}.fa-bars-staggered:before,.fa-reorder:before,.fa-stream:before{content:"\f550"}.fa-dharmachakra:before{content:"\f655"}.fa-hotdog:before{content:"\f80f"}.fa-blind:before,.fa-person-walking-with-cane:before{content:"\f29d"}.fa-drum:before{content:"\f569"}.fa-ice-cream:before{content:"\f810"}.fa-heart-circle-bolt:before{content:"\e4fc"}.fa-fax:before{content:"\f1ac"}.fa-paragraph:before{content:"\f1dd"}.fa-check-to-slot:before,.fa-vote-yea:before{content:"\f772"}.fa-star-half:before{content:"\f089"}.fa-boxes-alt:before,.fa-boxes-stacked:before,.fa-boxes:before{content:"\f468"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-assistive-listening-systems:before,.fa-ear-listen:before{content:"\f2a2"}.fa-tree-city:before{content:"\e587"}.fa-play:before{content:"\f04b"}.fa-font:before{content:"\f031"}.fa-rupiah-sign:before{content:"\e23d"}.fa-magnifying-glass:before,.fa-search:before{content:"\f002"}.fa-ping-pong-paddle-ball:before,.fa-table-tennis-paddle-ball:before,.fa-table-tennis:before{content:"\f45d"}.fa-diagnoses:before,.fa-person-dots-from-line:before{content:"\f470"}.fa-trash-can-arrow-up:before,.fa-trash-restore-alt:before{content:"\f82a"}.fa-naira-sign:before{content:"\e1f6"}.fa-cart-arrow-down:before{content:"\f218"}.fa-walkie-talkie:before{content:"\f8ef"}.fa-file-edit:before,.fa-file-pen:before{content:"\f31c"}.fa-receipt:before{content:"\f543"}.fa-pen-square:before,.fa-pencil-square:before,.fa-square-pen:before{content:"\f14b"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-person-circle-exclamation:before{content:"\e53f"}.fa-chevron-down:before{content:"\f078"}.fa-battery-5:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-skull-crossbones:before{content:"\f714"}.fa-code-compare:before{content:"\e13a"}.fa-list-dots:before,.fa-list-ul:before{content:"\f0ca"}.fa-school-lock:before{content:"\e56f"}.fa-tower-cell:before{content:"\e585"}.fa-down-long:before,.fa-long-arrow-alt-down:before{content:"\f309"}.fa-ranking-star:before{content:"\e561"}.fa-chess-king:before{content:"\f43f"}.fa-person-harassing:before{content:"\e549"}.fa-brazilian-real-sign:before{content:"\e46c"}.fa-landmark-alt:before,.fa-landmark-dome:before{content:"\f752"}.fa-arrow-up:before{content:"\f062"}.fa-television:before,.fa-tv-alt:before,.fa-tv:before{content:"\f26c"}.fa-shrimp:before{content:"\e448"}.fa-list-check:before,.fa-tasks:before{content:"\f0ae"}.fa-jug-detergent:before{content:"\e519"}.fa-circle-user:before,.fa-user-circle:before{content:"\f2bd"}.fa-user-shield:before{content:"\f505"}.fa-wind:before{content:"\f72e"}.fa-car-burst:before,.fa-car-crash:before{content:"\f5e1"}.fa-y:before{content:"\59"}.fa-person-snowboarding:before,.fa-snowboarding:before{content:"\f7ce"}.fa-shipping-fast:before,.fa-truck-fast:before{content:"\f48b"}.fa-fish:before{content:"\f578"}.fa-user-graduate:before{content:"\f501"}.fa-adjust:before,.fa-circle-half-stroke:before{content:"\f042"}.fa-clapperboard:before{content:"\e131"}.fa-circle-radiation:before,.fa-radiation-alt:before{content:"\f7ba"}.fa-baseball-ball:before,.fa-baseball:before{content:"\f433"}.fa-jet-fighter-up:before{content:"\e518"}.fa-diagram-project:before,.fa-project-diagram:before{content:"\f542"}.fa-copy:before{content:"\f0c5"}.fa-volume-mute:before,.fa-volume-times:before,.fa-volume-xmark:before{content:"\f6a9"}.fa-hand-sparkles:before{content:"\e05d"}.fa-grip-horizontal:before,.fa-grip:before{content:"\f58d"}.fa-share-from-square:before,.fa-share-square:before{content:"\f14d"}.fa-child-combatant:before,.fa-child-rifle:before{content:"\e4e0"}.fa-gun:before{content:"\e19b"}.fa-phone-square:before,.fa-square-phone:before{content:"\f098"}.fa-add:before,.fa-plus:before{content:"\2b"}.fa-expand:before{content:"\f065"}.fa-computer:before{content:"\e4e5"}.fa-close:before,.fa-multiply:before,.fa-remove:before,.fa-times:before,.fa-xmark:before{content:"\f00d"}.fa-arrows-up-down-left-right:before,.fa-arrows:before{content:"\f047"}.fa-chalkboard-teacher:before,.fa-chalkboard-user:before{content:"\f51c"}.fa-peso-sign:before{content:"\e222"}.fa-building-shield:before{content:"\e4d8"}.fa-baby:before{content:"\f77c"}.fa-users-line:before{content:"\e592"}.fa-quote-left-alt:before,.fa-quote-left:before{content:"\f10d"}.fa-tractor:before{content:"\f722"}.fa-trash-arrow-up:before,.fa-trash-restore:before{content:"\f829"}.fa-arrow-down-up-lock:before{content:"\e4b0"}.fa-lines-leaning:before{content:"\e51e"}.fa-ruler-combined:before{content:"\f546"}.fa-copyright:before{content:"\f1f9"}.fa-equals:before{content:"\3d"}.fa-blender:before{content:"\f517"}.fa-teeth:before{content:"\f62e"}.fa-ils:before,.fa-shekel-sign:before,.fa-shekel:before,.fa-sheqel-sign:before,.fa-sheqel:before{content:"\f20b"}.fa-map:before{content:"\f279"}.fa-rocket:before{content:"\f135"}.fa-photo-film:before,.fa-photo-video:before{content:"\f87c"}.fa-folder-minus:before{content:"\f65d"}.fa-store:before{content:"\f54e"}.fa-arrow-trend-up:before{content:"\e098"}.fa-plug-circle-minus:before{content:"\e55e"}.fa-sign-hanging:before,.fa-sign:before{content:"\f4d9"}.fa-bezier-curve:before{content:"\f55b"}.fa-bell-slash:before{content:"\f1f6"}.fa-tablet-android:before,.fa-tablet:before{content:"\f3fb"}.fa-school-flag:before{content:"\e56e"}.fa-fill:before{content:"\f575"}.fa-angle-up:before{content:"\f106"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-holly-berry:before{content:"\f7aa"}.fa-chevron-left:before{content:"\f053"}.fa-bacteria:before{content:"\e059"}.fa-hand-lizard:before{content:"\f258"}.fa-notdef:before{content:"\e1fe"}.fa-disease:before{content:"\f7fa"}.fa-briefcase-medical:before{content:"\f469"}.fa-genderless:before{content:"\f22d"}.fa-chevron-right:before{content:"\f054"}.fa-retweet:before{content:"\f079"}.fa-car-alt:before,.fa-car-rear:before{content:"\f5de"}.fa-pump-soap:before{content:"\e06b"}.fa-video-slash:before{content:"\f4e2"}.fa-battery-2:before,.fa-battery-quarter:before{content:"\f243"}.fa-radio:before{content:"\f8d7"}.fa-baby-carriage:before,.fa-carriage-baby:before{content:"\f77d"}.fa-traffic-light:before{content:"\f637"}.fa-thermometer:before{content:"\f491"}.fa-vr-cardboard:before{content:"\f729"}.fa-hand-middle-finger:before{content:"\f806"}.fa-percent:before,.fa-percentage:before{content:"\25"}.fa-truck-moving:before{content:"\f4df"}.fa-glass-water-droplet:before{content:"\e4f5"}.fa-display:before{content:"\e163"}.fa-face-smile:before,.fa-smile:before{content:"\f118"}.fa-thumb-tack:before,.fa-thumbtack:before{content:"\f08d"}.fa-trophy:before{content:"\f091"}.fa-person-praying:before,.fa-pray:before{content:"\f683"}.fa-hammer:before{content:"\f6e3"}.fa-hand-peace:before{content:"\f25b"}.fa-rotate:before,.fa-sync-alt:before{content:"\f2f1"}.fa-spinner:before{content:"\f110"}.fa-robot:before{content:"\f544"}.fa-peace:before{content:"\f67c"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-warehouse:before{content:"\f494"}.fa-arrow-up-right-dots:before{content:"\e4b7"}.fa-splotch:before{content:"\f5bc"}.fa-face-grin-hearts:before,.fa-grin-hearts:before{content:"\f584"}.fa-dice-four:before{content:"\f524"}.fa-sim-card:before{content:"\f7c4"}.fa-transgender-alt:before,.fa-transgender:before{content:"\f225"}.fa-mercury:before{content:"\f223"}.fa-arrow-turn-down:before,.fa-level-down:before{content:"\f149"}.fa-person-falling-burst:before{content:"\e547"}.fa-award:before{content:"\f559"}.fa-ticket-alt:before,.fa-ticket-simple:before{content:"\f3ff"}.fa-building:before{content:"\f1ad"}.fa-angle-double-left:before,.fa-angles-left:before{content:"\f100"}.fa-qrcode:before{content:"\f029"}.fa-clock-rotate-left:before,.fa-history:before{content:"\f1da"}.fa-face-grin-beam-sweat:before,.fa-grin-beam-sweat:before{content:"\f583"}.fa-arrow-right-from-file:before,.fa-file-export:before{content:"\f56e"}.fa-shield-blank:before,.fa-shield:before{content:"\f132"}.fa-arrow-up-short-wide:before,.fa-sort-amount-up-alt:before{content:"\f885"}.fa-house-medical:before{content:"\e3b2"}.fa-golf-ball-tee:before,.fa-golf-ball:before{content:"\f450"}.fa-chevron-circle-left:before,.fa-circle-chevron-left:before{content:"\f137"}.fa-house-chimney-window:before{content:"\e00d"}.fa-pen-nib:before{content:"\f5ad"}.fa-tent-arrow-turn-left:before{content:"\e580"}.fa-tents:before{content:"\e582"}.fa-magic:before,.fa-wand-magic:before{content:"\f0d0"}.fa-dog:before{content:"\f6d3"}.fa-carrot:before{content:"\f787"}.fa-moon:before{content:"\f186"}.fa-wine-glass-alt:before,.fa-wine-glass-empty:before{content:"\f5ce"}.fa-cheese:before{content:"\f7ef"}.fa-yin-yang:before{content:"\f6ad"}.fa-music:before{content:"\f001"}.fa-code-commit:before{content:"\f386"}.fa-temperature-low:before{content:"\f76b"}.fa-biking:before,.fa-person-biking:before{content:"\f84a"}.fa-broom:before{content:"\f51a"}.fa-shield-heart:before{content:"\e574"}.fa-gopuram:before{content:"\f664"}.fa-earth-oceania:before,.fa-globe-oceania:before{content:"\e47b"}.fa-square-xmark:before,.fa-times-square:before,.fa-xmark-square:before{content:"\f2d3"}.fa-hashtag:before{content:"\23"}.fa-expand-alt:before,.fa-up-right-and-down-left-from-center:before{content:"\f424"}.fa-oil-can:before{content:"\f613"}.fa-t:before{content:"\54"}.fa-hippo:before{content:"\f6ed"}.fa-chart-column:before{content:"\e0e3"}.fa-infinity:before{content:"\f534"}.fa-vial-circle-check:before{content:"\e596"}.fa-person-arrow-down-to-line:before{content:"\e538"}.fa-voicemail:before{content:"\f897"}.fa-fan:before{content:"\f863"}.fa-person-walking-luggage:before{content:"\e554"}.fa-arrows-alt-v:before,.fa-up-down:before{content:"\f338"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-calendar:before{content:"\f133"}.fa-trailer:before{content:"\e041"}.fa-bahai:before,.fa-haykal:before{content:"\f666"}.fa-sd-card:before{content:"\f7c2"}.fa-dragon:before{content:"\f6d5"}.fa-shoe-prints:before{content:"\f54b"}.fa-circle-plus:before,.fa-plus-circle:before{content:"\f055"}.fa-face-grin-tongue-wink:before,.fa-grin-tongue-wink:before{content:"\f58b"}.fa-hand-holding:before{content:"\f4bd"}.fa-plug-circle-exclamation:before{content:"\e55d"}.fa-chain-broken:before,.fa-chain-slash:before,.fa-link-slash:before,.fa-unlink:before{content:"\f127"}.fa-clone:before{content:"\f24d"}.fa-person-walking-arrow-loop-left:before{content:"\e551"}.fa-arrow-up-z-a:before,.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-fire-alt:before,.fa-fire-flame-curved:before{content:"\f7e4"}.fa-tornado:before{content:"\f76f"}.fa-file-circle-plus:before{content:"\e494"}.fa-book-quran:before,.fa-quran:before{content:"\f687"}.fa-anchor:before{content:"\f13d"}.fa-border-all:before{content:"\f84c"}.fa-angry:before,.fa-face-angry:before{content:"\f556"}.fa-cookie-bite:before{content:"\f564"}.fa-arrow-trend-down:before{content:"\e097"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-draw-polygon:before{content:"\f5ee"}.fa-balance-scale:before,.fa-scale-balanced:before{content:"\f24e"}.fa-gauge-simple-high:before,.fa-tachometer-fast:before,.fa-tachometer:before{content:"\f62a"}.fa-shower:before{content:"\f2cc"}.fa-desktop-alt:before,.fa-desktop:before{content:"\f390"}.fa-m:before{content:"\4d"}.fa-table-list:before,.fa-th-list:before{content:"\f00b"}.fa-comment-sms:before,.fa-sms:before{content:"\f7cd"}.fa-book:before{content:"\f02d"}.fa-user-plus:before{content:"\f234"}.fa-check:before{content:"\f00c"}.fa-battery-4:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-house-circle-check:before{content:"\e509"}.fa-angle-left:before{content:"\f104"}.fa-diagram-successor:before{content:"\e47a"}.fa-truck-arrow-right:before{content:"\e58b"}.fa-arrows-split-up-and-left:before{content:"\e4bc"}.fa-fist-raised:before,.fa-hand-fist:before{content:"\f6de"}.fa-cloud-moon:before{content:"\f6c3"}.fa-briefcase:before{content:"\f0b1"}.fa-person-falling:before{content:"\e546"}.fa-image-portrait:before,.fa-portrait:before{content:"\f3e0"}.fa-user-tag:before{content:"\f507"}.fa-rug:before{content:"\e569"}.fa-earth-europe:before,.fa-globe-europe:before{content:"\f7a2"}.fa-cart-flatbed-suitcase:before,.fa-luggage-cart:before{content:"\f59d"}.fa-rectangle-times:before,.fa-rectangle-xmark:before,.fa-times-rectangle:before,.fa-window-close:before{content:"\f410"}.fa-baht-sign:before{content:"\e0ac"}.fa-book-open:before{content:"\f518"}.fa-book-journal-whills:before,.fa-journal-whills:before{content:"\f66a"}.fa-handcuffs:before{content:"\e4f8"}.fa-exclamation-triangle:before,.fa-triangle-exclamation:before,.fa-warning:before{content:"\f071"}.fa-database:before{content:"\f1c0"}.fa-arrow-turn-right:before,.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-bottle-droplet:before{content:"\e4c4"}.fa-mask-face:before{content:"\e1d7"}.fa-hill-rockslide:before{content:"\e508"}.fa-exchange-alt:before,.fa-right-left:before{content:"\f362"}.fa-paper-plane:before{content:"\f1d8"}.fa-road-circle-exclamation:before{content:"\e565"}.fa-dungeon:before{content:"\f6d9"}.fa-align-right:before{content:"\f038"}.fa-money-bill-1-wave:before,.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-life-ring:before{content:"\f1cd"}.fa-hands:before,.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-calendar-day:before{content:"\f783"}.fa-ladder-water:before,.fa-swimming-pool:before,.fa-water-ladder:before{content:"\f5c5"}.fa-arrows-up-down:before,.fa-arrows-v:before{content:"\f07d"}.fa-face-grimace:before,.fa-grimace:before{content:"\f57f"}.fa-wheelchair-alt:before,.fa-wheelchair-move:before{content:"\e2ce"}.fa-level-down-alt:before,.fa-turn-down:before{content:"\f3be"}.fa-person-walking-arrow-right:before{content:"\e552"}.fa-envelope-square:before,.fa-square-envelope:before{content:"\f199"}.fa-dice:before{content:"\f522"}.fa-bowling-ball:before{content:"\f436"}.fa-brain:before{content:"\f5dc"}.fa-band-aid:before,.fa-bandage:before{content:"\f462"}.fa-calendar-minus:before{content:"\f272"}.fa-circle-xmark:before,.fa-times-circle:before,.fa-xmark-circle:before{content:"\f057"}.fa-gifts:before{content:"\f79c"}.fa-hotel:before{content:"\f594"}.fa-earth-asia:before,.fa-globe-asia:before{content:"\f57e"}.fa-id-card-alt:before,.fa-id-card-clip:before{content:"\f47f"}.fa-magnifying-glass-plus:before,.fa-search-plus:before{content:"\f00e"}.fa-thumbs-up:before{content:"\f164"}.fa-user-clock:before{content:"\f4fd"}.fa-allergies:before,.fa-hand-dots:before{content:"\f461"}.fa-file-invoice:before{content:"\f570"}.fa-window-minimize:before{content:"\f2d1"}.fa-coffee:before,.fa-mug-saucer:before{content:"\f0f4"}.fa-brush:before{content:"\f55d"}.fa-mask:before{content:"\f6fa"}.fa-magnifying-glass-minus:before,.fa-search-minus:before{content:"\f010"}.fa-ruler-vertical:before{content:"\f548"}.fa-user-alt:before,.fa-user-large:before{content:"\f406"}.fa-train-tram:before{content:"\e5b4"}.fa-user-nurse:before{content:"\f82f"}.fa-syringe:before{content:"\f48e"}.fa-cloud-sun:before{content:"\f6c4"}.fa-stopwatch-20:before{content:"\e06f"}.fa-square-full:before{content:"\f45c"}.fa-magnet:before{content:"\f076"}.fa-jar:before{content:"\e516"}.fa-note-sticky:before,.fa-sticky-note:before{content:"\f249"}.fa-bug-slash:before{content:"\e490"}.fa-arrow-up-from-water-pump:before{content:"\e4b6"}.fa-bone:before{content:"\f5d7"}.fa-user-injured:before{content:"\f728"}.fa-face-sad-tear:before,.fa-sad-tear:before{content:"\f5b4"}.fa-plane:before{content:"\f072"}.fa-tent-arrows-down:before{content:"\e581"}.fa-exclamation:before{content:"\21"}.fa-arrows-spin:before{content:"\e4bb"}.fa-print:before{content:"\f02f"}.fa-try:before,.fa-turkish-lira-sign:before,.fa-turkish-lira:before{content:"\e2bb"}.fa-dollar-sign:before,.fa-dollar:before,.fa-usd:before{content:"\24"}.fa-x:before{content:"\58"}.fa-magnifying-glass-dollar:before,.fa-search-dollar:before{content:"\f688"}.fa-users-cog:before,.fa-users-gear:before{content:"\f509"}.fa-person-military-pointing:before{content:"\e54a"}.fa-bank:before,.fa-building-columns:before,.fa-institution:before,.fa-museum:before,.fa-university:before{content:"\f19c"}.fa-umbrella:before{content:"\f0e9"}.fa-trowel:before{content:"\e589"}.fa-d:before{content:"\44"}.fa-stapler:before{content:"\e5af"}.fa-masks-theater:before,.fa-theater-masks:before{content:"\f630"}.fa-kip-sign:before{content:"\e1c4"}.fa-hand-point-left:before{content:"\f0a5"}.fa-handshake-alt:before,.fa-handshake-simple:before{content:"\f4c6"}.fa-fighter-jet:before,.fa-jet-fighter:before{content:"\f0fb"}.fa-share-alt-square:before,.fa-square-share-nodes:before{content:"\f1e1"}.fa-barcode:before{content:"\f02a"}.fa-plus-minus:before{content:"\e43c"}.fa-video-camera:before,.fa-video:before{content:"\f03d"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-person-circle-check:before{content:"\e53e"}.fa-level-up-alt:before,.fa-turn-up:before{content:"\f3bf"} +.fa-sr-only,.fa-sr-only-focusable:not(:focus),.sr-only,.sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}:host,:root{--fa-style-family-brands:"Font Awesome 6 Brands";--fa-font-brands:normal 400 1em/1 "Font Awesome 6 Brands"}@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}.fa-brands,.fab{font-weight:400}.fa-monero:before{content:"\f3d0"}.fa-hooli:before{content:"\f427"}.fa-yelp:before{content:"\f1e9"}.fa-cc-visa:before{content:"\f1f0"}.fa-lastfm:before{content:"\f202"}.fa-shopware:before{content:"\f5b5"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-aws:before{content:"\f375"}.fa-redhat:before{content:"\f7bc"}.fa-yoast:before{content:"\f2b1"}.fa-cloudflare:before{content:"\e07d"}.fa-ups:before{content:"\f7e0"}.fa-wpexplorer:before{content:"\f2de"}.fa-dyalog:before{content:"\f399"}.fa-bity:before{content:"\f37a"}.fa-stackpath:before{content:"\f842"}.fa-buysellads:before{content:"\f20d"}.fa-first-order:before{content:"\f2b0"}.fa-modx:before{content:"\f285"}.fa-guilded:before{content:"\e07e"}.fa-vnv:before{content:"\f40b"}.fa-js-square:before,.fa-square-js:before{content:"\f3b9"}.fa-microsoft:before{content:"\f3ca"}.fa-qq:before{content:"\f1d6"}.fa-orcid:before{content:"\f8d2"}.fa-java:before{content:"\f4e4"}.fa-invision:before{content:"\f7b0"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-centercode:before{content:"\f380"}.fa-glide-g:before{content:"\f2a6"}.fa-drupal:before{content:"\f1a9"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-unity:before{content:"\e049"}.fa-whmcs:before{content:"\f40d"}.fa-rocketchat:before{content:"\f3e8"}.fa-vk:before{content:"\f189"}.fa-untappd:before{content:"\f405"}.fa-mailchimp:before{content:"\f59e"}.fa-css3-alt:before{content:"\f38b"}.fa-reddit-square:before,.fa-square-reddit:before{content:"\f1a2"}.fa-vimeo-v:before{content:"\f27d"}.fa-contao:before{content:"\f26d"}.fa-square-font-awesome:before{content:"\e5ad"}.fa-deskpro:before{content:"\f38f"}.fa-sistrix:before{content:"\f3ee"}.fa-instagram-square:before,.fa-square-instagram:before{content:"\e055"}.fa-battle-net:before{content:"\f835"}.fa-the-red-yeti:before{content:"\f69d"}.fa-hacker-news-square:before,.fa-square-hacker-news:before{content:"\f3af"}.fa-edge:before{content:"\f282"}.fa-napster:before{content:"\f3d2"}.fa-snapchat-square:before,.fa-square-snapchat:before{content:"\f2ad"}.fa-google-plus-g:before{content:"\f0d5"}.fa-artstation:before{content:"\f77a"}.fa-markdown:before{content:"\f60f"}.fa-sourcetree:before{content:"\f7d3"}.fa-google-plus:before{content:"\f2b3"}.fa-diaspora:before{content:"\f791"}.fa-foursquare:before{content:"\f180"}.fa-stack-overflow:before{content:"\f16c"}.fa-github-alt:before{content:"\f113"}.fa-phoenix-squadron:before{content:"\f511"}.fa-pagelines:before{content:"\f18c"}.fa-algolia:before{content:"\f36c"}.fa-red-river:before{content:"\f3e3"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-safari:before{content:"\f267"}.fa-google:before{content:"\f1a0"}.fa-font-awesome-alt:before,.fa-square-font-awesome-stroke:before{content:"\f35c"}.fa-atlassian:before{content:"\f77b"}.fa-linkedin-in:before{content:"\f0e1"}.fa-digital-ocean:before{content:"\f391"}.fa-nimblr:before{content:"\f5a8"}.fa-chromecast:before{content:"\f838"}.fa-evernote:before{content:"\f839"}.fa-hacker-news:before{content:"\f1d4"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-adversal:before{content:"\f36a"}.fa-creative-commons:before{content:"\f25e"}.fa-watchman-monitoring:before{content:"\e087"}.fa-fonticons:before{content:"\f280"}.fa-weixin:before{content:"\f1d7"}.fa-shirtsinbulk:before{content:"\f214"}.fa-codepen:before{content:"\f1cb"}.fa-git-alt:before{content:"\f841"}.fa-lyft:before{content:"\f3c3"}.fa-rev:before{content:"\f5b2"}.fa-windows:before{content:"\f17a"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-square-viadeo:before,.fa-viadeo-square:before{content:"\f2aa"}.fa-meetup:before{content:"\f2e0"}.fa-centos:before{content:"\f789"}.fa-adn:before{content:"\f170"}.fa-cloudsmith:before{content:"\f384"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-dribbble-square:before,.fa-square-dribbble:before{content:"\f397"}.fa-codiepie:before{content:"\f284"}.fa-node:before{content:"\f419"}.fa-mix:before{content:"\f3cb"}.fa-steam:before{content:"\f1b6"}.fa-cc-apple-pay:before{content:"\f416"}.fa-scribd:before{content:"\f28a"}.fa-openid:before{content:"\f19b"}.fa-instalod:before{content:"\e081"}.fa-expeditedssl:before{content:"\f23e"}.fa-sellcast:before{content:"\f2da"}.fa-square-twitter:before,.fa-twitter-square:before{content:"\f081"}.fa-r-project:before{content:"\f4f7"}.fa-delicious:before{content:"\f1a5"}.fa-freebsd:before{content:"\f3a4"}.fa-vuejs:before{content:"\f41f"}.fa-accusoft:before{content:"\f369"}.fa-ioxhost:before{content:"\f208"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-app-store:before{content:"\f36f"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-itunes-note:before{content:"\f3b5"}.fa-golang:before{content:"\e40f"}.fa-kickstarter:before{content:"\f3bb"}.fa-grav:before{content:"\f2d6"}.fa-weibo:before{content:"\f18a"}.fa-uncharted:before{content:"\e084"}.fa-firstdraft:before{content:"\f3a1"}.fa-square-youtube:before,.fa-youtube-square:before{content:"\f431"}.fa-wikipedia-w:before{content:"\f266"}.fa-rendact:before,.fa-wpressr:before{content:"\f3e4"}.fa-angellist:before{content:"\f209"}.fa-galactic-republic:before{content:"\f50c"}.fa-nfc-directional:before{content:"\e530"}.fa-skype:before{content:"\f17e"}.fa-joget:before{content:"\f3b7"}.fa-fedora:before{content:"\f798"}.fa-stripe-s:before{content:"\f42a"}.fa-meta:before{content:"\e49b"}.fa-laravel:before{content:"\f3bd"}.fa-hotjar:before{content:"\f3b1"}.fa-bluetooth-b:before{content:"\f294"}.fa-sticker-mule:before{content:"\f3f7"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-hips:before{content:"\f452"}.fa-behance:before{content:"\f1b4"}.fa-reddit:before{content:"\f1a1"}.fa-discord:before{content:"\f392"}.fa-chrome:before{content:"\f268"}.fa-app-store-ios:before{content:"\f370"}.fa-cc-discover:before{content:"\f1f2"}.fa-wpbeginner:before{content:"\f297"}.fa-confluence:before{content:"\f78d"}.fa-mdb:before{content:"\f8ca"}.fa-dochub:before{content:"\f394"}.fa-accessible-icon:before{content:"\f368"}.fa-ebay:before{content:"\f4f4"}.fa-amazon:before{content:"\f270"}.fa-unsplash:before{content:"\e07c"}.fa-yarn:before{content:"\f7e3"}.fa-square-steam:before,.fa-steam-square:before{content:"\f1b7"}.fa-500px:before{content:"\f26e"}.fa-square-vimeo:before,.fa-vimeo-square:before{content:"\f194"}.fa-asymmetrik:before{content:"\f372"}.fa-font-awesome-flag:before,.fa-font-awesome-logo-full:before,.fa-font-awesome:before{content:"\f2b4"}.fa-gratipay:before{content:"\f184"}.fa-apple:before{content:"\f179"}.fa-hive:before{content:"\e07f"}.fa-gitkraken:before{content:"\f3a6"}.fa-keybase:before{content:"\f4f5"}.fa-apple-pay:before{content:"\f415"}.fa-padlet:before{content:"\e4a0"}.fa-amazon-pay:before{content:"\f42c"}.fa-github-square:before,.fa-square-github:before{content:"\f092"}.fa-stumbleupon:before{content:"\f1a4"}.fa-fedex:before{content:"\f797"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-shopify:before{content:"\e057"}.fa-neos:before{content:"\f612"}.fa-hackerrank:before{content:"\f5f7"}.fa-researchgate:before{content:"\f4f8"}.fa-swift:before{content:"\f8e1"}.fa-angular:before{content:"\f420"}.fa-speakap:before{content:"\f3f3"}.fa-angrycreative:before{content:"\f36e"}.fa-y-combinator:before{content:"\f23b"}.fa-empire:before{content:"\f1d1"}.fa-envira:before{content:"\f299"}.fa-gitlab-square:before,.fa-square-gitlab:before{content:"\e5ae"}.fa-studiovinari:before{content:"\f3f8"}.fa-pied-piper:before{content:"\f2ae"}.fa-wordpress:before{content:"\f19a"}.fa-product-hunt:before{content:"\f288"}.fa-firefox:before{content:"\f269"}.fa-linode:before{content:"\f2b8"}.fa-goodreads:before{content:"\f3a8"}.fa-odnoklassniki-square:before,.fa-square-odnoklassniki:before{content:"\f264"}.fa-jsfiddle:before{content:"\f1cc"}.fa-sith:before{content:"\f512"}.fa-themeisle:before{content:"\f2b2"}.fa-page4:before{content:"\f3d7"}.fa-hashnode:before{content:"\e499"}.fa-react:before{content:"\f41b"}.fa-cc-paypal:before{content:"\f1f4"}.fa-squarespace:before{content:"\f5be"}.fa-cc-stripe:before{content:"\f1f5"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-bitcoin:before{content:"\f379"}.fa-keycdn:before{content:"\f3ba"}.fa-opera:before{content:"\f26a"}.fa-itch-io:before{content:"\f83a"}.fa-umbraco:before{content:"\f8e8"}.fa-galactic-senate:before{content:"\f50d"}.fa-ubuntu:before{content:"\f7df"}.fa-draft2digital:before{content:"\f396"}.fa-stripe:before{content:"\f429"}.fa-houzz:before{content:"\f27c"}.fa-gg:before{content:"\f260"}.fa-dhl:before{content:"\f790"}.fa-pinterest-square:before,.fa-square-pinterest:before{content:"\f0d3"}.fa-xing:before{content:"\f168"}.fa-blackberry:before{content:"\f37b"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-playstation:before{content:"\f3df"}.fa-quinscape:before{content:"\f459"}.fa-less:before{content:"\f41d"}.fa-blogger-b:before{content:"\f37d"}.fa-opencart:before{content:"\f23d"}.fa-vine:before{content:"\f1ca"}.fa-paypal:before{content:"\f1ed"}.fa-gitlab:before{content:"\f296"}.fa-typo3:before{content:"\f42b"}.fa-reddit-alien:before{content:"\f281"}.fa-yahoo:before{content:"\f19e"}.fa-dailymotion:before{content:"\e052"}.fa-affiliatetheme:before{content:"\f36b"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-bootstrap:before{content:"\f836"}.fa-odnoklassniki:before{content:"\f263"}.fa-nfc-symbol:before{content:"\e531"}.fa-ethereum:before{content:"\f42e"}.fa-speaker-deck:before{content:"\f83c"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-patreon:before{content:"\f3d9"}.fa-avianex:before{content:"\f374"}.fa-ello:before{content:"\f5f1"}.fa-gofore:before{content:"\f3a7"}.fa-bimobject:before{content:"\f378"}.fa-facebook-f:before{content:"\f39e"}.fa-google-plus-square:before,.fa-square-google-plus:before{content:"\f0d4"}.fa-mandalorian:before{content:"\f50f"}.fa-first-order-alt:before{content:"\f50a"}.fa-osi:before{content:"\f41a"}.fa-google-wallet:before{content:"\f1ee"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-periscope:before{content:"\f3da"}.fa-fulcrum:before{content:"\f50b"}.fa-cloudscale:before{content:"\f383"}.fa-forumbee:before{content:"\f211"}.fa-mizuni:before{content:"\f3cc"}.fa-schlix:before{content:"\f3ea"}.fa-square-xing:before,.fa-xing-square:before{content:"\f169"}.fa-bandcamp:before{content:"\f2d5"}.fa-wpforms:before{content:"\f298"}.fa-cloudversify:before{content:"\f385"}.fa-usps:before{content:"\f7e1"}.fa-megaport:before{content:"\f5a3"}.fa-magento:before{content:"\f3c4"}.fa-spotify:before{content:"\f1bc"}.fa-optin-monster:before{content:"\f23c"}.fa-fly:before{content:"\f417"}.fa-aviato:before{content:"\f421"}.fa-itunes:before{content:"\f3b4"}.fa-cuttlefish:before{content:"\f38c"}.fa-blogger:before{content:"\f37c"}.fa-flickr:before{content:"\f16e"}.fa-viber:before{content:"\f409"}.fa-soundcloud:before{content:"\f1be"}.fa-digg:before{content:"\f1a6"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-symfony:before{content:"\f83d"}.fa-maxcdn:before{content:"\f136"}.fa-etsy:before{content:"\f2d7"}.fa-facebook-messenger:before{content:"\f39f"}.fa-audible:before{content:"\f373"}.fa-think-peaks:before{content:"\f731"}.fa-bilibili:before{content:"\e3d9"}.fa-erlang:before{content:"\f39d"}.fa-cotton-bureau:before{content:"\f89e"}.fa-dashcube:before{content:"\f210"}.fa-42-group:before,.fa-innosoft:before{content:"\e080"}.fa-stack-exchange:before{content:"\f18d"}.fa-elementor:before{content:"\f430"}.fa-pied-piper-square:before,.fa-square-pied-piper:before{content:"\e01e"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-palfed:before{content:"\f3d8"}.fa-superpowers:before{content:"\f2dd"}.fa-resolving:before{content:"\f3e7"}.fa-xbox:before{content:"\f412"}.fa-searchengin:before{content:"\f3eb"}.fa-tiktok:before{content:"\e07b"}.fa-facebook-square:before,.fa-square-facebook:before{content:"\f082"}.fa-renren:before{content:"\f18b"}.fa-linux:before{content:"\f17c"}.fa-glide:before{content:"\f2a5"}.fa-linkedin:before{content:"\f08c"}.fa-hubspot:before{content:"\f3b2"}.fa-deploydog:before{content:"\f38e"}.fa-twitch:before{content:"\f1e8"}.fa-ravelry:before{content:"\f2d9"}.fa-mixer:before{content:"\e056"}.fa-lastfm-square:before,.fa-square-lastfm:before{content:"\f203"}.fa-vimeo:before{content:"\f40a"}.fa-mendeley:before{content:"\f7b3"}.fa-uniregistry:before{content:"\f404"}.fa-figma:before{content:"\f799"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-dropbox:before{content:"\f16b"}.fa-instagram:before{content:"\f16d"}.fa-cmplid:before{content:"\e360"}.fa-facebook:before{content:"\f09a"}.fa-gripfire:before{content:"\f3ac"}.fa-jedi-order:before{content:"\f50e"}.fa-uikit:before{content:"\f403"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-phabricator:before{content:"\f3db"}.fa-ussunnah:before{content:"\f407"}.fa-earlybirds:before{content:"\f39a"}.fa-trade-federation:before{content:"\f513"}.fa-autoprefixer:before{content:"\f41c"}.fa-whatsapp:before{content:"\f232"}.fa-slideshare:before{content:"\f1e7"}.fa-google-play:before{content:"\f3ab"}.fa-viadeo:before{content:"\f2a9"}.fa-line:before{content:"\f3c0"}.fa-google-drive:before{content:"\f3aa"}.fa-servicestack:before{content:"\f3ec"}.fa-simplybuilt:before{content:"\f215"}.fa-bitbucket:before{content:"\f171"}.fa-imdb:before{content:"\f2d8"}.fa-deezer:before{content:"\e077"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-jira:before{content:"\f7b1"}.fa-docker:before{content:"\f395"}.fa-screenpal:before{content:"\e570"}.fa-bluetooth:before{content:"\f293"}.fa-gitter:before{content:"\f426"}.fa-d-and-d:before{content:"\f38d"}.fa-microblog:before{content:"\e01a"}.fa-cc-diners-club:before{content:"\f24c"}.fa-gg-circle:before{content:"\f261"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-yandex:before{content:"\f413"}.fa-readme:before{content:"\f4d5"}.fa-html5:before{content:"\f13b"}.fa-sellsy:before{content:"\f213"}.fa-sass:before{content:"\f41e"}.fa-wirsindhandwerk:before,.fa-wsh:before{content:"\e2d0"}.fa-buromobelexperte:before{content:"\f37f"}.fa-salesforce:before{content:"\f83b"}.fa-octopus-deploy:before{content:"\e082"}.fa-medapps:before{content:"\f3c6"}.fa-ns8:before{content:"\f3d5"}.fa-pinterest-p:before{content:"\f231"}.fa-apper:before{content:"\f371"}.fa-fort-awesome:before{content:"\f286"}.fa-waze:before{content:"\f83f"}.fa-cc-jcb:before{content:"\f24b"}.fa-snapchat-ghost:before,.fa-snapchat:before{content:"\f2ab"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-rust:before{content:"\e07a"}.fa-wix:before{content:"\f5cf"}.fa-behance-square:before,.fa-square-behance:before{content:"\f1b5"}.fa-supple:before{content:"\f3f9"}.fa-rebel:before{content:"\f1d0"}.fa-css3:before{content:"\f13c"}.fa-staylinked:before{content:"\f3f5"}.fa-kaggle:before{content:"\f5fa"}.fa-space-awesome:before{content:"\e5ac"}.fa-deviantart:before{content:"\f1bd"}.fa-cpanel:before{content:"\f388"}.fa-goodreads-g:before{content:"\f3a9"}.fa-git-square:before,.fa-square-git:before{content:"\f1d2"}.fa-square-tumblr:before,.fa-tumblr-square:before{content:"\f174"}.fa-trello:before{content:"\f181"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-get-pocket:before{content:"\f265"}.fa-perbyte:before{content:"\e083"}.fa-grunt:before{content:"\f3ad"}.fa-weebly:before{content:"\f5cc"}.fa-connectdevelop:before{content:"\f20e"}.fa-leanpub:before{content:"\f212"}.fa-black-tie:before{content:"\f27e"}.fa-themeco:before{content:"\f5c6"}.fa-python:before{content:"\f3e2"}.fa-android:before{content:"\f17b"}.fa-bots:before{content:"\e340"}.fa-free-code-camp:before{content:"\f2c5"}.fa-hornbill:before{content:"\f592"}.fa-js:before{content:"\f3b8"}.fa-ideal:before{content:"\e013"}.fa-git:before{content:"\f1d3"}.fa-dev:before{content:"\f6cc"}.fa-sketch:before{content:"\f7c6"}.fa-yandex-international:before{content:"\f414"}.fa-cc-amex:before{content:"\f1f3"}.fa-uber:before{content:"\f402"}.fa-github:before{content:"\f09b"}.fa-php:before{content:"\f457"}.fa-alipay:before{content:"\f642"}.fa-youtube:before{content:"\f167"}.fa-skyatlas:before{content:"\f216"}.fa-firefox-browser:before{content:"\e007"}.fa-replyd:before{content:"\f3e6"}.fa-suse:before{content:"\f7d6"}.fa-jenkins:before{content:"\f3b6"}.fa-twitter:before{content:"\f099"}.fa-rockrms:before{content:"\f3e9"}.fa-pinterest:before{content:"\f0d2"}.fa-buffer:before{content:"\f837"}.fa-npm:before{content:"\f3d4"}.fa-yammer:before{content:"\f840"}.fa-btc:before{content:"\f15a"}.fa-dribbble:before{content:"\f17d"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-internet-explorer:before{content:"\f26b"}.fa-stubber:before{content:"\e5c7"}.fa-telegram-plane:before,.fa-telegram:before{content:"\f2c6"}.fa-old-republic:before{content:"\f510"}.fa-odysee:before{content:"\e5c6"}.fa-square-whatsapp:before,.fa-whatsapp-square:before{content:"\f40c"}.fa-node-js:before{content:"\f3d3"}.fa-edge-legacy:before{content:"\e078"}.fa-slack-hash:before,.fa-slack:before{content:"\f198"}.fa-medrt:before{content:"\f3c8"}.fa-usb:before{content:"\f287"}.fa-tumblr:before{content:"\f173"}.fa-vaadin:before{content:"\f408"}.fa-quora:before{content:"\f2c4"}.fa-reacteurope:before{content:"\f75d"}.fa-medium-m:before,.fa-medium:before{content:"\f23a"}.fa-amilia:before{content:"\f36d"}.fa-mixcloud:before{content:"\f289"}.fa-flipboard:before{content:"\f44d"}.fa-viacoin:before{content:"\f237"}.fa-critical-role:before{content:"\f6c9"}.fa-sitrox:before{content:"\e44a"}.fa-discourse:before{content:"\f393"}.fa-joomla:before{content:"\f1aa"}.fa-mastodon:before{content:"\f4f6"}.fa-airbnb:before{content:"\f834"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-buy-n-large:before{content:"\f8a6"}.fa-gulp:before{content:"\f3ae"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-strava:before{content:"\f428"}.fa-ember:before{content:"\f423"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-teamspeak:before{content:"\f4f9"}.fa-pushed:before{content:"\f3e1"}.fa-wordpress-simple:before{content:"\f411"}.fa-nutritionix:before{content:"\f3d6"}.fa-wodu:before{content:"\e088"}.fa-google-pay:before{content:"\e079"}.fa-intercom:before{content:"\f7af"}.fa-zhihu:before{content:"\f63f"}.fa-korvue:before{content:"\f42f"}.fa-pix:before{content:"\e43a"}.fa-steam-symbol:before{content:"\f3f6"}:host,:root{--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-weight:400}:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2"),url(../webfonts/fa-v4compatibility.ttf) format("truetype");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f27a} \ No newline at end of file diff --git a/frontend/libs/fontawesome/webfonts/fa-brands-400.woff2 b/frontend/libs/fontawesome/webfonts/fa-brands-400.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..71e31852689289b8d7b94ce0541953df40f76500 GIT binary patch literal 108020 zcmV*IKxe;qPew8T0RR910j2Z+3IG5A0>lIW0i~(~1OWg500000000000000000000 z00001I07UDAO>IqkPrZ*V9JWPWXq5g1&AOAAf*9faX{JagbJny0D#hd9+VMXC$)P3 zR8>7Bgqx}c{Nblxe*5FEfBy5ozYL7s0kLey^?bws|JVJ$bM33rll)Pkd;+i)x2ZEm zx>hA_64ykWc@MbR84+WD0FWPHB{@xp7KZ0X2WbsvkRLDaS4JzE3kwt zAV`oA5ECehVKKHD6_xK-&^-Es@%zz_Uraqe;nM2sy#kU{5;w5Ar8dP4?pET&c4z{F zunePXx4Dn=iOKVqPCvf|t-ui3+LcLCP>%DH`HNE6{z%m^bih>JT-6Yl zR8%+DwDj}*GWFj%=iYNJJ>VH>W+cr>^8~Ba@Y}U_y|J6vK`;c!DS=Z=)4B*riO2=Q zq<}yDKc8p~MbF!`p2q@9$O~ShMf#G7CM^iVf)cjZn5)Afb2_XdT1#n@V3Af15!*Or zWaB%oyyvqszC&l6xol_7g)b_Fb;vj?`2TNaRzAfDrLl^+LfS$|TS{~8dq0f#Bl4X6 zkqIL*QAQ+CMkG*1WTK2n6e9sNCjnG*QAPHgDmM3=YO&`~^v)@=dz6d>MnnQ5BCG10 z4HQQNiYpT23KU7B*gaKbpWRj?T8wCM`jE{Y(PES|tljn2^VY9ouPAnpG`uw|M+?(i z!&@6eF-W?m6A?~K*T2e8t83mONtwo$1^*gjR`;!Y%3=qmCF5Jj+<)h9?R*d^8X?(i znH?--j=JmrT>&YI5R%Q7nbl-0gYJF0ty>jQ0U;!liHSI=j^JTSDI{$ZTNaE)J{bhL zyg038uf>C^*8N6o{{z4=*`|j#zI~o$Z%!TvuwLpv4%i6!vIleJKg&PaC3V)vzi4^+ z?Uv6kGsJlmqUFYUeWjZyfVS6>z*gzc?>Abw42(if=xSBV@3YBXyX}Cdr(D*fAy2B; z7C|d|2%C-bmYmGac-6_m_T%pU`JlS>C5j_KO-<2Y>F zRt!R=a%_#~8un1Wn;FZpL)1&Z*MX(frG)9bz_&+d|2=>gIaT_Q;Oz z6FAT_{*lIU2=i`WIH;q0W!sr7``&iqFjZ|b+;-_Uy)%Js>Qi^L&u#4%IlARAL86n= znZ_eIOPklCvI&jRTe6cFrtNOs6wYI7xhyQ{o9clS>w*Sgrkf-skIojwzeHa|NS>z2Cyc{VU|Dv@L8R)$N~7N{eu~w4;pHwAXILXo^GH#S3+^ ztsHbjEUKqe&S6!%ts}|%d>PkoO-pIAl$?R#aA{MU31~TZ{4)AYJJ6l&J0X;(!Q49L zeL&ju)_$+y_#3S;XplpAjjiEZ2L?r7kPqwtmsmRk^*^P22p|+d2!Kp^KVk<$t{j3) z2tjn&{|_CZ5mSI5PY@6R57Hwua-uj|pglUEBRZiox}Yn%p*wn@Cwieb`k*iRp+5#- zAO>MDhF~a$VK_!)&7ZYl)<#(yXKk8wu^nM2v{Tq=?CN#{yQw|No)-K6&7HU9-nM)D z^quvt#Jd{rmcLv1Zr%I*@B6*q^FGGg)!W@W!8^;lz`N4>&>Kq$DI4XWB2#g^%Q?9u9*L1FazpOO3wbT?>q^|<%lM{cb9)?eCR!N118#edCz&mZgm6bJxFkBrEH zd?A;r!VivYlSZr!tPS~1pqYl_vl^sV-SR$jnL09fIG z6=sD3R)`Mh|N5`?Yt}#Y5B*Jl)o=7GeP7?v=k*zV8qoXoZoNZq2lQM$Q%~2UbuZmh z_t4#SH{Dvd(k%hq5YP<(T~${GbR}I;m(zuHR-IXAQrp!UwMwm2%hgh~SS?Zu)qFKi z%~f;MEHy(-2h=n*RZUV8)p#{dja6d+H9`#sRDab^^;W%9Pe657byN*iMOBV_E$%5G z?#Z}20RRBv=D|bKmvkj!6Z{0!U>$gmAt}_{J}pe>K+n+=!tRV>nKj2=L+Zp1}<2$i~?go0hk6> zV7Fg;9k4G@0~~UUH3El@H35ft&0w*)hXXCZ5j@rk968no95vPs96ibt96#0roG{i4oH*78oHW)CoIExFoFaq%B_10BP8%BrP9GZq&KMg7&Kw&9&KesB z&K{cp&Ka8o&i$Av7@8@e07JW#P!B`9Bd_=1kNsYfJG2+$)VFg9MaW7eOhZ;z!VF}s zCCozBS;8D-+mtX5*{(yf0NEap?FB4Cb{1rp`FucjC1h6tTfvwAa|zqPS8tZEEqpyb zT%p7&e6wYVRr%(l600#7zr^YcmMXCZgOy9H$>5?AYcaT_#M%t59Kt#b7ht#`)@8US z4QYLbw=%pP8!&vH;R`r*yB)a1XmmRbVN|-K=#Ivwbf;2@&FRh_(iU_V(OrTq>E5P$ z57%z@6_?n7n7G7_#2h6iA?Etn?*qEA827Ogu{5y^p50iFON>lxTw+&Z`#$YrVh3U; zT)VL|Bj4PG*d5O;u_v(?R^8b9>|;M-A7Wpew!}fi;rJX7M-oTjaN^iw2uBhp5vSoO z;#?|mJaOR=P9QEOF2RY!<#Y*lGI1qwGfpAyB_6=}#G}OHxR`jEcomlsZxSEiO5!`> z2i!>fLi~zbh>jS6+lar3zcDE}5;-#NA;%!c#C_zrbDXnP&Gu`jT%AyMcbYFpLWPU z^dr%aO52Biboy~=2hdMQKRNAi`YGvWq#Z{;GnKT{>Eoe(2pmEKgf^i05PDLHRzv7l zqBRf(muM}7p(R=e;Z`55hwu`@tM8$?;WcB7;om^`3EBjf16Bsw57wMZC;@9zLMd3s z5{kjPmyiaws6)|KcO*ya))2isAi6JUEvbP{ZTiB5qX9@A;C5}g6NSfaCFS4wma z>|2S>gL#QAfc+}bMMw%ubP1A@5?zL*Yl*Hv(xXIIAz3?wu0e7RlJlVJkX)n^-Gt=f z5V{548oUkYHuy*yLU+MOfsY2=1D{GIdH_DNL=VB|4yi}r3&0nG9)m9jU-`Yy0lpJ_ zH|PcUULHcP!4H5R1ib-2O(l8$knVl+&GdtB*cB=2#sZGaD)fJT$mBksE8JX1$jT#yicN;dW(cWQ0Ck!3h zZtu`x|6VJ^$?#(KE1~IeO+9FLLZAmCI4i z;L4BRcIetSuU#eA;|d8$kC1RyO!91?Wb8+2QckPI+-HkwQRIbGGELG0U3VN^PnGsk z%P_3e#bKR$jlA{&IlhcAibCAsn}3hoYgciEE96dcA0c6qP7)VowWtNlNgByCK~ z@g$j~Nt!6B|UXX8mW$%~K5jI}hW9CoXG6O(a^ zov0Sod{!;WD~_&HhOwLz32_#IdCGgL+i+_C*E=>y6kFTu95b z{W$g}%{rxw8TAQEYPBe_Z6(669;LeO;7HeKeGy_ja<(iP1vYke=*9t>JW0|AtWc>sLhd9LSz;!~f zm``R!o(&XajN1;VyvUVQCRvJt&R6NWVcW*>QF<;8{uSL`uZz2-q@?o(!?q1wKR$Zx z#DP8xgCHE<9deBvbbWlSYgciID`d~V-aq0OO^>iBrt8ngXt(uc41&|v4_ymF)Q*_%t5r$ z?_E91ytm|r@scWT$T2|<;*Vbt5=Ol2Nt&dwR8r+dUd$`0CQ`*onxrsZKQHov(Dn7R zXV=$h`j@@3+-w5P=5iIJN%`=3)@3xC%ZKmaGV$Bm1XswAkT5TD7YlIDK70d|>8uC) zSK#^HWs@?c*_O3&FfaG=<@pVBfWuJNjRVcLER!upd65_I8Pqzc?AKjAadK&yV)<=fuTrQ!=>2?cFi2DA(r25Ha8^wgyNJbji7KhGL6L6)oSkXXMUs|_ zN}rxA6c{j(R8Wc+_Ef7QM-LhH;RBL>OR@n|87Uu_l!IU zWB1i4@|~T$fbX@rWm!saZt;2-blpFU-B;_nv&K3>4`l6h*szo_XlQfC{Mh6Vjw)FRnmfs6R zS5^=9Yqp)F^?I7vc5ZV#-?S}V2aM0IZUdmpvg7pHy141@8%d8Qka4ZyXfYoKjb_Puy=EgA&JPg;&E~h+ z3<4ay$9>9KMoqY72K!g&z~e=-`X46%U-d_nuR$D;Rh&vf=x5 z%Qg)_I{@jy+=^?1G82w9j6U!%2JU?%&_m6q$FnP#{2)@yzXuq3+Eit#p2SX z#X=8v$A*}t2Qy5+A0g^tR;uZ`bE~LQ)m{sYY>v62TdP#6$Tb3oezt$6$W!ZxBrB|=K_xkJc&JYNXuU@t+OL8vk3m0s` zxwI_nGJ40~_}u3RAxzgsnxr5zLPE54xHeQFEY?Jt%{Ag)7rU@=<+&b<9xg^h_rT+vo9>fvaSwcGw+yci8}bg0hkLwm5+A+C^BLXc+@ zNamJPyQCZ<@^a!!w;eZ3PMnx*Zca{|m>9OL8^+nY?mD~XI*z+`_O83m8isD$1}2-E zlY@iF=B8ip0-rjyzP-J@y>|MvVcCYRw~f>gVyv%G9k6-W>zA2t-G!7paerF2J3yyh zPWNkL;>zXAuvDXZu%5S_9Ry7r93Lsxd2HcwtLx%e3gLebTEHY(^V=-wm+FAKZdOzbAaD`L~lEnOcd+GzvuNRmUaIQlB=I-jM z^}4ctYVVC42l~mA_qE3+_v{&fZdkUlwvWqo-oP;1%Gw<{PI}Mw4sD5nh5_hH z@bmUA6Ef1aWr{YPKJc_yl&9T8AJoYq*T|Bm19EiKT=pTGVjr_AOOIrtcg2$R?D{E| z0o$_*znN?ba+Qm67?5T4!$UO6(ql8G^1KX@hy5{4CrRvkXnNKcNjNuIREx@=Rf`fm zN7v<<<75X-IjZGrPMT7U%8f%V=j{WQpTEkbH5{{aJ(ZHYkP4M<7?y2(bfZiur{1ht zwrNtPz4L8&UKei|S2n*`=GyT$UtRd*=bt$;8Pzn)Hh=3~?QQq!-LCuet74y~@&`pe zFZ0xaqwB3mwTN<#A79A@kg1X%`^evP7hQtD_8EasVeTuL4K}fCjYX9v&i8(=lO%Sz z?ahYo8*gr=^%8tS1mFB-Ahc9i4nXoH$NAFn-=$nhwaZ5x;tJ{1Q}_*srd(R2I333> zln~5Bv3vY);SPMa^p1Z8OC?kSsiPYt$4$`X%ABm;qmG)66}l2veYtw72bZ7vvN~6)TlK5z)gzJDM`_DZ-Fz`k za)OX5Nhe9Vs0z=JoWk3aN-Ck`CQ_+1-6?J2fUd8fUCXZ50MoE-{km@YPlNjWtl^>X z1f7q!HycOVZvE&k)3$XDFz;Nc`r)BJH~1vQ(zt7H+t~b2W2+9I>X3{rL#!WKj&2+} z^eQ7^9)6-j4$Y``$5Oopy0pCal9#;XC4)2pl5}9BoFjecd`GwZdU&-z%r^Fxm*~|i zNYcR|O>}awk1OO+8v>wx%XBE3@+_}?oeG|lkfvTSo@@a}(@l%*dJ{ua zP+n9jSI_BiCb-?+qNq0;Y0a^vvs0Z5YxPKQ3Y$Q9SWH{#KomyycJDLad+zm|o$8z! zCzh=wGtvz`aG-R<_1c}`K`pVErW6H)W!knBoH9ryspj0GLL8*2 z1;Jxp(^ir(#%Z^!x%x*Aec;t3v>}YCR+t+O1r12F$FE#57nVar%i{5uN8G0}&st=*CgaY%uS8bDbh)-|@?Td7hq)jy3Nhl^&Ua-7cRFP#_ z^4s4ClF!%`J&v_2f(siT7hKrKzm9ENa3L(qIz9}65Qmm!30q&G`{lCIpubc~9a&zO zkZO=#a%`KHc+s6&nre5xsKMUo3c=s2C#kyaHZ4i?_i`a@d^Z=uKK^426CgSMQ{=V~ zTv(=kd>GRh>G9vjG{&KAT7qDX9xymk{C$j)T{sI$q(hsi@GTl)vx%+0<{IKJQaJAi?}teE{Z(% z+0ksvG8ol9B00l;M5D~GEL(f#8NA3|HID` zMqWcmINpX}h8jsaj(yN(Ud^%z-cOWDrcoM|Njcq>21&e4F#FwOEwFX!XJ;kVBro!6 z91HEiH&GucVo~J9Z1U~6tlN$qam)9@dTIk%9Lsu20pO1(Ca5(w8x76y++a;)f( zqsid8r7}#IrsL>>sQs;2hN;!m8NqXtVe#JR(j|rNz}{| zig+*>zyzg~0x&_7)YAl~_4W091lDSGSrAf~hJX}G(KsQdhWTd zI$Kq}KIrz=J@YKnKy=c(c9j;mLM-BuHd&C<7I>}GixRVMv%?Xq<<~gN0C>HavzIw+NIMpQFzY==>Exv zA3nKH>Hf)wmvh0}Q%(^A@%fz?jPu=H&Kcqz2FkCmL2z2To>Q>9<~Q86vqS05&Ruu! z>`(_hw>U+;+pT@)+BwcBU0$Y?@pEfbaC(;Wvy=;pw>Ce*72=YTkV=J%YEkt&fdC)I zdKUkbe*e+){W;X@0fT;t>#jRSuZBV3`Q;o54=)O6E|dP}|^H0mC1DEkAnPvH9L zV1RUOeS3F0-NhT@c$7~YtxmHUH(aF|K@edun6kA^`4**HoNqkvs{gz3prl(=yhLDg<5jQP*o1hApj%Wvxbc5q^#CD+)z^6j zBq1AwjPi`?09`w0$^9rk`9XYmbN3rclX&(Opj&7L4TWTh;Wtvu; zG(5+)wthm|c9htTN9dLbv-xbkDDpxLY#6PzW`w1m)g%LIhf z?mxZ~IgaB*xcv8v!f&(+r_pFMoTAkLmOQ-wf?ltYT27XD?s5ihDyr2p_HMQK(fE1C zuBEkF5OARzQCuI?Y9D}U=^7WdlJdJ9C(6(b9shYQtZ(XLqyds5c2)y<#rUgHefvPfC)Hm6j|TK`Tuy}0RynzD6$VZ|28X%JR30a zv`4991-|>**SUURIh1;zQ`(ze_u3^>pH=FIx64Q*5!b$ipTV;vBHQE~xkTPf-aQKK z4l+!xeUZa(+O>;38)V~RT*%&}k}3+d2!D``T{am`R9zKQt}a+qi%Pj)dNLB9Fevi* zcv2;4VpV$b$r6)k600;CX|*W)u(v7kCeB@t!Zg+Ep=pMxX?PA3UW-z|3i3`Y)|o*W zq*O`@V3RUu%W^DB^Oa%Ho>GRMYDuKxxV^~|&vhvcg6=4cOt`LZ2+m{6G%2g4)5tJ9 z)9?~5@Gvw-Y1D9V4+CHr+VL%2wpd(${i5OnUF!li=w_DLlrm<>wbiYyl@&I_Px63s z&N`8j_4I%Xy;UgL>-N~6Ii~Kq&0&%-%c5)0btoxgQ?u-3)M^PZifzV04PAUPs&UgY z7$<}ngb>gJd<&VgVtiPT!QV=pK=h-|i=d7G(qMT>x@3f+vXR4fM^KzcymtS{_ z9PhpEb>I2A*L~Z;c$&;wLFhC#HqTY#%_K35^r^PeYPJ0t zk%W+adWch`Am_=Q=;NPZ^i< zS)pVm6@RmwrjbHKE%S0(P3MruC`rd&=A=m)sY+!)mbZ{QG;yyf% zm*I8z9DWMFOvtFn=hdRfH!+*cv{*AM29rFb)#Vy4@}io(%O*@#%=icfo5-_y805UT zgDiB7SVa@5-X|T$(_KiFUHXnQHBhpPNR4A{Iz;3`#!(kZtYn&W5ykIol<9Pmq`JD4 z>?284&ozK4T8$oH~s8jC1G!r7`1NP?mDWL1~>c z21#jMP)ZE|bxuKD)HuLGB#b2p8Nh3)|Hef9ohkb(p%l2@6evCarWDgqw4$uaDiYVJ(PwaF(#Ip2^D1|r|_ifb9{2-HzLFoovFl3wu zLh}tx*I^hD@wcBJcrOp=|CZup*osEW4@O8!4hn$-_=XV&XVQA28I|waXYLsnS zbTBy|ElFI*anq!hI?g@RTn*>dk{`KHz7lRJQ+`>`&bltQ4KQb zLX`&7G>PT(-@rwbMssj54tY_?i@d;x|C&+&{BM&m)ExiB$x{clAdbU8X=16_0Ko7= z984N|_&mlbbZHpDlme8OaTw|x^z#e?!{PjpQA#ZHhWrtBho6RMq zI5%U*#&bZsv15aMC>^d7>l9$VzsQCl_`iyxKZ8S(6M}v-siu-@PPbDIqe+fMcYS|R z;Af6+v~9n?yxi%~JGY`BFpmFI8{O-~alO{4#}VhaO#+8EZ*BDsvdnSOyRMPvYg^qo z2u-72D^|K)y1GUP;e=efN(rtIkMzi#oVSxL*&tR?teUediYnQ~w3^L}d=-OOrXrA4 zMRBU4IN8N?AG5j22CACvt>e^NO8?MaLzXwTLR$^3SX`#~ayZ-L!<3GUqEC2e-|L}^ZpXhyg`@ygL z!=L=_Z=)at#J+Zff5s7cgpg4_pUj3CNamenU?DCSTCSxMDo{(yo@PFq=U3maood;X*_@lTv^$_Q8FVQ%ZJSeS=$c`8p5qG2Oy74*MkCKMr39d) zr*t<6!3a$lqNZk8;#gE z^uX7d2%|8J8~wVcr0+RQI6UR5+_MP8_y$3|&VE?TQO z@%s1nXBlM{=qqj#A`#-bLL`iG#-0W~MA?lHuEQw3kun|PF-jSIjDzZ=T@1J%wlYZ%9l%OgED^S8N3gx&qxRkQ2NX& zh29Ws2iRevp#f{#bBu-M?kJtqmBhEbx>Ca>=hE75cRj|`&Ae-0!PoJ5(jaT(W^x&` z#()*}=CcOuv%Hu9x+MnLwnbg+W7>7~IBY_eK~)v5JWqv1Uk}aZ#XL>0ZZ8DLnZ=^l z1A4v1;>_}!h-a3PRDZ$|`pr5(x$;~pb?SPGgF?p*oz2ay@#;$Nmj-=ZIR7N+iM{*r z`EAjMv*7qSo%Qv3zZ;koQc20x%^G7&I1bfy>NtWirtxsK-Uy@N==i7qLvbmU1T}m0 z`U86>IpNV4_9Q(-N?g(>B{@&-A11BUSs~S=0%KdMMd}g#f4i^dWh~32n3ri8XFoP-xAXP9*~!{y z4L3W!5BPqk?fbsp4x~~th=c%?^gnIaTaIlz&6+*vbv&g2O}V~QO8P(BgPS%C0|TT@ z4D>vI!7EJZ~c}#e*1TNFWEeGY7@8q=?yph>G6kN`&vAe?J*xA7w93LN7|$yQ*s-54tXK@ z6!~33s)3p#ttsLR5Seu=*UD60%okOXN;Pa@kay58uPr!Ky9r9CF4n)&HBkM z;s*HBD#cxO52~#|8EZ4jg0$%ef!}m|!|+>-wLa!i>Uj`CZRlWzNvV-F8-`(;t@fbb zZkeWTG#lAnXw-|MQ3vXcqNq18^#ISK)Psuze&|V|Gg)hOTo*wYCKOQ;F~+IEWG(A@ zKEg1>Wu_>#A_b_3QQl&Vx{OgM7ps9G@bA${1*jimlrhTmC=7>#D0~5|RSUG&r@Qbx zz*9opw=N6=FbuIy<7hDG_Tngtd)>hxis|!fuIIV6)bl(q_2GGd=b=lLVKC_`?l^YX z&~-|61Jt4bg{OpP+fmcdDfT4blv=9LVT@A7I*hVZJ6IM>5=98H=pmjaOJs|jAh(ex z$s5Rr38`)cF<_*0QG@md9BNpmNjlwyNnygVZcx~GGM;48MXAav8|B$}k_{9a$5A?- zl#42yO;{#f3_zsrc(T>mc`=?KlXca%aGs4PqFT&1GU9FRN6lPLUhJle>d+o1f9^)AZDlSr0O zc%t$m&-%beC(_8=xnOV+#f{)*hfK=ys}@_t-_|J_^`+xym+B3mQD17;bzQe@{f2vP z(s4?~Y4!F}y?~)R$48E#>xP2^$Ix;7O&!p6*U=3_cO2sviUv8ZZa_C2N5?(Q;x{+F z&5hMxE1GpQG{@F-{oFZS*K9{?pnl%fbzOIV$uV?5H=JKu)t&xE7n((GlanI1(L)@N zJIEvCMdY>Q{e+BUC{r@?JZ_VBY-3T4=h;A`AFoQqzORx>X5)#>#*@KVN2a}1=Zg`8 zeJpk{jg`y_Rd7<#DpHx2(`r6j#lXdAHlJA2w%l?4*TSr zqfuW!x!Thxno-*{)-}~^tj?C!45Q(-I0ST1jiL=5&}b8&8l_qQP*yWdp|1zNrbnj3wBk; z$9qth|7@5Sldk(nt=p|VYIM>Iv1wdLy2himZnyS`+f6QhXY5_DT*tcTCZpXX*&QYB zMay-~3trsYNz(d5F7nszaHBuyVlE?#gi8h~-p-KllEwH^1OXG=%iHoc#Vj93Qgv^{QU2ud&a~WJk5W z&W;cN0Y`_6;gBBD;c!8R!^QE^szbuTkeGkDN5&I_0!GLI*}#R6*+g3-_|1V?mc4Z} z9*^ES|3ahQ7!2z5dVMfx)M|(;94E`(`o3DN634vL|B)WzJIRDxPwpjT6sbv+10g>2 ziC{dDN=AFhAhxIB64JM;lM(vJVtTa9szo(Pbp$g|bh{@?Nj0H|?bdvKFj(s;$+_pc zm}FUAb-Jk4mY3(FEX$j{UNK!>uGMh-iEgc4>oyv$3p$SDr0|2zH^x9Vm~W09H(!b3 z_}kAFOXzlQnwzGQ&1RF{)o!z*ICI1Peo@dk9vAgGkdIEDyzu%BKL9$N@pyhCXcC`9 zv$^@(^!m#O`|Repe)3#eql6G52)Xu)^bkj+PBOAgCS->YOp-L5#4e?b?&Tf7;KfjNm_4zqtf2^MqRuK z#zQxLytn$$?qLX5#qgi`TxUsN>-H zsPUZTe|_O2D;Fi_G(G+H7bDcAV0T_7gb+!{wIf`^5xGcSOMVU}R&kM#G*MDX$K?jq zG@VYSX;LO>)CQ6PSWG7-pD}Nen%yy7%%$7pm~_IV-!n)f6=jVlhKc|p;EKdok(!{# zs`Cly%^w%}9MrYWWKqq^S-B_|`-feYJjez`p5^nwJTJy>mv^49vWnz+^Vx*BP4$5c zX_V70aB1y>EZ;=Dz#-{@#b>I#fd-3eT271~kV5$cIcCBxpUOljwsyK%GmbIge9kAL zq?-Ib1vuvqZ*Ljq^*26(4(p((gHlEjT*;topp1bstg$h_p!(ehCpA^phS>^d%b;C9 z2>dPwz_?(;IEvyS*y@{83JTC41bTt*2R%$Q-AQK_hhdyi%J!!5@$+ooy|tn1z23?F z8|hmb>XZYVGq%RKWQURpa9Y3n;G{0g*`1W)cp)qjDP6{xbI#6l$f%6_J)qzKV_fht z-YWP6nAEb_XZ|Y7!JautZ@%@BM@}CcYmGkEI6zz#kJ3%##|W^=xnPVn?iDBY?!NoF zL7E!ics@na@8@}MkWj%mIAyiGSY0jh+Hqn#1CEaYAnp%Fd4CY^$p;utMCdw=yr^M< z%b3i7*TiRcZn)vhnWf@@whl_4)1cr~2)4=CCKG~k(8k^3#L2tvzV3Wfa|B z1v|&NV4R<9^hYhm1!n-u#&^+dGR_#-c_ugmWBZN%u+0SLoLd(RuU(}*Jnk)SA_R+8 zPQ$FTXn*WRMO%;eB(RmKQJYQpic||M7H_lk{XxG!*w+kG`zamJHQUlYb32;NsX?hy z*6&!xe!t)E@9VV^bJDEvUFYl^trn?)>eng zvJ8VAAN=`^?(b!~bG){eDa$f-Mun zRN4rfy&YAO%3|_&@GN!U7SFHMd~fdYp*E1OW>Gj8tgLKquB`S4VZ>f72ie9(cKl7m z5knkPyog3IrEx^RrI}^ThD_>?9Yu~^Ph{io`+cTqx}obz;{udXDK#%@D(MG(+}HO5 zshW}J3t<>x;A^bk#}7u7Mlq%F|Ec+&=O5B`+~3{USXu7%eBbwbz2%jSjcy0%{9epx z6jMZqaUNzP0qR!`;Yvsp#vpyGY1L9raSVJ+TN8ns4hG$hWp%oPLF$G^5c<9ldRntB z`-60lG=sz>C#!^1rJv2q=`}>0`DGksrJv2SIP+i48ke!k;&Ohuci8LoaOJSq>-CO$ zB{>dyy@P|JYvf9=*SmZLq}S`AcYN6Ep{93maDY$zVsPy*@Qe5aiO8LV0CE=dC|5G( z;3G;vEgLAe!ZGQznwL*W6-yt66JzoyL~%YbzU}y^6$C-hqT~%f)}*H!re~U_>AHsD zCa#-0j^mWx^~BXp>bQXGP*Zo4#C22C&^@W)6G6~AKBD&Jz(Ferj{k;I$rw0iocFj= zR9qG%uLQ-}M;uC>BNuQb<4P&Yn56Vd9Pds>J7evkYK`9I`85D~(*Q9YSQIlF)~meU?yo9FW})EiHtI~)}Gd{L@hor^S5 zgJM>R!PJfhmRZqcJQ+`v7L%m80>Vidsqv(U@$@fpRk_&2KDsE{+NRMqWE{m($znVi zWMg}hb)ULa1H7!#iJM6(as0;K0zmelL@b|;CHM8~!m zq^N1yWkwkX`0p|>N*Ny5p+V61jX3cD&Gf^NbIXzd_8~JAI$^99!wS*6Dg4 zy%&NB$^~Uq7#skIFe9a)0`3^t+_=D~HZoLTAc?yj%LXVl%|=T~O>^B=D^0GKpu3S8BRZN|J${i&V(G5;`^0pmi z+llYkju%DkdI!DU=JDrOCdb9Co4p=top`IA4ZM$}Ezj39(=koJJvr|7u=OE8ueVv& z02A{jqiBCTS`m@G78S*l@uYj5NM20KX*DSFBB}Bs-$Rm$UY!cvhj39zwWy@Rdk2x% zKVUo2Bu)L)B(CXP>MTTLo9i*WaM}ZTLMUVDRaK65>+G+u< zmTg6piNLQzLzD$>CkR-Uq(CIh)TUbGkbALYQ`(qa;2{-`TaR7M6KPyVErENcb2(wh zaiX{F@1tH{jeQT@RM&CN+<6-q*|`D+-t@!QR~z*@rP#Afck}uWBb6a*)wqAFI$0&> z62LhhP2b|1(X`H%-&@J=6SuPey@H z;d#mPzS_186Rq~zD+nP(C*;~yIaYrmd1VasS@{V06!~mRGHk#sAigOg2OJ8h<=G%C zP)n1PmSd@gCX+9E@-`r%eXML1`ai)VYfH62=42)Smx(1`rjSe4B)EKJ+$~IeA6rX*^hZ@tvda9^p zZ`2)bSw7WuKuW6X)VD0|)Hf`fN{+@ zv)!E#3v2%dh!!)ata;?{#g<^DLcGW+?^F zZ#3E-5M0<;2l|7p?eia?j8V!cGZ_OIqkYOa1LM@9ltr?1DR(0jj023S3io-2H_)?Z zpLgfMK?627)09HUggUkcB>};MrmPt>3eQKjZOI^LFvn)P4o#DaHG)7|wjGVrMAN95 zjoP-b(KtA`^L$nHx;Xy6-5aX)R-b}qI>VZrQHOE{$~n~m zmG#S_x_2W931S3qfEwzQ|E|6$2JO}v^S30XLGrdq>2>k-hL|tUG+pORN=VO*wzCXa zT|Is4%Cc?O)HTZorljb!=Ns)7kR<)kG&vUr&a8Q=uI6y46*525$0auW1Lq zZzc)1G!1L3r}v8`(6!aQcC%$M$xTggX1JcW+U-%an(Jk^3s`pOdF{Bze6Q*22GD4< z+m;taZtLU#^FR#0-mT}hw)%aXMqGybj}robna<9a1i^K26@gXYD^6*c?D*5ZIO-xZ zzo3s5fg{*X^#3(Y<4g)D*A2I`40L7X^exLPj-4u8!A+TxqTQZvv|2zC4}#Axsvryk zFs?~maLVo#GWsP3)@W?cbp=%tE!yhhztz1Y+jBRewD?U z!t_j(aT#lO_CKg)c~+MjO|#tye83MHZQG{X4^h+`Yk%6FEJm^gKZ{mtRkLfUH*5+& z5mA!sS_7r8H5<6Jfg#$25}OeEavYK&nGiA>&thr;0-|bQ=kxH=c}7)Tx15*r4ByT$ znck6-D&gko%m~9)*&mP{Pi(7RXD|GTFK$)U*4EaaI>8;~3^d!+N73=rFcN38!2q<~ zO3$dR30-ePNW5rWMnbYqNR|0zae28LFM%d^m49WmTc7*jgBz*q0*z*~0l038w?9ZV zZIGEJ9=s2Fk`B(M2_;xvhU=zX&vCqN>bf}o>~}Qlb&53Yd;qJSuBd;Q)$}AY~9JP3qj3YC}ARA=hW_MOqLe!^TPE(EZIB}#B7t)iw_3^&P zd_x%3PVBO~l+KF0_-S2>LS57JFw%77G>o$0%5u&q=lSaDXcWa*R*b7by$)C>x~}WttNb9KGzfe@41gf? z%jx>a^B4y>^Ssge^k7n}d#34VT4d;v(j3$D>gn{L9Mo!nmiE%(K&>`dO%kO6no5$@ z3PI>^A5S66EGWTiNleHns}@DR3YLW#%R-Cp?sb9Req_cp&6}KfWcT(HkDOS?(|e<# zH5UKRGxX!XLvs9gNG@Y-_11@%*VZ1nG`~a$BmCMCz7W zJyv`P!eX8CUUzd6h7-eeo3_t+-F6r?j93WcAn@rV3MPB`NU<=?KEXv$s|6tthPApc zn2>R+9VbAVwA!)Yza5=i2?Bo0McjM~Fau-$A6blI=j@p?XK4~|ZKO5z0)SG(tk(^L zQe6Iz=YdkwYBnvCQgEu1Ttesshh&GG%wCCLr^yC`4ozXEIO&Jxv>Z>SxC5u6R=*yU z7kMG(siL_xs!gY>tBkF#PN%hzB^U)L6R+LD@h`pSJvdu;leE*VrJ!l8(@jU_+ew(C zwA-nr6lty7Nx%B3+b*>+d4tU+1(!I_L@K$8d>_-S$g6VNMWrTbqO!qe`k4FKVi$?} z6s`G9%{XQAbHJK#PMK=`Pv3jLuuoa?LF)G(RK{tW|07#%Pb_M^=7L4-i=Y0l@3W}& z1FXIKX^UEf5Jt!~pXbfV$+nHyjx68Dcyc`8Aun)Di;zFBj~xH!hR!BkZft0xN5?|@ z({qBVhy<&IjU;T+!>Cj&-p4VN{q07+6XzJk#(TGhF zLZDJrR7I7tDpkckiYis9Dxke-k_8v;yyK2L@3`ZRJC6V6&O7h8<0W_8Q6_gjJ7$_) zyNcIg{)o#NGzl3Mboa9?RfS5KN-LGcW!j%-aetm6`xWh1c0W@8F7cDzPxN}P>OK0U zFBQjsyM!l}-nLvUzy7d$sNXrd16J)y?faLOUVY^XAw&>z&ENbG`6MG_axeJ~lH=`` zd65^fB0#;0(yoS)L8Dff^{8$~cN6>mVUXr{$tbs>K{Ts|%gfWCNbK z&Zhv?+B2PPe33?ofwV!NSyxGQb<*nt{oZ8W^x|-b)B7;e_p@4^YRWP-g$tu%5sik! zQMgzdfl`c$DjW@mqi9i#fU(_)27{Kal-?TjvOH;2SDJrn)8Qkexh(O?uOg^O9Xzie%JccPsq;k80lp7no6@OwRgy} zEHAY67cxmzp~|XE%Wt0g_EUy+*Yd;j+z))9Gx+{N2en6^b8i*0Zjx^Qi@%u72qp5` zU+_b`fTX!)0~tqgsN{yiMKQ?wqVo(VQi!wM$9bEbg+pigeMdTi3*qjK#!tRg`x+l$oId4CJB9?^?kPs zA;g`{=AA+aaeKDAbJI;byBTLDIKmfj^n8Hf-bp?~ex6)~hNtiz`~)FkR8D+-XJkCd z)gVsFX{n~wWL!-r)A=IH)p#P~qzv<7GMfwuP$2EGc%Tx#pX{6>KH{WQGJ_h8#|Z#Q z@w2>XnvgBID7vT?1zVJpDaKrneE=DjimzB-<`i)f1L9+~9xypZXiVRJlC zhB&GrL0ObB2Kr}$Qnz##%hPPzf^Fzv-1G8P-v`E|CywtMIun91sRO14!nthMq;xbR z3A^<`(+$fvZ6}~kqmjC?rYykw70t1A&87w@7-Ld$PL-r$S(6e%fH5H_!A#S(5ed$@ zrYT`-Lb$LQHw=R^#yMqC!kFZ)Ql?=DrMQr~X&QDFv<3`{+x>TG36jTb!_jJJk$~evepzy#`I6W^Y zj{jC!uyqPxWt4`Y1K0Nx%IfWQ{7`|*lSVVKRKMrf8|`L8D8U2;v!s*~Ftyk;C_pt& za1WG$QqJm>Hav}UWtjo?Jg??~1JuxTC}nV%rl}haWlAb)7!r_n7|=aN1z<23jDiW< zl9cO|3C@(JiQ^xwGisSIRAiVGmZnpHN+CGcDRqXXZJCzQFm)J)>*%`e`$6D(Ca>GI zH0^bi9yb~_$|$GQv1yq2-wUlZTfXM|32oJCy3##23<9BRwqq$uT`lrb%R|6rQdwN z)2BD6T3xL)W2fC>HQH!yHPcj<(`oU)MVdA*RF!UYyX&tp_V+L8+Rk{~nA1!C>guV) zFzIZ2Yy3?ZdR&f1O4VvZJgaOK_Oh)DR0tU*jYeJ7WUZErvw^Z~E9=X_U>tIeX8SK9qKWHn=g3VC^;uz1l#XY)en78$@%RBwcXsYhl6Eof_JE}5oi=;lz5env8$c;H zv~=4}mMIJ)pwP9|RvWZGSX%A(DU{~=hGv-I61DB7ruADbO8bM=55(!+J3EvoK(9M4 z+Ha%mZ5HkO!=Pp9I`wp21Fc(9a8Rmw-s)NyxNhAxbl6Htq_&U}RQi6t7RMN%xj%=O z;OEGY+^El889Rx;AgsodMLL~$VuR_tS`?soJcY+8GjSWKFhUSCBbfV(s!SBl9RJ^z zX+laN3`5&*wWI=LxV^4x6k~74vzYz-5D4!-;0G$_pzaNZkdpe|4u$(CF7R`3J?Hpl zS4m2t-O%eR4IHp-3;*u`H=ZylLOY~FDm{sn!6?&M9GwSnyThnQ2-A;dnq-5V)mW#- zlUeK{=~i4Jax3#imT}XDqY}xT@e0ztZ`ualdi?pC=K`)*!>vmv_sg_?yQwKaY3A*g z(tc4>)@_BUw^>TV6}VoFx!(2Heea4%o?FAMHP^%O=Zwltk4;U}H1qf+meMq3y?X=l z?t4WsoQVdRSk8+WA$n9Gj%dWsp)^sE`eq-01^M!qzElHhaS)hx5ZFc-#3{7C`ZDrY z-}PNak_46%wgcN1(h8Eq$n#epk@MEQlO};vKH18mP_-maonus*K;O*E_@+~O%mJ&D z7+mLellb04oTRI(Hw8DXuBL^rG1p6)#IUe!Ue%+FZqPOI}=Fq4>77)%J5e zbde#Ei{|gX2iqY~(H%-tqtQy>4gq>`hbOcipi@>$X)|G%d^3HTM3MT3xDse|4o^`@X}_ zmFH9GdEM?Xlp-isSJRqt{2LwHHjGBS)wFF*uh%*q&Y6)|oN*(uY^rq5G@aWnqRG+V z3&bMzY~Cyv0lr)kqBL*=++sdzpS54b((>soO1HP~TRt^k?R71y*Ik>R{)N-aOSo@) zo6;?$cbq!iKl9*2XU=#ZJN{9$TgB3Nyi~N>xVwM))Eyu5&YXGZ!83$_kZZq%L;M7} zm&91(p?wNsAQu_SYCsRFSzc0(lg=@4xDmxseDmR(c8auS7iApA-EOxVN9c|>cP(qO zwA_r_opvW#42MaqyNV44!%W*9^c@=jO%v1Xo-i##r|{fPXG07I{XSDRYPF(Rf6LVK zI5%{h9A6UiX<70Hqt!rbSbq=jYg5703F$?EkA*=i zy!-R5AjHRxpZngs|M-6wy^4fE>*=H&M)%zph3zE0^UfFDdFQVq4BJW44#T`OHzy)H z{)uk5cV(dbLghuDe5P`>Nc|G8pLlft^m;3l!Zr+>$F5-}VQ7zZ$F`dhsONgM(^D^R z5#PCS>5eiEX00aNP}gJE1+Okg;d;-jF{*B6*vDj$%-YB$87awi=qqn3Jv{@@?k8Ec%vYk?AvtId?3%x(L!gaXzKa6kQ_M$&jh#X@E zKT8z3fm|XFk#8X%AfyS^PzxOq&rzt9)0m`8A4-!jo=nq7NtIS!2GC{adofs4i^}D| zNAgWnNu0P*4<@#+telsJ&vM+5(gVH^fAXn}0v#rS>2&o2* z#TPgG5!0X}41ypCp+jTwg#@ti&VzP$GyqQh6yTp4w_8Ac5BA~jGg*>7(O+C|G#NRgtG(~~Ge&_uFGz}+aP#Xp!h0E$)sec6PJGoue&tt11q*xNS1G)+_ik-d zI-lQB4F*6y+CBTDZya@&&QCMl>*-5N+l!^bTs4}_QPFB?xTAl3_^wtQs8ziaK0*j1 z?zOAdfZO&e|%Yt81qwosR3Ucl`tZ=@*Z9>I1PJzt8FSAxx|QZfK|#u;dW-%Ke@ zor+5~bkZ*U^xk}vt7oOZMU#}$)Kznw@%OpaBCm<0vdPtSKDo%-Cor-uU4lz;B)?2(jX_u z9sU!Ec{(((&YqWXk}fLAHt~wD-S*Fr{^1OTF%iZm5p7*@#Y79y`y zS5sdxa(lb>Vq}x-5`uj06DD~_Ptk1TS)QqImaD}As0&kGWUdM!MVv<|%S8!}kt=1p zic+S5YL=AP_Yk^pKY#Ro{9D11Al|798cT{wX=#*d3~9ZiQXYT6#nI>8|G*gX+C+Pl z;4+cK)NF$RDae|K#^j+aF6UX>YhI5s5y(8>O(j+ZyDqNEIGYz`oXz7h&gPdcy}9gm zv3%*$o8Nq?+x>@2m)`v5vfF*_Y&M%A`jP**@XUh`KJz23xa zin6@$BR@g}A=j?zH4+`tA`^0k+(Sqx$+?3<(AXEJ0~PypuAH?TBJ$T!jC|iLuI5RZ z&WcH1xRIbRjfY3pbkH*EjB^-cfVar~GY^#Xr=W_jNo$6X<$ zopffl+lyoPo@s!e<)-PK;b%Ea!}AUElkLE0e*{U|AEXKN-!V#2@23euH>DplN`WLD z5TX%v?TBvUh$xbfbL0l{hvZMmRq}O0D%@mOb#n_;(xe3trXqF6lOQc>8zvT&vQ3O< za|jMm=**sq=WdB6o7-k|C69Hefl5>=cSHhTe}O>4y}) zt~7+9$8Fm*G1p;o$1w$E24j>`u1O`|sl$}LUVmT!lslnqS}KeqQ)tYyC{U|8E`@@U z4&#&pY~(shXu9wDqdujP@4LTZv72j2?>b6_69hob)y>~z6c9&JYU@Tsx#RnQ$s9(R z;6iE=K>AP^GbLMoP#Z*vC54oAM|u|6j{-P;<9e1!1FZX~6O zZ2Q~RRGcJgGdu(5y%(&;L-%ehWDza0Nx&8J#7ZjdX#}n^;?^dt3w>!*`WqS{_PD;=BPvVRC zX*r+qm;d*8J_|w|f4p8>oQofWuGLb*xc%=QrSpbq{8N^FerKtm$n)L3;Rtv8nobX- z>dq;h6GDh0#BPy6k$}|5I+>Ew+Ejz4Sxc)m`ldG9XRWt+*}Td8{LMG7W)1j3{r(4=QG^fq zLBAJCeUsIZJ?%P@{?r|tBT7>MFt zKa8-cbAbUUQM59*g9H)GIx_RNq>iSMf)nNa3nn%}3p-Bf`9q*hKkSREtW< zrIcgi*<3_Ye&s5bCHd}C2kFBi3>%#&x+A^wphmM;YBsQ2i{5OUJNuYazlGz^cJ&~R zLS5JNC<=S76hgU<`@_wk1g9CCVCkEt9ow;^V3yORlj$u*&GX{8d!pG`UTQReMq>$g z!LA-XI2g1Sb7%!h*R(i_B3;vL={l|}rTq55Z#IG;2pUa4aJR4aa}&0WcGF4gv!z-T zlYA2)1odL_LU|#fAbw1oF4Eyri&QdGiRSU9HWBMs50@7nf~Zd*b*kIYUzAcfogfS} zP1C|4479dmJMwD>+pEAUf@BM|`e;1s`IeNz!8zDY^gUyn-iA@V2Gr`Kyk6hxgdyO#>FA|m zZOdQ4_v&?!!%gaS&xZ@k3Pa1nr={fhoZTQ3a*;gl-MX+9zY-2)Nh7l$e%JwwR0m=? zdlcbwfY_tQ@#Bgt7Q%gy(zy5ig^x+GIiD{8$z50{_f9MfYBv=J#`_2RlyV+WFRG>8 z=Uz0a-&Jz^)>}>iLMEW@{8Y7=uUcTyu=Q`}@%wpGa0`IrQiqE!w;h~jXz98SAn=6b zY+cy_F7Tex0!rhUd6rd^ey+~&5e{%f8a~Zl8E2SJkx4eMifmpct1oO)#yEIX-TyuI z8WVW%L3P*1oK+_9D4z2L<50hYN<8@E`WuXywFq$tA#-|&o9sbFmgIn3B=?aQlDCo% zlFyPal3yeLoscS3Q8t(t)vQY5Y7nb5N?CX4tl*u?>93%qBJDvpr&#nh=2bDvZfG>T zHWYkXFt3VP;V}0p$=Jm#j?zhXM5Mr~#%jZMhC_k8(Q$SVGTM3nbTO}rd=oNOY4U`R zDAGYezwE_QaxNw3$#5VL0=zynKwM=04^bf~g%F@M3MoM;lme|YDH&6W%|y9U{GTc1 zR8|jZ8t1$j_-H`Ghv!hn7}$&DhVuvryxMbQkyou$gZ9URBmt6SaQue`|13&z4!Mw= zLr8EA*({~`U2=5l9OvgwF@UX$r_OPJpL>MEewrgSt~{NhaRuCAK4qlUwaH^1=}=g+UN?MHbrnf7|1{r>7oiz1HN z8`BfbrbRSDt{vf>gaeY27U{`I$J-<&At+G9^Xy}>huJ1lRm`(k6_rZWJT0m;u7U{$ z&Z|^aMHS=!|D9$1oncH3clEOuA6|R-;%9%ps(${n7avY;zWL^xld`)Ny^m?y$E5W! zQ+-U+KBml%S@MyWm-|mVv0uLYW24cH|NG_T{uA$-&1SQA{UrNsvLJ-+;E;^TgXB~A z_#8_N*F&C$CB>1$XDKZ#+>w)&bXF#_K`{ueX~Y@1|NW zCM)neP22lv4TWn8REA5-lj(T5M#1PtQAp7-P19_*M%lnL2ZIM5Sn9NO-Sq}>Ywg5d zy~c{AVu_t;IfmYLxEc&{N(JX4UYgEkqalO2YMAYNL>ScTp%;6eYjxW-P1S2nMmbkP z3MY=!#I`{PSud}pNf?HfZJKEtqmdXD+Q&L1h*=`HoNS6U1Y_-RNr~Upg zOA-p8mKLLaKl*qE065X>^&;ghrD+7pw56n>>qW6|iezOazqRJNfbS;0>*`4(Z8S_P zW{fk=m>wxjsxW9|ag=o14c8JPY$S6gx|Aq|8TjfG8^G zePl+dfa|KQUKg~}sUVX?h>dz~s>i7SRB6!tfVM-+G7PM(Ih@0EJ+x!Xwi%23D01DL z3)^;Ga7wW>9CDmK^MWl5vhD5jCuXxDz=y+WIZdJKw9#%yF+#)a8IFTA3{XzfFr=LNVg zh?27+6|3@MRuz7LUXoT+CydHRrHSc$P*f~kRK=py5p|}E3WZdu1QL)Uux3#<8)yxz z=*n&xCZSMCs*+f$bi2qOfL%4Jyr`ZU(w7;j_85($R3&MZq;{Npr9jX3cgN$7r<=jj zN%8lo4kMMC*8m!xCa;`a3QXPWjK{maFJ2uio#fs+`(Y6*ofMlms0Rg;J{G#Y`TX?h z`JAMMHGe?iX)-mS@AHiORNX5B{eS`NKo3f1Vb_kop<3N;OZkp5+1Z)suKyBy!?PWIv)y7Lt6|$+w`;4lxYqGMdmBa1Mtf6t z?6*8`dJPNwqWQ-24aeVUF3;6BcXuW~CW5u7k(!oeC5>n;5N8-WQ(lr-mSv{5qyRlL zo(y}WK{m;j2UF!;B=fvXbeJ#RA}<#Eknu#Oi}|b=`ROxYruMUe3rHYD{Rgp3W$ajv z^N!UU8S|n_i@_c;m6VB!lWd^kvVuU#G@H!Jc{ZNN`D~s^6;I2B0)BWIeazHTO{1>o zd4O!yQLm-gPiyJ^4@elh0?8z4w-}95SPnaL7Kp+Xr8GV8T?f$8J8dyzoLQ7wj9>ql zY6*#aNNcnwxy|TW8oUfP?4>CU&KT1*`816}#=?lceW$y}aiVi+nj{-iQ+dk>Ekl}a z7g(<;2{;VUjhe6H9x9%SC=)kHL`I_PhQwYSQnr?80)Bc0aeTu|LazNYC3rVkCOhN? z@-TUlyql0P>tnnm9W-ZDv50)f*%_-^NBI`<$to(an;m2D2~#oD&s63V;1E7!&~BRry3Jy!U&v^ZKG`HnyxqMx~6N5H!E&Y#+V_LxC2_WKLmzDV0g7X8hS2- zNSl_;aowcP^@U+jAm*U1m$z*8yPgz+yOse^#`I^X z@B2RB`@T<~+XE;SuM(7Uu>KEdtoIEJvuud}=~B+7#guyZv}qe@2Ajh-guIlZo1mRQI?9}==dQ1f*5{zH2K@4XGKLo+Q;r38AEZ?Fdic`g&&xVbufozMgzkjevYLawOc7CDlk;OWQb~ zXqSk*xZbvz)@m+H*X^aA3)*USrnQ=S^}*_@Z8bI15{YSM!*Zv{DeASnSavCMwl+4J zEzr%)>&7nNJbLS`RjXE`@Z8$KafA5{*WZh;)6r#(n6xS-MT|2^0;fUOjn+%vX_8f^Prcj_Myl$_^=R)0hHi%>4y5J7yB9W*l z!;dM$&~@8txPHK8w>KPy;c(dNN*?%b!?JbVFqD8Nm1c0kiu|rSi-HMmXi9nzE!(nf z)S9hC=(;6ADJTV{ke05Cq}8m!wk}ywP z$rB=4kU6H{)BEM%cKX4THTfa`Ch}9-cGhtTRU#R$J zF+uik_(9+|-c!WW`93aT+u`wdMYc^Ts40bP-WvISzt?O6y7=yFHhX>F4{vEoA*}9r zsc3LE@R=5IUN7=ZU8`YljUo5Fn(pW(YMx)>-^YI~&fVxa@vlYcj$A5plwH5F>bSNw z1T|W%7Jyc>sZ$JX%XL;(uNw|6B~@7GjMg2lfij-{V%;=AH-AA7HC?-Qgulrlb3gG& zjSysUS!8ipD8G!WvhrPIYM%Lhyd}?{R}`Oqe;@P_~$GtCl(To1z{^Os# z|Nid<`Nta$4iI3H*N^aeJpGO-IYaIsq+&5VrXFGYolB(>3q;1{eJJ7j?$OlcwuZr5QVmuS_)8kqsuLv9@e*&c9yMAeHfA zPYUyrW|;Ct4{4ODR$=G{8*tNd-@MdT6#D(HW$_L(bmMo0ZfHN@a2|ZcPzs_>C?VJW z3~#`v$dKGhNY!$$rJ_~j^Kem3D?Sx$x5c>qMA*{BU(F6l76Wv z^5VsmbHORQ$=c=KYGAp!HiVU!t6DxK0r zp4)=+_`_zi)oM5t7M}J13O&g4jm;zmN$v5P8GB{FtsR!zI8^m8OD5oWa6goD}|iz+R$ z+(kL_lIzQI5&W>fB6$e4X=F%Xyk-lYuCW6FDI%r>V>ag-YK3IWij*^KzcyJIzYUWFIBzsBj^Ryr}Yh zp(B#Y37a>@3t^XB=Evo{B)4R(b?82pz*0HpSzM;G`M4NQ2J>+?o(v}AEXw30s;OpF zM&-2b%zRp(300q2vkj`PYh3dIU*p;mwG|+x zWSVJ!S{k5~WV&f$*!d>ewx(&&bnY~r?H*vJr86ZJ-PM$?3qhGFmEwkJ0+y+BrQ~T% znI>SGN*hoin5GHN>5`^QgHqF!ng%MU1OVI%2f!Q_6dX&%&Pq14Y|9#DD?7y!@=BV3 zVb<3Spy{Z*!!~e-(B>8I^>G^QJ9m}}q~kMZH&f7}*nPp;!NL0a!NJ-^2hfz@Bf;aC z3&CR~T%-;c2m&g_s9|w5xLr>TlM5~-g`q1dePx=;x54Zh5V)LaQUEI98Xv_PK(#<) zR4B=~<8r2?U{nvd#<}Biril>*AH^C#H8<2acOGfZZW01sbFsTG=j%8ZoO3R2-7N}= zq`R`ZveHdaEbS0twZZ9%w8%QyCI{pad5nA;c~`p;d>dG=0UP0Ib4d~mwX6tj+qOf4 zq&M;05JBUnbjQ9=7}g#!C<_u$&+{~0(_l9!jUv~O*5hd!Sr$w)J?nY+UO2}uzI^XJ zC-;Nn73Fg7Dqa6it8RZ9B;^#}0mg%nXZ`*qCvXzyQdQk?2YV)GVQAYjbd(AsshrR- zt$NL}jMJK~l;;a!n;NK+Li1D3L&LByS!Uo`HZC3iyMu7h?{=-rY9V#Y(ugAD+EvQ^ zr~ib^$r*ARxsN2-_H4QG zU{M@@Wh@zYs`$g%PC z#&tJrZeD-=#+ln!mT&F9r=Rwczkcp>pZlJB?|sj6uW!BQJ*`_dHi&t=dm4$dD<|JX z9wX0?kCA7|uamDJTrn7MGaJOJL^HMf^Up+HV zu%!bQGTJaH4;a7&mlRxSU?HQQ*vzjVvv<+lz%v2<^|5u6g%lq1IlTKK3NNH^e?Fpa zMBOoMQqN!vv`Hh4Qha;kJj%DYQh`6t!JCR=U8m9nY&+~wn2d3V1BOQ*02o-2Gl0Ri z9iKy(Cc=XK{B0bbwZGKB=*Mw;Z6?le$ta&5XG&Sr}r5k*0I2;ad3{=*oDvHL;k4@3{ zoEK^2y7h+V0gltC+xGuJtp*A?m!RD)`vtfyJ;?s80Q7+gUFTfW`~q^IL(>`8v~X+c z(l$2??`2xIPodrUI@1K`>s}YAmiEKYmepr;oqB$^?|EfPwYKhgy;cy0K}$&jLdb*? z+)QpJq$(mM)gW1!A{E7!MzR#!Ir9sO+@S^brn1QIDMu;Uv>vbiDvYD78-|FJ)=C&S zOjv{L#;V^3?e+>GhYU+AeAl>trPHPKysp~551d#_5`#0p)4usse?VV9sn>2Rsq4~u zZDo<9XtzttS#7t0FkGAXzNF9^T@2MlhAYq8<=MebYS1a}@dcw@_+3ln_dY zT|uJU#|!r3gytH1O%%go7IR~+rNo9ww7>cLxAfz_!fZ>wc`(L#<9kzZd#4N zb}TDQEZcJ3?#&dJCP83Qs%)n<0!-5vVhxDG#MW*tZvuLW2f%m76yvY?kP=dW>$cfmz8mD|Nf%Ni+&!b675Tsr2QlwsSDu_)JmT`(>bws zvpMWei+Mt6QkZa=Zg1VWy|K|ipi}+$tMc|<&EvmGz=}Nn%2yIflZk2c&MvQ*rtV}t zrAb1|UKE$_+}fsSwKi#z(6s)|si8V$B}9_-Ye)DF9FddcR`NJ`E%^%hQw~F`Ckr1t z-1DK}6znUjNvdVFBP0Q~`@5S!GvRdu85dPCuIij5UF2D=W@&kxg5!%FTRIVojnNxv zuK4_8AAV~;&0<$W|L?E1W5E0Co-MCa}y<1k))HUPUND6`=x1WI$)Y{82FB( z2?{D*Hw?m>X~JO;cdd|3M)P(;HA08IY!- zYoM;{876cq2pm@_`Rv-M^+Dgablp*wZJJ=Zu9Tq}j?%&)icFIOLg;#m{OE7HS$1H# zt_6>=pxKCHfHBK&wHgiEU{p7nkLo5Drp`FcgQ8;eW zSYI0`$pPOB9S3l?w%6Af-QK#V->4hUx9T-U9i!dpbX^Mh&grvqSKEh& z_SD0N58wZi+WlC*a^<=Ouu|*KVCDF$_usES*Cto7JOa43gBl0(djEcoP-$Yc2m*v^SFj7USl4=}J zVX4NLDRHap_vLZ|feVwiDpgUcYEh??nTAwF1>X6HiN_OKloa+B;3)Atp_F(YR}RIN1}sd# ztN?Si#7}!XpDPELnLuR%#_`M7*4D6I0(1*A&dLqCwzh`z$POO;Su-(l`>nU%DsMR} zztw*!r91S|+h3u-bo(>>wOmSm(+A2Yp9WAaPMo-9c{p6TR2S?u>NT)2Z`T<$M`%al@jADun$_$KG@uD{j2=|q9=JM%bx$+FD5?zrcY zymVd5#}s%jIDhP(Z>8U|cY*_av5*vt=(<~y{X180e=yH4+_QdHu=_~{-1jgC_`?rd z3V7i5WN+_W9Du&y5g_^WEdVGep(MO^gwNrK_+&^3GO47BlQiMG=lTkcy*v;5Z;jIo z*ZonEoVm03pGCca$3N%9gWYxKUAC^Hyo!6!Y!?43Pg5XGbNtYg$G;BUu-~(6^|f>V z;}_`9aDuPTT*#W8?9{vy%h=zmS?$eqnTST#hg4|;f=1jwYj;~?{qrd_07GN zrQM$#^=DfrR##7K&3YrOoV;#(GTFZFSZEkMm-EM~4{tfMx zrfkpvt1CGE5wC{jWz;-8H~{T-DF(-%T1PELQ6N748&}ucZC!T>A%e(jSLthTg=Az* z_Q;LoZbC+jdEPHm@9aitKg=Ty{vsJ-#3w+Rq;Xj=BHH3~b9~+I+PYio4$~S~KL|kw z*$l6rWdn*vedYMG)b2WzI$fLI3#aQ~qUXLxKIm?0x4*aBw&{Nl!t2JH6j`<%0kv9Y zT5hK^oMjo1WwX_O-~3*iQv1aYrOu0OO6@l}fb%AsQu|X5;C#xa)c#9{Qs*xTDG0gt zD>%Z>5=Io^vm~otl*LtKm+X@>jQk}bRluHC6&0Sc3gE*$${l8Z2;V8amVZE?Edph_A0Ls)?5) zU_Nj_BAo|$>hCRNQ8B&Uo>`G+vdHH}7MG%&XQM1GnV(iZQ-$BJqd)J*LKlPm+9^vX zyO_T^R*%!OFZQ`z-p#ta^ZmRlKGhMOZ|(BV@A9rVFFO1l-sQjF?{5wUn}fl-`u&Gz zPU7Ky|J{SZ@h434{9tfC9hg7Rw&)_%A-x#G^SmUWRD(23T_dcQtgOKEKt11U#QH-& zd|DQC-S!>#TTByondb2in&$k^|Em3~zldj!UxR0kzr!?5^A-L6Uz(TwwZkmJN zrfNV>efQV#;y@`t2Ry|XW4fe(;BhDwUn=FQ|BTh$-EZ$3{ZE-6?i>BP&C~r)4L)a@W;!rUlVBZf$O*xJ;*iwH3OPXtI9U_t zQjEi-KL(?4LLN6DSPdA>7B~z%BQyWy^Gr?sw5H7G)flbgm+F+&j(?&RYAQQgtuPG3 z*6&ZM2eyVoO_@terqXEb_{VFM)^MRlkN*^5&~BwVFs(NJ{XAvBvG@xhoF3v^$rI&V zB{Qq8z$qTKOBxHp67WRu5D_L*T`F#UVqvSLQ~@gAfslp zy;tNw5N!5(cvac$f###hcr>E4+go1g_F$PIWd?;Il@u+{2bPLcrEt#;)~>VLPCFua>?8{R!gW2 zl_(vK6WoLYM4}oeIBZUp@CWf>JCS|)))RY-#&M)Lmr_X3qS%}Ddw{M*(IB)PYFa>= zCMh+9GIiawaSrjGX&OuzQ5ek!1K*?b*$w#Oc=L{Vs}09iLep(0JUPmNR%_nMhK(jH zD{8e`?Y^dbZ@E!R8p|uISsW`3jIA`A>rN2pI@;~^_00=wYlIPU?JACVgnAp~>1+q; zMNO(IDML7AMzmyd6RN5b1#v{6Uu=9=qc?W0gM-NAWRT^Q(U8liTmlYQUgoAh1a#fuVc?)ztyZOSRqHI3D`7z-*LJ2M;<612UT&{$c zn{tOZr&2&jsY6H!=sE*Ly;d|F*U~MED@G*+9zFoto$2)CiSp&$=IjG@eQBw8{DY|T z;H+Ao-#nXRvAA&ad`92Sl%zrlrI-|^<20q@ju6l_LlYK*0=Q|0k?(OpZ#F}dQA(9E z62T}4Zk8Ozg9S}OkdX%Svbg;C!BsiWwwo2D!r{Sf58ZY!9-p%>J#p{7Ph4`&j>kvM zMH5$!j$ShEjlc4hac}&RJ%oP`S|4T%Mmhoyr=wnHow588aTr5jU z%ktud={^_GE9#9#y~r7JbmZ@_bzRqOwvX%Jm9(5)yf`cAr(f2jYnE^y^SMDDo<1EH))oHN__5Hq0)0hD(vy(@%>e-ZCj`8k1?M zs3%-mwU?75PEE2u2H3vZFM63|AK0fCNY8p2GEbOlN3Q#> z>*_kAlnQAor2$el+~(r+>0K!r>C!MUEsavn!oab7E&!#;gfUllJ=fQ)y+0Mol|wCy z^)=zTrXC0>b>(s?TpggbTCL^&;19a3TCE1$NU5bW22mHTQlL!t8DoYfYbIx03T8=p zd>JWzFNuLT={+rlWdK47*I5AE@I0LX zXti%JeBThDL13Bye_jw$095mY;6uv<6}s;+8H5%P^Ed@{CBSso)udDkP+3t-H}kx2 zimkA8H1!Vf5osH%%_2H2*MhT+lRWc%Ytdao4N;1*FnU zNf}j|VE{jBO38(38kV7h`o5DW%BZlFk}wUe>2ghaN^ny!-7s`LFsw+L!q5RmEil1# z({&xkwl%?+#+W4p8`f4icL1S_z|sv7^&Es|L2#QgjWHhd`;pE$0E;rffFVq`ALx3d zQ2~n!&6gT?g{+4mO{Gj}7}kY67P_Guo~Ng_5{~0>u5sV@EeNFtL4bZY(LK*FOkLP% z%b5Ou5C{Rw_kE^2XxAXX(CL3NiF^Gx0g|}ii<2LAf`OwjA@!?n8xQ}J5kHFb?F71yA9vN{}YT;X$qx4HBFr{OheN&#<2ry zEz=-;pKXVcZQFJf+V-(K1C_n z^u|VL1E&c`sW@5D^j4=6MWBw`XbiIDyt^|R`abRf#ZW)IKG`HE$PMH{@@n$_96@CT zWp3g~{Xm_Zj&%`BQk6t%5zAE!_Asl;qzkEvRu%I)QmOs16Y}Px9o6%~udMPs5?fKj zSSS1DeJ4&ZcH+c+(^(!tv$ee3YC;P%&`zh*p+7@kr`|U``Y-XD;5z3z$GhMy`WfUK zhU*$e77}zG2eVnX+wIO~2XlwugM0p{`%au-Z{7&a)^bw=wC1wyIQHMihA(@RdhhlC z@7-^EGv{yS03XLjSJ%5n>>7q)xM=>R+u@D}|Km?D@vqF}lM&e=w~^q(fWc`;wi^J0Fw(e^)$Xc7?P$w6wUuj_ju6XM?5KELf5tnnpZ{^SstAJlgjY}h) zVZMITbVBK5dee>5DW%itjY~Zq_O;%v-I*Kge$(garSxm}-&R$WR@H6y-?p<$Tdj@t zIbO1{SHqj86MFnNcB?rYwp!L#zGAgn!(p>!<9budOXc`7T`X?BXEq1svwP;NtMxjt z6rwG%OYS015`w%X8BrriK~G5TBNO{FpUw*p8*OQ{8e3MU4hUEv;=t@49|#tJekkgRQNzr?<8S8PMx( zZC!WQ&Nju?)_r$xZ&S8=;=3C^RaDvA8db3m zeIu2Oxr*2Rm> zdF|$#o->?%W-s3Ky4PV)Z1A?%zkXOVFJ5fS>o?y-Z^^ZbH@*J#gb{h|2s^nVCvGAS z>YeIcB*TV1M?GLD*)us%IMyH8h9JPh1aPk-jO+CDi8UdqGRSieLXjnCaAPAZy zG?X2NVnb*9Bqv+sCD)D6^;%aRxu?OduHJFP7h|BqfYJ8cr;H>FNq>OiCbCR7s>LSN z+H@?6XJMMY@1jf7D{E~<%kLsh2LrnjKD{CSFw=G0b6YLfvvr+GDP7z1EIaa~G%d>= zw?o&mtxelL1!rj#~G)@1JrhCaFf1*g!!TlO#I!^JUN7JA)s+pQ*Xq>}}JSAM) zGRLjRv}|{5fpg8!G!uV#`jDh#k6!_i^;hQ#BA+xH)~aK#Tz*IEgW&nGd;w+$qNWU{4O?>RGIS46oWxC2F~7m zHuvmAGca#caBN=WY7r>wn%QDLn+pZ?Si1G%C26?)2hs&<+HHX8lb14XW9Vm%x zILeG9jX&x6o=}$Q*3#5ewq=K=9@&;9HQ#gJ?HY!(P16>hXWBD#2eIeMQM}6>$JLai zuTh?>AL3mW%cIf3?YCd|i`nqx3Erv8<;~(}$axZzDyqvs= zyq{blpCvy-zC^xE{>0XQunH_lQUnWo5^mKw$*ZDTq?4o|DDtY9Ai$Hg)>awyh}q2= zm6|NNNYq3YG^#R5Aps>-oXF%tbHa;slBjBOAv$het%hZAsTd|r6dSPdO;3_8CflcF zS67o2X9~5Cv`|;e?8)+C-?xJ>zwSO^lNxycV=iOf@*`!gVoW`%n37a7#VD|&0HpwR z6JrbtlnG8Hl?PIqLP%k8z76FmOf0jswXJY4##~96aus_>F{PNKTri5$8}<~=d>8s? z*Pq~ppMU!E*}A+PYd_Bp)aUXS;?hf@dGALg%`L`TcJ6%&`++ewz?Q^)$-=o1ZRRSr z!nq}+5T=yx_Q`w+Ifs8vpK#ADscBZp$uhOww`Hg zhLo1gS(A}X|1dPuWYz^xuSKD=@bJgv}W}@3t<=tDU>mWPD4qBox94J5M?VLtygPjACdC( zV&3gG58I4K&a73B_Xb&awb^VoS9^o3H}``enBx=d#EIK3og2haXOHqhzpBnOEO&*S z=p4U11QLI|dFs^W*pDf~^7QFbr>Alpox9$I>>>G83BdM{7J&ILlM4qqkpf~zXPWb7 ze^KOxNC!pw_&&~M)=#UEBhq_bdjE$mqPeeO9n&}c3%uhkm$=8d~M zjc;kMZ{#D28-$YK@k4f_k!=izx;}iGb~-D|oeuroXT2~CYpw&qWz=eC=!Lafyk*;~ zteoB5YPa2+9&Ilz4ac6RwA7>wd47IPiblQck4A%^DZ0J54w_{*y>O+`02+;zpVg!S z9Jl5Lq1$UV9YP2tpMl7|u@Ybt!X!AHA;^zukz!{>W0EYg%TM_bm%=V<%=ykLW#|O;%S1hT-5_ z9m5Fg)2-?R=kR;8cH%hFs9UY(4L3CETA)-M)jQAMHgxwp#oi9lyY`#-1b&h{hrE`2 z2RS07>SQ52t)9#Wv6PF7VZZgDfq?mgBJBZAJ?UaR35f@|AkihMWU&OQM$kQBS}Gdg zu_eg~QAkw073FC_>cO^2QqJP(Q$kc>brW2h8P^c`)PwX10YGPXG@-h)%j6d5Xf#)h z>r1s75XH@A>`(?2Yis>HFRChsrg6}~qQ*Z93Q%zsi&Lk@V~TvVSWKsl18}b9%S)}M zr6|{w7%vt}1^cV*>(1|Nht#&=o;tYW_UpK=t1o~-aLSk?xS|X~3gHJ#DG^96DWzOW z9!PG{Ck-JOqg1n-4;GCk&}uGE+HHs17GT>;oghe@DB4W>sgS*5u_*E!nl7PHrvHo% zDL9h?Z(abaYd4-*S~83@-38~mPD3d@*A#%J$u+}JntA*a3(i+oF6k8dJ<}3rl&*^x z2nqo(_JoU|kV5%^REh_ZD^N%&0?8~&2q*H|RebUKX!hSGx09Ce6%d zE$TQFwfe#7Y;dC!hO{9hMI1#DrOj6Jt#W<+=K0EMyJHE)IHjYqJb&(fM_;mZgK??2 zbR3g{Qw_FlTdpH0r2=hDuh*wl9P9c}cU^4@uA96+Znqijbqkeg8ui?Uly?`FWf+aj zw3a_*Hku4!P)j{YDFtdtf@SW}no_0;BVF{Fi9$#UKnUBieJ~RJYgh4kTp>CkBkoz5 zV&P|qp8Cj(F!|UkK8T~I?mRyB)sX)j`^hK|MOo(WtqfEr;w%~%m1yd|cf{_!f4^uc z6}V0yI!Aeyw>a$c8GzE}o9nZr1 z51UbR-S)HQ78Pr6j5RCDK(G$D(-nV;#vO43A(>neQ^$1OUec@}-U@#a&QYz_o+MSN8 zl$2QJ+b2xZ|Aw3M*G0Z*nwICA7UBfj+ePjn4AYupS*KQ4C|y}SWm`^~h9P~P7UpHg zH2nudDWwdhlF|`E2uDgK7s?vtQ%~|oP>>Vkfoq9DhoKUX_|8TB$WC@WX?s!NPIL0vcYsZosKdJ z%hZGbg4*8F5^b)wni9vqxW2N~4$oiz;8LRr^x>_q&(~VXie~x4(Mq>fb~=c`xl$nY zvDTOHhj^CQWJInbOT{{FmO~Jg|H6EqD}@lg*~llo?VXF` z&Ejs@ss=xOl<%$Fda$wrtgIZ|xVqcj*x1^ePCD&&XENP8F&=mR1yD$Lxc10*o?Bj0 zp{8IsI(XruR`(pED=YgO3Qi8jjmGM@-3n;8XZRlu&oMv3Q5qNf$W&UXQJhvT%1Yt6 zqc>l^$h(_};I+?%#i>_SHK+@_qT`XWsMEUu*ANc+Z!%>nt2xJHkKW zNG}o`5;Dr75dC}=1CK_Yp3K!C%bP|xF>W@9`340yJ%?3oi3eG(;uX85ugvWdVYxLf z=Xo}Y41)XbV%kP23x-N#{B4tC2;LMU-r^&w0dHW-8aGXFE|jJ;UAHV9jzggV8Xbxz z$3)TKO^%o8&rp2|rQZ&IFJ+W+{G(+Wrs*`H-KzoGy&9U` zFt7~6v>%gU(C7r!Ws5qEAe2f*rWH2gxFe)JN`v6fgqx1+j$@jJX`|wT-^+0%*c_l@ z7}sk-fabQpy+UiE+340h52@?MQKu8duA4I5SUX|aY(U|(V^YU?sdTsRT>G@Huj{oHu z=8dM|jTWU%B#lP(8oqfW>a`Kig6L!P5KR)0erC*CHm{uS&|XB#s*Fc!UKLm!xm6k$ zS&_}l_z(Kh%=dCr_D>J>;c47-*9|w^WlRkBvY-6P$3C3=@DC>+{>jgM?kD$8=CfIT z@@w7d_kH}Pe;p?%;bC)KQdC}{4T81Ho z|I6+2LS7(EBZ}*5#W0FSbwkBbqZuZYHe?%N*lC2ZG8$6uQ*0K^>zphMU>NnrXqXLR zLI@|lYe%?(Bf^LxJ+e*+RFSHxP*tR4U;weOMg>w!D65LNP=QW6R}~noVr^}072lU8 zE6dy4>+6j=8uhjH?YpK^eD^J{yRrG;gUuW18*WFude`couCCtorpK#_8AuUT_4s4k z+er$xRek85XFh*J^T7ujH~iq?y@Zj*wIk#>y4DqmuIDqh<1<~Ud8$%1|CI;72x}s{_GPU|DC&fFX$fH`$o_H$?tmSJ6CU8{orc#HvH*6e;Nt=7C9nh zcKH*~4q43nz{*d+EiG7QLtI>2lD}Ad5ZzC*8K6#&hA`}42oC( z5&WxMEZ!d3<|DK7F-XsZp$utNdK!GgNG@mVO_>#Tet7IX29!8`p04zG$F6zq)PhiUyKWKL@tq2a-G~J z1OQRjqES_svX*rz9Pfi!R&Lfg!1kR%IfRmTt($ut?)zSR{@t9@WWK6d`yiRZ0(S3NH||895> zhlf_Hhk&1dHwWPVcQ$*ls=i}?|L)9@|6n#76G;EM@a#Mw7VpYLR+OlWxSuSlY)Kg3 zy8Zs^Rb7pa{Lm9m_WK~|6P;Vyo2=Kzi!6=2Wv*F+} zyd;{v5x9798mlL711h9@^?LpA=GFyEsT5H3`(sKEm&?K#q2jtK5fA3`e(YwDk{$9s zp9sqk#qK!=k>hAkDhi4bDA$@)$*96Ok|sfAyCmwQqSUcf0zODvHUA%|(o_LPQfQh+ zH;8}CpW*wUSHT#@dB30(D3bs;w%sm$8iAw9GYVQugdmWiRzc97wc45?vhL9OTv32M z-{0L8fI$!zt(M0idcd_L=kE*WC<2Cv5Qg2}HKcXD_Y?xXtWDzNnL`K;QRLdrVXr?< zwI2cnpoCyMIrl!?Cr^-f5fWs_l|#cHRS~L+xS#CZ{JpxM@hnLbWcVLHgm>nu28xBt zB?;dnRXa@+&Vfmbz24?hU8TuvdYz&|ljU&&;GAJtj;5m zRp&)Mggg^c$Yxs~)p57&j0)?`w$`9AMPz`Y#ZNz%%+;NEmR4v|N_ zUbh&{^SlE)kKJkQ?ax2e>tC7I0C&FTQWM9AmyQ?9%v7_@@y-sKBC~*-qq}!bX1?e7 zv%_t3;BjDm&vFszAn@}@w^|dcyIJlE#B#b=)P$U)H|tUIUPK7?;8szdbjVo;RjoeS z_#}q1yhvr4_Dt6?{&sUDqOw$=sJm?fn-&2M@%<~Gm>e!9V}!xrC`}|fR}}>$$#57t zAt3Ig)}n<6uxb?`oo5;A^wJ=TVne~CS+2nIo#$B{`av5a;(6%D%d$GSFdC!u0|A}( zY+SU2KO9V_eQQ?-uG?+HWcuWVc6*jYRQ4v5{&!%YgCGFMuHN zU;WGQU*MO?SCLlYg55PChbODK4;zk7$;krHV7MDBt02bXKta%3pF;v0CSwqh(Zg&)IG! z(`DKal0suaPqeaFKiNRSe-@T~V1ScYFu3lVT(UrDY5PI|0@d0JlF9DwBndpNsWLQw z-%2e4$`P1A1f(J!Qw)`GTm~USN^!T<#Zjt_7uvSlJfqVPVkZ~^P^6)gA`p|v(^4`X zL|xDG0$1eDSPaBq`j=+s`X9C#qzMG?LrCL zI_vaS=4d_ZzHsmSekVziqytxn1IDW<3)KseCU7|&8n-F>a*Ofzl) zb6AKnhB;7)%`>sX}{?83SUP&xMe6&OfU_z6W{TLoE@QPo3*_oumGwH*la#C`3uAhujK=V7(~?lvWAODAiu9Wzg>jO2wY0lyRXv zfk5GYPn$hS(wEeZor!DBB zHvw*574~H9#|MKX%bB&-vOJIb2a&%zvEu3tfSXsjJz4qDK|juOX6@hoyMOoZ{@v)$ z{Fy)VXa3B~FTecq?ZZRF!^7LR4-XL!4{!h7uZEjfxjkL`@xdU;iuLlA-^t_e`oJ~* zI{#dEdH5>1cMr(Dd*)*w`?nwe_`iMoi+|_E7yr(SYdl=szP&iaVYF>h{~FwfUmyip zlQTk+wLD-@)h6^KvQbY}0F_0C(sQ>PuS7YkdR-(%RBoGfRMkx`U;BIV*9Tb&X}a%K z0Rt}z?fReJ*z3)b#Pi(vVCn)!<7nq}^^SXg!{BejLY@MAG=Vf3{Dp3oK?3d0>@QU9 zHeCGRLvL9wvn&|FTDDp#NZ;Gvf9DTD@y+Ld!!alpF3v9#VhDNlJ`%W3eDWdkRpfig zkCR^_zf1l&`8PlaNsgN36}~-7WwqgH<=4m(5IzxiB?7OFNnLH&1~Hsh)sTQ+jjmgE zp5bJtR-2G%nHK`BBePkmo(kejQ^ipu%3hKodv&$hE`p;!MVs{~0hA*vLDdYJ_dXt3 zle$UI#Uhv6X1n=IoN2{b6vIO6IGxS907`kG)?uhs7-}5`Qng2uP8f{R$vDbVqd=)7 z8AU<}1d5Du-7d?y)=|5N05t#;9Du^15jONf&d*=g$~k=e+o+vWdZpTkt@h5i%Q7hz z{lN|0L9i!{sh$k!TzTH?MKWBddl2d=v^j$5rfZ*^v~ejlBZ z2oxnZ-UuaO5F!e79UH_bh5f4v{2(vVA90;j@8p8h4cd@Qv%~P{4rghv zJ0Lg%e)q4FM+pc_%y=k%DOkEfGuicHaR>u8y*=*tq9}@b{qdq4XMLl%m)uU8vRfZR z7!8|$IW5a+sVx5p`Nk;h_s8RYKa57>1oC`5&T|NY_MWlZ@fhAymeaE2EIxA|ULv27`lsp7#$1gM(hT+dCKx z4vIK-NlwL(hJrkCaXf!ok!EA#!MC3O3S&MOngm12s}e8>Q2!;3L4ceK#n1md&qD<~ zcUYe*!8ry23hpyTfY?{>!*6mYnhOqUnP++J)v8g;A>8>@U1fU8=gwj|gv}Yq;tb%$ z%P^=B;LYxR^M|spc-`l^yQ6H7Mb{PLqaGr__(B)k@)-DRmbT}{z_oXscB*e#?$=*8 zc5mm{d0-od| zFC@yUsTw)Uzh3_C0Hlig={QUA^6_Jj9$%(7n_awg#pU^Io}^sBw+2~O9_F65oiMa6 zev#4_KS-k!V+`P=Q`gJaY&HO!?F%=Si!^neCBRw)CC3!pfNy#U#suQ?GR0aU#X4^Jo_$ zJhQ#3PTLE^Y#f!`aREqXES@4N({KjyHqM(vM!8=MLkGU!?K@7He*><4GrXR1&cL}n z0N<}lrHWi}tq{9h1PO4#nK3Sq)Nrm16|J_`bEHfN{mX^VDGRT`4WCi&`d!}#7Y^Y~ zPBKb6ld4*+>v}w8j4A)@9j`k*e;ELN1j3-*8V<`SDyPj+RpmKF-|KY`m5yg|G#z;s zFo+`GM{E_lI~S?YQsOUkC}q-}zV01ozG93`$9274Rn>$bv6Z~}9r9e9S*y>hEMQE< z8%xT_oF3PulS$Q7x{+yy7%2~OE(8-cbc{uw=Nv^tDVQ)xaRk4jWN&w#v{qA^LG=FV zDo!}(6fAl2{A@ZIPbNhNJW8!r+DfG;Mdgy1eOwJ5FrIV!8z~Q6CM2bdbL=y?= z`1MRCWwS12pV`H4KJ%NFMo0bL4-%|iU#v$mG9W8*g*-?8IUzHui2im6HD0!QkvH&+ zbmfDD4U0H(gj+XO{5QAR*8P$qs}+7#R}xoSd%dlzqhF#ju*+rP0CpiP>c`oR5TW!+` zo%He|P1(Ow%1CLnGD6stbIv*ETyV}9=bQ&NNs^di0HCZ5VHkk+)m!KcqbB7^|i(diyR2Z2eELYk}&Uf2o4 zkn{X)vfJ(VeG06Nz=VjZD_2fWc496VU|Sd57wMpniLe+sC<-t?0jPCfnYL?rHVWrT z7S;oRFV#`JXFPn%N&g7G3_nU3$;qA!f4~RHw~&_zY0AwyI|DcK@Hsy#E%G655MdHG zIXLn&O~z6O;@4(5DVGIaEy9i|newDgZNpKX8d(x8C)+@~Pl#8R{cmYmlu98Na9aC88G%pT_AK%do^e>SEdgo3Y5PzZhg+0TCQ zi+8Vm@%68N?Q6fNw2NEv0wnWzx85L5tC zt}|*%jTEV#8l)5)Xzich^R*IEW$eILcQ|MopMO>f0W5)gS_)mNO_BJo{w4UY@GIoq zK*Lt0!@3Qviq$&3OqY*}*Ll80k2Z^DvD{Rf7;K#1 zf`T{eY+Jy80RSdUrcKuA4#G%E${-8@D8^xyq_I(9kOn?Bwe4cu)6Tl2=nT?SNJ?P@ zh_qF73KvL$AVlmt`F7;`QP5haQr^95*MEa^W8-+#u{Lv|;_ufMkqc`ADFc_bvLcG( zC`-GYEDrt14~;-Pq_xwQ2TVyy@>Vwtxj+gCKtC;LO!S5?k1CY8-n=vNf&LlRk$~2n zTUUKnEp=5_#e%6DJ+i9E!^Sp6T@`sH0?-jB@^wC)B(ktwYmxBg^Yb}5=7~8VLY}`p ztGuqO#Dyxfu}9I%irn{v$P56=NSo9m+L^Ig&Z$5I6M9JJTmzKCNSoB7KVFEsuDBmY z8eW2KcfQf>n0LEy{>Nv=rd?~3Zm1-n^n}Rryzm78^P}k(me1?iU((zn1uMUebN=hX z>iKMDY^Eb^Qi3&0x-ARZHGI7-%VVuvNA6TR2R$68aR2n|?DW>*1%b{Do;y7|J)7`I zc%1L;pPW=R^8LM&le$89Nol3*a(R2Vs{79=rN2b~)k#$$-`zVI_V#v9;*649=0ZAT zLypNU@(_9as<3Qb*;s7A-=tX~>t+aLElVSE#0($M3_;cn5|~Vmk8hi1b&cy0(yWYFc_@Xr(a(^&j0Ya*>kfO58Y}RtaEWwgx&*#oBhX|qABjLi`KiqU-W$oUp0Gf z_T10D;cahwuL%Z${Mg`T|AD=tDH;-k#`{BbTrk=r(jg_;XM~aPIQcC3x8%PO(j2pm zW;=w+*q%z#>KhI2+19hE>S2d3pzyajhpVVL$gA7aLW(-Iv2m(O8kIe6u?=zAa8Q+d z-Rfq5C3!7fPL34@AzBeN79Lf?;R(gN>VWM|J;ue`_R+q9_n7E5lF3s0BVel7%&MSO=tlq z=bTC@{UDIqvl2mAFLef`3@-V$Ei(!^7Z$-egT{Iu3L$}Wp@cvxW#APp9$G&MO^{3s z1#KwkJIXUsc}Butq}Bj47XPdXW8b*M|9LMCm6wG2*!Z5bzNdbW`5rLegNqu_XrTH* z>wINhpuaSkPKyLeV+sIBg&5>**=i34tyx*BbrcQVVAyzrLGh3e6tx$}N#a5q3BiC1 z07cOQpp+soO1Y3;G@BKQW_|z=vnb9|=cq)Xx$g%Z86ieI&07Um=rd~r=LaGTe8mCc z*9CE8{Uox45JE{m{LEeQ2J%tAA+nKg25FWq7gA2|XmdojiMwNE1&E8KIZHB`4Uw$D zqYKAq0^Fc^)uO&#M}MXWj0NX_c^(s-^Pp%&e(GDqAjqA|owcqN`KdL=38jQH#^%oD zzjaP#LygIZw=>IjQm+PD!yF_-;Q-uy>qXpU2l#$KKnb7(P=GgakP_I4O$$&Ued+&C zD@X~SggEYQ&!WoCiJJ({?-N2}Y4-gYxKB3ZHhCQ(Ggou%n?~lDkibz}C$iq2LG6PM z*H3k89LO-pcSM&&haiqmEvu%M@a$kMO86O{Iu`_HCxo4OpJKOjQ1|M|;lq3D&^gY6 zn^A(3@nlRriuU+Dfi`e0ms>jd@b?(MzSHe+k4?6RadbV}J6VipjiEkiH(gaoLg6l$?;QQB`H4g$f{~>I(r_)J2Z*J`D2wBe~ zy{y6i3GczTk^}O3^1X!2Hua(~q9Vy&YZgGmKv~~bj?NCiBZ_>jaA4K$aIed+vH;A= zdsGi)U##q)cp)j>2Z}GU%u08S%G`pRZ4KAgN*X>M@2#pDIpdagyS*+2`Fd=(vk+b~ zQcBkC^?HP9F5z@AoX>{?Dx`{Jr`_pD2&B+_JlQ{-P1*NRXEpYEy&kh#%S)==kwWoF zSxz~iqQLeKu3SF2z$vr7Z|Gn!=rgJRI9#V__;|FptSTyjfzn!o@Dxg%U{Rz2e9{93 zp7ks+DFvO*7K_=GYD55vKHuBBboua*BTxk2^L*f(>j#lhpy>I&kF|Z*7#`orfCwCu zIEhhU>~Z4%`23QRN6GuhcaR?^zt;n=InKmA^`h)shnA*EGjE5nYun+Z=zT{gk6SM( zfKgmF!q^NbHhvQ2k|_Y4PHJgWY&zR9vuvrjIpwtx(upZPDVOM6fJYN3oeL`ApPXNg zgb*TvKM@NdM1202@^;5^3r_izS*uUaEX@LHDOapTQUJKTaN$s)HCcZa;kRD^L&iA8 z^N*o!?8q6#Kl!JCGR7EaE}RhzaCd*t={Ap}UT+SJC4iJVI12j%SG0mwJ9Z$>{|vE$CP1`n z?V?B$;{({34!wV%p}{9cog^QEf~U{_zNNsd;s8aO-dr#JFr+NI-Sxk92mh*s{%YL; zC|bi&PzoOAOmIX{fbA%>7xR(87yQ{%3|s4sspJyUTde%zM$~#o^-J@e-6FHL9{y{G zHy%Iq;PK3B@9l1@&Gz)+lhf%G;IB6Awu={s&GyU5A09TB-mo(twpPn>;to0;m`zVl zAMrh29MqTC`?w^JfPaC@EFW;t2Afz%^&}NOUGg}>>d<6ZKVU_Bv5Ts@1akY*rE-Fk z@$vDU>z6odDN`Rje|!w=`0{gq>Eluiw$pZ-Vz;+fce)JtP*MuKnq55G-(x%TlU4Qo zTnNzr1Mb6ka_Q2oJIBXmi9;qb$s(XvF2C^X@iCr%%e%O!fwtSz{r&);-Kr$-G8H8k zXEToT`Ps?tF6(_grzqjyX&{IjZ@*uGm&kSE-5F{nRzY^`s>o%fZdIDu2cbif+^K|C z>unuvO?S!s%0dSm`cH?8;g!WNhP#aS_WmAQ)*l7EC=wss4XY} z6|i$Mo5A@{uLhKT<~SRecp z6W*gGmbAnPz3Lr~T0?*I`oG@ud;akN0_qlg=z*Jqo~mzdLuVU5QJOIUxZUkLmlBFNGEpdIm$mh4hR?(0`_- z!oSj^G4?RU4*SqY@ZX&!Se)+ zDD^$uyMT}F-0&WOespCUupo#4Q(;69(V!DxIinYYJz8Q zqZS#HOXO+t4nm;W7U?db@LKM=X(%GrdE*SW1y|vo@?yxkjfx`DLOsGU6F?>4e{&9q zwvkiMorO}$-%SvcqwA@1k=2hSHVTz%g+IGV5R-l?U1U3H@&VhdmPflL< z+S3ypev<*WL#J%?J>dHR#tD988d>GSzcX>lL>-x&ViMydHp*R?46-~;)aZ1Qolh@e|e^g5PWV>)YIJE=e6DYQ0<2!tX^?!gvu!!hy8P5Ns4V`o?mwf5B zV#IiTZ4Zjtv#(*EJq?8vZ|SGfg*K0}z~js?mW+;K#8{ESOWx%BB>xXsRLP^;TAeID@rZt0+>NK3t2s%*qWSJ4hrHKgqq#S}y1R zRcER@bbQlv$w$jHTP#JB*Z#Oj+Sl)Bvz1kmt(&!^y%x>VtUTH*mfQGyg=ghvH>n3mH zy52U$y52UjmICI?TPPIAd;3qm_5JUA?PIUIcmI!0ZZCVeJuWBjc&yX=(n+(LPW_-L*1M5oRHfIhKOVY&dj3y}ot+Dl zR^p9L_U7CEdID^=QReoGasI^4M!-v-wTdu8Uj09K55ATNQjn5dBxIKH*@n+#<64Aa ziX+elOqXF}!y)*rM(^|Jw!wSn_s{RcRk->`5BmoP+h+fu50u@0;>k;Yzcbp5FNE!O z|77y18;?AC=gu0IJJ+s1_Sm&+yJ7Obwy(UhedU!q{k?X(-QK12qOrG=1TXXp-^atR zJkokNdH@EevnjxIdKNoE2th(#ZR1q!gQ6ua@hBX?#nEj$9(|-p@_6ScJPwlVfkeNP4`~nv7IllgQNT-(q=WN6H*lxA zT?058qzRn=WbmT%uKFj7a2UpexApml*Z_4D`zT`h355&g1PH-aEQwr>@&`w@eFM97AMJ_WO>9DCFwf&?bZ;HtwRGX^tG!?A# ztY|#o0DZkR+Q=5!QFRFz^McJ{SxXEbwYuaQP2=?@=%L*}Rp*wwJ#kfKuR71{vW*Ds zC}+_+r^=XDPnOq2;=m$+9|OJke~iqi^*$KvL!-1uk@}a#;>})-yBo zyc|TW)$4O5Nil3E_-xCwNWr!IAOKK0_<4&fwX$MEZ#t$_M#veCXB1Te6p>on0WVe* z$77@_hKNT|EGgbZ5sm8vJ_XO=|Jh#3P#O`(7{_Cq#0ohTUQfCZ(DRrSiM858q*1HI zU}yWl12>+3@`>x5Qt=%>_xX<)##LW;I|*1lo=&yUx@QrjQvAQ?np1`vfw8!co;T@- zA%F~1-$UwC0HYZf0x&5C4~)PV?O8;QQl`GvP?(=&i~;Cx`X;2Z>^V#*2t08Tlh2*Mi0DRN|W*O z@a{TIVK}_P7^6=CESMB>tN>zKHbMutU>l{W#{_RuS64LR6-5ZM z=Vo=5OR)!nKqx)30NbdoUBx855sku#!_}+6)2tQUNE5sYQnoLoz96{JmJ*Ul(Y=E0 zIe)dvb1*h8|J6f<(op2HvX@6ngOYiYNd-!Weo|ssL!b2*$h*m>`c|Y)&P2Vb<$$-W zWoOv|4)XP)oYZ~BI7LL_B+>=k8~BBArRbV5Wy3tGBAaPf*9Qlz6*7M_bz#SLt+lpm zwVp}P3LN@^gg2{Vu%I>gr4WqTu4k<6dS$!iDXHR~I8~iaOsS(REefV(o;!*G(kMyj z{rYF#vh4zWAoeKk%L7E+M-*laa7O|n3}M9QK9KuJ55$3za?lP#&CBO}XX_bQsT|GIua3RI<6HmO!Q5rpU>vmuIK7H&?0O;TG5q8Om5eqv)=HJ z%oFL|jBo8Y2M_)AN^b22&a*>!D0U8B0N?lLFMxBg@B8poFF+8azVE|GdHZw87pL<= z-M)>}3UWx^MSh5oX1iW)*J>}&)nG~>Zc~Umb*3u-Lb(VR1&2L~JBae)#93yL&2P6^ zCA0ynMFbdRn}M&0IJ~G|Pf4z)Gm)hQlGW-uS4r3fo9(9B{1_D?PXtFLk@K{}6(T>a zjRAoQJa4$rfkkcVe<=6S=;`$FN2XJ2&@)C#1&<^<=d0DVYpWF>S^O3b@C*v-Y06MT zJ1$VGbm#|q*8>2m;y4q7!C;{JStt?w@w|;b*Du$BxkEaq0Y^7M~e1h&SxKNF7|p#&t?~&di?RtmP1)yxpL<> z?B3p$lbt=SI-6U!ANloe7}CXGhp;s1{aZe-Pfzb$TdlgYBt1QS?Sq#ukH_>kt{g5# z<6!T#ulQL3)5a)ejMnBuiFJ^rJ%L-v{Kf8 zUNFW!sFXEI8T)UPGidyf^Ls1hB<6&Xz^kvoBk!xTu0O>&KrK`Bn#Or|U@r`L)NC7g z1X`_f+G@30)3Viqr%O~|UODX~h$;T?x_LhBO^X<C$Wi`EL_umuUu__H->%Wu8Nu zY@>IUs*7Ga#&9*)&C)O0Li_v&8 zoAvgJ!hq|CfN>hbp`u`y|5V0tXFHwwe%#`WA$3v2z=hq;&TiXwyR)L(olSdP#z!6N zfr*vMvO%ZcSO0770NmUGxQ}Z9-O~WN_rsua1)y>T9|Y4e0H$N$TaSiAIUNbuQDTOqZ!FtIZKY4mQ4*H7)I+s%Pb- zyCHN2+gdJ{)2pqx4#s8cbp?ErtE+6&^ccp~8ZQ(63JZc{W@*{Bw3GTA1=rS6G3o^Y z|H0TYF`os(xsIo#O_KllD8=xYq2?uHnPqg5Dk)@kDHZ|M%E7~l!fEJxtPASjExMo* zNu$(ojs{htPz&ceZxNtSj2QsUEv1`CFshY1>>IkJyV&j7gokNI@oUt%bU!#qCtAz3`64X_YQqLu*aVJUO zLEraF!rl=P6Elgr8QMgXP-655K+yHAyY?Enm1Z}0aXtb*_aE*#RsKFK3rfnUa4t^m|S&N04idVAOaj3B4B z!t+6=GuTh`(1q*|I-MZBF*!VJT4}F@=J0TGBmL%-d@An7bT>G)Q%JNp+naQ)N zev}YF@>lQUE_|6>AwNj|IlK;IEC@-eCM~uNp%<2~prX^LtMaHlMDCz~ogsOZEtZpP zv8-xQRDk7Cr6MNUEvu$nl#8w_E@szVZ5GQ#E#zXk%FszQR_oOo?xeTK7VhT87n=(& z&DORpHj8qB|8&-}su#-zty-FA?WU4PjgUvpcC)QrnA3$DOv~-6>CD<}^}4C>2~|XG zF^$TcJE^q*oT?Z`OJ6Zu^>i{(mj3E34*RX;@?(-Q^&W>h4X zd=V%g0Z@w&IHHGWZ0KwxI0B5UFAIRb9u-OE0>%_WIHcL@NZj`h+zJ(=aNwrDQT?&=q7gBLket`SYL9cL=4p% zsJ$~Lz1hxAua^M+Lge{E1A0-0D(k+aXrEF6O8QnAou=AyMo}RE^%zG?mw@8@WNGMmfG)B|i?0x|jNtX1+?t)1)&Ra@JUA{#afE=N zg#&?@ww(3ACT+wt2MDK~UNQPiq%|N1kc=S$j~LU2F#tq>-UR@Ny^sM5dV3%Y=YTtt zo@PFZjJ-Ja4|Lsi+0VaJTwVXRvh?EE zTVbY|cu^djn!(3e+fbC$hr@euczme78T&Wu*4N{JQWee8Ff|Fat0w&$D3~^~UNSSd>vE`-ai<5y!1oyw#~W z4iFJeG<(G`BZ{n(GK&Yztcc4w<51F!A+;{PM$Skj)izym;3C_tle=%c@#IN-Fj!vM zy6eUpH#Tq??e^wo)k#t_aNVMq-F|Yv(X9W&!w(N`+}qpx!L8+$px$ikpS*om6s{YX zY0_D2Zg$$ZjE#*OZ@lZTAAXqlOrBl8c9lMcE9jFe$dx?pT#*JErI4=y5vt5Fy~rGg zJD`pw1$uo|R3>RuE~?plfqg(VDa-NK?k017YGdalps6!wlm?vK!B!M%ICCrD z+o&ag%2Ln`H(j_MLLf;2*gn1GtoQiwe@k4~b(7R}T{r!e#C1UG%18;v$o!k)l2Ue(rt8-+=Ge>%IrP$pmY%dm7h$ zP#rPuotIr=I+lyB`h zK~CAQ_}RRSK}h&%7MCCVr z=3FV_>Kwg?th1DjOy3&wRx2-`K zn$WJg85+$rHKvzNwU(Bb$IHtpcgpka48w5XG@=ObyjtveTxuG#EHmwNx(y-rH`W7+ zgBLcN(6*35qd!f@lWD)*N?RIpy>>qJM!%nBjiyNmHx|z(O$?>s^W=8&BzaA|s#uIh zDU7ZiB{`#z>Piov0Ho-G{1K_6Q~ITJ9>;{_GhiLKNT;I8Q0n`hi)vBbr=qCMTsN3z zL;07DUa!wxCm8xZ{gLi))Q3{ea;s%NN67i|lA`^f)6FwmN{mfjtMwx(vt~=sHVwkj zFF6}r_CS=joqyV^*KM98U5hf@m}r_3Nz!#Zm;YXkbH`}4a<^7XUtpPzMd+(L7I~-M zFulCdRQ+AbIUhSVXN*pU-1B`Ut0kY`>J(jHKkFMruH?%K@@1!tDqG3o9t}7f-)3^~ z{JSi>lee-np0k5U#wcVw>ZWC&m+YfL?!EZr{Iu4&=Q(u`l5-@GNmj zk1UfZ`7QE4$iE_qXN349$+?onV6GPFB&jA&NmW1@dIOzoL_{zKn1#+Mr)5%2C%F@N z8Zwb`N<^oSR}3baXTDUj@M8iq4*+&F^I6a9s=QD*4K1pCo)^JS$fVRRjk|aQ)oF}h zlv5aB9V~EiZ0U2+e5$3}n_iRIU54!2Jj}`_O-;+9hQUJDkn%TT z-;@%zxG*z=5dHhdl+8bDcGNn7E~L&Va|ET`Oy*mKWqatR2{=5xdnkE&J z;y_@-a#)^T`uM_e49+YIeYO{KAbr^6Jn$VS5K=edST|6!DMc#e&~_~g5Wjsxk;ic zN&zaOC`l|r2q)y)ztBUxohTBJm@E+zW`3C$iz=0BB2}3ec~NcCL`895rb!B&=TaqA z>EC_#b1zUF{9{(H^B+W#4F7sUkKciQ|Hd*OH7+-Lx8d!_&%fcq==v?1l6=CwfM@CH zGtXPBZ}gc`p>7*ea4B?3X_O|ryZ1cr%xU_?r)|dcH7QRBDfKV=cBPbh8$=HMyi2MJ z>JwzKA`84b85QG#lzB%RhBcV$7d`Le@Zj+1Gl%yz4nK2r*l66>I5<8$JUl!;1UWox z93D3E!^6hmM~?10Xy7vsbG(rk<)7gY?;E7HUQPh@zki^z&RnUfD1{&cYDseS|;?q{vs-qj%BQ@8dgW-u^s24 zbG7_#Mui+?%={Qf*?ta#evG^Q_og;NGT!2Rn+k{IhFBhQ^a zA%y7Jq+6tpuhYwfRGDleO3QQ)+9mlkB(aXW#n|holl2wvk!ox=0bZY|B~wi~6iuQ@ zl#anX(u*d^P%|CJi8M67CmhrHvjENles8GgZv+6)7GQ6Dra+x1yHB zFI~-#DN>EiNTXFX2b}$GgI2b-wXCJGwXChu&eqyVCylMGu}V6ly+-9KH)pz4m9naG z*3`C^PP=7&TTACvBveZ!on0od#yUAf^N3x8u?}%opla#lC>{h!(?L>MC!KVOlTJIS zVS!}eP+A`xhV6jfX4hGeN%IfSRH-%&=JGhNXf{=K7KY2LETfIa0=KM<$Y3~S4>bQx z5F#)De*9Nd5Wufu(&>7n8xl5be>m*hDMpMDN{H`cvKg6jF*oF1m8LgQHk~R1E}C)S zrEPTE15q*;T;`T~Ivv9eIrv!I*U(njPKtG%NeukC){CS~1cvMpgggKSD2foUFl5a0 zJda=kL4cX(u_y%aPzG3#FMmRR0=5{w*`K4Nw?I7r$RkRl9FXT>0)zwsC9`YjwZNsT z_0kMNsDrz2Va5LX>J$U_s_$sPx&Uf>45d!b| zm(e>{00MMv_TN8guZRPBF%QBp%r4R(E_(el7yu7H9J*>4p>QK%trip3`UtSn_L#ys z(77TQ0Dm~d*mrXHZ}lf1*;4nvGJ2vO%ZikyTA?Do{`EYPzJ`iN`>) zaaQE*FpAQ|d>83lX&`N&IN5y7|Bgh%F1?B9Iz{sG1Z`#0~($Zs?n zxZ(f*6FLiDG@fs~(fF{@fRcHs;LIV)+?Ix`&&()-Tf~$ANJU$R^!YI3gh+XKt)8li zT}j*pvj)NP@=oZHfIj%yIw=oR z1ThVRAOc9!JQswBEKmCYQ4j_+29zF_6M&*H0rA3}e}J# zQq;OMJ6v0Z?ymyTB6Z#!*$Frw5)y8z`_G>3Jd4Qvd_KSa)b{ow2|}6~Jv&p^gPVgv z60Wt{gfu(6EF(&NcT<+U(dZtRAM+d*(&XJxdyOSZddDv$8I{*n%dK&P^+0dvbhTF)_U{7$88@5gS8h|R-i0* zcFGbavtvF%Hc66=bTmW{-2l@<4RQ)XdSxDOY{(d)1F3?{p~ZGHQjrD@@e-#pX;NR| z%RV@8nsGA>S3TFr3Iv~i4a=ko!G)&qs}yV!KS<*wX^)}^Q6WYvy&nA(It%^(h$H5I zJOg08B%ch1Ap?}>W~&dtnoGmcl8;8f$OWf%Isn;d97o`_N27eJP(SR-eJdOlbQb!5 z!9sjg^5K0;@LUIoqhXTnw%cH<0Jm+P#yusKHlXnAj;3iwyIpio1g&eoi09xmY}`Re zRnJfjPbm`6Q;(EH3r|gAyt!UEFY8w_Hy7ZtNQYMBP8x8Sx2#WzZA-1)UaYgyvJ7+5AIK;w z`lIv|r)%r?4hMP6Ide%;r+aU^{`y7(Csx;7+p;#II$nfc|J=s%a%>=ItgNgpdVRkc zu#2a-cB3I9R)(32e$_XTh6Qf_^d~>{J#V`Wi`$NWbNrFpK6d+?(LDZNwRgP>-tpJp z1^3;Aa9ro|*YZzoGCvDOg-|1hUM)IAq0-16BOVnB=l&G66UToS12J%X4L`7Z>eLCx zJ$@f*C;s%SfA&2m{?4o&pRbu9Ab&MDIF*@UXdeIH?>X^jzQw~|!U3KoCF?Rhy8olq0?pzzyRh_Y@S@BZWjbh&e^*n(nY<6ok zAdH%uo(E7Gz%o7Ni@rtsZ52ilTpT|PEFoz~VAOUTo9Zw;jh=&!%PG(f1JL(3YPB?N zG`hn^oe9xvx7)qpXnokI$5|9vn&iBmL{S(s#+d86VbO25!hk7dw7We|QKfmd?YdFq z`(B)+t_w4YDD4aeg=G^$NI>YdBi!0&VwudyW%2^@dYr&*xQtieO*Er3x=U}Schbk{ zE9qPFA=%u3I;KSJpfu z{W_WJAXhTUtBaaQS_M4t(nvx1SEU^3Nb49oEFV#@4>VG3i;l5+nxxRX=oM&&U7c`k ztEiUX=Ron}{6)1W=>!uZQhvshnat9sIa%bzG-#8I58C#{J{FZ!yT~n~c7Rj47M@Ph z@gyywmHi}M+xSE|A1P1e-`ERqMjOhDWRgtml9cl}d+_p=Rta3u9=VD^{B(PulHseo zngl3Ca|9~Q7z|x>D3BaHnHnZSgY6&L}qsFYKp6w6x^61QU#!;Om&i_e`CXHErqF3Tbt z75VW+NYhLe0ENoZG+Z3#1+)YT$t3P`vK#Ak0ce~PKrJqaCsaV%Q9%^ZCKgUbH-LAU z0hHjr4-r_zr33(hyl-q0iwFQ2Ba8%ELd=RO&0^)#)T=m4i515|G!U{FLjc~)0^4{` zmKU|u360rH-d<>$N?6oj;3tDY00@J@B(wu1ELGElJ@bydEcDPhRuHQ31w!yHTVdN~ znPC7nS=P2V@BuJ$&l*=AK(TW}T^9MDgE3$X7{PxcMLN--DAj}zMUjfa(@#bJ4Sp`l zAc0G`FN*pUSa>$==2zIPSAFM3I%u1=nNd-*<@zA&PzMwd)No&VGSob(T-&X#y14_i zXe!;*G?iCEw{~5RN`=b=Cw%^yNWJ^2q9(H zTPX|CX@9v`?nGX=NL1^}78~wb8;e;rLp}}#MtggiN5F@;(;;`&ikWqdR>hS~Ur}wd zT!Zwj{XwELgs{yya5`NrkA5lE&L_@V0IgA+XQK#_tQf6@6p;ncYHmdUDH}8y91P|s z!vT5#;}i>81O@0cv0fTt(g<=f%Pgb-tab{lFiOgVLW*=e9Hc?SC}FjS2S5;FxH){WD6N&eXfKZc|J$=H6Q!9gus0?osmt_WACo#~42Kc+ z!$h4WB8T`QWPJIU^=rW}f`~&Rtwgb9MnV+l6N~bb5#?Bn0*kt!&zU9wY!hIofi$96 zL1d!^NddZbN>t2>fY7Br*ehrXeCS z78lzPhP47%J_9uq+&V)=ZhEn=5qdG+9(`7-XcR3F6W>U^lglEKnD8VeP6837KnX##BST zGrK+L6H61DORp+)S~QTIipqbbUAH?qMFp@5_xe@4m|5?@k|@mv_K1WCUfWtX0zY;4 z^Hf!iM^&letyGr#qq0yZF>6JdA zO5OcDRTiUBSt|GlU6rG|A9y<6HHDfG!R_zw#m*r*7w_%wJ3u6jLSLhF4Qq|@`1V^e zVL~l+PEra%OzWieE;CUSMUC^VxYh{IW|}uqBe7(U-!~o`frw(1&Lx9+`uF05POPKI z!m8FS=K>BcU=gc9wRm)=V`A?ru;&1L-DI&?u8yV%aj+(YtNZF^uby{T6>B{?p5^)Q zI89lPZqGX(jI=OW9Uc?~3lzn8GM!G(Hrsoz)eT?kI&&_3a0y5s-d7|t9gO}>n97{>ba$rnDP2;7zU>lWP?`yD2Z7L)94)#_exep;^ zdGLU%qwDaR_8v?Y`=rVS!hTzp-Domt5z?e>@;1}bq~V($9pmeA#>0Kyb8_?YXoWuH zE8dKGwFf81H&(Nmxi6la>{%x9ZSu*uYMMf%NfUr12Q&u)$P?cLWjR^*)0vbdV0iHI z=+zOK7D5k3tyd!I-rg^ktL_Y(LpTr4-0?K#T@zPD1^6{RJhINUG@uKga>a@w94 zLoq8rP)Xt)f>NW=r`Pm!X_KOikF2Rfo(~k+Fq&rp02}?ns|V^j2Y_2=q5yqX*O}K^0SFp1J$hlL zz*u7d(wAPX>V`${2O&Z;8nrX$W<4ICot{ny17$QPHYQcP?U@fvCM*=S(LgSC#(yq) zZS=9x7e@aM9`;}k8+b0f1RjM?hp&cjhF^g{gZH4q9lj7B#rMNcjB;T@w_HVxf$5=L zrH1S_^b40rJxvJ}n-`B%s#^JZdbfpL+u*15F$)j)Z-b!*ErgtKkqbS7FbN3@+p*Br za9TjMD5jMcZku*4+>YxykqrEN_*L;-j=ah_?XF+0)^k}Kt;wq!A=ELa@3`A7SKBBJ zUJ4saSIX@YusfM%%`JFUEs_)XY; z7l$l`)UtFI0H6+YljK7nCc<=f${}hgOCC+|4anX}&o#QIW!E;6Dute25fGv*E(jfY zZ#d%t1@@~o{Ikk5p%x=x2t}n(*(^_w7RytlA~(|HxfEiTtpgz#t$f%A0kQ<&0Dkzr zjp!rzVT|kk0^EQYK`nXq~wc09YS_XV!?p zfOU+{-<|3b;HcbtCgVwwn#g}Cs z0MIUrIyW4S6t!^%dWvBYWn!iBbaT(W=XqZE0jUIlol-tY2DYw7iho6Zgr;q?lGBuw zccc^{YUL9mV(rshBzu|Kfl_%2p8=!+G%2l2lI5oS&}O;3rcs0aK}#ge&^Q+W@}h`; z-HKAiJ&~#`Gw%Q&&W1Yp5NGLV)E0$%h^DQi=QVkiwqR2!JLw9sy1d+D+|gZ4*UTuY zgSMi?WPk=P9Lx8;*D>%q>8=LJxnL2s0jVgs;Jh!GP?}P@#v0I+Cc!xu zJ7JL5g;W|m3)D&hMkOFYL1|q`P09L_K^eDwIF18ho;lQ-N}&*xF-x`osoVbn@oJ&ntic-zgEJksIZI~2XC~lY@l~Noq#<&#-;X4-NKGhuC zkjm5n;Yh|gw*jG@=iFf6TyUyODJZ4X34)k$#wc7ruo>l4Q-YhCMuo0xI#UL8!(>!i zwoU~_UDsM|*Ro-0%;e0{K%warP%_w5oC8v`7z1UTC7e@IpCde!;ToBed&ujvoy!+6 zDsQb)lETW{gNx@?`D8gQWh`T!moK7De!0;q+h8E4LVXj5mSed-E#t~8{eDB?H7C#R zZ*2{SK&P|2d+OBA_GmmZ>y9T2!_ZpLyG*0Vv#MHhb*cTvitqWomf$YM@L)6weBrE2 zZ@>NK?RrCZ*4HPK*0VvHrm>KqQD{)63t7+Owof_R0O^)#M%Iql9Fhk?aI2MV_4{mG?`3Q7zK!v!J;|QbL$66Y*L! zC}o6&4i$DuGd2O@HqianDYqaJg(9bL;B~L7j=Qqzi?S;Eo=Q@;k4nGb>^nHIaofg{ z)CDHHr)RpM>yWn7h-?R8RKs)HVCygq>YAEvSe9!^u#HOH0gq_7D|g88&6SvZGCU|sU=l|0eyAm&t`tc zqUvc}uR(IUaHk@&M~G100GASg?p>A1@LTmoHOG*%dbXL5)`OUf*7AG_rs+q9VHlBb znmA#2zHLIAENMtD&P_OLy@mMTEo42lOjvgK(a^R{GhMl0!m=F4+}|;6$FzaqAa5&D zoHl1Ev*K4vJI(4J>5+6l+0nm0j=RG!48u`3ieD0TI`s%}@4v|gjcWZ~{dU~E5=BVU z!SVkZr75Cl?ZVq*AdMT1@a4~sYOcArum8&Hh)&eCBm5hV$P36z$*af*$w$c*@)`1j zSA0*YN}8ym6G=+0;Cl-nxv{oV;A!(Ec`CwaXgME<9M7-#*<_l<9Iq5r`tfV z38XA7`dJ{sg2*kL`CTX+j0Dp zg1#7dF%?hE8GjM*oQ=&*=QkVCZ#0iz^%%CI=GT_{a$)AIPv#qg>mGmLrba7l-XAud zjg5^D-QNuF&yN|$YKj(QhulqGK;E4};^VAhu!$J1klTf(suUTs?AXP053@pn9>Rfc zcClZjn&+LajA zAwvVuch1-^%^90BHXPoE--Jf9IR0tUvYDHW0_T?+4V>2%EbU^3^oaef=a*#pjR>$b>XGo0nOF>sr~2D7<6ec9)3DJz(5sayK&5mj^@aH8{K?FYvIU(#Ucztse7ol>#x=*$=YOFiC2u0k>6<5PwS9 z%jkS@N2_z*BTmlqoz@+TImPmc(`Pm{dvfu@+0(YY?)a@B*!>fw_b8Q~pq2-%;M_(+@s(MexlyJY(A%lZzM6MefOiQ>)(q zV0-TBaCxOz+p0DvQY^h>-TZAHS-XXY>+=iCIGgVx&gN=tihS*Be|zWS%GbX3m;1G^ z$%~zD;)HWUe-EP?ioLeblr8iLjpI0`B95a7G)>c#@;HuSJoX4WKEUDer&@+#=GAf;SFvb_bk)*P3zTR9EYQBZ#;dmL6-+!@p|xl)3Qwes=?S-zkSSCcY5B{ zBaXjLa-uQYAtORYWzo{FoTpVhnx|D9quwA(qv3!D>*YyOPOI@`R$wJ=x8vuobqhA092`t0wCJpr}$T@Qj|%oy@d;3^W!k0S`wE+{(wX= zg5XJ`k+2&h!-&4rFuv!~^78WXrSCBsozBk2x|C@uMS8w7o^&5rU*A~258d&kdk$*D z^*nb&;rPEmg=yOw0IF-t6`@|;ahev4-_q#~abck28Dz>bydY9cK%|v7G+~U?%iCvky3MIV}FRH7C zeo_HU7ec3Old&&S_AF!1GB%AZ6CxF$bEWISI1IlShF=VU@Qb?f!6;r`&beukdbefS z9HK@+#od=^oaAek32YB-i-Ok#5JkK9(Gy~%a&RtFDu5QH&j2f7|| zQIBKbBoTy=1A2fS;lw5hX_FN~JT7>nxSwn%(-$_3%7cO$gM+I_2fbdecje&V@Tk{2>K$Ibe0ZgIczF5p<;#b}AcUNxhd50biO4Fslf0F@ zlYE-|FnN~z5+PMN&&oaIMXscpl#@l3q%jmRT&k+s*;I2{rKkx?$^h@O zbWu)IRb+8FNy~ZmWTP=h7)1dZ)Usc+cU#(3}eSMJ;Px4EkE=YLR_B!^4|P-c{h;zqpFi;59_&-D2Z6<7*_&9|IGfMduP?tviOVx z*dgmvu!pJe%JGl4bjf3?d_K7Zet+bmO455+x7Y4)O;3^)rG;T8rQ_C7tMxT4jz%;H z!hq3wqh1HL(C@9SjmK+iy*@0{Ke&APz&EYqztJ?y(lkx8tf7B|uAlLHtQv%|cl+&o z1_w?*^w4QM3&xUUi~62vdhmV6QLr7`p;Yh{nx<(@=vpmZ1LyU|$>U$SaVNDc%T9N0 z9Q6mSO^R`A&>uay@fA-`5S5sze?qM{%{*|4u|};6bkjQLHl?{GRdqc(8$rms{E-a<2|S{jTco`sdS!kvhaLSRFTRYhISMb(zj^o(Q zxh9eX=gysTT`_b_DTJ#O@~K%uv>Tc-wAM_sV?7v;p{RK5O!@z87 zjEkKUdpEyi-0yVy<1d;upk8;M=vWSxKf z>LZ}>h%V&XxSRmeQYmPp9&wdE8&4KdR)5f2-I!I2x$U_WiJq5IuTA=C+jYv;6kW?; z`v7!RcHF^(3EPQpquoxAQNP`2&|d~uA>&QYFwttQ8M>~st-PQ}l3uTyobuhlD5xJr z_(@3F?4(qwK6)$)S=yn#Pa$Qq%Nq4lEza$`pc`n>Mx)g>+SK>y@qlyd#0imX^Idj5 z1q&l;nNcsxYN4(eH~k=(FAe%(s7@>?OXsXsn@%T_$V%dv;ZEv%?2l*^GAPykBS_Jr zzDJXKorU1J*JjKPIrqGbO3{+z2ER!OejT#E z-S=M;0UUm-@Si)iEgYP9PHe_wMFUH z*6lk72kRRY-Su9V(spNgB@Zndu&uCdS%bkg;e-%^uj7#1L!L*zlYBoRBh3 zx<0Afo_N<+=ZVv7)@;%u`LTAHtocW5F(_~bh7O%sHr0WM>KYvAI*kCG+BVgo>vXm| z6H`rxO@DX>)^pOm(>ip`c61%T@yPCl3%h~`nli19@B2oeG)<3oFI?D-HC;)|jD7$3 zb8BmxdwZMf>zjLfn?G%D+vs#-r=shW>N+%PTU1BLG#xYfj>EKsxdbpwXpm&^{6ji) zN7wV?jU29>S$p(IwIxKLG}G+*X&QK@uPI%NQEkPVu1V93gEaL&u(`Ll`Fsr4cz~bo z|DuQZ2zd^9G5K!ti{x+NVuFi!ijZnrO{NtmTZP;CDDy3g+FVfG^Fr72T**Q8Rup^jkH8B2Fx3Tk5VV%2(~()z zE^GGJhEIf!?t5*=wgspbng-y6VW%lUP16RAT+d<5wCynRTo-|5*;3R-E3M;i08}}) zA3B=uM~r)F9Stm}7_<|-oLQD<>O#5}kkoCHaYq5aPtgip+fqOndAcqDp*{I3M`g4v zH{@J%8?4*1-8CEszx|;`WO|R(ywxE26(799MyN~HK5&D zJO0W*DbMqL`phGZ*mON-ap_jm$TrHR6a9(AN)I>-NHDaoFao@Zo#Z%OIDP-Fg&{@n5Z zVfw#4^4%|$?DjWsO;ZZ+fBnAaDUMt9erT9%jT(k~{ArI;`?kM!9GC7uTk^UKJ^kO` zIR3O$2!B-%LeB$se)$h{CJEt$*kpJkvXOON&cdE#S3(pwGm z&&$zz8kcoAy82aias7o`#sN~{S!vnn62h?gd^D`-UNYW|JP*&ml3J$bc^@H76{0kF z<@1x7?gwtNJ?)z&p8x(d45X$B$4N&B!IPE+0A_1F|1IX(vc5rhBn_3{Z+yW?IvjVMsyc*>i%KhLW6_IT}gvTq={;U~!~t zlqYlH&BK@q$JI2Qajt7jNCqV}jWR6=<5&k&r>$!`r&KB?4PB3FyE=z8RPZOtnWkzr z(-PdYl;WaZ7u?XZfrPQK5rv9UK}}=Abxn`LD2oZ6=lfDYN}YL)qvv>3**C4fL~bH? zlE=xb2oV{8HKHDZDki(=0-ZFO?^QRg-VrHsuR(538N9d>Y}BevWN{l=6;)WGSkSR- zJh6d;qXpxM@?zwBiZ|%W)HDI!8d4<`E&o?C1(C)Oqo$yOON((ULl1JlCJQC~ zc??npqU9I{bi;9V!_eIq4HBq#|Ib`QpMcbgeSg(=rOQj@v>FbR)^vF}Rg_BK_jy@z z-}fb@DqUWlwvyqnDyK`!X`^xcZ|iHZ7uTBrI!;O1sngGS&goN(ojU!Tzc38P)eXaN zhDPWZ#@4Xea2y%CHA;1+De1Rbo!RNrvren!OQkWL(wZAf$7wW&%NrZZ|71m;Y3Jwk zMY^@Nv~{quLg~uN!NKYZEf;_F7beU~Kq=7+;bcSN+@3X=4JK*#-fPm2-}e$**V3Er zxbwabn4V{Tx2@|bYcyQ`7@Ddt*8^|zgOEPwIiIubFNQ&gR}tb7a_t{*gje;cE-J3s z9)g0|3UC9)CIv;6x)?K6{P;wbR$ygxx?pV+Rkq|X?UK2VR26u*y7A^mKCv;V>Zf}C z$V&CLGe~NBL!~^G^=`}(+KghHXrnIju4wADWi4~s@rF~}dA?pP-LcXt!{xpCt%LH+ z>3i2O*eKSPhpntVR7ops-;)Jda7R!JYB#psmgm_6tG{mK`e;|*G$#l4va|9sd0?D< z=eD!(|C&FnzW}vlJz0M^yxY9xuJ-B1KC$h}`1|-Wt`JEYVH~P*#_mPQiAR^Di|WtLE*59+fZGiNAW28N`3NZT zU2KJhNSrWMic_jwH`ent>J5{$(O5&E9c{VFbv(gMtadX#6|O-s;#|PV_S3&mf8+06 z=5wpr%$8bhZDFjfo;svnV7Kg5y`0qnwM) z-k7dVCdHC-IX!>w)>{{UBRidGc}nD?ayI$!*3JGPn@$g2c>5jGsg_f&I}?%-A)-)8 zx$Q-jRwLQcfjD}97j$#;JlMoRC)U5CbNtuUa7gKJSS^M>?*%@%x^?~Ln9}j)^ok`d;ns_BHOqyvzgdqg!fAqL8Vuz1W=ATWSlud=1cp* zE*EFc-F4UgE*EE{uG^NBYUN=6;MDjJ3KfLCR|`WmDHtaBOqy{GE60pWx#3xx4P#SDL0NAz8p^6it7BSs6MI$A#{>t|_Hbt}6w= zxxW%A1u*UhOP1@|mJmugJ{JIE79oUCLazM+ehNpVPtFlCN{c*K@x)Ao(s+;T#e3s4 zNp!Yb~6#eOPMiO8hq=)UQcl{2t|8 z{f-i=Kcuv&QgT}d7}^?5Mm(JuTUyJE#>Qe zhXUf0Qm%Zj5QUVo5<-Xvq`Y3pPY5xTk`TfPAw*xq_sIrfvg1Hzs`N43v}|Xx8=&SU z=IJ7_OqSPjRC(jahRKVHG5eY>502o3MTK|559*B|nfjv)e9X}6O zeI4!f#*39)HJ0BK_r!~2YDm#@fFTqOx_F}zFeoCXaQdTP~%>Y-qKHE zAPC#(^9zSPKF&k*H}B+&O+SFC1wj;QnqrLc9!20czu1qHHRHYaj_ASXP1a%)G8&ZD zV7-&WIhJ=e02(7G=P9Sr$%`Va(OXuwg zRoJ2(Aqim*9i5X#MWIIkJUP~rC-ZPy++gW$nQ*UV{5JM{8s&JhaAL@I^Zn=)kOH9K zAizAO1h%!1kf4;)7BftMeBFZYx11>QJ+4eAt=EHqF4}F+a~*-q)IjF87JefF07COQ zlq{5?Gk|JR3jVO69A50Ooau(9gK3&ZxpbU*vtAD@=};4Z#bAOJzVL}rxJ3Hsf zrZ7zgN~y0r*S5jHbSFWVX*PvYj%}I@NzKq4SPotW3<{+y*V8+i#&kPmpK>rKpAibi z6r_x4l+2Za8w8#a#X(@96b2WXCPd0S^Jsc(Ge)6F&5DKYTa+pWYB3?w?HQs>GEG}| z79)-|DGj4-I~KUq6tA5dslG?4At~j$BPi9GV~ZrAF-oQ0B?SE*@_+O%TL{wFm50g@ z({x%^F55@CPgPY^MSAORA}utkGjyR+7b&iaMzM9PuNkm$x`(K);mm_uiHcQp@0R;I z_j8v%^YX|4tNX3*2__rAy!rww=6Tyvnr#^1t=8JjXGe95NA=sbqB(5Q8-=}5o~s25 zZFzX=zc7;Ij&mbMz3G^LQt>d1qVuX>v&3NYfjn< zHFY8NZ-MW_qrD#7f#)_TZ4{c9MVgjv&usvIXL77<=(*<7km{~r`&ywnne0dmnWN3L zT}l;`NB4u7QA)o!>G!C>aJX3Sd$oEyTMUO30{#B9U8{Nh^(6=inp#pap=nTD(>bS! zDJAE+0nTCGSowZcbtvsrcXiNgQ0My94jc!o9W)!%xqh_+$H73?ZAs}#&7pgvNwJ-R z3R|-eE;#hPmJ&h;B?PVqmNOj89Ji3LZVJ0pIwfUPCu{s6;&bi0kKfeP1GkbC!*D*o zv$CQdf;=lR@KSt8=A*@8luJGsY~zYyI7_u3e-|GNwxb0aknsZeSaY$xvZAg>z=>@v z!XMIC#XLndYpV1!L%2Kevw!u+_q_S7kKO&)TkgE~u6uBBjT|0+b^R4L-SnZI`OYnq zYIB}z1H$na2;z{q_de^8ERi)bCNr`{_Q@G?9l43zLhj;Rf0v&iFCniXuO}p3R9TW1 z^DK_yayC!1BrWqz4B9b$*K$myUyYlzBm`quGuiX3{Tq~KHFo$=MfI(0C~eV+nN(F2 zm*dH74~xC@k4|iUb@Rl%PdpL5jLEl$j{g*;FOrNsaWm&LFQW7*Cavj@JIBBCKLh9Z zPu$?Qo_OMkC;k}|Lp%N)r9%DOJ2(Sg?u7Fvo_OMk9=cupqGfT$__l57US=7FbI^C->@CVOk-r|jCuMc&N#CIpDKv#dQIkO&K}hB* zwL6qAMYqC$0dsVR@Flo!Vv1>0p8d315nPXf|(z%<8Hm~=+*~KuboJ_q(*%L z2EkO|J97#_06)Y*Q1rJ9P`^g0<$waVaSj#wkiat^)C|LuQgNY}py=wt@lv7Y2d%i% zXxO@icc1L_9Y;vj58SW#l!rkp^uOZjE)(3eTFuF3LY#AIIi8Z>bxJ#feQFV+uq(k*xJqk%23F-IX&?uabHUR5u@AufTt@*SzF;;Cc0^9j7cR;t>H($X=pNDhV7{=!Zo*l=f;&4HWf#o6a3Qi8=1lQMqnENBz~-RJalJ<#B|Vd%KfG(Cv* zGiUTT&^3*Qt{>Pov;d_W1T?WCN~6&}bj_fMDUCr5O~?KyqBOD+8U*g~ufnB`dcR+9 zP}gw~z4Db29LHsYMyH#m)CD1V4v}BXPyLz`@zui%&L$VCULO)t2U| zW%<0Po{ZPa^MVzwshAHlh1+&j2*g!8AKtj}109mX?AU41+T%4K6&NE6Ko2tKHeUymzwse>Q40U8kT@ zGI(AX=(;1~D2(9t`lGzf*d3d7T?gPgdb*h3cAJuYKuWf1^^=%G3h+i_B$XrOUcUz^ zN5j*Px~9b;JTHvWu(=J-v2|raY0@?WUFSl;_oK*wuuRPk4W|zGtFsf+%|%&G#XSkO z=-`4^mj>Q{{1HN$E*>YQ`Q>QPgVP(WNC?|BY#w{2kwl?0(j6xsb61=>+P1^R6F<0l z>8@EE%v&utO2ypIN%__zQM575)Xfz9b>%X?qv1J(y6aWoC8pS zG~)>y0F(6>cq)_eUOI)0xlJRG%#gp*|ekB>70zDQjI+L_tk$Y!2w9HvPRX0s+ zQNZb&0(2;ZX$di;;8x$E!v1WVWOrZ1tw*RUzQ$xWkw6QwpazQ4+sP}`<+J(*X2#{1`IC*&UTLUPC& z*hF2G0VlNr)ETZS7T!pqXoTJ#GoFXSQ3XMFPm$8UfYHZdI8!bVIkSkXTcZ(;m^~HU z;Z~jlMX|VzFp6CAj za&||W(Dn5*r#3ezUEer^a~S1Yc~6XO79bxlL0%vHvyKp zWfPf;A}OSSi?|D#_~EqbqTC1L$J?%oeW+|o1zp50C`g7?;Ue3_8kE}7S%5HWBcz6-2{$WEHc_kg|Vp;WrdNZwakdMS}Za;N{d2Erpk;X zO|{LKwqI3C!HQEG{;|gOn-wpjh2s}RY_HhKaO65wl#9he7V+G5N5j-wu~NxRCj6Sj zFin9@XFYGVbX!qDeIX-B6}qu*MvPt15g^;5avmStH_0yya zW*BZUdyrexd-BOZ{(>%5KUPIJCwH)}Ksx=X1MtlKMfm*f!2o~J@0>XcWJNcnI&>OF zVGu^p0iDHz@uieKl22-#$$0nPlK_0f0QD(61{dkAx19|>D}MsC$GyVilgc{*u~1RF}rY$iKI!m9E;{?35h8sH1=Q)lvTv8 zi&o~%XGLgl#W&L|(!|pgreuYe^<76(?N%#oHtM@&q*Pj4Y1HddByp|o{sd!j^cmtj zG0{@blN{iN9@v~wrp!)fipp&?lr#;!(`IZi9Qr0>Txc4XLNLaUzpLBnxQeo1tyVWF zot)e|*-yWHx!ZFs-!~MaRQ7FMGn3J1qnV^MjOz8^hZze!PjLz(?f3h2%QEv;yOCJB z@5i08Jh^`&8-Q~ewJ2NO+M4u3ODWqiEYEYoRxA9pY3WRl83Vdpv>ZkVp@d#LBF8u) z9%&I$sjSNVwzTZR&*oVIeUM|uO!M!$`{42*jNWZ8H|w$l@3(et&TUlRBp#G~1 z>}!+RJY9U;AJj`DWiBr$BB&`VyENovyfsa#$zmTQf&iBx5>LxlSdnE1=*VwrLTHBB zc5NvgrD>)vm1QYum`YQMac-S>nWJ$SmMNdxS?TzJAGB}iWXr&E_Nk78mghF=u7{?m zwUEzY+lf}EXblX*dpxl%ecsJ5?3PT1f(wbh6WT69Cy)$a0<>Q5bYfG}z&OA;lxA3( z4j4uf>IQW<8~yTW_(8iB_-G&>;o=XWmOc_&wq->jY9{4*Xj&=4fKt=e^&4Rt^$))M zl8ya--43 zc)R5TFci*2w$SyavLpiUT4g-J5#q+Bs-R9!=}@_-rfnRXGmB~xmn@IKl0{;D`WGWM z+O=AH9Mq6Te}z1%WInKU&DM3CU`m6M6iU;%sR>F_-&>7LopV!nq!60Ug>p3Gt+1@9 zjN7qg;Xa!CriqaGQDX$!i;d{nI6>bs;5D1_VJIJ3i&ooW@+AFCxo&>!xMJwXPO?R`?pF%@*K!XS=uE z_8jTLr-LW`)dtOLM|e&?gyd8%0lDwKHjc+GdU4^Bnbf}=mZEWnBX<;)Nm@?l?N9PQ zOG_KSsyeENi%rr*{hDE#kp0jwK@HQOjI(Fz`SP$6MH+s%gkB#s$1&DQBOwIbbd9H0XjhHE*t<|;hXQ$x=Kgye_^gZc96 zYF@O#j2GJ&jh2?PcC*6x4r$4*4xzS&m&o_pnQS5DQtPw&Su2*;0 zA>K%K$>Ze3`*b9Z6eQ0KRII}TW$IzavDh5kb8Q27=1!ry_<&G^l959$10x`gG>$Dh)M>r zH@Sz>-213~ES8>bBiYANW;$Uc*Pa#Q%*EVDyr$x0$$`~uUQEg?E#eX<7z+~;?>oTG zoor{_yg)^Lu6qIyJ5ugQxkLG8!{&?y9;FJP%|_i}JcvN4JP2E;kR2FAR|X{mxcm1g z6BsMd85!XCpV-NmhDm#zQaa?!qqL4AEP=Q!w}S0)e%I+TfRETZ;zmVdApSkh_t-3% z7#}fqlG2dUkU|VE0u~A88m!}g(*bsp@jZr{fQta9q(Z{bfIdBqmSYQj;549f<1}zO z0N1%Xt$3fY{)nNc(N|<=lEMNIA@C`sOt(OlQqlAU*8t;36uP7o!h(myyHR6ccn_yT zU`YAH6hKV*nxHzMQ?bTr41hlTLq5;UUx+hE(MzbwDbxIYKY+ApIt zW-OyLp-^y>XuuIOI8bUpug&OWOGGltfN%YF_`_LGyY}DkulPJE$zwii;!Q^7sZD7G zbUlW;-tlJhDvBqGo;a5~OBRhzwH7YEp2%tZ#EJXP@9r!Wmb0>Q$rjCqutt&xQKyr{ zf?H?fZ% zecIM)_3>o?{u^%^Nc;RbOL$A2dW})vZ`I?-6wdkc26sE1wSKeVaR$bfR*ffJDt2}` zYj5{zbuN6TR%>n4DBs-?w7z-6JtxMSjk;!@*n5Z&LR> z3KE|)Rn8`N?6*qBlxPa(!@y=2Kmcs;$xim{x|DI$?(~)9JnMGjNJ<@4*O&QjGtCv% zrD;+|aktxNj54KqZEWFhRp_8XtTmfJy|MC^RxPzH&NZMvh*Bw^*0eO0hEkoRrfEtU zvT0bRou+AOo0ef%rk&JkiEUcOPbf`SX-(4{r8HHGmHve1bULp4v|Vp>CgCGfg*K*Lb?#4Sbt%NohZeQX%S|>$eV}FZawr$&Kd@c^7 zkkT-W;wTOyAdI58wCy+*%(gGswrw+!#xM0lAoTZ?VKgWM>Mf>e+NfSv$KRyXjI6+Q zJ&Vy^x7U9(u{EV?^`WL~wAJ9E?nOZ8cS2igdQHfBn<**#{pSO{ZnsCN?YWNU`L+w& z4ny0{6v%sqzd{f3d7?&>gM<(;2O4+WL{+Al*hE^5T%;9Fe$UPISJ!X;-lb)_d#kfxUr4CDRU{gy%%pqer7JiGDu% z%jj>T=p-frvIBC!P}T5et*ULhdRDKSZllf~u@WE@46>m@h^cB(uXe?UpR;yjBBZ(7 z&gS~srBSBFf=}V9UvFpJ8-kWj0ZwDHowr)prgweEUCTPYtGAb5X)RY4<%mgNdg!fzx~%Jv zW$9s_K^$jyze3=7>*fSJtGGmHTfj1Lc92?D_%DhYEb?rNlCf5j0vL>~%es!UEIr)K zzk|=dYSJ{&G}FmmTI5IObpUyOG?8`$H~%s9Az#n{hd#vVl#r%r2=M@6NQ*r9)QG?c zSnMGn`#2sIn8Z!#y#`Q9OAPseV}>MY8t3)Bko+Jm^4KRv1owf(#v$Yj8o(g-aXKYX z(UoZ~mm81x?*`byMs+I0g1LAN_NKTGK5GOby5 z+f89h7G)+`=1U$}RM(8E%&Aldk!ZNGXb>wV1n9_fhRs`x)l#zY3z({iuHRL6OGQEL zyS`rZyPJXoye(%*>0u8~Ae!0EyUq4Ec%PQNtJRw>#cHkCWL8>68F<8c(L=NEg6-T# zSX%Bj{bIRYcTuN0i5U9Yt$=lsWge0dDPe%T85D)c{^*|PZ8qLz>G5nm&N9VFgwAH^!NK`rG`7i& zo6kDyP7jWqS7v`Su3`X$^zbkV#)QK@0T^6Ljy)5wc1B9Weh};mML~oKfXmogKt+tm ziit|Cgk8?wmB0w8bEp&pjMx^&23MnQyR$e(@>$?SL8}lD+VWb_xiAt#gAx9+r#E zXQ*^fVAf?-*DevEr#t{zj#RVeBG0Sv6p|g_`q8Tur%8%s(T3nT7pWV&1+Y1cFdIm$r4lNaXq_AB z3$!D7XiuVqr-~xG**?tly6x8<;PDsSSbYDx-~HI#Kiod?5xZ~yc6joUNA5lePagj2 zuikwZ*!;CKyv;jGk&Ut_>erq0Qnr0bvu-A{@+Y4^pU;2q@cY02@P*6057}RQ@((}u z*n6IP_dRcXBUE?)+XD~4aTMXc4IaiP;WX|!K@qjlAx6p2zeWE!iu&MVMcP>>>#ps1 zE}HJQ>w4zwO|_mWrc@)ITtL~YekH2rdS|oYdPS*%v+nYU%C?u?cDL=F{UDy?qTRY( zce3u+L-wEVv|cQ`wrZ=kYa{sLZ`+y~0Gio!+qPEZWoBCwUh!q13Niji>9=yl>WV(p z?)v!^9BRJV2bP;Mzwf^Mvh1o4zc0VM%r^Qb>zub%M5!eoWW95{i{N#S{8xbc?^n(P49fhzJ4M+7ct`MzX8`=yM=XF3 z0Pe*WyazXC*35HZ)=Dc4%sgk-%wr2*elPoC_ss?N*Q>GsEWY{t{8K;mO&1sM`>C&g z{imuYpM2|E-x|U?!7BM$$n-lvYzT?BF14-BdT$-4Jr6tU-ykkgipU_utoP17tI;byKNNnw_r>t7%(4FtQWB2`sE zV?g%&6Mf5JUn@+#aLY{QI3Q$)NS3L%Gbub2Vf2W%Mh0|`oz0h16Fi`?l4>V%S6XV$ z&uKSg(~V}o_f!?_RJ~ZPzoDzex9k_obu}xOJziE3H)!;`?Q|OxcVNA>PhqiK^t-Lz zK`8L2%f+%S99vA=mllw^ENHm$R_%h*XVWjVn1Hi)wn>?3wpikmpcPo_9Wb*7k-*Q5 zB803liL+E|h~qd$=YuEU5E%R#nV>QvI;5#q#@JXhI3K$SVPY!!5rG%Y=mS;CzKO-? zMFAu7xs(RYNSXn%G>s84%|eh+wS}pt5EBCHpcELJV3IO{=C;Ye+M6zLu ztnBHh;KSglpr7UI{LNMCy!Cqfz-smQdj0r%{pQu`?eIQ#f98MQeLno~-RHv(|Ih!a zcTrwecLfx=XrLL)dw%p7^E~~3JBs>t-nF$fJ>%(Q2WzDvlCmc2E#OMBaIsW^`(>~1 zWq5MPVf|8M<@&c_*OJ+4tscy~#d7c5-^^Md``vgGzHgxZA-oMF- z-KU$_9l&0<`i^;3+$?W&2bb^_!5ZA(BQPe6@44r>_uO-k(ne>}*dp|CFi!Ifmu*6! zLt;TBW9S3FzkNhEFPpRBPyjIw=l3|<arbN0q z{}n{p_|{$z;=G2!WBLRb6NbZJ41COoe8?~EFJqe3Q{oUpKtzmvh~p3M9S6U82q8S1 zl-7#Q0zf!eEf1>mvUV9j3c(Kt-E+q*d3w-{Y23OyiFjFr*QVYB)VnOpZB=dZCSt@Y zjrLOadeg4j>r!<)5N8zQeIwuiO^)x{F*aG{j@C_Z-rLc!bhG*nxH=*8c;if5!A>F7 zs8U#OXWw!Whq|#G$2grnLi20ZIjw`YufBHe?jNR2(=_{84oQ;M1le#f98mmHWSV3Z z;>)t^@USSV>gcd2_G0JYmGCf#*pj|(&ti8COgm(;JYz9%DTAazjF|9=*RH{p{roDf zWcSdN$~*Ho{)+c?fM*C0Kii(TXFBlbCQjPMS*?SI(ewVTe!ODOuZtiT^x zU{1y6smS<#^8UB&+EQ7bcUAp1Fb|mEiz0Y9`U?AnVgK|~@R9IT6!*yFo8ETZQfBkx zDYS%>+J)E@wUq9w@akxOGJWWwhc_G0OPAs3ps4}UbZ~rrcJjQJJlgL<(0Pv>9ZjkV z;N%Fc%RhY|;QajgSL0EZc^5)e8UrD&KIw4Yy<{vlGbTdi6HkmsY(;o*36b~<-tLjrUB5n?A&f@nXBQJ6%IcGsD2)I= z96m9+irx}^V)T2_zrz^b5JiIwYIuMj(y2u>dwK!W*?e{!ZS!u^mGWzH_|Ct8d5cR& zDvyn)H`~o}yEURMT+ej9iFnhnr)@+bA-_)c)0-g6tBie;QBOQ`079BuFH#Zj^r&ps z&MQu@SN-NTuYfY$-yv-^UV`hyUg_3#tC|yG>bSus@Q_2hgSB6qV|{=IWcl{gh{kL> zn*!zKED%06nUnl(c^_Zv#M* z7)E3vw5}-m=S$A=ymUfT7G+yV<7cu)0buR|ArbQo^4gG_(o}H>W`^Qih2nfwHU)SX zu!xi~&qWN01<-`dB?EkFSvkNMRlzI3@dRgVWzb#>BBD0N4il}8XLAy>5itTx0g>Qs ztdN)|SqDZUxg!K^6&>Wk9B7~<8Q-)23IjOk2q+S;e*hCmaw-6L4nQM3st3ctL;%ha zQXJ-4mLA}V%#o?)qR2DVYkR?=+Mt6lHcO+0G-)g;D zueN_2zW@-Q%gFtG^$IZvvC`Y@>Z-2p>rY|3SZ-JKVqKcdns(C*SZGLsFJb7S_by-_ zW03CAP-BZ&^|dsSU)AV_OXh1Jq%lCgt3cK56m$bTVdWn#SHL(vw`HEJ^c1 z2+pNYu9qq}R{%n#?Vq$QMsJi1cj!8h*5X)GoIf7uiUC;=E-B&PD}?VVKk#!lAtUb(Wfl$gc2$QHT0orj4>>3dX?QcL^8cMW~v>QUuGZmtA zTsI}-=TXCHFsAXAc-Zoof!*N&yP+FlSX|8!B5rV@G#wdo#FxErkNwwGMH8w>D}}8O zzns_4Joq5Q@qfJLfj|4R2Yl%9ejeX#r~g-L*ipt+sj_)UQCm2}8;`%(@F_@bG;m%0 z(@EBkfB*8y7vJ`?pFDl}=$=>NyW#nb<1c<;Fs^I(9`1hn={LRU`0Fow*&E-;uiy*% zi@1iLCk@giL$XXZG?~!NK}`dy7o%02&39>>&0PkE(UChM7!0;|t_x-8TG#Dt4+c0| zU0peR(MOBv>(%SFKS#E&d)^=X!RqRrKjzT`7cf#I7uX~@h&UT&bs4EiI?u&&5)dmi(V= znXzT|aDCWJ>BqAbP;xL(io$EM7XI9*?^uScrCkQvY!wkRO(uGCpY#4G=5W(` zCbZ-K@@us)1Y4%{{${5ObUT}kAb=k-kL=(O3k{`CE!rLP7N21Q*XPeW;@iUeBcR<8%84v525Qxy8J8! zyEjC)v$@gj;D>^+-VA(dWe55JzNu-JrE8jITbkBB4--|MT^Kdnp@zaCaOJvZbYL5DEkJ z=kxK?B+oZD^E^ra_GtQOLke66A?hz!W7G{B^}zf7KUf3RG|$bwDDk?6!6d!>{m=XM zyD(={!iaV4H}EfbmTZysE@^y+hQ5B3_+~985yNzl9LWv)gz(U)Yd8Skma+XP9NiM&;LOoFApY&QNuLjt&$opk1hvgg_p z@2;`fge*-MX(n(4VB#v*iKq!HQ)NgEl4bO`{ZjyWfkaap(zG)i^rM!cMbt_Q>L{D@KCb;GE0{Ld6&qv;1a6%@>}lcatUN@^PCl$la0${>|yS;~{b zbM0lzu^Ee`CIQZ*z$2y#ap%c1W|^^W81K{o#x(`kvO#^Ws&2`97JJ%D6$q3_KXr zGdIB_buuI82^qmw1hPkzJ~|F%%${s8Z;#GjM=s>8@s41(b+IkDdRz+ecs3Yh*>zi6 zy&jMawzf{6-P#)Tfo^YW>-zh)wm`Rc?z-&+lR2T|$6x*G$EBL8wEZj_3|@PP%R$3dwbj0L8Sv&PWuaSa7nru@GqS;GoA=& zt`}$XK~W`TQqBCwX4~kP@d2v1yYPFuT~sUjo9yf?aDexpBa`lI-?dj1VT1=&6eaED z!Jye{Ges$L?De(FN1e^_`omfngsnlVH%JmqipbDSBYA_|-MtgfqSvpsuFHZC>2GbF z%kdN4;hj6%6t#M-E|#0NgFNpH64Z@}w~vM>T74V!{k_%HG>QUAKh`vlQJr%k^zBCT zuALLtfHatiJNOsqO(xL!(OH#daT&#tWFV$Cl?xTVl{6Y2o>OGyq$=kbGbRi@kFsB< zj+z}1sp6YAY*oEOYdO8Ol?`ynyX3Cz9crH9Y1%ZhY;7eqlx;gn7;5()vM4I@T@Sd( zuWK<8cUys(7AJ<9ysbCP+ASS-Z*NoiRy+&he0UO~?*99IxB9&|Dp|MPVW4(rT5nKi zgEMX#?JcN!Elue!gdIzlDWGp!z_MYEHlm1g(CzKJTlEw7k2H89xPI=)KS>qvw3YL6 zp2{pssIqyLR`aaNn8tDn!Ldc<)4TMxzU8F9p|v9q4)#vn1{y_qzJ1RQt7N`^dNo}- z-MSR5uAIGldz)&HceikNf6&9TWz4=Dn+{x2~*^G64vCpR}`7pX3!! z=G4@;i-Xeh)}-{Lzb&Ab)3*Y(NI^eDNQm{#XrCtNu+PP@)8)#CGVHY+5&P>$Klx=c!au0)}CEs@XcIQPV8j`+L!) zHM+6Bx#M}ho3DneA-R`i?FzZha%G7!2kj(sM{ZF*Oyj!xETjgLfhtu|E_|!ii&PY+ zsXJ-4ObgxK>F!`)oMf{w3@9wiwgsj#oKB*QdDZ*=5Ns~_ zKId4QO!DH_<`1~(!Z#+6+ZLY8(j={BgpwvNb`fVvX89&m1cgfy669NyEXMOlmI5)? z@?{*&f8$MNbDtACH!+B%%I0Nwwus09J9Y2Kl+)z|k?Xq2Ty*e;q=2CVQrKXPnSyCM zWymxQTr*T(t5C=}0hgwZj^Dhx;!-e8hY9Gjb%!fOnFa$~2qifB-%ltZLDiMIE9YQT zz2Jln=%BW>bD)_1h^>+}1-!Xx&)N8+256O3l)bs@6Emx_*=dO2tk-H$Oisn)-@=tlv zWFO@`Q&}ukzK1L?7Q2|wsw9oktzI{0v(V=&#!aT{R0ygYlnZJYRB&qORC1~tR0GvC z?yQ!IefQnCRO?}l*UFe=RMU7+?%o_jsYC%PQ3|mFPU&cW8%lrBFw1i$jP-|1^Nus7 zdF!@e+-K>4)=cFMhPisPX`a8qG;h1!G5LoT2>&kfyL zx63x_LA}#uQrXYT6GIMSZk8F7-l$;)0h#>a0tN1ZoAsV?%UO_%gz6W&|iIvl2EuE`jL?la0p#phnXUr*t(rB)tlz%ZI z@3|6Jyvr*!8L(Ua${7eG&EhrM3Wg$C@ijRLTPKaJTJwiuji1UNhXBk z-7hidc$9iR;J-vA$xt_J$L)p=K($obCg-+t8AH#|!E7hi8_~L9&{PwI%6jGqg*#|YHXHs}f3>;r_O1bc%X_SI#@)8Am(xa)# z*`}UHspruTSV`h@-tBfx394znqXizG#DYQz3}e@!aN_Y~vf6Klfi1y>QE#2Nsc%}o zk}{M=$vRrMIILp4J9{@`7zX7`j5G?!F8Q-UnN2Q2$(z2d#qlJB4MV4lb1LLz#yNXs zf-t{gJ?Gk0{3x!F1M)oGe*{CE2JmT)mL-bGa@Iy+%V~Z(}oJbhX=+3b<(#uIGB# z*uXix3C5hb+Y2o#>ULw7GQb|UnwIGZ&V^%I&DL1gceb`yR$Z4drHzEp^`TNWz-1EG zmWzVD?qzjNH;nfFe%ml~t^TrZt*Y1E1kEUFB5~{WD(w-%3AuKap2HQ=A!o^@cy@B0 z#RnwnaXEMEj>XMkYMx7KJM%aMrL&i-%z_8#y;Bk=CR4gg-(t#aPS2spPoKK&(y7yV z0ea^2lg}OYmtJyaIjtS9vy&$udT@7#(zBdtx&a!;aYSjHBnkcXt-%0)>6l&53xbwq zp{Ys9Yt28a)>a!0pwU=et8SRh?UQ>#zv*SoY`E-fWz!`@WQM9re27==& zez&)jR{-a6!Ue?*N=lXxLO3DU#!|hLMGG%$4Xh$Q4d$LODx{lklbO>%T zCJe)HEzY{Yir`$QtWmS{n&-ka{Uoy7)Yp{K!azwFLKqK6C*a2jI#i1K)3!3KA&{f- z8TQ8*;QAZba5;yjfphnTF5?(8vsh;8Ikp(Z4%Ax=-R>C%21*YKr2l2nn=wVhpxkww zb({kRgTBjT@7fVv#}S#6d&y_}DtNx&lDKEJytr!+|2-Z~(8VQmgjC_^1%&~|SF%f4 z!y-reiJ{IV3Ah-#S;?DPXwYL7L?(BKo_JOwkddMICJPA0#ppDghX3CPitn1loghTF zv$4_X0`+=P8#BJO^T4z;?VE0TxE6+cyYG%wKHNKT-F0U;{TS@!_4V%V>FP1c#8B}- zQq|F1EQQa1r?i*I1 z0IperE8RpOWSL~V{&|=7H}CXJc~2U(KRl8jd2!eYGezcYn%7 zG+lSAP|OA(RMe=q9Zj>Nux)Z00)S$S3&o@rNKirqY2SU~&&jRiQSw85R{UT>CySba zPnN%CSO3kvN~$9L`NzoyMWymT&U{v-#lna6=tGt#F<6BGVMKfdAgY9 zS+_Hp?%j7{m$BUw_q~54{Iqm#-1FjUnATguwjGN%8|iS7cqeakgL;lOBW4Ntx!uIe_r9s|D}WU)$OFwg0}pzPYxxdf)o`fB&_e zonIq7)yA%~7ySuxmb{QWOUvIndS$mZM#0+l*7jO3+_XJwH`&$rrAu7fO?GX!>n{AVS#I~If|jalgX7N+w7O~q zE85@>ub}V_)d~h${rC@qwe8K}pAR>;*MhCC*u&v3(t$slxW1j)dOv8Xdb!i(dZ{RT z$x7@5qmAvgwe8Je@bM!EZw`aC?bif}Lqa9cb0+^o|4N27nUR~w+sVhs50fMEAB2QP z7AcYsN)d`_gD@_?m}d&?lvBJ@q*}11LT^##AxTqzt|_TZf)i`aIxc_+b*};%LSVsY z=nri?;z?`Ye3BP&pQ(BxT|PGUhwmwzRhXgQrUq&}iI}{AyV}6TXQrvbJjl0#;$l4k z&r5Da%i!T7^zWur2tlI5zVPKF3!$WGHC)@VT;I1-p*M8qnl0NHf*mjn4ZCH!OmFBS zwSC{U9NX;)&{jKRY-wes;e&q5WZysrseyV z37D4e=!T&?j&2ybV``eFnWm;`n)!0iQI6e8 z{?5*&dg^N$V~jDTX?|M2w6o(kKfq+m5Oq879n*>u3AP;OgFzlHGsq;eOvexGx-eRj z=?MLPr$Yc`K$^e7MNAusXK|E2;W75H8rrAHzQ1GNJvw;pm18@dX`C zqQV(!2AsxmS7i3!ZX8n{m=tOj-=QzS=kStMXjNCjEfa2zgoe4zoZ>D87 zoM|StW!8HjZ8BPY0g-hqzTP9w=Bm&M~EbiV+shzL2Z9O>N0-`bj26nZnVE&8vsb+ zBo(4d9mldb&yXf*%2-ar&@^M_xD*?3-B7AHSzo`vFRri0iINCh@5Yn!d9S}(6tx-- z%4wDbL9^d)cf#<*>T0{?*a(8Da9ldp^=f1D`s=o~IRhBm-oEa-@kXWVgtIw>~wnl7J?wlrsV-@^}JZ^_vZ7H@cnfVuvb)*v1V&HY__oOdxQwmzIKF9;|QO@ zujB71p$$UPd=ADRQs@La5_&n{uD4TVBApFeHb86Ri4=)vOIP+xz+1<8@5fP^5Z35K z>Rt$k3jwjEy}8xgHX$NAZ8p&W#op;Wo7p64)x8CXN|L{E;`7TQ2U5tU%N!v!Ua3r& z$u}`Cib9(0tm94{>i;8SU@}3sNt0kOpzSC7DD}jvmam;sFh>JB;74-4R~)D#lNT51 zr$a^JeI-@QpsS9vvf|P?YHD(U+KrbcZq&T}G>YZaeTh^^0lR+M=z`3_s7oWtW|P2IprtsGCYAh>r0!D^Uy+<9mT#*34PHW&f>ROFwkBhg zO6=x6^o4kNoN13sZd@Fz`5P9B-k-8Sbc5U0%4_h$B>AH=4y_j_xO+khXm z&|ZxqArME6x_iKsrczvm*{N*{=~{v_YDkrs8r57+Xv)SGXzN58BUDO|)*qH>u4I%c zjY-Y}i!mjc=0%!*3KGIr(mX-AUuQa(OgBu8JF&~S>vL`yjG1;Az$5Ub2#oU|(hUY^ z;AOK4K`G@**EJ>#X-OfN0_D8k>7#^%Q+aIF6{nN6X zGzSMqhwVVatSk_dXkKm3r+)?B3qKlt4JddFz7PKoy@!;IB zF)A;1-NU+0dg}x`>sbU|)32aC#CsaMeo}?tbpP3ZI-AetDYO+-ksZqkS1J_$x^``z z<}OM#&oXwr)%iB8c~m=McX#3rH0^SgtEx1|yZT}_wRvLiW?;r%<4L!Y4BjZXS);0l zdb?VyV&#Ofm-~8MVJ}^5dVyWh5;{QZ=c=|cVmYT>EEnsh-{30aCkI(0iHhfOJPT>W4@pp|n# zSB56n#%7DK|4SQ#pJ?=!BDA6)+h{0VX}!6@9Q#4fwdSL zH5bYH@MN!9$NAtuMM*R(5Rs}HCw3+(g`oB6(V);YtIZ>;LqwT_yp5~9?kWUQh(5f3 zH9#cFWA6?BTyJAVN-3&e<|5>&HC7zjcVXH&I!L?R`tK*5Rwm1w3o#Cis7|v_v$A3j zkvJn^u2CY9Vv|+?2#?2o3Q;u_N|ylVf0)VUK(&UrMLI&@1|k{SUt0^HnROhDkk~_0 zVf%?=<3b#>rUUkrNbMN{NV3BzSArqVvn)xR(i_TID@2-EtPP$y@vM*6CyFyap->Ie zl-+<>8&xZ7Y|&_Hve4YZY1X2R&XY-EIXD2pIAh{Wc?h&cjhle#W200^0x4H{tp=i) zZ;EOL3dVL6ZN)l~_7SiS@RO%Md(dpX7yzY=K~!Y45Q1~+7&~!_;Hf^I7o_el*8Vme>>r2OS`yb7iR&bBM)U=W7V> z@2zG{tW=vr{YXX9Yyi-OYxl>Awb4}!6k?jpaD;&NU__;cEfL_bnGadJP^2dg5m7sF zpcGr0ja=S}<2Z5N#ig*G9h7;SWf59ixH+1BShUO|T^1?IEUju%h~xdX8rPGMI)F5(S}z!c<)4c?uRcNy^_gPTQfnVUokn&L=eyhf=MztbR5)AYML< zMS~BMku~1g6oN7+kBw9Ew=sf7Vxv@A8Vi~!!#ry&lLW?C3=A0nSVjkkkR+^|stg=V z8&O4qXN)`zc&O$63Nfbz7z#viA3DMAUhwM}!H-4clRbQPSM7`&3~80ynvBM;Kou3( zfv5|2OJn^(>f7{pGz!)HJHjf?EnTiv(e(lJ%QuVS8O!Tu7w4DP&W}$XxGsz^8c!yg zvur!937foX>pU%%SP^-7a8MOZ#e9C%T^PyJx>SrfEmDPL)vgW>>d`q8g@kZmOmm?1 z@a*}w9(dr!t;2?BZ+QMU#ux+$0X}IzNgx$%S4a>D7G*N$my?rG=9LOA5o+F7!D;Wv zgk0;xwo=SVS`Rb8{m0o)3o4)PZuI+Z+Re-H`Y}a<}{8|9z>b*4p zF&Nps;L(HkKB)DB_dfX1;jz8F$HGTnHz1DV__6TO?_SN1kLRm%{%n75uY{?$x3@oo zufL=Ebw8p9?|tyi|KGcsA0Pkj&-VAC8jl9Q3h#v<8{-H+GkR(C>gX-;aX-6!N%T$8 z_eZ}T{o%M;OSzrZvsQJ#G25=iR3_4`x9fUVU%?aAy#$jamX!mzP;RfjvF#OIKug;P zr3#MeSvq)@{eblf^meo__SB0Tf2%#P-Gmc8YO{Uda#3IP3tx5eQHz(q{GHEv+5X2@ zU-;eccu~N%#n7bL#>NJd1SJ0IxW43rCAM#^1GNqKI{o51n>GI;N3Az^etEs`buZt1 zT&Fqwm{Plpiyz_L z|G<_u#n8dQcKg8nwVN0rJm>1A*MIq!u4b2mU;gF6iw8HJRS#GXDq9rZbDStjWBv>s z5=BYS?h#kDTDO(Wzb}8m=fY>)S+5T!0F#6D`pz@f>&XPZb^S}fyn5;S;42`{CojGK z^bGF40&m@V>~MVJmw)L_xxV@0$8O!iU#tg;2emKrD2gH#1q>~PMhTFG}Hk$3O+jNdw|!MF zInkn42#$47vaoX~Nj7Pyi(Dp!-sjR(`(`M6fB<%n95hfAgS;%jhjQY4qDh6r(OBK1 zK1hmd*@)_+!&;59Yq@+N-J?h2!=RK(ou8E6V_9ayqJRdR+OurZWRs!YD{a9el}}8b z*KxW@;%>u#-%9JCwfEsgnKv}TV1plmHg11}07WhcP{z#p_&;R9s1d}*`=K^|G(vzc}gk-;3`St=T^rcRP0|T zRpf>M_wc*CXvAgTSE+p1C{htT{qt}ao{CPQmqbrS-xEcBW8JD-kqAWCBnTn*G@*4p z?dBcZ0_FKZ*=J$N5RCS*_RfNmMi8JPr5wfh?Yx^8a4{yfg7JX8Y6O_3$CzH(&cxt! z?1vl$pLN}zV-1Xz9kq#b5x_(Fm(FwN{-Ehh?4@ z@R0YhX01a=Qe&aHe)T*6xHg;Sb=$H&LewSgZ4~Yn4*=rjY?jqct<)i+qR}WxPej5@*VsOvCe}ti3QxZqeh9uk za?vQdh_=z4=<(>=qaTm{5FUgNj-r!YPNXeu)5_u|2^&Tj1Fc!LbsCXMd9$tA?z(oD zoL1-#aJ{UcQ`cm1Gv#fLa^_UYeqdOIs#$JUv)l>wR(}Pe5POL`C1=-FySteo1m^JWUk|xEP1*HKy^q!2PnYa1Ku8>!{{4c|YLBynIfiYAUE9%A zH}!qIOax4D7PZ^StlN{+(5Y=jv)dknW^oFoLdyoDPCIdQ!7a<`3K1uX*X>K@nr%mc zX#$`Q%KV5h3|;CvPuadzC@zD@IV@*zJxWa@rbwjH!MUc3ZKA)}bs;oB`pFV5+`1En z*(Ic+>gyoq8fyE8s+bolKIc{Q$_VZ`6{qpn`u%>BwfVkAqdz!(3E4f- zXq?#n$J71(>B09y_2z)%N2BOezklk#iFfbbvwI;7FYMm4d$&GS;9NVx%W6R!_+&(? z7;~K3nZ3A3oXdS}l-9m3^7fbbZI-Op)XvYigKF=HekK-S`Fqu+1om{wSGNs2~#wf3nAj5f&5Xw`e zd@gq}mGykr-Unl~M3l6x$Xkl(+DueM><*=%Njb*=h8c!o=s28f%J-sX7~&lWqrt`? z46(j;rodl$mY4Qk_`*wzIi*|McmCezBF|SE=Z+JGA)vU_Fl>tTrJ~(PvcdA1wKXRF zHr`)dQ%n2%xwgH1`(4{R{P%ost^FnZE1o4eIY)?)!$!{H#aChp=8if-G;~5*1mr%7 z>4M;#2!XBTY4Cy5mp%anJ$xM~ZMC1XcY>nPSXx?}Wx#WgXM=pn_c;Uly^ZnV{b^&N zCLgv8`(Tt8OBWV{ejIy24Ycy~C)INuSe9+GZWwre3ed%_t#|Yrl;EADP0DOj4XLQC z#c+uiM=mBUd!pCW7`UjWRe%(fKO=1G^gb4a$n73C`tw;eVcf7h=|0=Vqq>viVEAT< z3z<%S+HnH^<8BnWuKN);iriPV+nuGN`|*DX{EvHK?eNvKL za)w;8xdwlXD!gB_m@lf74q`S9uIEL1PedcP0^U-T&To^M)~6}D@-rs=hJb^|GNEhSEH1VJZV34-<+&+Gl=z_Ls$ z%S_8MvqV!$rHQ7Lmf%@Udx07KQDnYQ(_VO?m-bs88r>8W{cIzFsXtj ztFyBtcQHwKcC4shp!-`OexQ>ky4!5{UbEq8N!rQlHBhGOp%VwKW^|%a-;0{fFcs?+ zLu<97ampBG`VtCbFf>kSrs22M8rD^>r`EBi+QTG-&Oy9B@Y^evMX6=2wEX~a(l<@3 z-k9aPYqkx{=YVamomwi>nx<=`G{MV1pHcK*@rpiZ_9L-lYZ_Ybd|MajzU`eYV4xdZ z5W)!|XXqgw;8s*c&x&3feMJ;ORWF-t1hg2hKFzLQEiEa>rS*~(Z@lr3-g>o8HYw&k zcB-w$;5lvIcDZf)h#GJ`X^l<7@mi&oNI;};AaZX<6XQ;1+-j-U_zPYl7LQeeh!O!Jk%Hga1ouV~5Sj zpe(6rs3_a68Cs*&|3YgjjgAOVF$Yp#u9UUSm)Tc*Ja`FlV;mxhGff=C!^gi3z_arM z>xB@VX?!pq;B8TaQS|h$fu8!oZ#*iHvjskA#ROhAvTY#Iz2g1Cr0X`Du1he)DjXi1 zpC23s6$iu;=KlSv!elM0SYbG=<5J`_CKX2!=Aa_~lE+TZr`R*aI8Iv1gY7YyTzS{N z2XEU6+ueGz-SnGo)8RC2wY#FwWiz{OJMGK`J)YGRKbU%r4#Qus)~l*f5I2=+k^JLO>heshC9nEjaM2MunQD-^i053)sww4}-t!J3$TIU(2 z051m6d8QFk$}s`KjQfy z_weT0t~kFJcJEZN5!yD6fA7^7FTVQsE?)dS_JXILB1Fjg2oZB3L$XYW$ck8HepROP zs`TMk^RTQkgw?!=Rmoth?t0dRuVQ@S;TPQX=+X;cd-o&9XRiO+owJ*de`R*-EjL`h zgvIT*{>3}5zy9E(5A5TwI-h^({_zw)bX?B=?fAt0CZRHv zh+#U4qk@A@d$p+2S+&qD=v0fUToidR0b8Vec5QLN2cGR{uq<;yX*FWbWmnQYPzZd?a~#0XHR-qrzf;pSKsOAXhOjN|f11r! zv#C?%?KQ!e#ta>7b!AZvnQcc(&%!8AZa4Mhc)dKfTgca*We z?Kn1bXb@=E;jvma)+N{=X`(9_z@AGDonBW7%}(7?bnOP1hMlIuNAXlnk=ru5P&qEC z{sQ)FG{qid{tfZsEY5}<;$XPz>WTQWyDv&5Q@rN*%bhR+qOgNmJBpC4uW#1!d^}4M zh&Vo3U2iM(V-LLOMdb@$c#mNOc`+O!3OmO?VLPiurxTuAnz|mv6+~g@0e3cxI$MrAOsm6mx!bums{Z8EK*G!_Z@v;{i`_)<^}v}RY+Nm0o{u#}<7 z=hHYztLbE+B9*JbL=@VY7jh!gD9xs!L%(8?7D+it(j;@Em5>*zTI@qrF8CmnIU2!- z_LTGDU=@-sO&2LQe0pwzk(8-;S|+6`l>}hSB$+2^QsiensVbFLMU(bjpi4WPt4b}Zx%GM4KITQ4Rz+H=xSXbSwjweQYiXe{ ze@>)HH6*>S%8PtbcgMbYp^|E@@@hP(ETt(+q99TBaMLSDRNGz7$rYu-Yv`49Th%0j zxk+-!FaWc5udcg-E30s-{^aoWgNX|s91YuBBy zz`xX4BC#rn94KvlnFPQljuYminF)jg?Y-2o(%Z{`O=+%>RZTI$`&1oGasZ<-^JS`t z#$b|!DnRe9Kk(|;-nm2i!N8b^Q9r6!6i}1I`0juzY%q`p1U=56D7;T0RV_*p9T0-4 zP65QR3UN-K|4%@Jv39CV5|+#$q->0dHIU@Mvo-t-!ChG;b?TIjMa*abtVJ=8!5Hm5P_%$AoT~_d(VTpwH5g4!r>^g=h|~8%rul z>|h)MAseLq{cT=c+4=iXpz$VQ$E4!3q{y=Y>?bh-6G@z^s+MQlLFkD6?q5^8s^;1Pmhunk{M2y2uZ$K%QU_>wZTLJ)HEbQ;R5G4hP`ND{GUz8h1iEgHTJcbhv-7K4c=Mju zYDGy3c`p1ZO^gJJHC8ecnvk&(XEP5(sl$ww63g;JnvI=u!FnA#pxA3RWv~1Mm8YN` zoWu#EARru%Me=Rzgb<6y3M*3qxXusAYeg}qlGvox{v=5T4aA^I)Wt#J5DP>gk^~b} z=8_~9#$Yq?IAt_@WDU~n>-ovuA5E2l;w%T$)|LY!l4fb_^1N+y;t6X&t+Ru%^I1~Y z`}<-LFji!}t=R?Hc&ffNA5NSmfQAP z)^x!$0OU|C8c2Z$5VY|xb$~z#8v;^}imD1QvIsiO98`6a4uG=#!QuWsYK>W0#iWqh zGzWmgm&cw{4hliV6o8P#8hBql7-WeL$5vnIOoKu|09z|%%&>xtfyfal5g}L+E3IiQ ziitq`e2^OeR4fL_hhS@xCulpEOi%>2NFl_DRqBfpBgXcILu$Rz-28TK9ppJ!!^$?2 zumR*K$|89BDfl^fD%t?R2f)|Df5I2yH{$OT(Iq`jA4Bh?ucq&!@AFaw$XUIWwysjK zkLft`(Y~?kW$727)^e8~snRnjx1eN^ljEFqa~l@R&8&9HBC&Hwx!qqc){UMvz%wEE z2h@H=LXwaZb1?migtwjc$M)52*tAWb5qD=@-3#+>wVb*e<6rdls6939HeDnS{LiZA zAiqfDT!xux;jik&GW#|59HXat?<$&zQqyYoyMEP$m_&#ax-GQb^_#ZQ!M0av0-D`S zjRKZijZIy(;Y~u_u3a|k=W44fnf9CTMofuvt>5%SGOntY&)M3}rrmDcJaV<0LJGjP zK|7|g-7FW+>i6rNlwF55@npSN@@B>ZW{{@lzLArvURJaDY`R`-Y+yr+%%(G5vQ#XA zt(whcPnGos!a%s`7wgq_<3JGWuh{!>Whz`)qi)tsx2t8|^FM`EHQc?a(YEG(v0SYi zw{Wt!kyPNP=WSJUiG=-KTgm4_)?8;|pf;GxV%Zf!*j;k`PnJ)b&6Xq$vH?^&V35{_ zq><@z{vl~rb+v93(-Ac%v{=sO{o(`Hvf0jMK6}NHuIZPnWg{OxZ`o|M?`dhQrYwYV zx7gHGO+wMyVz~;FgJjHMwn7ARP-}lyzv*`}`&gQ;vKb*I0Ga22-sr1#ng7}3rr&H= zEwY_=i)F{P#lh636TTx>sMxk+`8zy3JijiMktH`)gVu)x0FvyK@O1&Krg8kK|U`K#`fr=u}F^)&E zqKYGh0PDJGG&-g~&_L9(f*p!1fp+vf zh6hbvXmAfAP=F{Ev(nD$Es_Z|IqOp6lUR+k2BiX;J9;>$q-H@W=>}+wJ_H3yxuEqS zK2V^A?He+{2_g>%hx5ZjgVuEOM-3vy)vR9B1|0gwaL^tdP0rk7gqVsV49Ef>5af8U z2gqWpzy<>%@?waCGg{_9Vj_47%3?4qNsMVD2z8!U zgQ}$JMjuQR_jLFtIy~4b009O`8z*B+3m+g!6kY>Xfz)&gK*l~H4q=f%So6BXrBOm3 zB1MHF0N=RJ!omO#J_LZe$lLvp(w_Y*Y%NuLGS`!|V4w3{a%N2oUz8_F06y!zA zdQr-J(fg5BKp;zK|3TK7xDW+8F$O;bl;*oges7LX>l6X2lR()dF%AGeAkfkj$H5w& z2<1i_AjMhA4TynzY%c-?ks}k{Fo7|nyZ@dD&n_#PqR@a+6s!oeZiuw@jx}BL`jH#V6q; zx{7W_9~OOS^sUkNM!yRCa0oki7#@cw;8Wn8@D1>N@MrMPa2MCum`U(O)i`E?QM~Q0 zJ3Ppuu>tmF%ytty&Gd6oW`9}r?R?e&O;VN08jz1RJXYaHn?m0D!)W_ z>s7s2?pju!%yO3vt;e&?d9z!Wfx)zO&~+Le7CGKVxI3BmO}im&65|ef8zwvj~ zW!F|U*Smf`+qF|^)`<*;Rz1tEKSri5xu4Ec?U)x4~m4hCJL-SSN3jzY)Jxq!BwwY7|{dE3phTvz^Tz1(qNSnsB@l4uV(hBbH7 zS>JB^*>>G6XKgL(KDk7u-4jVNcUPn=()F`emR)}bxq_ZUE*e{!e%?>zor~=H-Fn{5 z+cs*@EtY)n8rb!6sGH6Xpcc~MkxaL>W^b&weJ6y*-Ga7}MP17G)^;k3`dW;Jcfkz*?nM z3>~_fkO&)PLn6ZNEssI#bNR1Fg)znSAW5=plwLv-mv{enAJ_wN`&dusiJ6A4h{VV| z0a>&nRH!T5j{_x25g9@<3Z$K(rI!_GL?yt5JX9J)L0G{af$ENV4FFN4X!il`{%si- zc1Ili4dcB@{Wt`1K$M&z4vknsBxfwZkksRCa1Esa!3EUla5}i@x93UYTWx~&?JOff36T+oEkMl(A3yfLVtPHQP zulLtrORMMW7Zo7I2_jg7am+f7DVob09}}XZ=*sC!(%60*Hr>h56lgq%phdtvMGBQ{ z>5qa_Ua3!^P!e6&EqBZL6`*Wb|6`c*oS7L$cfY(ehgEpLOWXc`!2A8H#h&-@`1bH{ zdtZrA-g!TtBzAr{86VBxtKLs3`=HU^ABXbpmzT`x%)tAVA-?V|%T&SX^u6px-VEO;)FIMKy7QDq7)j0ZkR|F{=^%R7yWaJ#Z-=tH=iu;Quc|7X zmLj90p>;aWvOuIlEJfj*HEm<;FYJ%UO`4_+5$h1M&V<#$SZikI2rYxSqqfHRq71=% z>wHyJm3yD3BZwe+I(qMW-}~P8!q>g)UGI9=yPzuXIUrS~b&UvVLNqXz9NIS(7GlW@ z@2qJWV{h#%mDn_Ci8RN7b;epX15(V%Bqteb1dR2rD4zL3tgq^_a`5=m(chS3SvyAf zB)mO}qs!=4^jXnYMc)+t2}JO0_yqV-_(OOa4X*H&_*Q%e{vkgh@SA?;gqwMAlCBPV9j=PO(^H<@MXvkj0WkA?|EpPvNcS-5Bb0 z-AKJF9fzsl>6a3mBwqj&mwe?~)mywue zJr$M^Xb28%Hl1>_JL^7)-g4azH9(}1x>(lz;wG>xxAQCD)XR}+)=iq8C9Gux_?M+0 zotEXUU-fGWF{``2G95@(!DCqtL+03Y>s?I4gA4FPFgz2|AtIFnD zw^`~?^E-vF8@4@6bZWD9E;}+^o8@xrw%=?w^&HAXzj`y9&zAkJzYT?nm|oPCRJmN; zqThAX0)9rHJx%o89j&%6}^W}WD?3Ycu zl)hV7R z=@g&^7!qg=UOR^jtQ5pa>j83-GiJ@%*_}Jjdy_R*`^+1ofY77!VVEc^fkYc5p9)n( zIbGK11*k#-589<3K8*`jvDybVraIRV2DC_U>8?%~8MVfxU};T#J<(zr(D7sA5_lr- z&?cQhm%tA}BL~o<8v>M4!DWH(MFHy7fyiR4($2@Ep^W2&161H>WG(^tL>oacCdzTL z1vJJpI4i(-vFqOImQch%V2;67GA$8(LJ|_Cl5@(Un;zRB4$L8i?lkK?8PI@MD{VDw zxDA+5A#sZ2#;8-=675~86M+!{68FAr7NOKcW)dtZ8LIEDx z0v#KrI#8j98Ig)_9SVFBL2#05hG^XxG-?{OT0Nn&L~)#;oeT$BlSKr{ld-;UIGj=i z!Xt^>v#5+xx=r`3O(Q)~2=gp|kyWT5#avNb8by^>+Bl$Cpa}tyu?9(Knz}|nWB{4O zNmeDC%Lxbiu*P4@h!i-3qiNu0h8#4^Wz3w0$C~{+cZjWO`D+yi%MgVWL!rNms0QLR1OKaii z=($860S3r+h*aeBj|a^N=uk7@(b0O+k;NZ`}PR_>w4!_9A-vDZC9o7ul%Hr}Ccrkm#eLcSc_leP{Hi(LY505j<3I0c*Go zUku+5zlA-%3g3Vqh+mH17)5uW?fO|KvjgZa4QgdJZL7NK_%>|XU1h{Fth&v1F3Y*p zO}}jV&2|^z%3kGKN#{8bH1Ov&cKvof&tVV=TGm>T<_ZdVDyyR_vVQVv;V`e$+kV|v z%N?#icvPvwONaW|cD>p@13hp0a<^S>y2S-dXWZ7)b_M3|c+2gkTg*LDq z#m<^lV_4L8QZF!lwq38rP|f<;V#Z@ww1NHI-Ev*MS=;<=QrJ+YZxapRW;02?tY)k| zzlFLxu~5C_(t+coN(WAWoDkaUh9FSm0Q5OVCy5CsID#dIk*t;^T+sX7|(+y_RQzZCGrl7cgD&vR<@{H&H#t z%zd-K$=3DI_JH+Zqwvu17UO{ z47qdOztR9L1p!=|I`GbU_;oAA9b`uStGq~9@9iBQpK5)&w;#t?Fem31{ng^I^`2rt zw>vxn+#8O^ezdA*vjs1m0Fbk!t+rjZM8%BypC;U;GKR`=@ zU^(0xScQt+@W#!SwNY$`w*nhQ#G;FQm1cwl`O&m~f}%BX2T@72EIz-OP#85M&o3Po zs|^iWrX92N8uA{cM2RRlZ*JPQmyzRWOf6t6V9JMA1X4mMxd+|qC9gw)B^%MW#3xC7-Oi}lqo~B8fc3RK9Cq#ENy=~R=rc(gtd2oC@PY1(; zaaB2wZy(1#TrT}OmF0OyQ}B+5v*Y<}vpM+;Ed~w_?k6=GYCV}Mk6brLOKjK>7lXq)J=q--j0S?a`UV}b19+wE>wX`Bh_x{eFjw$Op6X-L=cJYi^xCUKfD zFwPCjv=WKP<)~((-k`KzZ#I=fA%zf@X&Ia|OI2J}_e&^? zvQR=5S&UODl2)pS%c9Cr?}qG6%TbUzMy;Y1;(La9By4T*SD%QGhXjLvbAoSYt^mY``r7x-TU49y8rls z3%xH~=)SLazjt5n?H7A{7hbS;as7!y(>$ymxzD@5cMBne5_0Xga1B2}hU5l9D%nLs z8~}ZGr7F`He?93dAqxH|Vdmo7WQ-V_??vCG{8U=cv-M8Bwz_hgA3EAp*nYd+efs(v zmRDlV@t?~Kg?7EZvT~~*IaYA~P7sxSk!#VUxZ>X(hGOqC*w%YEvlr^geSmQ*w(7nv%jF3Kv4 z`(;gg=c@5!7xPS20H%UW6)LOdBDAavO_NKq_&$;Fi#OPJ-6lSr zHjw%8lvUEHKU!G;KO{li~pp)A33@X8plygEz=EL7p&j!cci8?4Ybqk$7M%v z1Fbje^;Sk0u9tKh4aaa14YnQjJ8dLiQO7Vq<0y`6LzXKF0{S4*S~IY1IIinZcwX8_ zVoFU9h>?1&UZaL#R*Sw+_yv8Oq$x;A=#eXX3AzDa6}Dhp-{`@u*V zmsMI8qq*>Nm@FLz2YY&I0PB2;c{xwjJWEwp1i^npqc_e^CdsR|J_ILD99J5(dcAH} zCtf(2P&69dj<;fa9`59xS5GYbev%$6j!!Aymzy66gp|_vq?E${{)Zpl+S=aU*qTna z-rmSJHkKNVe0^iN@u?HFnm4}511vjin}$+Jt+D*v_zElHO$RPDSAh7E76Mlqn z0@rh0z;!(r;rDe1t+mclZEbsd!|0T=PG?qj^5KzIF2Nk|cS-dD!spYS%qM6H2D-@A zvWb*ggPq|PnJFjLJcDupJ{j7|#e7y|E{-Vx76Adfz-lp{5Og}z>D-i(&oLnzy&Fl~ zX+#mn>Gf!dLfeMzM9t=~R->q;SwBsAKMecO4cjqI_&&$c6qn-5VaG{6x8DuYOP5B~ z1B#N3=F%v#E!z%b+rkKGZJ4DsAc=bm>U&fOF2XSJzx9>RX}}RkK_u}-Avwd7`XQ9w z@p+@AI6*%$%L|VVM22`Z&B{5}XF3^A%6S^4lVoL`3N1!6R^x5>VP<32sXb)AySaV4 zm}hCg<*jA0gJyI2)62~!=<+f}v$;(323oDGCrD4m16^08R(>i_n(aUAbhAMy7YR!H=rgqPA>*`@zD z`F!5Dbj=WptaybYoI#gd_e+lRCFQz*=(>NXuGw10b)RwFXZ~l`G!3(78iv-=bwh7z zhG9N*`#e}~o?{UikjDx7ep2h5RV=D$7dkvdH8p1Zh!r!UzNoUfaw0LPzCBr0V&kA2 z22ZP07WmNdcLb(onE_sOzJR}mVRB1T;AzS~-d2ipNvX6f-`5)JTBF|&CY^(;o_^6( zCZ#lrov5du)01|~4=g@raiNA`2L2FNEHikL`^C$AAv9$*(EMw-UJ{2&riP)k_7lCF z(UCHYzTX_K)z=M!dr~%U87_^4OyX6~2&CXP+G=5AdEgPgjw6zhrMQm3JxKir{OB*L z#e~QS2&<5f)|toO&G*jjeCvr_%Nj+YWjk)Wwe@7N1Y4uj_s5p?PZus+xbQpky>q*- zJ+bRJOGaAl>dB=7Rz0UW#*TB}g$oxhyp|BL=^V|=BnwIBUrr`u( zud3BNLsPwOVgi=k8HJ(a)@u2v(XhiHTv`so&~DW8yjJrRm4mEfQ+UT;LCZAekCk2jW=8FgIWF$}}71J|K!d3j@8uY=Z&G>u}xTFqe*Mt~oTvQ7u+bh1(4 z15sFb{<3AZ?N%!c5l3+&2!fyyM==O?>Ozk2AsmtSlPl!MkRZp~@gaN)KY?GzAL0Mt z8np-+MNI)WPBI^(4CaesQH;w=vDv{xFMI8vQzr(7pbLK<^oz*`OUhudU2@-NsNyVq zFA}HyG9B?%@02=SrV1N_DjJZp0>lD!lv;p#_guwCmVHpiWo#+x#pNgs5X zuBBTmv&`_qUld6ii?Uz3D8@ySj>~a%y*rz`TcARM;mC(4@CHTd+J(Tax~WPM6n%M} zYDTcdL5!aL*k+;O0YoCZ-FujUI;kNGwU6xqW1MKO9MqLij;1J6nj-<}FbI=Uo5^H{f-5C0!G)$j(oeYw z4g}mnXgfSL^cE8oG79yi+wEh}>BdGFNl?LBx?W3Innsbz(%Nd6Ca%M17$&LfFczEz zrBrADmj+`{76g|*Aemt)X1bEvf=fmfxMbjpD@LJfCRNZ3#sr757^OnjC8NSg5-quY zXRW3wcM$p>Wo6xQ;kn6c1*O#10469iG)QJ@8f8>*DZqsQ6y*Ypc~rUGSA|?@B0X>Hqf>L_S1E5KE(pTWgJ$4ba9EL)BuQ$cuRvMq zn2;H@KnZX$<{fYdE}mS>Ku5yp(}_?DO;N?iFSmt*bIK`%OzWc{v(v1iQ-Ow}N}F{zaFU*+1EvHd72H$0#xsY70+m7|MJH7<3gslJodu(MD#h^W2x=^f zJYO3Hf%qA(+l^?WQLocSl@zr&@Rib3;6;)CDmzV*L@LWlcV8{$k65-clr}ffb=$)0 zQrCs+B`60-hSj>@nwcbipy|4vB;gI-lmf)vt|zxlJf;NSPL|27gp7#0J7hMIPpYu@ z=(*Y@oL`+Xr%I@Rtj=-F?-)gk`OoEffxDWGCIx3MQ(7(QbzM^_$F@u>wL;E;cBh=3 zzMBDPwpI@2i(U_W*zZ%iwRP#zZTI#1G)|U2sq1aPbxzxcZkd(^=sF-326NkT>h&l_ z#}fiA*FzMwS`9~-p8J~dLPpv?>m02 zcOS(cewYQ{?zfmL%zWdUi#pj?{~iQ_TNB@5K7`} zNBAKeks2x5L7*YcO6mRr)H+dH%<_F-SP>B*uv{wLb+_xp_X z`~5!sm%Dbh;iLqo=kxgSs%)jk^ZDs@&vm`^b=P&>^*E@hjP&TY;xhR#`CjsQ^3%jO z6fr?~1#2a$}-pr}6*w|&Y*(|%7{<{Fkxc|hTV@vY|5`7Glp zz#C7Z!<{UGs8}CCHyfkFQ4g<1BH>8EpHF7hktOA8da*Pv7S&DgyPbGgKBLu|o{ObY zwI~R!7;tPy(@YVn4@wmTw{6svQP*_G>-EC0R^M)PI^Kqqax`#VjqyoLx$VS`KWR4j zA3BEs?RKrkZ~~^~HZ;wT;siyFp;lwil*vugvT!dnjWNTd zT-Vm1HyfKq6egix2)-TFYOV>*tw*NGsHHO&38V;3=K?&Aqkyq~(eEo>tCwD_&fs~G zrdrLAMg)O{dXi*d;8K8^fKN@Z91W~L&sBSGlt%)(K=$j>d^p<&BPbBZH6tVIV>mZgZGh z4dQtM)I;CQDoH(DRpx3o!qL>LJC>`j+U2TybF=Mv&PuH)YAeqFv@A<0&P>a;lw{08 z90Xi7o5OUy$km3(q{@r+bl7Z4@F0kWVk6r|dGKlb+LmQfwEq@7w@R8y>)BYZ;GHu{L7T59y4gX!c>P)hHZOt5?I+^JJzJifeg_dP4iH{W;P z?f*COhHc5|;aG7-GjAJJmEs9C5vT>)=s3>ZsVnL`9 zRdnxR_vaR6BwlunLXiZHm?ZI7SMNB?TgHBfFNZ=zO<0y=!ZJPAc9d;QQD84Qm*`aW zmuF?W^DF$i>t2$t%*OfUayI<0)6-W?#tqZt1`lSz^74rjC(1zF`F&@9r(+mkrcFMq zPK(o|5F&{wh2%ltJm_TXqu2zSCnpfO6hm;6_rS4FQDLt<0S4gC<_}Fpg+D(IIC{qHQ%-k zB}_{?I;YSegrOLt%(5Nbwk&@VO%DZ#X(@aaVJt zHJZ(arj6d->vWn^DbjSX5=FYPHXap8VhPs`1FsgB%CnN>TT*zt-;WH#Xm0LzdqI%) z!_b-4>zeZ0t*O^;6>IIbF>SSd!8yNabGSC4;Sz?MH`t!nJ+;18clrC+^4iv%@>Z*G zdc8dFjWxj|$8p^nW7XL+lX|^oz4e(ByN*o}MZKqHb3d5pBMQ@6TGtIqNBNu?hOR(} zMueGLSB~6nl{5js z$&6~jI~{WPFw&iEXJC_=!`AAIUaVRbDc4(j#*?X-Mv~*YdcLV~DXX!4Slv|A)OOO1 zCbbg`5+zk=k*L#MMxc^3&ul_WIjPJasupqiHlJ1a#ZcFn?l^;n!5B=dHriI6AJx4` z)y=>&4eSnFR!dI_(}eE2nx+|cF?NKk)dt-<=eqL!WH7k5-G*h^Yd81#nqaP5e#CVQ zv*)??*F4WOJB&BKOVfI;3*T>V6pl%QBFk#MTAK#3M=5iiE)Ygab;@&N)An4K!glPf z-6#TfS5{NnTv~6P>QK|Nb=x)!`bN+s0)-HkE?d2RmL!rUe-6&M9Yw*cA27YqD17NQ zn^6r$XAujUBui8`fzOJSR*PHhR%>7fP^~y{DUBzM?(12QrpvW;>e$MyH=57e8a8^S zZtyL`i~>)H^s|}@!pQS}(hs3)3`){`!}YB7cH8#X_UB+NC%O;n+ATLi-(FvDhE&u& zzoqFgty*Mq2AY_j=M2}jTj(Mw!Vs`+$Du3;0o$@IYJPIt4Pe3Z*9jqn6Lsw>-NqH7 z$ei3rE)g=yi!3e|`^bw)UM-5e=u0KzL`fx8bET}@O_J!_o9Bh9C&fS_^BY88Hd&lC zViY%HrCgojrn2HIOlLD- z@vnVchoMz#G?E(fN^8Grg8J+B!#E(7!2dN&AOcngYr{u>4UwX67% zxhUd|$dtqMt|Ui;E)q3&O-;Fx&@J_1lx9`(GU7s}`0TGL^{Y~DYU(XYy+vu8muU(2 z_{sD1N79)`IDdrml(92^9h-9gwkrm(M;UvR@t56C>HUoT-w@79$69}nLz0j#Ss~SE zMT?5ddEcM&Q^4SvlwlH=vm(zX^JXP@52SNCZ)gs{(eYtmSy;v42R|4@zRwaI-+2%Q z&5ce6t8``M;Aj=WkVbH6v7k7#t>EC`_^Ydy9bhG}?Bk<@RuJ5|f=+jRe{}^PJT;$p zdswB5#ie$dhig~oE=o(CypFt`%)5h1HX)_IMGYp^IHt3S@`$tA92OW$ggc>-y121i zRIEf7>3`yABXh(ok~;qz@8ZBi#~CV%33{HfZg3v;Et<{|+AUodcp=3thUax0%1p{p z;1*?=dg$0N^bTOxYJwSNsAq!=x}gD*(KK-fmX(5IifQzvZHrTshiOe?46JDA%|bn!;1EHe;e^K+=!+qAqiu)BUv8%2P$anpzrm%_%~c3C&=UE z1LOx0o;h((qc=Ja={|Qs)Gv1Bo`^gPQEGAE##+hf>4+}{I0j7R8*+053K~ti&~sKo zC&l~#h#;WeCbt(LcJ@^jN7Wtkl4@?Mf11|^X+q&TLE<=YoFuj#xK5HdE*v|)A$#Ds zplK3JR#wVtYs-IA(Cdb#@0%t;5qI^*8*jW}#SNgE%k+IS?Dhg-+wmY+wk*?GTXRgy z8hAdPiG`bK&-1%0E6cv$O-;*U-0;1@gDi@*wo;NJVr?ysDL!u18&({%%~W;b@?*+TF^7?cAz9nceb~-LW42R^l*D?XFFk11?{$>>u4|F*yi4GI0MbHP1{;G z4N8NsCID3n14<2Z-Lf~cjE-p(*{B1Q)?r5x%{@h~@`IUQksjF~_mKCJpCP|X{sR-d z3~$5d@yC=25qZs(#pHbmCgV6w6qaP_tN3oc#K&M0f zG9ZnuHmj!8ymD=|%hAh(!>%BUCMuGgdev-os04r)WnTXb!qF6DgIGm%62p(dU?PiI z3Lm}FTYb2k&eQQWHV{Q|;4dJk~%L!ziy61sa8}nIi>&={VK* z3IRIRjfr>F2;-h-3utU<9+zf%3YuYCjDa(MGrmd$Tg+urO4z#|i@d3UGw`Fa>-zpg9}Ua0zr0j58^Cw~ z&ufI1JseJZL23d>1@1wp4U$q<&uBM60vZH`zv^5EE;2!3TSG}*vxdx!fzUL>z%9U3 zP*5mGF{!GhKz9QzY!NW2)ExeE22@Ijpv)BQ2fs{GDe=4PXxSRhd@@9ZVL*dp1d99w z8v`DRwnT--m5g);8dD0)RX#Kg%=ML@oNr_cXemuH6Hl{H@1$XNB`V2-R9HYtms?&t z9+HAd#at;vA@TCtxFH1>IGzR&(xU{I$&}noNTn)3TGdEX5|dJ@7Ds76mO_P_RVWxp zrE?`|-jg5`!{ndTsG>e#lErp-5}rfHh#TcYS?%@o8|Ep2{wCJ1i+ z!%=j{ihfFyLP+hEuKTtrA<*x`wv*Nxjaus1bHMTGGC!c#XQhG9UfnLG)LyMg86-)7 zBpqCt*>}A(-?@YTNCi%Gsu8Ki1`w8v`+q%AUXnO?TXKA)YNo=->v6*plm}T7WM|Qw zQ>`vFoJ`NZDjn0yd{9K1VV(&vNSr%D(3WLM^lVLXVcQ790#K&eOvA`17&J}uxiB@x znX7!m&>Rj;*-5IKwq0W)O?ypEG0pWjr2yjC;1S~y=kBoGwipeQ)DKvcgTamDW>V(C zd*Nw3E8`Z{?X6PARa!0w2pr4iQm%~heqGamwxtv-TT_S|M&z_xn>T7wx|*Hv&K4d@e?xGIwD#T+JLXuquF4n6rhtJPtfKn6@Kss;Tjd$U1NUaG2`Pi$SjgVa<9 ziqVVR|2I0_le+6RW7mb&YFY5SxZyZzdsSD;t0@U^fA7SNx12cP9`+gyD@>Bf_{6FC z!eM}Et`t?2)@&^dgFu?KTJ!@fO&Cj3_6n9HoF@r;Eli8Ez;84?p9|9fOXBr@tuFun zsjaHljAJ-%gv4=Z6g7gmV><|fR#Lleb!EaDoq?ZrnvLw_tn7Ay(P+;PhwU~dp3ixT zLr~3(GRwh9s?!b#f!Qom?i*$N$xuyO_9`qu0Y2 zwo|sGTB=DpNvBdxe-qot@CzD@m?BhAD5`YP+VQKTi^;wGUJ82kCd?}?g{n8VLj~Oy$ebb!0(?i(Imw$wKAxlKVqx4%11FlhUHnT9L{Uu7}~Po#mi)5zPzyaZt`~ zqc?0e8jbo!FdSw>f1}=LG&XOh(j*0?jOoc{c$2%C_g>$}F+0B(bdcmFM%EO>RV^ zvANM`ATvzM?4!}x*laX#QKl%KfoxHgI_Vsn()TviI{|>LO-~Iu4_69WddaY5n z9#wH1$5O^|oX8Kfv@Fw_C}kYSv64v~$MXHS;f(6$h26BRl6)LOBF0v}ALU>(iDOEYjl;EUZIr${V}k9Z$piBz zkAJ=H)GgBxOlejeTd`xBoGN95;>t`3B(?Hf#~-E0qHw@CP2yfVisTOa$@{YGzR~;k z(pb-04@2$JZx6SH@I9rTGls5NHe){l|M!7Cp0 z<|n9@&CBrvotj^$#_q#|VqA33dudpZ$-g($Y#-zmd6xNug}6oi2L+Jx~|=H6lLeVBn{z^7oB{xe7XqY)upA*YL>MOJzHLm zms_oa%?-+_fIV-czSC~oXhLB4IB+KW>`{3-WyQ_S&BjP2E>~hc$8PemSd@QRh4jTMYa}Hn zl?@-Wi7AM+5^v2+AkqgD-d4QsT#{y0c2&mg}b`B>vRiIa@~;o801-y7lXV7aQMJ62l-*hk3+!}9M=xcFgV|a z<6GhQNhmCb!f9CKf<>d?io;?TEZz=Ft_I5v);KH+!wGgMo`RApD0v&os-Vh(swt@2 z4%OSCrVDC|p*9A!+o3KU>b`*GPeH@m&Mw`hG8#6Y%nqon>%3h%Mf)!bP~42;W7_g z?t#lcfh#8A%5u2M4cB}E*LmRja=5_`H(D^}hnrr8o4HxJW(n2y7?N%&(Jp1&MkwBW_<@UjzLalorlcy$h5yBuDdg}=Mu?}y>_ z7`zF*WrMd@!rK$@P6xc}hxcBGxiR?X>o6aM54`a22>izl|LuVPIbeG$>==g~v#_%j zc3uvFzK>@S7`t#DvJ9PETc9yl}yaSP(J@L?1_IvYNAz$abs>AmpT zG<<#-zS<4n%)+;0@ZBfy{S^E(0f$H7NGBZm1ddK2ijmw%6Uep_C7z9vLH4sz%2O!S zi_+RrrUzw>q3p*{ZYOg1k>h)m{{QB>WWd_7^=@kP5(j74XC9ZwZzd%7g{-uRvt#H z;%JQvwN9Yc1>|)i?*#J3(P`7Dtr(qdLmf`kv4GBe3Z2!C&W@sU{OFu1bgmPf*MZh{ zql@R!IzQ^Z8ugT;-j%3t2KE0H`Ryn$ih>;|PEw#pb;P1)QL7tp=dYS z5<^?|qf5KdWmV|1$IulHbY%ox8AsRH&}cilK8kMGk8bp!F%P=gj&7Spw?BsN=tp;6 zjqdWGyQk2-HgxY_(fu9h{slDdKwBHo)=BhWF?y&0O=P2otI%W>n%s{baiB+LPz>~2 zA9{2FJsw3*MA1|8=;>S0v!JO_^m{k@!#LXJK-(f{+a!A4gJ$CB#dP#aKYDc%{TcM< zF7(&8(Hk!GMi+V`hTbkmb51nZh2D>$_y2?DBj|%{^ua9puOIF3q8(ARV*%|PMZ5fH zR}AgxLi=oJ-)?lkfev+{xDS2kK_B_h$1(KD1p2fcedb4>N6{B|qA$nM*J1QcIr`R# zzAH!H--~{XqMu^u@H9H|1v<8X(P50~#MoSzq?a-FPE2MM#`y`R(1%&%!4yqktUEEw zZpD<|i79Kxl!GaM8B=j9=ENyXVuF<00yS9N2qp2S>tHfFRPGrEAeeiCzI zG3F*G=Ju7CJ6xDMKf&DX$J`Uc+m24GFJ_`2C{QK>019b9 z0Ei-vLjV|(nL(hhSU-U?Z=14F#ByVa)U$Cci9$XeOQnj}+%#sv)LejU@_FR`lJgv1 z0)lK`Z&2X!m}t@RF+-*q8B3r|zv;CtHQvh;{OHuPm$4I%TLQ*gtHYJFtGl-!pV- zD7lwjM4?~IqhEM>n0Lp`#M9YIL1&9a-AcdiKRUtjEr9DJc)Ot80IcfC(sA`oE zcZaR{+1BhlbQ`cn~1hqtHyA62uz_knm1B!Tx(A1JZ|BtVIh z2$CW|Nh6Chv8OpjGigrOc4yaTtexs?8>Ks2l~%1ZA8&VB=eF)GzjUW{|EBEx0{=u) ztFKqxUDef4)mSAUt1Y!1gIaD&Z8>qG%_K7kI~nq@mou~E<;Ja+X+k z9~Q_*>;od@=W)*eb3J;~hO_~SWQC?^1uB?4y3U+21=cB}Jm_g#9$y4GPS6_EG-AMpQ-z>H)tk_<+2z&HsQ z*;}L}f4)YOnUO5FE!x%{Z)?t#HnI)m*uY)~?DiCLy=^T{u2xjo0phC_1s>dG2lxWI z7zCclk!8-c_DZl4BHvYHN|jst2_s~HEX(d&jIIC5ABXW2QP`TcRLaZ+^A z3zleYA1HoYWFaje+@nyZOO)a%^>Y3kKk7P})G1@9i>!S01?y-6+UNKpN_D)bC~6y} zy8Z3id5Q9is7p%)FXpuE!Kl~nbArF~^3g7zfR9%%NTE(ep~U`OZ6`XiOv;q5*2+ zjZH^tF!QoC8fc#OzpQQ8v@P+Spo#CORBM27*4kpLZMNHCr(Jg2W3PSoJK&&04m;wg zV~#uFq*G2ix#uWVelHd~ji&o*QmvrXBK?CI>8 z>~ndeyjk8NZT=(5|MH0P zjPk7V?DD+wqVlrx%5o2p(o#mrB$*=9WR`4{vvN`H$X&TF&*hzbk+1Sye(3*NK`Uw% zt*$jSpi$aG`)EJyufuh`PSA-uNvG*dU7)*kx9-(_x?d0IK|Q2L^q3ykYkET;=*N&J zls*&)tqfgt{7xFDpi|5#;goX9IOUv3XP~pux#zrcGrKw5ylx4%uG`w}=T2}JxGUWq z?jHAud(1r(4g)DjMMknyn5HzNIW1^OD_SGbOB4N~>s_+`YDMs5@btyKyZHEFRiwJq zk$|+2k?ydmm*nVC<*r|Cjnb~#NBds%j;kNl<9g+?s((#=-NEiGcZIwCzk2vd_;L7t z_;&avZ!watPnrw-S;3zLz@L=%4g9hFvHbs=pXQ58n-?_O2uuf0&; zJ@6gzZS<}4ElJz>S-vU0k>DE+zV5z!;L8oZ?7nQitiD9xi$~f15BUDx+@bJ30q-O5 zJ_PTB)V+KcycfWG+`HYo*xS!r$?LIi+jGJ0Vt26H+wJVOb{o4k*wL*!ynRJhlvTwl zY~{DIS?R4rR(val<+m)W>Q0Whb9>WyQhG9a@Fe&6BR)hp5pN=1*~3D^csG6*|0;|w zj4zBY3@!{U42_rL7xA-rGkz1_hzr};DA2sfd+ljrgH+n`_E<7%cONkl8#*kJz))F+vWvm-u9!s( zjDaR1?HYak%$J(PNN@V}!ezwv*i3YUrsx~ZquHp!_H~J#M0cWm(Uq9yXU1pM(b``7 zy+Jf38WRnPLTU49&Ya$^;&=4zp?A?Y3^rI_6>wuHXz-i)zf0}3*Fi^}bk;>z-E`MO zPrdZkM}!^MUTtfAyU@x@~f+f^`AJkL(5$BnY;abu? zH^WaiYqtZN$a;YVWaGdZvbEqDvUT8jvi0BvvJK#AvW?(bvQ6MQvK?SJ+0&q&>=}-m z&jFkvZv-}xHv>z_TY$^RTY{^}hk-`qGr$AnvlwPMKu7Ww46_nbKI3-tdayU!0fv*` z1{yP<2Y8VIJsGA3pq>FszzF_$85qhRKLx``4I(ThH6%4^&rOX1Mv$6-k))2`K~iV1 zgVY6#B6SCkka~c}Nxi@ZQWY3YssUq2gTYwRWH6321B@rl1e-~-V%S8gC#}H*(t5|< zYyg-@+65Yu_JBsDgJ3f0FqlF*2I@#BVwg%gM>-GFNJR%`kS+%^Nmqbfq$|O0(p6v< z>1yV?xfNhG>2|P$bO-ar+y^j+^dOi^dN_s^q(@0l=m9E1@T7}DRS2Gj;2EGA1fMvT zy?F`ln|TH9L-accz6WYTFa&PHd;!o3f?t7-5c~$zf#4sYE{F}(1Bt%4iNq~GHserN|>4DfF8K->Z%9UIIB0)+6Q6Oc3Mj++bn~I2_F-R4V zs^hGaR0pZKp14wH7eO?T07&E<=p-#bTFSeVj073AUbr&J11mwMM6d#6D#*08!panJC0yJZM+Y9>4_9KCTQ?K>LFB z^QJ$5t)Rn!@}T2^X`mArW+H&;ppzJ88UPRIOdvYwf*2G5-2u7_m;t)m0kc5&0<%H) zG0c7db3hL;%s~KiK@Ty^5yT)j=uyyPz&z07E(Y^KuYg_y7J%Mxz*^7;z+%vkz!C^~ zfTa*hAHgyR)rU|(7rUXA09HfjDzF9|Kd=^@G{6k#p|k;~C>_CRN@s9}(iNPgbO+}s zJ;8a(IB@&Wjkd<4EDUl8Amw{WMtEXsIFGf~!2j*GIB(nOS9;`MGwp56oT`XDGnML9#c zD9R;DMNuwOdWmw6GEkIc_z@GgK|ieSCsi8Yfy%XtVvlQ zvKFP4$U2niBAZc0ifk^>LqTaSayezN$ZeDrB9Aa%oIC~Yo0C^S=`HdmWunO2l({1B zbN`)u2!@lNgW=>?kn;LRQ1*)aiLyuJf0WK5|EF{p`H8YYyhFJm-Ua2Qcvpe)K)kC+ zxhdXNqTCYiDpPKYcU9;f4&V!bCq)YIQNWWD1-JotVo-q31D==^;DvxEJ_YzQz>^9E z_zS?3B?|E8fG0Hy@JE0rbqeq&fF}(K@W+5BZ3^%)z>|Oi`~%=gL;*etcrxI*bbY1^6exlL-a*1mH7=P1 z_*6&%z6ki#G6nb&;8S0g^7{Mumjm!)paR@p;2|zR0S09d&;!`T*zzYa%Y2Jvp`Ufq zY?Njl=}X_DjIvG?N3oSW@<`(wk34ep$Rp4D#v_m9e}@KlvF&mL(co@=)8z(MuU^eR zzH;^IjjQa^EM&nmXdLb0xuQ7DCfNq0zQ9Qqe!(N+qRFJE@Ova7@`1)P-FcpGG3hDd zV({MFU2)E2k~S_jF5(p`&py{vs60!gBu7L_@?4KFdG_khh@rgw=m-Eo++JXU3wS zr)jp3|Wm!b^!vn1cBcGRI?ZAO>(|`<|4(^gYTbi{dDr zW|WO6^Q15RG)uE-6h|?TwYAfy*Vah*{KrYFjo5A_X#|qG!&|olY`2m_YDOLK)dRfJ zx3G;k5=ecyi&hVvZNzS}-k}@%d8^2s(|eK-dH%d4MDF2b&M2E3u2ovjKSmV93V5mR zMbjcu4QrF9h_o!^kagnSN~bNZ1gRYs^x#KBUWoM z!vm~$Ik&8EfF$nUv9t1?h7W;@@}JlHajlB&>x8rQ9jS9drB4}UVGu_{94773pQPC~ zBqQ~+6M;wslhX2A`?Fqz81-f^YF3swYPMG zX1xeaQ7w37%`P;Fxlk7_%D+YA_MLW!SD^X&al{nr%WWJ=GQ`5NFBBbDKcZAK#@3|% zq<;8~U&dc{^`evC(#yI$YCcb-B=3-f$aj37|9YJVbrWqn@3%=orMBwakc7z3`>#Kb z^0K<`>ngLovdSATbEAEz%ow)u*BvBMlFR2oIA78{o!MH|J-n!uKRw>ZvSAsZ7saC} zrUL2s&cx|Pc8uaEPIqyssDLK6TgkYsE{*bOzrxr#^7Pso!R22wZBxJR+!E4OJkU$7 z;B0xF`RebQx@o^~j2v#38T*ncG>$T73`CloQF_a8?b3hzhK$2?UC$5fqT%8NB6Xu( zPfiQ&fO7!YD_$zNBiuJ;@N;g&QJiKX;4bE5X*Nkl$tW<`G}**t6ego&L|_-$bC_Tl zPW#g^M6z5nO$@{7z0)v6)2uC%UozX$L-b_(-pDkw-%n3qRP0W}5HSp=TV1PG4^k=x z^_tb)`X%&ayWA7DrH9FDjxr2!q(72=x{Ik=>OmQ0KJA<}&siTYi$sKX8p7cm0Qe+6 zdC*WY10|zzFN!y^Uh3noFgyQG*t4~=mtWV>z&%gLM?C#aZ!UYbc8vhVn7v-7f;DI(OI*O+;(R!)t&lGIm;fIY5^^N87NA0EAM%;^- zqS@HX|C6niJ>1jL$gg|l>p2(~>>3*d2$;8TVuHsofSvV$6=@#>U$abCiD24t$B&+6X6YIG-+Yj=gT$PJ7+dl6`L2Nr{b>exOD&SXv>H z5J{|3U{|Y6HSKi~ySBFgBTtKeuI+$SJYfS<{}gDy5n9 z8|eCSGVWfnZ)~hSoZH?eWPAIZBt+i3xfKM6L9n%XZ#=JBjYrimo#d_dWVOun(>wejeQZvaO=6vE@ZE)%4yjPAtG*qT+;^|8J>j(re81O2N&b>|qiMbCv;Ifj3NBMvIkN;8Ucr{qIrkT3~HBi;?Xt z4Dm0InC4i49oZL^4)GXb0F1)Xn7qe0OHF8>dAzBN#&|A0b0!^+(=%sM)8c$}Hk-{> zS2^c=HNM+8w@kcaV{0X9)o~B*OHc6 zW=;jzyb=ys>Un2aGls~{pB;?}8IR7MpP?`-=Xb}GMia5wn2dMNvvJ4+WchW5WIQ^1 zb~Gkh2Gdydj~8YI_w1oXhqB{O5o{CEO|#U5n1;P236ZxLep(VDKaK2OAQ3iz9QKZN zShLw((B_3|K2x=@t;Zx%k~W;<8ySVQ+ov~>{R&Pe zN$IVU5P8Ka>sUN>>V+(>aBfE!rk$L@gY!qh!OC4kN}}ub3pTe1r(c(3rY($B9CM9J z0KHGfoE@~MbjPgH)~9FFI=H92)A`NVDcD~8rT*5ZQTiR$l+KlX(O>fPu%!m+PfTQ9 zFr3g5T+RS1k`Q^YLacyh+`D`kt^Q1Y+~7d_L{`mp0Q?-EP5;(3lfH4(!%8#k%SBT* zS=LkeBqp8zs|TYhG|{%9C8*7t-3J~G@l?mie3_*~%+fd#IJIfz_ue8Lv-GaFb8cDR zbn&aZbtnmukMI49RTj>Fe7j|F{>^{O^^a(4&J2u2ICG=eO0$_H|Nf@sVQD)iGQT|f z^?2l0+4?|!9l%Nb8m3A|eiU1bhQScaA!%k=rH_BSWSQox?LEve81s)d@k)W`KSYZc zs5NJKIRAdR{QZ1!tx}8L8(J=>qLpSD8eb)AiZVvS+a^Rg|Bt0Syi(Bf58^ZLh&+=O z%J|a!qrJ$OOy7t+h9#H)H~||`t1CFCGfUhUN1^}R-;IXBbKy+JYC_&Iz1=Nw^}KbM?F@zPJnX9$A4?h8-dX$wQl$Le0|FhEUtOcI8p z(7Xm1if-aVPjJU7|I~V;ffz?yhnEhK9rl)%5SNyE8mb<z)#6O8Pq>=JRw(w*EO_9ePUag_OS zdYol*cYDSn_M$FTC>7y0Vy#~DD_p01Grh-MTJlg+J+&y6LP7PUG*3l`AHPL@6%2z` zDtt4&NB5=S6__l0vRo*fitwjI$JdRr$B`9FLe%Ts9p5V}n#L3?N>@cB$QVU!J-=xi@RudZ0^ns?k8i>HYu5mmk;}nLVC$>L)U_iU zT?K3X$Qx3Tg!Gqpc9#2K*p;N|gAb-Dg6VJVYV0rXe9Kz!?}w-9gWtk4?@7GE{Y|FimN6mNHRj^`n45nXU*xiqmmhFMjz?ZeJ4q6|Aa#{wvc`j2M#_R% zi89kGJ@FINvgl7dQSw~%h*>86u^*$RMc=5pctv&dPvHF~qM3gZ&$?>K%MU#2E>Fw9 zV4``+#Q3lVq|q33j3Y){$fO|4NQ{lYUFzxc{;45rlm>>P9X$bo> zpRt7@KK3dYmqr`FsoAnUa;?|{?pXQD>8>i1yrHaky^%-QTs- z!f@7frZMcONDUcwJonr^!DuP}C#*YZ`PT)bB^>5ob>g#Va6RWrd1&x5zMROv4gsz{ zR28NG&PZ;f^u5lM%yh_@&pMv$o-@tjXn>Rv2{!&-npsS84+io7GyV4p%=b~ej;cX0 z_**}WwrIe54Kj7 zUbV(lO>Gci?-$DgO6_p{~l%Ux1@-s6Tk#RVF_Ar;O` z550$%GB7eicXI&^O)^m&cr5UBOOM3CSmQua|Pyo2Szy&TqhX_V+ z1^}ZtVxDqqxRH&hMvLb%NGz}JG^W^mqs6zl7unYIYbKz&;72uER4v?(8 z!@_)x{zf+HuIsr&Fm*af<=p#>Vy~DTVi1qUDRJteSw%z41){)(Kot3Wjx`C?IX0gc z?MLyv^F&7X=4GYmUg4;yeR38LP~?}o3FiXM%lII!WIk7EdZBKHeL^QsHa^Ycw_ z0U-}(`8;V6xDHwB%Uv93FA_MAp1^77=mq&Q^BDDgHhI2%=Dxs1G9k-*)zA} z2QGzqt}QMEs+XCrvu8IoU1xhL3OvkBna`-*BR|}W*2(ODoa}nBcwtZ~9Z;MLOujy@ zX4|+32BrYla{nKG9yG9_uOY6UF9pU~>a)yeRA83wBBOCCmZ0@U2M1zmrzDP&K(=OU zBTEBXdk{aPbKY2QSk^aRxBfbnZds^)@XrjjQ7D!8Puj+xd7dsC4VizH!e`O!EiEah zVS3rhZ&_tsFI&F;9zVd>>Xy}5Z*cx+uUmheiq;lYzxij}`bl0YDh<{6GudcJ-Sc8x zTI%JuYLyB;3@fwIw92|DAi+QT=>RxJIX8qO3Nq_XB0eT~RK)ij)K~Z$<#h)M0-Rm7 z2RI)NIk&9g(6Y)0As&J05cohnSbsENz3=@EI-K=LlDpVT2vM_(Jv?#emUY-SIGbG@ zr_MR&hxe~Cf6nK@d4S%AuZDIh1)+6G^3T>S4)&Yi8AZ6UVWxAs+9)sSydQu17yBCbCc@cq5WDwWW-YF)>EO>1yg zEfhJb#HB@#e6CUe?nJ>h_Uynt@Kf+)tN;UO_%8|r#I~p7C{}E7KS{s{Fs+d)$;=;J z^pMRYl)-d6h}}`*_tiFirZz)tkNW*q!*$AK$8EIw{n57h{G)D??Cc~-H}rj7RdwGF zzb;o+)w1u0e-t^nW)TV6c1_bz`T-Bx4DkRRyIE^Ae817CHEjn!eid~B-$(TQpc965 zU9X4X*SP<>F^ zqanX#Vn0r&8}XPw2K>2bNV7Ou#v%PQ8zNV`1mi4@hUj{+BXpDOIYKvI-f-P=p)j1w zO$IBSgSV$oAN@6!Y001REa^oTxKmt?Pf?c>9&ccJ)L_~2DT}g~{(a8V! zQ#SF!sWh*6=R2FWNH z{gPYhCoh}L5NETOy=*qau7OIi%6~>7mO8|s-3NV^IH6xhqzsu>E+~|#MA_t;P=?8= zQ^`UwAcXl$HyHYH#Dr)^J#_A=c`{;Z}JXn_DGHuvB7HQwn-2aI20bSdGaV z<5p^kNZtEol-=LBFc@MqKtS8By{736-p879HQPoyzRls@-*4$_tn?b(KSOVTwq2W( z_PNQY&jbYOYfLMed<39Q(p^mBDDF^!R@)#`P4_0xENP1(VO)$!2M-gwbf9S^J3p{X zr%^mk)oN$>7+dT4Sk0+Awa0F!q6+rgC>Cb$a6TVHRiIjJBZ_vrX4~jg#S8L(79c#- zwxI!iZoX9dmh@#CJ+|PQdNi8v+GNwrlXg&$(f;Lbln#2OLE?u(C%gX+CJ?kTx7~aTtFQ*;Sqo=jic#}iZ?0h7m%kyEH<%G zlG{ClG8SuW#!2{(ffAeOU?3AA||_neR&91QNeZ!ka{4DNGnlQUYW777H< z;Tl2Vl_?W_8Dom_+WERd8kI8VsMHjtK*po9XG3=% zp!`NbQECbrqFkvHM6y1=Z!}WwE^jL`74%EGTs)VRzA)E6qMPV{ZMpOiXA!9|;G>fJiS+s+pXFJVyFwRON>CO;0?35m5|IJf2 zz_3X!%_dGb24**txh!$C(PLp6sRI`uCGDdwS;Zo+BE9w6 zmtcU4LM4jRY#ZHy>DcvmXG}avO6L5PF>msApqvTGcg{<wGD=1sHfmWK zMllV8n6?ha4jH?~30fZ_7oF)u`nR;VZyR>>qBtg;iO2rA(o<}92G@7cq>e+xW3 zx!&5`kxk_`HXIZzb2y@Lr#<^v>AvY1S1nyy9EGY z(rldV&X`U@wq(w)MVtSbzzbq0zz5jfgE2ra)Ru`-kC)|QoOmGxvo%a79&&9TX_jWV zaIts&y30>o1bN6qT?Gq9dw?QcQ!g?;9s$>nuunXW=JNfBG zoYgSgskY7HXynB#Vx}QwnKzk+zJkPk{2xyO1cRwT^z(wBRGs^S`;o|nB&NW)5u_EfJd)jw7o>fV^UzdV6t< z6G;wx9f0!~2AvutUj#fNk|RR19C71BMt2!dgOT2GCcd1EckvbAPeYP4E;RaF;2!=X zUKn{F)-v7#Z*9!ZHr_hdO%U>}j`LP2;8`CQ0OG?hSL)?=)y`%1yZx9~W)Ahy2D%fW zFfknY0NE9rM@k-rVUc&!t|kCZPEBblc&CYlC*Dk5^M>;_1`G()&zE7f{vj}=mCT}z6?XEh$B-LKxzcoC2A>}(Fmif z9K&K*jIbjNoSJk^lTe@bK)S67^Abf3XzCjy4Koge-q)B2O|t=bYSl2!P%y2pi!i3f zqrrJy2q)v-1%PNP3H#ZvwdP^jQ_i&e-LHgU7*>$aa~#~k z8`?kUhTg`+2I6b`9UMVE(({p4D*yRg-QPtsc|RW_d7_qr!wGbLE$7CKMQE}CGr2#) z$<4Of(Qz_@SzbRX_>iq#AN;q1&+@bUczhzd>?44|-sD>yvf>OsZc6B2dgA}%F~1Nb zJ%lv%WPsd=`HN;0!B#Vhz{R!j|I1d*YdekEf+%X<{Esb(EpxtB`rnA<*7VvIJWKaC zizl$WDe)Lw9!1=R@m9$iIlAx!Oim0Fh@z$mQPi9sHriONW)y+hj3U@&)B%^L`!$Jo z52iQSbZW-AgX8R+{>SbZ7j^7oc%rm3*st5?bgnz7jgBIu2Olt=5%!G`4=@Y0F??b~ z5Yx$6>2Oqo0KPs(NE{7IiKi-sUPS%JufY@Lt_X2i|6`cGh9mt6GE(1-P{-5ZB6|LLO+l5B7VY-!dfkiY~L5IU#ri})oZ>he7_Bh zx$)LvE@Lp6JaK?AH~u=wao4f{EXx(#3j*6^A3@-8@xoZH6rdw*&)3HV^f1~Xeoq8u zfohL|`UTgV{|ES(6-UHP+KY?rgd0)J%vd&?GN#Rksdgj7iaW8CaVNHn2n|`|%^%6j zDL#$vMbE&kaZNQO7>2-*ComwT%smH{O6G;bj?NjjQVa*wm+Ij{ta{LW_-2ijYfHS& zOwJhn2jeN_uet*S=!As+p)o?O>n(8IG09v~oH>Twaq%029y*53qI=LY(GGeodL#Nt z^d9u_v6AJ~c$N@kSPH`@)}=T}14|2hDUQ$wa3YX&`prW}jV%Dg8KD7^L-QuKL=fX8 zDcH2)`vZ~!E_*L9`Q^l!E%x)am;5PiKM4E~bAq}ovIE~uDDtHbZj*q(s@LNvwsoRzz-l2au5Z5NI(xRg=eUj|0dDpe5V}QO*W4n6@|&FM zBK2A7V{n1f7th*9@TvG~g*r#@i7!CB^Rks zG16bW3kO?<^WKX%_x;##>kKBSf}VkX7@^JvbkhYxA7jLENAa-O07cV0_KE~o5Q%A0 zht(170GJ01jWq4A_Oqm$W`l0p<>}xCBO!W*i6HRGC!Xv01ol9*yydiBcMKZfh45Me z9(!!;F~)U=y~%OjXt~0&yp~6=pGg!cW)g#FnvQ({8?dHbPlPocg0RM0)Bi9g_-2$H z=@XzILLu%KL`wk3c$Np$!gVTI;*m#0#u+nTYkQvk8fIBy2`??3T52>rV(1#ifIWu- z#`}u_&0xG525_K^?{G6Q-cJ%X1x8qxr$@mlbWcy@Mj{xRL?TOQDoiN>*&xAdCekJ90*FRYo_J(DzTHe`qni!Hg{~{0=UB7G3#2fy^~*Q zf=q&O0#Cp&@66JwRtzk!QM{$U7o(G#CW7yj>3PAtP0g;Wf z*@X%6P>PT-Ec~RMb~nJ`ug=glhOab1#wKJjj0J3R#`fDqN7G37u=iEM`$HH}sRsgD zjPXrok`8;oou&7O)#>jXL}J$mtkeUd{9HM7ag@`2B8w$Kesu7P-;Wo=XwCO`?~=r% z5)GC2ttp^Ff%vws%qC}SdME%6z^Of65LBU44et+cAw5&T^H4lm?9Zcz&`S}5F6|Rw ziGmo%F#(e95KD%ukTxm996)XXtQZ5YxnZ%I-a-VHz^%C96UE{GOQ{%cz-roE065hs zml+}Msw)C7j~Ov#q!TkR3FSX50GOj`rVfs4=%xlN)B(iPBky>~?O>zcetI@p17h0e;rxydFhjb7?H)Yf)JFW}D)(yvD25~jb zacrH~I*TwCV9#5A$mc`w{c6ynBy3qMs8)jjJTGdL%j{{=AJX?x>(Z^ohSCt+g@Egt<5+iIwH-$bp5Yn>9gV1A zxX%cdJ@Y%QoArbAu5%|)POMr@S;Avvu9;rbycpW z`*%nyG}v^yQ>aLIC-{eac=r0dPr0iIg*lp(v_<>`2%X}VmH$iAE^Skiw3FmaY;|ce z%is01`#AS@lJ!aQzFte29SG+LdmuW^;P;UQH21Bq8FN1# zHa*~Rt~`13Z1jS9DYQ*LdEur25Vz3TQEXz=5v(l`8K@^KU~l5ELF_F7b*OPyfKwa< zwxAdZvbueDZi5Y(2e0Lqq!7ZKY}Wq(6EuScXaikB_o5dfr1BUIF(4UeNMce30;vN3 zO;r?n<7;W%T6OUc~h|~U>C$# z!1hjn9JUPYnH-P%l%m5O%PE8k{q1mc)9{@um4_(_!j+O^3P_Xm^-nW3PSk+$N&-!8 zc7-V;JtMwP%4OpFB-0~lP9CycM*7@g3YffqXZ>TOn{wX+@b0|R1Mv2xsYiF+B^7ks{N(sh;TFth2v$}qfF0!9vww@1XN-Y z%CcCLsALea=+#)`h77q`i5HW5UB^$IT3mEY(=;8m+d9DVn$oVdG6cTe*VataaZJ;G zcwUXrRk?}YuDX=f>lzRxkLoMMum{KJCNF>EaoR9VW8N_sGv;*NU|DPK3^R1&jESLa zY#tcnJxY&-ra7j}WTts-ylS=!6C}|RT0^&a|H*NK5mf0}rFIjBd~vcvU;>mgS9?I$ zmwM<}Hqz9ZwFj;xD`ZItqYp>HC8|V;$`wi5favhvK4koX!3ZaeGg}liq`ehvDFC&# z+Ta4N4|^S*M~Ecj8pdnTY+PN%^k@`okUUyfMHmSz4YryCCp`vqN@1X`HnxJlzfUZS z&iVpC__Neva8D4pQNSFB1(9naI}dg(L!*iC+FTQ=8J6DYb{he6qo`DhB9{ed46f5z zOCD&Q#OyJOp^UfMJ%~*pXsC*9{R^HZDUL?sy+`k#RGG8OmgNuSQS?`NE6zR!8Hxm~0SD(}tK^bdT0BPAK< zzTa%NpXsyGpTYN7XrUY(M`!gk-U0Zh^OeG-fthQ70SSkaBUGxxNlP_Mr>1K|Jfx)v z)o`!{>9CMWz`<7~aij_p?7Q~+hUd=@H)XXdH&2Z)9yy%*zH{o4M@~7u&$%fd8*x^9wPTdgnX#pXtZ9f{v5nEliKtD6;#>Zjy(>+nq*sEtrT zR9`kB&m)r1S&`@X6BSg4s?+w1 zvcr(AKbJGhIk#$1Q^0;?+w2yq{GWe?{7t9-e)?^MOoVPt{KfG{&@x&pnqxk|llPgFU@4W75)*3Pr;G~}Yg)T`vduD zW4giq*vtP;!`dG+L!bU%b7vlJXDqS3H4ngB^Xvp6%8O~Xh(_oXx{U5e&$d>Q5%ukA zn*9irK1fgKwpb!VlY|JaKs;HUE^*wDSck;`M8AL7jz$n3pXLP)GSj_lSwgIBiNCIb z%L>{}j-czYCG221J#cL-A(MEzgPwvwAe$yBY=>+o>>x$~DL&X{K_TkYxH+Ntb=a=` zGD07X(Rp^Y1S$zZB4k(_{b7dI3F1hlVVVq!6k!>l@JW$4-G58RO~!J@PR%~~S+`WG zn;6Qq2>^+gi;in)BoCZhM2MIUfnL9!9D~*wKbEs@&|CNKhErS zbQwK}o{e6LK8QYx{xAAfgu+#(jX5krh>dUx6oHZ3o?e zT&B(N7K7AVWaMgo+^{eRZ72P1x|)PG&>7C_lI*M@mI_(7DD4&oFVc8rfA0Si{SY<;tkI12Y~aR3fr zjx|>R{K)bON6;!~``84hg?S$~8Kr1@4;$Ac={GokOjyEKQzFPWjZ-L93Pg1_lFe%KfQ{ZoUuaz(;aXHpM^tG z3GWZFrX$wS3PNlqq>~XGG%uJFFk49)pGn4t1C>%2KDH-l)s#A(>7fT=JYK{yXja6| zsx*Ir;BV^IMa!r_IVS;`n-!pNy6OyY*LeccZaPT3P*dgI9?)XA0ZAOG9Dy4~F=65A zl|?Yo6*&_Yugq=!B4T{bFs@$tNOTx|E+fY{5{D<2u3TL_5vm%SZ+K$y>XoGvA(qqk z7?c`Uul#eh9fs{{o%0!5M>(ol93&$5Q-i6h*&t3t@_;*LLM2szo#??9;LgUJn1{^~DDom=~T;l2PUuiCc;X>o;sR_M#Jy1USwdpPLQgT$KG$@-_1yy8b2u5aTk>*n31;bg#d)@G49Bvy%IqmW| zxB7T>KY$>p>Uz+=P$vjYIbyyy76mSx+E4(G6rHNkD&7cwI)45M--+?tI-+Uwnp0pO;is}xs* z0Q5HAcFsd$V8;^zK&;)D5da`({Skn{?5qyQwrtz77Horhe;@$FFVHcI{R4N;S)j)e zDpoUR(!5iSCJc+LPt7|z3(%&i zqEQm{Q(>#vIlrOeh!O$F52~73;oP9HtbRn(gKF2FZznOt&~E4E_SH1)>#9MB#{#f; zr$>pcUyW>=`|tten3q(|_MIRIVZ^Z^?*1}2F#nRL5o&$$!__drm`GWvy4*+&J*q(z zCED!#YZ=F+SIi@T?g!NX3l0Q0cXZmT)L*UpVyN-PIPGRk~SchkW=scA!`fiOpCc%z%PBujkV+6{348DJjz6eVLj)q9dY z)8m=|5I5#HemQA2<>icM4tc4y^2=c9DIQa&lvtmmZdw|LU56-YZiIw~zx4+;UQ zpHkIN6V*=()$ai!=Wm=X_qS6Eug-xm4!;FDpFOXG+n=ofQM2- zo3Oi*ObMe?5Ar0=2YGu0?LN)&m7Mtun4zvzK~>QMN!2jz9?Yp|ky1t7!LRdiN>pFw zgB2y|E)J;%Qqc>aRY+(-XikSkGA!b>Tl9x2+3vajeM0(5T~ewiSsdyrmvUfPv6P13 zqcEhLKgc;ODOD&{H;leg6{WONsah(vbbYy0s+UTpQVzh+3eo3e*_5*4*cx}Lj!SyW z%e~(6cD|S|#+)wNrBXR$x*L|6Wi4K~uy|qddFzd%b!EAASuO`%%{Du}uEq5SAFMxE z-@Rj0KfTpxG&BGJfM>H>wKJHXaK#@y(wuJ#7YJ6M9kSxMpy%lGSy z*$wFj<|4dbsXC<*r8ik%m@$2vF)8JMu_wcjaxn_SJHjv{R~&7{ZdqovP!KUdWA}DP_g^*vFik?xT-~kB3Zm!!on1#hBAYyHqNN zpZ;|C>5wjUmpYndc70uo>&gKIA^P5O>#|%9x_8{sy`#Gb3*7-^S(as&6B$DW&DN^@ zd%tJ@t`q9QsY*$EHrIN@b*uKHk2;S!Aq0*GS;Jv0GfkWQYiSkJC{SJuf z*+#r{r*9di>loJNldc_uTB+>aMs1g+mhahtr}R`QQidUJQ%N!h$JW{ha4fT|pshF0 zJUam~RHIu0>Aj(xRPuyRbcceNEOuN&r#j|d%;99NTY9$3sJ+g0;z!`YFoXXeXgU|= zihA};7(%<9U%MnHlt8Nh_|Uaa5AbtR+ujVshtoACLPQ2_HG!|9KDrHEME9W|LT}gG zvA$mwP2m27!iltRVDZ!JxBFOMbB#$IqJGrxEc41t&|S;soJ!`i@CvFBAc`W4$|OsUFGs00=! zbH-~4Ru^Wi8b$igh$sWV;$2)5_uuhT@FXWXf6NsE{3W_)&u?RF<$PR=N}MqfZj>OX z0${zigVFMAzo`y1nkkJs)GrbZ8+g}%Ll{4m_b%V^8s|k)qO%= z<)#F+W#Jr_;7#{cn!#%!E2nCL<=k9=kT$syg|IC@I$3qMF4Q76J-?1$aH%#+$`?meR|8@O3tnKY(*RR9R zn9lU3N5&f7*wCrzz~+J(9p5nMv!{Qu1oh?VA3-CV{!xbC_@C&!w*7^?MC*tXiN~Pi zN_>ZztA6A`*9PuyKo3%8VH-1{`w@5HtmC~&V9Q+z5ZjsIn~Y`Pd5R5Of77#F0Q26^ z0?)EZriTJvqq~N#TQSug(iEg0HdTlvF4(hZJ$w`#M`vSkSmBQck4Ye(_0=;|p=r~0 zOmF^##|6%WstG}B@_4_e#Tlusq28S%hWY!cwR7DgZj!`Mb|aW4>YbhbXp~N-0JBvP2QuDlELJvb7HQb`0D>x=HNp0EDGvnTQU~DH z9+@da<)mrZ)NyWeh3}si(YZBGaCJYgts~S%YY2ra{VdP=O4hTfum^+H6F{9#2wzh& zY_sGNS!q^-ZV$T2Er4%pANIq@`>^)y?;&T!0iEtpIPjcrE8kun_Pl&Iiu|_g!QR`k z;l9;z-s&3o?SJxerC$Ci3P3*v5qrB|zvT{GbgS;aogqZ^Jq&U3WZJ05_tN`qOL0Ru zET}gSOqMtY9<7@uo!(k?+J_?;W$2OG-kO>FCGHveGM@IlWHGBKdnqAP?v9X zn~cgl0%2~zEfnDqYT;8Q4jctjEM z7Cy4Lcmx~SEPb#!yME6B?W0Gx?pZG<@4Q;z1NcF0vx8LVK^R3LOg--0N~Kh)te|WH z_PN!S&@$RU=MeIfBz~4qMh-JEhQSn+29qbrNazuXOP-3t!LW!xS4JP-!f#K2P>tgC z!2R)KBmpQi_B=y>$a{Q*a8>aU8gL60htPK63n0wpt^m!Ufmcxyd^0H&h2xcUQ=iO7 zqyswuC*XOoJP?ygz-%iSc;E$rIdpJ`)vrATR_hiL&p6&i{ zG_gA2T^t1=4_OEmOz;RozwJT4%)3@WhBVt~VN{Cf)fGs9P|;MK8syQTdIASN*1=31 z2KWGWXX@r5M7MUBma12ljOlKPJk;r-R~8|=a(j$%>^|iXY8G|~tdAp!$ppsxK}l|k z0Ku&Zd<7=xIyWW`&O?oZ=UpSL7(mq$m@;s@8YO+~+NuLscXvlOlrDkg1|F>bg+3S- zhY~B_=X`WBHse?~JCVzRPAk$(dFkZIOOonwvlB2k>X>>QoAJpJ=f1D7-1ntv#yb4~ z!34`?#kuc~E~Xb_oyum+g2<7kDI+cJc4IA)rYW5$U~yBLT6C#$H&j5u1i;4>NxbYl#A#gJ8pyj2!OxYkapJro?@TVNvB+f1C$2B zZ5MYa;Ad@n)3>aL7esizBtV-1#ChrUZmRD*X1Uxs965``f8(q-(EuGsh)@LKL1>FV zAg8Syj}6TogQVz2BueT%p^lh%&LVN#Xv8tGep57VMIM%0TM~Pi`q1qvUv1MSJCs2$ z!0ioLGV8?RGRo0qgp4*zu^asgd6zJ}iMmunXOjplEDRMl2CI*Qlhh$0H0qi8M((xv zaf+k$&d)Wu9%1@CVw&W6p4#Erf`cW~-=bDDCWd!x8%Gv>CLzzfSM&yu+$DO!_Mho$ z6%Yvq-_631E>E;^a4f)}o3=ZNM|l&nYwg2HdR0St>N^i-L8qG#8oN6~&6SO%g30kW zI`>a6y;=Du|E&#-pmoy9IlTdS=+;r2v_;wV*Sb(Xtx%q{;1=BA5Wct$@=zP0P!)-y zd8!sb6`ee#hDMThs^K(zN;E;z58S);vEMlJeWR66eBu+InEu4~on3=Z`~|gb`WLpf zL!Ezc=#FK>^@kp+UcC6)|NDQlix=l^A=3mT#8Z~_l&I?o2HbKnT0p1K0|58+S8C05RPiYk{%DQPanW7%RmKt#Wx! zG#m&x(aVlug3h7K=vjU|2~zDPcxI$-@#7@BuMje9T)$aQQ-E;x8B3DVF zK10C=LL$GIuSpEP0G7qwED#(($(rN% zfaTg+?tRAl^@DFjtYSEJ@OOdj7!@mk36_E3h}P2ba7MEdGj`7{^-AX)4%ed#=_~pCqp4D2exBWSmA&e@e@m=D>X1{enc#sTb! zia!?&CwjoMz_w={KNPd6>9i#VD9=66R9uCwiwDC^-~YZhI_|N#6i;_st!B38zytrV zi6BmhgTBjNKp8r%trv#T)J!ERNf&@7Me-;JAoDnGB0EiJAz=ndgaZx&W2v7++ny~>`7TRF4M>vxvw3Gd=$AKOOKV4LsY-+g;R^OVB!LfbF zeA|I|-pXvSa}E3%5THv9klw#};DxEM>-3jx76JQly8_Jx&zoL7;3s(K=f_E3(PRaaXtk9|MjUOA0hB({FzP?`*A*#5Bxzg z0|(P>*q?4^8SH1-bQ|`j+jFo~!r`Tskf6;UK*%)^f~Nw5M<-8cJ7s4>z%Mgj$?=hv zuH;#N$l^zvAKH8!I-$ilda4vi1@f`>jB}$DxmBitJ4;#F3f9&h$>!dQH2@3-i5{O; z*L6xTR8T761^-=K;kf&aZNMwNrWO_--6u@0iigpNr^`I!XL(#Fxfl#X#r6*Up14 z@Y}ab@8jIDOUG2v|Cw$x02zNlI{p``KqgVL8V=(|`Ilk`-jxiVRs~F=LYdYtorMa9~Wp>gQ zXBOeE)*{^1bgUKQGPv=thcUVjy@VU)6LkEqDV4};%2)afNafO)Tkr6FrSfD-11Zec zHd*P5CctgLsPc}3RWLsgvGQDc44p=s=ppnNJDdMl=v1RcCL4wYwdaM>j0nYYh8tZW zluK3~6b|Ll(v@K+@ZX#ygCki4p0Z$pl;2^-Q1#_9ASwTsTPZ$lEr5qPV>E0wyWM6pq|6kH zD=UlN{roj>6*K160^bj6E@RlXpY^t&(+?Ofs5aP=O2)8~%94XPqQfrnkba@p-CAh- zrdLwhX)jZ7(dp250oX>RX}IOQ6212=KZ0a+w0A_GJUY$#S)7b8>A}3)M3kf>$ot#> z3E+hP4*Nj>tP{>TCpreu3<91gm4pWd(e^jkgBV8@iP?rH?owxul(J=-cFsa2c z#&KLDi#%^3=2>wST|m!3d*~}#2(^zzOj21VaqMMBzEUJTDkM=o$yS+Su>zqLI5H%6 z8y4Cy?JFvP03N$K`E-P(wu%gQpr9Dxe6`=b#GJf7kt*wFao*2Iu>H4X1n_&pvOsW$ z5Yx6O7~t}VlB*k+#wUmK`;1-gx@B*v@`Dj1a$SK9h}<+V=LdR!-QfG zIhU!v6^Jr&{+(wHOv9OVo)vb}VgGT>kNSlvhVi^u*rOC&-vQri0n~mM`U)vH3353q zxggZgWq?vcm*>!c)D23++V`X&;X;~*L2JZ~^b9q0IVwqz8oHER5EhVvlj;Daf=j6z zG)Uy=dCRrUTh4x1dL!y$)N!G~vCCzSh1wQ9iPrrW)5 z>rU%LEWt&zc>w1&tf;`)!udR|r6U!9b_9L7bMahW9Ez;h{cJ%t@~p3li}fTLc}4eO z6b?0_u7ZsN^i1bm1lhDCpUZcJ9+`EFp0OOqvZPs8Z);cxedWQ?ZQIvj%5{*VBGX17zcja%>8t0^*aLx$tp@d;1 zKJ>dE=6{M-{%lFJuCD3|*0<$27J6A)mg88u5LmP8>&(F+U27GJ9c&dmmoFBFF~fM> z_HCp6ld#EyL{*?=o$fPaG#ZUWR83IBRNba#->)Z}YtsT@MraQwjC1lbp}p+yRc^qX zt1|*&wsQFVgGx(|Wm|K0C5-C^2JaKW0c}}Uqtlqg-5WenC(>=V0FlohrO(A(gBim? zpCL@^1frBiqm)t+p$IcS_}~^JVnie2Q>18pPF$g?(y0Bt`5eZg3^7I`F-8csZA=LB zYYAhBMc0R3Xl0g|s*2%R!|`5BmxIl)0GGq7@MC~jxZ6uxf0V=EH+1WWh8d(Y(~tYx zaxxe-`(X_a%sA};UMty_@%1jZC2 zW^l1;U|{|Ffq_+m8w}xaHZw7i$%a3=G2dfprWOcH6lP`$6M=wcYF1Bv(3#?*f+JP-bl(uc1UAvAXRaM+4G5RD<&hl(ZrOD}t?<3^d`w#CUMv0Ktf^Nha{hcMOC~tEx&0#t6l#9f=sUN1F&4k%+A# zL7Y{G>$u(~*)aY60a+tDkW3F+j8 zkl?KM{XZRUq)p?T_vD-;oNH|n_cxoBOYTQigQ$xQ7ZVNqV{TtNYj*&h-I3(VJ)C(kmT4I za6@KH`B?038LmgCrb6jxK+T#xDXStwN&Ms$6^|fS-pOV8EoTqRy)7_%o(GM^pTy_? z$n_F#C7Z;EC)ZKFJ|2Qs;A4b@3LQns{8-VrpYNrq?o_r*pFrb!ZaF`mYKr$#Kby(- zvQ&2_y-~M#Djn^m*3;g*3T0nAuCZx>xX>tLR1>q#P=sT|kc!B`38jR8Z9tSR-;J^x zc!n^Kgf#IGBJN$!wTh+-g=35{R}@WC6s{xA2||ca#yBCIF-j0Zm@)i&SI%(JnQ(4! zWo?#w=J~yAJOW>W|Nmi`>?FTEZw9b2{~^Mzg3iNu6J7za;JGDNyLC!=7NP@hucb+x$+I z;Mvd^sS3(5T%FBqQX#y($j}glcQr&aTLD%DXSqoumL2csSjDBhWY=?xYw&oeyk)x-}7&Y zpPmEsVFlt2f7=#EotmHKr=du=vz91bg8aN}lgJY%Pw*U~&;x(e%jW zolq+6OqUCWkl|Gc~JFYc@tlwv$QL{y7*D{ z0R!W9%D|XTAmsJhd+Ti{>4!3pS*zP#CX=fRjw^s| zxz&NusOfiVgK61;Hgr(>lD6xwj}34RJmt-g{5OLb=FR*3PrOAM^X8Y{ce_m-MSvmy z|7BbpBQa~|t&ZlFNgQ-)XA+YjPxUz+_nXj8VU3Ph4S#Ck9Ag~4Y$9Y{=VCc3;%BC+ zOIEH!U2~tKh)n-_n1EeysA5Y?NGP3sb@r&ho)SvAeESo|?a^uy+U(jb*IB+E*1`d( zB-|&11UlpMvOt|Twidk9kWM!?U?CJc>ibb!5dt@~?G~-S!5XP2q`tmMVT>r2r4YuH zruygp-XjMv28-}nv=!ZnzK#Kp;d%Tl{x|6$r;~Td_vuc0FI}d;W|AFezvW@Rz+dEl z6&+$qJSo1afRa&Wlna$tl^?1pbx!@LrfD~8uWCQnHGNE9&~Me34aeANe9~+%uQvZE zAC`Z$+N>)BO5ps!&x7aMJ@zlFW~#pKG&pn4heM}_UJUOHzw5TRKdip9W^=7lJ6Zd7 z?LQ;M$S3PI*1g#9%f@2ko!*f5<5i2%v!fqx>S#K<>GRE3#92R4-OV$7nR7Fr z&PKE6=bCe0&0m=RWAESl-tWJ9z!*4t;Om3M!Dk2mI<$W1-C=+D<&m35em;8fm@#(e zxE#N7{G;RFJ!RySe@>iVfWo1|H&?G;y+jvL zbHjf(zPxE>b87SFw_LxqW9w(PJ-YqT9aB5$&Oh#YbMlwFADlY8XK2s&_kMLhKj0mB z`{4D{nd#5ZY@GFGKYi%T(q!r3!@0vh1^|FCl7Qgl`DJS1yRTq%F@gx(FyDqjz~2+( zwe2+m(Cyk7bivbY9I7GHra=d+Z_~krGur}C1*=%_9AgncR0V(xe(hr$f*g*tVUYM& zZ3M>Pe?j`8jlm(jrHw-hmD)6zM7Oo+kU+m{3xJP?e@X>G;$IcY^A&IOjM7s1WXa2U zg~d|I+gdtu+T8SFXD*RRq`u_~<@w6eZn?r@snogC2XFh)l6OXFCcfmIEH72Oh4R8_ zbJL67vDxyxw^XWl)ANVCO1V6D;#ehdv{G4E>PsdMm**?f$2+dXZ29CF=_PIAou$R4 zW94}-pYKYfQr*3&Tvyg>xkP^YW_q7@^*5z;yu5JM;;|z~D_+ZNtG6{dYzrtr8Rnq^ z9*n{nbX%>Iqt2_XKkp#+={v6AzKm~_z2Oy;b+>%isfb#$Vu+=*cBT_Vbc#7y* z?ZpG2s;a8083}r8Fv%V=o<1)JmZ?yZLhqOGCSs*mYnFIt zQXG~dsdlD5>jw%Ye@6kHh|c=gRb736lFVifviIz7kcUUSe>^YgK;2FRn*Izhfs6*| z0aa1^i6+#i1R||;@^T1@fOM9(KnNlXW`GX3npwL$RLI{`)ycet$OGYE|M@4JX8+y@ zMk5VKmNbgvICiX9%5IX4728siY#J{W4)@w?4=6{x*Za^O`e@tF+avS=u|E*!n9}CJ z`FXN({@-(}>fX8)ZiUpHtGlMVx~FHRLrr&0*x9Mgvs#5w+*NMnRRBgvC=t|T0R{#{ z5ZFNbVH*i-yiOpZ8b^!`UT2KodyF%-pZ(YNx8VhPf1Wp;{X4hR_wFlgroWk|nWsKY zGb34+HKy5s0nv;N4k$o^At7-ZAzew>l&wzdO8uLyq;)0PlI?6we)sNg+0K@q?E^#v zwN{Z?JPq_TJj(9AGnJN7zw!@8|9I-@wEvMVaCsO^u6ipnZ#>*C5P zdhKqjsOX}iqV_7MJu9xP;tp!7xQp6qt34@-f=&?mHq9h7@vl|F=}nJVMS2y*nF{V#tsD6~V2; zDXc{5)kud!OEf|_;Q9ZhCihviX@D(Dy)S_wED*gnBk*S=Fk=~%s>mr_yM1hroi^Gk zJGG$FS!F6|T5rlv7jr=iQ&=wm1_uU))Aj$~ueADfGLun5RQ!32`a@O^qs(9oIrcD& z@~G`|?iKEP_X*Di;hC8WX6_Ko;SkK+Aeg)3-us@LzxT}CY3A-^xC025JIIUzNM<$w zGGY;o=;*b|+#P}g;9B1SPyk2@Ao+)g%n~AhR`(1rn9(F7Dw|+L)n^QA{TTk#^Ni?` zJ!flKT7RuttJX^IUs4Z~qPHTn)MATbtNmJ*X7le9MdTLUehNLvwtvTdeP3_ey`+J)*@1f|*m1Y2E!CWflSiTbAW&47IwwR{s;X*nlZhG-6F; zT5Ya>X+Q`|jBQ+`(MWGHROy>vJ6uv#3T9O6ui!>(In zNZwX#;Q?tsv5mg1+aI+jAu1wFyvX@12)bT;c=_Va_4!@jnD&1Fc){2Gcb5LFbZAaB zU;?j_e;lwm)P@Gm;pgzvIhXuCZgijj4M+F8$`aluAt*QByX&4X0-e-7;7EUM-&nyD zFq0kA2deOI*?0c*DU$WWpvU=YgkEEUw(C7mK?%h0Qw1+qbQc>ejan90?Hag zQaEw(ZUv)YxE{=l4Q2 z3a#NCfz$Tnjb{{}nrzaUaRH%}@9+=w)AUbKZO^T%jHeXGyvT83&HaXkA1PniIEo#j zU!?izYM#8VDMp8-XOj;bQr5#cpEz($@#GnUoY*dM@&c~UvL-9EaZ1=Es%syv%%N`BckoeB(W_UTV zEINLLWO#8lddKU=JLjxm0_W_Dw9ENWj`{Gkq{CeZVfp5BRW+>BBaeOS%6XB4O>%3t zZ3uZD98}HREOPYN`mLCUg=&Gj2OBNVk>50KpX1x0pH~RhSzlD%7bz(+qof%DbprE_be-K&xV4&Lp@vPE$E^=hmO?_wxw!|%Nv!N=$4=5!?A z)S!t}zl)$BsxLZtwKc(K?R@m;Yxe(uu;aA)&C3I$&)_P1@+pJmw#s^u5$l{1d=Q4u zhZTBE4%QaN(fXzAePiUksTjs}D$GO9Fu8wZ_&wH~)gdo7>1$A zC4M*lkE?#yt%gv;=k*Lpa8j#8)=>V7ou5gZ&kPpjf#_Mo|7QkU?XbGtqxtwe(8w_M zJ=6qepw-pncGO_(d`RY+BH!Kpl1cx3E1VoRxDwi0tp%~-<>ir=Z-r{5_`+L_n;|AWRHyBOV!pUXOK z%6=4kEWx{LBGrh-0xka}FM_yGzrQ5wtzDkJ=F=2ogDYyDcz0fZZC^`lv;30$EF))Y z1s0B%vaO+G~@XW@sS#zONiS(A^idov0aqJR;{GO3l`L5o>%X{umK-vE*4FmHrOr z<(0>;%yGEBzR1ZozKLv$PyT)&HSmeS0{J7?>FM4jg-;GH`A#^(F)Wwz5MHi6g>qEl z7zb~$AyD(2em%x#ta9A*yt!Gwli9wzubeYx2pP?VkH{B4n)Cdj)~OaG=Zfopf%^W~ zkn>k688iODLC(jvMffIhF!nxGapHr%7o7FWL#g>U7=sY6LVhZP-nr-4nv&23dF-PM$gSh>fYG-ugVDr&~zxM$^fj3-FJrBaP77So+_&BzJ^w!ar(#fw}$Z)pkFJ z8=xKTZtd-vz+J$Mv-}J3?SKy0^qjZ+0o`@)vQ2bM;r`!ireA&uMz4C9%tM^@u!xW! zWzh~@(GA_v13l3Tz0n7K(GUGG00S`ygE0g{F$}{o0wXaBqcH|!F%Fxs1v{_{dvO$} z@DM*tI+NYxFl9|Sv)ODnJIqeA+Z;B>%^4fB^X&q=&@Qrz?Gn4pp0uazxmem*P8ZL` zciCJXm)GTU`P~Y4)4g)9-8=W*eR5yiPxs6HasP9liPlF)qnpvK=uh;QU>wF{e5PRr z=AdDWjuG>*01L4&%djjfvkI%R9ow@5JF*iyvnP9T7{_ruCvgg=avG;|24`{+mvRMH zaXmM1BR6p~w{R=BaXWW#CwK86&+shI@jNf^F<mjseTl1eg3E~zAq zq?L4%UNT5V$styXN)4$c4WyAYmlo1eT1yXEB1>gi-V%9B=1-A7TmJ0%bLVg36Z#ZB zt*`1w`B{FsU+H)F-TtsY=1=*X{*iy|pK4L9pjEV)cGtc-P{->`ou{jGyYAQ1dQLCt zUA?al^__mv@A^L^39(QxR19;&>ToQa3g^R>a3eeqZ=U3QvhT^kC*Ph{cv|ymojkZc zZm=8fM!N-WiQDS-x&!X8yW`%uFYX8Tll$36_E~*?U*EUz{rwO>-Ou-{{93=!@ACWo z5kx{{L`5{jLt-RBQY1r4q(W+>L0Y6kIaELuR7Ew^Lu<4_TeL$rbVm>LL_dtf1Wdzp z%)m^{!fedLLM+8HEXNA0!$xevJ{-b5Jj6SEz-Kske8JBM_!Yn5cl^ibjKSE9!+1={ zL`=e@OvAKH$4t!3?99QO%*A{x%2F)HYOKzBY{I5&$#(3@5uD5!oW})R%%xn$7HO$gX^qxti+1Rkj_agO>9o%1tj_6zF6pxF>9L;Zjl90IhpM4^s2%Es2BC3i5!&`4jD-m>1!ltnSOm*qHLQgV zuoZT~9ykO?;S8LI+wc%xz#H%YFYtj7h=fE)g?I2Cb74)ah4rvLHpV8{4%_2?JdGFd zD&EIO_zYj8HwIz|Mqn%^U=pTcIy&(Ke!=hfk1|nKDo91B6j@ZBDpL)rM@^{}wWm(h zm3mNL>Q94d7>%I`G=*l;Y?@1pXbCN+RkW5i(RMmQC+R$0rR#K?p3+NtLvG|neiTaK z6iLw(OYxLK@8~^!q;I6?H~nRU^KyPJ#wFO~3S6CQa$RoC?YJX%<=#Ayhww-q%hP!l z&*O!>n%DCt-p0H55Fg=Fe2y>h6~4)L_zAz@*X+g~?9Blj!eJc23H+YF@(=#W$jpCq zW}QRl)_HVZolh6kg>`XVQaf}FU01i!J#;VKPY={%^h7;dFW0N}2EA49*GILx4$`qY zUfcSIcDY=JVU#y28a0f1Mk`~yF`-KLvK_j%X~WYtrybq zp1j}dx^Rhe#mcWa?>ZkkA3H;wY0i)BYdC+{Iqa%-1G~RH!k%w0vA5g%?NjzS`+8fQ zA+m_9BD=^T3by_dGv!4)(SBi9(Qk6xLC+BL#5%E2?H2pQ!B2BaoD&zsMR8BKi7*i( z(nLmE_(6!(YiY=wGMCIN^T~p(S5lRe6=kKbS5wxLO=WA@PIi^uWIs7r4v{0}IJsDE zkel1|{`sfm&1E0RXVOgu&m_q-`T38j3xU%v^uA*sz+;cSDwmOg{df&*yc-xi!=YsHNWZ4VZ8~xEt7Bhg9rWIFAbmx zH2DKHJ@!6H}+t6>eShfS~(cEdh6(q^8)YjFE*f*}%OAO$kuJ^aRESPN@o zeQbbDuqn334tNmH;w8L+5Ag}Uz_;j&!5G&1QwIG#`bt!t>QGZ^xwe5cj7HN0nncsv z=9bY)T1)F`D;=R@bgoT48RR)XnKI^mC9xxNaURakMVGC{HMtJA{5t)42#??~Je_Cq zTwcJdcpY!#Exd~lwoiVRFYqP4&bRq7Kj+u{mOa>u{W+-3k7Z|1ZSz}a*4btX=)$_# zE?1pdsn_dG1Kj?u#+BwubtSrDUD3v`t(bB(%T+E{-a;$I3bWj-x7K~@u6512VqG*0 zYpb>1T4Ob^YFgE-5>^f?v*|LG`Poc2W6eO*%Y1G=Fz=d|%}eG*bC0>*+-j~fmzZPC z!Dbi7AIB$0s3XYX>Bz&mLT3u?Ewr=H`1CA2O9(; z9lzpd{D|-I4T!JtDL%&gco%QuO}vg*@iJb-v$!9(aho#$45As031Ki&|+})ks$sP0S ze7AGkoZEz3xh3EhfSbEnZvNzM0Jy&E0j>+U4&d6Z<(jVUYJjV{3gF7FR6WIf^4WoI^N}z1f3Z*oht3j;+{|E!d2WSfBM+hqYOY)mW8PSeX@Bf#q0& z#aWp7n3uU2#$X1Lbo8SaJ?TNgfBeI5{J=MS#V35gOT54{+`(;J!&RKe863wk96>6Q zk%%}%;t&pCFLq-SHex;2VHuWSF&1GyW?}{=VVIT&eKl-3IdZ8z} zq79m(DH@{@8lpaGqPmEP2r8i>Dxe(7q9lr;5V9Zu{_ugi-|oA6=N`L9?!LR{uDT2E zfZOKQxYZ(Vnj0=2A|fIpA{wC)8le#yp%EIP|9@_)V%4mRh1ncyVL^Jx?&uv0u@Eye zD`)wvkkzm(_Q6Lin|0F^P17Vzu}L<`X6ZTAzsMsjLhtAutDui`nJ%*(I!fp0G##T; zbeJ`<0`rhfu_@NWJ_}Q9j}5astdDK6P4KJFfYsqGs3(u z!FJel_JlnVDugPbN=Su_kYQps?W7&IqoH1?5UPX% zE<0p9?0_AxcGkxB*erW5Gzb}?f)xx!?Tb(;6tS3w#bg!XZDdl?vMJT zKGQz2Z+r(^VaxmuTi`qRcD|Oc;g|S&{tmlo>ui^6Y?48JkU8HEu7 z(nXjD$}smUJtT+V0Xin^xsu=DA*CJ9ME(G?9sk61)4)vuHwN4wI6`N#fddq-o&0s} z5EPFQ$VYw(P>@0trU*qTMsZ3|l2VkW3<;8?h{zyKOj*iNo(fc?5|yb!Rko7p*1HXE zqub`TyIpRN+v^Uwqwc)B;4Zp*+=K2R_pp1!J?b8FkGm(`Q|@W^oO|BA;9haBy4T$6 z?rryx`^W?v3{bT z>Sy}3e(wwTVScz@>Ua4)KGUD{r~FlayMM?(>>u+_`e*zL{!Rap|H}X7|FxW!VX;-V z8dl5dk`ths2LSyaNCHW~0TGY}1gHR11PrJK)CaNvjer3_K4AE-Dh!NJMSzj2C@@MD z14gUjz!+5m7^_MG<5VeNyebV$P-TFLDgjJVNno-{0aH{2OjQ}cG?fOXs~DJ}$^tW0 zIbfD556o5-fH|rnFjrLq=BdiSd{qTlu%s$*0oL>L`g_jNR)zfgG7BuJ4iHuw1>n=$Pq}Kg{*?aImlW_oQJ#t ziHndGkhlr|5hU(_)q=#mkTsA@L32QI8{8|9+z$5|BzM5Q3dx;tuS0Sdv>POML%TzA zFSIWt?}Ai;jundSxL9#%cfd2~O6fA}~g5`ub4W9{d1^9;`t^|J-;wtdB_aQM0 zn&*gl(OgB$hqNZK2+|O-C{iI7N9u|7k@q7uKt7Pz5cwctBjkOEjgj{!HbFjs*b0N= zh^>)6Aa+N(f!JTWvjfm!*}))xPaKNe6Ne#xN*s<{i6f8;aU^m_9EChYoPo3paW>Nb z#CaH;O_j<|a@72*DyPt5SIYU63+7x^ zxfa-iax3gfc@XxZJPCVKo`roVZ@|8kw_rcYJFq|HTR4F7GaN|y4GyCG2M5!S(7_?} zQ_xR|L+NJ{98SLw96^5^97%s697TT*98LcK97F#U983Rv2glLBME^35r~gcFBK_BJ z5<`>1$qe1n!6^*g#?b9Jm7!k+r&AY%GpI{-a3*yb>asYCx})G6>Kz-h@l3w@`1z)zmxG;#%rm)EDQub5viZevHSc9rZK3 zPW^)VHQu9s3w%WVPG9y`&i8zp@v(WxFfzl8_>o~|hPf%@G0exXHf3^#bs09HEXuGM z!`_r-8TMs3fU+^eK@2BTHfK1M;cUvD4CgXjMmd1t3Wh5wM=@N(a4qE+hFckKqa4R@ zH^beO6Br&~cz|*ufvA*|h(J7dJi#8{NmiE)VWDd!UtENDv+ z6QW#9Ow^&CLQG6dO1XrXOla#7lcQ}&Oo4JaF(vAO#8e&16~wf}Qj{x+Wz?aqL@Z0J zKzW>4QK&}}D|M`g5i1jGQJyB&AvT}ORQI6>o;Zn;#K}TEhd2fGT;f!epNZ3S*<`0PhvgUI zOyVrcuf#dTxs>0D^N9;7|0XUH>g2@5D7O)pbS(cPt{|?W{GYgnxb{ZHRb0mwP!}hz zC+?z-K-@#zM;(WFhIorQKJgCmE_FKMBjPLS48%8TsdEzF5#JvRRq+ED;UZ#CT3vyI)bm|)9 z7;35Okz`?wrPC`yWU7ws%DA$lvb*P(=(~#3q zHzlW6hw?Ky135Ew3vw301mvu!+mN#@e{6v{%-PAgsN0hBkPA|GBo`(ZqwY>FNiH>q z-b5}#u0-9JTzMI@9!#!6u0=hBT!-9%dNjGAI%?!b7xf169--bs-rKP)PToh}Pra3VgnaxMW{iA>e3g1P`6l@m^-=N@P#-5h z)$Z&wP@f<_*L7%az97G%K1qH<{(<@o`4{qU)R)O$Kz)t;Rh#zT$ls~2lmB81F(LVH z@_*Df>F9;}E`0?0$kg}gqtQpFeoUX3J{9#-`n2>pj~nNx&re^7`Vaad^hK$iz6^a; z>QD65=urPlUz5HT^?&qr)Y2M#UHXGWsm z&(UV5Kd&*{9Q2pzZ_(zYze9hYwjlijb!dCjKc{~|Ta-QsZE^Y^(Uze1;rEDz=>0Ti zEKKh=dOczhdW~L3EJp87A(o)`-Z*J=09s1^>9!))lSf6^T z5F1jjYKe`g4^tmG9;@nOcqn31>Qf>%qdw;mn^Rw+zCvt4eN7^^rhX)18|tSbwxxb0 zVmsegia3;9 zL&Ra^x*g(hazk>Xy2SzVF!FHXT=Gcr7~*{L1U4`(CQl+yCN3dQCC?--BhMz!A+9Db zmWXS~%XIsqn-Fmwd533QPu@%3PuxI0L_U0`xKBPxK2F?9K8c7s$frHxPVy!4W#TUK z4Q*iDL%vOZK-^1yOnyQgmyV)ZQ7NzYbfi} zuBY8b*_d_*?M}+pw7Y5dQnsbtPkV^61MLyoQ`i-#_A=!F+N-qJDF@Ns zAyE#cy)VjPv=2SX;k1uwpHPmVeI`+kqJ1gK(X?+wIfnLwD96%%_9(~E{-OO#Ii9Yc zL^+YJh;kC$C{a$P8$*;+=*AP}RJut-IgM_z4J@bAO-VNu8BljlvA#Nx4Bljon zBoESnaW{Dgc{p(oc@%jJ@c?6J`Jjjw$cH`RMe;H7apEQNX^D7+d`ZNs#Yov(R$u~s2LB1v8P4b-q z<1O+7@B9N;$8AfiFlvlaW{FV5a{7WJ}BmZfL&&mH!;rWO! zsNtyLiLa;;=@8#gqf=v?7fEX@M0`(8DB=fdQjhqNnwpx1_=%cM1I91ZjMPlTuhe|h z0>tms!V*!`Vj?=Vl!!m66+GfEY87fV;%{nAz1#SYT9;an_@CO4+QX36k1Jq&E;j|H{BdMclBU8uY(MF?AqfV!d zPMs;y#-z^oXk$?qQ5Vz3rY_Z|pp8piPF+bGkGh7sjy55619c;966y}>PTFMDJ=DFl zDX0hJ(Wasvq8_GAO+6~lHZAoy^#pA?>KW?Ua|wOwdFn;lOw`MWHY@dNhc+Aa7WMAA z)jst(^(Ad?>U%_+kNU}@%}@PG{YG1W`a_~EM3qNdnEH$Qo3;pjpgh`Q^hO^-Tbw?u z#DKHt!wDzRhu5%Z1U%YO^pWTz)0U=>Dv!1-eGK}TwB_hyOSBc}6L_>0=~L0CrL9Dt zNusSrUr@Bw>C1|?27P(a)}*g2+FJD0L|dD_p=j&SHxq4L`VOM4N8e`y+xqnV=m*d? zn1`TrIFycnVi}ZMX?b|?~7s+ zl)e_eGL(K5#bzk|k75h@{ZQinLZO^K;9wFeLW;5|R3_FNg(}4Qpiq@qUlghlR~d!+ z#GQ{qbK<+A(1utEg|@^p3hhZzpwNL7Wl-o$ig74(C;kBxdJuOD3Vld135AJ7|DZ61 z*dZuPHMd&_DaNC)fmkCHb`d`ag}uZ-j>3M#dZBPQ(Z?tpLENh-oJri3D4a|DeJGqy z{0bB~GzWhkCP{9+W(B1IDv&nG$_#S08~A%?pM3v-u4TrU)_B<=|muO)gI#p{V5 zh2l-bUytH#q?m%@9R!c!oqE6gBt8gnlTdt!xaufALRu zXB3|z?l=^mCT<;y&lv7Sh@Xn$OGNune3|%~D852meH33M{y!AoBDx;McZh!*#gB-6 zkK)I~Pe$<*qJL5RloS(D{EWD(QT&_~?NR)KIFI5t#2<&^Z^S)|;_t+-LNODajp9C{ z-%d(xXvgYr}Z^SCqVpIlujhJ45gEZdj+Ml zNiiCwbBHg8(pB2dlXNx2FGA@$q8m}Vp19{xxyNS^HUnixY&goE*Z`D`*g%vEVuMgFLu?4j<%xd?l2%bazkPxP;NwQ6v~Z>jYPQ_v8pJyAhs6eHpF_O z+>zK`lsggYhjM3PYf$b&YzN9ciSe;{HJS1fru+ zKAE_8P(F>=I+RZ*ZV1Zf5O*`m=Mp;x<@1QU3FQmOdq|Q39=@Jnejo7g%>=6gJbVj* z{14#aI|$}$0T16pFunnJ_+A43GvML-2*#TL58qEPZNS415ugA({0M>kE8yYB2-Z!2 zho2yr0`Tzj1ZxO*_(g)T33&L21fvgl_%{UdBf!JIBbW`q!+#){p9MVpCjx#4@bF6n z@_4|*uMqGxfQR29n2!NG{4T+KG~nS63DyeW;V%g0>jCdN(7rOc>r0s1eHjaLU%`y~ zDrVf*AQ&$KyzA>`+&3`ezKI$4EzG!YWAWT~PW7UQi8b$cyxkbjsTC&5X=tX(OH5u2Y7UjVATPS&J&Csz@z&RjAsHK-AJ&u0FQ1W zSOMVCg9+AIz@y(J7=HtJbcFz~1Uz~j!Tf)~qjwO@uLB-^j9`udk3LB-Zv{O1TLO6% z;L!oWd?Db`mn<*b*CCiwz@u*w$YTJHzD+RR3V8Hgf^h}#=z9e6JiwzL5Xch&kA6rn zJ;0+M6Yv{=M?WEuZNQ@=g7NQwN53GL-vd1QB?0aQcr+szzXbgGZ3N?Xz@I;hK(+vX zekZ~FJHVg6j$j&qKmP>5_!i*L|4^?F{Stx&a6H8^PQd^Q9w_Jn>|q`x{hU@wK&v#V zx@A?A)sjwh64=zHOIe=f!RWyc&OiF#2aO-}p#Dc6^x)aoQOF3}kraqRMze!R3f#VY zdG^!o%a^B@kzbx(zU)U)hW8^^f|I-VoTTHsj}PEFoInU|fN+o(S=ym?97KukZ&u|n zFv_aTv)rb3qLUG0QZi1KS&|ESDi)-dqrAg z`QI=b#}Zu?*yFMdEP63cMpahBK~?rAk}+anPf&*#o+~$ciwkI&#G5M&)X7|=CAaOM zR*&Yk{uF~4*5_kxnU>8x5yY`yH0p#HhFdQTJvSF=A*dCG^(bLX;*>GDjE>>CQcy}9 zD!8vKn<|c$<8V3OXg9;qb&Ms+7-KRuC}lS1f}55_ZQHOtujX4eRUX<76>`4OUI;_i zF;dAGoOr(yaE#+R+jkuR%V9Cdv$R9ej_Yj(X_jZyrdiUA9@*by%oW0&P3`k>bQiYT z?H0Z?gHQH)R|uD}+4MV$BeopYYT@$Biep&p_OgqP4{(eV*ezcxn>w`>mSoEYK3VOq zRplPqXRES>Mddw}j9u~sr_?a`Ezk4}!*;mvE|p95={)p8w{XLtbP${{Chs^$4TF1v zJLfM@F1TkH_PN`e!0_#7zqiv1jUG1)dhU+AT?4^C{vmz{ufPca>t;*%4N24bUeqKXiuGXC6c9~+GmZ1Kk)ZGVo*nLermLr>HfamXN2RBF?Zw$&OKt#*6e>W$-Y`} z|8m=nZ~`X*!j8gbT^kPeFbfKHZ!aJy z{}^IMgSv-m4#LCsDSMT5(hN1t3xlSiZa9cWT0fd99D0i?g)~^Gp zP18=EZD1#oO>_%&(l4v+DWqNTIs;?!hcTn-#d`#&N(HbdEZtP zAx2T_36W>tC>a|wJ_&}gx)t+mdppZ9@S7&*oLZ*I&3Y0|x@Q=xTN#sWEc+(d00_&x zXx+EZu&bzb4qkQ>)9Uk!52J*LbJ4oW6!=U~(>Cyx(28E+As+m>na*$;iCE=D=X z#hQJPFPEb$Ywwcb>*nU*P^Y$J*Qb{<@%H$ZpNrhsnyU+Ihw0UC?C@&kGO~o1=Sa0G zU;5eMt9f>o6F=PJ>O<}9?n|)q)l;EOqd1CtMXzXK9kM*jt0ba@JbEnPxb_74AFhx>J}oBW|=v=wr<_?JM%h4 zi=JPWX&-UDUhdjo(ut)nYFYOdoPIGl5C81|q1MaB9+%J*kuZWuNdoa05t zBp7+|W-T}k4+Ow0FS0zfhlf#{+Wm|c(sK^-EHA5~beU&kS@nBGudvJ;_O`HBq`h%g zBZNt|4tu!VE!$;x88Mxc5|_K>sO&DIlyfPL{YI^ZSgSQgjtRfrek~EM#TdqomN2I_u4>am-RXPVyoxtL`U>;@S~R zx(+!i+_?vySv}eP{)Wc#os0|5MRZ081$)iJT3L%R>g4-eM8>%A?%4;w2LOCN zJ|F4;sA*wSfurUB$-JB8Wz{Vco#1mV)8wjzqMY;DG(Ze+#5t$(93bq_!j7Xv_m?)SayZDdbSdu8!awS@%@p_B=&G}c zUo%dIo2Ea(er+4?9@~}7;sZBe|6~k~|U$|KPY7475?$)*5$pz)tX4}=~i*x=` zh27QcgbVXM%(ex>Y19@Lmdt8v8Fd@8>G440lmJfQo-ID0ciA zyE~Y7+r78ZQ-wsA79?cKR?p~`ka5S4@+GTv6B2J(RAxVzs$D15@ zdAktA2g(QXy=rH5l|OgxFLou864}*PKUg1YJaE@VWG)x(+8*w6h#D@ZS4rdfq)Z;T zwM`WCjY-9Xa9u>Uf;?~T1VGo?RtVZGF9FX|6y;uaamiz>IEFiqf-s$`D;`Hv;#pLs zdAcTwCp}qVho_Vr*yJh(x`_$M4Jpbh2=~UvV^m7>%#DR(J_xLbJ#jNBbR00WXn+rG zajsETWfcsnvX5=an0$ja1A-P3)ygqR3r%B4<8+kE?tv1|Z&5_TIZs!pjN~_|uwt&u zh5Ncwb9$3AFLAi=JYqP%`&;;=Enagc8=o#SHoN=QMEfV7n|cOu94@@S{ylsWx5p9J z<25aD5T=^#rz&U zmT!N%)Oxe-@)9=)+?S}Sm&P|)wY%@GSx&N87NGtMDgR>j?Vg7dM~7Xc^=l;utKF`SW8LkproQIrjym=R(`%y(_B~W=G`r~_)7OG8Q$kI z^gKguAC`!29y%1MlSm%%_zmf85a<3Y^*JOfCw4Dgx^$_NXhfZKd`5X{56dszmVd*o z!E$%)F3f&K$j^$e@-H9yCq#q>y7zjP6}IYR~N#$dSsadMF8PD@dRavQ5cD> zcAfvu?Y7*mE_ku#g4%}ZdBTk%*RrK@_5=4u8LfDo` z4790D9Qo-$%NivKN6T!vtDv@F7$#}893?9^=D@8G`j9}2hZs7J5E|G{ADK+tMTrnu zp?v$5Zd^O`3OPTq)N@?jmW&}tww4M6TdQ9xAAs@@yM$oAc;K5x$M`0mHB=5aP;u{r z8aB;xBdf|P;+)$rd=W_Av*FF{=x1M&BlO)bfPgB^xu4S25m`>>Z?&oODu9%i3@QtQriP?YQ zvPRUi|G`XHmT7TM%*HW^F`0cUCNYkM$1T7d1&rWCdHm~cb_>ZuxTG64u)^D%fXy0$ z-F`b0!u1fHKllTKp!b!y*Z<%T@X1!8=Ud+5Av#P5@4fHa{$p=_tNZru?)%<59%CwT zF^ar5u9o>2J8V0GbJrlwYgdZzYaL>^oD1jjWqg@y5XTXm|NAT5$hY^(f9G5{4l&%% zeGdA{#etTIj25a0WEMvebNK8#zie^wY#K3#WBkjY(A7cc&pz%uGeM~EUX00qkYo)Y zfMe*u4FF-BmCNXLUOgBX&^#jS+)N5pY{e0&+0e$!Y=p6kyrK>g#R8*YvAv5l&$Zwg9?p zr<^9It%YG425of9Wb{}DZFwd+^mXnbw5dz=zd=8hHZ+Eb2)eaLE2|G zdf{gxDX<+gs{Y{`R3n8KS2w#`6Qb`k7!aa0!B%T9IOAxZ^xxVhx?8VfiInWc zrErlP;rkblA+Fqx8I>bf2zMkMC&r5>_A_sv_u~ba^lP;c`}1$VgzZ-EZHT1X-#xKH z$jXV`ewX0ed#yIk_EE@aiT4$v>hq;Y3LGKd+GlovVd#$-8~I^~{|eHd(e2|YY0J>V zwOP#4C7bHwkEMN^=?Sk&SuD~eeRJPG|I|N^Ac#)S=w7`I;NS(qKzpBpEjSDJh6lo9 z;Wr~Fr9cPPw(3MDCyJ1lw-n#J+_0>Y4jq=2PU0+05@BBL<0DxF~M)R3ME zGB-1*%6@O^y9IQywYu6`q<`t0$G&PK6f&BP+6n`OYTt2CDRi8Rs1&N;l0sApQC+&+ zLE-El*5|aITYs09yZfqefUxYE-4zGc?V2UU4GyrDm zp^0aZXVh-twa9%HdW9X^G>Yvaild^5TiEGuCPf~_)K2ss>g2U;v%gs-n#(v}<=yvm zUX^*KFw3hlH}Yb$ultoE5WJqYX_kkbJhG~6{C(^d<*=*>G821d~ z+K%s-(nA9gEi-N^W!S#&#ipTr+d?uIhRA-{s3g>PEaf;0#P*dzY`Z3?5U$HSRK7ud zpGT&l{;=nG!uOYw9<4U@qGp$TL>F-sp+2=0V=sR}AVs~Sf8}_>?~MoZYatoKFB(dr zVHiSLj_UmtSc;2jZ#_UIk7e}Id8!?={vq?FqU(ogbwPn_n@h`M)SpV{l z>xy%6br&N3fs(qw1UUXfJl=oJ7sH+KS~k*)z31Dm_MM92UJ(~@(dp}?$n9@|-VRS_ zSXRR=V(z_3ERvkeQyiH0+kZVND}1LJX~$Hm7T9haQERC|Wf-_lBa$_r`^qpRb7R*I zIa&mf*mNOB6`76}LEALr*fG0JHwdKkq=7`lE{!~y5=Td->GDWHu`&$PE77vJY&vmVbEnzIENiu)I9H7; z?~Xx@Xu=B?G~g<@01yUDyoA(DE3&kKq~LNiNo}f={!+iJvOGO%43~z--W%vr447}^ zVb5$z;W$z>Z{pM;%k#(JZ=mA{>2LC=hp@OqQTO)UGe9Au)_ut}KbrWWrzm9@N{J5T zmi3U)=<|jl9OuAJz=Iuq=~5&GP90s)<2vmloWcU!lhJu8OlPRha9s`0{p(m(MdW9J z!ZgdvYKQODSB;K(?um%ryG=Itu-h*>MXxxxN)w{5BD$wZ^74357{qmbU;gH83Q_qF z16idBmZVW=j16Hd{Ct4u(N3WdzwTWyevA=>Fy&DTjj% zV@Zs$)6NFThJ!uA_6EQlHG2L+OuSeQ~r>#w)-k3GlfN#Zy}zO0Q0IXg@llmDO))rWYHy82KYLr>uS{9`9-F}KxnGsL|o zbKe)-+a`#J{t4&t50bIjr(XrVvfRJF<1_3zPH%RwX99r6)9-iyDfHms@I?3x_!I!9 zT=_{7X44gw0icf?412|JP?dXFrW=t$n-q4IJX5%Z=!ELV;(J$Rp3#KbbO(!m*x(hs zHUPFr1dH?Yr%nrbiUl#h4|SP!HLFG9 z{OVlz;{n&^VnMLUYLVDzplOMA!fKJ&qk{9(xz?l+1W5A!hcqGjp&p|598HLRj)&;I zUK65Uzx{%DJnWQb7!+N&y^kJWkzlpknPt0AjybFS z`aO#Y@v2>Wl17AVM%=B9Hkh*#306xy!=T;zN`A@_g2r)^v1V4gw&yJ$Zc@QH#<1TQ zr?3h40tho|C+OC8$~KVZRhejoxdD=a;ws6I3VF3iejzUIHd8i6Q$&5gSp+qwK zotnq-v)m8WtjGOOJt+{+uEnRRD_sArC}cE_q(HKv;_KtH)Sr9wd&iL!c%wvA%u^Ck zA!3&Z5dzp_ezWB;glpjG6R6CS;nlM*F9=1yGzlu=53!rle%k>h64vne^Fk`Va}BgU^J72%((7)NmG`)- z*Az~1ZT}zsD3wRjd^x}Xu7Uf)1L2YIBzPt~2cYT%ko1FM3yY9lMNDm~Tj+<|(%7bH z0m&h;j4?SvSe%(u%ZiGxxPe=VggRr>ET_kLKxFu#oV&iNq`^Q;qma=> zqE{x8G4xL%ng@L;jNtfy>bHwSNAs;7ZKOjFGT*3tB{MfG-?r{O$>M9 zXu_Dhz8>w~TlMwi67n_=-1@+~9+=a&ZWWl?zGIxiDqIKmg9pQ-0lIX}Z>1J)7Mq1m zQcdj~v#M<2GpKjV%6blc$S-Y4F+UgZcM_*-!deZi9I~Gy$)^BGv z!g<^=YDmJXS9wR^CGvzZ>Cxb%MDOymI)>LFs)d16KF5Dhq0d*=1p5~4$88sPqfv{9 z*=doVGE&sN6)8LmD;n=Z3TNT|0M(#MnBrpSsGb?pnwz9HP5MQjMv`Ljjkl@2io>$% z6=lD2g@@bhf9>(i>bUzW3&ZmK6#M~X_zz3TRe$D2uUClbW$te&MD={6eh}Y@N`yT* z4Lc8(T5p!YWOy^zzGFL0CDC*RYLlLFFr<-~Nbqg8m$)!H*@kQN=jxk=U-k*Q^ zVh(J__U&u11=j=gMTO-C!8=^V?`?UOX4GOFsJ`Evvs6}PmG`?PTw;l(s38R~KA*G& zV~Bic?8iRI^*nEO>@p_Xv@CUoyq44M^=P?-I_ZoG){|7jq(zB2dl<(3VVZOH5ypFcPFi7= z&?=(-@V~J8Dfh}^4O&l45d6u@(`k>dQfR9riu0*ZwE_CWOk$j2{6J?3sGpqu=anl_ zb8Uhu0rONZSzwRlVhV1CSHXY7SK)hk%3qVE9SX*=q8TLD;G2m~q9#_?-Pyn_*HxbQ zu49ISVcG8$B_*S^lXvpLa8Q>c&0=608pDNrXjLK4G)oXE3RS6jgcr0?JJE`eOvB;e znejG(1 zkJDxpq6`AA)J=<0{^(RH&{6vtHeO$+@JOg`^>a9Xj%4gl1QIsaj6!S%i>*9cTU#T9 zX7xH6HK%R_TFWSu1fdJY@v(lMsKQ8o`6W7ru*f0a@P(Rz^?F7LSzBAn^44OofEY&2 zIYv;(FuFUWXMc8v$|rT_y*A~+?SohlA5Y1Xa0(pOVH+L{x3Q=VFiDz<4qKreM==(3 z5Dhum?iom=$Q>wJ?hwb1smkGC18LkJwst9hJs;2YIJIqm@RH%X(sP0kVsO3cz_)Gs_{_aar@OZA+gU3N zO+wVnvNA;?=9VHg_(K$HwUp5iA>cuQdX)-7tTAyjP5de(4T)Qej?b6Tfo8nA4RLcg zN--{1tAbFimQR1()WW7)@H;KRUUS%ekI!3hQ`f|v>3JusD60{Q#|P}9-uQ}GOThFPiCeI z%?@~=Sso>{PpVo}Nk{R<6qlka)^rcR<6l} zcCdqOJO7b;LLXy4oX2vl4W{Ezs?(MW^}q?grzT*Ug@dx=0=Q23pkSKt)T#LD9N*8v zj!$X{HifTegVXP6Uio^0ZAX)2S<)Q)^Wfhsa>L-oLb=Qd;pK7xiO9J88*u5FHE&1v zqt{f%RUP^8Jljvn3}o9Zl;TJ#yki;u;3seU(97guyioa^HR1 zY4mKgT!n5y2$(s{fYHe}bSbN-ut~^q%?~^uYE?c3;Kb)_aE7(OZ3MzNT#$!%&%iKq zJs)GgN1-*(KVXMdc7fuvb8@Bu*=(M1iNPwi^HRYxo}T4$rh(mz&|K7C$WcV+7{kov zX7gFdOcwkLg?z^!nnxXU6M9%wVzbq@WS3mH)%-0&yp8xwMn5mD!6*Gvi@2pjkGX{$TE}-0+uQLk(0FKvm&!CIGc95%h*{aNse+OE$)KW@9fFK?7QSSDmk| z?Vj7*yj3OZ=gefokCgf`9lsCU@W)@U(y}^M0n~-RTQmliNNrrlz>NUA=90>>ZDVYk zVX!1gSOb(RwN7A$YN*qLZXj<9hmjW#BQLI5`T22;7!cDWo}&#Z8b(BZOuCU_j4|G5 znoY6}oHP}cT*(Xb#|vDJ@QwEwL{&^E&Z;ur0m;M!6JKHHTQA(ZitDOks_eBcfUA|h za*fYaQ&DHrDK%r$k!0cZ{~LDD5S=b=fx`*CxaliO{eV&4>_RiDA%{&r8Kkii&KZ)Z z9`~E^a89oc(l`PJU-h$u$jr8WMV0fdb}rv0WYILd4cVOklqm;mrwOt6L!xEXdX^*0 zgmmU?bBzyw2PD0P$E~*lfOPLqP*wscl>p>Mqj9UQFD!iz$o17iLHg;pO91dz*7KG-VWni61~6t9j(kJM>tNeOJr{ZHxjX0(I*o3^fxTqV3!*ypJwjs< z7%P5Y0?}bD6K`8{S6T`IR8*ges z=>mOiq@yj!DP%mlAswamk=`;um`-FJ8Jgkf09F#dwkd?wirf`5i^+LiZ}Lm8tTlB# zFEgg-n!?IqRXF3HJ}k?Mt|^N0;aL4SYJ5()QVoT@;*f}8L^;WEgOjg@!pm`gCVBCh znU~aS37~Bs{|z&A1$t_65@c}FdSZ1N2czbOUoDr$LF37#(>eQj@NsM!MRk-d0*wD! z(W^1f=)}9`mks|xndfc;Zn#QaemZFPTg(&xX28OkUm~D$my+}!eosG9KKL+a{QoXZ zEWLXd-HaYazkptOhacs=lLjhXuwzJy$cM<2IQz+GCfC}CZOVuw9U)aEPpKgeN)~s1 zY+wBmll-7>(;wdty!E!ue2fF%wl90nejKkE>x3nBc1bP_SPu-$h!hFn4z=i zZuBaI(%271DJ3>Z%W4;zTS7W=)a&;s87Xpby?AWc>)X>aW$YTThQc7_ql>eyk&Xf> z^mAe5R<+fA;pxwfMj-*}F*+5U^V zR)1=ZsooQb`zp89+iNrN)@!DjxNifF#i?nl>DQPqr=EP^=Q=Yi#O z2`3BPZXuNsyA`1<8-^^)%rIp}scBNmWYb{sB``wS$Jm!6?q7<}e5S7HFZ#CJ&Tj*B z?!G9~KQ=Ko=Wo&{>+{X0rOm12SKQ+HkcCf)!-=zVcR!6dz=k zwEa-gb!C#D@15Qn7Ue2TzPNFQvoALm3Ut%(MaCxWTDanmccSpeSFEfunZGnrXCAB| zZSg~?k1%%zQ*;8IMpvscqzyT&G#!kUYA{T)bo}A6L6inG38Hk)JWt-a^+^kQ@@uV} zr|MAfb8{9GeN%V3^%Ty+@YW~veO>Fp`}yOQzJF zM$V=3cQVGqUM~xT%w)Y@7Dh!>=tA!ms;c?SqW^@gX(AfqA*}W)`MgnT^r~SLa%M6f zkH=XAnQVl($FycJL#NS=d7=Wn+E9jsET>$yjRDgH-er`I2AlP`xh1L+c*DCB6f(AW zLFA3e_KwN?$oB!Z&HlE4Wm%P^*Sq<*J^u$T%bF;BjWOOY)RxgkJ_%y8bTbLlVeC?Q z`RAVZ{1wsxg-fUv%}r{^eya9d>>&$ZA9C!RA6BaQ4ZiWq{?SZs6s27gP+CX}ap> z^RB9Ei~?2{y4hl}08l6{k`8x?^knt)>D5G!#=S0bk?ESMVk{fL%ZW{nx0q&4!!+sIkwBH7$=Q%=+jHsj1zrnNC~ z$2oCWMjUzCp_hsbNVcIK1;f>tCh;gQ%5OV8*@j38Ozb~|*-h~5ZSbrhWc>US-@9$Z z?f-qzwt>L2Ct)^6SNDEXcov?WWPx>l0?)#;9Gxk6c6tDp1qZp^Gc%Z?H)o0w5Y%)j ziAW?4nG+N|#?}xRbYu|tp6}&EgKhY$N3PPY>1sMkMpf`PVT2Jes^P`m*5P{G95!)q z)r8CcvDJlU^a};c0_I@A7_d+^ZK+^cV1p!q4VG1qY%|0d0|sm>U|EHN4TOM`U-l1R z@a#{@{uEY)$#b#8z_JQ?=Y4IMu2{^n$PFq~g&&SpC*6?%VUxt^nlnUqqlXcaa5&h< zmB0_~<>9ayb*2zP;70aKo)hu&8c}^j7eLJsfgBl^K(ZvH0SiMR?RH;6Ap@OjyRy8i zaUHY(Yrd|hJRYO#J}fjwX0FDgH~-Nh*WWUz#CE8v(j6vIqXo8I{0wuQytc;0@{Y?s zGioeA3wW6;w6_wK*!m(ODcPVUI5*Um%R4TM`<2|d0namT7mILe z%9*F2SZvQP$*z?0P0nLhI{%2_8zJzi0km>tpw0NQK#9&(MO2Tr7s0lx_Q*pz=X8ea9mFceGShU`v`24IN(4RaJc3W7$ZOlS_?e;ndivRY@rQsxQ(q)33S$P>uctXsWI;fI};| zmY#?U54>D$HOdw1@Bb6T|1A*hs+lQF(G?8xv~Q)Q2p=^QG|KcAjM4$*sz&0qm8ec_ zK)`R1Osj7%DLQlVi6~p=w#C(M278n!nx@NCz^dXGZA;cTW1T_)goxf`Ee4<{y3SIy zwU;zz9u_GTu&QagObkjH<0-q|ZN>vWg34k{IlVky|;N&SJi1rbbo)p`2{uM=L$osd*CJh91m%#}yYQg8<8r z0Xgug+L0I}pyo=W5|BgOluR1VDyDwS;6>FbslwYcDoDyikp!Bl>IuI{I$WFPlQE4# z?V=8$%L5%xI_8YcMrJ}n#KxhjKFK!_zLHUefuhh%QC{zf*=!1Zf$GUgXSwFM%!J@E z@oS3m3>?2|&0vNC)Q=xiQ3%o7jcazHxdm}El?E!m6gMViOjdB#0dng^bbkOuH4hi^ zs$x966~|x;IMcsqDDd-ft>!w8TdT!Gcebf0s@iC%s-i3`d=25HyX5=gyjAaZkF*ys z^c3@&11I=h$Enww&3fH&e)V~H9kgy!>&Rcfc5Kki3bbtZI z#?Ca_86j-Wo|(U&HK{H-TmDqnO8}!hh8_ydFVWX^&QOB3(D7|df)vmn?PI_Oekj;= z&pQ|Oh?SUkiS521pV>^7Fm*NHanzb{r#R!?)rZeuy7D7vP! z)rd3pHl(2AN4M4UITnK=45*o)4Rk^q{49iewK~;$q8Mgz6HdLxHY9@~p#J-GU`V4h zp(KiHevlDeAEnqd0vTVvdc|Z+XbFs&;~XuVcQ_}-s;>CQW*KMx!ENS)9+v(cCR!MH z6vA^ckJpDWzQ-8@RWqlCu0e2KQLHm8V2tl_#^fJmo6?1N9s9fLH~Z$>Qjmvs(RuV3 zLLeRjNkNK+X&PTOkWk85F=DTYh8jTVD6Z{TK}qmqxwQaxx2)x!kg7U+F#CSXdV|c^ zW%bfMuhAck+V^s9SwmH2>~_vA>vmOT>~)-5*6YR}3yY0*!*w}cv_)2F$n=jTxFAQ= z1qSWujH>VR#+<)_ilS=JzZM_wG!}v~rSZL%#rcpiRlVJ^IKQ1SRehahasImTw>|%$ z50%Bm-rZodmT!eWVtP-d9}UhGUgP{P2Q8y51*tOv$)|CGuckZ~#*ve&NSz5tgM-_; zuBV_NAtjld)b_tpc*qtMsVAx~plI|ndZz|NARw}7AJf@FD4``x1Z~2szBAK|n;opk zR?N(9rMku`eAls{)M_gqP=4F#z#=r*%{wmp*euPDG1tjw&xw;Kex@noppe=jFLfcJ!O6ENdsow z*o^9BfJlEy9;!4xBzkmh&)MgU$+9fB=Oq?PB}JgJuB(cmMr~}&(XoPIp)tY}IsVvb zbum<`12nE$1E}6}xxO1MtmvvD2$b^Wvg%snPJlZXK6JdXJyLk2N9Qv+41i zby4N1Vi`DZz8^j`gpKvkhp8GcO#HshwoZs3s+t~{@uh=zP0>%DP#67(Dyo{U6C%(T zx2N>+c*1D?ty+uO^?RDlyh^32RgQa|Zr<5avs>3J&KMMmsz|lPVgbY?FdWb(P;PiloFx&umTy|yw{;NYBVwF^Rc0u$6iL$#?r?m-CZ29S<`MNL-04_sc5voF!X z^)56cA#TRaEeOa=H|QuGs>DTvHtcQi0dz0`+Krg<)dAUJy^$|7b0=pS0J(hok>T;< z!$;cr9Drfwc1%{zHS3FCE$0mbXbU3va5;h&1>i=0{uwY140xc;k8{8#-ZKE3yau97Ok8#I7+4qB})Y9KPOe8M~ zXK%{xOzQ-eIH|g zzE|!iH3rJXiq&)_NT>|6>AKW(y~BIU)Xll)e2o1|Nxd$~u+4R=mty&U^9XIA!$>-a zHKoA@v&?AY{_zgPPU3YT8V0ZDz7IErHSWD@{AJdE;tPiHkU`M>&pcGFF$Heeu}OHl`Hv3LPV2-!Xcb!j-TD#{SsZYT;eL?}QO4Qm4$ z79~s|bP6`gcf%-IadlnST}t&6Cm(s_s&v}SnXN0 z4RDW&meG(?Pks)&rNc>C{yKNW!ZqF%b#FFB!wjOC%NZMQb~>7{6$cWuPG@uSmtPr& zxt#iyuc*0PIF5YzkrvoZMOpl8LgOpFehlYSNFdD$=z=Cldk~0^2us< z_mgow_Ai-dM1)q#JF+5ZJp|$+qB^IVJMvHnITs)PZb#P9~K9GTfbXxYN-mnYAuUB3|E3 zFT5LJ2>PNdb#~XWD9P&?&wT479Vc4a+)Dl~D-fyI+XNW940GUbd-87AJ?hJr8crx{{q)Hvpm^d9VY zDEf#q-u&M=+Vm9C;}5Q}&~O-k5l`R(lF^Om4)g#sS*j|e`iW~B+`!LPQwgf0J%}8r zg=vefHcFSR)rJj;1}%s-NZNZ&7^7lPU==`S%cIU06*=$(S?tq^_x= zK#2aXPuG{NE-Z+kFs?J1j7C?FIb*@RsrI|{7jCC65TfgvD#qoX*ZXm(*N3vsWMB%H zuTB!o7=Mw@KBQLO)rb)hh?sJ-j26-He#*e<>q(HL{~LI{T`g>|A<(QPHopi(a@hw2 z8ILwI^T5o^@^@S3*>pMYCbss+1RzlOWyN0##t?oQhT(Urr<9#%-t@gx8)V^oA2R@q z$F!QwQ>tlkSruw(H&;-Cu8d~qP!&9da}XN>@%mieLJG`^uTfN|3}~DN(lTpem~cD) z+?1py$C8Bk&@Nx&JmRinz#BhZ?fk9q#Rqc_=H`(suX%zj3*I`V>+dICgYlHy53l;W zwJAx>`P6V6BjOz1XgDs9IG?{2)*j3~n5(}%Ls?$)E~9rl|F@=~hxI=ZOn@d`o99-6 zHX}anVeSKp+mOukw7SrY(+?zC7f^}a?k>Rp=G?N*J-Dgg(NwiuR;p4})l{`qQYuoY zs+w9VDJPi1FXe<*j++&gF}1!_)yvJetTCq5mqM*Fd_%Il!ntK_hE+x0;M}rSB&xzk zEsOJWAKnX`F|8ao%W7q@rZT2hn(@1LoUgtA39VF8HC3&Np;juXs`l&mmCJv0nXFhA z=Z7e()i&OyR%D{(X@>CCJmTmWx*e~w)Fg;ef|9_6Q5Adk5(JvSpbgs)F$G9QrJlyZ zKoiHV>j<&GO$lTfy;F}dxlOpcT)Vk+i!X_0^X`D_Lb+1*l#n~A*Od7}@$&L<%SacO z%Q}w0e#q+eIW0(48Dmw|bh%ozJ8IxN9k9&cy_`}(P*nYyO1<0+>P9~9$en+wx?~n% zG3pxiIMvUA9InDWdc!7o>8gG+2uQJ_oI%fQShs3e7JIJ+t|%B}S(gjdv6>+ii|xXE zQ&23nV{%-f#EQ7KLJ%}$n2MUVD+&gwSiOay0`Zx6G`cP%ql?msTnTEAYO_!vfRKjx zXiE9A{{}jNcG0cqe)K4M19~U=AZIoXk4e)(DvRL_cq2svBtEyq%{T>Zf(XAE>cZkH z6--IRgDn@ZZ~FLw&i&ee{r#`+-kL0{@GVuAp3OFgA^fwCICGw``B8#&WBZ5mSuI+Zachyae+JS_UL+JM;JX)aca5avC4CG-%C_9Dm+;pcFxShT@wc}%{LU(;5w@mz#OW~sHN~G?4 zz!*1MCzkM$MqqZe&z`@T=2BTL4*nMIiYW+|U98bUej&7$MOirSE}V?w8OuJWu?00} zy5#D~AQ^}|z)>MZDi0UUE+vD+n3GQ9L~g6Dsg|kTGYi6;rE0X+Z(2>Yay5(ro0h5p zgj!pBoxreXshXx*xzMGT8+V2&ic_?Y5ANFg1u%U&Zf-*y^zsdDPGj@)E-QrXdw-QE zHy)ylX-{a{wawQXJYrMx{z?gK{Hn%IU9T~j-mdA#P_dCIszIW{C{nVm6N+YlWIYeJ z^`q(@DhlqcD;aL9P%@i0KL8^%pa+-A!6aJ^^euOH0s2VTs<;-6diQC+VwPDQLe$YR zqZcCl3oAK$KnyR{mY6L=OErQxo$DrPz5%J5r^YWSV#YM(vPeMEH1#sbEX24hQB2?!#HgzCDgih>{lv%L{iaycEqT?8oS0!@{v1gcIXksFLj zSd=6TAWB5nRZRp85+Rt`&T2XD>t3^5$a7tmiKbH`>3;~SP9#y|j7fqh5kibW77#)N zq00w+_wE{6K|_?GGd{c~)m1^ODksB;p_f4VGmal5L;e2NV3fv$dH~4o)c0H(2$7cz zf;2Kf(v;H2n+syMdtxp3i}^D;g#C&hfA{Q7vA>gfisXtmo}}%YB$^t?onRw9%vKy}7$ z?^Vj6h03-z@WIorLr>pUJ$2s6|weGs$dkYyQy@CH0%oyXgC`(nu zuSSx=FoMYNc)8pV<8oQ^dZ5D&(GB@EE6g@k;M^1EJlrZ*bU#UD@kn7B#a&L}p$@80 zj(ElWkwyHIQ&t@|W<4@WN9k1IP7cJ4UJdrX9n!fA>2>A#bbM(%`z+rlrWYVcVgybs z&R4C8uB%gCj6A=L$q-ClFOJd|Yw}#4FpQE>I*NBNsLiDkErd$sC7!RB@WWp4#T82; z>PgEwsS?RrdGXo;hipN1J?H|ar=|q+6tc^mqFB)3`sXFv_igEUKI%@uxptEClN#r* z4M1M2&m+5xGQ+Zr2|E{U8w|@bGGdw}ZifB7JrU*6afBMfnpg97BdG0~C$x#28gAmf zb~O<$fYWC42~0NL$bzGy;G#3Rz`nU(4}54g$rsQBV>isQY{TqlG$7lj zztlwq838ryt5z6pf)^zraW3!0{8BiX;=47qacN4)3<Oq1PiMjgSChdBeIb_yH4_UyKA* z67bTVk5j_6pUqN?>YxmeO|eWl5S&Sxj+N;k1M;l|J_U4gV`HISU)b0?8Q4`}o4`jwbaEPjiR5a$_CluU;x92z!^c*K5-whQ-RYNTX=!Q3!&=EAwe=+op zMn=OBT4tmtqdbSoi>YFaSP1$^Ner!*@ye!diT%_EY64ZrHPR&ju)`cKwWH z0IKjw35vzGsL8S{Yht@tgsOa#jS%CxFS=bL?|&6HzE7H0JP{8gtocw4ZK61WpV-(U z^dhnipQaQItH5JMEjcPjLuzlNBB+L)=w+si@yXgk1g=|`q@>u#olGV@HuDA(43|ba zH@Ke1PdH<-xw3qCxzWg7e9sUj*<>=NGw1HEMH$|aagL~*YW4J4gq)gs8D-=_e~k|E zaiqxmQ{2MYs4B9~hN#Cx)dP(QDYm#<13pphcB=+ch&G$LQkHG_*6g#t&*0@(Gs3JQ z5q;VPP!*tx6~z#0Q?C_i8xE(RyLZEh$r$hTGWd-c-JuC*%;HERyDNGMy+q6M8q=<*VnC2d zX1@jqPQR=A!K|8Q-RwrWf=PRkZ{2^n9XQRk}`}%)_Ep#J#4SEwoPtHsc74C`hY*vxWH6t2+r|^Xr^dNJjy@WT%?f#*&>y+!pduVTzrjRf@8ssx3uP)$*3ARspH)mR3bV3#+9~nSBw8#WtZWj|(8A zq!N8C-`FKa#rE4T)KS$PMQN$3qLdCPNN(;^heX> zsQ{^G+UZrAWP>4V3$8$m+2rxympaWsnw+1hS@6HF#n`vy?iM}Ra??GoB9CHq#htF+ zX*VFHXWW@KL}`QuShRw;mjbuQSY$puIYQI5rA8nYFd?B}ttV@k76?J-uH`Udc?CI% z8kVa}a|HE~Z$91vMrf}2_!rvdpV?(>H**Sw$C9V;SRt(gK*&=2*aN{1`ZTPaOVjW` zH86G$|CbxE2bY7mHR{h_(%a2XFJC0QeDScyvUeV#bLb)TCWIP;v=3_MD(ZDN@Dp?k z;>aM}31}SXrHZkPuP|X;XP*MX!!N%*83leal!!`|y}p_y42Blxe3H)7zZG`WS&Tf# zFX@yDf~x9GZ)wo4GD=ysKUj8~x~dxtq9~dIq4OA?lJ12p#__RyXG6}TDz*Ci}{3F$^_*Q4ib)F7MNLOnM~B91>XnGDhq zqYsv;B&~<2(om5%Ku>cCB4t?3PfCP%X||wNuw2(FSH47qNMV&?a|r>J9^1)pI9a8@ zL0w(E>#CZ9Q*b|UG|hE2FmwrHMY|vSJfBiiZ>%d)KqA@v?G>Y@ujir$3Y zgI+*Ci{PK4)%}zk_=(tJQwOABd6q6njS#)F)exweWWT{0n_36?c|ju+ujRVDkbJA! z_I|hKN01Rm98+uTp6Lyt?NPnG>H6juzho{PWdtsKv0uOkPZ95Tv#t28$Nv+M8Ott!o#4COlhvKi2EOPkO$MM(7rV8nv;jp%2$76+g@M(&+A>E95okN+jE6 z_Gq{o<8W>d?PI%WFKB7Kc4KX|-zPPm&(=!JOZvm$rg{+&)NH*6SXtFjbX|#U)@@#% z;n%Y4OzV7id8qF&%{`9AKFzS5Mtu<Mkc?6Asj@RjXeb<1bV`OMkxa&kt5KyiF$mtZx zAZQ#$fCkDE`xJl9*y7*!E}e@<;9X@Lp^*K#PZ^9()akp!bzj6T&$f4EjKKZE%Li}( zQxu>sI)&~;t3qAsC2+lC3~0u6>Wvaw#EZiw5X%@s-5_ZWN2z43in~C2h%m4yAQlS< zZRoiq0REHYARUqj(B@)D1xeX6Kv8IY>n77QtT)uC!T^jtMG|Ozi185tBDm#<0MXjM z|MM7Yz894(toe#+Tk&0qQF|qdoX2> z^AIdAvo}B9s~%!6Rbu?>Fnn>3Q&5RWhwuBtC{C^u#Yf>Dl;gY}ZNKVebZ(PWB2vX* z;?5mgL?d)9dKG#$DkmG4qk4j_9xMR3BC}o46!2!!aVSFr_)tij>QZyqN2%N32VNck zziw4LU?+l0axqwIqWP2A*EL=W zPwAALv6N5>Z5pJn1BB6W1j(1biJyX(k;}+$`bP8+dY0iA4bX);s}MK+&V;ujTO%yo4|*`^Wl<=~LVfMHpjZ#NI|q(0vM@oecSEemI_^Ej3TzqIW;@ppx4#rFYx zzfvt|+LFwb;3lVpKIdn@#=LSp&a)|VK6V5iy;^3+Bckcr&2pAld%*`5Wx2nw2Pt7= z-l!TiYv7zeu4#w8)%RY*7?UjGz5ksq zqlM?u`K{4uh7VC4NyE~H%+p0)kLwVN3O^y4KO_ZU83 zD!>9K?|)5fx}&rilOdvhK%NDbuIrfLeiIiZ>_9VCuHd+znF_W=0H%29-I$LE?YLmZ z7@z;pZkS1ZfejlNXKZ4zH^CT>dg#yaQpjIJh4I5xCQFiLd1+#K!G=?S>?Q6KGwTpJ zb!wa>e5(^mqB^IQ0+@Nj;Is(yxfUaD0#LxN!k!$@AksATe{_**X5)GcJ>sZ#u~-Ya zIA>NX7H!oz5_j?%=5Nt;M|T~rY20yjN7uO-;TpPRw1sxi^@y#>K3+5&rF66nqfHo8 z`5QR@&!(2EjM6u{Y9SdG3-*6%*T)m=p94Q{I=47x-tMdbKs}ZIFHI(gJnsz_hjn9Y zz%SqY@v^`e!wmbamXBY^YpN`(THe=quE2TWrx4s)%V%<2^UndgJg_I~cCy7(i->cu zDvf$cF9(Z*KorPvxrn>L7tIe`T5AqHZzW_a5 z49Lg&d};u%Hwve)A-iZ~suaHyR!CEp|3j<2MzlNs(}Su^R|y&{N1k3#l+Ov?OW`gA zeJ0D-K&)e%89ITk@V+*riOK_sP6zT0fpF|vTpmV5B#((|k{1n==m9qm#nw(!loQr5 zVo_nIwQcJh!Hc`QlmGePxi^wcqQp3(#>{FLi!jNyrvHS9@TvL}bplf~L>p)u9Y=Sd zS6v^9@=2$Da8WA)rXPc3SN{bw7PQ;LBi+mH`+^eMY2}eDZJq~SDi#3Zog{h0^Gg;T z{b;(W2dtBp{!i~rI2g6DhsoDhzEZcjMo-f(-t;hmWk!Eg_CDB8reZR4jC&+06PjKu zeVQh;vWQLB+O=peicqRH+NC(o#7~x7NhBynqZtpA(efoFFBwL37~)!!|Xr7zQu&F{B&AQ80~d4g9%C)=gCHx#u9+lo32wr`zh2( zD4}I&D)S+804t%P{%D+xZN(ngPxyYRl$_zrC>;eTo%WJtt?W49TIGrX$~#plTP`?G zS(9Z*qDA%F&vBQ3p(Sss6WNXx#%(QVvJ8$>exvF_Snc1ICjhSc#DLs3LgO{5>{Hm+1pk4G?l`6T{?sZ8uxH#@ksB;Vh8u2Tv_TZRmwyiMULOF z?Pk+XJf;ms&`toJx?Hh%Z5M^%{5;FuO(G6u8J)mB!A5ml>qFPPXy1i)nDxz zj-@uXebL{=s_gnqa${RHT~*YUZBsR?(t>B%dec*gGc?qqYsz}nHg&_49Sb4E5N8m7 zp)a}c9J&v^7X1YJX@4u}rBn2L<6OGe$jmfr*@S#z!C>)Pdq3Rd+zHS60OXC7RDnO1 z%*Ej*?VTQ7u`HbMAW9j`Nz%K0NAYBX0efpc{zY^9_?GkCd9R|BZZ-gnn@ftKs%wg} zrm7v==Xg0EMeQpG7G*pj#BYGd`|}1?7$6WY7?=2IfF=$Mlc_q5yy3MT5m_?frVw=aiK5j0|lLc!Djf z;#`-&_{Pk*{ukDm3A`rkHQ>%3YHBu0F>~|c*5Jx^6y;xbxek|(YS|+_$Xs(D)|eO3 zgKq8mkdmK&`h6HpzxB|R`5w>Kz6WXP?l|B3AA8l?PI#aG&yt_b*<}6mpTz7<*qQ$c z&F6$80E3g&H*1KN(FQt&Za{aTSEIMo#xlf7vK+JT>K@#?{WlUf_&np8RTGpB8@~O8M;)|J`}a7@vobfiW=v?3(VihtB@Rdlr2HI-L*T@U2&d z@G&jopCxW)xB}H2TE@(;FTU3Na{;#}4Ljehe(=Mx_sy7z*H{MbqdyznfgVI}L?8M9 zzkE~SIyD@nfjb%?E5!I(Y9>l?pr0+1ZhVaRu1v4)tkSt&O6_3)_Cp_o@tpie_ez5( z72$70D<>3+MlVJf(RT=N72uq zUqW9;zl;6^eZMXv?OTtkFT2L-#ixSgy%0=Jl&3rA1tVqn;(LM`;!d6(J|FRhYst8X zN|U%tH~2xk#-#;xbQPCa&{BX2;!2_iYJy&p@J$T$B*>zHaP87I_Mlo#P{6Iaz*v*o zJ*x*zQhQHR*=2pqSpJGGVLu>l%XuGXp%3&;K}yAf46+mTdJJg7DzF8VQTT>}5wnV+ zw8^hR#RR|qlcvQrws(&SU_LOUs&3p@BbG&K_ZhlMhYy$l=0gQhQTeGlu`CjP8otUj zZhbo3(mKLFUHueS6|wM8^`1S%S91&;zziwKL5Ep8-gj#X{Q2uN@vCGoy_jEzR9vQN z%A~T;v{Xk}5lv9mNynxE44~SiHG%G-xJe)v<#gep209Z@|~4lx3&kDly}!;<2< zg`X(6o@^Bq{e#_G=dWCdN4Q}>ZX29Yx2Q|0KuK;}Q;TDE-Z3F2HPC@O?S}Gxlm`W-F?y*qK?|GZ+;pdbxLaj+oJE8SI}H z7YTVwvLODhn&D`o zA!B@>Gd4B=+qZ!j?N_u!?FIwDSd(7mu*a3QvZ*cvepm%I)D=%hhj1ukE9+k-`C@D~5>1Wb=Iq$AB;sm`ctID)8;y;P#^#qD zG+i!Nu_(!Q8govJz;i}*3{bn6>=`w|_^xXn?ouM(-hc!ht841jD*!83tC|i5I@7pa zCNzj;u$%hUG5m>F^rAA&8`~C99aw9jfkh5$CJP2AC@EfVDhLU2fM%RZqhaa?l+YOd zThX#ma5`&&k6-rdpg2>u`9V?ndNsJ#nExN~Wr;Kx{UFq-=asQ@Dqigh2%QD>zz2Dv z>X16ScY=#oDC!lG^aog+_@QDf-K?M%Wd-v3-3{cWtGoJ2rz;Hd~VV^Hr$_K0nI+NQvDsGmU%zhH4_|;SDmTScLh1-?bTc z9!%DBcXz>>wcRx<{b?O{iJ-V-N{kkL``4n|(5n#wuQseT+RcOkyfM!4+TCRuhQ4^ErRXON|S<#o!L0wlaFYG;(!mCA|v41%v26jc&*I_;a`2#gO7vJhsO zut*2tsyslYWS@Bw80lTPkt6JOK;oG-U9^ZY4svI{86n3LM5a2-{RGS(wZV<10cl7m z*FOD&HGS!!`^THR=xFTpDoqebNHtwcpPi;_TKes6o`f-;Ws6)#$(d|143n>?VvN84 z_;{rp<3w_%=QSE;N687dpgL9^`1>^j)JDhH(4c5~zKbWY5P_zmY93!+7r5s0vDS=N zQ`%6J?qYw>aW?F@neejEI{q)q^Q`aH2hX$>0~FyFZG}_a32vTabNpvCt+P_DNGiu* z_{PouMb|%lv1r@SJt8w#h;=b;b)|`O67|q2w9{Fmz^8%_&ZZ+huovO&&Ss@>xm+0La?0+N234H9Bxzgb zhIu~#U|n=wQUAsHl`y-%gl&8&Jm`!5u)(jHvg*$NKoC=zTgKV4W!qMHRdY*Rls z+ejEA$VpYZzQN)vdu}eTB^YW4jHbvAnU-l`nSLck3D;Qyo`A zDS8;a5xpP%v_H6nq>!d3v6)I>V-mreeaY4ZnehlO7m~rfTM| zF@fWjn+BH~3(aytGZdu}s03l`wK}o9grQnZL?TI729l%$#&`iZHw?koR0#a2=+8fI zQVYE|wLU=@yHo$z!`>eJ*xV?@ydbFIx*UA95Sm<;7nXg`Gmg)<=M^sw1}maC{|~EB zEC_-iEP`!2j#zXYtKoWvallkI9?BVaTFT?uS|dggJ+eEU8rkEvPtgD!M+nK&m=%Ly z;jeu&OF#WDDcc}va1RUr69XK}u|-x60{eE)Hk6eW#k9QJ?I0*K!E&st+flt(aXnR* zRnM&y>rwmaSDo2^V!+2WRrC7XvYk{hY;SkhvkfJ6Y>W3jP1QC9!*GLK&M{3VmkV6O z5H`Peoub{QF|il9o!O#Af%e$udBbl)uCi3OH_g{;jg>fbzuV%Vs4 zE6OKs zzg^?pG&$E|m5J9KWcAyF9hG8L5ulO~mLxcvmc;shmLC4+V)`#&%QiI4ur2?<*b{7- z|JgtN6EQx%mgIh?RI)YAE|r|TWdT@L9wCC*l-=;L%eN*k*z*KH&XoPe0xLLqKCEeb3RuX$zr1iz~jB1o#L z*Gd{Eq|~sPNP>v$Lo`7s+BP`;L|3(FC_DKM*mhA6R4RtQk3?ou-E8GrIh=a^)! zSff0MjaZn(V++UsvA}uhWqjpL0jDl=&Yg(7%DLtE#k?Mm~Lqc5;>>37Bp%AcUyZ zrCOn2$d0So@unSb@YhRPW!d5Dc0DU-i0(xXBa}AN{%9LUo4D2M=l~@hp+!{KGZO+P z)tGW(6a_vf5~6tk#}G`=M-q0xL=RNrt*WG%DK1Q81q=GK3(HInPHQYj%U91&0j_#q zN1o)SslXEeU*2K2`MUuwann?8FAkvn1(?%sJ}?Y8r(i+v?Q_#q?kH>{Y=h9ZxRQk; zbQ;}*-i>|+eF1&bXT)lB@+pi2EuBjIz+>)abVK0Qk0INDmT~0BYP>NI_?!n^-!K{6 z=L@G5BXJtO#47%v_l&z0N6S9$6bdkduSTJ;1cX0gV=hk4cwxASWB+H^Z8Tke5Kb|3 z@l`Xsr{I{mvaYF-sZ#B>em2j-Fbp+w2JVBJ=#rX?=nnKjye1*peHnSp z2H^FoE~}m|iLxm9KB$gk=w`9fY*tE!ZrJYpE0(G`3AYpgW`uAB_0Y(Ng0cf0D>(NS zK~pWZ!N~+yl`<`Himlz2rD~jf59iTU=*?`{sm>myM5^&YmNMctqH@``EW2Eep8XD*{~*n8 z!?G%sdc9JythWzjmcjy;dH#n{6J_WuLV<({vq2A#Ktbw?`+$%a4Jg^md;SEJSqCKI z>iwAP{is21imEg#mR)mjap3lllSZTF~HU62KVj;Z|g$K!`y zQdQhpJJTOvM^VVA87q>a3JwDX$gq;fm!V3C&+%13>xCarOEercW=kr z&-NKVQlNdh5XD2_M7kVM2y^*D67{pdqIGfCcoFZ*o#c`yAC0g z9?5)zmgX&)pZ?O6|AfvtkV&`V%fAkD_<9`e?iyM`HzCwW=<tVILcuGQ@{X)|c~8?c)6}ku zFz125KRZ+-OV&-0w#tSadv!C$=l6PkfC)~%dRs+>+4G-Vv9J&nqp0YKqOQ5NtU8)$ zYMOR!ZmHlpOjYfRRE;LXQH0FD1=o${oLn+nAGDcLod3pNM*`o2`}8{dOp%QuG(tP* z7K9vo0&>@eRpRkT5MQ)nT5%d!L1}XWw+ehO9y-|d{}m-RVq#H}isF;pFJ1N#_*Gf% z{cmLc&8x`Fu5Sp=ZF*c;wtk&r{u`qBlG~&r+;EW0K>~3-OPt0guR^z>Xxxv*Ac$k1 zuMiH(^zVTqwBJ9e>wgji{e%G9HKWVt`E278%S#x4W!53CXy|>-wkyU2z#K<@ekbTE zbQ8KO6;>|jVf7>_`u^foI6GO3`q+2c$Rxtjag>J-&i$#83^8Zvk|Y^OPVdtuapP;; zD{}|v8uSoC4Nvq!474VrdQ@kF;;kabG6S+g6}o8!;zd~?+lZC&)Q3d=wyCLBE}S|E zm;sn9$-dOz?HHrFsi;4v`y0L+(*|hl0X`8G|F`J_2J~sr`=flRSp2d+k7HSo{>I*`G%8IR zbW(e`^Ti=?oC_9_eN({YkQ-m0t0fEorY{rgLL>6YWBS^~C=$o6@MTS#aNmQ|fWvQm zJpzE!;Q4$qzZebW!-oEtz8msUIix*?x-$mw>=m?&-j2S^D?dRKq(`$)5$M5{xS3A# z-OAcYKk$QZpXW^jd@l%*vf|#3YeF~#(9h$vSlBJ+_!Y+qZ6o4JK@PU z_}7USBQQGA$8cN>WS911oyI~=Qe(8#xqxpdDxYVXw$bSPT+UO0S^2&2EoPnl7avAv zBFPOr1~j(oTx|6X8Nz3aP%i~7n}i92b}M<+ee@UVt_u|$s8-m+Q_qj(=O+qnPVd4| zSN|6NlvXY^@^)i#38>6S8`0;B+@NpM>0s{XRVQfR(~09bCY3i}0)8Zcu@E2Rc|hj6 z(fmJ)y#AiwpXz!$it;TWSP_3q)c;sjx8Q9wfY90Fx=}fwjUXhDpt0O(o*0vBDS&^kLJ&Fca(yv7qfNzE4 z_VS0mv*bUE?hk!_Z)S|~-+K?tU*U{}zD&&n&l_eir3!4*(Fcm=GvG-5j<=qie~K|a z1J~m-(|mEa+@9R6bB69jZ$Q6f#hDtkU^+5(iTq^n-@*G0UA;*OoxFgK84){##SFfEv)|lh*K( znwAHV1%9QG&x1^)ck99)99oWIJ`DgN(Z24BqPTA=JDRpL|3Y62_dYn%&p4=!wmDKd zm~a!mNM1r*B7s{)DwOBxnMa89g2I#3xTS*ex2>#~{27RiF5HJ!3A6sZw7E9VJ-89I zbn)k}5{u)8A8ca!8Z8l_%{X{$t~%TSV!&b!8m^O-pCN6=m;0@^wRO1On*zkBg`xY`9UBt?;yq{$U^?Qak;#$bSN4@sNr z#!5@@1x0y4)33V0p;n~ADcH@CYl%cn0|cS1V3<@&YO_)ZbKx8^k8*CfO4lCH831>u zVx!i3{lQO(RiB)t{9!HJ*nhZSj#_`uN{ROpuE*)c7XpAO-h}Z<(o}n>?xtD~U zOSvEL6gJAcS!zMUp65TP!f}n-lt#{e08P+y=y`rN@uM4>7>tl1uzjL*^E)GI#!+kw zktd}?$0()4#4G%Vy0D3NH#)QcUjys96ivgvmuzlkmejbpI+frBtIb&5;r@B}f-}zh zeCH>;V~c>OC;69v5XyT8^U#>Mv}-~)_+~Gf6>MFm`N9PsWBm#y{5B3O?=>5GYlM1_rgIX!v-GzKR{Ln_{!V1cvHd;m7Tw?>F z+L>2|+K`UYng_G0wfkD2grJ)5;|Ct48f3iv7qlOLcV9+V@iH7Q^AcsUGe7UhjFzt9 z?s-e)?s-$ezROkXykn@=d3bTZw2JxZ|KNn-tp$KWYH<$0xs`%tsXN>?l^yO9)pCp- z#dP@&ihD7uijE=#9TyqYC>41&HHV`#p%z5)|EKv$uOD1t`=CGA0x)HZEWP&UmGyoz zFwBctE9+&g44^lK|1nu0OLh+cW@t3F>tJ)wfP>k88ovo452dr6hwz&+xibo=kA~4w z2?%I98rHl75iKS83zI60T*vE)m&9HI^Or3QzE}H?8GFk^^BGJiw5Sj#F9FI9s z&pp6a&G%q@@cyg7Oz{j7E$ZUCoj{Lun8c-VvxDOILI&P<8xR47PTE&8mYri1H{+zv zIZ_R$U#-8+kSCZtxyUJ9RfG&EUYR<#xh2#_BXk2o9xHmt?M8M5ai$re(t%lE55iZE|E?9_3KR-$eRcKp`z+!P zD%ChHDx531s$=krgf8|;yy~3|JKw2P=&9wU2^`(Q^5$l`R?{?i?CxLo9)MdQ*{*gU zL#NP{=q7Y0&IP+ths}=moptb@FnA%S0zY|304K1smQ%ytu^C5Ih*AThGzcMc$PLnH zx|9kUw7;L3R}M#<8`&b!x!m@NuCq&;=@{2^TAJnX)iV{vba`(#)?J(BwYnbw)M{&M zC(Oj^R?gvG&H8fAG{L&M2_H2S1tCl;yx$bdD?8F%ItDcz=opmwMaR(0`>fRAnssu~ zF;wg1ahvJx68J&Abz*I;R)ZH?ttM{A7)&#lGwst*H#SNlKNzJ^vOXHFli|8JT-S%|%5a?x*X0jIpR1GMy7W-OZ%X{F zC=6eg7Ph+9HC`eYs|rzn1OLbGFOROnQ~1s6?}3G(b-ck}t>_H>gu&=YhH!aCge1W9 z9ET99$I=*nWD=srerHQEIBGUGTeo2@$L+q5%We6FZZ?|$xqPf~4%pv2-^{rMe<-S| zSiVxFL~0#nSWvH)Kw*x-oBmww{5(5!rf_ll^{obFd0IB8; z1Ly;VH&g$;u2a!w6?EPAibc=Yb*OGEoFW2zjc+d2qqtR%3eb9hf??!B0+nx=Ht7WO z_AJhvU z&DibVGdlyO8@0<_+nRb#Fed6glee+NQeL>^#vJC(I0WU06ldTso;`RekYd=R~p!{tm2}g<2&V@sdzT24JThbPc)* z-GS~$k7*qYd+T6Q#DSk8CS&le{Xx(-f0pvbA;Q3u~D6-ly$(-px1K>YbRk0Vvc!sZ>25JG~w-M|y9o-BBKeFWL^CkwL!tYD(XS z$EJDZ4SkK${4YL({8Zs|?@zUNWVPpMD}BxEHtWmCXh$}6ltU5ft~$;4V%%%dbLjo( zBj}Up=j<>;no%^}%rOabc!DrT@f=tE^qOq?dNF!Jw+9_7Q3WGv>Y{Dfm;LEIMIELEH$y)nkb>9al6r+X8+mDh(joW6AFs@s13-YH}Hc6E)>HABB|yzu?#i|DK98|b&u?;#X4 zw}Bcmb%-`^L(Bsb{rx$~An^RaOXsU9z5)M5`JzXi(AZNR4hbdfu4tpoqdvl98)QwG zM4Y(@2v^qwj>Wnjbd)O%M`@4@qcSAJ$cw$zm;I7XD$?fbieVV!035eE?aKl$6P?pg zz}oV4x|(GT3!$v*q|61mKdrl>V1;v+NHvvK2p*9fHt!B`DC=<605Nwcf@aJm2*1nU zlkM{x$n5ocz1?1~*Ww9H92qM zkUqbPLL9;vxpRS=lR+??d9(XLv$?$7YzB`oCOh-Pj?CC2pvS)N`|;PT=F2AQYlzp2 zflxg80D2PPhlXB2D$w_QqcssjnP3(37fA&e(E|325u~%h25Z-rMi9|>yTyp1HwZ9$ z0>W-|N|c~-JB8DZ1@O#G3~j$Jumw7I$6*M5mYS}b5W81Bzj$b?m%pN{IF9mlg{dyQ z-cgzIbxR-7ZBGg>I`T}t4#44sD81zEbB8hEhlBB_uuzMy@zeKY?Pr!u0CWD^mTdu8 zwgnrw2^brVG}N-RYT8y;HRp(QYJ(Y1dP0+MBknDG1KBc{;l!EOyCZZr-bOWz_THqr zuKHaDhM9wqb+DRyG}LMt-&NIXe)p6Xe1@Aj(*X1<%HV5$mX#5>WFJ+50{$voO~;1v zC5mYhW@RHIbh_J~@rJA8rBIF;TvhU0MZP@p*OCiEu;H&noV~2}J3ulIH zxV9i@s&3RwQ(l~ZP2E%90dEaWQx-*4)o*pNHjE>oisJl7?HaUhuJ-HZ)n}X+K6UO6 zof1J*ily35&fge@HjaoR`u-ZS(K6aWH=O1Llk?5aghX*@X;Ejf>abs>r34$dmeUidg6#r><2>f6NiltH!mXvJZBwWGV zqhDIa87o^hgwUD(~aTucf8!PnqWB06-yv5?l67GkcLZC^&ex zpASvbG{dFcK56Ci)(wNjqpL9s$7YciiuIklod52{t49|d+TPRkVu2U;8ik#soZ|*7 zpSS*OV7F_2DR13ixS2icG8{GMPr1 z@+2JVdyLS?2%OjB&3spz*x$?Q+`-)T;~893Hok|bi;Tciip-hkl#7{46fI>gKKto^ zsLxMC%4eypK0A-sqG!Lhbb>jxq8VkW7>GjuNI9=`c`srvJddVQA zq>5jnnidv=s{cOmm0tf&#ytlGJR(F8B?59m6CtY=u0E#|8jhD;ZqwN4 zNMOob3SiV5pfsws!$qrp$SIu4rNf)U+pUKwTUZvw>os1!Ji}d>p%FTUb~(Utrz+FL zA<)6hmZ^BQVQ7uO3oToPyJw1iVK!v#i-sOpM^Y;nm-GuNrX+pCTZU^~?)HQN;)9k0&6 zvzq|Yo`FI2c;x!hNYqGDYY9Rl#~H&b2dMsyYn=DU0`SrDeU?Ft_a2QccrUQ6L}vs2d&U(CAc_f2tnK zj4QQF>r{i^AW*aDifo`hRU1?HsHq_fo%Gbs%J3V7VZs|Mh3`VCVP$9BOn6556x-kd z!!V1mnNUk}H8&(}8q%+IN7v<&AcM^JUd z46+=L&GYFP?WFihH*R0MXm@$Krs>{YTCBgGqWcO={)26m_a9~91B%j&D|}J6Th$6s z_CC$4aheVIeX3nc_2_ddtwbscc_=mO3morCbTeaX?SSGJ)5xVs(h1fcaBHIr^*F{? z6U}tBJ|Nb(Ah=^ToH7T!hh6`4n>&uFivop1TA#1s{Wc*?v2A+_Se7W36nxCBh6`6l zm8zErSOn#D;Q77>OE#_fJ?C$}#>80D4AW5Jit%dp020Nv9h(rg3lfnP$Hs79#j;GZ zbV`yG#nO|!u4#Jyi;w12)w6x0<87k{ekQZu5L3wbwxfsLC=rSrhsUU;ZQuyG3LIo1 zN|)qijFRqT=Kmm0_`OpyQ$QNz(^w)@y|SE*^yJ1d;P*3-_x-%P4*1U8+oh-|sn^Ku z<_X4RA-F=G#Si^w_cXsaGLQ{2G~EV>emL&g;0VLh>@>GcTSmZqLnXNzu${-fqP)D2 zQVis@@f=Ve#y!(_RY$|A_?7iA@61zfu7jrgcZC_PsPJqTWsY;DXZ~l)l{K$A znwJt~5rNORJ)4ESL&540C(_TANgisKKiMX_5#8J8%{r4IDMAUI%)ub)62^Q9c}%is zZ4%qDX0BX zW&u#&BY8|*0eAX*AYYxclCjUdN3vHx6aVFd(#vDHyN6c>UER3y`N4kk$6`^x@3C^5 z7!CX$NorY?7Yh^qlahzowp=dt=lLF~9uxY9caJ1+Lhtb$ZCiClCc$c(tt})iFyf__ zysg`yB=95S(jVm8>~?MzH{5dGZ5#{Mv`Wo-qj#^Nna@|WNR-hFFWQFbcyZ=6Xq<>C3nHLE z?H4kY0=+hXLqn*;>Se4r)huTpnp!Qh>}hI^aASw#S*H0Qf5!88aC@s6_~DIMXuyHJk1S~YBi6M^cL{%vNBO^mY3->^M4aTuLpWd>(c zM}oxA+e8O~btf4s#P$vH<(8@oB+(YQPHp{cjxRaM+u9t%ZB)R40W3_VG`$lj6jw^7 z$r!(oonX>u52YPNKJ0?M;e^2@lJ~2+CQ|`xKjOv}ou@bIihir*sJWn`aDN$v?0@we z_zmwf`;71VFbZM&SC(E4<5+gHU`A2gk7AsIQXx$|P~qh==qRce?e+bBGN=L#{Jd_6)N{C5=M5dxSaW*NrMkf6vw63(&G@Cs5<0d;J|6T2=lc#R&Fsw zkuk&MZ!TNEXFo%Qc5YPF9%Sn)NJV*cL{fJWIiqc%47lSW0rKjrsT0Rf(2J)Crkm-g ziUor<_y&|YqCx0r#Z5URiiwH8z!ZBg7#NI?2H5^7{H|ZOza4!(UmE26J2fJ6&NZ@z zRZ*Y|0=|-oqG1>YyJ^B1ONs^TO78!fL~>2uCVb2j^}kNT!MZH}_JcG3&@HdV_UdN!gN}1972MLTH{)E6hs27@r7u--WLmn>ZsQ(!2g!lfe(CxDYR}@wQ#eo|ENF z<2szwYp`N%`>(ZR}uOz+^s z(hhT~s&q{u)Cku{i$RI9W^ewv#4J*^PNZ059N9)!pc^I1douz8k zz|F@mTh<}VyYn?`57JZg4PT%l7uCjgpKK6V(J=io@qD3bjNba5`Kl?87yxw<2EN!8 zr7b8tL6GaYM(HL1i^EyLJ%>}NHU#plv_?GZHXDO6wo+UV9d<9xwm`^NhbyRq zuEqdMQT@xIG+0;7=z=*0ebJ4#g*-BZyxI(yrg`ARw?AvtZwBLF{!__vx8@9|F{6pG z?Ac$H<%wd?HEL(U@~dAx|KoHyBeRvs?~7nW^B!H}R+GMgED(w#@~bNK zqT5yeQ+q_%@5!6_3qG;YwaryIX*0)OzJIRwo&WFOVV#8| zP1k5f1VKwRO{W=#C88CvyLOmpX_W|q_8LvoXh=mteT^tyspAbFT13YYcLaiyDQ*L- zOmmQ=!JvIhgXjp8#>&JT07)-#wU_1i*0h!_%k!eE2rI@9w7NM}T|uZ;%QrkIv7@%o zKh^Pn@f$1W>rs29*~l;5P_9;m`TlH8a;)KK5gqqlwPz{NLk?4mOon)Rf}+@pxu%|c z?ThAK7Kt%(=#71PrIL##4ML|>p(Vs-V)@O<4lm0L!WdI^DW%4VL5rL?wsmKa?bwtTliF8 zu#Yn7!5cmZOR11d{Vez~Op$@w5N)Gt_(Rs-fIhAP2z?>wbo4|AP`oNHJ%#}L4~B|> z*2WyVM~OCx{PEDOWS;qtr)J!Y75%RC|1hN*d{d(oNIu`7tl4bj^AZFH(KBB-VH#j8 zD@KzqczH*#JjpR%>`#^U#G>&VH2NHSct7PE%~Z2cq(;zAMT>>z9BVa?j#x9Zuv9Y% zu&S1Z%g80Qz#Gggj8atw5@j5;M#(XODIeg1-lkjMi?y_qcw9b7FLm#%-MyOYVgde_{%=uOjhpcYfaSF$H|Chf2l)OE z)AZ8P=QGm^(>;;LMEWNviy!32>obRrY|zw$UHU8*Z^7x_sLzsEbovQP$3)G-rGF%C zB;E_qDp$x+fn=zr0Yf(cXjY=12TIaaPtO=f3P4YIrDs@UcxH~6M zBIZ^p#!{1kB++h{N)l^I0;a2X-|obSTF${HL^8u{_r2fn?j9jM83>gcLn?|wJ|~DG zU2RB&g8VHk8NN#9>s`PqmR?`vqq5)E{*k<8#-X&3Mun3?pD~9f+YlKL{|F>aqVmuZ zhEGvr_XoT2M#eb2?h10t737x1E2LYmAh%36G_HW~#{GD23fTs~_1pTwXG6|drv94x zYqJG`UB~IO%ILE(~Gsa)sWi8Fu1~dZvZwBdXGV}4v%co&K zgL6-vaxKfoBxZ$;_=zmbWY3{*8*QPpQFSvlzi5)5S~J1$0@9^#5YG*^`O(znugWML zC63Io|EJtJc01#lChb_(a-$g-jk97p(Y)Bw{q?9xgRebJt~H@}XA*f$ zjzIJ@!TQebNbw@Zj(qh2-Al1#DEIe3vJDd4hduxc>6Z}jK7;a|>gjw&uSSS9yucJN zV+^Ac++ zB+=V|siwN~Uu7&8|DV0HNyE-BSF7SpuBvUe|GsZ$eq}=FW}CVsAO`npLwTg6?8H#&=*#+5|tyg}GcfE;4|;^lYAk z>3odMo#-I#Fv*?U-8~1?foOlp1%%e_Y0M`u$vrJz%wuj8*DTeQ9J!QkOe2#@VB(9(a7h)SlYC0ayFg2PdxYUAsULrMF|epp3(#ZvR=ui zdsKBCq6uG7lv{C|3EsK0Xxp&SRzK~#0Qr2QwNl`e$mg>d(#rJpSUx zkvDY|Wt_ss!7;AO@A9{x_x|kV6v91sdE#3$?)&~d_ND(^sy@LCMQpkGpV|*zp@Gth z<|pJbC2VK>L|m_gO%bZ+@-n`j62m?fSbA`hfyMy8mw2?A36xoNqe?q%*o-4-6;aAH zHf3r*LI@F?YS-L+zbKr(V0%DD)g-+C7S&}_sAT9eDmFu~cXfv6`_H8(>x7XXWNf-5 zN`0_LunS~e?C>w9%;eVQ$_6m@E`qUO;dBMLM~Srd?!#e>{v2jv?0l)m&opVf+tkT+YPLay3So7N;Y)s?BzGz?f0RQ1cx2&amos?q$PCmijJ7{n1L#Kk)|65lxBL#Wi*?`+pPzhfbUVU0j+>$)WDd$iNtTeF#kKT;3ilcjI6j;saqTHk8e*Sk-M(H`ueaWb z`rf`3O6`dFv|Ohs*E#v}BmD85SwhFq)wAk`*AeL&S6i0jB+!5DMf%DRw5A*O211i@ zHxw?z7h57&7hb8R{KQeJuB1Msfa>?^+|m@TP|o-eblB*KpoItH$o;QYu(%otM&^`6 zup7Na`Y(1jVi&>hTh-%}vIe1^ij33(@So9OL=zZMvoeJCPa9QXn4O%g7qTfqRRCLl(Ji6QZ)HJnrAf&A~2~${>ypukUwXop+l4FCDu7!sCf@4F#>f$E4rcduC zbRIqW4!PP{nxKf~Q(9HBY{nyn&Sd08=PB~JQ0P6(k6D;D9`ENac9S*z+PLRB^?-J+~tOHVlB6I7zes&OR6Q{Rj+8D(PlqVs;edTjDo7#bo$YX{o`7CD#a0 zkh1$*qI)|a`Q%mAW?mqKVzIqYD9}3%*soxVTtQL+beS<(2T)IxWlLVStB1DGHRweY z;dU|nM=>FwLBOG85LYmZ0V_PckcPWf4OYnoq$Gc#G-1jF8oWjWm*_s_dAo?wxBz=_ zBivVeKRU35r;3zYS11+yR8dl2pj27UNy;-@7y$ce>yKC&%7;1IJp)h`Q&mkxg+PYr z9EMd7e0Y{#?a$$q1f=1K&%^9A5}aIC!7LB5tj%0nuZJ-{)M~nI+l<+^t=F{Sn4AjK zta7|P7?g5EezBNTUzEpo7F5QQP!(fu5879$8VgNgb9UZCA&oqsL=@tzG#hm(LOW5L zOeWDarzOL!=iqWv>`A8c*<`{F)5Jzx_zH4Rf>Lz)to%`&c&PA};zSb(D`c}i1ugcc zfMG&`LmFCt4u)ElgLf!^dL{Mkbn1e+^U{6b-JB}~%gQ??##)821XPTxr5M%w_o;~c0s1g7xGgHO*Z^H|>GO z9a^^eddGQD#kq?rIJ3PjlN?rGbgsGzc5QP<({@bT&=;QZG4`KX(2aoe3Dp&oDa+la zG}Y;3{yUQi!qAOos2&|2AmaEnoO6J@O{JqQaLYy19d%PJqqYgdU3Q^zM6G;+1^+%aQWS2EyynTSn zZzHptQ(|+E_}VwU0tOCr^a`@0Ck;NA9Sh%!qEv^lfShS6RN`$3-Nc?Hb_3~~ zVQV?p9;TQhhCcp_uoCD zX__udstV0z+WyMm>!KuSgN==WCP^YuH9cNlj&)Td zL{rx%B#G=-`?TrZ=O#UK{zvAZUmtyWS(8MGXqpBqe;J0qa{cvh^;GIaaU3~R_44I% z-dzz-iafXzf*IOG+vo&3tp}i-yIbT_5^lAT4g?b{?DrN@{RXy|YOQ*8<1)&LWa8}w zX&f1l##M-EZ2IVO|IYbT6GcgJ7~@hH@29Cd0p^a?zHcW&i8JOXk|=7^)0-Qwd!4ks zEj`ZFmFU&WYW<1+z`gkM;Yr(r{S1{#dhIZXPRN zDyi#2$gkJ`sd(3icHP4=5!I*zNJ!}jT+%)0*=uxwL$)t3U@A=KjG2+|1Fnu(8Qz%O zJKnu6xX5VGj(p!(^uj6le$);qQ+Kp&JvSB*YQ%CNlt=>$QM1p{VlNXanq&?dOuqiaueCjVyKOmpY1>RURO zqc)B^}pL4e(oLg3meILh~?7^wmk%~Xxor{Hu2DB5ym%lItQsp zqpB^qf)}2I5$*xP6r)?1h#1_-hLds1i8dtJ{ZPjvI~m=Bp0U$T8f;QdEbx?g?f!Rq z6mA@HOlUf44tvAgcOH?H1kuo~K+A-CPyJFzjT4lXk|ittCl^EOg2&@=`LG?eUE$v)NM+Ow%|&z?U8ff! zt{MxMvF;_-RG`>Z>2C=(n_>Kl+7W`Eg+J=GBJvSr!KD_ZfhvdyEAWlQ-^>{%JSP(> zzkWanm2bTTmjy7)+&Nqc`-qwbe}sbkLUH8YW0&w3I57{*Zw|{!uJ^YXX1H7~JbbOh zGFcLFR$aXAeQ;NyT*em;YFekai$3o}N2@^uFSv#S@P7ZU<4Vci z6w-ND%FIEuicWgCt)8q!MPRQ|)~aoohGuyR$zZS;?tal=6qNzUAp7xPvhD<1-LNen z@_e>S-LQ_Zk16bL`|$-IuV=0w@>9tq?6o+&Sv>Yz1r4Z00If?n@eHI3_7<7bVyNnyEZ010Z(WAw*1 z0Rvi2UV_-lJxo1(Sf@~aTI*>Fa2C#;C!H2PY1w3bziqaB7Uw&7Wyjw6q-Fj1!o+Di zoa5c~M&|#R3;+4>5fS?rd@KT(O+t1F0ci1sBG+|ZEE>9Q6pOxL0M4%w%gU?#B<$re z7>0k6t9i>JYs2E>whUiNLV0H=X{9;;hZG7?$}<0(@?O|gp|!k272`+-0M@9~-1QI) z!_VV_<0xbo=Y7KfqBu^W=95Lfkt*+;MW@hB2vVQKTM_E(Gr@o{#UyNo9`jU?!Dst? z&`HdY6Pxhnk+Ba5V6>IBc@|1L2BvVAYLny8GH z(V<^cSNBLT%Ye$ zZFdlPz|t`4sWAJ}I?oxyzKo+NWn-3&ng=PxaVFjB$+kM&@gCHl2fSrouZ1 zFRfg|9>jK{J7uKF&Y0uovYrRo{R1AIkB{HpwQPU4PEm*&Xqz_zSS=ZTBa^?ty6IH% zuTJ14!$@`pK!^#akq=kFC?(D$pLm>Q?3hkDnPBoXKl*b^>&;q>eMx>o;dngfwzY*~ z0_iwgduWKBMDMc9CuM#;t9m}Qny&VHrwhGd?Zh9JjIA9Z7$#l>6IQQqYX;9a`Q68j z#29qk9|%M}{7A9bUMh87H%wC$7t;6xn@{;N<@^Ho*-al~+wRgYamr7SV4V*ea&W$M zoMMc(E($S-L_!ks2vmIw7&+Q4Kd=PhwJV)+Iv&6Lv59t@C|SX|G)!>D3<_Iy^gkCU z=mGR*^ltQFl!XqbtNqoX69+1IpFcuaC?V%#PnP#()hbXU*7E{^Cw3i$S+r=Wdk>f#0-H|UeZ!%vF$&0t^ zWvhgyH1EZQN4*TiVq3m24Vlu&L9rK}nb$r)jH+`#Yko4^NKc~Hq>Beg7U`xCs$uKV zIq%N$oTn-0mX)TKHN6VpFJs-ozP?#zdY5MBP)zpu-<#0pu^;w?I8W1U1fLQ02j-n@ z@stJcs<6cuoyzK|=Q0TN)aPs>1jq57vDD93s%3c~C%=zwet1LvFe^C}sF&s!V8Rn{ z;Vo)ZCg`i5F)z>HHOKZhKr?QxAhm$#fzahC>iv2glbyp4G<-(}E2ij{fJJBeO0-nA z5hHxVbWOIcLcZ!)0GKyVJ*bBjd}XG~ZEybLBNPfuW&!p)MNQTDQx*%H6QBNn7O!yD z77{zSzx?r^J@xJ1ZKWsXI6v7N6619u?qTP>o&1R7-T6;wT_W+Z$7fzrBN9rAF96|b z(OyR<(Js1OCf29A>H#op)2by_1d($TuSSt|DL7I4%H%_bRGD$V){~$E4$y2{O#|U} zFJ=b{>8~e)@Zh*`B@a%uq3*b+S2~|1TBUy63sw?q*Zu`=xT5u`E2I-wRG)`nqHw+& zviVHB)!GKl^4{D`mN`N^I3Az}x5ByK)Vspq4*=EC>KD`3mTc%oV75BeJC~5}&R9uw zEedh#nbDR2A-C*z;|jK|Dv@PUwQRfs^?H)j>yWP|_4;z=uch0)1UpvAF0Gp*zgj6v zQn^y~N2Z=c;^ll3pF_Q#tRZZf6~b@c{KapEygEdSYFk2mgc@aasRLY3K%-Q}FdNc{ z<;m~6agrbS9vwEk8b--`Xg6~o2yAVypkgpZUdS&jEVdhPGSB&JK7;JT(!>P-?q&2C z4DEWnr_;rrWY5%UM%XG=@^>sJj$GUKpY*F1T437mQGTp zw`43xN24}NrS<6Phpdq4um>m*jHd`SpaYnuX4+;55SosmnWoXhFDB}ij(Q6}CQTU- zLXMWYkV_SNd(M=zPvx1}C))cm_{bC*AC7ADS!^IxfRxHKR0oPjvlhfPIxcws&-*Y% zG4U>Ef6giM=GnpX(0m?d;5g=={R-RTbN|YpnT`X0{QUEM%L^6}0>2DaUlEPG*BcHC z%$ON&fxX&I@E>|N!6_``GMcwH_>dVod|5Gf=0B3JzFD0a$PI1>4~de9+NUcKkR~HRpp5aK{$>x7GL5%(t;8wv15P;v!m+tV^S7Hg({z zx8b_?xA4lDWPq8h!`pP3;X!idHt)Nqdw7tX;nI%IBm>cVpXITd_Hy`LY?hymd(czp zeF#a0q#QPr91`=4m2NDJ*Q=A@$wcGxb~zQ3>M?&<152x|hm`eRv)#xs(W(4uB z1iPXyo$pxf$RO}NY7i}mSZPdjro^hk{|7pP%w&c=w1mN?OJnqPtu|)CUJ54a1^InW z#wX_*om3aXh+m$;zhNQWayvrc8xZjmesmabs3dTJDndhWqsn@9xSG04gm?q^+ieYC zR7JtU8TEr6xgS`#Bq~c7FDW8rlA)$aAHo>AKcZxNXU%p^CQ2Hn<>)0^eY!EvkkoTb7~3`qzh*^^dG8qxMVP|HN~7 zmJDX-3WS@bU||fSI+BsSa#HqCAo&zPOSlpJD+Y{fIS*tTfHZ*5vLcEmF?120#%}y8 zg+EhN-oKQzuPF?V6xMxJPN{$j`sz*u=p;~1b8_m~;i`QjpGzWCo5nr$oK7>uYFF4Eh!(LZ|a4J*( z2VTswxQ(v(k2w^qI;<9eaqG-w$5~!`-W%BZ`b*k9+03m-yjs2I&l=UdS`eZ4_FPf= zbYNO!9Vhbuc%4{gF#mVpy~Jglxlg^@E9t?M>;B)!@@-X=eQuNGzwvdR&A=*I@P|K( zUy-_mu|OKxG!Zpzk?T~(q^b0}AOOf7yS70U;4OnwX*_L1dnsT}6j&#kroWs0EbDiF zOPQ^Qn#?h z?aIm+Tw^b=hk{eq6f9YAtXBPQo}@k@>w}56M|s{*C)M+Fc$EC)(hRPl`*Bp z3K*1Myqg3XB~oi;jpjhIBonn$+K$ zq{{P$W)KrMt{W$T#^!7Vq8Ec5yh)%cz!t`^cV;t1=^x#x`QwQUYO5i{5(r<% z!w!N#!supfHemRKzOF3WJaM}20&v}83(aPfZk}+NwHnSZLeKW@PE4azFX+tsku5VS z*>-_jCOrnRK_e?2sZkEJHREQyg)6C4VKsX^3049aEIAEbyS}c%9Sd@m6??6kAJms$ z+~n%|`n9l!lfgB_8&U|3jP!b8+bZ;W%J@ZaGR{J-86+5CS*m=xj?h;{iWt!iwa|hL zY{z)#@f^SS@pl@I4KQ(;tUm>_SLai@%v_i}&!Imf@=;vYD{I8asp$N}>o{TrC0>YT zItpOf6BgIbzD4$G7}p#_Gb{kh&GU7 zWAWt4MZ+<)Gda-zgBiv2EY^jg3Op{euRzzJ*Py3t*xL@y(s6=`XF$3**IB|I;8l|$ z-3m1--`8jc?kjwLV;D5!!LI6LB0yqPQl9GZvnM^ zzpziTjAx{RuNtB8HX8`d8Lg7YV5D{oS@j>=w30AWG+~PUdF8GP_I(V-e4JUErUopt zrZc%eQt~z)U|v`KC-}AUbp$!JRCL1t1-RS+r_(;jx!@G??mKMzf>ZpkLwK@K0(y~S zKT>cmIL$^;qC1ACTZ? z1h^sU5gT0|i-D9X1;CA4Wy~I2cM6t7b4?!e=g+8wM(BAbQxd(-4=d<+_kGw2(8Snxuj5(WeMPfO zS6#|5TtBm$Mx+nsL}?N%*-;3hG?+8bW6u??Jmx+=6Wdc8&wYWybA`RNHF%E;;LbmM zrEcep^~O1M7#{w=AO7%t4j*|*=b+$5CGA6Elzk??vt+8>8|tk>CLi(=>KPYR9fS9U zp6RG_R4!R49~Y;l!%jy`lQY&Jp4!{n+pmI#;a+8Z4lFcAy}DHf7?}=1P0;}sCY8&R zEM9A(?~adwIcW{-KhuIVxA>7_c$b=Z%URrfJDI?qXSN)&^NK#a*A&;E%G!y~>}G_V zVPc)pw58abLaC4@z6T5q8}W0}3HEa^IVpz>Q|m%DZe(tb3GqWC7&)A_@)s4%Xe>}^ zjCsd}EIb@;1WEs6*sUu6Eao+t8HhDB!*M-}Ha0~FwC+XhSA*?>n4%4-nqfMXmu*nw`gRFCXVPg3Qa8FoJ!gjSrC!0stmX+VAe~7qAAegg4(F zRJ*O}BGs~2>%EXNAsKDNV=B=+ePL@h=t7ophhTqEV4h9GWKrA<{lYdIu>af#oE0G? zUH(7kKiWW7d*~Z?<>m$DBt%TyW#t+P-CS=4j)br)$VKNmXLhfq!~2$LV)iC)h^F1o zWEnt~+5H!VJtJY4+GT!4*RLU>sD4rvMRJWEf_ph1!eJ_RD{Mt9=_@t)>C>QTznWiF zT}O5Nj`fQD{T}{_P*VPNEJeR;I|Nd|20_`#Avrn77^+PamlDwlB z$JGiBAX^EL+v6RzwRCfHIRJ!A=LNq)O+gD|rvk=|!?h8^_Rk4OEr!`=4>m`fP|a3( z$_XBguIuEZee-c`=pYnvMd8nn+%v>%NoxWfGCqD^T&u;g#|&&RFOF-qLn6h_wXoX5 z^ZCFT9*wT+Zf~b)Ueoevy1m`KZZyIdtk4yzA+0FzF>Tum{x5t5>8OaVLNO%s0>s2T zNh=ze>Rye;TtEJ>L%>ijgU)-ivGj`IySDG!mAfmJWzYYFi4{>%`OO-8@$MI|Y{SPz zZNMCu&dtDQEvs^O#rA#M{_jjb$5lnFi0s9?U;O3Zg$TtT+3~h1D9IdSI0zm*PAYGJ z1Q{1i!r=m{7C*t&E+p>bl@t0IMEeozTBx^$5Y+ss=dr>bQ@SAQ8DrVZzl14|@lT6h z_tOPm{DAlbaoio78B9<(S}tm>Tk-Xijfb~U1<(w~0`=4-`ti6BWtwQ^0+Y|5CWahq`oX=eLOnwps6Qm#U1)vN;yoH;(8K=@A{Qg(v z=YRh95B#j!%y2CDW6t{*7vF#LcoH7`g$zTPYBF=P_ONf@jhwQg{`hK~&gF6dxm*rH zAHbg=o>-+*Q7K{tY73U}AMgK6ch|ok<>7Oi-ElC3n%RaN&G2bN>bRCQ*a z4obhc{`?eALDRoD!`2~s!Ik%})$#+|F+aCo>+)-U&U7qz)3YzmL%!FHBQD_~!;@fv zI3tKdUZanaMiuQy#tUH znTDx33;Kl)-hD&!Nk5B_+F(#`A+W^#kR;mE$hjJcKd2rHsG{DQ!wlymZdI83S1ROklGtR?A zwT0XlSC;%dB7L9mf)>c)>lmdeD(~!%)gxJrWmU-0aK-*V@qx7NjT7a`(pBtEz(EVUuYm=PeH5TP z`wR}DtAAbL6mo$CY3>oe0tpXi#cqXDz&_T0vAgJqj?Li7dj|Rn-Th$+PDiPgYmo;s zHjhE;kYEfn$q7;y?z}Sg%>~&0*xJW&WF0+*-tKAFs(t`&xi|&vbdV%HQKh$@juL^- zPdvC)89F0lRR>7pTL>kzG21WAi#eRJoqk_a&fK)3^#`ZUqhrYq{T;^mhn29pbe1QV zs-faB@BrS+`|x|31KQRic3>`fg!6;HjpW$eCRudN%xB0!%Lp}~HABwog9p-U+%K_B zt=(TU|IY~UXms6nZ>2u*gg~3$a{)9X(0bcVzG8jl^|;aeKVNghb2;{@7mb=_}PT97ok_l zxZ~F|M){KLNCp#*eqFGdV}l8~oIg!x{^X}LB$-!;$<98o?JAeiYGAE2rg;S@HFsnv z(XPIxCYr(pbbuU$8a+$77sLyK*#}dxYu7LS#a=+8vgNB~SqWN?5PkkOlS#rVgFkW5 z|5STEK1ggJ)f*cet)a+?ZA4Dc4OjC6YExT~_ZgoC0apq4m)~E+Hg*PR#{Au52W3F78S_n8#pQEa>WFj$lPyNQ>`6{v>dinZN9Hp zI+_83`)G0ouiwP zb4qW}Dw%7AB>q)>$X@|*Pl$LW(OQRG4|$$3rUPIeT3)u3Egq-2=1x9{YfFW^(V`n$ zHCI*+!?Dq*8iI$3Bp8K4cpk=3ucZavuXc&~m6h%~AJny+>1`}50L1apBi9~DC8=nd zb!XpB!lhk zWKbys?sQHaYlKyq+KQgnHQlzkrWG_D!mtltQMKku;A&dFS_!M=a*z#&@E8x~eGVz-5!pVr}o`*7{bv zg{@eZ54A~e`AEJLqQ&TrI?uriEJVHD*3Rg3BzjK2t{PVJdC_88#d9BMgaCA=S*=v+ z$M_aZ?c^l|x7u5Za>dF*=1t+s2D&a3qIakQF2-aS2c(Ue)ETATowr6a)^N}S6lmY| z6a5R`4Kzjh3(bEzcEA*z`Mw9#CkcDbQ;}%RD)Z~#~*E&@&E@1A{Z-!ZoKsMa6N}u+F`G>a72(6g~ zwEAK*Nyn^>*v??mQrl($`rD|?!^k+)oFf*zukQ!1$hu}k8KvxV+WwBMQbVG11wW^hF3Dz`~MQ~O+)UA9> z8R1$tQS*k+g+W?(xg~qpamK?kM(_I-p zJXQg9!2GzGt_#CnzxP}w0b3y|P{0VVmbe=v!`zHlCSWubESFG;hDmK4RnAOpMh3+oLn>U? z6O#pH$fk0_xZX;J)L<;!FWv(m=>{6Zfvh*lHAEpC zKuBwKwZnv%&H&ZV?brT^`bK7FFhLD;2G>r`DK8Ge1;D5l;kqPoij-5QN7s#;Z4Nw% zBD8|5@9~&s!l>Xn_QG{=`Qb~}jx!UcGV)Sx&};=sj|eug6Xh|`$*@^OQ7xF$MQE)a zH)A32OZGq$@*29h542J@;U`^972DKt^78{pa=>up9nTMnjw`JKS@AJ;m<~WLp;&ud z1q{RaSdJTI+bHE7SY{p!iv9Uc-?^>u-MZg46%b;p#fabyhFfz0K1WSW#c{%pdz0^X zi=NLzuRO~ulFZmqO@vZvagr11GW2RX-bLn7hr-yKZwA{gK2dNAP7yGZ1I#ZhI7P*V zeOr;`J;k2yP1FxwfE+jng@Th5ud08E__7P8DWj-n_~!LXXc)#to%K6nO`1mLi`WUe zaE-L*OP~dxg0aQ<_po!ULUZxO_4|vV3?sx+3dd|F$K%Uc#_E^JOyh@5Z*GALQiL=Pv$R2*G0Ik7=VMW65bR)!b@f5Y zWnQZrFicanOkmv6Z}Wc)e(AB=(o*fQiNXH|aJ4jl$D7b;1Xfejkb0&jr@DmKlOR%e8q4i9E zfaRa2LomE>%f~H+)(Ry*{PlR%xp>e&Q!4y4RVe5ez=Zksu&sHn>H}-nXC{L*i1X_!GgKu}6eLOEWz!`hS5!qYMBVGj~vzgC%lB~ua|uCHZXuU+mhkXmom`tMXD^6$J($5Xwjlcr>~m?uf6<=8~Z>NdTa#0-zESz=fOE z*4TQq01yzO0w^j45G1DZrOi|QK9%LZ#&;lhc?Q1(Gh`wcA<9 zNurj@Z!<3`N1&&0Tx^{l;xeA}qCd6{&%(_AR~Lt8*nzv2ixqf(h+aU_r%Bun>9E*1 zP+48~L1KzIpP34iz+cxYx>YzSh6a~?K2*&FgX=msgsl1@iYn7)GvhvD74gj*lxHJz z>AfUJu0n}EE2>RXxQoc%i;?q!$)j-nx5{e+K-SXqk`lfmWaTABY^mmwX9D1J9|YCe z&&v;g>v}aE9_fr9)aSEL5?euTO=n({n%9zQ`j?;+znDx0f3t!}vma?fFS$Gzq?z-w z8IWCRn_TjUFaMB_!Skb9qfxa~RaH%qF6I?JK*3a1Rkf;(M!oKPfc=L& z{J8p`zJYRXHKUwuiSVhYX>m?(5G^ZDz<3YHGWJgl`nAxuWl7TwQ)S~hfNC1LCdsxP z*7}1JK9=RtfxS15b-NX_7DQ41eKoVv?H=n#QP8LA*Y%_rw~*0<933T1OP8^0>EHo~ zJX$g`FSK&R>q(JuxOOuBZnW*9rWAesU@PMOl$*E}rWg=J)3Xun_gWtvv)!%hwgX7# zl^{Qtf`QEeqS!*_opdI$1U#{OK$!8{iNY+~vDVkumO8G(MNuHaqRamCVp!EB&IO~< z7;s5&w&mN2Km<|bj@wyUTVJ;v+hWSs+pp@?a8VZ|K4>%yfpdxPRMoV5(`b*FRPAq9 zxs<^*@Ku(My703^=f=4rhC_$$yYG!E`OK6dt7lxg}frU)Li1B;i(y-mN)2&&^=y4W_~uP z@S1k_ro87eD)`}WKdwS=*Kri0>6#`=#IDzulx-Nvk1jzkPC=pOjrOxe)AWY#lbPf6 zZ3MkOO|*r5-So#B_z(N8wV`eRxKPs)?YK|uL~LV1&r~>@3w-0}Ehw0ZaLHQlbya5j=qvjIUf zK5|5zJjD%&o{eVPwrZhfbnH^2q2a;905v4vxSwf@RbgYwFg)`@b3SPnvS*u7iaCKZ zFr3pk`sDy1czLF-L+b6Yw=p`KxnI|)o77g(QSBrf2qm#Pv4(s|j(7QfkRj0b>kaWy zV(t=Bn!I6n( z{0+Iz!V;&<`w%rv&ofQ>p{w5d)|TgweXm7>qS()RJN-3W zdiVg74?nnxtlMRRsBhZ>Q}|(1;JUKBtms@c*84?PR@Q(o%}>5< z$q-+zG?4rf^9&2wHvOb7LsUaH3==woIonRO38}bZ?7HC-#U{O=^v%&rF+nn(l-RFR*j;1jNzDa#WYYcthW&>~4=Tb46 zX_zN<1H0C3OC#TNX}yqG1cD|h)TrT0bD+%nv4q+9ze#HI2ja1S-U%xo1-s~mGDJ{a zrOLk3^ouoT#;#kZLq7qnCQ1e=3jhU=fwhQ;Dz`LS#wy(Iv11jjyL;{oCCNt>NZEoR zWkckpGi2irD$mS$C|TmxJsf+ZWH9gr>hu7YZ;sboXU5pynSPLuOMj6kcuu(ZuJsDm z7$|ZDOCGr&%tLr4etB}&>S;B%0VDpRZ`JufSQh7}zTji*f8iA8mbJ(E-i%rMkM?HH zE$bBaeSGSNQ}0>UUfRgFS&^QhW}HqXd8S4T$^>-B$@vB(?VpE`cmAA^JABCTf#+kV zmDW)OZuPiKSi$Td+-YMj8s_a!jWmcMWZs4sAndDJZ_<`ffoX!1&R8mnSQU*cqYg^Y zVRR+B0ojh7TpBfpt7+nUY4lk@Vh*FGEnTm}=;8(7ywaSA7*Nb&Qx)i(suN0LCA< zP>EX)|5&D@G=Z`3WkL9|P=38Ezg`gb9yeQumY0GLUtqRrS*mJTrp^BF@nC8BP|JMw z^@8wvS^kR6R`CvPHU4q9KUiAcjLpYqwJN|ug zjqVQD+Hkp*uHoIe39aUkd#Ej=5fT-+q8R%Z_$Hv^@#vS_pvk;t8~j8q>J5&MFE3*v z$dp_q(RBl)t0cg(7aU(+#>y9v@$4+QU&xo|mzZI0e1eykj|ZNNfm|tpu}-C{2$cnb zmzR&13%}-;5cb^gYwTQ`J|YO|1?Zk46V=cVowlG%-lagrIeUkf>&@(!%ER~b{v}q& zY^~4A?h&}u3x>W!6_vI)w=6&P*TkAP@Q!MT+)RIfZT9<}fsdQ^u1s5&#ks#_=+jx= z{m)o>H`sKBt9#evBc!oKQeP{fQV5GPMueuOzX+CP{-)kVah&5EJnJa3+6%T6@!;Qo za5fo5=nz6k(Q>=Gu!&16F0+nMLZv9%3tND%DiK4wUf!oL1-jO@FU%$lpC%Y)ZhtIL zdV={^de))Vy$`h2q9C`FMxp7XTO)_^f53xlbWD#<5WOUIT1FQ`%4hvE0g=m$RnKGd zeY^~D|cXpTz zAZCN1si+xC-(Cc@QMzhG{D~17#Xx&{!l%%>XVjYU{<1Fb%-)>F96=>SlDp&fpE(#l z+{W0;Ek;JN@2?>V)Ha4@bM1+ENuFZH0Ej4hxcE*T3| zQEV=+Z!GWq_($6{mqEoibQ$#ZOUvzzU3&Camp9gz8&lIEtQuD6pQUj;4dsJA75U^ok9WjxIFh?XpFk`h*@^KSOjyy_rNGRB8uk10tT$a zcYIU}B?v(`g-S-mYCLSz>a$L^@FF@)3lwtUMZyNrfc zM)Kx6AVA{f--NHi7h|q9Li_wbXtriN$=}3tSqkXYjNuU{pLbpk!&;|Pdx5DcljRqZ zBS(^6@8w3f+lZnILAe}w-v07ee_v5F?SkXLBM#Uv*E*eA7``COOjX$nyOi$7UH09?zJ4TWk!b3t zIafJ_|Il4?iWz5&uA8UPE=HbTriS;BH=YNYJL(-QTPMc$ zA6HN7=gsdYXw{|Y?h*gb+5#FtEdBl${C%KsurW8Dt3OBei$jErw)!x zc2f$TDXX@be4ZT{G}{@BEVzad?B+#s{Z%%y=k{PGV9Uj)b<$Q3t)f%t7UY6+M#8=1 z^s48St>EjK3ViBP0Jvyg-Kp`GE#I51a`-8x%qSfkO!o1%uBwJ(shU$R`zDxCP4X9^ z%R8k^-v#_ze@s^OSQd#Yze422>f8ZYUr1YeZ|u~J*(uIqq+iG+{A%ff;v zI{BP)BHiGUe5E2nub^`oi-I7;wB*i23A7-{0HQKi^5xnLTTx`^8_X-}T*j1801P8@ zcp(WeP$~NRFs>jWt@U%&p0({!^rNyPw zQVc`HH;3!xc<=<1gEJ7{wa&cp4Mt%;p=i7Q$fjGA4io33!)Rz;|Gd&@n0SqHgL-Yt z+0>+)HKukk)tpae<8ibjpV4?a9v?urtgSoYc05!-C}yM(GwF1Vrs-Ve6cTQYbI)KexoR@^Wyayf3u9yo?Kpx-O1!5N-kOC@!MVyA z!ud&0uX3DnfnPMtOW*WR2}QK5>{YsZ%rjeYqM@o0u%(a&iGmjaXm)am7muLMs3FJ` zU6^Na)uOSx)8O=ZZr_7umpy!a$rv8aXCI+vABQsE%NqRyg}Ri%BYhUg03lNrfz?!d z)#w#m_k>gCT}B?Gd~%wxiRm~1j$^j{p94)GgAAFx-<(s-*p1KlDOe5CpA~!$GDR~O zr@=sAN88osrm_*0kZ3b3iMYYIE ztgdK7CHJ$DO{XxKPAgcI>@w2hE!tG16GxdCu(3o5SKPoZ&y)qKBfHN+dXl3sC7AhW zt`5zwJ4I>sy|>VgrpQDnKs?aci~Ahq1wTROf=KhC&+$Wj)X>?b?C-k=JRv=Q5R_mU z9j;M=74bjOL*lUgJ;(VTz=lKPUe0^p!&nfen%i>xXEg0+@DeQTT|5EJi=Wo@PxGo2 z%*)uv>kJ7%gHC4ZD-*>#@x2PWUjk4Y)C>P{Q#asEW|M51aq1LRO^Q(r1$3H7XB2F5 zkkN95EwJR;(wN4ut?0V)6l12M>x%f0IOUi-{{=dy1|ry}z2Uxx9(TUe4n!h5v)~xJe~pBt%eKHNvy-0 zybeAodl6B_PWgqLX@J~Y)ZL3US{N#!I7~_r(0i#$=#pD~0V_H&8 z&OlaS=1Jd|9A_wC zKBnnwh>B=c&dh@2vXoet4E*Y%0QM@YLR(HognCClWME$ z;L?d8icP!g;0Neo`sw&xJt5r$?lh{|>N9k6Kut?PxQh;*aw>?OCC(3cd} zQfikoz5@`vKUksA!OP3XmlqcNpwccDfh?6vQ8b#I?_aY!qB6s2G!?Q$HWwC5Q3UBN z^8AG+(q6X3qI!4KSb)!rl59@=4j*Ix9kB|uB}jXT1VaHcLN2n{SB#@;i~P#{i*!%z za=xph3OuM$9z9@HuxHlTeJ|W!U?&KMEF(zYAO0B+b{m~&dmF%YkFnbrvsKj@h6z=*?YP8Sf&W`mdu91>_u=IgJ0YeOcokbT zz(-Fn$7H&9E3izm4se8U!x`=GQI5gM=DVI6H0IxG4_?6^ln}`?n&>>*ZZ#6vBD%6% z-nSV}PbwTJ1E~rhgX3Yx`$UQ8g%+AFd~-#-Qw3MkAX-SJYIUhs4WsC}FCO&Ioa*<< zdb9wV<`$x9WJ9C106wd$n#i7R{zXqS5q7@)BvGc8={>#KS=2kll5^F$r($fTP6^$; zh~<@&TBo!5v}amW7A2y8YY(IpNbt6{&XA1qs43+NyMj9U4ctMuQ3k7K4Wq=0sO=Mx zl~86|E=+;$Qy>kf19rn@lzqE^48vslnCDt83P4*uuNQ2E^C^tOxtFO{J5cf&;NDOR zk@zDwA<$`@AvJgQ_5uXYx-%Mfbo*2X=|M2!D$tcp=goGRtkWJ0EoaWMlVy%mC=|op zj@IdHIy)&dy|=IJ7;JRf+3a*Qhch-`7rr0_!gJT zs*xFiez%u}%^+H6j2zk}@1=rC?$yG1hOW2(8WW@+5gJQDB`6Zy% zHC8w2Tv`Zh7cF|a#mEKutv7071^rOTh!-lM6)S0ILNnk;@`t)LjtrY|G=pbe@491H z*xk2HLyo=EJH#2iZ@A>|ytg<1fdM&{d*sMT10(IBc@NaxX}H*%kLxS=o*bAm4Ct*J z^k&oMt53rZ#-!9GWzmmD9z}YF?+Kp;2ZZeVokXbrhk81&m{j=laAPJ?7b!$m$cF@QN9@2oIjSBc3?gMf4<_SDv>}9S zy8kSxsUEusJ*13>mPw*=qASwh%v!Ja48wJ#j%q?ea}JKrR|ZwUx^=h%L!_EWhEbDV zbUZL=Ijn**(-bvltELKn-UaNN=O5S9`#Au=U)3HzZw_qgtYOM@hsSnrmZP&Ylw(*= zvLC{$x?}gnCN&J&6ay^2VLxSR`5=imo$h1*yCpOE3|f^?$S0&~qhbN(b4FjA(BlLA zP6F@ISg<6kPlqBeZVr&8e=@1VE~w{9YQ6`W?p=pd>RHBXGH6LCnB5bCM1Ii_Jim5- z;Mhu|UMO8;{G+@m-><<7FOGyD{teYDxo>iyF6}gw%3@0bA`8;c49^dPtQeG2(R>EO zNz{`%3?>rz6A?aj-g7av>uz7_;$#-3?Gswj&cHCzBZMy;;juE4*7rG;(Fy#VSatqQ z&C=whHsZ9)eXr2l?9^&%G;x9k(Z{j0Oxt=#^}EB0e0&&B7g#z&{ddLC<3!WxbV-P? zopF7TE~l(BOX)NQ+aR^b+)Gc8!k;JX7nx5#MK8(xQq%8)ls*GZBSDZqfuq;MvGmc2 zf4I*_0X}*Zd>;S$%LgzOlvW}nvLhH!0OdI}PWRFkqG?-!;D@>6kR>BRfQ~C^%8E-s z@rea}?G*&!K9kaNUy_2l$^(4RxV;*59|g9Gb_9ISAgXzNi}Ix zWRsesvwQY4-@>&m4g$Yg@u7$VpFNIhRlHXZ3}Ej#>CxJ2}eGRoGCH)+(<_8SB&Ko zz7JW^FEQ1B?dy2#k4C}J)jySvg4jYP;z`fOH1O9Kk9Yyxz-`I&e2gjmxVgJTh9UP(s8xbS7ujg)Ps%2ds@0@hKkrZV-3%){*n#2j=j{>>kt zve(fmf|pNEkhFY z%3$j{E5G(-rH6mBJ7^2-#=js50%Y^q? z>Vd)d{?e~6Ga*_=C(CFFs)5onBLQbR{NU-V>x(vko&_jL3DDf>ml5;=9d~V$hCq<> z(a?|2fLaI>mUDvjoc+G+b9;2XA9Hnt5CVE+m=sIarz3mljp*H$@J`aEvCYOp!VhpK zfVGyfm*ZXzi?n=j(wF@JcR+~0Sw{8cL$f3%IyTpQ{N0q$5rhrUDN=^P2Rv-hwJ1nE zU(wASrlVx&SHT-JQA5?4ApXow&Dg6OFr=R3>+9L&r9%>V{&wfXyLe(Vjhf8`X6YQj zt)D)zY5Uq+8)jLb%CV+-h1y@A;+I@u|+SZoJ6r?P5eEIDqYVbo5Q#m~?E z|CTl3Rg_l?{h@%*Q*Ju*X&{pP1)R&P`*5#ga=NePbNE7@hyjf2cN;T=8r@qWFQIz* z8FajmeQ!yIQL5#i06=Q%fCaPk3U43%$FKZx(Fm%r`3EZs*N<*zEzSOzH4E1bC=)AX zf6SwXs|*{#LHUQ0y??*@-N+Zzq7f256sRLoDB7z^>Q(!>%QpmxDm-F5RqtY}w(RP< zUyi`&93z1ULz|dY<`OAfbTF4U+OR*_8=A{IE<0y|Z5Qb%V3CfTuoJU3^E=y%J!j@Z zGX!Fvb+d=o(Gd^e8r1tmjXM_ejlb$m8VBLXGVRkih#z^~sGaxQ#(cBr`LE-Bx1zdz zpBRU>EQjskaOwGP2Kl^Okso{U%ffP{*=)}L=lYM4tpRoab-v~nRJHGq<9xfbREq_n z_Sh#>QE+ODx6D6yxL0OO{o6GRx8mUz%t16mGdV+oK3qM4-K8mBO^`I^C9F6>hT8&k z`#oY3obE*rHhidI%lXYN3uosZ(v&xo@d_OtEigk>bTfZx=As^pY1TU8Kb85 z0(3rfRzf60e?rITariUO@TGJgz)MrD%bT!7C$@+ymh2=Fty>q&E|j3mw1~4?*JX5Z zE{`yZFCX9#K87l&fe?UCmN$iSH2Siy!s$Sq1{9)yvh7``_#pf9gTDGVyw$ef?G*AZ zVGr)U;oZmKo&V$%^6sv4TyOKOP9g8U+qUN)+kNA^kG1c;J;_id$Lqs{656+5_%_cE z`s?69MUkT(w;A`LxJ)1Y*^wTE{rXK1#{8TDN?Otu{F^%UR1)sq1NbGaYAUmIUFa0;Pyb+#Y>?2fqaUYN2Dh zqs9Vs;nXZ9E|oX5`bRqz7WubN?j0L_V18EQbpi{G5o-v-DGnphkIO?Yt10+i-w}12 z6q+d@&`BkLRK9v*gu+bysf^GEuIe5oX2J(V#BV4X)}X}fMo+3Wf4wmbgPyS^G5D1f zVEj*(p;S$QY7L$uX1+5#owFBl@I~|!I-W#}31q7Xtjvqx#{xpkQldG9W$>2BP#|NQaY&%+*!+PJeycx)u6yM&yh5cN0?hfT z(#hmPR_Z+A+f7hK%q7zy>GluRC#t$6(T6xCWazc86NSaWpxuwfKNI$Qrc<3ClTj{mo$E?u*DdB)64EQWpDu<{k?Wc#q*CNQ7`itqvr)2tUQQ8# zh6sjxz4mb(T0*Y1c)(3(%cX#s;fiwi;hp06@-p`19^B#1S=5rbXYaVn%g5K%+Qp&) zl#Kj=EHCE^a|xx8*a&Eu(o3w^$96CO?aWq}mI{SJVQFb~ zF7{c@?9zMbf57`r?wFA6cMy<_o`aXskI=Z@2ukrfGs%KXN(#reuT*CGHT>Ps@==-D z%UYRP+Xo%XKgH!Apr_ykx)TH_m>Di`VC*kD{E*DKFZ+mXOHT>YmoA@)yLt`B_=(fZnrp>z{2c6vIV1j>~q-=V(opi%$y z=Y0&d+S=NQ>WypbZk~DvI*UhqjQyob8Lb|Da;=9zVD8Ildq35Ws%jbwqjG3>MiuHoFYX5x*d1*Nl)dFt}NDZkDr(+;H zL8HLiy3FmES1_({Ney#nOQ+{q28KN$qxpd$|Da$W03`(o1pC;GOKQEYO8gc&mU?^o z>|tzyb|46WWDvaEz~ML!>WxX%wJV?7H|b-71R!ntN9|Jhd3DP<Q%TZjJXj zuajQnh-ng;fy6DSH?doA2tIXVGmf|pm>&u;5Zmu~C+C*+&aTt}Ubter?qT1@@3bt= z-+8}931!_R#64ez%-oDnBck?1o^#c)1hC8*Qw-%W(ywYOUci-H>DJdeBS{=14|=*qh_?LABrIb(BC(?%fUkY(@y5!7#Q`~ZR=3T3KW z0HSGDD&4Ji3vU^U!rq{orus(xmGy+#hQj{?4UgF&+AV1(dPR5yrpV)8bW`t7L>-YMeD?c}|fQ?RU z5L=Z?wyti$Y7-|r@;5dbK(fm3_sYAOQf@Tb!BU%EiX!(->B!%&Z_d2QjiMziw#l8* zDA#2%+q@`>rYNSkQrv>Bt9brGDl|9o798n2i!%nT7zp1ySA7gOJT)+7h6j{Z&2wi8 zWT}9JPj<+#UJbR9y|SuYtE-wMNJPKZ^+pSgu%qcTd6~(IO{1v%I|0KWbh?y;qCG2S z6)7|pMn@Vg7XrOq`3(Nj=F)N|Dlk!S~<6p_5dM z`q7cZb^1cTx1C@&J<^3O0)H3y8B#*xs|S9IJs<#xN(pqs&<$A@<%N1|7&0lL*Ain* zl0-?;sw%upl#$ zQbqIJT*o%3C<&3i!y$ci!xCV?f&>B)6eeI%W_E>_4rnWTjHJ zveJJAxfLe@RHSl2sm-j8RitPrdKOeNu<)-6lL_e*$dhL9zdSU_CKH=H$j^wHs84g) z*%~3v1gA%dm&CHm^!H|s0#l>UK5aUiO!nJCu@i3abT3pYvve6j{l#D{5zn9f;>!ov zfGMh@qv#HFHx5R+PK?60s(dBS%G4$dNHbK+J9)ZkFY?odj$g%@te^~Yo!fYn(j=xY zE1WCm7ywf>T}iOlqUf5+0NAOsxw^5>MmVuxzty@{ zC8x;}8r(fCK|`oKsC2>6*$Mq#t_KHM#UY0ncqc*f-p?&=#xxD0Yc(smf>Y*UpBW31 zf1TmFE%?ZvAD;iwM_^?SWjz09&e&zr~S4if7=@QJa~OeH=P^H6h5w)oVSBj z1G+p>Q(`2)#N{o_f8}!u5}BS9rIgv_w-fUD#zOgMKs*GjZwR=|Y5=hs`9Z&bfvbT; z9HiiT|8E$)p3MStC>;e7DisYS`=5W1wSAbnccp4{Ti*R9@F4$rVo1!s)ahm3=u{n!lH6sPpsOG60yFKV{u4!%%|HvqJu(Fn{l94Z&j{NbwT#SRok8 zft2tBk{(PR`nsU46g}KFaU~~8F7`t%(IgDktS!$ z{8PC*=AE)l*abTusrt+15gLaPsB$Luotl^DhQafC8AqzI#MVop_dZA8{r{}-A80mkPaV~1W|s(#Z;DWxkaY(y5A?$HRW&d0O@<6 zV8}4Mvd?A9pVjv=QQBClSo93lFCW0O^wD?>FX;2pC2UR|L5rgUYIbz6JxD6$SfsdH zw;z@zN#o`fgFepXTM8C5rodFxk88nOOi@6=k@ZF_#!FkZe~o&j!PQVPkvKnjjd(R)`tM3rXjk7TwFN9&|i&2pX1-&py$&X|Yc> zLX1Cm*X}*qn#@&`pjg=0Ji@U06vhm=dTqZ+D?f&^T*A zEv5(Ex|fKJu!K0JgIQoA2^hr1K355`BnKMH#_S8Qd=wKsgvDXNuJ{yvI-t_E&@w+7 zK7;*^{H7=<7!B|@;XG5@7zv~+qA6#;ZBW9lQ;wu~-BQV^5o1>x4UajlW)$)@?kBIz zacl3vqOREi*M&f`tv~{}Ly;CFRn?m!^PIfmy7?1~J#o&Jb{*>eDDz!nw_2+t5C3lR zgV@!WQ#4HotG$I%Nk64niVCI`JnnN2%3T_f#Ky;Aay-X5WT>Pei@14MfwN-;Mi;4| zt0vo`nPL1Dq=&PM89(5L*KxSO1`;Z^_-KV?8G9oNqG`tETZWG-jF#&gq(VN~?Guk7g0%SLv)@oNu83i;0AsI=BffGOO$Ja?Aoj8g40S#RNsQtENY*( z93DP=fdA*i!^8OCGLjhIlR2Xz=2~u}MX9f>4Cen>AoWTER80G3xxke^{&A@Ys3?705Coz8F76W`M3P{zf7eU*7BDW{ivfx^k9VE+ zrke>t^#`GK3H62betiofFB<;8jf4+e#J2x9@I{grjeUNe zi2i}DKbF~|`47O1Ow?>MHE4scjn!OER=?*X<6KT16Y@W-(hNg2_n+~`P?I=ZP&@vl zR1!^ZMqD82LU=Gq4RaM9d`9h`#OIu9MuJax0L9icp&>fopK-1)U{fZdxvuxXZzX4P6K5ClOi~FUehGSD% z7EBOCdUVlu^D7I1c39Lo-IDo+xOK~Y;{3Ui=f$JW(gzYkk`M5ZzUTxdsE9%|KqF-5 z-Qi74A)ey2sMD%hHLFI|dBb1>I84n6qO{=$-X@7r@){L^Ag5wz4)WVU%^RiW3Gqf& zZa6gSnNX)_Tt;ROu;f4QWPmQC_er`gWyG`?yMi&xB$@3r9SCi_WZD?p80!VVwjryg zLy2jN79*y}n5Jo*8;%<7*cjWZ*oN#=MB*m`HUEX6rvj+^79*zXnuJ;V>z&RCY>U{o zv9wSIP*u~ARZ-GpAg0BzNHoqhjf0!Vwv9y_(*}ub6!V3TZbPru*V`c|I}~&%SuW<0 zw%XE%JgH`}R)KtaF-`=D@oYfu)wKj{MGE8J~2k^cSr&Yj_1tF?%E>g(E-f_Z@6uM%NT=Nb;d0F|O<Md7=J({;XraI;*KJkn*-$aC-aikJM%)v$MJ^ zgMo^yy;XPwQTUZz!D?jYJ@-=NZN>hG4X@xvPD)BrqdZ(q-P4dxF+o=C!qPqBzfqWy z%ja_1^w?)P797X-4KCmxx3F4Vi0U=puhpZ4qQrIAvd#s;Im>c&F2P-nfVtuOj)R3C zH?o&gn5yP-`JBQOEtku~J+)$qQPZmZ*wZp=59M=#rYJ1`<8}Mi$+&5l0H)E5PZ}n; zIv0IHe39!en8p)Ykjv+pqWt)0yq%SbwVG+snq^WrtHZyJ5qw<(DWTaqT9It`3Ht9f zZ$m(XGt9{}s*dx~43Vf9X{P89IwmcO3LVVmz*6sEz&L(bxLHI0RfDV~p!# z*B#d}pzE&tm#u%pI@ewILqWLu+H0@A|ElXGcz-zz%K%GzuwXQwjLbzv>Gu_7(O7u0 zX)M5A(^!z@1*7>y_2%%FR;6040Q~;F;dtTq0V>sM<+JsB!&@bcP`gEavwl>qqbq2~ zPWV;83E_?u6tLp)1MYOO3cbwS*yEO;=ng#6Oh;+MXvJ*b76R0e<8%<4?&h}P7t5=w z<+#XoRS0DNtRaA2ZYq|gSaL|}by54Ws_8VZ->4%hlip9xn3SV}pv|8z>mV3seK`;` zjTaNChv22Ri$yR7@ca_M(qWS_#W?2*Ingwjd58n>Qd|Iz(Mo?n1wm~EMW#qbFebfE z)Cd{lOpY~8r&UWf4Ji=4vxdrK<2u;`5f^}Bv0Vyw5qyM_W4P+j%?LR$-C)|mRQYjt zC%L1TVc@V1ylS}zw>B{ceP;oh1=;_CFW(s{`CRo2)m&bQY~fjSDEGe#1!&3I>XT-n zRNDd0oH^qHxV!f`fvMv@YMSmoD5U_YJjcFkn)KH94u@>FDMSM+*v<(FzQyZBn-0=a z5(k)-@G1}#DSHUZ5`~E`Fjk+m{_)V#xn`WPau8gBp99(5jFccKGsY*yFZgm{{=I_i z!%1f0e8BSJ>4w;{N|&R4U&*qfgYMiDaL=4^p(^64H3Wd)qXz8|<@xl{m=d6&5F3rG z+$O)Tr5V-fAAgu&FMpb1uXaYlzHK31kC!|wovC@4p3Zxid{~lY3BLSk$~=6UG7sNE zm>0fumk+VW$Sv4o^fdMu{WNJGf%ZhZjT&eTAx$hLrjh3tQ^pGF!SJL>Yvmp=!`b$o zcW!6+S1k>L#dCu2*xw}J;(I)2wI0(cLRT$yu>6yrbsAZ%RcQyZk#eVQ77|;YxT$v?>tFw zH-LY@2C3L9zDgHEip-t~Kchk-$V28>f@oPCYMSOQ7$ly(unGo<8wouGu^1s6BfQL{ z543nhb*bC}s%T+1B9gPHOslZnK zAdWL8*#szup%#U3#&TB}jhB0bUdWgv#h5J!LY{PNAWyti(- z*%f7fYJz#^od$q8^<~jL?oVP>xI*4(@|Z-&d}sGAEEtZdbi1nM81spa_s;&$>Z+w0 z&e5Zep;|A>1Ld(Us|(c?pB{z2NKd2#UUJlxr;djgi*ZXUBByETDrt@E`jHV zq3_9D_lB*V_wjjI)-+jOJ8qMnTY^`6Io`mEbq#)car7{4!F9{WEb62-m=%A}7Ofg}@_1N0zb418*}LVqKPo=Dp!0!AE*OO|C>C7lt~ zH`ebz1kwo8Z&MV7F4PPFqqabmL{kj_L)8=}%S=hrg}=6jyspL~n$C33FBE)FXSznJ zQgE@TC|`XyA}T~ovGaLbVX7wU8q;)jmDeQIr*Dxo-REzzo^Hg$bks0kPd(0Ga%Lb; zZ>E1)UKT_ZKoy0jRgf_gU}bOQt1DRrgL+;CeC&M}xn~*YvixdmFiO zIk$1^D#ok2>H0@+z4fT?n))intK3?)!^#Hd8xcT#Hzf(UEkQ9*uDAhBB&oR5&hxDf!SKKrx5L$i7%jG|A9GXLav6h;C3yNNSF=lrSTrt5(CqRbTS z_s&^h!pt_|%c?sc8!{#RD-6oK8;JNtRb}$8EiuPY05Nq8P_1H@U`&k!C-p`&Md`wr zFk&>=#+OgkNkG^N5H4oqoo$!b88>L_*BfCdEe02Z95?lGD;)VV8 zj~||=YTE-!o{f;*zBaU?S!qv;$x%AXAVURIDNhQD3Zl(r=$k0!WK;@L-lH!1b9sCN zrRX$z4SF;B3_?Qb=uJ5c9g!yjSP%?3<|l*&`lL&}Iz@5O%aLHWItD?O-QnO^Y5!kF?@`HrF~ zXg0TscTl9lvTb)1TL)ySwh+=S(5;#(bP3soT!_sCUJubJ=y$440YC||fqS(cQ$HJv z#&ub1I-QD z05D|uiDgQcEkUM&wM+r%^4>bEA!CBJgI2t`d9G0t4750X7Rr^Se?!4@V73_NId>5q zM>inUh=sW%XxgwDd0w1oh!Nl+xqa;ERe=})lmarW@ae4@8B62GEBy2+nG$RVg+gFs zLgiCMaf@O>D2Bb1$TCTLVNnn;-4csG4Yl&0rg6@Dy66z|kVza-@12t*Q9en?Nm-Pn z_OL0+R6ZIzE0VP0#7AW+i}7mv1+bQva+0w~HP=i4bInvEgb+jQ@&Wu8?ECCM9n-~g z)eitfZaB16b$GyY z*LrZ(Rp4ElIcgm$ zU|cxVik63)#eoK4eh-IXHl6l23b}5Aj8kF{?OtbCR^SmvHH+U>*P;dcWqjuI8Q9ow zQtNa!YaG&68zg&XwTCvnE@*MsGYK#{i-fU<PwJ@#9xtef;>UqH79M zwywG6)?2T+W=laeYl3oiOEp#gLM86?)3o1f#vIM)*4cOcNX3WgH!0K~c&`Nx#a|A@9v~HM$!;j^2S@K%YflE${mV_!jvA z8MJ&_In1LXTu?>RS%r+*+oHFN`9xP&0AaDZR6eptdTeNjoDiogcd}kC8!Y?FhOX!j z%#y@N4o}(@O&hz*{-nXCYd^8ffh$IJ0u5Ep^^$pnuWU3&&Jj z*1G`t`NYFIcmB)aI`-i6{3^X|ZUI=Yp8tZQ>$>j1^=J_8lEj=_W`t+VRq}jAg&VwKyhWY-5 z&N3Py1oSZCm;#n5SbD#i47MT0FYQ|0hM2-l_k&tXhGs8axxZYlwpx|>Uj;?Ybo|z0 zE%(&&(;{7vo?3nyK7GH^LChQPCfWU!R;yYr&%c+eEw+5e)QZ8=%TGxQRD62*seTYY zgi%n2h8RKsCn5@63259p%KGg};w^r&t3$ZHlmBT23mUgf<6j!^nDh8bwE_ULtV{R* zCGYpDAzVur{*Pf>boRjD83Ge=wH1 zob<`XO^+MZX|keE@W+sWbz1O9Cl7Vxj9X25!(OtEd;K0rS+tIOu_b48^@M|UA%_O( zuWMTudVM$XvaGNHhJ1KOsYl_~lcc&AL!Z`kxgI}H(_3&;j+l?(d`M0tM4T+jp8une z`+7zLF|xl;n4Tp|7yv|>NB~%rP1iGI5d%n+E^U=$%d<2`R!Lu1;SUNZTsG6q$Rna8 zxl!awqBz|l5x;Kgx_!05mlqXP5S8$ACaB6HAS1?&z!*QSv8xndo(ytVQ*UM34Ze?x!pk$c9-d1>A!;Mk7!o7&BKB9cLEvGQ!JMsx zCNY^n59C6Mjl+}K!ODWO)j8!Hgg<$XoKI0 zw}INg_P>haX>F_{2(-x!n>XiKoK_b2=<;?sb;}m8BKYSog9!MIWk6s>Ek_SyhTasa@I1Uhp+<$hEZVp$;@KOQ^ zZhV(@w2K}R!eS{H_Q*-tIstJdiUKox&C^ehDRaHWFRBhYW<$nYx-bO zdlTG3x8{wn*41>B^oM;u0v_!PR>P;(4~kLXhr}q7oL^g77MhNZJPhIehqjD&LyWck zu#eaem*JV7fvrRLPmx{p1!0@*p4=={`qMp1G9db-P47virjXKtpt~OmW(kfVkgh{d z8{ht<)sUTzCszJ)ygyNWl|!FkcnYx$ih%T#H*RDKc8gRH*FOGq zb#v5MfO+;Rc29JX7K#zL2}#c-gLSEwng?VA*ZPo=^hg_?rB+Fx{ZFvH zgl#)q*~A=J)%RLYfz<%*47)YxPO42A57ZIsvv~w7b<*(zKkiVxCI&tUk(7!g&xFj7 zXAYxg9EoXW+oA}{LoHY#uqwGu@glqvjwC^tcO2RlRn7fe>?@oxGoqCjMzBX=_jF+B zq~_%I}W^Li-LY%Azvw5u4@%)h5UWGAlgf~cVE)sBd+_%W%Rr+LU|k%;_6(@#Gt0d zZ!=s?bHKQbTPBSvvaj>uR~9H;xb@cG%ljeJ;8mthMeT@si0)fZcb;2BJRcZCMKPC^ zB4rf{uP{zMC0iX0S1Ef?yXtJce!Wj<;0sa|mlZ!5LTW{Q>}Am z*vlrp(9GZhNHT_EA)<<+`J9k~51N4Q$~PKIy3ClYFEtwZyRe{vUm%40nxfFCPy{SX za1rv7lm|_ht87x|dlGKwVu{#Xff6tvV6JcoEw-yQEtqjVclflm{ELVcxy$6z}7V$vS$z!b!oh zDn_lWkRDEFZ^hMEmutcH7fYJ!u+l&wLOGyg{=cYcx{KRg)1=3gdVrFWUcA-1a%`A@ zk}Y`^r(U^k*@09>(A=D#VYO;Czg%7O@2|?-8Eq9@nE&Id!!&m|hS`y`j@YVtI7g|f zy<9HW_RfHEd0DPym}7oVrlCc258rGavBcoK7mVPU1*yd9rpmLp%%ZX{xo0!;774*K znnJkhp>nV*V;rkev00QRfQgZhd4?mJNrRG9(jni&p!g8k(j zpU5|+vNawbuIQ2^in(Q6CIpvJqsJLTk&PJRkjj7T{)zY=W~Qv_uBob&GY7z7oT{qn z>Z)uq?D>Rg7)uh9EdWbql7z9w+L&D*Mmcl>p@1m?d~vJ>Q(iJ=T8JCtrtmHwg!`TB zK%E8)0tmV>;1Xo7IMCnzDwWPI7;2vyTwcUx{k%k-%J;1T?%O1squikFgc>FZvGlMg zFq>ukn3TPjB#Ohrt98(C6e>W;i>mr}Vpjvuu752Dex(Gtg7fr-^Eux8r96@jgL))g ztu+(?ne@@Ju+(Ku%YZ1kt>La=6bh9}p6UW0~W6^S7eqaB9;9D7#dW*V&HSlCf@ z$J?9*IM-@JX_daFc+fwywzOJP$*T_z`bzKt4%pu_2~lgSOKXQuf-ZeQ!TemCu?y2eV~Zi#8Ss$h^c z9(f*g7LSG;qQ8Mh7dzm23x*~GR^$U84_}f2D-l4XV4&VL&QNpB=F0J2Pbs*U1Ly$X zlGyigLFx65uQZ!E)lhYf{l)d0Mo2_sT!$|h|958738Bq6aR>(m*F7tz$N3z9pMDGv{lKd}tsOsB$*X6!TkD_e$|=JyJwShi z<9ws%ho41FEMh-(rL(jp@KkHVk2PMK^X*#` z81F2&)%d7bdwl8cw9FKRR^9Lz>7+&lpmO3p7gt4rR!_7?TT30q4tzI+-P;^n=`3xH z+QwV8dsyqaj4mU|8K!ahH1Km4FyWZ&6hpu^RWcrB8{UL84P#gYeua}HDHE`?i`-S; zS*A7Mg(A%=_-1lGFUMlJeh%U?_m?-z!{fEWY4(WE9dNG@w2I0@?+YrvnI}I7;FohX zC_zhd(B>h!8&rm>S81x2B_33w-d)4_s81|d#}ctIU#deGZ@Da?#9Ifq3e+RC7xf;4 zFJe6=)K6fo;6%APz`4?MHRx?S&Lo)DTDS(Va4Eisvh0*Ab(j|CDDC_A%JjSoTkZo~ zloVQPdkZFniLAA&SAG9&{%$>ko=U&>)iX(^VwI35ZpIf*CSj7Z((51GEeF-*=Ta4F z`N7(fdj%R?t9h64({}YBq5chDGK2Eja9AQx@owTIQEP5m@P_e^5r34wNOrjsw^k@P~K09AAcV%WO}|eKhL>k<;%53ue474 zSM%|Dmr>NpC z>_oM{XWmjIfo2aE2L4xK4UCF%qo6b#=R}44v!W<8oBwQP|63EI5Se%n_D~+3ep-y0 ziQzjl=3L?%nv-#TxbMXaJM(#^*E@b=CAZlfL*QK7<$PD;-0;u+>unfyj#00u$48yT zqoX`(EytR>YgxOBM9j72cKhm6Ih0-3@kiorNzqf*i)2hxL?&E+Khg8e z^;8fQxrF|3jV};yqt|NXa=Bcq)tg%rdgpUn^MhQ_UR+sOYzMhl7QrF5dZ>b8v=!Nu zjI;J357}c-GvkLe4%JFX+Zq^Q1{W!B(m@2)%z^{|BbdLuu{7levE-7w#%D-J5XrYJ zenYz~AuQEoDan-OTgtJf8xn>%B@gL4Ce5kFCcz2MBsRi%=JPk`GJ_up9ko^#85id) z^G}{h2H12s1ASHosiizGANZrWah-eewVpq>^vmtRxvdwjS2maWiO3#A{`xaj^ei#K z=;@}LR@s6M<%=M)UrQz!!+EZdGj_ljKTwHh!$El2!jBF~%_%5q{V6#PCd+IvwBN=V z8;f4fF#nLfLB*b_w=h8gm>MPn_9fsvOJPPxxEw^-%1PYRK77JGIkg`|E4Ce~uS9`A zq<7rQDAfJSrWs6xJ)iS3Y7`1&g_v+?dW94U4YHyp1m`7dSE@eE`kxyR?gVP`OS=R9d#Y!(~G`j4>57Uu)2`U)9mAz2OP&O zpQl+mi5wfCYB&m(7rrUG^#`=c{V0QO*1XyfY=ptlNmiT8C$ukyU}7F|@Uwfa3GB_M zFouwIOGgHNP@huE5Pprr*!rJds8{gb4 zej#pBw9jjmBnu_|Ze*Smc>E#@!`&mIqZ?rNcH+~`W9n~SDjX2Ubn$)=laOn6E3Fnf2GXB@UHoLT}TLIk2#Z2aeUtnpV38~`uX6hmv}bL#esrw~G-)+zgze@Nu$r+%!2n=sxkH{1VOX(sV54Ef zbs^(X!j+{TnSXvCcxnE5*)U|dPBsi#v>HYbhKicdR4SD%TpdpDF85!5wSpZO^88=- zT(V^X*P>tp5~G=2XTv&Fw1E0vJQH2DIzb56Ub|37jl=9PN(usJRXOo~l4Oz2oycpG z`$y*^7q*<04MYB;2|YV$KU;eI6=q+EK7Vv=@?X2KB{1aI%Em-#KH0R`ZZAH5VCCMf zQ5_6$K`PLq$|&)PcRaf@PeJS%Ou#lK2Lb(Ty*-rC3&CXhm;qrHGS`JTwP0v=;rR@V zW6NO{_Vv#9Q3s($G)#yYONh;l!^rbOyRpF*2oJJDfE;h5*}*bIY|-dJ_WBphvN2e! zaeb$b-vl$-v4ty;9fgd3r6EE?&)48ZGio&z)^76yCy6Q4DR|Ea*I%3mZO|m9i(yfG z;as8WYgq#lLj)smW7=S^UH=0(3EJ&oZGUHHrx(?kpxh~&rhKO&uzJ*+KYRKEJ8xRv z8P$8esLpWQ&uCfmzhE_=Vr+3;_zqVAs`G<4ykQK!U$=};bI?C+Sasir|Ne$Iym#Rg z_#(lmaS*OB!rU0^_ol(_zlqR*S%khC{!cHy(zS*I~~8ud}gr}f7gRy z;;F~tIm6d07oEu0r)*|UO;e)Bk5g%SGXI~*8C5Oka^?B&$@KVfD&LPrFiboz?)LIj zdU`wfk5Z}ChLveajGW_W>5(f$Uc5#d)_x3XC~Mx3=skQFUI;vZfYxDTCBtU-P7aO| zl534OPE~rAx(1n^fqS_hrmdPfpUesVJ?etZz?d>nSjNViL$;-=L;Xy_P>sUPNc2G? z)bHdJF;tpw7{Gjh<$)21qXpk1*n18~$2xbuS#*-iloJEMD9TQyxfw6E>{=f*!fJch zGy~3*=4kr=8+E4W0A|*cvoL53yUA_xEY~CpT>dlAr(<@Qr1aU&90-^oL|X=cEl?m znG}3|8ve8C{T!W4~{5Lz1X$ z{?XrGvj1F{?l@e>xuM80aFm^z9+<4ACzpQ9m*`)?7|O4_aSr*v8jk1 zNu?5Ww6KjONlFvQJXKz<)%EKHhh$!BLtq>h1WI+Ts6tcn9I_5C>LD9E`?pOD?ESji zxsUn-A_(Fzz04Mh#SS>SXSv%a4C|le3h3iGe%S@E{}d*XXiE8Kb0SjMsxeCZ02q$2 z7RbUrl@9c~3oYJ~DM!dtoz7&Mc2U=pq;iUy#=Ye7V$4jE>EgmdVMoD&&P_vI3%nCa zv`z0WEx-%vYIS@_!(|TXw9qqQBR+@_zB+MI(av+1?H0U9oif7rypsU1M*Z_dm7Sa;R(7t~Q6o zylu(6GY5ru0%NqGLt%0*GKL*mf10Y`})+ayd@aX)tMXKVyagu%J zOW`@xmH4FlbyY9{D?}>gsLkW@% z^8+BNAR7{^Kxm*o;hQG^Kn9`}-ucG>RIEbZ$h;LgW!W(q!9{=vGA34}zbYS*j*hLz^(a)P)Jq-_YUFJI=?}CtJTf{^LCE zPyOj6Ce-W6S@iAO(&4kidAEB`eI9*3&5Js;Kf5mv=k_^<{`MHhqR;HU)A!%0GNr?M zc0TvtsWPp|n6_IHhkWKh#w8HCNntP>n7@~!2v2pGaHPPGfOS>XJ~07944gJ)j9;n( zbxmI2gpwP!r_@IuQG$p1{m>%{n90V?Q12qlMnVx+LIEZRDB>Ni4JFBej1?M2t^&zr zD2M0|w)G>n=uxrQuDL_9*2bp=;nP>N3kir^p27c?5nl~kc|<9KC^C?l8Ot5WP1LHX zX`*4|;o}ynK(^t?7SLf-66u4&VV9?=)MKnC(T^RYLSetCKelQ%Jk1%KWgCd%^RU!Y z)gHz_Vwcqmm(DQ8vjYk_7;|m?Tb#j9A<>f(3y~CCft6aR7>kpTTGa3ymxak}7Cx&p z1%Q8G{+bWWz6zxj-ki>#1$gT~qEw&%l0i4%4IA?}CL*OeTw~Dsba?q2eMi|66;=>L zPj5?9ZA2Sk$Cxqs_3v3yoKJ{`pcClmT}2e7+ku9TT}J+kzl654UL6VkY>!!-pp>-7 zifnGv33B@`|0pf`67^xuOauu(G^G7{ z22g@&F7x~VJV8&)jPB}xS#a}eqae4|dLf;H_wW{#`fe$y@8 zZIxJN0HwgRh=dV)+;qF|UvZe` ze&Q4J|7ZinQ1HCPTd}|Ph;CW)agPl`0-=50o%im08nqqAku`5$1O4H} z#j!8M*Mz^HV|K;QGx2wgnkbbg5qS90QI9nAlOJ#{ImX?)o`-W5b_}yI7+!0PS(d{msgBet-tE&P*iRfRp$+#}u z@iD~B4IlrV`=olic|`r)8gdb@K&44J6m`%V?r`%~bRNAf;*{Qz522q%pG99pGxTlr zXXtMb&n+FO+mIPTv}h_pW+vLcZ?eX=!E9!G!6Qq99V9t~0fG53SN{vUj6e6-<+dp_Z5_xm z*`~3(?A52Q(EjEI4f*OTXc$Ealb=RkLsGj=lfV!9g&QE{xZ6hL0nO^s4`pZFlh z00=}g4UNuLt7Un=l`LtMF?d|g+-l<$I{ZPhF~p&XiuH!_gHYn1$X5n zPW4Frj~YLRC%wdW#uqJ}rQ^DyM2=i}{stul?ekB0Y{_L(I7lh&H4ksUVC#mFg!1xG)*z ziEi$nJV=O*NLT=#)1RmkjK7h?LSwWer$s6X4iPM9f!4BNph=@LTM((+yjF{3?9zSL z86t4%{s2zaD<1Jp`i}lZ?Q)_&D5c8ve6Cn!qs9WzWlhs55d_WA{8c-#eqOHY-XtuQ zj$1VFkJybX^V@a=-^*>haRrsztu|_)ThZ%e1EoQDsis~(x|py;sH8yey&(Hf)Z#?x z?vHdo=7ASdKaS9w1fzht&3H9#M)1QHVJe<1&Sw%N@N56|rD|2eio$hS2G;AJNXdtn zEeNX=&CqbED2k4`5%(CiF~;FtEed};S)9)VQDAu#_WdE0N*!I60V|4re5vy&FF4h6 z4brQI`N15WyCm;A92KSC?n6O+B)&KXq`qRQUUed30?;#}R;dWUG~du8lbf7-&WTbB@_XhB?QQL}W=pA?|i*>XJ;Px12>HswAP

gcuh(yM)5x$wXlMQz` z@{Y+%0_31o(9v@S+%W?u*1FKJGcSlS`B84<2*l%|X8e!02*<`c2Bw$>zeiVs@c8Wo ztGRN?u|&bY!FI~Iol^82kEeoR*%}8h-ZB3IL;8-8GqxMn!DK`pPl2%;RunG_4_~7g zhOT2lxms2=H5iZI&Kp~{DCTm-qH|0(dk1lNydz}_#@uim{`%{}etCv5elV+f`m^z> z!bJlvmbxWOSh2YZ3K80dz*EklGED2tRSi^?OLw6eWwY5Q4aec1nmPQpEHA+*Dla1c zn}&1geedhNPE}vmyQqH93B{b8xfD7dROz&l-gat1!lu-znAC8#f_~IjkGy6(NJ5$>k%YZs=qfI-#IYw?so}I)OnAeNFdqY`YxkuA`^CuvL$A} zm}l#n3D5sy@K#e4{=phuWwuSczdgbGt#WxGTPT;U;zFa-X)F}yj}joB1E}q}uGgOO zGB&G(c+_{&_0?`dw6GAtw}1s^8swCod!3g1rgzGu`#tFRW%e#6Nu;0HESTPc6tb^j zq@X~)NboMm$z}|Us7=N4N6NpC~!bv`T2^~he+%N?E zovIF*5Yz=mk05h9&cbwuS&WTr-Ik{L9k2!4{ldd^|4da3L$OROc)+=3O#|L#mr_`( zn3Z;?RO*Ri=^?0!o5>mE2dEQ|B0WpNIkfK4kReruiiZ*FYI zKEKV%Vx=c*-FS@#{)XT8ZACb!s+cV9p+`wT(-5z?&IT>w-0PD%z2{am862@3!I?>+_ zok_TlGqM)kzrA2P@p;=NYMNnani#LQTOGR)%Fa7;Wv&@_sUGasjhne*1&X8e_U}b+n>KGFP{X{LE@Mlv%lK_`&Zm}2IGgbkMxGaZ0Jf0W`1@WL zmY)g~B5$}3ZoeOh83K>N6tK660S!g8Cj6m5yrM&%xa^m!K%|W_J&H)@#;A&in*fgS z*ZY7oW>^;I{ViMeJWkW=E6BdXi7-HOd6^U0cLh6Ia0J(v371A=9N~^H z3+}P#WZw-QUgg4zJ^j3eqaV@fKWC9Gai8%lsESUuMp;BuuC5tN5B6?6@{VH8zhxEr!a zD1jZHY6gNbRvQI9+AAcdljmVodoiZSmEvKr4nq&7N~KyXVbonpp{?^N=v=Ifl2C>V z(DIj85xEY)WUOJ%v4&*^J(z*PoAIeV?&R0S7@zE2ARpNkas zoG$3^U;y%S9DqM31F(0z0D$z7Z441g*-7eA+;vIO>V?f=9s0+@>a-0S^%T0_xhKD4Z+b!z1BI0V&3CoPm8RpAuvG>J@LLsJr}~ zS8Y<#+rey`Qc>GUl2I`lRyvxbz92WiUYw{=mZ^AAH*W=<5lIlu-HJME=y7H~jbgzv z@xas`bDz3t6^hG3?$v9LmLv#*g{$s_Q`D#fXPD%Ic00%s4C;$2z$-whtnpsKE7^9j zVuR~e7d**vi`;|87F|d37OJibb|t#Ojg0t8W1mzT}@8!qW&5uW|GJfol8qPN{xYT?HrvGb93FZ5}U4s&q z$*L+_YgE_i@1F&9aBM)_+=$G;a6S`Uv(JyV$^zenq2g>~|1&x^|8qB#wcrI7&3{2h>F2x4SRb$F=(5 zy5|3mNh48F_*1_5JO2M+gkBQ}VgN??dxo#o57*Veu^F_cdI21eivs0Qpx;AGN?+P8bMO|vTQ>PztytsRWQfas#D0je_~npTNdZ{`{lB) zTVoTf&s}kUUS|wT-szvz;TH~&-?XDM=KOxnE$e>Id&6!2{Ao|ZjOqFNPyZxty2|>0 zM^S2d2t&@WYp^0hF;SnRib9`L2{m>y(=j^+eUxkal-@hXW$#=c32+PdbNmP&}Z#bX9j{1(>^W!s8;sYZB~Xjlc2 zO4CL+82IJodvNKdl5f3<86dq&9}ZDo)svtAlISL!=GC^Xrc_@oj~dnM^!s<@a0;p1Re zp~sm}mliESN^+FIG3pn@kV$GTlqda*dkDGbvsY+#T7A%Qk_?#Q*Z|_j$E+JQZZ<1{ z`0FG`=>ThJO^7c~&kob|&!jkWLfbS)tf8w&UX@lMt-$8jB10cwtNFdQ6dObTlW zBG~s_-RGi?A8%TvS!0h;zG&#M;!AD$t0yM&O9T=J&J3$`7#zoJn@+6s*+tQ?b3&~O zgdLW$hUNvLy~X{@>**qW6+9uoW>wm_J^*lgv)A9ztFYg@JWOjx30WYrOpexw2gbt# zUETa2-emyx7_A*8vP>2TIa;Ii1g&d+`UoMXW)n2f$(AGqq>*w^b#T-w#)Xi97B-E)4ACH{ zzJxwECHqiP-DF=9iK3d*9~_JUO`)*}oiO&*b^Vew-H~pDZIjfqZLm;{w)50=znz}d zgstOjvsjQq)YTM33$ZjFZ|+@h%m2=Pa;t$#JeA8eFYiqTf2hctRz8ab;ooguZTO>f zaFA!V9>8UI5m{qb9IJ0TQGrDDSZHoTU`HYR9{*USmv}+xKVQ+;QSFjO_Z0O9R8{E@ zRAsLz2^$|G0AvO*BLL+2bwMK1Ew@MnKB*`#nEW!5WkB3~p(T>B29$zn0;Qmhi6}~U z-6e^1x73~XyDFdt??}?ZA3|&xxY$hb`i#U6;Etw2`W%_#Ge?aDIM?Li5gk9|x~gUk zANzA1?FTN&q}2@lQ7*8!&$U_QBZ_Zn_RNA~FhC58C$TzH3C78uvOzJ5opX?j zlV((gJaBY0i7hNer)GfHOvaGCjt9P46lkPV)jQRv&OkL?=oj zh021l5@j-6;{hxy)&%=JxoIeKfaCg==^QMZ&{H>9YH1pMrr^h%?_kG011GW)%j_ev ziF$9^Jve{^5I4RAlc5WmqA*mlb>{uVLZfLFr!24Dg_=$V-J05MFj?pCg}VmSh zEWGTfi99QmF;2kNZ|s#|h&IM{E@xw*RyUb89*GJ78X}odE!NB`%r>rQial)G7~3{J zZewiMw+T0AsD~_x<-71{_oa$jQZ4F&0|IWF7wZS|?}*O%xe_iGdOu}qhJ$xa&2Z*F zQ?&aYYu$C*#>rFHm>a7WxL|0k)2`jw*?1;S+TY6j{|n=mQykB~v9p{nVEbcjR&~^O z@KheEqmfUj@NziW-AHv2NG=1APE#)t+bK8ex%V}yxSMaO`|H;OE(2?$0 zJ{~tsrSiZA%~XDJz%s&51fRM_ ze+V$PZ{a(SuPkH6boh0hF}%ET{OgXYd>9Up_7`XVEZ(aty{%ib1MX0h;C6$o(9;5wM%zi6(^?9sUVZJFOaxq{*$v9rF{M zx4Cn!v09uPfs^DuL@|M^!%=xm%~+9ImEFnk}9i)T4T9w$Y4NhgG+F zGfv6bk)RB|L~R!fuWp;lfzLNKZX^KQz(6oy5O?m)9^{#92wbK)Z=<)ep@}j-QIm8| zOPk=&UzH~P6_6V@Hn<;9nG1%@@_{IJksuKFoh9DlEpad?*QMGCE{ob`>C9}2@>uye zf?$TIr&#?p%K$WjQMwAz)aECnG)^OIeg7bB#jRS;7dL&EF9rRh1fmWh5G8>+lV)2NqS^u#E8|~G zVvJpeEj6cs!+Xe!hWlQ>+s|CJG1Rb+u`im>nUOKnhHHTB)lH&tN3xs1)k>WygdGX{ z7kn&rCZSx!ZxYM0=@-l?IGN20bzFvwsVyP4MeznJ4dBn_4WejcT*zs2zPCXb&<>G} zxp5a)HCEl(sWNR<_2!g01z50R7?!CR!&R}=rGQ}yd@v5;I9N=ZK%ZVytb;-auHH}8 z5E#D)&o4Ba0@#*2b1fSncdx1{%0tbCe8k9%-EC9sf$d@1J|GqPY8FUsWAlp0^;ogU zJU3eF-J>X~`p`y=6gXq^-8N+&#vY?>+*a;?ra19ar|}pPl({n4Kwv!@Sdd1Dd0tG0 z|F42raCj$%k+%tvmkj&KF!HkEm5SVfv;Ro~tse6VCd>JnlZ?0~MNo!(DRw}@!-@g5 zr%kA5YXD_MLR!NyLP%W=9q^EazQPNZstH%k53Uk4)p}vgzKroNwIOU%?9D&#VM?f# zEjYb&cw@&H*`z@yQ1KVf6l%eC=t-CY!W@LCVk_I%cc zecdU(%~B{=F%}w>QpU(xJN%So88X#O+Zs`hzj+GV^PfC{Lc3jWB)q)CQ&dM?efu)Q z?q4b`YXMD?xRo8e=JdP1u0y%9v{WgBt_O@WhKu@CD`3XB{0)k|fA=tL&)Nq(2ue$l zq4&;|f`Bu|i_+enROF2P@8fC}5nP_ZoTj&0(>y$j=xBMgt6U{chA0$!E9jMFai6X&MB3Ns;Cz(sESHtO1bXsUQ9O+YDZJlqEk{tj{B#P z&Bo1ly9NZcG;R*Jkz-+-{D7u;vO(jM0Szh4T|`gJ`GJ|}7ee{`o5q*gkeyY_`#ekm z(SALT76qWK@7vfnmFE6_Q!&R1!fi6(OAmr!YmJXCp>ybV61C3c3PA9OqNL=9*sl%b zieiujs-UPfQBEHK%mxWhQ-PvVl?I?-bINgzc7HnC{Ugu5d%39n zt2=ou|MElGAl}*IUz>mbYqBYMXOE{5SE#ZuagY0+51)}}eD(6+U%wCXt7lJmA}3#) z|BtVoJ>iK4g)?zmh`-kCDc>oimsMn=1?jEz!%zb{!ViOoM_}}FpFC`bSK}s)7u+|5 zgqPN46z!8atMYwjiP3!hs2}I(Ac+4Y4gxVw-@o@Mj4LA34aR^GyvhVc=S-0)Rv3R& zVSM4xA20)Yrp)vX0X`R^q`g=uEVdItllD8pnS!aTFhgeo8_X``x*{;HFl#*)dVie+ z(VNO~0z!rmfoFt4MZyHA11JJ8>b_8K(=T+0SS+lU{ISb8e9)0wh87K;j zKs&8*|1Evkv)|(LGC-Nesmj`Duu&&`!4Voz49-OHaFl{W7NwwpD@0TEi6_XctaV?U zGB&GI?3EwE9;0>j(eK{;#V4e0=g{X}OrI45EG8<3y4*kZZ*4NN=gtqytWKGSA1Qm7 z)@O|U^%KYXi-28)J}QU;&Wl<*#tEEAM3*|8C!D34Gd&8v@XV^$vw6vZEWpvV_}EZ-wLj68aW(E*vx~73&9Z$Tm7Z zHWO7V)|TcU_&01xqA+YIs;(;=jB_Pqpd3>`Q5L|0*T^i;Hg%ow9}j^U@0%Puww;IP z{!P`QGAt;{`@aQQsBq3U6kS(Yb12j+FG^RyHUJZLO%PH@&Or@AM{zT5$+AHB7&tDZ zaq6RnuK2y!P$1N5Dt@p4sN~vt+y4(lD2Xs?wMnrwx;7bLk>$@^cm2-7##VOZ@c+Wa z;!0~p6i+NP=+UirRD;vqu1%rQJbbb&Y&4tZLXNDg?DnqOV$P8>XLh3H<<(!~c=y!F z)%?+;Hxnu9wHG8ZNEbpv?exUv=CZMJ=8CgI_`PBjZEl_}hi49L)^6E6u`nDSu6C|J zalAQy65@7aSyR25;~ujA|(WzUz!?J7?l6Uz%Q=izj zWpfUi{BvVP9%{ zdk2(b#{P0S|J7KQi{<6z)=aH7$gTfIWVY+-svuZ*uig1g!0^SN>TNHme+#8rdlQpv zE<0cM3eI@*A5OU8ue0PlitEUC-Mu{mQV@6a{wxw#b-fVm;t* zDV%GsdX>hxq5(Ec$Kj?E+%?xthy%C;;Bp;QP2rAX8W^-&KhOg&>$MLpQCVfNFZ;fM zV>|m=bPnB#?z3bz(hk_mg>^h`e1s;=cpDxu@QpsHYyI4*5zMoWX{V}M51fidIg3K# zSfY9`N+ZF%ZVDWyvNXcB4FV=KEIz?aDqz9&nqdW6*L2STJz;3Hv=vv^0k8UdLxG%P z{Qch>%KQh4u~w>v#V300zO%~<3zhZ))-(VtO0K7>62`!Ju~J!Bm}(}p_vk`<+gqMb zOw%+0nWky>JkNujRoij{WRCP|6avSQF47w>Xx*`*WYI8a#$;C|NaLK;_whZU`Kigu)i0w&qBOQ3nYo0v|?}`h4BDsED zzSEdYMXYexhByhZWS~>Bo@CG%ph`H-H`sYQ;sj#5qT-?=w_umlaf~}0m9;@EK|)$!+|rOdwp6NJG9;FA{GXnp~;zMgO&SHk`t5 z7i}9Z7*`5dC}pOYxnHSx2bg4vNe}ykknLUMDVSp&)J{{^+F~{x6ixS0fP(u2>F0_x@kkxr%v8nf zvn3M?#L6ZPkeO&KunvFIzRfLf-5Skxsm|rLUwht#=X9D|AzYU~t-DSb8tQdaC2a9o zLe8XXao9R>>=#DZ_puV*t6`Pb>o<|QaF>Qb{YbR{LOCZ|OTc7rvAeiA@9vnu4BdcY zsg&+1hIuD*eBiS&rjfhgWds){$yM`Elg~n=aZrbn>``<+&P>KO5|~TwY5$8ezUr-_Q4S+=y@LdHJz?S|$!XWds#383slL-G!pOU1Jm0 zcO)=_w#T`m%DqF^UjNA!^<$WB>3u%9fpz=AbP5gh%y`8-n5vr{ueL%(VwUQ1Z|f$K z5(6G=#;dac8B~pO#{7|Eja4U71*vp-{F2xwd$=ow7-h^&2mPK`6$W1XsewLPux!bP>x&PmT=RwYPpgiKsEty5JO=n` zR>4mxz#>8RG)AdZqbvh}=?(F41RB;1YWV(qDbv+&YRWftrTa*%zMAk2dtXaq74SCsibj%2oxxC8;)Fh3lJ?|wcR z-w#1Z(8lKfSZYamMG*qWG<1gJ@Q2SQ<9dohbCJl8B*M?G`rS`k^%G?^%ztH(mkiUr z+7V-Xt+#e32xP@)lQZ^QulK^{6oBIebH9mwaiPZ;52w>`yHP$wUqg}1HEkn=0&CS4 zrf>|JvZ}4nJ`V2tpmjy57A^J&spkW1LCvcnRkcCWG|+5SHH5z)bq1^WUjlwGzu;q- zV1NDsOk(Ky(k<9>O+$WIk{*@~)1ChYq9vUs&BmRJ=di!F=HtCA8}IIx@zhRIYxY7< zCbLtjq1{Dn-)5Dr#s*?1cuQ1n`%Xl6x){j$C~CK(D8E(>k?oPWYh$%8>YUc>-};o6 zs(~rJleZ_~RBY>G+|*RB;Z)SG2K7aJO^+dxIm>U1JEl-gjIN;E0S#ANN?Iz~CDl&B z7e82vrILEzFZEYr4gPrzd1p+rU+;}(c7(YUTq@F*w%DroB5~mrphD7kSDRbj>!@zyWXq)vz^Pa8K>BAv8d1 zZaLJ5x_39f{MY!k@F|porHn>NpzI`Qt}vAQblltqiG^6!({zoUmiQk|o09sU|EWrg zgh@ z={mpRrdu6ZCJUc2bzMwEF%fm${LBK8W#?9ekcg1HVf8LLg6xz=ns@M@TpBPnulayOy67yz_9*hB$CSk+wPFq;5l2mxi7V#EN5A8-IlSb$JK5mt5A7;aO>5Kv5KZO(fb z`vF6k1!(^7MLEmL1sJf;!ZT@p-Eyp}`5NfNrRO_zIpb+OhzD^d!8T}S6l_VT&>wPi z_r|dHA>)cs(3sSh?W1vRsgo1r+cK$qBwJluTnt#?FewB;9M`JRj|+^cSz0{7$eA0i zSzAL51p2{#Zjsx5h2XxjpNi)C$$()|n)RBHAl+(>TAJw*zcWU7QI3 z=r>cdK6`bY&GL~w6R=wcEpKZ zly~!asDX6o98M?W$#`HNabzFVP2Jl7z`Ft1R3auk@f}-Wz%qm&EMZhu+Waq`@`%_C zL{!EIAr!hc#{g7>Cw6Hve9_N0D{4MYPEXM?rmP4`IdI<8n2?=D;WLqeDFy^i;|A5xMh5cL%p- zyaqJPgsNH4N1-&}&ARGv7pP(%Zga->>fD1@V|uOvSHf7V`$dJPY8CgI1~BaKa%Z7C zw?d1dK!P4EFp7JHuuE(8H1A-plvO@ylP#G8$5Iy0TsaOqLBDIc& zyD%K}GeaIEMKI{qCgTC`nyblCcAe+l93)AwNXVsH6p>a+=)EP7I`mWG+|R1Uk??NV zz0v`QLd~1TI5G}6q2qw-l^*{dzBWjb;AYQT0to!t<7uW&#|=?UY;KZXo&9ne1huW} z_rG-4m`Dg?qR1hx8<)KNo#RpXM7eb*Uq=Jv%v+g7I&Kb+sT`h-Kr;O4L&lO^y$7rt z?309S5^Uxj6~b#%{|33qNxN{~m+Rz9r@^sgD6C82N=j z+EmFp&M0MZep{H1s#5?dj^Ys?pm~LGfV$n`u-irO!c5D@>)?8oYw`t!eD;%cH0_T8 z{YmB{wUA#^@!Zrr6*GpW8!aDH~_$$O<@X#}f zRH_=2#srC-V&3MW3xb(MHakvYSkZqfXKiwa6nD%q>CIWMD5vw=%Nd()k(iw!~ zO2>Cjj@8vCT-o;kny2WQJoyS|=&2@v=ae`>Zd~mN#ot``be{`0$`Nv;w6pG<{2W7enAe$l!Vi9?UHpWIy4Eaz*BQ=t` zz*J2?#o`vop)>)*xACBW&p3pjc3<8G#{ZpgS%`yjDl@|F9;KYs)@&b{lCl0*H8a7e zl+Zu7F_HDU`7~7>LlbmL7B*G0xfaF;vmxwbU>|@1g5qhH44?%ln5KC`XwvRLL>!LwApb1BY zv?Ud!{_9Dy3vkn$#w?DS%_wG@ch<_@VtM(@V!0cXHgCV+rxSyaI(KmUeF`BnP5d*j zdHXRc1%Qxr=UMeyr%^8*&jDKFax-42&h{S;bW^y--aog0(KfDVYX3INvMfAeJ<}?f z=xRc}S%N`lb;i&(&a?(^f|XiXW`t8S3z!Q7)`mz;ntPAKnF}`Q&hK#a+67tQdSPUk zFx0A4{OkPYf93d6{jV+cMbFj?FSt~#!u(~7Af=or^4!194YuR^21HYHj7}_s{Ys-z zt#};hou!mGg`3#!+C!ysIXR|1U(N~Q(v-{fdgb5ePAlb7;=x1%n-LvTaVN}(0I?N<0<7zZM!{abw&TokUenf4* zB`2rg;li@F7%IRS&&@fsE`xpTl6}@b5NAKB)bd2Bk@`9bz+oPR3&uQa-+UiZj^gEo zg)&!;GI4BxW_s|??iDSR(`Yz~t03shCU_;U6?Z5GH#DP*FP*51a&jwx@rP`soz8&Q zG=snw4CEIEp?dS0AODb;)AroGIU{Q>E%kVmSr_S=U;fUMc?H8V87>==la0t0>I%B(EB1^@AV%($~2 zQ`0mkUN>5kCCs?1Z1-FQqKqPFgpuUF%lsff*o!Y`9QIV|g@ae#(tv8z34BJGfB6+a9%OJ}GY&tz&Hr z!3To;YfaMjQkpKwiB)?;8&SGc$faNPZND-YewcfRnA(%mhfA z_f(*XPC>H?k*FeulPJg;+C1gzQ<3_u+po;oM82{@qmS%%MUZ z@h9!b3kgvSq;Bf-)Owfkd=J3IOrT-e_BS`erTJUIt42D3<)0Q@qoj}_SgvNe1S|hy zH3(pP{vZ?>(?A=8>K3y6XO3Xq*T{aaPZ#jFRE;&Hq&C$*B;|~|M@7yvkkawT;9-NC zjSakIVZoBS*WYL~vDv&#-$dc#wiH6zpPnu)+Yctsg&wq*m!_Y7U^Yu9mCoQTs}Gz; z1t8Tx0dgD`Pzp_tw4KubVmSDVjEk=RN%*(mAGgUW*%QI2^4*|qj`(?yWy2TD>CqRV zJ0@r&bm!Klb*uGK>(!p9Np)j(kJL*F(xGcM34~X46ARck?%5sYh3a}DwD3)#Y?*< zlqP3JjV4Ip+&{80{(eVDXf{Uu-QE5!xkkyJX$^w~H^4nH;E;}#tySwfzdyv6X$qT>dL~Mfv<2`#U@B&oU{gTKr6lk*=RfO( zW=%#>K7U8wU!o!g2qXY0J$?o**dnw!e@azq(f#mDg>&O*h-O5~{5{H&(D+x@d+vXs zn$9|@MV-H&VvzvjWGIf3LU;bqF&02N^~@uMgFJEZ#>L=`iNjD^+VA@1QBB>Hbkt-z zPB#jlo)0okyKC`%NFBx~Q`*>`YsCBw$@mY+0DL)J-Z}Ds0#FG;y{ zq-95JT^ijY?`Yu2tY$Kt&CCG&8fCelF9#ipU1~25ZnyxScU-O=L*{R66LjL&pL)HK zO#S}-H`j5G`_5t?UBf2iD9^La7nmW0j{o{ouRlk1HD9CNNj_bqdG=oE4~FfX>4>u& zQy?;$jy=g`=0E$#Wf-&wK2b6)_)NdqfS}ez$LoPO)bko95bH%zBL%nzSdcAEmRd1j z22%U24oC1UPZ_hX);(saaMVu2aiJg zq#VhZz6X?uj8m#>;_DQA?|Hs`%C=A0{;<&mDV_Oi9VwyN7{a#z7D*UVcZ=xn&2Fia zw{XT7=tvY_ z#1%XHd9r`i1-KWcn`=0tDae%8lV7ndCOE)&33G25{eYWDh-Mh-0dx63+gJDTXO z04&&I68uGb^1-xJx$wJ2%J6w^7`oRR^SrQfJH>L{}^`z zN~nw?NeK;F3)Gpk+90L#MuILO{DdPVv|AI0F0|}bTY0D{g1urt(R}qg9AuVPwj3z| zl%u@x*2=O$&O2Uh`AA#YHRI=Z(t6l>oAqbb-^$9CX|3=n$5!ATlesSyt=7qQ5_*rk zCTT<>w?jEP=Mb=)5-4K$*LFAyd4((DybHlX7++4PJ^G{PN*cMh+v{Zgu?y) zRZ*$J3lOXVfZ%}( z$Pp5VQl2qlJgDXCO6y8GMVa=&o^4}@CmMkAc9GzY!=%HA!NtQ zR!lNMgwR|`DTQXX5QOtR|7r|?{}nV9JX13QB*iYHI+FES4J>7^Zt|C^0j)z9WIB?} ziX5hF0PxHOf3gdFn&!MC^n`j6IucInr1_Nh0?X|sq-LgD^rCbfs#(8k<%YK~;Q);< z@UW#{d)w*Lm&JX)@AX2+=PH)_Vn3JQ4Mw^NJDpy%8lg*TkE~HV|4P5sc|zf~m;X%c z=LLD3z=c=8`lqCQmhwP94~tsx%n^D`dg;)t41RO$(+b!q64i*ZEOgy>`Q09*1s{bS z(dtE4E@}}AW!Cn*$C7JBU-|9l!|UXiCH?$MYA0OBEy9ob-~4NIP@5i1rrYp4!8qse zhki@afU~4U9*;Qqr2#$|Bku;xhVHQz292opvXU@IZrkVzyulEM>r;u59sQ zxqC54a2A_V3jcN4*r)PasPS%)Oz5rEAb>Auy+^mUnddRvwqGIDSvK~09C&B37JCF| zFl{@f0AE0$zZKSwlh5x2b5jczU`dGdg%AYQQCh2|^7P)>!qU=$O$nv8?NnA+JIXOi zO8eZt7j+SoMuKZCy&vd$OkqlBTVp%xQY-g53W8w!qHFrJg!W8vX;a6?lf3k(V#U#crRPE zFROpRFG+-xw3u)g%%iS$1u~&?N9Ttl4?Ku$wkuEAv34VhVq4xVMt#xGp5KbQ`8Xbn z6P8WzT30Km0T^7JC;v9c9|WI!%Gvy|o{z;9s}l?LpZ!N=1l{f?;|UP>Di-Avc*YrI z13#P8g5H(7!|ABJ+3ms(Qyi45QEcm3w79o%#(gV!O!(a%ZivLE^2Zh)ZyR@e5RdE& zsWM7vg%>4S210^?3M4Pu63xF|$|~!7520zcSt&iVXFpz$Ks##v8dIJBTk5p#1Ff2C z)-`k)JO5p`dLc3n{LSVUqpWPI>TdoGz9sP|Xq{Pm){Csy#fPVWl!SVeB8%{8H5SGg z3j2fqR<;lm@dhOY?GJT_^~8z}_5NUEoWnx-Jmk3P@qk9#bYoOUBS3v*G}|}Ed}4tI zFL4ag#X9RZQMPlJZKDni(cgsgkGfv3Sgdvdi7HeVi~fa8j5`2%zUBGPpk$?04q+u} zWY9qfVDm!1Sgdv+EbrWP*N&~F00db*Er)JpdgZR2a;WJ1b%f=eyWmnaE;EeE01#IZ zy}oR>f)pmMF&4St3JEm?akUyn0>dq>h#TeRjaYMp_p31g)IbV1uI6qx?aQN#G62A= zl2w>eZtl5d2jNRlR&I_;hW)eFxc;-cVqI(9ZP`pNpQ=$jSfLddzfq#(Q%uN)gbYj> zK!F~`t8&$xp_SFC3|}o-vs89nLCIpNW;pVU*irMkH(gzs7Dh+AZn@OV+PFr(6)Q|? z7$=XYm!)F6HhCk|$;r`h)L$e)eF-;WW(B%MjD~eX@V!pC4D%0Z1VT8DzTwAuDvo%u z1OQ7{T0YS&;8q7Q-%E#;<@WMUa0KOYr#}B1;pkG+`Hw)vK3O(E$HUbQGYO;=a1k@IBOUBqy7NIh<76aF>5dmBomwy zYzbqW^8yg{g>CJ_8ufz|k~Hw?8X(c_B+fg=h%Gp4C+jbg>X$eKt5eGC=lxrt`v(8^ zdR<1HQkXa|XFOA3gn=dOnTk@Y= zcwjG>(`5t};#q37TJqBNBoa$A8D+@tw(40iSJR*I&T)e)#PFtg=xL1Z{xO1~8ib4_ zNh3Xv&=k7MZ8!oO56z|lXTu&OVA5mbt!1t}C!zdC4|&*+rk%S!K@@Oe1Y=q@muNFV z{k9{cFafY5p3)o_iO~inYXq6CD)IK4T1cE=8+?1Jm{hSCVXt&) zZcXEy8Eq1aFQ421S)E+j6Sw!Cdj{XKO4hP#wlr51%2162T&KD`%6V5<*kV;iZ7!sEugWnXR!^fYpl& zz20d04JoBA2T+d;_GCfEkN{NCFfM~)yXWx{vZ?;2UD%Qb$goTs`2vd?Y-oK+(68Mu zWl2KrjI<4n%NN|QchfURy+Cpnx0aT&8S z;zr%+@+g5rh@z~*JU4C!w_kW;Ucw8jwOZ0_T&Ha%Pf=nJ4#ILdVYG9--Dwb1@3{8b z)m2dsTtme0jmpvsL*LoMfNlczb34lnL_QKZYSM(HP8yWB+=4hILk-3OHmrT-R&>tj&(H8D=#s?-D*fSk;i|!T~ z^{4%jugwrH2GT?DY-XORG^%3;zh2KUaaf)%cF@N3;)nTi0qLf5q(PvKVQB0~f512$ zCY$KzDMHn_mxr{Q^@c4^5`@n=K$xV9R4QA3V*rRqKO6S4E)5B7#)?Nx$5f(_tzw}I zcU)a>W*EAFv*x;Z!29D>7Dg50G$XF!CM8o8@*9jC1DKHNLZ?ay91&9OEK~^w491C8 zdzW{(1i?o&Ae$1z);ZFlLQqG>ROrD7wGmPu4FK6Zij_k>xBT_k-wKz!kKNU%uP4cR zy&(%?=q+ikyU*9lu16gehs;L*e+8{?cPCs2PyhxaDIf8H2I?+!SgRgE{dn%0PaVn>Nt>bEDsQ< z4&8tM{G0hA`o*i*A}}Co7-L{L7DW{4I(lFcvzWC}54yzOIBLy3pdeVykjkeQ53_)N+W&Btg{P@_t4mT8R6W)8QcVb!NzLbG|0E^pj^{r-y=f>_Lu?@@ zc>;PSuE9P~zK`Q6^DYF)8O<^yK&Pq$=M`$A(Q_drGO#iKgPEq)vvzw`NKY4?Sfi74 zb-~bwPcLqwqKnQ3qn)~A+XV|PEuJ{Oa{Bai+jOj{KzMp4Z9Wf3({{Ub`tHBg@9kGv zK7D!$ZmP!98cWw-zx0(XpHfNNjaQjCeY&(}v~;@fw>q+p%Y8R79Ss#BWVA9m2ZLe+ zlm#H#5y5Wbd_3f(a=E(6`JG%VZt1O?)pEJCIyJUPSgx4kB}&UeT7z2XeJFn)d)#OR z@Jfk^ej19RuC%E-YVq)7Wwm^j2F}MWB?u)WuD+g=yY2@$!U@wql~amy8caQPC8ac& z%3JW0H^lFoAN?GdeR%#(S>fu@XR^;z>kJO8rghrdbiwPXWD58@FV>er_EhMWop;h> zim}dzfC1B;DmtMBB@x3vZZ*~47s?nQmVjnUsMt==ko6x&x{D)Xv8@NI_DzfuBwRsu z$J~o+W-1lK^r=%n6-VFz?NPGfM9iZ&7k(#RBg$~H>wDgvG6h4Dxvn}yycp+&?MGq- z4fqV=0<0NpV=~BpCbx3RWei}#Nemcpa$kW*%p1FAv-10~B`>R*wJObo4iJC_Pi*qG z`NOu3EfZsV2{oHg37e z$B%CZt_x5(dGe|?jnKG2Mq5a20m1tWZueq1i2k`IxMluZ4LcDc@TG8xk|B)OT|h{e zNEQAq*a#ke8@=KiM~KIm?3SRHHC+ORpq@*YfqMYzR+a2(QIYC8Q3M*qpv zW$0j*3>jefpL=kQeV^dGMMuz;YG|}X(`&ArA!`nH>nhuA(DRl^yr*Lw+H>fxA?P9Jtg6rY3J!%a6u~{v%oiwFlrU1`67?vr*{=k zO;5k1_8F=-NPKxL<5S#x69+~}IJA6Pmwtb?o;Z7NSe<&ry zZIbW|%8DUyJ=5Q?r4wyI6w@UT;%nm9?VR!UFe9M@=hDvKe@=HG25O}hSrA9H`$19O zXv{$&*OVANO}))gsLBOr7q*S>cejoJpz8bJn21ti0HUN61n8yd!`S^WcE{FW`&&i( zko(wDE%}^c?l6h=IuWONU1KBKnA!l-xJ%?xZGLpXAk^pvg#N=NX?ygw)I(qu;3+ez z_6=BCKeKBO>DmxH<|)%PMhPPVBIs>K7G(d?Vy5Uh&@0(4S@0C8r;k--v3cYwo*?nYTcZX5I^SIW)g~Aq=7HJD8KG*@t`J=_Yv1D6+R$ zFwfAPzh(|#Ix~yFt9?&jq$DHdZIWkx1^ykrZVfDJ-zOmn%xhTMr>luLeAW#^_kuB_ zzSdIqm!}As){VK~hM_x}Ib>`0m%sR z??$Jh?s9`ueqfxP#X`mj_Zh?F^r8E#x;m3N75C|(IB@J0arRJ|=R}bY2nXbzaOm1ghfjvYmHHY!fK86lP zw|bp^Yyv`bA*VVbq{z61YmdFp%`KTj24#N#qajPrTK(OX&??e7Vs+@zfi)}6ob&)V z=mkd1t`>gZ^)Je^N=kNiV)H%iEY5%Sl2nf3rCmyrQnOituQ+;-+mo|Q3Vl{y^xehz z-(1p8V)ms{vsp?KI3&^mz+3Kfmo#zEF(RRih>UI_r=T2U?K_G@ErKM&Q!9qUT7|gE_x5ohQcVMtF$`=4Nn@dZLyi|^&QaOi8 zrPFEc+6_m*h2U^tP0O)TaS=z0i}jl44(X!HW7qk<>(%Oui%~52SMT7N#!my*FR)?+ ziF@0O{x$l&(H}&E9&$%V5VDu;{c(SACELb)coH~Vt(09~f1Gkn_$;`zAXuQofHF?Ktj8CY6*dhVb|s(@Se4i66*s^uIt+i6EwM|<~xwXD#-*#Imt=sEcQfhm1t@Y_`w6!f|du>Z=DL2>l zec`oDDYf2OYfJfaYinz3*F3l{iC1ER@H76XZfHV<2_0$l^7X8fVB}J z4%OvV6Ev-(*Mhpcz^59LvR(0xGv3&puNww&zEe62v00P5H$A{hP@s7p+$6bfW& zFM-dbNdG`KsH2-MDPVf@b|vlD-#0gDOJ!^6x^e8fQrjOlQfkA9**6S3P5&3{eIy%( zFthJqW^5N3#8xYsKdEh_=WgG$v9(mT8as%Rua?pvgo!aM!;WFr7RZ=grFlwLac${}7G~6$Dfjz6ZZV z5u%JKw2Kv^%NjT3!h&pKeKXeh?fuUt)(f)PlnWYfJ-Z?q3xN^AI*;KWaTLbHZ~K;g zf&x(HBzc}VOx3z8=_k^aZtXVo_B-*qwR9z0No(CR&oufA)wkMD)km!#qmfb=P1(oS zlbA=kd4(*t^6HEWEH7qU8m7CV>P2b*&(Sminx;l3$*@2Z_oCBboF=1bKTdHAU^Mi- z(_*p<8-sMX3mc<2iTZ)eyTvaBK_GRUC`DxyAZBZAGZcoBDitSM2K)T8g&|^EYkiI+ z)-t%CBi@hJ`QNR8jF*x?hz?Pbwl*cB_@6sM1j$k?VFgnrH(OLH0$+VK^P?^PXfj_# zY=71M^&tos5NL|eO8_wtz=b$|9#aiyA%us7;2;JZa6ox1N+mO*4w6-*TBr-epxvk4 zxNc$L*?WuQey0+?N6Pnv6&QW$^LvtL(K3N1&k5hH97nx790%&t+m&9zkc*gNAU%4% z^`d^2#&{IxE;b*Eb6^hKZyej}K@4+6eOQ#Gpe@4EDLeOqtLunvB7ttPpa%QkQJjzI zcogTb*zfa0sXZ1fmMsewgR9qSRYLn%BrlD38q<1h%~WO;vsSB9yw5oZt=3krdNK7_ zr`g!=_4{zx@55%le{tVZ8`)>;{878nTi*hX9hFg0bj6;aC`Nm1+gk56UPqBubK__G z{eGNw1}Oil*w(cc|5jXf!OA#~V;U5+ykMqZVX+C2wxCfbDdUV}HnD$xi1Lc}KfFqg zW(UGIg$>NFwNx_3_xVZ%py}0qSo4|yX=#xKSJ69^80r9jyO#LXs-LW1e%ssLCYUnC z42ua%r6~{df-9@j1qZ6lB_xiSZTkH3Rc$BIF=0lmz;|+u}GA zV}ERA99Aw;f@bH3_ysWr#(C;1`@6fyoH(bW-QE2|!7ou5*LFcIbCR0En1{@oy%HQmVNBW9)RkskFp7%S5E*YsADbQM8>=#8m_3@nnZ647;ZR` zN5W`1s%`*6gaJwayeX*T7^T4vn~i!R7z3iUZ-SMQ5aqRITOcmfF_a|em=YX`c5|&P zL}?{3zSaa76G^?%41LhbI1UwX#DiMBy}0VAKxqP$i?~tGN{Kd9D&lH^TZ%JUKb0Vq zoT?MXR0|93QYop?Nhzy0V!2#b!me#hlTimPn!x>I-(;^dfW5<6pWVL@WdG0UwyNanXO z4-6-vK4WwP?_6kWOnpYPr@&Pr$4Mf9TWE(;j~3*!!`rrIf95fWq{+uVW1GB@{h&UX zT5Zb;H>sG;1IF*d6&Thd$s5!UEIl&j?B*tdBec1BRz!wqaq$RH1pLjTc^`P9k7?-> zJLk{uoX{;-Do8{+D)U;sXP$z;g(qddP*=$T&H;BsCeS69UXe1P_$7vx@1hap-m!mb zH*RQn3obLPe7xMgeO0Eo^=t$d2Rg}Rf0=rVUh_<|eOtSA!4*)7_&`ij0K zt}XuIDmpTvP+XhAeoKqDF~UqZKWsbkTt6wqmY;P*r1T8>D9nXq{UG5yO}MObYa-Ey zoVK2lMdX0fwXy&W5HMDCZe?#2Kg?#f5=+hOA7Dt1Bl$QFxVJWe0Gl~8C{O+LYlvSZ z4@&KA8PHpsW8u0#?XI{^s0Xm|o82|6)ENrJA$_{=rmnfc0uFHC}(kN`q7 zcb@Z{ou&`~Qq+!Ltd`)VCiK;{vQphX>^C1u5K3Nd@WqI;d2O+Id4f>#P_uuyQ(3!i z*@V8jo^q|V0Hxe2K-Es8pK%>=az#+8S$B%Ye?RIPD=y#}I+Wr29#6)`n*!>i9MhKCG0Cp?KiPjjj_EvUIoY?IhQ`%wnCsF&l zZsbH!(&AUd7>??QC?&W+^}AZkH#f#zPiw%&)T-+{JI9wD`3W39pY|1zyduA>t~Ic1 z`)I(+n)<4V$6!fZnI=t|WweiO3uV7G@Ag3E*y4VDsTv$%z8V&0JAalY=@#m96fF+A?f>ci79Pf*&)6usJ%E)@ zl1}+Yf2f)DEpC;g4W`C-jS?Vq7z8nb#CE`S!8aHi!VJL>PMF0vqY5GLBa9O5q@%2q z>e<3AI}NUnFm(;4j0j2@rAk-p4oAKV;0BlhFymm(q5DuOIb{@x)YZD)Ep5T=elAn1 zYMruf$zcibyl+~StQ7qLSfFKN(Il7GO~3ba+4n&!f2D1VwzJIE#%;G#*%uhm6ndjZ-*L4X$P)f7V~(aqKc z>yf%zDr!93s@iztX5o;a6d^kN>G8!lC?&}6N{;g*h)xN>#c(huf2z3#@<8e^8DL~T@c^l>TbQz=yn?~tJlAo)a%K6;&^=9FAO)2AB-t| z4`a)MU$C$r==|An250;mE@75MS2c`F*U}wtOw+;-Y~=U~*`Yz=La#EJ5`9pst_PhrcA{ZR0cdznKzaFWjmeNBxiC z1um9Tz#k__IRAgaZ8bm3N}QLnEOw4rlUdd?ztMJ$>elMGcrGbmB2O*N?u_S0QaKrTW`fvjcs! zy?w5~xK#H7*U5*&ZR8eIguaZw#giBllq87$TfJ-}EbPGZC!GZ#z?mHqiUz|Q9~M&a z1rUgH+ouW=tsf49`+0~l+oc}Ngw9rl7*{?){eS^+t~O&VOe9-4I=8)zqOk2cUa(Xu z!9J4CSMB2%vm{PdjHy;$C5hI`riM=Ybt!i{wh$05EthW`4AHd$#QKTy!h$Uocq=RA zr7Z9?RI5wn$%d8^%=S)s%5;t^PbOLj@QxiXM^E0B5`>cSJ$JZ?WTE%02W+L+VtHr= zm%LiUHKt;c{#P^_eBK|&#R2hsaibz#-S`XjqUiUFqEU}x&AE=FdSmUDdO5f|Eb@OwbQ}-)hsJ62;%9D@(!g%|<=Fx9L{C|J7OaL& z5i)XEa4y-=Jf&ORQhKzIj9Q?nU}VNq3v&d7)|aLE6_nWEXd$*^M#_kuIrj>g)B0u- zX07q7qd!TlND$K#Ie-)SCdp?e3Z^vVT2=y?pqN{S?}Qg`&Vgr|zCFHDagLpGIm^mr z2Xn64i;HcUp=^i41X;Jo^+;)c8B?lizVG`rMJaY^BvL<`YQ+@Aa#{T9tUJ%XxBb)% zy0cH|P!aq0=7ZVnW2>9z@(GyTqCp{`(uJGNL`8!^ONG*1$d~knJeMe^!s%rG29=7o zWub5a^ll^w=H5G}@}?M9k8)o?Po zetF@u>3{RD54c=nL}_1Ajya*&WyYrx6NOmAj%skh-@APyrPKCfnyg=+3{S*06}-O*C=B2|6LIj1)8IomPb*QO{$PgnU!aa#8;Y8coi=)tw2rMqnb~o1) z)!G^QL>QA~X5L0R4yqT;g0rGE?Y<$2FpsSPDUz!t5HflNGdKwX0G5TS8HP3 zSW78O`2wGoqnMK@0$%MaJ}EYuj`XCqnG}KM3^UBRjbmF2=?Ov{-=O9e765sx^+V~*h-bKb|W z^aMgWaLp?ApDH6;N$@>mVr^?eh+fiYd;44zQUD-GV%Jp|MZ&noF^o}Q3f8Y*eIN9N z4_U<~I*uz~ll=*EGQ*jZIbseNQ9pjV6%#to_O;ec)_vBCBrgl6Ibwf3pXTG6y|@Aa ztuq`Kv8{tJtEE{)`-iC*#SXMO(7u>r#|e5<5jYM$HJi<5dk?TliEp2VY00Np`(9e~ zZN0guZGUlbb8&J1(H(letrs`7?Jo`tK161fU8*OcO5#-8GD!zXeFXSXP>FUa(&Zl@ zJfRpT;o{=PXmK&ZjLV%nA3+Am5m2;QXB1 z;Qc{A&bJUN#(-4hu!%=!G}!-9LXdFdehvT;W{fdRc#C(4GVt6|$@PFTe7M6$7<~*G z#R+>ucK||!IRFkL2mt}aYH^HE9M{;Y#Cv2d+5 zBAhoJamI`42PtyRk~MNm0OKp+G<-f-2{B`pdeP;D_f7Tq{+$=4$#e^iHtbPC@5$Gd zB<&CS@n{Q1Tj&WtCI;}|8IOzu6u8oYG26JHl>6Ur-a&`~q-o1IF@b@F2iw!+0lewOw2>8)kwu28n@M3Z9m?S;;Bls>oo+tJL+D1Q zCr{C&iK_m91)Ah6osP@2PA5e-?*Uuc@f?2MjUv}IcBK+AKYP6ZGhliRA&!x8;;6yJ zlF}zm-hRi82yujs6Gu(X*Y7d^Vj}{HIajas<%36wl(gO-P6Wu+_4S2ZN`L-EOtvVd zjNt}iOy17{(a+W~W9r!)5XEGZ?eQZYK5JYzid@Ax0s#=#007E%$VDTH9fL4O=*By4 zKY2o{CC-~)_L#q!V~|m^Y`@s8{tXFYG91*Xl*E@ZUszvXJ%fR65=;^BXG_LV9WzG1 z{cOb;UMynv)p?!&GH$jWwQyAszUN?Nl+PK-+Y?zy($Z@>}e3%6iCpjrH@^f3_m}z$gL5+hh-8jZ! zHydPjvI!CQ;G?acL6xnY9Ij!rj(m7bR7U&D#mbd6+Mg(mC#n%Lz+40i#nEp ziJ~Z8$W?S3;)ru`rMxnxbr;i9=cP2>Xg3lTlWyqmpauQ@&2A^ajH~0PP9Il{lc3|? z-0#;+QcpH^;(G61z(ZveP%I^;0V-n|+`-Y=lP9Zbtyf*X`Q|QHd$qKB^5ogRb*ld) z?9ki$>9sO~K*|76d9A77NH_0f^fmVGv$tW@0!M)zb|$Im`upL;>9_TzjPg?aUjaCy z5AXKh>Lq3OUqhszZzRPlD}*SFF)!NJG5>CgTJ?UKZ?c>2x1Q&nP*KaCWSsF~7AyN{ z#!(t3rLo^0u*n(DOB-B5+uP^h#jK2qAjl9X5r`&to?Jv1SrutfRi_JL=TUzJ5QbW>domN4drs@1*XQov?Z&V7x>yH%h^k*Au;T& zfA70Y1K<3T=qZ`L+$onqMr^$HE~%~k%jdSY(P215z&J4C75#qdq%O@-0F~)npxPdJ zzsR)UDIbg{(+;^fg?&aKK`@{mC;1Bfpi|iXaQ*vdzXl=r+62zj7QbmqrvrcWRt=RZ zzhd`PldEeJ#DDDj^$(w=r%UEHg)?yiW6%FKfU;7bi%WQ9I}A44WEnja=!(Lm!mNSUPqdsmOgW@#demwY7U*fMwiDK3r;mddt!hJJlq02cv5 z+%7LI_WEmAbNpP}{eC#&__^gy*arZPA1ZaoLuu}5ZEODn9CLl>&}VmI6G=h)Y_7NB zm2G`x^L`gGNTUV80L=H9m3jBmT1v!TW%zp$dsPh{yv_H%s)|6{-kz^*#QKn){*PJj zu|8eD6$1ijK=9xIA{=8D>5uy3VYh^M`Zu&&^z)5Ddy90(8`CX1+UamH$hWZ2O%GE# zsyBAPfpJABC5kV$##M%ZLT5r7!-QbglfMQb>;Juh0%Fxz@sD~|@_@f4l;(mVgb))# zDIu5!7OoHbVnQjym{CeF03cBv6;Qs$7FzXm8M7H5HgvpCfBJsT`qNK8L8EU=|K^U~ z1$|0OK3ezi|JobBgu{DAJHI=(-e!H=`hxX4{&lh59;yVZq3=rAw(O7TBrWdb<~ZNN z-HYV$`lF4(USZL{*JaOhe$m2RE$y~~hl|vzU8!BW&TT8;@68|@E&mBK?g^Z7*EH&- zk`DdfyfS_fLdAOVS+g*m7O)Pv-)&ic2X?7(eP2kDQuj_@NJ?B2MD+QKtF>5K{*T9z z)aRR(iWH7hL0nuQ+XUAi%nDM+TIvVk>M>tRN?kWNZ%~Q4Ua&4F9`_=vJ|xXAN<~*1 zT+KTz9-b*+#Xq3<2u2U6p%u5_mpwr9hruGCrXuMeR9 ze%fi)=iHH6bk`JvX4 zS@EgB=rmserp2rllSdm*!|})p4`CgAe{OVQWI9Cg8xVEj#_kppC&CdIRIXM`*ZSFXgu z3StP39nR^xkx*|^&3_+`b8r6lhH*0bccr3aaLu3=hPAQ&)s;0J5YhW`e(ybqJqupznK39}VQSHw>Q(|NPwH4cE%x(G-#S9p=ycPnBO5B1Lp)^93vkVQYk}IUs>7aC=Bm z3h!q;Mc}?zZM^^e8|uX_pp>)upIBY<{l8DCbpo8~oX`Oy7^euO#0)6OHRV9oX_ymU zL{jRVl27^{;E65Z33N$KCM-hwsS1t3NLoP7#^ZTp`dMz``pjprV4a{15)^p8!@B^a z5HO^iaHA05Cx5U70DcLoD8bA&E@d|xek&}nqdp~DKnE)c&NE4IBaOl&1L~=^j%&_JG3fsPIbmp{FmAjT zVQlcQ7j?i36ANB0Z^1tc+t$DTwnqerxm821C2Q{Z97|!2jF6c7JI(AMyJqkOiGaU9 zg6F+298BAZ_4OpK9$QB>PS)2?d^K=kRP0NGU?P18iebWeC#lb=)IBnsPIk~f$ITg! zrch!#?lQ8Qe<&|}>t!QlE%3R+>B}<*2-9&uVs!?y1+WVCJ$=R7vb$BOlu6My?(+eMlYe@9+AT&&6XvkLNt3mG+AWeaFWkWoGGs)KY`~OC ztHGuxp5gl69bZ~i1<^}{YSJf~k|j+FwdxYRs)|iQHR<6keLqc@HTARsB@(Frdeh%e z25S{bb-c0?Ybq;igXE#LN;24^cC@k**{l2BMT%8g>4$J-k?(nqE~psf?`AIa(3Cfk z_@yEjK=!s{9Ho3Z*#&qkT?kA07yLavQ(yRPJyRjCes-UbTcI278;a`f@X3nh5np(r z^m#SYzxRLDOy6D=!~5W_X(eR;vsE7A?W*t2MGs}mH)`3+K;i#4A*7}qKupi)8yq{M2UN>+|q}BUO0HUc@(d*%tHeWLhk|bF7yqnOf zYR>;TFz-{k5(miP+jzgj&e_e&^7Yo7jaRl7>A`JwHc$n~Tn0=hP}v0zOM=m)YCrGK ziTdXl4*KIJY7f?xHwH$Dcolmvg+DF@!Pbkn!b&-spL^!T&uHhOQ4%+Z`t7&hWz5ZP z7`hQ?z&8bJhii*|wEY&|^>+BhQdlWRTQA-Ug865kdGRxfG_X|WqEY|)_Pg9LbZ<5$ zg4q{(nb}%PIfLj+@vgVm1E^o!AId+}KAtooX`)%>9U#KuszY`+E<^?59Hd}%?O?y} zb_)f$&w|%2;b1Y8k3O#y>f7ig^+ z6sr+wP(Ko1^W92$U4VGU1ILWhGV*%mzEyDWPu02B@m8~XasI|g-20BfP1h?Yp;FTO zh84y=<=(0BTRS&Kt_Nm3f5(A~dSi41M|EemqQ4o&>cy0o-G=v_EpJBa0uah}9h`wZ zxvtScn6lN+c1tM}PXDr7yut8j=159&h!~lB@I0~ZB4<^-vDj~Ba5%M{9PMM$ws7{{ zmPJ|KR*c&~A3)DEY9VC9lT~m6YE(!^5Xt5i1Het<4D1Q}YopN`xGo&a2a`p&-1+9m zz;)q6utnZ0!DfsNnMZS$w;ve?*23_2Tmbi*?PJGIckO$n2wi6PK;7b<8?r-D`O?6ZtmFSO)9vQ@V-YRL=WW-hF!5)r#%%o=nvC-}Py5 zukA@?>tgIq7O0AAY>Wz;NPUj}I}XJXZ!o$Ma%&82<#9=&BqU4)Cgbbat$)MDrD!p5bg-ho!bX~ zZhwD&|AG?0`i`Sv0U4*IBn&bB0*Rw^68r56t)m zph8JhGD(IAVj{rmm;z)#vhFDGso@vB0*JpgNky4f@b4y)tx-a=Tq% z`Fw4s;<*4({?s+)roqFfX5q-@y4a{zWhTRn?tP%}myD#F6M^mo@;3H;EQ&2t)vl7u z1OZjQQ&APJ(BMrmR|LZfsOf(O?Ur%{}wGp|~vWqhA{YqjdZ zj2r%CR1JKfM0*Vx_(2trZ}9B=`Sa(`_s&0z2svM8Aen_!=s$1)evf_<>gp$`DX?|~ z;EnE+EG1-%uk;s_NMTHglH)fjHim_0>(Ck3J#%O)S^#V-Hy(ddFBF`OR%^p46u2bI z}1)w`SJ3Ab> zf*o*8>k^SLoH9^ir9lHTXv|f0=6_bwvUu?{XKdb(@ja7^AT^WI zXXxZaw&?StQqMmIF9^tMQexk|FhyVH5})qY=0-S3`F_ zIHTyga*Lw;aeGELAe#Sqs>GNM@f-w9STJXDbUiScKOsp#&M3+)imtOixoP%8ff1`& zTn9)zIEkRsNRCtzWITaZK*?GGj1PytsEwZf31xzR@$->{p{}BM(@m=5o{>SPGt!`hc`#*Nx_XR&wpZ@Rw+OaGl0; zx#DO1by0iM{2z=P#ut@KLWa|SUhiBt8sYcm3*GH-;X2K%)fV3>2uI=Ws1w=30Ebn*DdFDIscB0 z!Fr~E0C7N$zj5Ii3$Xni&bwYtSS*zwCm5=x%1)V1zzb#Y738BP+Co<&)QDX`V{)aH~r-kEO z(?IgPjv!PdFnA6C^7*_ij*7w`2_<3v30ac(DWSX}GWF1Kb>V{l+pO&N>v4hQBub1! zqfuMa$xYY&LcwztV1zo-p)>3%cI7VNnyyO)c5NmpGA@+{Z7-0Yp<6^p(K&QGx;yIe z>xmZ)lUb_dMR-~ScI}9@ViZ~oB&{Gao^hcA@du-m`(ax%(U2w{pDi%kn%oOR(|ZYa z8MuG80DSPc#R8(g{D~5VkcHuV7KX4FhP_Rg50;v2sL2_dUCOqj<3JYLyjNMCZ;O#J z%%WC;zTu)kSR8UF@!9gN1+Nb$+Ph9xEM1d!ByI#S4Gb>rNSbafgUA$Km{ds=+K87^ z6mxzMIJWHs5Sof_^bI-U1&>7l5%dp;-VuE;Bn5mqO$t~LDLtVi@#wYwCbiXfsMrsp zDDW}NM2dd_jmf|Fp7%ug96&A~`OpmICaUw1w4UDcxm0f)RfMGF;?b54ih;S~~Xix%g1W#du}rQ(~-D?p?6f0B<6L`WZY^?B_%M#mpWOvf#D@>xjSym}YO_^e(-XUS2YMJi?VS}5 z6%L@B3;4s#^nFTVOm2YEacJKO@)vp(V}fKvc;wSNweH{|P3Lfr%DM)Rt5g3lW`u-l zD5QKLd~ARC#+u3Cs%%SViXbRVx9qRyDK+ZXnWQhDsd-)9pfo@K-8+iKK$Zc6u4qKW znxX>+Sq`q>UYl)w3QM^aE2`?RbdH>aHRZ35o?!Rn^W%Uq-j}63ApnHr34MRPRx1sa zmLCLuOBt4Gwffxm;h67Aq9CZ6&)6?IK4ZS73W6v>Z(|!}>E<>4$JaW;^%?7bid-%I z0oXd)KgQV(SC4cmq_*!tPtY-El=w)(P61^qblMvKaY<41nXV|3;^sZdo6Cst{OGE|Z`! zQ-0uBSf^xjh_$A=@)P^-_QGgf#EkLE-MiuL-4zQ2JoPvVdk`)t@AnJyALUFzk&3^^ z8H05GBal;IjQ?IyVxpP5u2omi?e72yE{M+zc5!(%<&eUhhgX@~dIrlNwJ;5;jecv<$Pv_X@oO_zijX?Bo)s$eEezU}W zFayKP&G&Mq0i65hbpA4^3Ul2;&T$kg2rP@pEU--CbI!R>Oe- z@KWS+o|p~@6bH#uM(|7*9>9qbzv`DVNP|`=1Fcpr=A>)WWVsu@y21rbmIxOns{+%q ze!uEPWUH!BxNxqxj}+-Vt%6n6_V!uo?U;^g8u=l(QG)YRP3*%M{8I+6o=xsH z?DY#dOD!{R-EQo)UTD2ORBgf&64 zO30?&NJra3Q41gtBL;0DnYtO^}CUicu_h^gMk4cj3i#HbR8De2+_R~Bon~^MP2sT zZH}9%>o*Skx#6Q#*}LyrLpk;ffbK1?e4&yHw6^0LwmoXJP*_+f6zE$fE>+`Bq+WH<=x;9$<|9_riqUg8c+$%JJk$On7kkzlYXiR zfi3y-!7ZCprlo4$FRA7YlOvrU{%_JW^4LC8ps;fL)2Eq`-%v$C-ebz7U=n0i0A}_z z)e22xb>t1Q>lNeKfo`>e5f(jHHsNF=Y~=H<=)b$yQ}v9Z$YV;VdIp$E$CMyC-pcc& zn;u1llwj=(6)`i-)^qGS$fy7oKCAt$<}r@yS*pmqh!=XhMy4oqS5bE1SKZ11_j0tn ziwcsmMqmKWD&Nb1HANEWuBz@nKdLM1ialuNB)xvG--W(tPSCS&PSW3oenhAsDAX!y zSg4y;O!-#qzA`Zc-*agdj}7^cq!8n?*;z3Tr9YAz_7hsC`+C5(i(-fg!J!E4Kavf3 z4O;ienrz6w3%cvPvzYk~6m1*iHBmPVU0joSai3!TL-_rr~zq~#2C&TaOKYXFlc;ST?zU={D zQIz|rVbJ@&jeI-z?fi2mP4nb$pZu0Z^gh&j^tW13SZSVwSrNNM0@)V&P5Et3$vKxM4mQM z6?~7>TnBE|8BrfuQW;a1?$kBP^jpDkYdfks%oHfag3QqR`x(EjV33vauu<^c&GPyB z=?$Xl>_mw&MoWhn(^ax@x}MLo@t$CZGB|E|g&2jm;SLz}ldyrZzsW4i=|Pv20P;2> zv9=`CI(xlgjm+o>GQdEM@@#6#CEV~r3^gaECaOZhC=q@)=wbC#SWLYuxRqfE1a`5i zJ)<-q>jr_~of|nbyZ}kIdf?$4h4@YS;Z3X|Bm^0X=2U{OcpVHt64O^G*7}@?=rRJIY6VletrVEz>z+3qb&qR5XU!FFxOg zncsI31p;59AQcOZ4A)(>iOVhkeo*I{Oph&P;G|G&aFU^l(9qadpzbvoRnOuoVUyW5 z8#7t4XSO1ao?F&cA!F$JJEpn2JL_1?dJz9wf8ubY2Y_t7zQBYFTmO{teeK(^Gb z#nfKivH|2%*@Pcjig9;!Lh-rlnX1~pu`pbBVo z|82jd*l0U;gP2W9!JB=A&Y}m=>+>6XizK}gLBy)+zHgxqcD}G@W28ck z*=E9THc1cy{#a;b)bZ2|U{8EvyAF|o#WmG7O@~tT8WJ7lo}F0B}%Idf&}UQ z-D@psGadp=5gl0stU}=hC@^s#6@{ETI={HW$M*Ct_72u_+YMoTO!n3 z^Jj#BCPO}}7_+@yCyoeMGf>+yzDp8Vv}R6kjlnP6U&gOIzo$_XrM~)G_-gbldOw zHr(!dsR;PA_d;@*&MIQaWD-9G6P z5)@+bgN9yjj7z|VywxRW*No1D%ii_=4<02XVIWBaB-<&7q6;~_kdFcQ($e+U$1#(I zoV<9J379d<)O8G?=!T))>n}u0%S+4QqMQ?Cr5Rs;{n8RwagLSWuNeksAZWT}G6jPm zKPSqXG5?>yb|{F#mTFC`hk)O7(X%mRib0n7iZ(P%I+moLW~KWk`KF2_|><%;n$8-t&e zu^?dvmcp>0>n4CKnX)7cSi%gzR2UX?%>uZmZrc(m7McWOF_fu`1(6^mB6Qi$9m<8i zb4V(wM`D1&;zWc@Kxc3XeK!)sOQ~6lBl89_N)0x~Epb>1l$h+w(Ex;CM;(R$&0J_6 zb>+D$m6g=Qt|?dCM3194qc5Z1K?uAgj<`?@a0yZ7m;V0|HAxb=lX?#deh`jWup*4a zMFyvE2>}lFBeKH*%}n_t$r(jMa!^8@y zh)$sE;uO9aM8hOj-9u()xJ%zbr}BfSrx31*0J6?lpP@``#?kQTm7low1Jg!_QVlfv z8iJ#fLG2B1_8)O8cY%sn`#c-YIc+>1n|rYKO3p$}a>CIfa>?k16e> z&|;kd=S$BU?H_~$je$!>%5Mt(3)FsjjEL%!SUSsJ9D)#9yJ#9dWMcs zQHYjg6*A8JrsoHOft4CFwJ5J#uQReFMKCVex+TXlg1wR`zRDgMISHQ<56x#bX^e^X zO6#8Y?eX?Z8m+A|YNJEwCTvBQEmV}39!WZh|0uczxfq$DKOn!5A9}ykND2Z8AGQrg z>C2WB@dXu+d86b zJp(8lPiLg}$P}067ia>DNQ^wEKj(F2* z=M~5(F9ayy#QuDC#{rCOo5FG?pnq&WX>*D0tuhS83pD+4i?$Myg|5~`&&V|H7PgXZ z=eZimYexizB^DHU1wd*0htm}jdGXrZf0|+KZ*IFLnRWfEG`#Wq$QID=cxW9HET)|i z_y$@jtj4W>+_eP0N(}Ermm@7SS$P!y!wbeI0j{iZEE0+CYFv*1k?W_L#R56~e1$X(2XB3?Av9yTIP5uW1)X0+|hKo-;U=u#j)TG(adYh}+GERs<-eSYQeQfOvrbpx9mOZ+5bV*1Hl+38mN%I6`q^2U<_k z!)m7W>Acac)I@NvR2L!wQg-RRPjj$Gd%-oE%z zvtF&gx_X;u07I*j@&nBx?G|Hct2gIuo*^kMjOX3FTQs9qCvWZ2@nVM@ekU0j)9B zA+RK;K&N_Ex_FuvE5jDKb(b$3lJg57>bZ|0bB)C)>6SS5JG?e zAOZ{k8^eHLz1rJFL{&>tU_uCBu;3ON4_l8}nckQe3zLhRr8;V7O4pelMdN%-A|{MS z#c3)r!TMkI(KwHP)ooaZVEkWW2Ti7wmJ}eS4Ch5=ANh|*20r}bgD4Fr5CuAyA<5{1 ztQA}B;Se#g3W8{;qU0@AE1gxUa*m=DpW_xEtN61zD4ZyF(66F#QlA0 z0nJ$^5+petJn*OY>VfwDR9AThx@t{FU_hy%^c{)j5VBLzg<~0zcPBZWY#Y}y#@dWEEjuffa{qgyT(T*(OXbE_>#ET>1owM={cTSK2nC<$ z_iy_|fKc!R41oF`C+Gt^>b`amhC%y2<+w_rAh4Mb%oa4pn08?&bXyj(s?U52Eyy{1 z=uyi8Um24Dz6ik0q9|a}72v3|XiO*5n0aS91t}oo>yoc^XD>a7#2t^_Bmr@E>L~Bz9Wkb*3x)K<=uC;1DDhn53MPaf_=X_u z_@3hc($e>oMqoQ$;An(hKlG&F!Zn^t2^>Lsery{rxkKQzq>Gb59MRH+lH}o;Ga;9y z3nd!G!O23GQY`LBJY$c~r|`kRV^nJ2bqxZ+eb06%(aH~82f%RyUui-e+w-{qWL($R zk}@x_ELo)v_uz>)M-(QK3nDJ1T{bb%CX$VcH)= zaTL@EBk72)Ps?c1gYI}}#305ljE=681DHE-LDN=Humx?u8!mnnV?tP+fCA1z0jV=W zaEwTou`WSteAQP7p!IqkP@#0ibp;k&F4d|w;2M<`1{AGR=?aw3X1Crto8?HjlCC2P ztRiU*gI%pHNiMK(-HKKPf_i-&0jV%TaEwUz(Qd^P86yOTh(Lt{@cI8G5Me@Ch7m`M zA&&4`{{`PigR2_Ly?ckqh_u4tcf-2m;0etVk9$ZOTprP+*gM- z%-go)N9Tt)d(7U2jlsrX!_L5myG1unqTJ0Zpe)E!k0c84bLN9~&Wnv4dLM$|_Qd)6RYL_W-xo#q7 zT*vrBWPouX4`3+4Ky8E(5&_1601aS%ThOSfxl3r-rm0gUL=w}nf01||Kt?D<@!&P_ z6)u&!F~SIAoB{Z5OetlRiVtj|&UjL3id+Ih$i;v$5Q7j>mk4>GKnP(&7SNNr7Tyw# zO3GP%fq`GCQbq_YOzRB3Wf3c}&#QNR*1J`v1=rT)GVvg$tmy2(WZDTklrL4R!%NVC zwQP)OGJS6Vdg)O&BfyCEv_x?G{y*$JIR3x?_y7Li|5YlmVwnFQO6UIv60lMT#tx?L z3dGM?NeQ*n zFy7;-7oP_>AA2d^oB!y}b)UKpcHWwbJ%si|`c?-!F7?;%q!eMtr4e>q8a1fv;E1{o zjuduWYEx=c*TFV`ZCMsBx6B7Pvi{Nf4`9-**7+7?<-4hf_hb1RR3yQ`bd+x4#8Ssh z1@(odlax=U6TXGEAsQva5rk;!0qf?L0nfM4kVQbD2mWP+-L}ec618q*!St4HF_{k2 zgx}t4$~c>Dp}gpi$*xl~4`9{1ro+K>w1qb45#SWuh@&(y=5e$|CI-)$+L-pol2a0E zhr$-|mOk|<=u}zmmLDnAmsGc$8-a@Jd2R(T0xFjmma-tIV8(^!^kc9;08s8mvFic2 zUKqJn@3<}igt!vwOtyw`f>08XMNxdO0^CI!CI}^%tYv4EP&^IbE{h0vqtJ5!JU5P9 zp;pg336yKW8L9?Bw%lHRYF7jRD*KfxKve_i(-U6?__`2_!UhBgVE}L|1Wy7Dw93*W{SCWi5Br1$_2Fgqw0XZ!a!38e!D5@@*9`Qie*Y$YQ@}x6@iFTzGO{RpZ~s_;##Qw4PYe>?ic;Q@7R<;Mxd!*8@=ZR%7OSY>h4MNuLzw$k|^QLRUy zrK{vE=dseANkbj_vq1T0M93YicBGI(FOa%oE*{JRL>9e28|Ft(x5bW|?-?mSfG|cM zuzt=0$ljtIN594PoiN#wI;vBm#O_Pc8b!RWn&As`wV9O0c?kf(OB^HRozLL_{5d~y zP@MJ?mStIe1>|SV{cpD}TCcI*Zv8jwFRgzC2_?Itko7JkkpJUu?80cln(1`)d+Z09 z9F6(<8WjkBm&7b|y~isxj}CdNTEMmh$UwoK%~@p>k9sOO`-7m$=*_{rhwH^_6#nDC ze}V zXZbR-MQkE*YDN@+?*7Z$&$9g~L6?(s-;8dxo*jD$gp*P0Z=oxjZe5Bfgv+3wI=Ald z0Y)5y{Cee{77Oq3P!gUjzV{aRW4J76rPW#@gpf6W`uu}+fHh1AU23%| zRM@SFb@~wGv_M<*W@eC-%L&Z>i&9?MHHXG6d)FN{5L31|zc+Wp+Ldw@VT_y8)p}!P z+QbOsYPr&8bE|8~+^OW^iCch@)5jWN9EXi#@NbOSQLUaKl+|mI{eiazode?-&|))W zUOUKP+xh}9jRz>Mt0sCt5#>GZghR5X5Y44){55uqjd6Rt1zpb4DdX)e9QWg)56wi~ zU2Qn^4<9Q2VDsJUgN;FMj1ibU@e?hOF7|*0n!2(x1BW?*xPyCyr~qs#AigVB+Q9w32JI3a`?t zIHJH&hOfPbBU*c(R3M5C2cZ&j2AU?Dw&VM@YG4fq5mQ>~M!-~DttGMTMrY-9s8(e= z(5^UDf*=4JYAABhLs^l1Jbc<+k|EOE^_2NO% zH#K|!JuTr1x@*uo8e3sO0=BfnZlrAlT*-o6~8uLMvYqVr}iBjeTwN`{6E;R2Nd9F^i!jutmOpT#_wPGADC@E@- z={qN`kO)A?sOchrb@@^QFj(mY%6_I)=9gDJNFy$5g#g*oMVLBZJ08xhoIaq>I3P*^ zVFbugh7yM}hL(FJ8-P*f`=N0|$BS|@=n}Y6s#juwxKb}wFaq!XUMcdN5R3_Z9~%H0 zv3D$E+#%Fx7a_z{btlez1fFAqL+>o6@5Gfh0;F^~ZoFaQ)uk*s85FVhma5v7yM4@) zw3v=Nd8cR*Pon+d`or5D_X0b_hNn_saS){^w}D)#Okf85Y)9(8-5 z+`=IjSlpZH(7bk)D@*FuenCs}jMiu5um3pe_D4&m`hPHTzhV3Tr)o-%I`WK^XQcDr zT_tM+f;+9^Ov2B(6$;9LVDr;rrBTo{nNFAm(AahJX8k)4Z=m6$f7IeG%5ijm|LCTF zfbqzPk5Ybd$7>~qOL;9j5%uGdJ1g}SHJ8P%%Xs)p_I&$;QjopaUuY{A_8P0$)|TwX zgb^dMyC}WOz18pfSb1ZbG({SQBhp5v5S0Hn`SMixzH`n&ptR>Xo0al%DGcqt%XwvT zS0n#oZ@=qJi}Q zUmT)pD%gY*F~&?`M=9zrA)t-IWNM`=mgCLpIw9bCzi_DwOxeV+rZ4V3N;$X1;|{1> zVz3?e(<|jl`$pXbdRNu{HuVXO5dw4OW zsc-J-9&%a^mT-{>t>s}OqdA#b?X81}NikK9%4K9w;! z&3d{Msyw=7r5!rxGNiD@C&VWm{)Qe3QzsZ0ta+30;@IX7GHd>xkbsZV`z%2760|>XKV!S{Tpz-J z!2C%WAc?Z!2F`N-WfzB|xu|4>oyp6jk0^9fpw399WkQie3}#>)qnQhas~TZP@Q=(q zE*y;X>X2Q~tM+ot7;YkZiUA@cYy%~1mylgX3D|f<5U^7eHIKLp9SW%91Oa!$c4+W8 zH~w-;XA~4rMuwPP#|XwPgjyIAb{)k-!d9ryEKAtCGkO}1MrX6`skLqGT31`AV(M`Z zKI);Ir=#iUZ6;6O+wUFez@`(c#D8U6DAH-(13uz;I(1=GG+~;{A4_2#__bxopUQq+j<|Jb; zc~E=BZ`LU1be3ZP(QJND@VqPbjB-wEzs|XrY2tZAXWsNe__`3!DRo}_!$R7=KYVeg z^qe^Xol$zq1Wyz_-#tD5{}2S#CjxVd((DyN)R3yx6sk>?EKANnbzsBHW@@cidm}r= zMenm7upY9WXT8v}(otqX`*q$XwEULuZ!cp$)(jxtJ|%D)ap9ISk|3lTt|Cv1bTln; zp7K#K&C_X-OaN!e(}Iuo+cdasM(OMMP+!xDt$dM}iT^DdfcUTOygH zV-U_hE$owW-YWD$MYlF@wm;jSor|wvV>GBY=ksQLfUr$VBnQNG7g~>anYwOdSX5Y; zXipm;(mDdLuYRgpBC02}IohZJTea%}99eI*-e!HsvOvOb;uw<}N_SdYf%pt4OfIFk zh`eY)!Boa?H`RCQp}buyEvAZ`Em!_jmyO;;e^Mq?UpI5GI5nTx4qjpkz@>W28jB;hQ@zCJ2JHtiXs7 z(!w>4AYAWelncS7Ew~wmDZ=T_=*|24r~9X$vEEWDz2z;X(p%u?{6Q7tK>mWq5u#jC zOflW0q~gepg^c{N5W`iuO@jp0u$;HMQ4&x)zY6eOZjdlSFo1L_go-}Jln^BdLfl(; zWHz(FsBvxf6MJiuCUrrM{-{S{*||M=kSMI4`@TB#)?*9m*iDe( zmfF)NWDgSTZ_lXr(?f{;peDA3i6+)*2}V;myxd3;wH$tPC0dq4@NlT^c+5vds8rHN z=1ts$D^+An0xg>&ls%BfCzvC)*pyDb_kY`CiSS!-+zNQYyj9;B_@3tv9Dj8mR%6$- zGQuOsz_d&gV?N-MCo%ZPV!I}s$+g#Bd+o%LRoW^NZm4;cb)$8Mmv&cdqHRqY zDhEu@0%8lgv;-jV5|Zbeb%z4%$qE#(KV`6V0#!|UxKm9{B^kTx?YQtt!-;d#O%CDa zkw>N%tyK-#vIhE`g`qRf%antb9U-;%c6nD)YS1rIh+Hde1^2w%ZL2jSgLZwMK~xiejfqXSC{Is=)EvZ?^8gC7oc^ z{eX~AAs+gvN8jG)e^%+XUtdQE09L7V?<~?3suY@xr?veE6cE3|SjQ)zAlk1a4dBTY~IhUb|s66mJmWpiwy+1 zzt(6V*S~c-LG!(K;!cNpr(EG`%tnqKQv^S&6f8~}WvJ%1b=rD9bTF|HJENVq`B-d+ z(s0@om>@<$s8x`Nbu63iim_dR%$4qiv(1&Sk- zTDJ%0wr#pTxUMZKAhEmeHk6Oe#Ih*|wnHLfON2dk95K*pEh)FOWFcxrL=`a{h#c>H*V9q&bq^T(0Z|T z;3W(Y$VCNaGLBMib9S-}pXw|}D|aDhA|enx_T+Rj9ZN1XXE>Tp#@(C?k~O!@qb3YT zMS98+M!qk0ib+U=>2bXiNH*i*w8$t4VsmuhG#L%XM%;`QMnBC(3n(SWxxH@SlK-Qt zd$V-Lg8wu^8h;hI5Ni%UPAG-JM;!nL@QVt$f5HHqkGe?xBEYwPo?(p7QX$wa1QQ8g zx|s<<&l2-<5)=BLZy|)rw@XUMEkfM=2}vl~qmoFvM<|hSRmSX}R0!^;LelkDF@)#^ zBde4Wb@-Du%cvCeen9F_5lXAlT%ZWCSN$kNW`n_uX!l-V8wQ20CbQQ^gwl5l{Pasiynw<`5*!R2KkE=w*fY+2`i^|hE_)I-@8Ok%V?@BR`j1SZqg zB;_3dc$weyyYL%n`pW`9d{c;T3IOrTpLtRUAwE(9Ap~3&LO|&wLJ09Bd{OItDkZ&0 z$VDn8-9Ox?@&}R~`2$J!Ew~iIg@OfGLYTS{g_{k4`+E8c z;zdd|5i0)~vkY;A0Jl;@>9i+(ADE;hw7I_#nP4+9j>xRci|M6DI;y0ZlfKT4t_`T`-A=# z@FI#$G8zsh&IluV-6T){L12GFAy7-J0uy~H^LmXfeUtt?6<0$igVrNu$3U~$YtQDF zC~+AAY6yjQ8lbD|a0oq=_-y`!<9gc zq)4vvN~LzmGB?#P(Mj-F14Oc*Fe!K)qix*lJEmO3w z)@PPAjVhoaRy3>9uVj%X$_O%M^T9;`(V@XeT4N7oRXN6CqU`&L3aj+38!r1kAPlmq zobysigoy)Lvu|t3IoQ3vw^tW@$x&4a7~LrZ{jJsa%bXi$g%%`)DAl=+UK~*>cNdoc z4F@-#hrJ}*Uy!|e4)R>+TukUyfcXEc(J)PR zf!9|hnQqWS#hnz7rwO1i5;I;eO^#2fD+;KnY7bI1AkoT{ZcUHm^ndnJ)T+rF{US=A z909EMA3ks^0CIzC1Oishl$4}NT}dcWwR*j#C?O3QamIKg8}onVfaKMI2Y_7GBvPuZ z1jhhqyq5s+h*I)eL*`$TN)eAhvKLc8#9TYvOOQ|dK~d{o0P zYFC>BfTPx!RH7C#g4LPOd0a{LI=(DYo$&zx{4;%_BwLpb065_pg$WC5Z>2OrC|O!i z2(xt^o?!^91+B}ErZm*+KaJuV+PIN-Fb}Ih4Q6(jKgqC`_wO}8#@VZD zppL(B2PVE<$UYUa6s0eF{t~QmkKa%(mY(NzuB#tYICETXgXmPial(}(3X!ww54hk} zST4|@R0v8Nsle+DVv5MtetZESEn(E2$!efI33cP^y=T*63*y^#Gq5@4OlNtsnZsl6 zHeMB+i`8-F0^B&m9;kBMKnAb0Oc(KvmJLY=26rti&OU34@>_I87KZL-1x3RWXLw5I1G8Vool$ zTFH9P^kzHO-ElQ&F#*p9d0JQ^EQ;L{?L)aK-=3Ltz60L{QjUV8aPPyL?_%uVa@wM% z%yX*yc#l!7ZCf^}5Y!y^Z$y;}L7URH_2c_WOzk112_-=un1;VsF#mfG5h(yv&R9e1 z24lGb5D;!6POt={y`&?zMwZnH8%lKDQXCZoyuA%d<`oLcev+YTZ(uTo53l7#x+g&P zYK>rPd2K;?!uLxVHgJne#t47@n!ljDE7f-Vhx47m=nMA(ZcC*{+*nRG_Lh{4|Lt94Pv>oD2@m!JAVSdm%G}I}8Ba<$gWJ}6$n{U|v&*~fk;3_+&h+Wot@PnE zedTU%qy2ecz&M~qtw-<;;ks@v7@UXe3XJ=+Qsbq6W(wDJ$7>wk1b5$aE7am|vsjqv zCr@+F0b>#SdAw-;yXUnkv6wtW=V@EJ)=gHvqm8Bxyz>T>WyK)7Hxztpna@wnAdIFxd6&l^fS=5bPK__Zd1kKKxj zF&|r?bh$!Yg!UfVTwrluF^b9}j%vi_Jm1>dzI0}2qVunF?Cm#{^E)bz!+3dxXs+5M zFS6SYAd3F(#~E9G&>0Pm-X7fjQy~&aNPSP$^Q5%-P_@6;dV%$F>mZ*OnlNd?Fax=O zYYw1u$g9Y7%>-pVoX5~x1(r)xoHHu2u6iw1;D}Yf8VKhK!EOz2*6GxgVEM(|G4LMW z_c6|V*VLcgjtySyIM5^oMhIlhz#vp@TdD?r{pvPvW~6Mi+l|TV4s;yyM4-v!HeSxM zMx%@|G3NSr!#nq;)KGG+#BH={AM9<$_}nwb_nk2HxC+DaQi=pe3AEB0V2z;EF>1?} zYQ<+vX+}Tw_Fh|0@qH8OzD0E2GKV?8lgQt97?!b;cu* zFEx%09lslf;l#E>RB%8o z?m}mR3R)_)$w-fjb?7h0@#=6R7QSQ0T_xHWYxt zB0fw^!{KG7R>UJ_?@-A{pcU=#%5rNZHV)|j^K7PHJ;LakC$>jKAj|qI(-o#%SsuH? zFZor&d2l8I$HW29S+Nc_u3q4J{VanvbsPCg&{fU&nxYH>kn*pwq@lv6xll--n?o|Z zcfg?=^>0qYz{>jis^wIq%`kjS8Q#WW<@hX#Je^IqU0LUu&A+U4(gRVHeqclgmR< z%8QyX?Icm$Q6tTHgzO4lJ!3nGuc@AP%o0Xbg|DD%iQa{(pTRI2S+yIL_gLEr{1J-nhw&}Ir*RxT+a z;_#+ZytGnx=f81cJ3)*UvO^p=HsM52VL`%O1Qle+nF0{ac#e^>?{xwRXdAhich$J; zgPmI4#?&}K+|lSdu)^P^>W$Ix1QcmK2IeP-H|so<_zYr%1I1`Bl!!_<2nvg-ErRSXV-BQFBL5;Yd5^+d*00+=;Jq7b&_tJp2ZGYC#yym%@ArK{8P z=ciSzj!tciPM;oaoO;HBQmK9VInO!WradC3ul8v>=m2yATN=l$B2;x9BNxHWhHn=Z zJJqV61LS_S+S&h$@Lc_Gx8)r72I~SA$(G4zIO4-9)dGMP*nFKQadOxeA3?6N_sDQ} z$blh-O*wpc&)D-jZh~=>a~|GR8S*#wtP@7fR+HLksS(E0JH?Wfw)%e1Z>Qpb#SCGiJGVNjF{a7k-V)|P!OXdcx|WcRgMwU~wd2g@@q36%jAX}9DS9X5EIR0wrJ9Z=}1J?ycd2BmB3XE6yRuaUvK3L8Y*thh zsmf*|X|zS4x>af488VsNK}lvy>d&uAbulCKj;~|-rWA$>6C$VQb6V&GRHR-3b7HA(RLfTx5~Y}(5Jo)KbO9x)J6)vtqv|8y zoSsbK>ussM_H%^m`hOEBAssF`2X-5&T?56j4XV|p12ve7Vkqf?%36-pUUM8AbvHJK zk6m|dH82kS*}PgK8;{Qfa(Vi;AZ5rLaIQkMw1{nrZN~t*8M>&bgLqy$`Z4Q`)=yf8 z{VCqC*+L@Isl-N-+?%3YnYjJDRX&w+33i=&gxhbR6sANOo!o}WRGho6bO(*{cpKD6 zqR$C=1CMsOj^DA=C?W@dExYiM$n!-15g&cXUn*9czP2G*iOW0ro|Hyl%%n_}?I^}M z0_NVFGv(MSm6BmxBB7Np zw>xbbn^1oO&?s%&+Fz@hszmFLdLrf6PwxT_gKU3)|A^_TK5F6FaR%jbXT9Q)QaLyE zbJ^nXYdaCkv1(RsZCcmFRbr!C@M-UGX*8v$%_+|YROQJYOjD4jV3ecjVcS)7wS61L zjI%g(dk>VSlj&sq^h0*(>TbEd)Xd!5?y(qL2y^-f-4D9XJ5L;I^{&YyO8+)n^6hj% z=?_Wav>U$9{^>RCZ~0oorA9Qo#)ddqxaJ>Wp)(Oeh)HJwHYxx5#eIGYBx$=EXNAMH z!Dk#H|5G_%SPCNJz4MoRc)p1gyjmS@3pt9CY9_>X2!4nAt2 zTAhLq5D}@K9!@)W=v$%;8tjhzj=V$m0C==!I$ixgRlrleCyaBJfa`HO|2yRxwXF0d z3U1B_gc(Ebh_R827(-@*u?>UR{AcIyz|i9i@hW3gqKG{8(&4L)5uU$Kc%K;*mYaS{!{kqPbVt;B?UkUWs-FN~K%l>!Q3Sj(n0JV?zMp{`>ujcN-~m1t1y|NE zDb^TU7CcJjEmQC}*b>Mk2%NaO405o{q3bSUq0Ja~4w{_hte_8Q^gHS?4z=3N^JHPU zNstjN!CM16{Z+DUB?k`WQj|UxR?5-2F#Kq_*=$DcKnXejFIQ=Ozm&HEh!FJ~03io% z+-x>W9}UBEVWk{>tRw^{&S0x^=D&26l=o}>&BA-AY+1j^?~3&I@J5#gm^uMOAmsa3 z?dN@eSAnPvS|?P5Ct-MYXG0Wsi@{sc{-FKy=)rK*=k2&itftgXxp+KlW89CE(KML8 z>EYM;d3(^#`=iPDH|U*G*%8Ldsx;cV@jQRNBP$5NHCons^fdDzK%vcPmQj%6%t7VeX7rymYOrvp zD1L>p%LIzMztO~3YUjLRGu^;)r8X-8IyKayDwQp3GXnp``(rzR%6Vg}bQEWSC7k@I~mCC|WwF(Q$?}tVQ{m@LA z*QU-zYsb3HddSLw5JW8)!%F|uG(F^WvZ2K%)b2nPYnAZ^u{^oJ{>p!`Ti>zcy0d1# zRjHV?3s4?_eQS1@A8|w}%9+7=JRZvr=dJRZd?ko()smG!Xi_(Ox3;7SFIHOo?%C6X zyszSj_f`Cg?=>C&=)KCqcrjwxNCJ_JJv$2Pu9z`-} zS6Z(1Rc68)%i5oO#5V%O|J1JEGFp}&XV5O5fd%D1=4Wiib&@v1ny&Mo)4wud-&Z%5 z_fmuagO^zQmUY&8nf1OqZX0H&5^Xa%Mmg#b|vUVTve#*6fUQ{%f{dM5zUMfKcf9;i7bx zjgJ!71^W!RyFT#wg@0K3`o6h;_%(zvQY8$4e1b5JV~i2;5dbc!=Pf>IgkN^=>w`m2 zQ?$B@sO?6!CZ{C~E8`r=b(S@|>t0vXB)D!8y7w}rz4;MUAmjFqKZS8bFvebjk>5nW z*a#;c#+XD)6aL2Vl&C)*d!KdDdXe=?>y6fCO;01M(2IfK0ep?rZ$oiV#riVF%_BUV zl$l7Rt61 zowa%7XLIu)7z@8k7rau98=PUyTQ&QHpVoNQF)i0ld8_LDU2MmZTgVWCU9;<2yRF^U z*1;qnh95V#Ka=V*&F{hid?RXfN~bId0-#DSUsQD)G9oFDJ~zvuJF`%KaK=SoZW;{G zbbpAKRU6;-`@qw;Y;8K=?HX)vZSL)DZW?#yU}tM{FOh5Ioh_I@s@M8gb0y3qonAte z2}X|GJegul|3aLI$YoMNp7+mQf_7Gx^)R#|tmW}V!g_gS2PI!7h`i+pC0gnYdXi&y zR+hC+(EQ55FPSGOBd4Dz`>0GhSlBQ)nA($zk?No$S(x(%lap(6YV6SbxLsW~Bi;b~ z&3Ccj@-w|^6>S|mJ6-S9o=XT-jT>nbtU3P!89c1lB7390_uyMMHWZ=cxwYQ<^z5-M zRITwjr~F2m zjKVXu>^)+l7s2=*5ND0!2#0!|M*VTu5_hHOYxAX_}j_HIXVBg(^N~59hMRR->**LHy@y~zcQYf+V<=XnrZ6W?R&@|Y}l$P zlb9z+V16*mS+vw{86|1bmccE1)WGMZ^%yzW8K}ZJGlk->-8=V}#BnE-Ss;iUD5&%F z(0IP%5X|dA%VtdbAufudENK(oMF2?Cg*BxP1t$8s4Js$cz!7jR9H-^RsNq*Ka9OF; zYNn}ZLOAikuOGAS;x`C_Y2HjIVLG_>f|cd#PoF=3`ugRSsx2r8 z8GY2k#+tkaBMV3q@TPZ-aVrC4!Q0;uSFyeQhoFZO^GPyd$Os{3i=Hy^FRTk7R0~q`8;x?^@#Ne{0d-+PqfAz{p4hEj7)K2sBSn=Ub1W%S!iwTnqk%JACmsZ6*wfwu44o^q8O8MnP3bw#(+r8M&!p4S!d99 zFF!jXNBh|&YY5D#59#8M{2@YDhX75|nb+{x(?INN@gpTbpZ5djD32C5zTP`7c8I#oS z2-4J!SLMTDUgfYo#&f+!qchCAW1BHVr{_;1C;>N#{O190O=OOiRo;RUC7hxJfn&0! zngC7vp0<%@5JEFue5LBXGSOv3--tdv%fbnuhjKFB~=87vlzg&;O? z$P2w{M3WL5yJ~Sh1x1S!(N(ARDMzDjY;meIKO*B~c^~wbW*`%J({#IIA`lL$??G`- zLKv*%Cw&x}aeR!*0#zi~*KTlv!E(vm*svUyNGuQQhL27k86541`=4AgmzFMT|NZ>H zWIitqP&?DahIM26x7e#gF7#_8ZTe1d?FVI+q24>L^Rgo&N%QjnH(c-0UhKc@%AWl1 z$03{_T;8etjeg9gXO*l4Z0vd*r7g3@bB9d}Z6#@|7-~b>)6rzSQM6o2&HP+1HJRoX zg=bUI3K5q6R+?2%_LJMP-@$amt*%h z4eKk*EA@9fuA9s+CawYJ-OvXJIO5h3s)wM@L7cwh^qr?lBFU8NUgddL;}MVR&Ohx& ziR*%KlgRxvER^qL%tUG;ebz+Fr&h!PT>}#Px-yDeGOrtes)g}BYX#U1X!ZeY`Th%g zZc%R@7&Cu}Dq9(jNMm|)8b`sUdGnrve$SW}_I#rL1|0bQ??=Xgg9A8DhWQ<{3kTMQ z^|19}D=+YSE8=VKrGj1Y@f|;_V1n@P748TT+v9sZR`5zYODL6_^U;s;{ED!#D|-2X zNIqHeJ9zXFcrpgW)UHp^te$-UIS4}}5zHDC|I#_FBjnj6l>;#I&@QCYeov;Mnt89X z-?!qD@;tM!G#V`}7|&A*Bc~IkM`^TR5LR~|uaxX8>b`4z4IqZE-x&%3-M2l@w(U{{ zF8ZZ+?v_DVRT_6fW~ z(NY<7=2`yZ^PJ@-kB64Vmw&SSh!MIRkr3Y111tA_3nc2 zXU(OhwbkXNX6E}>4(Ct7U1pxHOWzh)o9pWhhOqi{R`2=do^==UYSp-|saEra?(CU> zR3RN1qZu4!L8*5!3);$I`#J`Z^!-$m9Y$FpxK(fR=`4;Ww3@lAuaL9#@!>yJcRDE! z8K6!2-Ev$ZY!~P#XsWBzZf>k$bbS5)CwZIyB1Y@TZ#Oq0K!h)EZgpF=+LR%LHz$oK zYK}KCLTp;AwYpoID~uqB#*Ma*))AJ-5u#Qb3~DtY93-!Xv{Xs~(o!jfV+u8N7(0d~ zaa-|6Ry&xfC`ytjQVe%jenK~p;;J1d%s6ofN2qsdu|@pG21bl@;n+^U?_rKW)`ChU zs7XK^d;R{-G3XM8@J7QYt;JJ41ahI4>kkl4!z$*(2Wo5(t zzO6&em=3k=|7fw61hy(KEUaL#uZ2_9 zm!-X2#zk2L%sUFmZYxl`jY@SLO(wT-850F*E-`OGi%UBS=HT`!hbBh6^)BA(xgpF)p(z(1Ta0u#OPHa@C@gxR?w-~@hi zD69}ToJ;-O?{9s|TGSN(sLikZBP`^={f)|!DWSP;jjiK(OA8BZ0RI%K`2ejg=$InF z!fAr7DnMizqbE?<1TOxH8!<`VczN(F%46ire?FzmxaCp_?#ROkEw9X0I@S5V8LA`W zMUp%5`E^IF3%9<5`3Z^69&hY4pTE=CX*{&k*lECVf@0>%KX%KdQc9WmqqLnbvDwNp zO8qZCp#H6r>>wgejC;yiclmbYxv}wX>xSL%>TcK#PjyqkY*6XeedLYB39}6`}Sn4Ed6X58o=~+Cu!-nbdQ%J;45mRnn#(OOEqhy~z$Y*t!T?6IQsM`t|4ck$ z=t?H`c_uS&obESWNONt<#*F3u>|70k8!yOQxAbn#ud7IOUdQ>nORkH!D(Pnib4EgP zgAbIXjT4u9ac-_fj}|!=;B~|l8JaYWLq-m-1#Z}|3$Vf{woQ*G(@~2wD^ap%X3!{W z!@=D|)?UyH=d`k@obzHvun}5Zj zYOal?W}Ep)heGm#Mcl!7%nb>$!|q8(jKxyRIx z22-bf2$@@O2;LnShv?v!`6$Hr4i64896Gxti-|t_8T<#y5TumP&ze2!X6t#@CUU+$ zK;x@H9Mvos4TfoV+#d*?OmhGBZM|eQYJ&53O>1Jby%A?zN<3^*gS=d9bOO-L<8j7! z&G=(!4G@?I{=oRY*+Fld?b^PN@HZHr|1Q~62l<0O(#m^1rHqW4O+c$_*G94A4VZCRSW z#z}D?ZyK@!rpJFW2@vrWkx>}TNJp7d^tAm9MP68(2QaSnBO@iz#6Fj9xz6DhK-}0!}DIdbuT^)D&?$noNgzJ^=Jo>Ss24#E5LK zVj}_&+k(uG5m;ODIAlC$hIc)`L8DkHC6kECT4-#)mK3QD^gd~vm@Jq|QuA#Smc_^G zgK(aY#06shq3|dx`s_;xL~$cenvFDX#1Wu}MVF7R$VKxCkM>bAXmC40a2P8ZH5w6> zNjb9p(8OTtvxAu6O6q&8UyC6&p>GH2`(psQu8gC_Dhc=Z^+*Y|BT8bWy13|y)o}jL zEhlR4aTn|zz-EHFYW#s|ejB|GzEyfE?^#x)(}~?V02G`;IA0LO5KSEc#|iUMTMCA~ zl|pwnYRs-?#M5c>SzR=OGs~0-!%_WC2 zO115rI^{$e!|SS@rDjL#_?Fe$-Oi1FcgCEr!Y?O$eVr%oTUlMHo^=VOj$>3b!BzsebaGvtXe&mjV)Yj6-7aRf^iGr z%hTem%qWG1G)bEO(MS?HL`s?YY%_$%!ugYMsI@!)bR!4>g0KO1xmq9Dm&AalY&~ba z(t4-$5nq=hdnAbn65^^448XM;R73vcidRfVR2E1;+mP`>Tp>>4Mpx2UDJI)U`(fC< zhT;Z^Gw<_tq9l!KSP2>CrKV7?|7{jfEeV?7WlTT;j-*{tBv4W~fgnV3;$vnAP&>8( z`E?h-jkTZ{lr1^Km@^i5DOYH7dgm`tA-QAwuC4~9IP|JD2t6l^69ki$mGb-sV8=K8 zHLWu>p1Is&xLjdcYCtsvisDETi4`M(GQ<%QTp?IYv4d^KiQ`*!rBzT&6>xnde{6Cl z1Pi=I`|Pn}Z2sGXJGM$)9Yifs2|WiT1k+l5?Li)Y{s#te7nLO-(p+R&);=6TW(&7| zQ@dKNbKLfb@=qV+9?Y|ibZYnG@G)Anj&hvIiZ>!rp}m%tSMpBg=>J(Q~)MEqy82abaI;e+4z{XPNjrhH+cxqtWUTW$jwwD21tCT3E<$@__Vx zfAL>hRt&q>Mi#u=Th776JDw!Tg4b&&Bo0yd@DPuOt9kiCYj_6XOO#T4{B8p2>PCLm zTiHBxjqpyUZ@v*@j4u*G*!^b+-K~f$xlesMvxizNYRPAQ)8@%d+bUaUC|tU-71NyI zH^WYcX$1506|q9W-D5;7&Ci9l@Oav)`|;Z&Iz_}5i0^A^vMG>Svkk0~g;)W$p;e;i z01`{|I;C{%S}in6Vk#sdoZHpFUpBTu2*V6vO2oU=6nZ!PplWkYh!hk{>4vqM{q@6` zp3_9NnhlMDjzBH2g`zvpBZ#7Ax(TsqKU?^g1-LDV@SP5U6=k0IMUB|2_c1p@Vxi33-&I%0Om!p zhjG>?FS!7w{?jTf*NUxiwz;cVIL2t$gz!h}%IPT1C)1Rsh!meo?hqdcfx3Uu|QC73|iSs0(~u1$RR~rXq;0kH)xLa2X9`*+$|@74lXf;?u6ij|AJ<;aq~iu^q4( z+h94ZH>u-6b0+}^a-=^$g8hcqRlLLd8Pj9o2qksjSD=Il=zjArhph4({JMpHICK^tQu>_GYy$i``nvs~YBQTrC^8bHw z_(2z4HbMfgR2H>k^V5q-aGDeBtdGa*4kr9`kX*Tpx~IAA=*3EfgODcw$!;M{dQzJ4 z>S|M9Y`^AR`Zcy~vnXP=ZNEnE^^(`v7>nlW>bT*?JRwRLO~L<1U6s+f!WN40V5kXs z0C_mW6NQq?UV7RhCAd_N*cwPe39`$vbR&kqip$SMMCj*hzX{a^Zri%Bkrd{1ZGjW) zl&aN|g9%@-J=eM8v=I{7g|=tP_ZAEoM-dlSa&1k9f*l{Ds@Cft>W2G<;tRC<+5sG#_R~d)JRaW%;mfP z)0YB436hlTU)PmNyIra124>8rR3=&#Z-YyXw$e*i92V6?Eb{=d5VHh+XbhM__ z{lICeb^!aiId&#Im7xAczKi$vITKZ&+IH^C!YB%}`;68Usv@!qMLm^lRib8@NJHV? zMI-)_xGi9W!|_H+Y4JA{Nm?_An)AJuWed6?qPn|w-`UHH%n0?ZbS6F0Iugy-MY%k!4>XD<9D6PeaM3JS~~6aCNUtJ&re_y z*^%|25{Hbq`xzV4$>^w~%jy8z^u~*|SMV;@FKUELzI1MT8yRkl{qZ5BPWFvaADY<_ zCp+jiedA(%Z(c*~qHPQ}XnXtIQf?5^uj#0>JMXA}8~R0(eLmGBSBOe31XYxj&Qr!V z7#W%|W^xwHK355EvUQrcpzFhC>-@cm1#lGJ6jtWHbM2>6z{t5iRdV-@VM{z`)f|%{ z)U*c*R2QJ;y4ghru1a(XR--o1`gjG!!UiM$%sPsAG|odHdKz;-d`^L3D34!~Vf0X7 zn9s?wz5Ttt{XNA6nzO^%>|jy-U^d&Io8Z;u9NnXk%~A!UlFep@(k#z1`*fhpv(UEs z)(PuY>p^baqWhc#+U92cRGm9KwG{!Ng2|dXYM1O0mg;sX6R(cpYH3 zD^i+|w6#ueIDS^U{HE5Wxv)=;1Ur-zqLJ8O*$< zT=4qO-(*L>wti!RA6>Ko$>Hnc#EoqpxT~x4*N5_@ffSCz#1x@BDy5S0H|UhS7mfvt zRXKm35{|=U;4p6X&};qQL;2imZ%peOh2#0|4Oc5Aco1t5C%ctey9_V3=aN zQZ|Q!{x;lTI)aK3kfmM#H#xG9@`hkZw#*S)PWn+QWG}UJZYi_nb~fQGb(2Q`Hdhja zk~af<7~o6I9M-F&SRb(dQWi%a z6E9<`S=6;CE+2l%8ODz!^yPwCqrHW?HH0WlM&hzGhttV;=;)fT$Ymn&i^>6;VCQKt z-jC@9EB!6>lZ1*}r9b#@puzuE03?4rz*zG<=URj7LP&+6;R$L#^^1r(w&Nl2T`zFx zLHxiP!VJQWlmr0~>HuilL&$fHXB$k7mVzRfKYi~oG$98x?E(TL=7{6Ie`nVq>e{*o zB1Cq(SoC@V6HS7=*{?8GL5RGDeNDOmm|!lFR7yb9h)!J;FaQR$cx;?X0f4D%Fc#^= zzFQym;}_sb8o?}i*GV^vNJk=#JH_eN=QOT7E+rzWi$RAHXT1sC@zt$3o}>k9eL9(9 zyeqFq+SmfH+u;vv+vervM(MSxTkYrQ)2CWK=OiHZT}(*bx^K<%=7+vXjPd=1i8L8v z@7@j2UKb6wObM}1eFP&P@iAm~K&K2%fg>?96fC^o5a8oBvzkfbbq-3_*vCIOO$Qctu@~WLZ-^M$Gb}@zcvr0P=i3_ou^h%1tQ;602Q|!=0FD zE-%jOo5qSqo7V$Dw2#ob7r#4k*AePdMBw_(h^sh7NrDDU6RZiCl=RC$AS6_T#HWlQ zk~XK0B*GX;OL4V~7_+_DOh`8#DIUeDlo^N7wNuxw8MAinsWr+Rla+K7aV1E~g2mXg z8H-C`^|eyG_ApYM6G`#Sz!8>_;dxsnOo@-NPbhYMns@dCII^g93MS?l3(~w8k5ah? z#Ce{GeR(b13V&plIx@zS+QHhA5k0XWA8!;J1y8sM`>k_cZQ9~*jKpBo#oPz@Ua)fQ zrP=e|epj&csyH3tw8Gv2tbQ{Cy%J6Y3@A;vSfF-nnHiv)K+_EDhcZ}rKKcg@pZS}h zC|z)HINSf+Z+CEC&U#5o^E{J2*lm?$R0*eI3QSFIzw?x3&z}FOAQ?9aCRo={-QTz* zoSt8rOLUn*dpi~CC8b-nGU#ZqO1$BPO0zC6h8-uNm8GB^CD}<(F&ApXaeoXiqzkBF zr0>iXnHbPEIk9M+vRaog!YPs3V6~_yx_YPq6&AGP76-H#h10Eb8g~so7w>N&9*+lD z)dWb^ePaUS5`u-K8d0e{uskJcSV-wdAOhe|%3ib$DHU6S2ZyL<^`gs*ATlQsc+WlDh3>TAoei?+*>SA$hjGfcA)HJDAfG5G zH*WFWMXQw_c~lb|SgUkiSSlxv%8T2P%KvxYW41W)PWUT~xnMKOw_KnzhLCW*oux%| zXRTU`y-u3I+_Qd{gRg04(e*FNN0V|XyhJT)=Su-5`q7Ea_vWvNBAWkod@O67c-Oys zMj~Ll6x=ONXH0O6(c;n$+{w9!y&!L|&aWKS%mdnghPRDAtvPxOR$Poej$~0$Z)9IA zg(L+uW6PXSJ%&dNcr~Ft9zyTY=K-T`2P*KfSfVhE z%&mEEeRRWM5EEew$e9V|C_di+@LZ%8b-Ux0w#CB%vDZtKgX)~Qp!I-qYm9)23Ht-k zm<-G)4o0otgd_m~y#IOzUkXgGa_8Dw13|sFem<{jgx#}iYjxb{_0P@UT)uBGXrg9O z+zczJO{jk+?=(?!IJ!UbSKwR3?u+vB@&NY+gPvcrJx{Fmdp$f@TdT|`&R}C>fcu?J z$w{5SXKB0L$AigaB;8dDetJu0ty$T%&WJZqCzI$+9|e>%b(jv&Cn2Obc0a3o8HYc%IIe+E3pevYzs?;FA(Wg83&C)VQji{-!BigZU8M$$a zR@eZ@!|E2ndJ#|vIcVD(U1I={pr<-;p_FrioViza$@3xR2>wr)zmk5rQa@gcBM9n# z;P?UB3M$)>m?N=jYNjl>xIDjYf z704fueIyWI28;2qb$H0h0JmUcI+-3#R#qxyh?C`&*=%JwiJ@FsS($W{)OPN3B>w?( zy;>jYjb3kZ?8NTMDnhF(yC;rKdc8&+;1O+0kq-x}E`ZxzTdm4ZOMIHuNaIwa!HozL zt8v)=q{hLkz_7Pon=?07PP$xNfu17>sz*mhM^|iZIO_pN8tH^soqN8ZJ>c#4B23P% z%x1vG0Q$oJOTBQ++30f?aYdjiUWD_;vS`Xcm@{@amm2+6EityjNZKLm7Qg{6M=G6} ziZ4&sy<-ar5JvspxpTdK6aq+L&tG|av0p!ZOh|8*s%L~dDZxP%Qq8AA4{~VAkk;y5 zx*~;*vD4|a+X%JWosNUCEhu%#`u_ucDVns{I$E$~E?4-T9+ec!JbF`Ga5fYQ)9asL zydBVx!jQE=m*z&;=6&kwhk=iS*ATj{ETi3F0+K4#L5N&Ek;2h6?Znm4}jnrmDJ-hcdATkrwVgA(W+wek4s z*;Q9>Y}D?cARj~?MS5G{acatCjLT(1xd;6O&8~H|wP%rS6q(q#fZ`^=Sm+2Ol0B4W z>%&5 z8ev9qXB(yM4?nd=eZKW~T&0w9x4ErSEiCy@beL~{Ms}W|6b3R-igU^?^z#e)R>rrW zurDUO!760*B{nz}T~z3XoWntKJX~P>ofN{#xiKi<&(m~%SkAIisRa9_EGw^T9YhF} z<4=n{5MbLRU^`~cmx>dCZH!TB-M#-EGSBya*DsZP@0TXV&aZgz>2j8p=Z9&i(Y+kl zPt)s^@&JGm+cu63iLo&tO22;zXoM&;TCaJ9`D-Eb{8GvPUElY9>3d;*MTRHA57}>J zG@1aj(NQ=S7^4$Gqg&)uKF$p-cp)hl4X2sfy_1$ zwXy?t$p)0{B?xR=xKXVpVtfASukJ<0f&OjlJ#d`Bvu&cy*rJwQ2868w)J5uZQv>D# z31gM$y}~rMfq+1M4r_LccHT!YU1Qo&<~qhz1ToD{bAB#xY-$70JKJ&9QwsQc?}i&M z{9oMf`IJ5fb0*={;>Gnf78!~K50U;5?H=~Ah$r#+uv70CmDXlz-gZ6kK4MjsJAz`3oV zF2q`V=;+{JPM;?L&W!c>K3qQECs_VCS(fcvO7qLsQ#2))FU@VxiB;AnLyZ4#5(YR{cGfJ5(o63-ds`;JrcpGmFo ztllv8^J$!*isJ(#KD)*WE9Y#9Ehfm$8VJI}u>U=4;mayp(~&2++j_`)J&aYVY4#9T z9Oh)FEFEnwNeN^2bp=MNI$+9nte$W@&)l9OC?7wc%`z*;m!vLNZ5q#iiu6*v4spx- z@>(t+tQ`hQ&B9vE@4j&VwIBGZHfZtFY354Epv00~Uer25Hyp^;x$-0z?to;kj&2#B zOfmH3T6+kJ(2t!M3&B&i9yY*~JK^%DaeL z%Q#wv<}TsZM&<`gvoOc;&tOM?I@ayYc5E~ZyBzNzXgczYwcLT!I@s(_$2}eW#EG|R z&q??Y_H$17YI?B|dk+B4oaA=(tMu;9^?>y<>&@2t;um3qKbfK$g9V;}4=hlGW9BY5 zUoS>8K_kf|L=iiGj26UiR0&)inAN-^%EocF--yBXm)hm+MAOVKd<6E0Gp}p@y91ECysJ-qe?nn}Z&1?LmiZAp2g0Y+Ld66CIo@Q2u5rk2_Kua!+x1% zSkAv67~b!)riUtD=Hl^okvH6}G`}Rc&=c3Xt<-~Qqn`K(MM^~o`ANNzK3JwExLm&_ z0V#BVMKd)CEX9U#3@1}Doj%R?uq1>D(0NtG9_KgP6C}D$p~b_AGGMkTp?;99({2x% z6wR{Of2_X8;}KQm$|O=r2os1hy^3ra+dWXclY|2NA`d6 z)LNQ9UF9K_#pJ$)=t6^eH=xA2Sh)HT=pOyT&(r#jM9MwealoGPJRRte{O` z8L8O=^}jf&y;TD6g$T*apmLvV84;<|C_2*-IxREKuN3(}<>oz$v_Bs72h$NR!mF*x z_hjR=;34cdGym(=hnTos*P^L$O7Hx6-*E_g(a)-vweDR=NGPrsZd>*z4Hr?=zW?Ep z!N=d;YANtRDX((=30Qo|KK$?l``(Gkt*g!;o``!IXUd;U;r~o>emSw+X8Yd6Q2DWcB`Y(=lpk0oa`g*D;EXT>l4tk2Y^)ItB={q0DjN}Giwwc; zT@-J~GMq7{yttj4JOq^=jZABVDGwSAxMzGn*#rA?V^DdeZuw3$JX9b)LDW*ei@^G( zgB1oF^kHYkCg&zfw}it%payjPz=$Wt10CfP%F3>&wKn&>LnKsW*|s2a)SmmxGk0f= zwH=yMR9ID5czI5aEfXd}aaE&LAQ(9bX+yb-K85NZ;2Zr1LkJl;fiy2JaVdE7l}zJUh))MmwBz!AGQpr1v&)hSCr?nJ$gD zM=I7(|F&*#UQ-cOjhh^cAL^C4K;><(-M})8K~0ZvxnkK5e*@j-AiEm{S4ls+Duga| zGPU79F-X9Xmz|SiFDY~^uw}bfuBxqFwbGq!Ti_@xvFGGu=Q&>U_Uc4pY@Hg}bDsWm$@OTFG2U!^TKH#gT->8tU2H7hgQs(TCbZ6db( z!oo6ZPHw@LRm;oDRLjbiuijjcn`1321YTdWOQMunLs?}@t*tVpLqP8};84ks8Z2;n zp}dl!DCisB=pA{vd3pW~QtTC)n*nF|-XjK#92%5q07>DM56VBdsfVS6weCdl1(sm`>-&$RSvn`DHEdH@o<`o%S#a<&yTYb zk&i=KMkwbqS#@u2lc4Sql9-UXPb|BoSUW7q^K~Lq`Sfk!g&H45%C>Kh(%=oK&EtaK#BPXw z1-7DxNoxy8Ww6XfX?exVG$zuu24zX`PY_b*al)2i^KQZ{TZYYMbJ#nE%N>2I)s-`5 zk&nKhaARl0Vau>NntSY3^Y{BA)}AXh^g!9>2PTYTgL3Rb)y2P*1-E4u%yoIg8nu=M znNHatAZKJYnn}>zfQ`0YWk*Bs7@~(=ROCwsl8C)z*9t!{UgfZ;UKUjI<{Vp>dbUc^ zJ_9bwrT11czB*g6b6N7M79np8U~3clD|s~;8sHY?b=Am0P|17LT9Jpv0YurLM% z)le`9g>EPufWldDw1Hz1ib7D7hT>sxHh?Pxt_dg!KuHu9d128AEG~t`-B8*FW!f zw87CAz|qriOb_hrgI&#VY$qJo4o$%E7r^dOIN>We(GMpz!^wU)IRPzk_?;I{vBN1N z(CUWPQ8={+PHTeGz_!xDIZL!Ocnd;}rBx!k;?f)&%@H0Jp`UKLB?O!kr@!al>7M z@Yhm^4#GV{aPJ`87lr#H@W3bx_QON1@V9Yzcqcrv6CRDia0DJ}g~vVc#AjuiGHj(WtAMv4ZI;wa)A zLtHILi5n^PA!R|NEQKtsL);_CG7IAIA(idO@Fo#*x(>$XYM5E`h8M zAT=3?&xh2uA{#G2HjN;4qsZn_WJ?_ghSk+WKnv*(adKXP6Ja(+K@ zp%=Mu0=X!LTs(?&v?7-@A(#4)%leSc1aieNa%BMN@*rIu$W>nCsw8rCFLF%_a?K3V zeF5@^cI4Vw1{)92_v_H^tB+jIgr~2kvlq(J6n*5 z1-WYs`AY=(>l_lDMD89$?g=CJhLM2?a$gT}zZ-d=9eE&$47MW=+K~r4kOx!9Lm_0y zjtuo74>uwY&mxaBAdie8kG3I?rjg-sonwBXh`;3FN63~SiF=UvG_q#^*)xm$V+?sAgp65`v2oPJ%zl{hP;tL z5-ub$fFx#-H(QW5Q^;FEApeOV|7}60Cy;mBkarWv zdjaIVA>@4*^8Nslj3FOPARpS14`awjqe!Y1`PhSe;zvHMLp~ix(mRoTW61xS@^u3FCWOomBj5EQ-=&c6W5^FdWNtO` zBgpu4UgVbyn-T|D7v8z-7taHw4ych zsIM3GCDB?RTHAxxPN5rv=*9_jQv+J(K{tbL@u6G7=s|s`zaKsL0(5H=8t6fPHG&@M zN4MG0Z4+p{9jzZj51U1|d(iD8Xu}}->j-+d9sNx+8Z1SFY4nI7dPEXEGL9bAi8f}S zjbU_$1KklpkDfq}=|^`a&|S^wt`vIgBzoLVw8@7y4WP$+(c_2F-PP#s1bRX%+T4zw z=tWN)L{IXfCk>(}_o6M0=x@{L??%v5I?&c8v^9pF+JT-HMNc0`&j9_s4?VLNZJ$BU zilb-uq33{}(~h1qg@)?UbG_)fGwAs)^!#4*0ylbL8+u_Hy{HGhIEHo%p_g=`m%7nQ z)97WB=;bc-iVpP3F|?~2y=oG@I*4B5N4xvbKZMb12hr>N=yh}G_0{P0DfEUgdSd_$ zThW_5=*>R#<{9*lgJ|zKdP@g-OB(%C9KCe_?Q^4loHC) zk!JL+PV_IWXtWu<+m7Ddi{4X@-ZO*V+lk(nK=1EHAF!hj#L>Yp`d}&g&;{td~i5(Wj@;SQ33Uf{rH9=bF*yBWQdM-IGH9 zF^;|vMaSyVu_XFp3jJpceW?!}_oM%Eqc69kuUOGnhR}%ybRvbm8bV*4ME^aF?wvzl zbD*zvqpw@hH>%M$X3)ea`eq0ERwMd0=-U(MR0w^?ioP?C{ugxGk512{@5a#gg6R7m z^!+I`8ACtlL_e58Ka8Ls?L2PStMlUI$gx-r%<%z^=otsj#g#Mm7e`!r@@7p7neQ<%Uw zA{fU!rl=oNG>a*2#1s!G%%U;O;x2o39}ANwGXp?HD-eyQ`3a0naB9XF}0nTjeg9=NzA5U zOkEdda|p8~h&iYebI=^dKROSgCTY|(g_?bVT694z>!6lV)GCTv4?#hr;1%@1TJ&H7 zwJn3%bwTaRpbkw?hbt&F13eT(4_!eIUqFu}P&kA-RzMw(p-yv9=Q!%}8|u~tb-xF7 zzkqs7Ks|m!Jrk(c2-G`_`Yc6#L#SUL)F0IU5E?iH4GN<{Su{9_hBiRM3ZvoqXhan> zvJM&r8XZGp_M)*9(6|g5{{)&)0Zjp!24?xJfhOglNxz{;0~9%h9_@o3y?`Dcf}Xes zJ(-W5I)r2raeNenG zdNY9DDvaJfhL#RM%fe`RK3e_-S}_K#OrcdNwE7fUTNtfPqV;9a`ZU^D25tNTZHl4I z&!8pB)&*_Lq3uz$6SQ**+BF01j-Wj;l<0yIKcRQx=)EfF{R-&)W9Wk`=)*ba zqn&7P8137O4usHwEc!TvKDiZr@)J5_bU1(xZ$w9ipd)E?G=+{`K*#5x6Iam596Egu zI#V2-%|~Z5D3wE>zKT9;fU2XfWD5PbRJ6YMBl{G`7!816?Cx%x>NyO zT8b{G&{d;rYti)#%3ML;UP0e=LEo1_KZMYaebCP}&@a!R-;(I}ztEp;(O)t2cNG0| z0p+To{{ra$D(L^cm@fE+->@d&0s&m83NF$D7p;Md#c=UBF0lZYJcUbb#d!&QV-S}P z;?g-BFb+iVO*QaMzu{XN;9HXT);{>Qw)plSzCDZYNZ|Zq_|6QzD~s>B72g}f_eJpi zx8nN`;j$52t_&`>6PI6#E6l(Z!?@BETse!Yti@GRxY`6Kj=_z>xba%tWG8N##m(m6<`LW?h+BeN1#s&+I9LV;58($E;0N2{ zHUZpr1a6na?FZoYIUG8MAN~S|58;kc-1!Rb3hw$F?zR_qkKrB(+$)Rw1aaRi?)L;9 zFai(kf(NGXpeOL)3?5ns4;z7pC-KMyc=QB3CXUB9z~fVR!Ua5eBaUS8<1_G6WAM}Y z`02g)nYH-YD1I)4pO4@vaXfV|o;C+h?}BI4!80r1S$TMNaXe=Tp4$S?%i{S9@Pa5_ zxEC+Z!;4qq=oP#qj$fF9Up$3hDuZ86<5#BOSBK!&qWJY5I3C4s6~=Fe@zN%EX$G&% z$E))2>MD3m3a?G$by2*2E8Y;n8!q5Y#qpK^-f{|W&ERcI@%At9j^cRdt9Vx$?^%iy zG5k&s{B9J#cL={f1%EgPfAkaHmyh>Xzz5R!<1{|l10Sk_4`uM-3_kh{J~js*&*GB_ zd}?dSARpBsA76rel0rVsLq6Sye0DGL#Wl#6S0G=dk#EY7Z#$81zd^pc5&8aFY2@HF$e}-wBNre?S0l%^Bge~--V9JRcO;F+Wa!wGK#hiqOJSTHbL9&L_6xxjyutw812hL`;MXg zAE1M)(ZTo8p;0tZh9ucX&*XeDLOSorwyXh^U&${qBHW)8K=-$ zMd<7*bWRgcPX@zF9WS**$*W4CM^$piB3OuCwZ+Wpq22z6RwKIQK2KL2Txo?L~IXGn38YjOUqEcGOQTwn4nCW%A;T z=MDYbIUUDgG(C~nZG(8n8jt7hvSVAqUEan58=jl-_oQRfyQ|MUUb4sjFPKA<-HC2; zb=os$dpmm~GiIaMgf`qex+7!!T{bY07n>bH%EZ==j`*>=*2_e`4a}4&In5!c0F^|)t z!6(LL?eL^jjqvZ&oWc~wKkE1-6PQMl$&6>xYfShmvS)2cO@CeD+3P;|f~3C~0{{R3 DW9(%e literal 0 HcmV?d00001 diff --git a/frontend/libs/vue.global.js b/frontend/libs/vue.global.js new file mode 100644 index 0000000..3b164ab --- /dev/null +++ b/frontend/libs/vue.global.js @@ -0,0 +1,18323 @@ +/** +* vue v3.5.23 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/ +var Vue = (function (exports) { + 'use strict'; + + // @__NO_SIDE_EFFECTS__ + function makeMap(str) { + const map = /* @__PURE__ */ Object.create(null); + for (const key of str.split(",")) map[key] = 1; + return (val) => val in map; + } + + const EMPTY_OBJ = Object.freeze({}) ; + const EMPTY_ARR = Object.freeze([]) ; + const NOOP = () => { + }; + const NO = () => false; + const isOn = (key) => key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && // uppercase letter + (key.charCodeAt(2) > 122 || key.charCodeAt(2) < 97); + const isModelListener = (key) => key.startsWith("onUpdate:"); + const extend = Object.assign; + const remove = (arr, el) => { + const i = arr.indexOf(el); + if (i > -1) { + arr.splice(i, 1); + } + }; + const hasOwnProperty$1 = Object.prototype.hasOwnProperty; + const hasOwn = (val, key) => hasOwnProperty$1.call(val, key); + const isArray = Array.isArray; + const isMap = (val) => toTypeString(val) === "[object Map]"; + const isSet = (val) => toTypeString(val) === "[object Set]"; + const isDate = (val) => toTypeString(val) === "[object Date]"; + const isRegExp = (val) => toTypeString(val) === "[object RegExp]"; + const isFunction = (val) => typeof val === "function"; + const isString = (val) => typeof val === "string"; + const isSymbol = (val) => typeof val === "symbol"; + const isObject = (val) => val !== null && typeof val === "object"; + const isPromise = (val) => { + return (isObject(val) || isFunction(val)) && isFunction(val.then) && isFunction(val.catch); + }; + const objectToString = Object.prototype.toString; + const toTypeString = (value) => objectToString.call(value); + const toRawType = (value) => { + return toTypeString(value).slice(8, -1); + }; + const isPlainObject = (val) => toTypeString(val) === "[object Object]"; + const isIntegerKey = (key) => isString(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key; + const isReservedProp = /* @__PURE__ */ makeMap( + // the leading comma is intentional so empty string "" is also included + ",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted" + ); + const isBuiltInDirective = /* @__PURE__ */ makeMap( + "bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo" + ); + const cacheStringFunction = (fn) => { + const cache = /* @__PURE__ */ Object.create(null); + return ((str) => { + const hit = cache[str]; + return hit || (cache[str] = fn(str)); + }); + }; + const camelizeRE = /-\w/g; + const camelize = cacheStringFunction( + (str) => { + return str.replace(camelizeRE, (c) => c.slice(1).toUpperCase()); + } + ); + const hyphenateRE = /\B([A-Z])/g; + const hyphenate = cacheStringFunction( + (str) => str.replace(hyphenateRE, "-$1").toLowerCase() + ); + const capitalize = cacheStringFunction((str) => { + return str.charAt(0).toUpperCase() + str.slice(1); + }); + const toHandlerKey = cacheStringFunction( + (str) => { + const s = str ? `on${capitalize(str)}` : ``; + return s; + } + ); + const hasChanged = (value, oldValue) => !Object.is(value, oldValue); + const invokeArrayFns = (fns, ...arg) => { + for (let i = 0; i < fns.length; i++) { + fns[i](...arg); + } + }; + const def = (obj, key, value, writable = false) => { + Object.defineProperty(obj, key, { + configurable: true, + enumerable: false, + writable, + value + }); + }; + const looseToNumber = (val) => { + const n = parseFloat(val); + return isNaN(n) ? val : n; + }; + const toNumber = (val) => { + const n = isString(val) ? Number(val) : NaN; + return isNaN(n) ? val : n; + }; + let _globalThis; + const getGlobalThis = () => { + return _globalThis || (_globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}); + }; + function genCacheKey(source, options) { + return source + JSON.stringify( + options, + (_, val) => typeof val === "function" ? val.toString() : val + ); + } + + const PatchFlagNames = { + [1]: `TEXT`, + [2]: `CLASS`, + [4]: `STYLE`, + [8]: `PROPS`, + [16]: `FULL_PROPS`, + [32]: `NEED_HYDRATION`, + [64]: `STABLE_FRAGMENT`, + [128]: `KEYED_FRAGMENT`, + [256]: `UNKEYED_FRAGMENT`, + [512]: `NEED_PATCH`, + [1024]: `DYNAMIC_SLOTS`, + [2048]: `DEV_ROOT_FRAGMENT`, + [-1]: `CACHED`, + [-2]: `BAIL` + }; + + const slotFlagsText = { + [1]: "STABLE", + [2]: "DYNAMIC", + [3]: "FORWARDED" + }; + + const GLOBALS_ALLOWED = "Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console,Error,Symbol"; + const isGloballyAllowed = /* @__PURE__ */ makeMap(GLOBALS_ALLOWED); + + const range = 2; + function generateCodeFrame(source, start = 0, end = source.length) { + start = Math.max(0, Math.min(start, source.length)); + end = Math.max(0, Math.min(end, source.length)); + if (start > end) return ""; + let lines = source.split(/(\r?\n)/); + const newlineSequences = lines.filter((_, idx) => idx % 2 === 1); + lines = lines.filter((_, idx) => idx % 2 === 0); + let count = 0; + const res = []; + for (let i = 0; i < lines.length; i++) { + count += lines[i].length + (newlineSequences[i] && newlineSequences[i].length || 0); + if (count >= start) { + for (let j = i - range; j <= i + range || end > count; j++) { + if (j < 0 || j >= lines.length) continue; + const line = j + 1; + res.push( + `${line}${" ".repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}` + ); + const lineLength = lines[j].length; + const newLineSeqLength = newlineSequences[j] && newlineSequences[j].length || 0; + if (j === i) { + const pad = start - (count - (lineLength + newLineSeqLength)); + const length = Math.max( + 1, + end > count ? lineLength - pad : end - start + ); + res.push(` | ` + " ".repeat(pad) + "^".repeat(length)); + } else if (j > i) { + if (end > count) { + const length = Math.max(Math.min(end - count, lineLength), 1); + res.push(` | ` + "^".repeat(length)); + } + count += lineLength + newLineSeqLength; + } + } + break; + } + } + return res.join("\n"); + } + + function normalizeStyle(value) { + if (isArray(value)) { + const res = {}; + for (let i = 0; i < value.length; i++) { + const item = value[i]; + const normalized = isString(item) ? parseStringStyle(item) : normalizeStyle(item); + if (normalized) { + for (const key in normalized) { + res[key] = normalized[key]; + } + } + } + return res; + } else if (isString(value) || isObject(value)) { + return value; + } + } + const listDelimiterRE = /;(?![^(]*\))/g; + const propertyDelimiterRE = /:([^]+)/; + const styleCommentRE = /\/\*[^]*?\*\//g; + function parseStringStyle(cssText) { + const ret = {}; + cssText.replace(styleCommentRE, "").split(listDelimiterRE).forEach((item) => { + if (item) { + const tmp = item.split(propertyDelimiterRE); + tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim()); + } + }); + return ret; + } + function stringifyStyle(styles) { + if (!styles) return ""; + if (isString(styles)) return styles; + let ret = ""; + for (const key in styles) { + const value = styles[key]; + if (isString(value) || typeof value === "number") { + const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key); + ret += `${normalizedKey}:${value};`; + } + } + return ret; + } + function normalizeClass(value) { + let res = ""; + if (isString(value)) { + res = value; + } else if (isArray(value)) { + for (let i = 0; i < value.length; i++) { + const normalized = normalizeClass(value[i]); + if (normalized) { + res += normalized + " "; + } + } + } else if (isObject(value)) { + for (const name in value) { + if (value[name]) { + res += name + " "; + } + } + } + return res.trim(); + } + function normalizeProps(props) { + if (!props) return null; + let { class: klass, style } = props; + if (klass && !isString(klass)) { + props.class = normalizeClass(klass); + } + if (style) { + props.style = normalizeStyle(style); + } + return props; + } + + const HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,aside,footer,header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot"; + const SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistantLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view"; + const MATH_TAGS = "annotation,annotation-xml,maction,maligngroup,malignmark,math,menclose,merror,mfenced,mfrac,mfraction,mglyph,mi,mlabeledtr,mlongdiv,mmultiscripts,mn,mo,mover,mpadded,mphantom,mprescripts,mroot,mrow,ms,mscarries,mscarry,msgroup,msline,mspace,msqrt,msrow,mstack,mstyle,msub,msubsup,msup,mtable,mtd,mtext,mtr,munder,munderover,none,semantics"; + const VOID_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr"; + const isHTMLTag = /* @__PURE__ */ makeMap(HTML_TAGS); + const isSVGTag = /* @__PURE__ */ makeMap(SVG_TAGS); + const isMathMLTag = /* @__PURE__ */ makeMap(MATH_TAGS); + const isVoidTag = /* @__PURE__ */ makeMap(VOID_TAGS); + + const specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`; + const isSpecialBooleanAttr = /* @__PURE__ */ makeMap(specialBooleanAttrs); + const isBooleanAttr = /* @__PURE__ */ makeMap( + specialBooleanAttrs + `,async,autofocus,autoplay,controls,default,defer,disabled,hidden,inert,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected` + ); + function includeBooleanAttr(value) { + return !!value || value === ""; + } + const isKnownHtmlAttr = /* @__PURE__ */ makeMap( + `accept,accept-charset,accesskey,action,align,allow,alt,async,autocapitalize,autocomplete,autofocus,autoplay,background,bgcolor,border,buffered,capture,challenge,charset,checked,cite,class,code,codebase,color,cols,colspan,content,contenteditable,contextmenu,controls,coords,crossorigin,csp,data,datetime,decoding,default,defer,dir,dirname,disabled,download,draggable,dropzone,enctype,enterkeyhint,for,form,formaction,formenctype,formmethod,formnovalidate,formtarget,headers,height,hidden,high,href,hreflang,http-equiv,icon,id,importance,inert,integrity,ismap,itemprop,keytype,kind,label,lang,language,loading,list,loop,low,manifest,max,maxlength,minlength,media,min,multiple,muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,preload,radiogroup,readonly,referrerpolicy,rel,required,reversed,rows,rowspan,sandbox,scope,scoped,selected,shape,size,sizes,slot,span,spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,target,title,translate,type,usemap,value,width,wrap` + ); + const isKnownSvgAttr = /* @__PURE__ */ makeMap( + `xmlns,accent-height,accumulate,additive,alignment-baseline,alphabetic,amplitude,arabic-form,ascent,attributeName,attributeType,azimuth,baseFrequency,baseline-shift,baseProfile,bbox,begin,bias,by,calcMode,cap-height,class,clip,clipPathUnits,clip-path,clip-rule,color,color-interpolation,color-interpolation-filters,color-profile,color-rendering,contentScriptType,contentStyleType,crossorigin,cursor,cx,cy,d,decelerate,descent,diffuseConstant,direction,display,divisor,dominant-baseline,dur,dx,dy,edgeMode,elevation,enable-background,end,exponent,fill,fill-opacity,fill-rule,filter,filterRes,filterUnits,flood-color,flood-opacity,font-family,font-size,font-size-adjust,font-stretch,font-style,font-variant,font-weight,format,from,fr,fx,fy,g1,g2,glyph-name,glyph-orientation-horizontal,glyph-orientation-vertical,glyphRef,gradientTransform,gradientUnits,hanging,height,href,hreflang,horiz-adv-x,horiz-origin-x,id,ideographic,image-rendering,in,in2,intercept,k,k1,k2,k3,k4,kernelMatrix,kernelUnitLength,kerning,keyPoints,keySplines,keyTimes,lang,lengthAdjust,letter-spacing,lighting-color,limitingConeAngle,local,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mask,maskContentUnits,maskUnits,mathematical,max,media,method,min,mode,name,numOctaves,offset,opacity,operator,order,orient,orientation,origin,overflow,overline-position,overline-thickness,panose-1,paint-order,path,pathLength,patternContentUnits,patternTransform,patternUnits,ping,pointer-events,points,pointsAtX,pointsAtY,pointsAtZ,preserveAlpha,preserveAspectRatio,primitiveUnits,r,radius,referrerPolicy,refX,refY,rel,rendering-intent,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,result,rotate,rx,ry,scale,seed,shape-rendering,slope,spacing,specularConstant,specularExponent,speed,spreadMethod,startOffset,stdDeviation,stemh,stemv,stitchTiles,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,string,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,style,surfaceScale,systemLanguage,tabindex,tableValues,target,targetX,targetY,text-anchor,text-decoration,text-rendering,textLength,to,transform,transform-origin,type,u1,u2,underline-position,underline-thickness,unicode,unicode-bidi,unicode-range,units-per-em,v-alphabetic,v-hanging,v-ideographic,v-mathematical,values,vector-effect,version,vert-adv-y,vert-origin-x,vert-origin-y,viewBox,viewTarget,visibility,width,widths,word-spacing,writing-mode,x,x-height,x1,x2,xChannelSelector,xlink:actuate,xlink:arcrole,xlink:href,xlink:role,xlink:show,xlink:title,xlink:type,xmlns:xlink,xml:base,xml:lang,xml:space,y,y1,y2,yChannelSelector,z,zoomAndPan` + ); + function isRenderableAttrValue(value) { + if (value == null) { + return false; + } + const type = typeof value; + return type === "string" || type === "number" || type === "boolean"; + } + + const cssVarNameEscapeSymbolsRE = /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g; + function getEscapedCssVarName(key, doubleEscape) { + return key.replace( + cssVarNameEscapeSymbolsRE, + (s) => `\\${s}` + ); + } + + function looseCompareArrays(a, b) { + if (a.length !== b.length) return false; + let equal = true; + for (let i = 0; equal && i < a.length; i++) { + equal = looseEqual(a[i], b[i]); + } + return equal; + } + function looseEqual(a, b) { + if (a === b) return true; + let aValidType = isDate(a); + let bValidType = isDate(b); + if (aValidType || bValidType) { + return aValidType && bValidType ? a.getTime() === b.getTime() : false; + } + aValidType = isSymbol(a); + bValidType = isSymbol(b); + if (aValidType || bValidType) { + return a === b; + } + aValidType = isArray(a); + bValidType = isArray(b); + if (aValidType || bValidType) { + return aValidType && bValidType ? looseCompareArrays(a, b) : false; + } + aValidType = isObject(a); + bValidType = isObject(b); + if (aValidType || bValidType) { + if (!aValidType || !bValidType) { + return false; + } + const aKeysCount = Object.keys(a).length; + const bKeysCount = Object.keys(b).length; + if (aKeysCount !== bKeysCount) { + return false; + } + for (const key in a) { + const aHasKey = a.hasOwnProperty(key); + const bHasKey = b.hasOwnProperty(key); + if (aHasKey && !bHasKey || !aHasKey && bHasKey || !looseEqual(a[key], b[key])) { + return false; + } + } + } + return String(a) === String(b); + } + function looseIndexOf(arr, val) { + return arr.findIndex((item) => looseEqual(item, val)); + } + + const isRef$1 = (val) => { + return !!(val && val["__v_isRef"] === true); + }; + const toDisplayString = (val) => { + return isString(val) ? val : val == null ? "" : isArray(val) || isObject(val) && (val.toString === objectToString || !isFunction(val.toString)) ? isRef$1(val) ? toDisplayString(val.value) : JSON.stringify(val, replacer, 2) : String(val); + }; + const replacer = (_key, val) => { + if (isRef$1(val)) { + return replacer(_key, val.value); + } else if (isMap(val)) { + return { + [`Map(${val.size})`]: [...val.entries()].reduce( + (entries, [key, val2], i) => { + entries[stringifySymbol(key, i) + " =>"] = val2; + return entries; + }, + {} + ) + }; + } else if (isSet(val)) { + return { + [`Set(${val.size})`]: [...val.values()].map((v) => stringifySymbol(v)) + }; + } else if (isSymbol(val)) { + return stringifySymbol(val); + } else if (isObject(val) && !isArray(val) && !isPlainObject(val)) { + return String(val); + } + return val; + }; + const stringifySymbol = (v, i = "") => { + var _a; + return ( + // Symbol.description in es2019+ so we need to cast here to pass + // the lib: es2016 check + isSymbol(v) ? `Symbol(${(_a = v.description) != null ? _a : i})` : v + ); + }; + + function normalizeCssVarValue(value) { + if (value == null) { + return "initial"; + } + if (typeof value === "string") { + return value === "" ? " " : value; + } + if (typeof value !== "number" || !Number.isFinite(value)) { + { + console.warn( + "[Vue warn] Invalid value used for CSS binding. Expected a string or a finite number but received:", + value + ); + } + } + return String(value); + } + + function warn$2(msg, ...args) { + console.warn(`[Vue warn] ${msg}`, ...args); + } + + let activeEffectScope; + class EffectScope { + constructor(detached = false) { + this.detached = detached; + /** + * @internal + */ + this._active = true; + /** + * @internal track `on` calls, allow `on` call multiple times + */ + this._on = 0; + /** + * @internal + */ + this.effects = []; + /** + * @internal + */ + this.cleanups = []; + this._isPaused = false; + this.parent = activeEffectScope; + if (!detached && activeEffectScope) { + this.index = (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push( + this + ) - 1; + } + } + get active() { + return this._active; + } + pause() { + if (this._active) { + this._isPaused = true; + let i, l; + if (this.scopes) { + for (i = 0, l = this.scopes.length; i < l; i++) { + this.scopes[i].pause(); + } + } + for (i = 0, l = this.effects.length; i < l; i++) { + this.effects[i].pause(); + } + } + } + /** + * Resumes the effect scope, including all child scopes and effects. + */ + resume() { + if (this._active) { + if (this._isPaused) { + this._isPaused = false; + let i, l; + if (this.scopes) { + for (i = 0, l = this.scopes.length; i < l; i++) { + this.scopes[i].resume(); + } + } + for (i = 0, l = this.effects.length; i < l; i++) { + this.effects[i].resume(); + } + } + } + } + run(fn) { + if (this._active) { + const currentEffectScope = activeEffectScope; + try { + activeEffectScope = this; + return fn(); + } finally { + activeEffectScope = currentEffectScope; + } + } else { + warn$2(`cannot run an inactive effect scope.`); + } + } + /** + * This should only be called on non-detached scopes + * @internal + */ + on() { + if (++this._on === 1) { + this.prevScope = activeEffectScope; + activeEffectScope = this; + } + } + /** + * This should only be called on non-detached scopes + * @internal + */ + off() { + if (this._on > 0 && --this._on === 0) { + activeEffectScope = this.prevScope; + this.prevScope = void 0; + } + } + stop(fromParent) { + if (this._active) { + this._active = false; + let i, l; + for (i = 0, l = this.effects.length; i < l; i++) { + this.effects[i].stop(); + } + this.effects.length = 0; + for (i = 0, l = this.cleanups.length; i < l; i++) { + this.cleanups[i](); + } + this.cleanups.length = 0; + if (this.scopes) { + for (i = 0, l = this.scopes.length; i < l; i++) { + this.scopes[i].stop(true); + } + this.scopes.length = 0; + } + if (!this.detached && this.parent && !fromParent) { + const last = this.parent.scopes.pop(); + if (last && last !== this) { + this.parent.scopes[this.index] = last; + last.index = this.index; + } + } + this.parent = void 0; + } + } + } + function effectScope(detached) { + return new EffectScope(detached); + } + function getCurrentScope() { + return activeEffectScope; + } + function onScopeDispose(fn, failSilently = false) { + if (activeEffectScope) { + activeEffectScope.cleanups.push(fn); + } else if (!failSilently) { + warn$2( + `onScopeDispose() is called when there is no active effect scope to be associated with.` + ); + } + } + + let activeSub; + const pausedQueueEffects = /* @__PURE__ */ new WeakSet(); + class ReactiveEffect { + constructor(fn) { + this.fn = fn; + /** + * @internal + */ + this.deps = void 0; + /** + * @internal + */ + this.depsTail = void 0; + /** + * @internal + */ + this.flags = 1 | 4; + /** + * @internal + */ + this.next = void 0; + /** + * @internal + */ + this.cleanup = void 0; + this.scheduler = void 0; + if (activeEffectScope && activeEffectScope.active) { + activeEffectScope.effects.push(this); + } + } + pause() { + this.flags |= 64; + } + resume() { + if (this.flags & 64) { + this.flags &= -65; + if (pausedQueueEffects.has(this)) { + pausedQueueEffects.delete(this); + this.trigger(); + } + } + } + /** + * @internal + */ + notify() { + if (this.flags & 2 && !(this.flags & 32)) { + return; + } + if (!(this.flags & 8)) { + batch(this); + } + } + run() { + if (!(this.flags & 1)) { + return this.fn(); + } + this.flags |= 2; + cleanupEffect(this); + prepareDeps(this); + const prevEffect = activeSub; + const prevShouldTrack = shouldTrack; + activeSub = this; + shouldTrack = true; + try { + return this.fn(); + } finally { + if (activeSub !== this) { + warn$2( + "Active effect was not restored correctly - this is likely a Vue internal bug." + ); + } + cleanupDeps(this); + activeSub = prevEffect; + shouldTrack = prevShouldTrack; + this.flags &= -3; + } + } + stop() { + if (this.flags & 1) { + for (let link = this.deps; link; link = link.nextDep) { + removeSub(link); + } + this.deps = this.depsTail = void 0; + cleanupEffect(this); + this.onStop && this.onStop(); + this.flags &= -2; + } + } + trigger() { + if (this.flags & 64) { + pausedQueueEffects.add(this); + } else if (this.scheduler) { + this.scheduler(); + } else { + this.runIfDirty(); + } + } + /** + * @internal + */ + runIfDirty() { + if (isDirty(this)) { + this.run(); + } + } + get dirty() { + return isDirty(this); + } + } + let batchDepth = 0; + let batchedSub; + let batchedComputed; + function batch(sub, isComputed = false) { + sub.flags |= 8; + if (isComputed) { + sub.next = batchedComputed; + batchedComputed = sub; + return; + } + sub.next = batchedSub; + batchedSub = sub; + } + function startBatch() { + batchDepth++; + } + function endBatch() { + if (--batchDepth > 0) { + return; + } + if (batchedComputed) { + let e = batchedComputed; + batchedComputed = void 0; + while (e) { + const next = e.next; + e.next = void 0; + e.flags &= -9; + e = next; + } + } + let error; + while (batchedSub) { + let e = batchedSub; + batchedSub = void 0; + while (e) { + const next = e.next; + e.next = void 0; + e.flags &= -9; + if (e.flags & 1) { + try { + ; + e.trigger(); + } catch (err) { + if (!error) error = err; + } + } + e = next; + } + } + if (error) throw error; + } + function prepareDeps(sub) { + for (let link = sub.deps; link; link = link.nextDep) { + link.version = -1; + link.prevActiveLink = link.dep.activeLink; + link.dep.activeLink = link; + } + } + function cleanupDeps(sub) { + let head; + let tail = sub.depsTail; + let link = tail; + while (link) { + const prev = link.prevDep; + if (link.version === -1) { + if (link === tail) tail = prev; + removeSub(link); + removeDep(link); + } else { + head = link; + } + link.dep.activeLink = link.prevActiveLink; + link.prevActiveLink = void 0; + link = prev; + } + sub.deps = head; + sub.depsTail = tail; + } + function isDirty(sub) { + for (let link = sub.deps; link; link = link.nextDep) { + if (link.dep.version !== link.version || link.dep.computed && (refreshComputed(link.dep.computed) || link.dep.version !== link.version)) { + return true; + } + } + if (sub._dirty) { + return true; + } + return false; + } + function refreshComputed(computed) { + if (computed.flags & 4 && !(computed.flags & 16)) { + return; + } + computed.flags &= -17; + if (computed.globalVersion === globalVersion) { + return; + } + computed.globalVersion = globalVersion; + if (!computed.isSSR && computed.flags & 128 && (!computed.deps && !computed._dirty || !isDirty(computed))) { + return; + } + computed.flags |= 2; + const dep = computed.dep; + const prevSub = activeSub; + const prevShouldTrack = shouldTrack; + activeSub = computed; + shouldTrack = true; + try { + prepareDeps(computed); + const value = computed.fn(computed._value); + if (dep.version === 0 || hasChanged(value, computed._value)) { + computed.flags |= 128; + computed._value = value; + dep.version++; + } + } catch (err) { + dep.version++; + throw err; + } finally { + activeSub = prevSub; + shouldTrack = prevShouldTrack; + cleanupDeps(computed); + computed.flags &= -3; + } + } + function removeSub(link, soft = false) { + const { dep, prevSub, nextSub } = link; + if (prevSub) { + prevSub.nextSub = nextSub; + link.prevSub = void 0; + } + if (nextSub) { + nextSub.prevSub = prevSub; + link.nextSub = void 0; + } + if (dep.subsHead === link) { + dep.subsHead = nextSub; + } + if (dep.subs === link) { + dep.subs = prevSub; + if (!prevSub && dep.computed) { + dep.computed.flags &= -5; + for (let l = dep.computed.deps; l; l = l.nextDep) { + removeSub(l, true); + } + } + } + if (!soft && !--dep.sc && dep.map) { + dep.map.delete(dep.key); + } + } + function removeDep(link) { + const { prevDep, nextDep } = link; + if (prevDep) { + prevDep.nextDep = nextDep; + link.prevDep = void 0; + } + if (nextDep) { + nextDep.prevDep = prevDep; + link.nextDep = void 0; + } + } + function effect(fn, options) { + if (fn.effect instanceof ReactiveEffect) { + fn = fn.effect.fn; + } + const e = new ReactiveEffect(fn); + if (options) { + extend(e, options); + } + try { + e.run(); + } catch (err) { + e.stop(); + throw err; + } + const runner = e.run.bind(e); + runner.effect = e; + return runner; + } + function stop(runner) { + runner.effect.stop(); + } + let shouldTrack = true; + const trackStack = []; + function pauseTracking() { + trackStack.push(shouldTrack); + shouldTrack = false; + } + function resetTracking() { + const last = trackStack.pop(); + shouldTrack = last === void 0 ? true : last; + } + function cleanupEffect(e) { + const { cleanup } = e; + e.cleanup = void 0; + if (cleanup) { + const prevSub = activeSub; + activeSub = void 0; + try { + cleanup(); + } finally { + activeSub = prevSub; + } + } + } + + let globalVersion = 0; + class Link { + constructor(sub, dep) { + this.sub = sub; + this.dep = dep; + this.version = dep.version; + this.nextDep = this.prevDep = this.nextSub = this.prevSub = this.prevActiveLink = void 0; + } + } + class Dep { + // TODO isolatedDeclarations "__v_skip" + constructor(computed) { + this.computed = computed; + this.version = 0; + /** + * Link between this dep and the current active effect + */ + this.activeLink = void 0; + /** + * Doubly linked list representing the subscribing effects (tail) + */ + this.subs = void 0; + /** + * For object property deps cleanup + */ + this.map = void 0; + this.key = void 0; + /** + * Subscriber counter + */ + this.sc = 0; + /** + * @internal + */ + this.__v_skip = true; + { + this.subsHead = void 0; + } + } + track(debugInfo) { + if (!activeSub || !shouldTrack || activeSub === this.computed) { + return; + } + let link = this.activeLink; + if (link === void 0 || link.sub !== activeSub) { + link = this.activeLink = new Link(activeSub, this); + if (!activeSub.deps) { + activeSub.deps = activeSub.depsTail = link; + } else { + link.prevDep = activeSub.depsTail; + activeSub.depsTail.nextDep = link; + activeSub.depsTail = link; + } + addSub(link); + } else if (link.version === -1) { + link.version = this.version; + if (link.nextDep) { + const next = link.nextDep; + next.prevDep = link.prevDep; + if (link.prevDep) { + link.prevDep.nextDep = next; + } + link.prevDep = activeSub.depsTail; + link.nextDep = void 0; + activeSub.depsTail.nextDep = link; + activeSub.depsTail = link; + if (activeSub.deps === link) { + activeSub.deps = next; + } + } + } + if (activeSub.onTrack) { + activeSub.onTrack( + extend( + { + effect: activeSub + }, + debugInfo + ) + ); + } + return link; + } + trigger(debugInfo) { + this.version++; + globalVersion++; + this.notify(debugInfo); + } + notify(debugInfo) { + startBatch(); + try { + if (true) { + for (let head = this.subsHead; head; head = head.nextSub) { + if (head.sub.onTrigger && !(head.sub.flags & 8)) { + head.sub.onTrigger( + extend( + { + effect: head.sub + }, + debugInfo + ) + ); + } + } + } + for (let link = this.subs; link; link = link.prevSub) { + if (link.sub.notify()) { + ; + link.sub.dep.notify(); + } + } + } finally { + endBatch(); + } + } + } + function addSub(link) { + link.dep.sc++; + if (link.sub.flags & 4) { + const computed = link.dep.computed; + if (computed && !link.dep.subs) { + computed.flags |= 4 | 16; + for (let l = computed.deps; l; l = l.nextDep) { + addSub(l); + } + } + const currentTail = link.dep.subs; + if (currentTail !== link) { + link.prevSub = currentTail; + if (currentTail) currentTail.nextSub = link; + } + if (link.dep.subsHead === void 0) { + link.dep.subsHead = link; + } + link.dep.subs = link; + } + } + const targetMap = /* @__PURE__ */ new WeakMap(); + const ITERATE_KEY = Symbol( + "Object iterate" + ); + const MAP_KEY_ITERATE_KEY = Symbol( + "Map keys iterate" + ); + const ARRAY_ITERATE_KEY = Symbol( + "Array iterate" + ); + function track(target, type, key) { + if (shouldTrack && activeSub) { + let depsMap = targetMap.get(target); + if (!depsMap) { + targetMap.set(target, depsMap = /* @__PURE__ */ new Map()); + } + let dep = depsMap.get(key); + if (!dep) { + depsMap.set(key, dep = new Dep()); + dep.map = depsMap; + dep.key = key; + } + { + dep.track({ + target, + type, + key + }); + } + } + } + function trigger(target, type, key, newValue, oldValue, oldTarget) { + const depsMap = targetMap.get(target); + if (!depsMap) { + globalVersion++; + return; + } + const run = (dep) => { + if (dep) { + { + dep.trigger({ + target, + type, + key, + newValue, + oldValue, + oldTarget + }); + } + } + }; + startBatch(); + if (type === "clear") { + depsMap.forEach(run); + } else { + const targetIsArray = isArray(target); + const isArrayIndex = targetIsArray && isIntegerKey(key); + if (targetIsArray && key === "length") { + const newLength = Number(newValue); + depsMap.forEach((dep, key2) => { + if (key2 === "length" || key2 === ARRAY_ITERATE_KEY || !isSymbol(key2) && key2 >= newLength) { + run(dep); + } + }); + } else { + if (key !== void 0 || depsMap.has(void 0)) { + run(depsMap.get(key)); + } + if (isArrayIndex) { + run(depsMap.get(ARRAY_ITERATE_KEY)); + } + switch (type) { + case "add": + if (!targetIsArray) { + run(depsMap.get(ITERATE_KEY)); + if (isMap(target)) { + run(depsMap.get(MAP_KEY_ITERATE_KEY)); + } + } else if (isArrayIndex) { + run(depsMap.get("length")); + } + break; + case "delete": + if (!targetIsArray) { + run(depsMap.get(ITERATE_KEY)); + if (isMap(target)) { + run(depsMap.get(MAP_KEY_ITERATE_KEY)); + } + } + break; + case "set": + if (isMap(target)) { + run(depsMap.get(ITERATE_KEY)); + } + break; + } + } + } + endBatch(); + } + function getDepFromReactive(object, key) { + const depMap = targetMap.get(object); + return depMap && depMap.get(key); + } + + function reactiveReadArray(array) { + const raw = toRaw(array); + if (raw === array) return raw; + track(raw, "iterate", ARRAY_ITERATE_KEY); + return isShallow(array) ? raw : raw.map(toReactive); + } + function shallowReadArray(arr) { + track(arr = toRaw(arr), "iterate", ARRAY_ITERATE_KEY); + return arr; + } + const arrayInstrumentations = { + __proto__: null, + [Symbol.iterator]() { + return iterator(this, Symbol.iterator, toReactive); + }, + concat(...args) { + return reactiveReadArray(this).concat( + ...args.map((x) => isArray(x) ? reactiveReadArray(x) : x) + ); + }, + entries() { + return iterator(this, "entries", (value) => { + value[1] = toReactive(value[1]); + return value; + }); + }, + every(fn, thisArg) { + return apply(this, "every", fn, thisArg, void 0, arguments); + }, + filter(fn, thisArg) { + return apply(this, "filter", fn, thisArg, (v) => v.map(toReactive), arguments); + }, + find(fn, thisArg) { + return apply(this, "find", fn, thisArg, toReactive, arguments); + }, + findIndex(fn, thisArg) { + return apply(this, "findIndex", fn, thisArg, void 0, arguments); + }, + findLast(fn, thisArg) { + return apply(this, "findLast", fn, thisArg, toReactive, arguments); + }, + findLastIndex(fn, thisArg) { + return apply(this, "findLastIndex", fn, thisArg, void 0, arguments); + }, + // flat, flatMap could benefit from ARRAY_ITERATE but are not straight-forward to implement + forEach(fn, thisArg) { + return apply(this, "forEach", fn, thisArg, void 0, arguments); + }, + includes(...args) { + return searchProxy(this, "includes", args); + }, + indexOf(...args) { + return searchProxy(this, "indexOf", args); + }, + join(separator) { + return reactiveReadArray(this).join(separator); + }, + // keys() iterator only reads `length`, no optimization required + lastIndexOf(...args) { + return searchProxy(this, "lastIndexOf", args); + }, + map(fn, thisArg) { + return apply(this, "map", fn, thisArg, void 0, arguments); + }, + pop() { + return noTracking(this, "pop"); + }, + push(...args) { + return noTracking(this, "push", args); + }, + reduce(fn, ...args) { + return reduce(this, "reduce", fn, args); + }, + reduceRight(fn, ...args) { + return reduce(this, "reduceRight", fn, args); + }, + shift() { + return noTracking(this, "shift"); + }, + // slice could use ARRAY_ITERATE but also seems to beg for range tracking + some(fn, thisArg) { + return apply(this, "some", fn, thisArg, void 0, arguments); + }, + splice(...args) { + return noTracking(this, "splice", args); + }, + toReversed() { + return reactiveReadArray(this).toReversed(); + }, + toSorted(comparer) { + return reactiveReadArray(this).toSorted(comparer); + }, + toSpliced(...args) { + return reactiveReadArray(this).toSpliced(...args); + }, + unshift(...args) { + return noTracking(this, "unshift", args); + }, + values() { + return iterator(this, "values", toReactive); + } + }; + function iterator(self, method, wrapValue) { + const arr = shallowReadArray(self); + const iter = arr[method](); + if (arr !== self && !isShallow(self)) { + iter._next = iter.next; + iter.next = () => { + const result = iter._next(); + if (!result.done) { + result.value = wrapValue(result.value); + } + return result; + }; + } + return iter; + } + const arrayProto = Array.prototype; + function apply(self, method, fn, thisArg, wrappedRetFn, args) { + const arr = shallowReadArray(self); + const needsWrap = arr !== self && !isShallow(self); + const methodFn = arr[method]; + if (methodFn !== arrayProto[method]) { + const result2 = methodFn.apply(self, args); + return needsWrap ? toReactive(result2) : result2; + } + let wrappedFn = fn; + if (arr !== self) { + if (needsWrap) { + wrappedFn = function(item, index) { + return fn.call(this, toReactive(item), index, self); + }; + } else if (fn.length > 2) { + wrappedFn = function(item, index) { + return fn.call(this, item, index, self); + }; + } + } + const result = methodFn.call(arr, wrappedFn, thisArg); + return needsWrap && wrappedRetFn ? wrappedRetFn(result) : result; + } + function reduce(self, method, fn, args) { + const arr = shallowReadArray(self); + let wrappedFn = fn; + if (arr !== self) { + if (!isShallow(self)) { + wrappedFn = function(acc, item, index) { + return fn.call(this, acc, toReactive(item), index, self); + }; + } else if (fn.length > 3) { + wrappedFn = function(acc, item, index) { + return fn.call(this, acc, item, index, self); + }; + } + } + return arr[method](wrappedFn, ...args); + } + function searchProxy(self, method, args) { + const arr = toRaw(self); + track(arr, "iterate", ARRAY_ITERATE_KEY); + const res = arr[method](...args); + if ((res === -1 || res === false) && isProxy(args[0])) { + args[0] = toRaw(args[0]); + return arr[method](...args); + } + return res; + } + function noTracking(self, method, args = []) { + pauseTracking(); + startBatch(); + const res = toRaw(self)[method].apply(self, args); + endBatch(); + resetTracking(); + return res; + } + + const isNonTrackableKeys = /* @__PURE__ */ makeMap(`__proto__,__v_isRef,__isVue`); + const builtInSymbols = new Set( + /* @__PURE__ */ Object.getOwnPropertyNames(Symbol).filter((key) => key !== "arguments" && key !== "caller").map((key) => Symbol[key]).filter(isSymbol) + ); + function hasOwnProperty(key) { + if (!isSymbol(key)) key = String(key); + const obj = toRaw(this); + track(obj, "has", key); + return obj.hasOwnProperty(key); + } + class BaseReactiveHandler { + constructor(_isReadonly = false, _isShallow = false) { + this._isReadonly = _isReadonly; + this._isShallow = _isShallow; + } + get(target, key, receiver) { + if (key === "__v_skip") return target["__v_skip"]; + const isReadonly2 = this._isReadonly, isShallow2 = this._isShallow; + if (key === "__v_isReactive") { + return !isReadonly2; + } else if (key === "__v_isReadonly") { + return isReadonly2; + } else if (key === "__v_isShallow") { + return isShallow2; + } else if (key === "__v_raw") { + if (receiver === (isReadonly2 ? isShallow2 ? shallowReadonlyMap : readonlyMap : isShallow2 ? shallowReactiveMap : reactiveMap).get(target) || // receiver is not the reactive proxy, but has the same prototype + // this means the receiver is a user proxy of the reactive proxy + Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)) { + return target; + } + return; + } + const targetIsArray = isArray(target); + if (!isReadonly2) { + let fn; + if (targetIsArray && (fn = arrayInstrumentations[key])) { + return fn; + } + if (key === "hasOwnProperty") { + return hasOwnProperty; + } + } + const res = Reflect.get( + target, + key, + // if this is a proxy wrapping a ref, return methods using the raw ref + // as receiver so that we don't have to call `toRaw` on the ref in all + // its class methods + isRef(target) ? target : receiver + ); + if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) { + return res; + } + if (!isReadonly2) { + track(target, "get", key); + } + if (isShallow2) { + return res; + } + if (isRef(res)) { + const value = targetIsArray && isIntegerKey(key) ? res : res.value; + return isReadonly2 && isObject(value) ? readonly(value) : value; + } + if (isObject(res)) { + return isReadonly2 ? readonly(res) : reactive(res); + } + return res; + } + } + class MutableReactiveHandler extends BaseReactiveHandler { + constructor(isShallow2 = false) { + super(false, isShallow2); + } + set(target, key, value, receiver) { + let oldValue = target[key]; + if (!this._isShallow) { + const isOldValueReadonly = isReadonly(oldValue); + if (!isShallow(value) && !isReadonly(value)) { + oldValue = toRaw(oldValue); + value = toRaw(value); + } + if (!isArray(target) && isRef(oldValue) && !isRef(value)) { + if (isOldValueReadonly) { + { + warn$2( + `Set operation on key "${String(key)}" failed: target is readonly.`, + target[key] + ); + } + return true; + } else { + oldValue.value = value; + return true; + } + } + } + const hadKey = isArray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key); + const result = Reflect.set( + target, + key, + value, + isRef(target) ? target : receiver + ); + if (target === toRaw(receiver)) { + if (!hadKey) { + trigger(target, "add", key, value); + } else if (hasChanged(value, oldValue)) { + trigger(target, "set", key, value, oldValue); + } + } + return result; + } + deleteProperty(target, key) { + const hadKey = hasOwn(target, key); + const oldValue = target[key]; + const result = Reflect.deleteProperty(target, key); + if (result && hadKey) { + trigger(target, "delete", key, void 0, oldValue); + } + return result; + } + has(target, key) { + const result = Reflect.has(target, key); + if (!isSymbol(key) || !builtInSymbols.has(key)) { + track(target, "has", key); + } + return result; + } + ownKeys(target) { + track( + target, + "iterate", + isArray(target) ? "length" : ITERATE_KEY + ); + return Reflect.ownKeys(target); + } + } + class ReadonlyReactiveHandler extends BaseReactiveHandler { + constructor(isShallow2 = false) { + super(true, isShallow2); + } + set(target, key) { + { + warn$2( + `Set operation on key "${String(key)}" failed: target is readonly.`, + target + ); + } + return true; + } + deleteProperty(target, key) { + { + warn$2( + `Delete operation on key "${String(key)}" failed: target is readonly.`, + target + ); + } + return true; + } + } + const mutableHandlers = /* @__PURE__ */ new MutableReactiveHandler(); + const readonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler(); + const shallowReactiveHandlers = /* @__PURE__ */ new MutableReactiveHandler(true); + const shallowReadonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler(true); + + const toShallow = (value) => value; + const getProto = (v) => Reflect.getPrototypeOf(v); + function createIterableMethod(method, isReadonly2, isShallow2) { + return function(...args) { + const target = this["__v_raw"]; + const rawTarget = toRaw(target); + const targetIsMap = isMap(rawTarget); + const isPair = method === "entries" || method === Symbol.iterator && targetIsMap; + const isKeyOnly = method === "keys" && targetIsMap; + const innerIterator = target[method](...args); + const wrap = isShallow2 ? toShallow : isReadonly2 ? toReadonly : toReactive; + !isReadonly2 && track( + rawTarget, + "iterate", + isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY + ); + return { + // iterator protocol + next() { + const { value, done } = innerIterator.next(); + return done ? { value, done } : { + value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value), + done + }; + }, + // iterable protocol + [Symbol.iterator]() { + return this; + } + }; + }; + } + function createReadonlyMethod(type) { + return function(...args) { + { + const key = args[0] ? `on key "${args[0]}" ` : ``; + warn$2( + `${capitalize(type)} operation ${key}failed: target is readonly.`, + toRaw(this) + ); + } + return type === "delete" ? false : type === "clear" ? void 0 : this; + }; + } + function createInstrumentations(readonly, shallow) { + const instrumentations = { + get(key) { + const target = this["__v_raw"]; + const rawTarget = toRaw(target); + const rawKey = toRaw(key); + if (!readonly) { + if (hasChanged(key, rawKey)) { + track(rawTarget, "get", key); + } + track(rawTarget, "get", rawKey); + } + const { has } = getProto(rawTarget); + const wrap = shallow ? toShallow : readonly ? toReadonly : toReactive; + if (has.call(rawTarget, key)) { + return wrap(target.get(key)); + } else if (has.call(rawTarget, rawKey)) { + return wrap(target.get(rawKey)); + } else if (target !== rawTarget) { + target.get(key); + } + }, + get size() { + const target = this["__v_raw"]; + !readonly && track(toRaw(target), "iterate", ITERATE_KEY); + return target.size; + }, + has(key) { + const target = this["__v_raw"]; + const rawTarget = toRaw(target); + const rawKey = toRaw(key); + if (!readonly) { + if (hasChanged(key, rawKey)) { + track(rawTarget, "has", key); + } + track(rawTarget, "has", rawKey); + } + return key === rawKey ? target.has(key) : target.has(key) || target.has(rawKey); + }, + forEach(callback, thisArg) { + const observed = this; + const target = observed["__v_raw"]; + const rawTarget = toRaw(target); + const wrap = shallow ? toShallow : readonly ? toReadonly : toReactive; + !readonly && track(rawTarget, "iterate", ITERATE_KEY); + return target.forEach((value, key) => { + return callback.call(thisArg, wrap(value), wrap(key), observed); + }); + } + }; + extend( + instrumentations, + readonly ? { + add: createReadonlyMethod("add"), + set: createReadonlyMethod("set"), + delete: createReadonlyMethod("delete"), + clear: createReadonlyMethod("clear") + } : { + add(value) { + if (!shallow && !isShallow(value) && !isReadonly(value)) { + value = toRaw(value); + } + const target = toRaw(this); + const proto = getProto(target); + const hadKey = proto.has.call(target, value); + if (!hadKey) { + target.add(value); + trigger(target, "add", value, value); + } + return this; + }, + set(key, value) { + if (!shallow && !isShallow(value) && !isReadonly(value)) { + value = toRaw(value); + } + const target = toRaw(this); + const { has, get } = getProto(target); + let hadKey = has.call(target, key); + if (!hadKey) { + key = toRaw(key); + hadKey = has.call(target, key); + } else { + checkIdentityKeys(target, has, key); + } + const oldValue = get.call(target, key); + target.set(key, value); + if (!hadKey) { + trigger(target, "add", key, value); + } else if (hasChanged(value, oldValue)) { + trigger(target, "set", key, value, oldValue); + } + return this; + }, + delete(key) { + const target = toRaw(this); + const { has, get } = getProto(target); + let hadKey = has.call(target, key); + if (!hadKey) { + key = toRaw(key); + hadKey = has.call(target, key); + } else { + checkIdentityKeys(target, has, key); + } + const oldValue = get ? get.call(target, key) : void 0; + const result = target.delete(key); + if (hadKey) { + trigger(target, "delete", key, void 0, oldValue); + } + return result; + }, + clear() { + const target = toRaw(this); + const hadItems = target.size !== 0; + const oldTarget = isMap(target) ? new Map(target) : new Set(target) ; + const result = target.clear(); + if (hadItems) { + trigger( + target, + "clear", + void 0, + void 0, + oldTarget + ); + } + return result; + } + } + ); + const iteratorMethods = [ + "keys", + "values", + "entries", + Symbol.iterator + ]; + iteratorMethods.forEach((method) => { + instrumentations[method] = createIterableMethod(method, readonly, shallow); + }); + return instrumentations; + } + function createInstrumentationGetter(isReadonly2, shallow) { + const instrumentations = createInstrumentations(isReadonly2, shallow); + return (target, key, receiver) => { + if (key === "__v_isReactive") { + return !isReadonly2; + } else if (key === "__v_isReadonly") { + return isReadonly2; + } else if (key === "__v_raw") { + return target; + } + return Reflect.get( + hasOwn(instrumentations, key) && key in target ? instrumentations : target, + key, + receiver + ); + }; + } + const mutableCollectionHandlers = { + get: /* @__PURE__ */ createInstrumentationGetter(false, false) + }; + const shallowCollectionHandlers = { + get: /* @__PURE__ */ createInstrumentationGetter(false, true) + }; + const readonlyCollectionHandlers = { + get: /* @__PURE__ */ createInstrumentationGetter(true, false) + }; + const shallowReadonlyCollectionHandlers = { + get: /* @__PURE__ */ createInstrumentationGetter(true, true) + }; + function checkIdentityKeys(target, has, key) { + const rawKey = toRaw(key); + if (rawKey !== key && has.call(target, rawKey)) { + const type = toRawType(target); + warn$2( + `Reactive ${type} contains both the raw and reactive versions of the same object${type === `Map` ? ` as keys` : ``}, which can lead to inconsistencies. Avoid differentiating between the raw and reactive versions of an object and only use the reactive version if possible.` + ); + } + } + + const reactiveMap = /* @__PURE__ */ new WeakMap(); + const shallowReactiveMap = /* @__PURE__ */ new WeakMap(); + const readonlyMap = /* @__PURE__ */ new WeakMap(); + const shallowReadonlyMap = /* @__PURE__ */ new WeakMap(); + function targetTypeMap(rawType) { + switch (rawType) { + case "Object": + case "Array": + return 1 /* COMMON */; + case "Map": + case "Set": + case "WeakMap": + case "WeakSet": + return 2 /* COLLECTION */; + default: + return 0 /* INVALID */; + } + } + function getTargetType(value) { + return value["__v_skip"] || !Object.isExtensible(value) ? 0 /* INVALID */ : targetTypeMap(toRawType(value)); + } + function reactive(target) { + if (isReadonly(target)) { + return target; + } + return createReactiveObject( + target, + false, + mutableHandlers, + mutableCollectionHandlers, + reactiveMap + ); + } + function shallowReactive(target) { + return createReactiveObject( + target, + false, + shallowReactiveHandlers, + shallowCollectionHandlers, + shallowReactiveMap + ); + } + function readonly(target) { + return createReactiveObject( + target, + true, + readonlyHandlers, + readonlyCollectionHandlers, + readonlyMap + ); + } + function shallowReadonly(target) { + return createReactiveObject( + target, + true, + shallowReadonlyHandlers, + shallowReadonlyCollectionHandlers, + shallowReadonlyMap + ); + } + function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) { + if (!isObject(target)) { + { + warn$2( + `value cannot be made ${isReadonly2 ? "readonly" : "reactive"}: ${String( + target + )}` + ); + } + return target; + } + if (target["__v_raw"] && !(isReadonly2 && target["__v_isReactive"])) { + return target; + } + const targetType = getTargetType(target); + if (targetType === 0 /* INVALID */) { + return target; + } + const existingProxy = proxyMap.get(target); + if (existingProxy) { + return existingProxy; + } + const proxy = new Proxy( + target, + targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers + ); + proxyMap.set(target, proxy); + return proxy; + } + function isReactive(value) { + if (isReadonly(value)) { + return isReactive(value["__v_raw"]); + } + return !!(value && value["__v_isReactive"]); + } + function isReadonly(value) { + return !!(value && value["__v_isReadonly"]); + } + function isShallow(value) { + return !!(value && value["__v_isShallow"]); + } + function isProxy(value) { + return value ? !!value["__v_raw"] : false; + } + function toRaw(observed) { + const raw = observed && observed["__v_raw"]; + return raw ? toRaw(raw) : observed; + } + function markRaw(value) { + if (!hasOwn(value, "__v_skip") && Object.isExtensible(value)) { + def(value, "__v_skip", true); + } + return value; + } + const toReactive = (value) => isObject(value) ? reactive(value) : value; + const toReadonly = (value) => isObject(value) ? readonly(value) : value; + + function isRef(r) { + return r ? r["__v_isRef"] === true : false; + } + function ref(value) { + return createRef(value, false); + } + function shallowRef(value) { + return createRef(value, true); + } + function createRef(rawValue, shallow) { + if (isRef(rawValue)) { + return rawValue; + } + return new RefImpl(rawValue, shallow); + } + class RefImpl { + constructor(value, isShallow2) { + this.dep = new Dep(); + this["__v_isRef"] = true; + this["__v_isShallow"] = false; + this._rawValue = isShallow2 ? value : toRaw(value); + this._value = isShallow2 ? value : toReactive(value); + this["__v_isShallow"] = isShallow2; + } + get value() { + { + this.dep.track({ + target: this, + type: "get", + key: "value" + }); + } + return this._value; + } + set value(newValue) { + const oldValue = this._rawValue; + const useDirectValue = this["__v_isShallow"] || isShallow(newValue) || isReadonly(newValue); + newValue = useDirectValue ? newValue : toRaw(newValue); + if (hasChanged(newValue, oldValue)) { + this._rawValue = newValue; + this._value = useDirectValue ? newValue : toReactive(newValue); + { + this.dep.trigger({ + target: this, + type: "set", + key: "value", + newValue, + oldValue + }); + } + } + } + } + function triggerRef(ref2) { + if (ref2.dep) { + { + ref2.dep.trigger({ + target: ref2, + type: "set", + key: "value", + newValue: ref2._value + }); + } + } + } + function unref(ref2) { + return isRef(ref2) ? ref2.value : ref2; + } + function toValue(source) { + return isFunction(source) ? source() : unref(source); + } + const shallowUnwrapHandlers = { + get: (target, key, receiver) => key === "__v_raw" ? target : unref(Reflect.get(target, key, receiver)), + set: (target, key, value, receiver) => { + const oldValue = target[key]; + if (isRef(oldValue) && !isRef(value)) { + oldValue.value = value; + return true; + } else { + return Reflect.set(target, key, value, receiver); + } + } + }; + function proxyRefs(objectWithRefs) { + return isReactive(objectWithRefs) ? objectWithRefs : new Proxy(objectWithRefs, shallowUnwrapHandlers); + } + class CustomRefImpl { + constructor(factory) { + this["__v_isRef"] = true; + this._value = void 0; + const dep = this.dep = new Dep(); + const { get, set } = factory(dep.track.bind(dep), dep.trigger.bind(dep)); + this._get = get; + this._set = set; + } + get value() { + return this._value = this._get(); + } + set value(newVal) { + this._set(newVal); + } + } + function customRef(factory) { + return new CustomRefImpl(factory); + } + function toRefs(object) { + if (!isProxy(object)) { + warn$2(`toRefs() expects a reactive object but received a plain one.`); + } + const ret = isArray(object) ? new Array(object.length) : {}; + for (const key in object) { + ret[key] = propertyToRef(object, key); + } + return ret; + } + class ObjectRefImpl { + constructor(_object, _key, _defaultValue) { + this._object = _object; + this._key = _key; + this._defaultValue = _defaultValue; + this["__v_isRef"] = true; + this._value = void 0; + } + get value() { + const val = this._object[this._key]; + return this._value = val === void 0 ? this._defaultValue : val; + } + set value(newVal) { + this._object[this._key] = newVal; + } + get dep() { + return getDepFromReactive(toRaw(this._object), this._key); + } + } + class GetterRefImpl { + constructor(_getter) { + this._getter = _getter; + this["__v_isRef"] = true; + this["__v_isReadonly"] = true; + this._value = void 0; + } + get value() { + return this._value = this._getter(); + } + } + function toRef(source, key, defaultValue) { + if (isRef(source)) { + return source; + } else if (isFunction(source)) { + return new GetterRefImpl(source); + } else if (isObject(source) && arguments.length > 1) { + return propertyToRef(source, key, defaultValue); + } else { + return ref(source); + } + } + function propertyToRef(source, key, defaultValue) { + const val = source[key]; + return isRef(val) ? val : new ObjectRefImpl(source, key, defaultValue); + } + + class ComputedRefImpl { + constructor(fn, setter, isSSR) { + this.fn = fn; + this.setter = setter; + /** + * @internal + */ + this._value = void 0; + /** + * @internal + */ + this.dep = new Dep(this); + /** + * @internal + */ + this.__v_isRef = true; + // TODO isolatedDeclarations "__v_isReadonly" + // A computed is also a subscriber that tracks other deps + /** + * @internal + */ + this.deps = void 0; + /** + * @internal + */ + this.depsTail = void 0; + /** + * @internal + */ + this.flags = 16; + /** + * @internal + */ + this.globalVersion = globalVersion - 1; + /** + * @internal + */ + this.next = void 0; + // for backwards compat + this.effect = this; + this["__v_isReadonly"] = !setter; + this.isSSR = isSSR; + } + /** + * @internal + */ + notify() { + this.flags |= 16; + if (!(this.flags & 8) && // avoid infinite self recursion + activeSub !== this) { + batch(this, true); + return true; + } + } + get value() { + const link = this.dep.track({ + target: this, + type: "get", + key: "value" + }) ; + refreshComputed(this); + if (link) { + link.version = this.dep.version; + } + return this._value; + } + set value(newValue) { + if (this.setter) { + this.setter(newValue); + } else { + warn$2("Write operation failed: computed value is readonly"); + } + } + } + function computed$1(getterOrOptions, debugOptions, isSSR = false) { + let getter; + let setter; + if (isFunction(getterOrOptions)) { + getter = getterOrOptions; + } else { + getter = getterOrOptions.get; + setter = getterOrOptions.set; + } + const cRef = new ComputedRefImpl(getter, setter, isSSR); + if (debugOptions && !isSSR) { + cRef.onTrack = debugOptions.onTrack; + cRef.onTrigger = debugOptions.onTrigger; + } + return cRef; + } + + const TrackOpTypes = { + "GET": "get", + "HAS": "has", + "ITERATE": "iterate" + }; + const TriggerOpTypes = { + "SET": "set", + "ADD": "add", + "DELETE": "delete", + "CLEAR": "clear" + }; + + const INITIAL_WATCHER_VALUE = {}; + const cleanupMap = /* @__PURE__ */ new WeakMap(); + let activeWatcher = void 0; + function getCurrentWatcher() { + return activeWatcher; + } + function onWatcherCleanup(cleanupFn, failSilently = false, owner = activeWatcher) { + if (owner) { + let cleanups = cleanupMap.get(owner); + if (!cleanups) cleanupMap.set(owner, cleanups = []); + cleanups.push(cleanupFn); + } else if (!failSilently) { + warn$2( + `onWatcherCleanup() was called when there was no active watcher to associate with.` + ); + } + } + function watch$1(source, cb, options = EMPTY_OBJ) { + const { immediate, deep, once, scheduler, augmentJob, call } = options; + const warnInvalidSource = (s) => { + (options.onWarn || warn$2)( + `Invalid watch source: `, + s, + `A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.` + ); + }; + const reactiveGetter = (source2) => { + if (deep) return source2; + if (isShallow(source2) || deep === false || deep === 0) + return traverse(source2, 1); + return traverse(source2); + }; + let effect; + let getter; + let cleanup; + let boundCleanup; + let forceTrigger = false; + let isMultiSource = false; + if (isRef(source)) { + getter = () => source.value; + forceTrigger = isShallow(source); + } else if (isReactive(source)) { + getter = () => reactiveGetter(source); + forceTrigger = true; + } else if (isArray(source)) { + isMultiSource = true; + forceTrigger = source.some((s) => isReactive(s) || isShallow(s)); + getter = () => source.map((s) => { + if (isRef(s)) { + return s.value; + } else if (isReactive(s)) { + return reactiveGetter(s); + } else if (isFunction(s)) { + return call ? call(s, 2) : s(); + } else { + warnInvalidSource(s); + } + }); + } else if (isFunction(source)) { + if (cb) { + getter = call ? () => call(source, 2) : source; + } else { + getter = () => { + if (cleanup) { + pauseTracking(); + try { + cleanup(); + } finally { + resetTracking(); + } + } + const currentEffect = activeWatcher; + activeWatcher = effect; + try { + return call ? call(source, 3, [boundCleanup]) : source(boundCleanup); + } finally { + activeWatcher = currentEffect; + } + }; + } + } else { + getter = NOOP; + warnInvalidSource(source); + } + if (cb && deep) { + const baseGetter = getter; + const depth = deep === true ? Infinity : deep; + getter = () => traverse(baseGetter(), depth); + } + const scope = getCurrentScope(); + const watchHandle = () => { + effect.stop(); + if (scope && scope.active) { + remove(scope.effects, effect); + } + }; + if (once && cb) { + const _cb = cb; + cb = (...args) => { + _cb(...args); + watchHandle(); + }; + } + let oldValue = isMultiSource ? new Array(source.length).fill(INITIAL_WATCHER_VALUE) : INITIAL_WATCHER_VALUE; + const job = (immediateFirstRun) => { + if (!(effect.flags & 1) || !effect.dirty && !immediateFirstRun) { + return; + } + if (cb) { + const newValue = effect.run(); + if (deep || forceTrigger || (isMultiSource ? newValue.some((v, i) => hasChanged(v, oldValue[i])) : hasChanged(newValue, oldValue))) { + if (cleanup) { + cleanup(); + } + const currentWatcher = activeWatcher; + activeWatcher = effect; + try { + const args = [ + newValue, + // pass undefined as the old value when it's changed for the first time + oldValue === INITIAL_WATCHER_VALUE ? void 0 : isMultiSource && oldValue[0] === INITIAL_WATCHER_VALUE ? [] : oldValue, + boundCleanup + ]; + oldValue = newValue; + call ? call(cb, 3, args) : ( + // @ts-expect-error + cb(...args) + ); + } finally { + activeWatcher = currentWatcher; + } + } + } else { + effect.run(); + } + }; + if (augmentJob) { + augmentJob(job); + } + effect = new ReactiveEffect(getter); + effect.scheduler = scheduler ? () => scheduler(job, false) : job; + boundCleanup = (fn) => onWatcherCleanup(fn, false, effect); + cleanup = effect.onStop = () => { + const cleanups = cleanupMap.get(effect); + if (cleanups) { + if (call) { + call(cleanups, 4); + } else { + for (const cleanup2 of cleanups) cleanup2(); + } + cleanupMap.delete(effect); + } + }; + { + effect.onTrack = options.onTrack; + effect.onTrigger = options.onTrigger; + } + if (cb) { + if (immediate) { + job(true); + } else { + oldValue = effect.run(); + } + } else if (scheduler) { + scheduler(job.bind(null, true), true); + } else { + effect.run(); + } + watchHandle.pause = effect.pause.bind(effect); + watchHandle.resume = effect.resume.bind(effect); + watchHandle.stop = watchHandle; + return watchHandle; + } + function traverse(value, depth = Infinity, seen) { + if (depth <= 0 || !isObject(value) || value["__v_skip"]) { + return value; + } + seen = seen || /* @__PURE__ */ new Map(); + if ((seen.get(value) || 0) >= depth) { + return value; + } + seen.set(value, depth); + depth--; + if (isRef(value)) { + traverse(value.value, depth, seen); + } else if (isArray(value)) { + for (let i = 0; i < value.length; i++) { + traverse(value[i], depth, seen); + } + } else if (isSet(value) || isMap(value)) { + value.forEach((v) => { + traverse(v, depth, seen); + }); + } else if (isPlainObject(value)) { + for (const key in value) { + traverse(value[key], depth, seen); + } + for (const key of Object.getOwnPropertySymbols(value)) { + if (Object.prototype.propertyIsEnumerable.call(value, key)) { + traverse(value[key], depth, seen); + } + } + } + return value; + } + + const stack$1 = []; + function pushWarningContext(vnode) { + stack$1.push(vnode); + } + function popWarningContext() { + stack$1.pop(); + } + let isWarning = false; + function warn$1(msg, ...args) { + if (isWarning) return; + isWarning = true; + pauseTracking(); + const instance = stack$1.length ? stack$1[stack$1.length - 1].component : null; + const appWarnHandler = instance && instance.appContext.config.warnHandler; + const trace = getComponentTrace(); + if (appWarnHandler) { + callWithErrorHandling( + appWarnHandler, + instance, + 11, + [ + // eslint-disable-next-line no-restricted-syntax + msg + args.map((a) => { + var _a, _b; + return (_b = (_a = a.toString) == null ? void 0 : _a.call(a)) != null ? _b : JSON.stringify(a); + }).join(""), + instance && instance.proxy, + trace.map( + ({ vnode }) => `at <${formatComponentName(instance, vnode.type)}>` + ).join("\n"), + trace + ] + ); + } else { + const warnArgs = [`[Vue warn]: ${msg}`, ...args]; + if (trace.length && // avoid spamming console during tests + true) { + warnArgs.push(` +`, ...formatTrace(trace)); + } + console.warn(...warnArgs); + } + resetTracking(); + isWarning = false; + } + function getComponentTrace() { + let currentVNode = stack$1[stack$1.length - 1]; + if (!currentVNode) { + return []; + } + const normalizedStack = []; + while (currentVNode) { + const last = normalizedStack[0]; + if (last && last.vnode === currentVNode) { + last.recurseCount++; + } else { + normalizedStack.push({ + vnode: currentVNode, + recurseCount: 0 + }); + } + const parentInstance = currentVNode.component && currentVNode.component.parent; + currentVNode = parentInstance && parentInstance.vnode; + } + return normalizedStack; + } + function formatTrace(trace) { + const logs = []; + trace.forEach((entry, i) => { + logs.push(...i === 0 ? [] : [` +`], ...formatTraceEntry(entry)); + }); + return logs; + } + function formatTraceEntry({ vnode, recurseCount }) { + const postfix = recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``; + const isRoot = vnode.component ? vnode.component.parent == null : false; + const open = ` at <${formatComponentName( + vnode.component, + vnode.type, + isRoot + )}`; + const close = `>` + postfix; + return vnode.props ? [open, ...formatProps(vnode.props), close] : [open + close]; + } + function formatProps(props) { + const res = []; + const keys = Object.keys(props); + keys.slice(0, 3).forEach((key) => { + res.push(...formatProp(key, props[key])); + }); + if (keys.length > 3) { + res.push(` ...`); + } + return res; + } + function formatProp(key, value, raw) { + if (isString(value)) { + value = JSON.stringify(value); + return raw ? value : [`${key}=${value}`]; + } else if (typeof value === "number" || typeof value === "boolean" || value == null) { + return raw ? value : [`${key}=${value}`]; + } else if (isRef(value)) { + value = formatProp(key, toRaw(value.value), true); + return raw ? value : [`${key}=Ref<`, value, `>`]; + } else if (isFunction(value)) { + return [`${key}=fn${value.name ? `<${value.name}>` : ``}`]; + } else { + value = toRaw(value); + return raw ? value : [`${key}=`, value]; + } + } + function assertNumber(val, type) { + if (val === void 0) { + return; + } else if (typeof val !== "number") { + warn$1(`${type} is not a valid number - got ${JSON.stringify(val)}.`); + } else if (isNaN(val)) { + warn$1(`${type} is NaN - the duration expression might be incorrect.`); + } + } + + const ErrorCodes = { + "SETUP_FUNCTION": 0, + "0": "SETUP_FUNCTION", + "RENDER_FUNCTION": 1, + "1": "RENDER_FUNCTION", + "NATIVE_EVENT_HANDLER": 5, + "5": "NATIVE_EVENT_HANDLER", + "COMPONENT_EVENT_HANDLER": 6, + "6": "COMPONENT_EVENT_HANDLER", + "VNODE_HOOK": 7, + "7": "VNODE_HOOK", + "DIRECTIVE_HOOK": 8, + "8": "DIRECTIVE_HOOK", + "TRANSITION_HOOK": 9, + "9": "TRANSITION_HOOK", + "APP_ERROR_HANDLER": 10, + "10": "APP_ERROR_HANDLER", + "APP_WARN_HANDLER": 11, + "11": "APP_WARN_HANDLER", + "FUNCTION_REF": 12, + "12": "FUNCTION_REF", + "ASYNC_COMPONENT_LOADER": 13, + "13": "ASYNC_COMPONENT_LOADER", + "SCHEDULER": 14, + "14": "SCHEDULER", + "COMPONENT_UPDATE": 15, + "15": "COMPONENT_UPDATE", + "APP_UNMOUNT_CLEANUP": 16, + "16": "APP_UNMOUNT_CLEANUP" + }; + const ErrorTypeStrings$1 = { + ["sp"]: "serverPrefetch hook", + ["bc"]: "beforeCreate hook", + ["c"]: "created hook", + ["bm"]: "beforeMount hook", + ["m"]: "mounted hook", + ["bu"]: "beforeUpdate hook", + ["u"]: "updated", + ["bum"]: "beforeUnmount hook", + ["um"]: "unmounted hook", + ["a"]: "activated hook", + ["da"]: "deactivated hook", + ["ec"]: "errorCaptured hook", + ["rtc"]: "renderTracked hook", + ["rtg"]: "renderTriggered hook", + [0]: "setup function", + [1]: "render function", + [2]: "watcher getter", + [3]: "watcher callback", + [4]: "watcher cleanup function", + [5]: "native event handler", + [6]: "component event handler", + [7]: "vnode hook", + [8]: "directive hook", + [9]: "transition hook", + [10]: "app errorHandler", + [11]: "app warnHandler", + [12]: "ref function", + [13]: "async component loader", + [14]: "scheduler flush", + [15]: "component update", + [16]: "app unmount cleanup function" + }; + function callWithErrorHandling(fn, instance, type, args) { + try { + return args ? fn(...args) : fn(); + } catch (err) { + handleError(err, instance, type); + } + } + function callWithAsyncErrorHandling(fn, instance, type, args) { + if (isFunction(fn)) { + const res = callWithErrorHandling(fn, instance, type, args); + if (res && isPromise(res)) { + res.catch((err) => { + handleError(err, instance, type); + }); + } + return res; + } + if (isArray(fn)) { + const values = []; + for (let i = 0; i < fn.length; i++) { + values.push(callWithAsyncErrorHandling(fn[i], instance, type, args)); + } + return values; + } else { + warn$1( + `Invalid value type passed to callWithAsyncErrorHandling(): ${typeof fn}` + ); + } + } + function handleError(err, instance, type, throwInDev = true) { + const contextVNode = instance ? instance.vnode : null; + const { errorHandler, throwUnhandledErrorInProduction } = instance && instance.appContext.config || EMPTY_OBJ; + if (instance) { + let cur = instance.parent; + const exposedInstance = instance.proxy; + const errorInfo = ErrorTypeStrings$1[type] ; + while (cur) { + const errorCapturedHooks = cur.ec; + if (errorCapturedHooks) { + for (let i = 0; i < errorCapturedHooks.length; i++) { + if (errorCapturedHooks[i](err, exposedInstance, errorInfo) === false) { + return; + } + } + } + cur = cur.parent; + } + if (errorHandler) { + pauseTracking(); + callWithErrorHandling(errorHandler, null, 10, [ + err, + exposedInstance, + errorInfo + ]); + resetTracking(); + return; + } + } + logError(err, type, contextVNode, throwInDev, throwUnhandledErrorInProduction); + } + function logError(err, type, contextVNode, throwInDev = true, throwInProd = false) { + { + const info = ErrorTypeStrings$1[type]; + if (contextVNode) { + pushWarningContext(contextVNode); + } + warn$1(`Unhandled error${info ? ` during execution of ${info}` : ``}`); + if (contextVNode) { + popWarningContext(); + } + if (throwInDev) { + throw err; + } else { + console.error(err); + } + } + } + + const queue = []; + let flushIndex = -1; + const pendingPostFlushCbs = []; + let activePostFlushCbs = null; + let postFlushIndex = 0; + const resolvedPromise = /* @__PURE__ */ Promise.resolve(); + let currentFlushPromise = null; + const RECURSION_LIMIT = 100; + function nextTick(fn) { + const p = currentFlushPromise || resolvedPromise; + return fn ? p.then(this ? fn.bind(this) : fn) : p; + } + function findInsertionIndex(id) { + let start = flushIndex + 1; + let end = queue.length; + while (start < end) { + const middle = start + end >>> 1; + const middleJob = queue[middle]; + const middleJobId = getId(middleJob); + if (middleJobId < id || middleJobId === id && middleJob.flags & 2) { + start = middle + 1; + } else { + end = middle; + } + } + return start; + } + function queueJob(job) { + if (!(job.flags & 1)) { + const jobId = getId(job); + const lastJob = queue[queue.length - 1]; + if (!lastJob || // fast path when the job id is larger than the tail + !(job.flags & 2) && jobId >= getId(lastJob)) { + queue.push(job); + } else { + queue.splice(findInsertionIndex(jobId), 0, job); + } + job.flags |= 1; + queueFlush(); + } + } + function queueFlush() { + if (!currentFlushPromise) { + currentFlushPromise = resolvedPromise.then(flushJobs); + } + } + function queuePostFlushCb(cb) { + if (!isArray(cb)) { + if (activePostFlushCbs && cb.id === -1) { + activePostFlushCbs.splice(postFlushIndex + 1, 0, cb); + } else if (!(cb.flags & 1)) { + pendingPostFlushCbs.push(cb); + cb.flags |= 1; + } + } else { + pendingPostFlushCbs.push(...cb); + } + queueFlush(); + } + function flushPreFlushCbs(instance, seen, i = flushIndex + 1) { + { + seen = seen || /* @__PURE__ */ new Map(); + } + for (; i < queue.length; i++) { + const cb = queue[i]; + if (cb && cb.flags & 2) { + if (instance && cb.id !== instance.uid) { + continue; + } + if (checkRecursiveUpdates(seen, cb)) { + continue; + } + queue.splice(i, 1); + i--; + if (cb.flags & 4) { + cb.flags &= -2; + } + cb(); + if (!(cb.flags & 4)) { + cb.flags &= -2; + } + } + } + } + function flushPostFlushCbs(seen) { + if (pendingPostFlushCbs.length) { + const deduped = [...new Set(pendingPostFlushCbs)].sort( + (a, b) => getId(a) - getId(b) + ); + pendingPostFlushCbs.length = 0; + if (activePostFlushCbs) { + activePostFlushCbs.push(...deduped); + return; + } + activePostFlushCbs = deduped; + { + seen = seen || /* @__PURE__ */ new Map(); + } + for (postFlushIndex = 0; postFlushIndex < activePostFlushCbs.length; postFlushIndex++) { + const cb = activePostFlushCbs[postFlushIndex]; + if (checkRecursiveUpdates(seen, cb)) { + continue; + } + if (cb.flags & 4) { + cb.flags &= -2; + } + if (!(cb.flags & 8)) cb(); + cb.flags &= -2; + } + activePostFlushCbs = null; + postFlushIndex = 0; + } + } + const getId = (job) => job.id == null ? job.flags & 2 ? -1 : Infinity : job.id; + function flushJobs(seen) { + { + seen = seen || /* @__PURE__ */ new Map(); + } + const check = (job) => checkRecursiveUpdates(seen, job) ; + try { + for (flushIndex = 0; flushIndex < queue.length; flushIndex++) { + const job = queue[flushIndex]; + if (job && !(job.flags & 8)) { + if (check(job)) { + continue; + } + if (job.flags & 4) { + job.flags &= ~1; + } + callWithErrorHandling( + job, + job.i, + job.i ? 15 : 14 + ); + if (!(job.flags & 4)) { + job.flags &= ~1; + } + } + } + } finally { + for (; flushIndex < queue.length; flushIndex++) { + const job = queue[flushIndex]; + if (job) { + job.flags &= -2; + } + } + flushIndex = -1; + queue.length = 0; + flushPostFlushCbs(seen); + currentFlushPromise = null; + if (queue.length || pendingPostFlushCbs.length) { + flushJobs(seen); + } + } + } + function checkRecursiveUpdates(seen, fn) { + const count = seen.get(fn) || 0; + if (count > RECURSION_LIMIT) { + const instance = fn.i; + const componentName = instance && getComponentName(instance.type); + handleError( + `Maximum recursive updates exceeded${componentName ? ` in component <${componentName}>` : ``}. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.`, + null, + 10 + ); + return true; + } + seen.set(fn, count + 1); + return false; + } + + let isHmrUpdating = false; + const hmrDirtyComponents = /* @__PURE__ */ new Map(); + { + getGlobalThis().__VUE_HMR_RUNTIME__ = { + createRecord: tryWrap(createRecord), + rerender: tryWrap(rerender), + reload: tryWrap(reload) + }; + } + const map = /* @__PURE__ */ new Map(); + function registerHMR(instance) { + const id = instance.type.__hmrId; + let record = map.get(id); + if (!record) { + createRecord(id, instance.type); + record = map.get(id); + } + record.instances.add(instance); + } + function unregisterHMR(instance) { + map.get(instance.type.__hmrId).instances.delete(instance); + } + function createRecord(id, initialDef) { + if (map.has(id)) { + return false; + } + map.set(id, { + initialDef: normalizeClassComponent(initialDef), + instances: /* @__PURE__ */ new Set() + }); + return true; + } + function normalizeClassComponent(component) { + return isClassComponent(component) ? component.__vccOpts : component; + } + function rerender(id, newRender) { + const record = map.get(id); + if (!record) { + return; + } + record.initialDef.render = newRender; + [...record.instances].forEach((instance) => { + if (newRender) { + instance.render = newRender; + normalizeClassComponent(instance.type).render = newRender; + } + instance.renderCache = []; + isHmrUpdating = true; + if (!(instance.job.flags & 8)) { + instance.update(); + } + isHmrUpdating = false; + }); + } + function reload(id, newComp) { + const record = map.get(id); + if (!record) return; + newComp = normalizeClassComponent(newComp); + updateComponentDef(record.initialDef, newComp); + const instances = [...record.instances]; + for (let i = 0; i < instances.length; i++) { + const instance = instances[i]; + const oldComp = normalizeClassComponent(instance.type); + let dirtyInstances = hmrDirtyComponents.get(oldComp); + if (!dirtyInstances) { + if (oldComp !== record.initialDef) { + updateComponentDef(oldComp, newComp); + } + hmrDirtyComponents.set(oldComp, dirtyInstances = /* @__PURE__ */ new Set()); + } + dirtyInstances.add(instance); + instance.appContext.propsCache.delete(instance.type); + instance.appContext.emitsCache.delete(instance.type); + instance.appContext.optionsCache.delete(instance.type); + if (instance.ceReload) { + dirtyInstances.add(instance); + instance.ceReload(newComp.styles); + dirtyInstances.delete(instance); + } else if (instance.parent) { + queueJob(() => { + if (!(instance.job.flags & 8)) { + isHmrUpdating = true; + instance.parent.update(); + isHmrUpdating = false; + dirtyInstances.delete(instance); + } + }); + } else if (instance.appContext.reload) { + instance.appContext.reload(); + } else if (typeof window !== "undefined") { + window.location.reload(); + } else { + console.warn( + "[HMR] Root or manually mounted instance modified. Full reload required." + ); + } + if (instance.root.ce && instance !== instance.root) { + instance.root.ce._removeChildStyle(oldComp); + } + } + queuePostFlushCb(() => { + hmrDirtyComponents.clear(); + }); + } + function updateComponentDef(oldComp, newComp) { + extend(oldComp, newComp); + for (const key in oldComp) { + if (key !== "__file" && !(key in newComp)) { + delete oldComp[key]; + } + } + } + function tryWrap(fn) { + return (id, arg) => { + try { + return fn(id, arg); + } catch (e) { + console.error(e); + console.warn( + `[HMR] Something went wrong during Vue component hot-reload. Full reload required.` + ); + } + }; + } + + let devtools$1; + let buffer = []; + let devtoolsNotInstalled = false; + function emit$1(event, ...args) { + if (devtools$1) { + devtools$1.emit(event, ...args); + } else if (!devtoolsNotInstalled) { + buffer.push({ event, args }); + } + } + function setDevtoolsHook$1(hook, target) { + var _a, _b; + devtools$1 = hook; + if (devtools$1) { + devtools$1.enabled = true; + buffer.forEach(({ event, args }) => devtools$1.emit(event, ...args)); + buffer = []; + } else if ( + // handle late devtools injection - only do this if we are in an actual + // browser environment to avoid the timer handle stalling test runner exit + // (#4815) + typeof window !== "undefined" && // some envs mock window but not fully + window.HTMLElement && // also exclude jsdom + // eslint-disable-next-line no-restricted-syntax + !((_b = (_a = window.navigator) == null ? void 0 : _a.userAgent) == null ? void 0 : _b.includes("jsdom")) + ) { + const replay = target.__VUE_DEVTOOLS_HOOK_REPLAY__ = target.__VUE_DEVTOOLS_HOOK_REPLAY__ || []; + replay.push((newHook) => { + setDevtoolsHook$1(newHook, target); + }); + setTimeout(() => { + if (!devtools$1) { + target.__VUE_DEVTOOLS_HOOK_REPLAY__ = null; + devtoolsNotInstalled = true; + buffer = []; + } + }, 3e3); + } else { + devtoolsNotInstalled = true; + buffer = []; + } + } + function devtoolsInitApp(app, version) { + emit$1("app:init" /* APP_INIT */, app, version, { + Fragment, + Text, + Comment, + Static + }); + } + function devtoolsUnmountApp(app) { + emit$1("app:unmount" /* APP_UNMOUNT */, app); + } + const devtoolsComponentAdded = /* @__PURE__ */ createDevtoolsComponentHook("component:added" /* COMPONENT_ADDED */); + const devtoolsComponentUpdated = /* @__PURE__ */ createDevtoolsComponentHook("component:updated" /* COMPONENT_UPDATED */); + const _devtoolsComponentRemoved = /* @__PURE__ */ createDevtoolsComponentHook( + "component:removed" /* COMPONENT_REMOVED */ + ); + const devtoolsComponentRemoved = (component) => { + if (devtools$1 && typeof devtools$1.cleanupBuffer === "function" && // remove the component if it wasn't buffered + !devtools$1.cleanupBuffer(component)) { + _devtoolsComponentRemoved(component); + } + }; + // @__NO_SIDE_EFFECTS__ + function createDevtoolsComponentHook(hook) { + return (component) => { + emit$1( + hook, + component.appContext.app, + component.uid, + component.parent ? component.parent.uid : void 0, + component + ); + }; + } + const devtoolsPerfStart = /* @__PURE__ */ createDevtoolsPerformanceHook("perf:start" /* PERFORMANCE_START */); + const devtoolsPerfEnd = /* @__PURE__ */ createDevtoolsPerformanceHook("perf:end" /* PERFORMANCE_END */); + function createDevtoolsPerformanceHook(hook) { + return (component, type, time) => { + emit$1(hook, component.appContext.app, component.uid, component, type, time); + }; + } + function devtoolsComponentEmit(component, event, params) { + emit$1( + "component:emit" /* COMPONENT_EMIT */, + component.appContext.app, + component, + event, + params + ); + } + + let currentRenderingInstance = null; + let currentScopeId = null; + function setCurrentRenderingInstance(instance) { + const prev = currentRenderingInstance; + currentRenderingInstance = instance; + currentScopeId = instance && instance.type.__scopeId || null; + return prev; + } + function pushScopeId(id) { + currentScopeId = id; + } + function popScopeId() { + currentScopeId = null; + } + const withScopeId = (_id) => withCtx; + function withCtx(fn, ctx = currentRenderingInstance, isNonScopedSlot) { + if (!ctx) return fn; + if (fn._n) { + return fn; + } + const renderFnWithContext = (...args) => { + if (renderFnWithContext._d) { + setBlockTracking(-1); + } + const prevInstance = setCurrentRenderingInstance(ctx); + let res; + try { + res = fn(...args); + } finally { + setCurrentRenderingInstance(prevInstance); + if (renderFnWithContext._d) { + setBlockTracking(1); + } + } + { + devtoolsComponentUpdated(ctx); + } + return res; + }; + renderFnWithContext._n = true; + renderFnWithContext._c = true; + renderFnWithContext._d = true; + return renderFnWithContext; + } + + function validateDirectiveName(name) { + if (isBuiltInDirective(name)) { + warn$1("Do not use built-in directive ids as custom directive id: " + name); + } + } + function withDirectives(vnode, directives) { + if (currentRenderingInstance === null) { + warn$1(`withDirectives can only be used inside render functions.`); + return vnode; + } + const instance = getComponentPublicInstance(currentRenderingInstance); + const bindings = vnode.dirs || (vnode.dirs = []); + for (let i = 0; i < directives.length; i++) { + let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i]; + if (dir) { + if (isFunction(dir)) { + dir = { + mounted: dir, + updated: dir + }; + } + if (dir.deep) { + traverse(value); + } + bindings.push({ + dir, + instance, + value, + oldValue: void 0, + arg, + modifiers + }); + } + } + return vnode; + } + function invokeDirectiveHook(vnode, prevVNode, instance, name) { + const bindings = vnode.dirs; + const oldBindings = prevVNode && prevVNode.dirs; + for (let i = 0; i < bindings.length; i++) { + const binding = bindings[i]; + if (oldBindings) { + binding.oldValue = oldBindings[i].value; + } + let hook = binding.dir[name]; + if (hook) { + pauseTracking(); + callWithAsyncErrorHandling(hook, instance, 8, [ + vnode.el, + binding, + vnode, + prevVNode + ]); + resetTracking(); + } + } + } + + const TeleportEndKey = Symbol("_vte"); + const isTeleport = (type) => type.__isTeleport; + const isTeleportDisabled = (props) => props && (props.disabled || props.disabled === ""); + const isTeleportDeferred = (props) => props && (props.defer || props.defer === ""); + const isTargetSVG = (target) => typeof SVGElement !== "undefined" && target instanceof SVGElement; + const isTargetMathML = (target) => typeof MathMLElement === "function" && target instanceof MathMLElement; + const resolveTarget = (props, select) => { + const targetSelector = props && props.to; + if (isString(targetSelector)) { + if (!select) { + warn$1( + `Current renderer does not support string target for Teleports. (missing querySelector renderer option)` + ); + return null; + } else { + const target = select(targetSelector); + if (!target && !isTeleportDisabled(props)) { + warn$1( + `Failed to locate Teleport target with selector "${targetSelector}". Note the target element must exist before the component is mounted - i.e. the target cannot be rendered by the component itself, and ideally should be outside of the entire Vue component tree.` + ); + } + return target; + } + } else { + if (!targetSelector && !isTeleportDisabled(props)) { + warn$1(`Invalid Teleport target: ${targetSelector}`); + } + return targetSelector; + } + }; + const TeleportImpl = { + name: "Teleport", + __isTeleport: true, + process(n1, n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, internals) { + const { + mc: mountChildren, + pc: patchChildren, + pbc: patchBlockChildren, + o: { insert, querySelector, createText, createComment } + } = internals; + const disabled = isTeleportDisabled(n2.props); + let { shapeFlag, children, dynamicChildren } = n2; + if (isHmrUpdating) { + optimized = false; + dynamicChildren = null; + } + if (n1 == null) { + const placeholder = n2.el = createComment("teleport start") ; + const mainAnchor = n2.anchor = createComment("teleport end") ; + insert(placeholder, container, anchor); + insert(mainAnchor, container, anchor); + const mount = (container2, anchor2) => { + if (shapeFlag & 16) { + mountChildren( + children, + container2, + anchor2, + parentComponent, + parentSuspense, + namespace, + slotScopeIds, + optimized + ); + } + }; + const mountToTarget = () => { + const target = n2.target = resolveTarget(n2.props, querySelector); + const targetAnchor = prepareAnchor(target, n2, createText, insert); + if (target) { + if (namespace !== "svg" && isTargetSVG(target)) { + namespace = "svg"; + } else if (namespace !== "mathml" && isTargetMathML(target)) { + namespace = "mathml"; + } + if (parentComponent && parentComponent.isCE) { + (parentComponent.ce._teleportTargets || (parentComponent.ce._teleportTargets = /* @__PURE__ */ new Set())).add(target); + } + if (!disabled) { + mount(target, targetAnchor); + updateCssVars(n2, false); + } + } else if (!disabled) { + warn$1( + "Invalid Teleport target on mount:", + target, + `(${typeof target})` + ); + } + }; + if (disabled) { + mount(container, mainAnchor); + updateCssVars(n2, true); + } + if (isTeleportDeferred(n2.props)) { + n2.el.__isMounted = false; + queuePostRenderEffect(() => { + mountToTarget(); + delete n2.el.__isMounted; + }, parentSuspense); + } else { + mountToTarget(); + } + } else { + if (isTeleportDeferred(n2.props) && n1.el.__isMounted === false) { + queuePostRenderEffect(() => { + TeleportImpl.process( + n1, + n2, + container, + anchor, + parentComponent, + parentSuspense, + namespace, + slotScopeIds, + optimized, + internals + ); + }, parentSuspense); + return; + } + n2.el = n1.el; + n2.targetStart = n1.targetStart; + const mainAnchor = n2.anchor = n1.anchor; + const target = n2.target = n1.target; + const targetAnchor = n2.targetAnchor = n1.targetAnchor; + const wasDisabled = isTeleportDisabled(n1.props); + const currentContainer = wasDisabled ? container : target; + const currentAnchor = wasDisabled ? mainAnchor : targetAnchor; + if (namespace === "svg" || isTargetSVG(target)) { + namespace = "svg"; + } else if (namespace === "mathml" || isTargetMathML(target)) { + namespace = "mathml"; + } + if (dynamicChildren) { + patchBlockChildren( + n1.dynamicChildren, + dynamicChildren, + currentContainer, + parentComponent, + parentSuspense, + namespace, + slotScopeIds + ); + traverseStaticChildren(n1, n2, false); + } else if (!optimized) { + patchChildren( + n1, + n2, + currentContainer, + currentAnchor, + parentComponent, + parentSuspense, + namespace, + slotScopeIds, + false + ); + } + if (disabled) { + if (!wasDisabled) { + moveTeleport( + n2, + container, + mainAnchor, + internals, + 1 + ); + } else { + if (n2.props && n1.props && n2.props.to !== n1.props.to) { + n2.props.to = n1.props.to; + } + } + } else { + if ((n2.props && n2.props.to) !== (n1.props && n1.props.to)) { + const nextTarget = n2.target = resolveTarget( + n2.props, + querySelector + ); + if (nextTarget) { + moveTeleport( + n2, + nextTarget, + null, + internals, + 0 + ); + } else { + warn$1( + "Invalid Teleport target on update:", + target, + `(${typeof target})` + ); + } + } else if (wasDisabled) { + moveTeleport( + n2, + target, + targetAnchor, + internals, + 1 + ); + } + } + updateCssVars(n2, disabled); + } + }, + remove(vnode, parentComponent, parentSuspense, { um: unmount, o: { remove: hostRemove } }, doRemove) { + const { + shapeFlag, + children, + anchor, + targetStart, + targetAnchor, + target, + props + } = vnode; + if (target) { + hostRemove(targetStart); + hostRemove(targetAnchor); + } + doRemove && hostRemove(anchor); + if (shapeFlag & 16) { + const shouldRemove = doRemove || !isTeleportDisabled(props); + for (let i = 0; i < children.length; i++) { + const child = children[i]; + unmount( + child, + parentComponent, + parentSuspense, + shouldRemove, + !!child.dynamicChildren + ); + } + } + }, + move: moveTeleport, + hydrate: hydrateTeleport + }; + function moveTeleport(vnode, container, parentAnchor, { o: { insert }, m: move }, moveType = 2) { + if (moveType === 0) { + insert(vnode.targetAnchor, container, parentAnchor); + } + const { el, anchor, shapeFlag, children, props } = vnode; + const isReorder = moveType === 2; + if (isReorder) { + insert(el, container, parentAnchor); + } + if (!isReorder || isTeleportDisabled(props)) { + if (shapeFlag & 16) { + for (let i = 0; i < children.length; i++) { + move( + children[i], + container, + parentAnchor, + 2 + ); + } + } + } + if (isReorder) { + insert(anchor, container, parentAnchor); + } + } + function hydrateTeleport(node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized, { + o: { nextSibling, parentNode, querySelector, insert, createText } + }, hydrateChildren) { + function hydrateDisabledTeleport(node2, vnode2, targetStart, targetAnchor) { + vnode2.anchor = hydrateChildren( + nextSibling(node2), + vnode2, + parentNode(node2), + parentComponent, + parentSuspense, + slotScopeIds, + optimized + ); + vnode2.targetStart = targetStart; + vnode2.targetAnchor = targetAnchor; + } + const target = vnode.target = resolveTarget( + vnode.props, + querySelector + ); + const disabled = isTeleportDisabled(vnode.props); + if (target) { + const targetNode = target._lpa || target.firstChild; + if (vnode.shapeFlag & 16) { + if (disabled) { + hydrateDisabledTeleport( + node, + vnode, + targetNode, + targetNode && nextSibling(targetNode) + ); + } else { + vnode.anchor = nextSibling(node); + let targetAnchor = targetNode; + while (targetAnchor) { + if (targetAnchor && targetAnchor.nodeType === 8) { + if (targetAnchor.data === "teleport start anchor") { + vnode.targetStart = targetAnchor; + } else if (targetAnchor.data === "teleport anchor") { + vnode.targetAnchor = targetAnchor; + target._lpa = vnode.targetAnchor && nextSibling(vnode.targetAnchor); + break; + } + } + targetAnchor = nextSibling(targetAnchor); + } + if (!vnode.targetAnchor) { + prepareAnchor(target, vnode, createText, insert); + } + hydrateChildren( + targetNode && nextSibling(targetNode), + vnode, + target, + parentComponent, + parentSuspense, + slotScopeIds, + optimized + ); + } + } + updateCssVars(vnode, disabled); + } else if (disabled) { + if (vnode.shapeFlag & 16) { + hydrateDisabledTeleport(node, vnode, node, nextSibling(node)); + } + } + return vnode.anchor && nextSibling(vnode.anchor); + } + const Teleport = TeleportImpl; + function updateCssVars(vnode, isDisabled) { + const ctx = vnode.ctx; + if (ctx && ctx.ut) { + let node, anchor; + if (isDisabled) { + node = vnode.el; + anchor = vnode.anchor; + } else { + node = vnode.targetStart; + anchor = vnode.targetAnchor; + } + while (node && node !== anchor) { + if (node.nodeType === 1) node.setAttribute("data-v-owner", ctx.uid); + node = node.nextSibling; + } + ctx.ut(); + } + } + function prepareAnchor(target, vnode, createText, insert) { + const targetStart = vnode.targetStart = createText(""); + const targetAnchor = vnode.targetAnchor = createText(""); + targetStart[TeleportEndKey] = targetAnchor; + if (target) { + insert(targetStart, target); + insert(targetAnchor, target); + } + return targetAnchor; + } + + const leaveCbKey = Symbol("_leaveCb"); + const enterCbKey$1 = Symbol("_enterCb"); + function useTransitionState() { + const state = { + isMounted: false, + isLeaving: false, + isUnmounting: false, + leavingVNodes: /* @__PURE__ */ new Map() + }; + onMounted(() => { + state.isMounted = true; + }); + onBeforeUnmount(() => { + state.isUnmounting = true; + }); + return state; + } + const TransitionHookValidator = [Function, Array]; + const BaseTransitionPropsValidators = { + mode: String, + appear: Boolean, + persisted: Boolean, + // enter + onBeforeEnter: TransitionHookValidator, + onEnter: TransitionHookValidator, + onAfterEnter: TransitionHookValidator, + onEnterCancelled: TransitionHookValidator, + // leave + onBeforeLeave: TransitionHookValidator, + onLeave: TransitionHookValidator, + onAfterLeave: TransitionHookValidator, + onLeaveCancelled: TransitionHookValidator, + // appear + onBeforeAppear: TransitionHookValidator, + onAppear: TransitionHookValidator, + onAfterAppear: TransitionHookValidator, + onAppearCancelled: TransitionHookValidator + }; + const recursiveGetSubtree = (instance) => { + const subTree = instance.subTree; + return subTree.component ? recursiveGetSubtree(subTree.component) : subTree; + }; + const BaseTransitionImpl = { + name: `BaseTransition`, + props: BaseTransitionPropsValidators, + setup(props, { slots }) { + const instance = getCurrentInstance(); + const state = useTransitionState(); + return () => { + const children = slots.default && getTransitionRawChildren(slots.default(), true); + if (!children || !children.length) { + return; + } + const child = findNonCommentChild(children); + const rawProps = toRaw(props); + const { mode } = rawProps; + if (mode && mode !== "in-out" && mode !== "out-in" && mode !== "default") { + warn$1(`invalid mode: ${mode}`); + } + if (state.isLeaving) { + return emptyPlaceholder(child); + } + const innerChild = getInnerChild$1(child); + if (!innerChild) { + return emptyPlaceholder(child); + } + let enterHooks = resolveTransitionHooks( + innerChild, + rawProps, + state, + instance, + // #11061, ensure enterHooks is fresh after clone + (hooks) => enterHooks = hooks + ); + if (innerChild.type !== Comment) { + setTransitionHooks(innerChild, enterHooks); + } + let oldInnerChild = instance.subTree && getInnerChild$1(instance.subTree); + if (oldInnerChild && oldInnerChild.type !== Comment && !isSameVNodeType(oldInnerChild, innerChild) && recursiveGetSubtree(instance).type !== Comment) { + let leavingHooks = resolveTransitionHooks( + oldInnerChild, + rawProps, + state, + instance + ); + setTransitionHooks(oldInnerChild, leavingHooks); + if (mode === "out-in" && innerChild.type !== Comment) { + state.isLeaving = true; + leavingHooks.afterLeave = () => { + state.isLeaving = false; + if (!(instance.job.flags & 8)) { + instance.update(); + } + delete leavingHooks.afterLeave; + oldInnerChild = void 0; + }; + return emptyPlaceholder(child); + } else if (mode === "in-out" && innerChild.type !== Comment) { + leavingHooks.delayLeave = (el, earlyRemove, delayedLeave) => { + const leavingVNodesCache = getLeavingNodesForType( + state, + oldInnerChild + ); + leavingVNodesCache[String(oldInnerChild.key)] = oldInnerChild; + el[leaveCbKey] = () => { + earlyRemove(); + el[leaveCbKey] = void 0; + delete enterHooks.delayedLeave; + oldInnerChild = void 0; + }; + enterHooks.delayedLeave = () => { + delayedLeave(); + delete enterHooks.delayedLeave; + oldInnerChild = void 0; + }; + }; + } else { + oldInnerChild = void 0; + } + } else if (oldInnerChild) { + oldInnerChild = void 0; + } + return child; + }; + } + }; + function findNonCommentChild(children) { + let child = children[0]; + if (children.length > 1) { + let hasFound = false; + for (const c of children) { + if (c.type !== Comment) { + if (hasFound) { + warn$1( + " can only be used on a single element or component. Use for lists." + ); + break; + } + child = c; + hasFound = true; + } + } + } + return child; + } + const BaseTransition = BaseTransitionImpl; + function getLeavingNodesForType(state, vnode) { + const { leavingVNodes } = state; + let leavingVNodesCache = leavingVNodes.get(vnode.type); + if (!leavingVNodesCache) { + leavingVNodesCache = /* @__PURE__ */ Object.create(null); + leavingVNodes.set(vnode.type, leavingVNodesCache); + } + return leavingVNodesCache; + } + function resolveTransitionHooks(vnode, props, state, instance, postClone) { + const { + appear, + mode, + persisted = false, + onBeforeEnter, + onEnter, + onAfterEnter, + onEnterCancelled, + onBeforeLeave, + onLeave, + onAfterLeave, + onLeaveCancelled, + onBeforeAppear, + onAppear, + onAfterAppear, + onAppearCancelled + } = props; + const key = String(vnode.key); + const leavingVNodesCache = getLeavingNodesForType(state, vnode); + const callHook = (hook, args) => { + hook && callWithAsyncErrorHandling( + hook, + instance, + 9, + args + ); + }; + const callAsyncHook = (hook, args) => { + const done = args[1]; + callHook(hook, args); + if (isArray(hook)) { + if (hook.every((hook2) => hook2.length <= 1)) done(); + } else if (hook.length <= 1) { + done(); + } + }; + const hooks = { + mode, + persisted, + beforeEnter(el) { + let hook = onBeforeEnter; + if (!state.isMounted) { + if (appear) { + hook = onBeforeAppear || onBeforeEnter; + } else { + return; + } + } + if (el[leaveCbKey]) { + el[leaveCbKey]( + true + /* cancelled */ + ); + } + const leavingVNode = leavingVNodesCache[key]; + if (leavingVNode && isSameVNodeType(vnode, leavingVNode) && leavingVNode.el[leaveCbKey]) { + leavingVNode.el[leaveCbKey](); + } + callHook(hook, [el]); + }, + enter(el) { + let hook = onEnter; + let afterHook = onAfterEnter; + let cancelHook = onEnterCancelled; + if (!state.isMounted) { + if (appear) { + hook = onAppear || onEnter; + afterHook = onAfterAppear || onAfterEnter; + cancelHook = onAppearCancelled || onEnterCancelled; + } else { + return; + } + } + let called = false; + const done = el[enterCbKey$1] = (cancelled) => { + if (called) return; + called = true; + if (cancelled) { + callHook(cancelHook, [el]); + } else { + callHook(afterHook, [el]); + } + if (hooks.delayedLeave) { + hooks.delayedLeave(); + } + el[enterCbKey$1] = void 0; + }; + if (hook) { + callAsyncHook(hook, [el, done]); + } else { + done(); + } + }, + leave(el, remove) { + const key2 = String(vnode.key); + if (el[enterCbKey$1]) { + el[enterCbKey$1]( + true + /* cancelled */ + ); + } + if (state.isUnmounting) { + return remove(); + } + callHook(onBeforeLeave, [el]); + let called = false; + const done = el[leaveCbKey] = (cancelled) => { + if (called) return; + called = true; + remove(); + if (cancelled) { + callHook(onLeaveCancelled, [el]); + } else { + callHook(onAfterLeave, [el]); + } + el[leaveCbKey] = void 0; + if (leavingVNodesCache[key2] === vnode) { + delete leavingVNodesCache[key2]; + } + }; + leavingVNodesCache[key2] = vnode; + if (onLeave) { + callAsyncHook(onLeave, [el, done]); + } else { + done(); + } + }, + clone(vnode2) { + const hooks2 = resolveTransitionHooks( + vnode2, + props, + state, + instance, + postClone + ); + if (postClone) postClone(hooks2); + return hooks2; + } + }; + return hooks; + } + function emptyPlaceholder(vnode) { + if (isKeepAlive(vnode)) { + vnode = cloneVNode(vnode); + vnode.children = null; + return vnode; + } + } + function getInnerChild$1(vnode) { + if (!isKeepAlive(vnode)) { + if (isTeleport(vnode.type) && vnode.children) { + return findNonCommentChild(vnode.children); + } + return vnode; + } + if (vnode.component) { + return vnode.component.subTree; + } + const { shapeFlag, children } = vnode; + if (children) { + if (shapeFlag & 16) { + return children[0]; + } + if (shapeFlag & 32 && isFunction(children.default)) { + return children.default(); + } + } + } + function setTransitionHooks(vnode, hooks) { + if (vnode.shapeFlag & 6 && vnode.component) { + vnode.transition = hooks; + setTransitionHooks(vnode.component.subTree, hooks); + } else if (vnode.shapeFlag & 128) { + vnode.ssContent.transition = hooks.clone(vnode.ssContent); + vnode.ssFallback.transition = hooks.clone(vnode.ssFallback); + } else { + vnode.transition = hooks; + } + } + function getTransitionRawChildren(children, keepComment = false, parentKey) { + let ret = []; + let keyedFragmentCount = 0; + for (let i = 0; i < children.length; i++) { + let child = children[i]; + const key = parentKey == null ? child.key : String(parentKey) + String(child.key != null ? child.key : i); + if (child.type === Fragment) { + if (child.patchFlag & 128) keyedFragmentCount++; + ret = ret.concat( + getTransitionRawChildren(child.children, keepComment, key) + ); + } else if (keepComment || child.type !== Comment) { + ret.push(key != null ? cloneVNode(child, { key }) : child); + } + } + if (keyedFragmentCount > 1) { + for (let i = 0; i < ret.length; i++) { + ret[i].patchFlag = -2; + } + } + return ret; + } + + // @__NO_SIDE_EFFECTS__ + function defineComponent(options, extraOptions) { + return isFunction(options) ? ( + // #8236: extend call and options.name access are considered side-effects + // by Rollup, so we have to wrap it in a pure-annotated IIFE. + /* @__PURE__ */ (() => extend({ name: options.name }, extraOptions, { setup: options }))() + ) : options; + } + + function useId() { + const i = getCurrentInstance(); + if (i) { + return (i.appContext.config.idPrefix || "v") + "-" + i.ids[0] + i.ids[1]++; + } else { + warn$1( + `useId() is called when there is no active component instance to be associated with.` + ); + } + return ""; + } + function markAsyncBoundary(instance) { + instance.ids = [instance.ids[0] + instance.ids[2]++ + "-", 0, 0]; + } + + const knownTemplateRefs = /* @__PURE__ */ new WeakSet(); + function useTemplateRef(key) { + const i = getCurrentInstance(); + const r = shallowRef(null); + if (i) { + const refs = i.refs === EMPTY_OBJ ? i.refs = {} : i.refs; + let desc; + if ((desc = Object.getOwnPropertyDescriptor(refs, key)) && !desc.configurable) { + warn$1(`useTemplateRef('${key}') already exists.`); + } else { + Object.defineProperty(refs, key, { + enumerable: true, + get: () => r.value, + set: (val) => r.value = val + }); + } + } else { + warn$1( + `useTemplateRef() is called when there is no active component instance to be associated with.` + ); + } + const ret = readonly(r) ; + { + knownTemplateRefs.add(ret); + } + return ret; + } + + const pendingSetRefMap = /* @__PURE__ */ new WeakMap(); + function setRef(rawRef, oldRawRef, parentSuspense, vnode, isUnmount = false) { + if (isArray(rawRef)) { + rawRef.forEach( + (r, i) => setRef( + r, + oldRawRef && (isArray(oldRawRef) ? oldRawRef[i] : oldRawRef), + parentSuspense, + vnode, + isUnmount + ) + ); + return; + } + if (isAsyncWrapper(vnode) && !isUnmount) { + if (vnode.shapeFlag & 512 && vnode.type.__asyncResolved && vnode.component.subTree.component) { + setRef(rawRef, oldRawRef, parentSuspense, vnode.component.subTree); + } + return; + } + const refValue = vnode.shapeFlag & 4 ? getComponentPublicInstance(vnode.component) : vnode.el; + const value = isUnmount ? null : refValue; + const { i: owner, r: ref } = rawRef; + if (!owner) { + warn$1( + `Missing ref owner context. ref cannot be used on hoisted vnodes. A vnode with ref must be created inside the render function.` + ); + return; + } + const oldRef = oldRawRef && oldRawRef.r; + const refs = owner.refs === EMPTY_OBJ ? owner.refs = {} : owner.refs; + const setupState = owner.setupState; + const rawSetupState = toRaw(setupState); + const canSetSetupRef = setupState === EMPTY_OBJ ? NO : (key) => { + { + if (hasOwn(rawSetupState, key) && !isRef(rawSetupState[key])) { + warn$1( + `Template ref "${key}" used on a non-ref value. It will not work in the production build.` + ); + } + if (knownTemplateRefs.has(rawSetupState[key])) { + return false; + } + } + return hasOwn(rawSetupState, key); + }; + const canSetRef = (ref2) => { + return !knownTemplateRefs.has(ref2); + }; + if (oldRef != null && oldRef !== ref) { + invalidatePendingSetRef(oldRawRef); + if (isString(oldRef)) { + refs[oldRef] = null; + if (canSetSetupRef(oldRef)) { + setupState[oldRef] = null; + } + } else if (isRef(oldRef)) { + if (canSetRef(oldRef)) { + oldRef.value = null; + } + const oldRawRefAtom = oldRawRef; + if (oldRawRefAtom.k) refs[oldRawRefAtom.k] = null; + } + } + if (isFunction(ref)) { + callWithErrorHandling(ref, owner, 12, [value, refs]); + } else { + const _isString = isString(ref); + const _isRef = isRef(ref); + if (_isString || _isRef) { + const doSet = () => { + if (rawRef.f) { + const existing = _isString ? canSetSetupRef(ref) ? setupState[ref] : refs[ref] : canSetRef(ref) || !rawRef.k ? ref.value : refs[rawRef.k]; + if (isUnmount) { + isArray(existing) && remove(existing, refValue); + } else { + if (!isArray(existing)) { + if (_isString) { + refs[ref] = [refValue]; + if (canSetSetupRef(ref)) { + setupState[ref] = refs[ref]; + } + } else { + const newVal = [refValue]; + if (canSetRef(ref)) { + ref.value = newVal; + } + if (rawRef.k) refs[rawRef.k] = newVal; + } + } else if (!existing.includes(refValue)) { + existing.push(refValue); + } + } + } else if (_isString) { + refs[ref] = value; + if (canSetSetupRef(ref)) { + setupState[ref] = value; + } + } else if (_isRef) { + if (canSetRef(ref)) { + ref.value = value; + } + if (rawRef.k) refs[rawRef.k] = value; + } else { + warn$1("Invalid template ref type:", ref, `(${typeof ref})`); + } + }; + if (value) { + const job = () => { + doSet(); + pendingSetRefMap.delete(rawRef); + }; + job.id = -1; + pendingSetRefMap.set(rawRef, job); + queuePostRenderEffect(job, parentSuspense); + } else { + invalidatePendingSetRef(rawRef); + doSet(); + } + } else { + warn$1("Invalid template ref type:", ref, `(${typeof ref})`); + } + } + } + function invalidatePendingSetRef(rawRef) { + const pendingSetRef = pendingSetRefMap.get(rawRef); + if (pendingSetRef) { + pendingSetRef.flags |= 8; + pendingSetRefMap.delete(rawRef); + } + } + + let hasLoggedMismatchError = false; + const logMismatchError = () => { + if (hasLoggedMismatchError) { + return; + } + console.error("Hydration completed but contains mismatches."); + hasLoggedMismatchError = true; + }; + const isSVGContainer = (container) => container.namespaceURI.includes("svg") && container.tagName !== "foreignObject"; + const isMathMLContainer = (container) => container.namespaceURI.includes("MathML"); + const getContainerType = (container) => { + if (container.nodeType !== 1) return void 0; + if (isSVGContainer(container)) return "svg"; + if (isMathMLContainer(container)) return "mathml"; + return void 0; + }; + const isComment = (node) => node.nodeType === 8; + function createHydrationFunctions(rendererInternals) { + const { + mt: mountComponent, + p: patch, + o: { + patchProp, + createText, + nextSibling, + parentNode, + remove, + insert, + createComment + } + } = rendererInternals; + const hydrate = (vnode, container) => { + if (!container.hasChildNodes()) { + warn$1( + `Attempting to hydrate existing markup but container is empty. Performing full mount instead.` + ); + patch(null, vnode, container); + flushPostFlushCbs(); + container._vnode = vnode; + return; + } + hydrateNode(container.firstChild, vnode, null, null, null); + flushPostFlushCbs(); + container._vnode = vnode; + }; + const hydrateNode = (node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized = false) => { + optimized = optimized || !!vnode.dynamicChildren; + const isFragmentStart = isComment(node) && node.data === "["; + const onMismatch = () => handleMismatch( + node, + vnode, + parentComponent, + parentSuspense, + slotScopeIds, + isFragmentStart + ); + const { type, ref, shapeFlag, patchFlag } = vnode; + let domType = node.nodeType; + vnode.el = node; + { + def(node, "__vnode", vnode, true); + def(node, "__vueParentComponent", parentComponent, true); + } + if (patchFlag === -2) { + optimized = false; + vnode.dynamicChildren = null; + } + let nextNode = null; + switch (type) { + case Text: + if (domType !== 3) { + if (vnode.children === "") { + insert(vnode.el = createText(""), parentNode(node), node); + nextNode = node; + } else { + nextNode = onMismatch(); + } + } else { + if (node.data !== vnode.children) { + warn$1( + `Hydration text mismatch in`, + node.parentNode, + ` + - rendered on server: ${JSON.stringify( + node.data + )} + - expected on client: ${JSON.stringify(vnode.children)}` + ); + logMismatchError(); + node.data = vnode.children; + } + nextNode = nextSibling(node); + } + break; + case Comment: + if (isTemplateNode(node)) { + nextNode = nextSibling(node); + replaceNode( + vnode.el = node.content.firstChild, + node, + parentComponent + ); + } else if (domType !== 8 || isFragmentStart) { + nextNode = onMismatch(); + } else { + nextNode = nextSibling(node); + } + break; + case Static: + if (isFragmentStart) { + node = nextSibling(node); + domType = node.nodeType; + } + if (domType === 1 || domType === 3) { + nextNode = node; + const needToAdoptContent = !vnode.children.length; + for (let i = 0; i < vnode.staticCount; i++) { + if (needToAdoptContent) + vnode.children += nextNode.nodeType === 1 ? nextNode.outerHTML : nextNode.data; + if (i === vnode.staticCount - 1) { + vnode.anchor = nextNode; + } + nextNode = nextSibling(nextNode); + } + return isFragmentStart ? nextSibling(nextNode) : nextNode; + } else { + onMismatch(); + } + break; + case Fragment: + if (!isFragmentStart) { + nextNode = onMismatch(); + } else { + nextNode = hydrateFragment( + node, + vnode, + parentComponent, + parentSuspense, + slotScopeIds, + optimized + ); + } + break; + default: + if (shapeFlag & 1) { + if ((domType !== 1 || vnode.type.toLowerCase() !== node.tagName.toLowerCase()) && !isTemplateNode(node)) { + nextNode = onMismatch(); + } else { + nextNode = hydrateElement( + node, + vnode, + parentComponent, + parentSuspense, + slotScopeIds, + optimized + ); + } + } else if (shapeFlag & 6) { + vnode.slotScopeIds = slotScopeIds; + const container = parentNode(node); + if (isFragmentStart) { + nextNode = locateClosingAnchor(node); + } else if (isComment(node) && node.data === "teleport start") { + nextNode = locateClosingAnchor(node, node.data, "teleport end"); + } else { + nextNode = nextSibling(node); + } + mountComponent( + vnode, + container, + null, + parentComponent, + parentSuspense, + getContainerType(container), + optimized + ); + if (isAsyncWrapper(vnode) && !vnode.type.__asyncResolved) { + let subTree; + if (isFragmentStart) { + subTree = createVNode(Fragment); + subTree.anchor = nextNode ? nextNode.previousSibling : container.lastChild; + } else { + subTree = node.nodeType === 3 ? createTextVNode("") : createVNode("div"); + } + subTree.el = node; + vnode.component.subTree = subTree; + } + } else if (shapeFlag & 64) { + if (domType !== 8) { + nextNode = onMismatch(); + } else { + nextNode = vnode.type.hydrate( + node, + vnode, + parentComponent, + parentSuspense, + slotScopeIds, + optimized, + rendererInternals, + hydrateChildren + ); + } + } else if (shapeFlag & 128) { + nextNode = vnode.type.hydrate( + node, + vnode, + parentComponent, + parentSuspense, + getContainerType(parentNode(node)), + slotScopeIds, + optimized, + rendererInternals, + hydrateNode + ); + } else { + warn$1("Invalid HostVNode type:", type, `(${typeof type})`); + } + } + if (ref != null) { + setRef(ref, null, parentSuspense, vnode); + } + return nextNode; + }; + const hydrateElement = (el, vnode, parentComponent, parentSuspense, slotScopeIds, optimized) => { + optimized = optimized || !!vnode.dynamicChildren; + const { type, props, patchFlag, shapeFlag, dirs, transition } = vnode; + const forcePatch = type === "input" || type === "option"; + { + if (dirs) { + invokeDirectiveHook(vnode, null, parentComponent, "created"); + } + let needCallTransitionHooks = false; + if (isTemplateNode(el)) { + needCallTransitionHooks = needTransition( + null, + // no need check parentSuspense in hydration + transition + ) && parentComponent && parentComponent.vnode.props && parentComponent.vnode.props.appear; + const content = el.content.firstChild; + if (needCallTransitionHooks) { + const cls = content.getAttribute("class"); + if (cls) content.$cls = cls; + transition.beforeEnter(content); + } + replaceNode(content, el, parentComponent); + vnode.el = el = content; + } + if (shapeFlag & 16 && // skip if element has innerHTML / textContent + !(props && (props.innerHTML || props.textContent))) { + let next = hydrateChildren( + el.firstChild, + vnode, + el, + parentComponent, + parentSuspense, + slotScopeIds, + optimized + ); + let hasWarned = false; + while (next) { + if (!isMismatchAllowed(el, 1 /* CHILDREN */)) { + if (!hasWarned) { + warn$1( + `Hydration children mismatch on`, + el, + ` +Server rendered element contains more child nodes than client vdom.` + ); + hasWarned = true; + } + logMismatchError(); + } + const cur = next; + next = next.nextSibling; + remove(cur); + } + } else if (shapeFlag & 8) { + let clientText = vnode.children; + if (clientText[0] === "\n" && (el.tagName === "PRE" || el.tagName === "TEXTAREA")) { + clientText = clientText.slice(1); + } + const { textContent } = el; + if (textContent !== clientText && // innerHTML normalize \r\n or \r into a single \n in the DOM + textContent !== clientText.replace(/\r\n|\r/g, "\n")) { + if (!isMismatchAllowed(el, 0 /* TEXT */)) { + warn$1( + `Hydration text content mismatch on`, + el, + ` + - rendered on server: ${textContent} + - expected on client: ${clientText}` + ); + logMismatchError(); + } + el.textContent = vnode.children; + } + } + if (props) { + { + const isCustomElement = el.tagName.includes("-"); + for (const key in props) { + if (// #11189 skip if this node has directives that have created hooks + // as it could have mutated the DOM in any possible way + !(dirs && dirs.some((d) => d.dir.created)) && propHasMismatch(el, key, props[key], vnode, parentComponent)) { + logMismatchError(); + } + if (forcePatch && (key.endsWith("value") || key === "indeterminate") || isOn(key) && !isReservedProp(key) || // force hydrate v-bind with .prop modifiers + key[0] === "." || isCustomElement) { + patchProp(el, key, null, props[key], void 0, parentComponent); + } + } + } + } + let vnodeHooks; + if (vnodeHooks = props && props.onVnodeBeforeMount) { + invokeVNodeHook(vnodeHooks, parentComponent, vnode); + } + if (dirs) { + invokeDirectiveHook(vnode, null, parentComponent, "beforeMount"); + } + if ((vnodeHooks = props && props.onVnodeMounted) || dirs || needCallTransitionHooks) { + queueEffectWithSuspense(() => { + vnodeHooks && invokeVNodeHook(vnodeHooks, parentComponent, vnode); + needCallTransitionHooks && transition.enter(el); + dirs && invokeDirectiveHook(vnode, null, parentComponent, "mounted"); + }, parentSuspense); + } + } + return el.nextSibling; + }; + const hydrateChildren = (node, parentVNode, container, parentComponent, parentSuspense, slotScopeIds, optimized) => { + optimized = optimized || !!parentVNode.dynamicChildren; + const children = parentVNode.children; + const l = children.length; + let hasWarned = false; + for (let i = 0; i < l; i++) { + const vnode = optimized ? children[i] : children[i] = normalizeVNode(children[i]); + const isText = vnode.type === Text; + if (node) { + if (isText && !optimized) { + if (i + 1 < l && normalizeVNode(children[i + 1]).type === Text) { + insert( + createText( + node.data.slice(vnode.children.length) + ), + container, + nextSibling(node) + ); + node.data = vnode.children; + } + } + node = hydrateNode( + node, + vnode, + parentComponent, + parentSuspense, + slotScopeIds, + optimized + ); + } else if (isText && !vnode.children) { + insert(vnode.el = createText(""), container); + } else { + if (!isMismatchAllowed(container, 1 /* CHILDREN */)) { + if (!hasWarned) { + warn$1( + `Hydration children mismatch on`, + container, + ` +Server rendered element contains fewer child nodes than client vdom.` + ); + hasWarned = true; + } + logMismatchError(); + } + patch( + null, + vnode, + container, + null, + parentComponent, + parentSuspense, + getContainerType(container), + slotScopeIds + ); + } + } + return node; + }; + const hydrateFragment = (node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized) => { + const { slotScopeIds: fragmentSlotScopeIds } = vnode; + if (fragmentSlotScopeIds) { + slotScopeIds = slotScopeIds ? slotScopeIds.concat(fragmentSlotScopeIds) : fragmentSlotScopeIds; + } + const container = parentNode(node); + const next = hydrateChildren( + nextSibling(node), + vnode, + container, + parentComponent, + parentSuspense, + slotScopeIds, + optimized + ); + if (next && isComment(next) && next.data === "]") { + return nextSibling(vnode.anchor = next); + } else { + logMismatchError(); + insert(vnode.anchor = createComment(`]`), container, next); + return next; + } + }; + const handleMismatch = (node, vnode, parentComponent, parentSuspense, slotScopeIds, isFragment) => { + if (!isMismatchAllowed(node.parentElement, 1 /* CHILDREN */)) { + warn$1( + `Hydration node mismatch: +- rendered on server:`, + node, + node.nodeType === 3 ? `(text)` : isComment(node) && node.data === "[" ? `(start of fragment)` : ``, + ` +- expected on client:`, + vnode.type + ); + logMismatchError(); + } + vnode.el = null; + if (isFragment) { + const end = locateClosingAnchor(node); + while (true) { + const next2 = nextSibling(node); + if (next2 && next2 !== end) { + remove(next2); + } else { + break; + } + } + } + const next = nextSibling(node); + const container = parentNode(node); + remove(node); + patch( + null, + vnode, + container, + next, + parentComponent, + parentSuspense, + getContainerType(container), + slotScopeIds + ); + if (parentComponent) { + parentComponent.vnode.el = vnode.el; + updateHOCHostEl(parentComponent, vnode.el); + } + return next; + }; + const locateClosingAnchor = (node, open = "[", close = "]") => { + let match = 0; + while (node) { + node = nextSibling(node); + if (node && isComment(node)) { + if (node.data === open) match++; + if (node.data === close) { + if (match === 0) { + return nextSibling(node); + } else { + match--; + } + } + } + } + return node; + }; + const replaceNode = (newNode, oldNode, parentComponent) => { + const parentNode2 = oldNode.parentNode; + if (parentNode2) { + parentNode2.replaceChild(newNode, oldNode); + } + let parent = parentComponent; + while (parent) { + if (parent.vnode.el === oldNode) { + parent.vnode.el = parent.subTree.el = newNode; + } + parent = parent.parent; + } + }; + const isTemplateNode = (node) => { + return node.nodeType === 1 && node.tagName === "TEMPLATE"; + }; + return [hydrate, hydrateNode]; + } + function propHasMismatch(el, key, clientValue, vnode, instance) { + let mismatchType; + let mismatchKey; + let actual; + let expected; + if (key === "class") { + if (el.$cls) { + actual = el.$cls; + delete el.$cls; + } else { + actual = el.getAttribute("class"); + } + expected = normalizeClass(clientValue); + if (!isSetEqual(toClassSet(actual || ""), toClassSet(expected))) { + mismatchType = 2 /* CLASS */; + mismatchKey = `class`; + } + } else if (key === "style") { + actual = el.getAttribute("style") || ""; + expected = isString(clientValue) ? clientValue : stringifyStyle(normalizeStyle(clientValue)); + const actualMap = toStyleMap(actual); + const expectedMap = toStyleMap(expected); + if (vnode.dirs) { + for (const { dir, value } of vnode.dirs) { + if (dir.name === "show" && !value) { + expectedMap.set("display", "none"); + } + } + } + if (instance) { + resolveCssVars(instance, vnode, expectedMap); + } + if (!isMapEqual(actualMap, expectedMap)) { + mismatchType = 3 /* STYLE */; + mismatchKey = "style"; + } + } else if (el instanceof SVGElement && isKnownSvgAttr(key) || el instanceof HTMLElement && (isBooleanAttr(key) || isKnownHtmlAttr(key))) { + if (isBooleanAttr(key)) { + actual = el.hasAttribute(key); + expected = includeBooleanAttr(clientValue); + } else if (clientValue == null) { + actual = el.hasAttribute(key); + expected = false; + } else { + if (el.hasAttribute(key)) { + actual = el.getAttribute(key); + } else if (key === "value" && el.tagName === "TEXTAREA") { + actual = el.value; + } else { + actual = false; + } + expected = isRenderableAttrValue(clientValue) ? String(clientValue) : false; + } + if (actual !== expected) { + mismatchType = 4 /* ATTRIBUTE */; + mismatchKey = key; + } + } + if (mismatchType != null && !isMismatchAllowed(el, mismatchType)) { + const format = (v) => v === false ? `(not rendered)` : `${mismatchKey}="${v}"`; + const preSegment = `Hydration ${MismatchTypeString[mismatchType]} mismatch on`; + const postSegment = ` + - rendered on server: ${format(actual)} + - expected on client: ${format(expected)} + Note: this mismatch is check-only. The DOM will not be rectified in production due to performance overhead. + You should fix the source of the mismatch.`; + { + warn$1(preSegment, el, postSegment); + } + return true; + } + return false; + } + function toClassSet(str) { + return new Set(str.trim().split(/\s+/)); + } + function isSetEqual(a, b) { + if (a.size !== b.size) { + return false; + } + for (const s of a) { + if (!b.has(s)) { + return false; + } + } + return true; + } + function toStyleMap(str) { + const styleMap = /* @__PURE__ */ new Map(); + for (const item of str.split(";")) { + let [key, value] = item.split(":"); + key = key.trim(); + value = value && value.trim(); + if (key && value) { + styleMap.set(key, value); + } + } + return styleMap; + } + function isMapEqual(a, b) { + if (a.size !== b.size) { + return false; + } + for (const [key, value] of a) { + if (value !== b.get(key)) { + return false; + } + } + return true; + } + function resolveCssVars(instance, vnode, expectedMap) { + const root = instance.subTree; + if (instance.getCssVars && (vnode === root || root && root.type === Fragment && root.children.includes(vnode))) { + const cssVars = instance.getCssVars(); + for (const key in cssVars) { + const value = normalizeCssVarValue(cssVars[key]); + expectedMap.set(`--${getEscapedCssVarName(key)}`, value); + } + } + if (vnode === root && instance.parent) { + resolveCssVars(instance.parent, instance.vnode, expectedMap); + } + } + const allowMismatchAttr = "data-allow-mismatch"; + const MismatchTypeString = { + [0 /* TEXT */]: "text", + [1 /* CHILDREN */]: "children", + [2 /* CLASS */]: "class", + [3 /* STYLE */]: "style", + [4 /* ATTRIBUTE */]: "attribute" + }; + function isMismatchAllowed(el, allowedType) { + if (allowedType === 0 /* TEXT */ || allowedType === 1 /* CHILDREN */) { + while (el && !el.hasAttribute(allowMismatchAttr)) { + el = el.parentElement; + } + } + const allowedAttr = el && el.getAttribute(allowMismatchAttr); + if (allowedAttr == null) { + return false; + } else if (allowedAttr === "") { + return true; + } else { + const list = allowedAttr.split(","); + if (allowedType === 0 /* TEXT */ && list.includes("children")) { + return true; + } + return list.includes(MismatchTypeString[allowedType]); + } + } + + const requestIdleCallback = getGlobalThis().requestIdleCallback || ((cb) => setTimeout(cb, 1)); + const cancelIdleCallback = getGlobalThis().cancelIdleCallback || ((id) => clearTimeout(id)); + const hydrateOnIdle = (timeout = 1e4) => (hydrate) => { + const id = requestIdleCallback(hydrate, { timeout }); + return () => cancelIdleCallback(id); + }; + function elementIsVisibleInViewport(el) { + const { top, left, bottom, right } = el.getBoundingClientRect(); + const { innerHeight, innerWidth } = window; + return (top > 0 && top < innerHeight || bottom > 0 && bottom < innerHeight) && (left > 0 && left < innerWidth || right > 0 && right < innerWidth); + } + const hydrateOnVisible = (opts) => (hydrate, forEach) => { + const ob = new IntersectionObserver((entries) => { + for (const e of entries) { + if (!e.isIntersecting) continue; + ob.disconnect(); + hydrate(); + break; + } + }, opts); + forEach((el) => { + if (!(el instanceof Element)) return; + if (elementIsVisibleInViewport(el)) { + hydrate(); + ob.disconnect(); + return false; + } + ob.observe(el); + }); + return () => ob.disconnect(); + }; + const hydrateOnMediaQuery = (query) => (hydrate) => { + if (query) { + const mql = matchMedia(query); + if (mql.matches) { + hydrate(); + } else { + mql.addEventListener("change", hydrate, { once: true }); + return () => mql.removeEventListener("change", hydrate); + } + } + }; + const hydrateOnInteraction = (interactions = []) => (hydrate, forEach) => { + if (isString(interactions)) interactions = [interactions]; + let hasHydrated = false; + const doHydrate = (e) => { + if (!hasHydrated) { + hasHydrated = true; + teardown(); + hydrate(); + e.target.dispatchEvent(new e.constructor(e.type, e)); + } + }; + const teardown = () => { + forEach((el) => { + for (const i of interactions) { + el.removeEventListener(i, doHydrate); + } + }); + }; + forEach((el) => { + for (const i of interactions) { + el.addEventListener(i, doHydrate, { once: true }); + } + }); + return teardown; + }; + function forEachElement(node, cb) { + if (isComment(node) && node.data === "[") { + let depth = 1; + let next = node.nextSibling; + while (next) { + if (next.nodeType === 1) { + const result = cb(next); + if (result === false) { + break; + } + } else if (isComment(next)) { + if (next.data === "]") { + if (--depth === 0) break; + } else if (next.data === "[") { + depth++; + } + } + next = next.nextSibling; + } + } else { + cb(node); + } + } + + const isAsyncWrapper = (i) => !!i.type.__asyncLoader; + // @__NO_SIDE_EFFECTS__ + function defineAsyncComponent(source) { + if (isFunction(source)) { + source = { loader: source }; + } + const { + loader, + loadingComponent, + errorComponent, + delay = 200, + hydrate: hydrateStrategy, + timeout, + // undefined = never times out + suspensible = true, + onError: userOnError + } = source; + let pendingRequest = null; + let resolvedComp; + let retries = 0; + const retry = () => { + retries++; + pendingRequest = null; + return load(); + }; + const load = () => { + let thisRequest; + return pendingRequest || (thisRequest = pendingRequest = loader().catch((err) => { + err = err instanceof Error ? err : new Error(String(err)); + if (userOnError) { + return new Promise((resolve, reject) => { + const userRetry = () => resolve(retry()); + const userFail = () => reject(err); + userOnError(err, userRetry, userFail, retries + 1); + }); + } else { + throw err; + } + }).then((comp) => { + if (thisRequest !== pendingRequest && pendingRequest) { + return pendingRequest; + } + if (!comp) { + warn$1( + `Async component loader resolved to undefined. If you are using retry(), make sure to return its return value.` + ); + } + if (comp && (comp.__esModule || comp[Symbol.toStringTag] === "Module")) { + comp = comp.default; + } + if (comp && !isObject(comp) && !isFunction(comp)) { + throw new Error(`Invalid async component load result: ${comp}`); + } + resolvedComp = comp; + return comp; + })); + }; + return defineComponent({ + name: "AsyncComponentWrapper", + __asyncLoader: load, + __asyncHydrate(el, instance, hydrate) { + let patched = false; + (instance.bu || (instance.bu = [])).push(() => patched = true); + const performHydrate = () => { + if (patched) { + { + warn$1( + `Skipping lazy hydration for component '${getComponentName(resolvedComp) || resolvedComp.__file}': it was updated before lazy hydration performed.` + ); + } + return; + } + hydrate(); + }; + const doHydrate = hydrateStrategy ? () => { + const teardown = hydrateStrategy( + performHydrate, + (cb) => forEachElement(el, cb) + ); + if (teardown) { + (instance.bum || (instance.bum = [])).push(teardown); + } + } : performHydrate; + if (resolvedComp) { + doHydrate(); + } else { + load().then(() => !instance.isUnmounted && doHydrate()); + } + }, + get __asyncResolved() { + return resolvedComp; + }, + setup() { + const instance = currentInstance; + markAsyncBoundary(instance); + if (resolvedComp) { + return () => createInnerComp(resolvedComp, instance); + } + const onError = (err) => { + pendingRequest = null; + handleError( + err, + instance, + 13, + !errorComponent + ); + }; + if (suspensible && instance.suspense || false) { + return load().then((comp) => { + return () => createInnerComp(comp, instance); + }).catch((err) => { + onError(err); + return () => errorComponent ? createVNode(errorComponent, { + error: err + }) : null; + }); + } + const loaded = ref(false); + const error = ref(); + const delayed = ref(!!delay); + if (delay) { + setTimeout(() => { + delayed.value = false; + }, delay); + } + if (timeout != null) { + setTimeout(() => { + if (!loaded.value && !error.value) { + const err = new Error( + `Async component timed out after ${timeout}ms.` + ); + onError(err); + error.value = err; + } + }, timeout); + } + load().then(() => { + loaded.value = true; + if (instance.parent && isKeepAlive(instance.parent.vnode)) { + instance.parent.update(); + } + }).catch((err) => { + onError(err); + error.value = err; + }); + return () => { + if (loaded.value && resolvedComp) { + return createInnerComp(resolvedComp, instance); + } else if (error.value && errorComponent) { + return createVNode(errorComponent, { + error: error.value + }); + } else if (loadingComponent && !delayed.value) { + return createInnerComp( + loadingComponent, + instance + ); + } + }; + } + }); + } + function createInnerComp(comp, parent) { + const { ref: ref2, props, children, ce } = parent.vnode; + const vnode = createVNode(comp, props, children); + vnode.ref = ref2; + vnode.ce = ce; + delete parent.vnode.ce; + return vnode; + } + + const isKeepAlive = (vnode) => vnode.type.__isKeepAlive; + const KeepAliveImpl = { + name: `KeepAlive`, + // Marker for special handling inside the renderer. We are not using a === + // check directly on KeepAlive in the renderer, because importing it directly + // would prevent it from being tree-shaken. + __isKeepAlive: true, + props: { + include: [String, RegExp, Array], + exclude: [String, RegExp, Array], + max: [String, Number] + }, + setup(props, { slots }) { + const instance = getCurrentInstance(); + const sharedContext = instance.ctx; + const cache = /* @__PURE__ */ new Map(); + const keys = /* @__PURE__ */ new Set(); + let current = null; + { + instance.__v_cache = cache; + } + const parentSuspense = instance.suspense; + const { + renderer: { + p: patch, + m: move, + um: _unmount, + o: { createElement } + } + } = sharedContext; + const storageContainer = createElement("div"); + sharedContext.activate = (vnode, container, anchor, namespace, optimized) => { + const instance2 = vnode.component; + move(vnode, container, anchor, 0, parentSuspense); + patch( + instance2.vnode, + vnode, + container, + anchor, + instance2, + parentSuspense, + namespace, + vnode.slotScopeIds, + optimized + ); + queuePostRenderEffect(() => { + instance2.isDeactivated = false; + if (instance2.a) { + invokeArrayFns(instance2.a); + } + const vnodeHook = vnode.props && vnode.props.onVnodeMounted; + if (vnodeHook) { + invokeVNodeHook(vnodeHook, instance2.parent, vnode); + } + }, parentSuspense); + { + devtoolsComponentAdded(instance2); + } + }; + sharedContext.deactivate = (vnode) => { + const instance2 = vnode.component; + invalidateMount(instance2.m); + invalidateMount(instance2.a); + move(vnode, storageContainer, null, 1, parentSuspense); + queuePostRenderEffect(() => { + if (instance2.da) { + invokeArrayFns(instance2.da); + } + const vnodeHook = vnode.props && vnode.props.onVnodeUnmounted; + if (vnodeHook) { + invokeVNodeHook(vnodeHook, instance2.parent, vnode); + } + instance2.isDeactivated = true; + }, parentSuspense); + { + devtoolsComponentAdded(instance2); + } + { + instance2.__keepAliveStorageContainer = storageContainer; + } + }; + function unmount(vnode) { + resetShapeFlag(vnode); + _unmount(vnode, instance, parentSuspense, true); + } + function pruneCache(filter) { + cache.forEach((vnode, key) => { + const name = getComponentName(vnode.type); + if (name && !filter(name)) { + pruneCacheEntry(key); + } + }); + } + function pruneCacheEntry(key) { + const cached = cache.get(key); + if (cached && (!current || !isSameVNodeType(cached, current))) { + unmount(cached); + } else if (current) { + resetShapeFlag(current); + } + cache.delete(key); + keys.delete(key); + } + watch( + () => [props.include, props.exclude], + ([include, exclude]) => { + include && pruneCache((name) => matches(include, name)); + exclude && pruneCache((name) => !matches(exclude, name)); + }, + // prune post-render after `current` has been updated + { flush: "post", deep: true } + ); + let pendingCacheKey = null; + const cacheSubtree = () => { + if (pendingCacheKey != null) { + if (isSuspense(instance.subTree.type)) { + queuePostRenderEffect(() => { + cache.set(pendingCacheKey, getInnerChild(instance.subTree)); + }, instance.subTree.suspense); + } else { + cache.set(pendingCacheKey, getInnerChild(instance.subTree)); + } + } + }; + onMounted(cacheSubtree); + onUpdated(cacheSubtree); + onBeforeUnmount(() => { + cache.forEach((cached) => { + const { subTree, suspense } = instance; + const vnode = getInnerChild(subTree); + if (cached.type === vnode.type && cached.key === vnode.key) { + resetShapeFlag(vnode); + const da = vnode.component.da; + da && queuePostRenderEffect(da, suspense); + return; + } + unmount(cached); + }); + }); + return () => { + pendingCacheKey = null; + if (!slots.default) { + return current = null; + } + const children = slots.default(); + const rawVNode = children[0]; + if (children.length > 1) { + { + warn$1(`KeepAlive should contain exactly one component child.`); + } + current = null; + return children; + } else if (!isVNode(rawVNode) || !(rawVNode.shapeFlag & 4) && !(rawVNode.shapeFlag & 128)) { + current = null; + return rawVNode; + } + let vnode = getInnerChild(rawVNode); + if (vnode.type === Comment) { + current = null; + return vnode; + } + const comp = vnode.type; + const name = getComponentName( + isAsyncWrapper(vnode) ? vnode.type.__asyncResolved || {} : comp + ); + const { include, exclude, max } = props; + if (include && (!name || !matches(include, name)) || exclude && name && matches(exclude, name)) { + vnode.shapeFlag &= -257; + current = vnode; + return rawVNode; + } + const key = vnode.key == null ? comp : vnode.key; + const cachedVNode = cache.get(key); + if (vnode.el) { + vnode = cloneVNode(vnode); + if (rawVNode.shapeFlag & 128) { + rawVNode.ssContent = vnode; + } + } + pendingCacheKey = key; + if (cachedVNode) { + vnode.el = cachedVNode.el; + vnode.component = cachedVNode.component; + if (vnode.transition) { + setTransitionHooks(vnode, vnode.transition); + } + vnode.shapeFlag |= 512; + keys.delete(key); + keys.add(key); + } else { + keys.add(key); + if (max && keys.size > parseInt(max, 10)) { + pruneCacheEntry(keys.values().next().value); + } + } + vnode.shapeFlag |= 256; + current = vnode; + return isSuspense(rawVNode.type) ? rawVNode : vnode; + }; + } + }; + const KeepAlive = KeepAliveImpl; + function matches(pattern, name) { + if (isArray(pattern)) { + return pattern.some((p) => matches(p, name)); + } else if (isString(pattern)) { + return pattern.split(",").includes(name); + } else if (isRegExp(pattern)) { + pattern.lastIndex = 0; + return pattern.test(name); + } + return false; + } + function onActivated(hook, target) { + registerKeepAliveHook(hook, "a", target); + } + function onDeactivated(hook, target) { + registerKeepAliveHook(hook, "da", target); + } + function registerKeepAliveHook(hook, type, target = currentInstance) { + const wrappedHook = hook.__wdc || (hook.__wdc = () => { + let current = target; + while (current) { + if (current.isDeactivated) { + return; + } + current = current.parent; + } + return hook(); + }); + injectHook(type, wrappedHook, target); + if (target) { + let current = target.parent; + while (current && current.parent) { + if (isKeepAlive(current.parent.vnode)) { + injectToKeepAliveRoot(wrappedHook, type, target, current); + } + current = current.parent; + } + } + } + function injectToKeepAliveRoot(hook, type, target, keepAliveRoot) { + const injected = injectHook( + type, + hook, + keepAliveRoot, + true + /* prepend */ + ); + onUnmounted(() => { + remove(keepAliveRoot[type], injected); + }, target); + } + function resetShapeFlag(vnode) { + vnode.shapeFlag &= -257; + vnode.shapeFlag &= -513; + } + function getInnerChild(vnode) { + return vnode.shapeFlag & 128 ? vnode.ssContent : vnode; + } + + function injectHook(type, hook, target = currentInstance, prepend = false) { + if (target) { + const hooks = target[type] || (target[type] = []); + const wrappedHook = hook.__weh || (hook.__weh = (...args) => { + pauseTracking(); + const reset = setCurrentInstance(target); + const res = callWithAsyncErrorHandling(hook, target, type, args); + reset(); + resetTracking(); + return res; + }); + if (prepend) { + hooks.unshift(wrappedHook); + } else { + hooks.push(wrappedHook); + } + return wrappedHook; + } else { + const apiName = toHandlerKey(ErrorTypeStrings$1[type].replace(/ hook$/, "")); + warn$1( + `${apiName} is called when there is no active component instance to be associated with. Lifecycle injection APIs can only be used during execution of setup().` + (` If you are using async setup(), make sure to register lifecycle hooks before the first await statement.` ) + ); + } + } + const createHook = (lifecycle) => (hook, target = currentInstance) => { + if (!isInSSRComponentSetup || lifecycle === "sp") { + injectHook(lifecycle, (...args) => hook(...args), target); + } + }; + const onBeforeMount = createHook("bm"); + const onMounted = createHook("m"); + const onBeforeUpdate = createHook( + "bu" + ); + const onUpdated = createHook("u"); + const onBeforeUnmount = createHook( + "bum" + ); + const onUnmounted = createHook("um"); + const onServerPrefetch = createHook( + "sp" + ); + const onRenderTriggered = createHook("rtg"); + const onRenderTracked = createHook("rtc"); + function onErrorCaptured(hook, target = currentInstance) { + injectHook("ec", hook, target); + } + + const COMPONENTS = "components"; + const DIRECTIVES = "directives"; + function resolveComponent(name, maybeSelfReference) { + return resolveAsset(COMPONENTS, name, true, maybeSelfReference) || name; + } + const NULL_DYNAMIC_COMPONENT = Symbol.for("v-ndc"); + function resolveDynamicComponent(component) { + if (isString(component)) { + return resolveAsset(COMPONENTS, component, false) || component; + } else { + return component || NULL_DYNAMIC_COMPONENT; + } + } + function resolveDirective(name) { + return resolveAsset(DIRECTIVES, name); + } + function resolveAsset(type, name, warnMissing = true, maybeSelfReference = false) { + const instance = currentRenderingInstance || currentInstance; + if (instance) { + const Component = instance.type; + if (type === COMPONENTS) { + const selfName = getComponentName( + Component, + false + ); + if (selfName && (selfName === name || selfName === camelize(name) || selfName === capitalize(camelize(name)))) { + return Component; + } + } + const res = ( + // local registration + // check instance[type] first which is resolved for options API + resolve(instance[type] || Component[type], name) || // global registration + resolve(instance.appContext[type], name) + ); + if (!res && maybeSelfReference) { + return Component; + } + if (warnMissing && !res) { + const extra = type === COMPONENTS ? ` +If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.` : ``; + warn$1(`Failed to resolve ${type.slice(0, -1)}: ${name}${extra}`); + } + return res; + } else { + warn$1( + `resolve${capitalize(type.slice(0, -1))} can only be used in render() or setup().` + ); + } + } + function resolve(registry, name) { + return registry && (registry[name] || registry[camelize(name)] || registry[capitalize(camelize(name))]); + } + + function renderList(source, renderItem, cache, index) { + let ret; + const cached = cache && cache[index]; + const sourceIsArray = isArray(source); + if (sourceIsArray || isString(source)) { + const sourceIsReactiveArray = sourceIsArray && isReactive(source); + let needsWrap = false; + let isReadonlySource = false; + if (sourceIsReactiveArray) { + needsWrap = !isShallow(source); + isReadonlySource = isReadonly(source); + source = shallowReadArray(source); + } + ret = new Array(source.length); + for (let i = 0, l = source.length; i < l; i++) { + ret[i] = renderItem( + needsWrap ? isReadonlySource ? toReadonly(toReactive(source[i])) : toReactive(source[i]) : source[i], + i, + void 0, + cached && cached[i] + ); + } + } else if (typeof source === "number") { + if (!Number.isInteger(source)) { + warn$1(`The v-for range expect an integer value but got ${source}.`); + } + ret = new Array(source); + for (let i = 0; i < source; i++) { + ret[i] = renderItem(i + 1, i, void 0, cached && cached[i]); + } + } else if (isObject(source)) { + if (source[Symbol.iterator]) { + ret = Array.from( + source, + (item, i) => renderItem(item, i, void 0, cached && cached[i]) + ); + } else { + const keys = Object.keys(source); + ret = new Array(keys.length); + for (let i = 0, l = keys.length; i < l; i++) { + const key = keys[i]; + ret[i] = renderItem(source[key], key, i, cached && cached[i]); + } + } + } else { + ret = []; + } + if (cache) { + cache[index] = ret; + } + return ret; + } + + function createSlots(slots, dynamicSlots) { + for (let i = 0; i < dynamicSlots.length; i++) { + const slot = dynamicSlots[i]; + if (isArray(slot)) { + for (let j = 0; j < slot.length; j++) { + slots[slot[j].name] = slot[j].fn; + } + } else if (slot) { + slots[slot.name] = slot.key ? (...args) => { + const res = slot.fn(...args); + if (res) res.key = slot.key; + return res; + } : slot.fn; + } + } + return slots; + } + + function renderSlot(slots, name, props = {}, fallback, noSlotted) { + if (currentRenderingInstance.ce || currentRenderingInstance.parent && isAsyncWrapper(currentRenderingInstance.parent) && currentRenderingInstance.parent.ce) { + const hasProps = Object.keys(props).length > 0; + if (name !== "default") props.name = name; + return openBlock(), createBlock( + Fragment, + null, + [createVNode("slot", props, fallback && fallback())], + hasProps ? -2 : 64 + ); + } + let slot = slots[name]; + if (slot && slot.length > 1) { + warn$1( + `SSR-optimized slot function detected in a non-SSR-optimized render function. You need to mark this component with $dynamic-slots in the parent template.` + ); + slot = () => []; + } + if (slot && slot._c) { + slot._d = false; + } + openBlock(); + const validSlotContent = slot && ensureValidVNode(slot(props)); + const slotKey = props.key || // slot content array of a dynamic conditional slot may have a branch + // key attached in the `createSlots` helper, respect that + validSlotContent && validSlotContent.key; + const rendered = createBlock( + Fragment, + { + key: (slotKey && !isSymbol(slotKey) ? slotKey : `_${name}`) + // #7256 force differentiate fallback content from actual content + (!validSlotContent && fallback ? "_fb" : "") + }, + validSlotContent || (fallback ? fallback() : []), + validSlotContent && slots._ === 1 ? 64 : -2 + ); + if (!noSlotted && rendered.scopeId) { + rendered.slotScopeIds = [rendered.scopeId + "-s"]; + } + if (slot && slot._c) { + slot._d = true; + } + return rendered; + } + function ensureValidVNode(vnodes) { + return vnodes.some((child) => { + if (!isVNode(child)) return true; + if (child.type === Comment) return false; + if (child.type === Fragment && !ensureValidVNode(child.children)) + return false; + return true; + }) ? vnodes : null; + } + + function toHandlers(obj, preserveCaseIfNecessary) { + const ret = {}; + if (!isObject(obj)) { + warn$1(`v-on with no argument expects an object value.`); + return ret; + } + for (const key in obj) { + ret[preserveCaseIfNecessary && /[A-Z]/.test(key) ? `on:${key}` : toHandlerKey(key)] = obj[key]; + } + return ret; + } + + const getPublicInstance = (i) => { + if (!i) return null; + if (isStatefulComponent(i)) return getComponentPublicInstance(i); + return getPublicInstance(i.parent); + }; + const publicPropertiesMap = ( + // Move PURE marker to new line to workaround compiler discarding it + // due to type annotation + /* @__PURE__ */ extend(/* @__PURE__ */ Object.create(null), { + $: (i) => i, + $el: (i) => i.vnode.el, + $data: (i) => i.data, + $props: (i) => shallowReadonly(i.props) , + $attrs: (i) => shallowReadonly(i.attrs) , + $slots: (i) => shallowReadonly(i.slots) , + $refs: (i) => shallowReadonly(i.refs) , + $parent: (i) => getPublicInstance(i.parent), + $root: (i) => getPublicInstance(i.root), + $host: (i) => i.ce, + $emit: (i) => i.emit, + $options: (i) => resolveMergedOptions(i) , + $forceUpdate: (i) => i.f || (i.f = () => { + queueJob(i.update); + }), + $nextTick: (i) => i.n || (i.n = nextTick.bind(i.proxy)), + $watch: (i) => instanceWatch.bind(i) + }) + ); + const isReservedPrefix = (key) => key === "_" || key === "$"; + const hasSetupBinding = (state, key) => state !== EMPTY_OBJ && !state.__isScriptSetup && hasOwn(state, key); + const PublicInstanceProxyHandlers = { + get({ _: instance }, key) { + if (key === "__v_skip") { + return true; + } + const { ctx, setupState, data, props, accessCache, type, appContext } = instance; + if (key === "__isVue") { + return true; + } + let normalizedProps; + if (key[0] !== "$") { + const n = accessCache[key]; + if (n !== void 0) { + switch (n) { + case 1 /* SETUP */: + return setupState[key]; + case 2 /* DATA */: + return data[key]; + case 4 /* CONTEXT */: + return ctx[key]; + case 3 /* PROPS */: + return props[key]; + } + } else if (hasSetupBinding(setupState, key)) { + accessCache[key] = 1 /* SETUP */; + return setupState[key]; + } else if (data !== EMPTY_OBJ && hasOwn(data, key)) { + accessCache[key] = 2 /* DATA */; + return data[key]; + } else if ( + // only cache other properties when instance has declared (thus stable) + // props + (normalizedProps = instance.propsOptions[0]) && hasOwn(normalizedProps, key) + ) { + accessCache[key] = 3 /* PROPS */; + return props[key]; + } else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) { + accessCache[key] = 4 /* CONTEXT */; + return ctx[key]; + } else if (shouldCacheAccess) { + accessCache[key] = 0 /* OTHER */; + } + } + const publicGetter = publicPropertiesMap[key]; + let cssModule, globalProperties; + if (publicGetter) { + if (key === "$attrs") { + track(instance.attrs, "get", ""); + markAttrsAccessed(); + } else if (key === "$slots") { + track(instance, "get", key); + } + return publicGetter(instance); + } else if ( + // css module (injected by vue-loader) + (cssModule = type.__cssModules) && (cssModule = cssModule[key]) + ) { + return cssModule; + } else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) { + accessCache[key] = 4 /* CONTEXT */; + return ctx[key]; + } else if ( + // global properties + globalProperties = appContext.config.globalProperties, hasOwn(globalProperties, key) + ) { + { + return globalProperties[key]; + } + } else if (currentRenderingInstance && (!isString(key) || // #1091 avoid internal isRef/isVNode checks on component instance leading + // to infinite warning loop + key.indexOf("__v") !== 0)) { + if (data !== EMPTY_OBJ && isReservedPrefix(key[0]) && hasOwn(data, key)) { + warn$1( + `Property ${JSON.stringify( + key + )} must be accessed via $data because it starts with a reserved character ("$" or "_") and is not proxied on the render context.` + ); + } else if (instance === currentRenderingInstance) { + warn$1( + `Property ${JSON.stringify(key)} was accessed during render but is not defined on instance.` + ); + } + } + }, + set({ _: instance }, key, value) { + const { data, setupState, ctx } = instance; + if (hasSetupBinding(setupState, key)) { + setupState[key] = value; + return true; + } else if (setupState.__isScriptSetup && hasOwn(setupState, key)) { + warn$1(`Cannot mutate + + diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..2b9ddd4 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,34 @@ +server { + listen 80; + server_name localhost; + + # 前端静态文件 + location / { + root /usr/share/nginx/html; + index index.html; + try_files $uri $uri/ =404; + } + + # 后端API反向代理 + location /api/ { + proxy_pass http://backend:40001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # 使用上游传递的协议,如果没有则使用当前协议 + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + proxy_cache_bypass $http_upgrade; + } + + # 分享链接重定向 + location /s/ { + proxy_pass http://backend:40001; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + } +} diff --git a/nginx/nginx.conf.example b/nginx/nginx.conf.example new file mode 100644 index 0000000..eb84b66 --- /dev/null +++ b/nginx/nginx.conf.example @@ -0,0 +1,52 @@ +server { + listen 80; + server_name your-domain.com; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://$server_name$request_uri; + } +} + +server { + listen 443 ssl http2; + server_name your-domain.com; + + ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + # 前端静态文件 + location / { + root /usr/share/nginx/html; + index index.html; + try_files $uri $uri/ =404; + } + + # 后端API反向代理 + location /api/ { + proxy_pass http://backend:40001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + } + + # 分享链接重定向 + location /s/ { + proxy_pass http://backend:40001; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} diff --git a/upload-tool/README.txt b/upload-tool/README.txt new file mode 100644 index 0000000..32125ed --- /dev/null +++ b/upload-tool/README.txt @@ -0,0 +1,92 @@ +============================================ +玩玩云上传工具 v2.0 使用说明 +============================================ + +【新版本特性】 +✨ 支持多文件上传 +✨ 支持文件夹上传(递归扫描所有文件) +✨ 智能上传队列管理 +✨ 自动检测可写目录(容错机制) +✨ 实时显示队列状态 + +【功能介绍】 +本工具用于快速上传文件到您的SFTP服务器。 +新版本支持批量上传和文件夹上传,大大提升工作效率! + +【使用方法】 +1. 双击运行"玩玩云上传工具.exe" +2. 等待程序连接服务器并测试上传目录 + - 程序会自动测试多个目录的可写性 + - 显示绿色✓表示连接成功 + - 显示当前使用的上传目录 +3. 拖拽文件或文件夹到窗口中 + - 可以一次拖拽多个文件 + - 可以拖拽整个文件夹(自动扫描所有文件) + - 混合拖拽也支持 +4. 查看队列状态 + - 界面显示"队列: X 个文件等待上传" + - 文件会按顺序依次上传 +5. 实时查看上传进度 + - 每个文件都有独立的进度显示 + - 日志区域显示详细的上传信息 + +【目录容错机制】 +程序会按以下优先级自动测试并选择可写目录: +1. /(根目录) +2. /upload +3. /uploads +4. /files +5. /home +6. /tmp + +如果根目录没有写权限,程序会自动切换到其他可用目录。 + +【注意事项】 +- 文件夹上传会递归扫描所有子文件夹 +- 同名文件会被覆盖 +- 上传大量文件时请确保网络稳定 +- 所有文件会按顺序依次上传 +- 上传目录会在启动时自动检测并显示 + +【界面说明】 +- 拖拽区域:显示"支持多文件和文件夹" +- 队列状态:显示等待上传的文件数量 +- 进度条:显示当前文件的上传进度 +- 日志区域:显示详细的操作记录 + +【版本更新】 +v2.0 (2025-11-09) +- ✅ 新增多文件上传支持 +- ✅ 新增文件夹上传支持 +- ✅ 新增上传队列管理 +- ✅ 新增目录容错机制 +- ✅ 优化界面显示 +- ✅ 优化日志输出 + +v1.0 +- 基础单文件上传功能 + +【常见问题】 + +Q: 支持上传多少个文件? +A: 理论上无限制,所有文件会加入队列依次上传 + +Q: 文件夹上传包括子文件夹吗? +A: 是的,会递归扫描所有子文件夹中的文件 + +Q: 上传目录是哪里? +A: 程序启动时会自动检测并显示在界面上 + +Q: 提示"API密钥无效或已过期"怎么办? +A: 请重新从网站下载最新的上传工具 + +Q: 上传速度慢怎么办? +A: 速度取决于您的网络和SFTP服务器性能 + +Q: 可以中途取消上传吗? +A: 当前版本暂不支持取消,请等待队列完成 + +【技术支持】 +如有问题请联系管理员 + +============================================ diff --git a/upload-tool/build.bat b/upload-tool/build.bat new file mode 100644 index 0000000..e4fa176 --- /dev/null +++ b/upload-tool/build.bat @@ -0,0 +1,52 @@ +@echo off +chcp 65001 > nul +echo ======================================== +echo 玩玩云上传工具打包脚本 +echo ======================================== +echo. + +REM 检查Python是否安装 +python --version > nul 2>&1 +if errorlevel 1 ( + echo [错误] 未检测到Python,请先安装Python 3.7+ + pause + exit /b 1 +) + +echo [1/4] 安装依赖包... +pip install -r requirements.txt +if errorlevel 1 ( + echo [错误] 依赖安装失败 + pause + exit /b 1 +) + +echo. +echo [2/4] 安装PyInstaller... +pip install pyinstaller +if errorlevel 1 ( + echo [错误] PyInstaller安装失败 + pause + exit /b 1 +) + +echo. +echo [3/4] 打包程序... +pyinstaller --onefile --windowed --name="玩玩云上传工具" --icon=NONE upload_tool.py +if errorlevel 1 ( + echo [错误] 打包失败 + pause + exit /b 1 +) + +echo. +echo [4/4] 清理临时文件... +rmdir /s /q build +del /q *.spec + +echo. +echo ======================================== +echo 打包完成! +echo 输出文件: dist\玩玩云上传工具.exe +echo ======================================== +pause diff --git a/upload-tool/requirements.txt b/upload-tool/requirements.txt new file mode 100644 index 0000000..735d771 --- /dev/null +++ b/upload-tool/requirements.txt @@ -0,0 +1,3 @@ +PyQt5==5.15.9 +paramiko==3.4.0 +requests==2.31.0 diff --git a/upload-tool/upload_tool.py b/upload-tool/upload_tool.py new file mode 100644 index 0000000..ae250fa --- /dev/null +++ b/upload-tool/upload_tool.py @@ -0,0 +1,499 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import sys +import os +import json +import requests +import paramiko +from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QVBoxLayout, + QWidget, QProgressBar, QTextEdit, QPushButton) +from PyQt5.QtCore import Qt, QThread, pyqtSignal +from PyQt5.QtGui import QDragEnterEvent, QDropEvent, QFont + +class TestDirectoryThread(QThread): + """测试目录可写性线程""" + result = pyqtSignal(bool, str) # 成功/失败,目录路径或错误信息 + + def __init__(self, test_dir, sftp_config): + super().__init__() + self.test_dir = test_dir + self.sftp_config = sftp_config + + def run(self): + try: + transport = paramiko.Transport(( + self.sftp_config['host'], + self.sftp_config['port'] + )) + transport.connect( + username=self.sftp_config['username'], + password=self.sftp_config['password'] + ) + sftp = paramiko.SFTPClient.from_transport(transport) + + # 测试文件名 + test_file = f"{self.test_dir}/.wwy_test_{os.getpid()}" + if not self.test_dir.endswith('/'): + test_file = f"{self.test_dir}/.wwy_test_{os.getpid()}" + else: + test_file = f"{self.test_dir}.wwy_test_{os.getpid()}" + + # 尝试创建测试文件 + try: + with sftp.open(test_file, 'w') as f: + f.write('test') + + # 删除测试文件 + try: + sftp.remove(test_file) + except: + pass + + sftp.close() + transport.close() + self.result.emit(True, self.test_dir) + + except Exception as e: + sftp.close() + transport.close() + self.result.emit(False, str(e)) + + except Exception as e: + self.result.emit(False, str(e)) + +class UploadThread(QThread): + """上传线程""" + progress = pyqtSignal(int, str) # 进度,状态信息 + finished = pyqtSignal(bool, str) # 成功/失败,消息 + + def __init__(self, sftp_config, file_path, remote_dir): + super().__init__() + self.sftp_config = sftp_config + self.file_path = file_path + self.remote_dir = remote_dir + + def run(self): + try: + # 连接SFTP + self.progress.emit(10, f'正在连接服务器...') + transport = paramiko.Transport(( + self.sftp_config['host'], + self.sftp_config['port'] + )) + transport.connect( + username=self.sftp_config['username'], + password=self.sftp_config['password'] + ) + sftp = paramiko.SFTPClient.from_transport(transport) + + self.progress.emit(30, f'连接成功,开始上传...') + + # 获取文件名 + filename = os.path.basename(self.file_path) + # 构建远程路径 + if self.remote_dir.endswith('/'): + remote_path = f'{self.remote_dir}{filename}' + else: + remote_path = f'{self.remote_dir}/{filename}' + + # 上传文件(带进度)- 使用临时文件避免.fuse_hidden问题 + file_size = os.path.getsize(self.file_path) + uploaded = 0 + + # 使用临时文件名 + import time + temp_remote_path = f'{remote_path}.uploading_{int(time.time() * 1000)}' + + def callback(transferred, total): + nonlocal uploaded + uploaded = transferred + percent = int((transferred / total) * 100) if total > 0 else 0 + # 进度从30%到90% + progress_value = 30 + int(percent * 0.6) + + # 计算速度 + size_mb = transferred / (1024 * 1024) + self.progress.emit(progress_value, f'上传中: {filename} ({size_mb:.2f} MB / {total/(1024*1024):.2f} MB)') + + # 第一步:上传到临时文件 + sftp.put(self.file_path, temp_remote_path, callback=callback) + + # 第二步:删除旧文件(如果存在) + try: + sftp.stat(remote_path) + sftp.remove(remote_path) + except: + pass # 文件不存在,无需删除 + + # 第三步:重命名临时文件为目标文件 + sftp.rename(temp_remote_path, remote_path) + + # 关闭连接 + sftp.close() + transport.close() + + self.progress.emit(100, f'上传完成!') + self.finished.emit(True, f'文件 {filename} 上传成功!') + + except Exception as e: + self.finished.emit(False, f'上传失败: {str(e)}') + + +class UploadWindow(QMainWindow): + def __init__(self): + super().__init__() + self.config = self.load_config() + self.sftp_config = None + self.remote_dir = '/' # 默认上传目录 + self.upload_queue = [] # 上传队列 + self.is_uploading = False # 是否正在上传 + self.initUI() + self.get_sftp_config() + + def load_config(self): + """加载配置文件""" + try: + # PyInstaller打包后使用sys._MEIPASS + if getattr(sys, 'frozen', False): + # 打包后的exe + base_path = os.path.dirname(sys.executable) + else: + # 开发环境 + base_path = os.path.dirname(__file__) + + config_path = os.path.join(base_path, 'config.json') + + if not os.path.exists(config_path): + from PyQt5.QtWidgets import QMessageBox + QMessageBox.critical(None, '错误', f'找不到配置文件: {config_path}\n\n请确保config.json与程序在同一目录下!') + sys.exit(1) + + with open(config_path, 'r', encoding='utf-8') as f: + return json.load(f) + except Exception as e: + from PyQt5.QtWidgets import QMessageBox + QMessageBox.critical(None, '错误', f'加载配置失败:\n{str(e)}') + sys.exit(1) + + def get_sftp_config(self): + """从服务器获取SFTP配置""" + try: + response = requests.post( + f"{self.config['api_base_url']}/api/upload/get-config", + json={'api_key': self.config['api_key']}, + timeout=10 + ) + + if response.status_code == 200: + data = response.json() + if data['success']: + self.sftp_config = data['sftp_config'] + # 自动测试并设置上传目录 + self.test_and_set_upload_directory() + else: + self.show_error(data.get('message', '获取配置失败')) + else: + self.show_error(f'服务器错误: {response.status_code}') + + except Exception as e: + self.show_error(f'无法连接到服务器: {str(e)}') + + def test_and_set_upload_directory(self): + """测试并设置上传目录""" + if not self.sftp_config: + return + + self.log('开始测试上传目录...') + self.status_label.setText( + f'

玩玩云上传工具 v2.0

' + f'

正在测试上传目录...

' + ) + + # 按优先级测试目录 + self.test_dirs = ['/', '/upload', '/uploads', '/files', '/home', '/tmp'] + self.current_test_index = 0 + + self.test_next_directory() + + def test_next_directory(self): + """测试下一个目录""" + if self.current_test_index >= len(self.test_dirs): + self.log('✗ 所有目录都无法写入,请检查SFTP权限') + self.show_error('无法找到可写入的目录,请检查SFTP权限') + return + + test_dir = self.test_dirs[self.current_test_index] + self.log(f'测试目录: {test_dir}') + + self.test_thread = TestDirectoryThread(test_dir, self.sftp_config) + self.test_thread.result.connect(self.on_test_result) + self.test_thread.start() + + def on_test_result(self, success, message): + """处理测试结果""" + if success: + self.remote_dir = message + self.log(f'✓ 已设置上传目录为: {message}') + self.status_label.setText( + f'

玩玩云上传工具 v2.0

' + f'

✓ 已连接 - 用户: {self.config["username"]}

' + f'

拖拽文件/文件夹到此处上传

' + f'

上传目录: {self.remote_dir}

' + ) + else: + self.log(f'✗ 目录 {self.test_dirs[self.current_test_index]} 不可写: {message}') + self.current_test_index += 1 + self.test_next_directory() + + def show_error(self, message): + """显示错误信息""" + self.status_label.setText( + f'

玩玩云上传工具 v2.0

' + f'

✗ 错误: {message}

' + f'

请检查网络连接或联系管理员

' + ) + + def initUI(self): + """初始化界面""" + self.setWindowTitle('玩玩云上传工具 v2.0') + self.setGeometry(300, 300, 500, 450) + + # 设置接受拖拽 + self.setAcceptDrops(True) + + # 中心部件 + central_widget = QWidget() + self.setCentralWidget(central_widget) + + # 布局 + layout = QVBoxLayout() + + # 状态标签 + self.status_label = QLabel('正在连接服务器...') + self.status_label.setAlignment(Qt.AlignCenter) + self.status_label.setFont(QFont('Arial', 11)) + self.status_label.setWordWrap(True) + self.status_label.setStyleSheet('padding: 20px;') + layout.addWidget(self.status_label) + + # 拖拽提示区域 + self.drop_area = QLabel('📁\n\n支持多文件和文件夹') + self.drop_area.setAlignment(Qt.AlignCenter) + self.drop_area.setStyleSheet(""" + QLabel { + font-size: 50px; + color: #667eea; + border: 3px dashed #667eea; + border-radius: 10px; + background-color: #f5f7fa; + padding: 40px; + } + """) + layout.addWidget(self.drop_area) + + # 队列状态标签 + self.queue_label = QLabel('队列: 0 个文件等待上传') + self.queue_label.setAlignment(Qt.AlignCenter) + self.queue_label.setStyleSheet('color: #2c3e50; font-weight: bold; padding: 5px;') + layout.addWidget(self.queue_label) + + # 进度条 + self.progress_bar = QProgressBar() + self.progress_bar.setValue(0) + self.progress_bar.setVisible(False) + self.progress_bar.setStyleSheet(""" + QProgressBar { + border: 2px solid #e0e0e0; + border-radius: 5px; + text-align: center; + height: 25px; + } + QProgressBar::chunk { + background-color: #667eea; + } + """) + layout.addWidget(self.progress_bar) + + # 进度信息 + self.progress_label = QLabel('') + self.progress_label.setAlignment(Qt.AlignCenter) + self.progress_label.setVisible(False) + layout.addWidget(self.progress_label) + + # 日志区域 + self.log_text = QTextEdit() + self.log_text.setReadOnly(True) + self.log_text.setMaximumHeight(100) + self.log_text.setStyleSheet(""" + QTextEdit { + background-color: #f9f9f9; + border: 1px solid #e0e0e0; + border-radius: 5px; + padding: 5px; + font-family: 'Courier New', monospace; + font-size: 11px; + } + """) + layout.addWidget(self.log_text) + + central_widget.setLayout(layout) + + self.log('程序已启动 - 版本 v2.0') + + def log(self, message): + """添加日志""" + self.log_text.append(f'[{self.get_time()}] {message}') + # 自动滚动到底部 + self.log_text.verticalScrollBar().setValue( + self.log_text.verticalScrollBar().maximum() + ) + + def get_time(self): + """获取当前时间""" + from datetime import datetime + return datetime.now().strftime('%H:%M:%S') + + def dragEnterEvent(self, event: QDragEnterEvent): + """拖拽进入事件""" + if event.mimeData().hasUrls(): + event.acceptProposedAction() + self.drop_area.setStyleSheet(""" + QLabel { + font-size: 50px; + color: #667eea; + border: 3px dashed #667eea; + border-radius: 10px; + background-color: #e8ecf7; + padding: 40px; + } + """) + + def dragLeaveEvent(self, event): + """拖拽离开事件""" + self.drop_area.setStyleSheet(""" + QLabel { + font-size: 50px; + color: #667eea; + border: 3px dashed #667eea; + border-radius: 10px; + background-color: #f5f7fa; + padding: 40px; + } + """) + + def dropEvent(self, event: QDropEvent): + """拖拽放下事件""" + self.drop_area.setStyleSheet(""" + QLabel { + font-size: 50px; + color: #667eea; + border: 3px dashed #667eea; + border-radius: 10px; + background-color: #f5f7fa; + padding: 40px; + } + """) + + if not self.sftp_config: + self.log('错误: 未获取到SFTP配置') + return + + paths = [url.toLocalFile() for url in event.mimeData().urls()] + + all_files = [] + for path in paths: + if os.path.isfile(path): + all_files.append(path) + elif os.path.isdir(path): + self.log(f'扫描文件夹: {os.path.basename(path)}') + folder_files = self.scan_folder(path) + all_files.extend(folder_files) + self.log(f'找到 {len(folder_files)} 个文件') + + if all_files: + self.upload_queue.extend(all_files) + self.update_queue_label() + self.log(f'添加 {len(all_files)} 个文件到上传队列') + + if not self.is_uploading: + self.process_upload_queue() + + def scan_folder(self, folder_path): + """递归扫描文件夹""" + files = [] + try: + for root, dirs, filenames in os.walk(folder_path): + for filename in filenames: + file_path = os.path.join(root, filename) + files.append(file_path) + except Exception as e: + self.log(f'扫描文件夹失败: {str(e)}') + + return files + + def update_queue_label(self): + """更新队列标签""" + count = len(self.upload_queue) + self.queue_label.setText(f'队列: {count} 个文件等待上传') + + def process_upload_queue(self): + """处理上传队列""" + if not self.upload_queue: + self.is_uploading = False + self.update_queue_label() + self.log('✓ 所有文件上传完成!') + return + + self.is_uploading = True + file_path = self.upload_queue.pop(0) + self.update_queue_label() + + self.upload_file(file_path) + + def upload_file(self, file_path): + """上传文件""" + self.log(f'开始上传: {os.path.basename(file_path)}') + + # 显示进度控件 + self.progress_bar.setVisible(True) + self.progress_bar.setValue(0) + self.progress_label.setVisible(True) + self.progress_label.setText('准备上传...') + + # 创建上传线程 + self.upload_thread = UploadThread(self.sftp_config, file_path, self.remote_dir) + self.upload_thread.progress.connect(self.on_progress) + self.upload_thread.finished.connect(self.on_finished) + self.upload_thread.start() + + def on_progress(self, value, message): + """上传进度更新""" + self.progress_bar.setValue(value) + self.progress_label.setText(message) + + def on_finished(self, success, message): + """上传完成""" + self.log(message) + + if success: + self.progress_label.setText('✓ ' + message) + self.progress_label.setStyleSheet('color: green; font-weight: bold;') + else: + self.progress_label.setText('✗ ' + message) + self.progress_label.setStyleSheet('color: red; font-weight: bold;') + + # 继续处理队列 + from PyQt5.QtCore import QTimer + QTimer.singleShot(1000, self.process_upload_queue) + + +def main(): + app = QApplication(sys.argv) + window = UploadWindow() + window.show() + sys.exit(app.exec_()) + + +if __name__ == '__main__': + main()